From 20e66b4d9846e2cbb2bca11dd18bcf082de8fbcd Mon Sep 17 00:00:00 2001 From: kobayasi Date: Mon, 17 Feb 2014 04:11:46 +0900 Subject: [PATCH] Add libgdx support. --- build.sbt | 2 +- desktop/build.sbt | 4 - .../src/main/resources}/com/jme3/asset/Desktop.cfg | 48 +- engine/build.sbt | 4 + engine/src/desktop/com/jme3/system/JmeSystem.java | 2 + gdx/build.sbt | 7 + .../java/com/jme3/asset/gdx/GdxAssetManager.java | 47 + .../com/jme3/asset/gdx/MMSFilterInputStream.java | 109 + .../java/com/jme3/renderer/gdx/GdxRenderer.java | 3187 ++++++++++++++++++++ .../java/com/jme3/renderer/gdx/TextureUtilGdx.java | 499 +++ .../com/jme3/system/JmeSystemDelegateImpl.java | 133 + .../java/com/jme3/system/gdx/GdxAssetCache.java | 27 + .../java/com/jme3/system/gdx/GdxAudioData.java | 64 + .../java/com/jme3/system/gdx/GdxAudioLoader.java | 26 + .../java/com/jme3/system/gdx/GdxAudioRenderer.java | 193 ++ .../main/java/com/jme3/system/gdx/GdxContext.java | 145 + .../main/java/com/jme3/system/gdx/GdxInput.java | 426 +++ .../main/java/com/jme3/system/gdx/GdxTimer.java | 97 + .../jme3/texture/plugins/gdx/GdxImageLoader.java | 140 + .../com/jme3/texture/plugins/gdx/GdxTGALoader.java | 572 ++++ gdx/src/main/java/com/jme3/util/RingBuffer.java | 75 + gdx/src/main/resources/asset/Desktop.cfg | 22 + 22 files changed, 5800 insertions(+), 29 deletions(-) rename {engine/src/desktop => desktop/src/main/resources}/com/jme3/asset/Desktop.cfg (98%) create mode 100644 gdx/build.sbt create mode 100644 gdx/src/main/java/com/jme3/asset/gdx/GdxAssetManager.java create mode 100644 gdx/src/main/java/com/jme3/asset/gdx/MMSFilterInputStream.java create mode 100644 gdx/src/main/java/com/jme3/renderer/gdx/GdxRenderer.java create mode 100644 gdx/src/main/java/com/jme3/renderer/gdx/TextureUtilGdx.java create mode 100644 gdx/src/main/java/com/jme3/system/JmeSystemDelegateImpl.java create mode 100644 gdx/src/main/java/com/jme3/system/gdx/GdxAssetCache.java create mode 100644 gdx/src/main/java/com/jme3/system/gdx/GdxAudioData.java create mode 100644 gdx/src/main/java/com/jme3/system/gdx/GdxAudioLoader.java create mode 100644 gdx/src/main/java/com/jme3/system/gdx/GdxAudioRenderer.java create mode 100644 gdx/src/main/java/com/jme3/system/gdx/GdxContext.java create mode 100644 gdx/src/main/java/com/jme3/system/gdx/GdxInput.java create mode 100644 gdx/src/main/java/com/jme3/system/gdx/GdxTimer.java create mode 100644 gdx/src/main/java/com/jme3/texture/plugins/gdx/GdxImageLoader.java create mode 100644 gdx/src/main/java/com/jme3/texture/plugins/gdx/GdxTGALoader.java create mode 100644 gdx/src/main/java/com/jme3/util/RingBuffer.java create mode 100644 gdx/src/main/resources/asset/Desktop.cfg diff --git a/build.sbt b/build.sbt index 18a240e06..46b162dfb 100644 --- a/build.sbt +++ b/build.sbt @@ -10,7 +10,7 @@ lazy val desktop = project.dependsOn(engine) lazy val android = project -lazy val gdx = project +lazy val gdx = project.dependsOn(engine) lazy val niftygui = project.dependsOn(engine) diff --git a/desktop/build.sbt b/desktop/build.sbt index 3deb9750f..cb096053f 100644 --- a/desktop/build.sbt +++ b/desktop/build.sbt @@ -10,10 +10,6 @@ unmanagedSourceDirectories in Compile ++= Seq( libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl" % "2.9.0" -libraryDependencies += "net.sf.sociaal" % "j-ogg-oggd" % "3.0.0.20130526" - -libraryDependencies += "net.sf.sociaal" % "j-ogg-vorbisd" % "3.0.0.20130526" - libraryDependencies += "net.java.jinput" % "jinput" % "2.0.5" libraryDependencies += "org.bushe" % "eventbus" % "1.4" diff --git a/engine/src/desktop/com/jme3/asset/Desktop.cfg b/desktop/src/main/resources/com/jme3/asset/Desktop.cfg similarity index 98% rename from engine/src/desktop/com/jme3/asset/Desktop.cfg rename to desktop/src/main/resources/com/jme3/asset/Desktop.cfg index a102e6300..483ffb226 100644 --- a/engine/src/desktop/com/jme3/asset/Desktop.cfg +++ b/desktop/src/main/resources/com/jme3/asset/Desktop.cfg @@ -1,24 +1,24 @@ -LOCATOR / com.jme3.asset.plugins.ClasspathLocator - -LOADER com.jme3.texture.plugins.AWTLoader : jpg, bmp, gif, png, jpeg, sph, spa -LOADER com.jme3.audio.plugins.WAVLoader : wav -LOADER com.jme3.audio.plugins.OGGLoader : ogg -LOADER com.jme3.material.plugins.J3MLoader : j3m -LOADER com.jme3.material.plugins.J3MLoader : j3md -LOADER com.jme3.font.plugins.BitmapFontLoader : fnt -LOADER com.jme3.texture.plugins.DDSLoader : dds -LOADER com.jme3.texture.plugins.PFMLoader : pfm -LOADER com.jme3.texture.plugins.HDRLoader : hdr -LOADER com.jme3.texture.plugins.TGALoader : tga -LOADER com.jme3.export.binary.BinaryImporter : j3o -LOADER com.jme3.export.binary.BinaryImporter : j3f -LOADER com.jme3.scene.plugins.OBJLoader : obj -LOADER com.jme3.scene.plugins.MTLLoader : mtl -LOADER com.jme3.scene.plugins.ogre.MeshLoader : meshxml, mesh.xml -LOADER com.jme3.scene.plugins.ogre.SkeletonLoader : skeletonxml, skeleton.xml -LOADER com.jme3.scene.plugins.ogre.MaterialLoader : material -LOADER com.jme3.scene.plugins.ogre.SceneLoader : scene -LOADER com.jme3.scene.plugins.blender.BlenderModelLoader : blend -LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, glsl, glsllib -LOADER projectkyoto.jme3.mmd.PMDLoaderGLSLSkinning2 : pmd -LOADER projectkyoto.jme3.mmd.VMDLoader : vmd +LOCATOR / com.jme3.asset.plugins.ClasspathLocator + +LOADER com.jme3.texture.plugins.AWTLoader : jpg, bmp, gif, png, jpeg, sph, spa +LOADER com.jme3.audio.plugins.WAVLoader : wav +LOADER com.jme3.audio.plugins.OGGLoader : ogg +LOADER com.jme3.material.plugins.J3MLoader : j3m +LOADER com.jme3.material.plugins.J3MLoader : j3md +LOADER com.jme3.font.plugins.BitmapFontLoader : fnt +LOADER com.jme3.texture.plugins.DDSLoader : dds +LOADER com.jme3.texture.plugins.PFMLoader : pfm +LOADER com.jme3.texture.plugins.HDRLoader : hdr +LOADER com.jme3.texture.plugins.TGALoader : tga +LOADER com.jme3.export.binary.BinaryImporter : j3o +LOADER com.jme3.export.binary.BinaryImporter : j3f +LOADER com.jme3.scene.plugins.OBJLoader : obj +LOADER com.jme3.scene.plugins.MTLLoader : mtl +LOADER com.jme3.scene.plugins.ogre.MeshLoader : meshxml, mesh.xml +LOADER com.jme3.scene.plugins.ogre.SkeletonLoader : skeletonxml, skeleton.xml +LOADER com.jme3.scene.plugins.ogre.MaterialLoader : material +LOADER com.jme3.scene.plugins.ogre.SceneLoader : scene +LOADER com.jme3.scene.plugins.blender.BlenderModelLoader : blend +LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, glsl, glsllib +LOADER projectkyoto.jme3.mmd.PMDLoaderGLSLSkinning2 : pmd +LOADER projectkyoto.jme3.mmd.VMDLoader : vmd diff --git a/engine/build.sbt b/engine/build.sbt index b25ff608f..564ec78b4 100644 --- a/engine/build.sbt +++ b/engine/build.sbt @@ -15,6 +15,7 @@ unmanagedSourceDirectories in Compile := Seq( , baseDirectory.value / "src/desktop-fx" , baseDirectory.value / "src/games" // , baseDirectory.value / "src/jheora" + , baseDirectory.value / "src/jogg" // , baseDirectory.value / "src/lwjgl-oal" // , baseDirectory.value / "src/lwjgl-ogl" , baseDirectory.value / "src/mmd" @@ -45,3 +46,6 @@ libraryDependencies += "xpp3" % "xpp3" % "1.1.4c" libraryDependencies += "com.jme3" % "noise" % "3.0.0-SNAPSHOT" +libraryDependencies += "net.sf.sociaal" % "j-ogg-oggd" % "3.0.0.20130526" + +libraryDependencies += "net.sf.sociaal" % "j-ogg-vorbisd" % "3.0.0.20130526" \ No newline at end of file diff --git a/engine/src/desktop/com/jme3/system/JmeSystem.java b/engine/src/desktop/com/jme3/system/JmeSystem.java index 865906956..0901c93d0 100644 --- a/engine/src/desktop/com/jme3/system/JmeSystem.java +++ b/engine/src/desktop/com/jme3/system/JmeSystem.java @@ -31,6 +31,7 @@ */ package com.jme3.system; +import com.jme3.app.Application; import com.jme3.asset.AssetManager; import com.jme3.audio.AudioRenderer; @@ -44,6 +45,7 @@ public class JmeSystem { private static boolean initialized = false; private static JmeSystemDelegate delegate; + protected static ThreadLocal app = new ThreadLocal(); static { try { diff --git a/gdx/build.sbt b/gdx/build.sbt new file mode 100644 index 000000000..2cf88e252 --- /dev/null +++ b/gdx/build.sbt @@ -0,0 +1,7 @@ +Common.settings + +name := "mms-gdx" + +libraryDependencies ++= Seq( + "com.badlogicgames.gdx" % "gdx" % "1.0-SNAPSHOT" +) \ No newline at end of file diff --git a/gdx/src/main/java/com/jme3/asset/gdx/GdxAssetManager.java b/gdx/src/main/java/com/jme3/asset/gdx/GdxAssetManager.java new file mode 100644 index 000000000..875ce1ba7 --- /dev/null +++ b/gdx/src/main/java/com/jme3/asset/gdx/GdxAssetManager.java @@ -0,0 +1,47 @@ +package com.jme3.asset.gdx; + +import com.jme3.asset.*; +import com.jme3.material.Material; +import com.jme3.system.gdx.GdxAudioLoader; +import com.jme3.texture.Texture; +import com.jme3.texture.plugins.gdx.GdxTGALoader; +import com.jme3.texture.plugins.gdx.GdxImageLoader; + +/** + * Created with IntelliJ IDEA. + * User: kobayasi + * Date: 13/10/08 + * Time: 0:56 + * To change this template use File | Settings | File Templates. + */ +public class GdxAssetManager extends DesktopAssetManager { + public GdxAssetManager() { + super(true); + registerLoader(GdxTGALoader.class, "tga"); + this.registerLoader(GdxImageLoader.class, "jpg", "bmp", "gif", "png", "jpeg","spa","sph"); + this.registerLoader(GdxAudioLoader.class, "wav", "mp3", "ogg"); + } + + @Override + public T loadAsset(AssetKey key) { + return super.loadAsset(key); //To change body of overridden methods use File | Settings | File Templates. + } + + @Override + public Object loadAsset(String name) { + return super.loadAsset(name); //To change body of overridden methods use File | Settings | File Templates. + } + + @Override + public Texture loadTexture(TextureKey key) { + return super.loadTexture(key); //To change body of overridden methods use File | Settings | File Templates. + } + + @Override + public Material loadMaterial(String name) { + return super.loadMaterial(name); //To change body of overridden methods use File | Settings | File Templates. + } + + + +} diff --git a/gdx/src/main/java/com/jme3/asset/gdx/MMSFilterInputStream.java b/gdx/src/main/java/com/jme3/asset/gdx/MMSFilterInputStream.java new file mode 100644 index 000000000..9e49d9c07 --- /dev/null +++ b/gdx/src/main/java/com/jme3/asset/gdx/MMSFilterInputStream.java @@ -0,0 +1,109 @@ +package com.jme3.asset.gdx; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * + * @author kobayasi + */ +public class MMSFilterInputStream extends FilterInputStream { + + int size; + + public MMSFilterInputStream(InputStream in, int size) { + super(in); + this.size = size; + } + + @Override + public int available() throws IOException { + return size; + } + + @Override + public void close() throws IOException { + super.close(); + } + + @Override + public synchronized void mark(int i) { + super.mark(i); + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public int read() throws IOException { + if (size == 0) { + return -1; + } + int i = super.read(); + if (i >= 0) { + size--; + } + return i; + } + + @Override + public int read(byte[] bytes) throws IOException { + int i = read(bytes, 0, bytes.length); + return i; + } + + @Override + public int read(byte[] bytes, int i, int i1) throws IOException { + if (size == 0) { + return -1; + } + int readSize = 0; + if (i1 > size) { + i1 = size; + } + while(size > 0 && readSize != i1) { + int i2 = super.read(bytes, i+readSize, i1 - readSize); + if (i2 >= 0) { + size = size - i2; + readSize += i2; + } else { + break; + } + } + return readSize; + } + + @Override + public synchronized void reset() throws IOException { + super.reset(); + } + + @Override + public long skip(long n) throws IOException { +// long totalBytesSkipped = 0L; +// while (totalBytesSkipped < n && size > 0) { +// long bytesSkipped = in.skip(n - totalBytesSkipped); +// size = size - (int) bytesSkipped; +// if (bytesSkipped == 0L) { +// int b = read(); +// if (b < 0) { +// break; // we reached EOF +// } else { +// bytesSkipped = 1; // we read one byte +//// size = size - 1; +// } +// } +// totalBytesSkipped += bytesSkipped; +// } + for(long i = 0;i caps = EnumSet.noneOf(Caps.class); + // current state + private Shader boundShader; + private int initialDrawBuf, initialReadBuf; + private int glslVer; + private int vertexTextureUnits; + private int fragTextureUnits; + private int vertexUniforms; + private int fragUniforms; + private int vertexAttribs; + private int maxFBOSamples; + private int maxFBOAttachs = 1; + private int maxMRTFBOAttachs; + private int maxRBSize; + private int maxTexSize; + private int maxCubeTexSize; + private int maxVertCount; + private int maxTriCount; + private boolean tdc; + private FrameBuffer lastFb = null; + private FrameBuffer mainFbOverride = null; + private final Statistics statistics = new Statistics(); + private int vpX, vpY, vpW, vpH; + private int clipX, clipY, clipW, clipH; + //private final GL10 gl; + private boolean powerOf2 = false; + private boolean verboseLogging = false; + private boolean useVBO = true; + public boolean adreno_finish_bug = false; + + public GdxRenderer() { + } + + public void setUseVA(boolean value) { + logger.log(Level.INFO, "use_VBO [{0}] -> [{1}]", new Object[]{useVBO, !value}); + useVBO = !value; + } + + public void setVerboseLogging(boolean value) { + logger.log(Level.INFO, "verboseLogging [{0}] -> [{1}]", new Object[]{verboseLogging, value}); + verboseLogging = value; + } + + protected void updateNameBuffer() { + int len = stringBuf.length(); + + nameBuf.position(0); + nameBuf.limit(len); + for (int i = 0; i < len; i++) { + nameBuf.put((byte) stringBuf.charAt(i)); + } + + nameBuf.rewind(); + } + + public Statistics getStatistics() { + return statistics; + } + + public EnumSet getCaps() { + return caps; + } + + public void initialize() { + + logger.info("Vendor: " + Gdx.gl20.glGetString(GL20.GL_VENDOR)); + logger.info("Renderer: " + Gdx.gl20.glGetString(GL20.GL_RENDERER)); + logger.info("Version: " + Gdx.gl20.glGetString(GL20.GL_VERSION)); + + String shadingLanguageVersion = Gdx.gl20.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION); + logger.log(Level.INFO, "GLES20.Shading Language Version: {0}", shadingLanguageVersion); + + /* + ContextCapabilities ctxCaps = GLContext.getCapabilities(); + if (ctxCaps.OpenGL20){ + caps.add(Caps.OpenGL20); + } + if (ctxCaps.OpenGL21){ + caps.add(Caps.OpenGL21); + } + if (ctxCaps.OpenGL30){ + caps.add(Caps.OpenGL30); + } + */ + String versionStr = Gdx.gl20.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION); + if (versionStr == null || versionStr.equals("")) { +// glslVer = -1; +// throw new UnsupportedOperationException("GLSL and OpenGL2 is " +// + "required for the OpenGL ES " +// + "renderer!"); + versionStr = ""; + } + logger.info("GLES20.GL_SHADING_LANGUAGE_VERSION = " + versionStr); + + // Fix issue in TestRenderToMemory when GL_FRONT is the main + // buffer being used. + +// initialDrawBuf = GLES20.glGetIntegeri(GLES20.GL_DRAW_BUFFER); +// initialReadBuf = GLES20.glGetIntegeri(GLES20.GL_READ_BUFFER); + + int spaceIdx = versionStr.lastIndexOf(" "); + if (spaceIdx >= 1) { + versionStr = versionStr.substring(spaceIdx, versionStr.length()); + } + + float version = 1; + try { + version = Float.parseFloat(versionStr); + } catch (Exception ex) { + } + glslVer = (int) (version * 100); + + switch (glslVer) { + default: + if (glslVer < 400) { + break; + } + + // so that future OpenGL revisions wont break jme3 + + // fall through intentional + case 400: + case 330: + case 150: + caps.add(Caps.GLSL150); + case 140: + caps.add(Caps.GLSL140); + case 130: + caps.add(Caps.GLSL130); + case 120: + caps.add(Caps.GLSL120); + case 110: + caps.add(Caps.GLSL110); + case 100: + caps.add(Caps.GLSL100); + break; + } + + if (!caps.contains(Caps.GLSL100)) { + logger.info("Force-adding GLSL100 support, since OpenGL is supported."); + caps.add(Caps.GLSL100); + } + + Gdx.gl20.glGetIntegerv(GL20.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, intBuf16); + vertexTextureUnits = intBuf16.get(0); + logger.log(Level.INFO, "VTF Units: {0}", vertexTextureUnits); + if (vertexTextureUnits > 0) { + caps.add(Caps.VertexTextureFetch); + } + + Gdx.gl20.glGetIntegerv(GL20.GL_MAX_TEXTURE_IMAGE_UNITS, intBuf16); + fragTextureUnits = intBuf16.get(0); + logger.log(Level.INFO, "Texture Units: {0}", fragTextureUnits); + /* + GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16); + vertexUniforms = intBuf16.get(0); + logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms); + + GLES20.glGetIntegerv(GLES20.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, intBuf16); + fragUniforms = intBuf16.get(0); + logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms); + */ + + Gdx.gl20.glGetIntegerv(GL20.GL_MAX_VERTEX_ATTRIBS, intBuf16); + vertexAttribs = intBuf16.get(0); + logger.log(Level.INFO, "Vertex Attributes: {0}", vertexAttribs); + + /* + GLES20.glGetIntegerv(GLES20.GL_MAX_VARYING_FLOATS, intBuf16); + int varyingFloats = intBuf16.get(0); + logger.log(Level.FINER, "Varying Floats: {0}", varyingFloats); + */ + + Gdx.gl20.glGetIntegerv(GL20.GL_SUBPIXEL_BITS, intBuf16); + int subpixelBits = intBuf16.get(0); + logger.log(Level.INFO, "Subpixel Bits: {0}", subpixelBits); + /* + GLES20.glGetIntegerv(GLES20.GL_MAX_ELEMENTS_VERTICES, intBuf16); + maxVertCount = intBuf16.get(0); + logger.log(Level.FINER, "Preferred Batch Vertex Count: {0}", maxVertCount); + + GLES20.glGetIntegerv(GLES20.GL_MAX_ELEMENTS_INDICES, intBuf16); + maxTriCount = intBuf16.get(0); + logger.log(Level.FINER, "Preferred Batch Index Count: {0}", maxTriCount); + */ + Gdx.gl20.glGetIntegerv(GL20.GL_MAX_TEXTURE_SIZE, intBuf16); + maxTexSize = intBuf16.get(0); + logger.log(Level.INFO, "Maximum Texture Resolution: {0}" + maxTexSize); + + Gdx.gl20.glGetIntegerv(GL20.GL_MAX_CUBE_MAP_TEXTURE_SIZE, intBuf16); + maxCubeTexSize = intBuf16.get(0); + logger.log(Level.INFO, "Maximum CubeMap Resolution: {0}", maxCubeTexSize); + + + /* + if (ctxCaps.GL_ARB_color_buffer_float){ + // XXX: Require both 16 and 32 bit float support for FloatColorBuffer. + if (ctxCaps.GL_ARB_half_float_pixel){ + caps.add(Caps.FloatColorBuffer); + } + } + + if (ctxCaps.GL_ARB_depth_buffer_float){ + caps.add(Caps.FloatDepthBuffer); + } + + if (ctxCaps.GL_ARB_draw_instanced) + caps.add(Caps.MeshInstancing); + + if (ctxCaps.GL_ARB_fragment_program) + caps.add(Caps.ARBprogram); + + if (ctxCaps.GL_ARB_texture_buffer_object) + caps.add(Caps.TextureBuffer); + + if (ctxCaps.GL_ARB_texture_float){ + if (ctxCaps.GL_ARB_half_float_pixel){ + caps.add(Caps.FloatTexture); + } + } + + if (ctxCaps.GL_ARB_vertex_array_object) + caps.add(Caps.VertexBufferArray); + + boolean latc = ctxCaps.GL_EXT_texture_compression_latc; + boolean atdc = ctxCaps.GL_ATI_texture_compression_3dc; + if (latc || atdc){ + caps.add(Caps.TextureCompressionLATC); + if (atdc && !latc){ + tdc = true; + } + } + + if (ctxCaps.GL_EXT_packed_float){ + caps.add(Caps.PackedFloatColorBuffer); + if (ctxCaps.GL_ARB_half_float_pixel){ + // because textures are usually uploaded as RGB16F + // need half-float pixel + caps.add(Caps.PackedFloatTexture); + } + } + + if (ctxCaps.GL_EXT_texture_array) + caps.add(Caps.TextureArray); + + if (ctxCaps.GL_EXT_texture_shared_exponent) + caps.add(Caps.SharedExponentTexture); + + if (ctxCaps.GL_EXT_framebuffer_object){ + caps.add(Caps.FrameBuffer); + + glGetInteger(GL_MAX_RENDERBUFFER_SIZE_EXT, intBuf16); + maxRBSize = intBuf16.get(0); + logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize); + + glGetInteger(GL_MAX_COLOR_ATTACHMENTS_EXT, intBuf16); + maxFBOAttachs = intBuf16.get(0); + logger.log(Level.FINER, "FBO Max renderbuffers: {0}", maxFBOAttachs); + + if (ctxCaps.GL_EXT_framebuffer_multisample){ + caps.add(Caps.FrameBufferMultisample); + + glGetInteger(GL_MAX_SAMPLES_EXT, intBuf16); + maxFBOSamples = intBuf16.get(0); + logger.log(Level.FINER, "FBO Max Samples: {0}", maxFBOSamples); + } + + if (ctxCaps.GL_ARB_draw_buffers){ + caps.add(Caps.FrameBufferMRT); + glGetInteger(ARBDrawBuffers.GL_MAX_DRAW_BUFFERS_ARB, intBuf16); + maxMRTFBOAttachs = intBuf16.get(0); + logger.log(Level.FINER, "FBO Max MRT renderbuffers: {0}", maxMRTFBOAttachs); + } + } + + if (ctxCaps.GL_ARB_multisample){ + glGetInteger(ARBMultisample.GL_SAMPLE_BUFFERS_ARB, intBuf16); + boolean available = intBuf16.get(0) != 0; + glGetInteger(ARBMultisample.GL_SAMPLES_ARB, intBuf16); + int samples = intBuf16.get(0); + logger.log(Level.FINER, "Samples: {0}", samples); + boolean enabled = glIsEnabled(ARBMultisample.GL_MULTISAMPLE_ARB); + if (samples > 0 && available && !enabled){ + glEnable(ARBMultisample.GL_MULTISAMPLE_ARB); + } + } + */ + Gdx.gl20.glGetIntegerv(GL20.GL_MAX_RENDERBUFFER_SIZE, intBuf16); + maxRBSize = intBuf16.get(0); + logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize); + + String extensions = Gdx.gl20.glGetString(GL20.GL_EXTENSIONS); + logger.log(Level.INFO, "GL_EXTENSIONS: {0}", extensions); + +// GLES20.glGetIntegerv(GLES20.GL_COMPRESSED_TEXTURE_FORMATS, intBuf16); +// for (int i = 0; i < intBuf16.limit(); i++) { +// logger.log(Level.INFO, "Compressed Texture Formats: {0}", intBuf16.get(i)); +// } + + if (extensions.contains("GL_OES_texture_npot")) { + powerOf2 = true; + } + + + + applyRenderState(RenderState.DEFAULT); +// GLES20.glClearDepthf(1.0f); + + if (verboseLogging) { + logger.info("GLES20.glDisable(GL10.GL_DITHER)"); + } + + Gdx.gl20.glDisable(GL20.GL_DITHER); + + checkGLError(); + + if (verboseLogging) { + logger.info("GLES20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST)"); + } + + Gdx.gl20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); + +// checkGLError(); + + useVBO = true; + + // NOTE: SDK_INT is only available since 1.6, + // but for jME3 it doesn't matter since android versions 1.5 and below + // are not supported. + //if (Build.VERSION.SDK_INT >= 9) { + // useVBO = true; + //} + // chekc Adreno200,205,220 bug + if (Gdx.gl20.glGetString(GL20.GL_RENDERER).indexOf("Adreno") >= 0) { + adreno_finish_bug = true; + } + logger.log(Level.INFO, "Caps: {0}", caps); + } + + /** + * resetGLObjects should be called when die GLView gets recreated to reset all GPU objects + */ + public void resetGLObjects() { + objManager.resetObjects(); + statistics.clearMemory(); + boundShader = null; + lastFb = null; + context.reset(); + } + + public void cleanup() { + objManager.deleteAllObjects(this); + statistics.clearMemory(); + } + + private void checkCap(Caps cap) { + if (!caps.contains(cap)) { + throw new UnsupportedOperationException("Required capability missing: " + cap.name()); + } + } + + /*********************************************************************\ + |* Render State *| + \*********************************************************************/ + public void setDepthRange(float start, float end) { + + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glDepthRangef({0}, {1})", new Object[]{start, end}); + } + Gdx.gl20.glDepthRangef(start, end); + checkGLError(); + } + + public void clearBuffers(boolean color, boolean depth, boolean stencil) { + int bits = 0; + if (color) { + bits = GL20.GL_COLOR_BUFFER_BIT; + } + if (depth) { + bits |= GL20.GL_DEPTH_BUFFER_BIT; + if (context.depthWriteEnabled == false) { + Gdx.gl20.glDepthMask(true); + context.depthWriteEnabled = true; + } + } + if (stencil) { + bits |= GL20.GL_STENCIL_BUFFER_BIT; + } + if (bits != 0) { + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glClear(color={0}, depth={1}, stencil={2})", new Object[]{color, depth, stencil}); + } + Gdx.gl20.glClear(bits); + checkGLError(); + } + } + + public void setBackgroundColor(ColorRGBA color) { + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glClearColor({0}, {1}, {2}, {3})", new Object[]{color.r, color.g, color.b, color.a}); + } + Gdx.gl20.glClearColor(color.r, color.g, color.b, color.a); + checkGLError(); + } + + public void applyRenderState(RenderState state) { + /* + if (state.isWireframe() && !context.wireframe){ + GLES20.glPolygonMode(GLES20.GL_FRONT_AND_BACK, GLES20.GL_LINE); + context.wireframe = true; + }else if (!state.isWireframe() && context.wireframe){ + GLES20.glPolygonMode(GLES20.GL_FRONT_AND_BACK, GLES20.GL_FILL); + context.wireframe = false; + } + */ + if (state.isDepthTest() && !context.depthTestEnabled) { + if (verboseLogging) { + logger.info("GLES20.glEnable(GLES20.GL_DEPTH_TEST)"); + } + Gdx.gl20.glEnable(GL20.GL_DEPTH_TEST); + checkGLError(); + if (verboseLogging) { + logger.info("GLES20.glDepthFunc(GL20.LEQUAL)"); + } + Gdx.gl20.glDepthFunc(GL20.GL_LEQUAL); + checkGLError(); + context.depthTestEnabled = true; + } else if (!state.isDepthTest() && context.depthTestEnabled) { + if (verboseLogging) { + logger.info("GLES20.glDisable(GLES20.GL_DEPTH_TEST)"); + } + Gdx.gl20.glDisable(GL20.GL_DEPTH_TEST); + checkGLError(); + context.depthTestEnabled = false; + } + if (state.isAlphaTest() && !context.alphaTestEnabled) { +// GLES20.glEnable(GLES20.GL_ALPHA_TEST); +// GLES20.glAlphaFunc(GLES20.GL_GREATER, state.getAlphaFallOff()); + context.alphaTestEnabled = true; + } else if (!state.isAlphaTest() && context.alphaTestEnabled) { +// GLES20.glDisable(GLES20.GL_ALPHA_TEST); + context.alphaTestEnabled = false; + } + if (state.isDepthWrite() && !context.depthWriteEnabled) { + if (verboseLogging) { + logger.info("GLES20.glDepthMask(true)"); + } + Gdx.gl20.glDepthMask(true); + checkGLError(); + context.depthWriteEnabled = true; + } else if (!state.isDepthWrite() && context.depthWriteEnabled) { + if (verboseLogging) { + logger.info("GLES20.glDepthMask(false)"); + } + Gdx.gl20.glDepthMask(false); + checkGLError(); + context.depthWriteEnabled = false; + } + if (state.isColorWrite() && !context.colorWriteEnabled) { + if (verboseLogging) { + logger.info("GLES20.glColorMask(true, true, true, true)"); + } + Gdx.gl20.glColorMask(true, true, true, true); + checkGLError(); + context.colorWriteEnabled = true; + } else if (!state.isColorWrite() && context.colorWriteEnabled) { + if (verboseLogging) { + logger.info("GLES20.glColorMask(false, false, false, false)"); + } + Gdx.gl20.glColorMask(false, false, false, false); + checkGLError(); + context.colorWriteEnabled = false; + } + if (state.isPointSprite() && !context.pointSprite) { +// GLES20.glEnable(GLES20.GL_POINT_SPRITE); +// GLES20.glTexEnvi(GLES20.GL_POINT_SPRITE, GLES20.GL_COORD_REPLACE, GLES20.GL_TRUE); +// GLES20.glEnable(GLES20.GL_VERTEX_PROGRAM_POINT_SIZE); +// GLES20.glPointParameterf(GLES20.GL_POINT_SIZE_MIN, 1.0f); + } else if (!state.isPointSprite() && context.pointSprite) { +// GLES20.glDisable(GLES20.GL_POINT_SPRITE); + } + + if (state.isPolyOffset()) { + if (!context.polyOffsetEnabled) { + if (verboseLogging) { + logger.info("GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL)"); + } + Gdx.gl20.glEnable(GL20.GL_POLYGON_OFFSET_FILL); + checkGLError(); + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glPolygonOffset({0}, {1})", new Object[]{state.getPolyOffsetFactor(), state.getPolyOffsetUnits()}); + } + Gdx.gl20.glPolygonOffset(state.getPolyOffsetFactor(), + state.getPolyOffsetUnits()); + checkGLError(); + context.polyOffsetEnabled = true; + context.polyOffsetFactor = state.getPolyOffsetFactor(); + context.polyOffsetUnits = state.getPolyOffsetUnits(); + } else { + if (state.getPolyOffsetFactor() != context.polyOffsetFactor + || state.getPolyOffsetUnits() != context.polyOffsetUnits) { + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glPolygonOffset({0}, {1})", new Object[]{state.getPolyOffsetFactor(), state.getPolyOffsetUnits()}); + } + Gdx.gl20.glPolygonOffset(state.getPolyOffsetFactor(), + state.getPolyOffsetUnits()); + checkGLError(); + context.polyOffsetFactor = state.getPolyOffsetFactor(); + context.polyOffsetUnits = state.getPolyOffsetUnits(); + } + } + } else { + if (context.polyOffsetEnabled) { + if (verboseLogging) { + logger.info("GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL)"); + } + Gdx.gl20.glDisable(GL20.GL_POLYGON_OFFSET_FILL); + checkGLError(); + context.polyOffsetEnabled = false; + context.polyOffsetFactor = 0; + context.polyOffsetUnits = 0; + } + } + if (state.getFaceCullMode() != context.cullMode) { + if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) { + if (verboseLogging) { + logger.info("GLES20.glDisable(GLES20.GL_CULL_FACE)"); + } + Gdx.gl20.glDisable(GL20.GL_CULL_FACE); + } else { + if (verboseLogging) { + logger.info("GLES20.glEnable(GLES20.GL_CULL_FACE)"); + } + Gdx.gl20.glEnable(GL20.GL_CULL_FACE); + } + + checkGLError(); + + switch (state.getFaceCullMode()) { + case Off: + break; + case Back: + if (verboseLogging) { + logger.info("GLES20.glCullFace(GLES20.GL_BACK)"); + } + Gdx.gl20.glCullFace(GL20.GL_BACK); + break; + case Front: + if (verboseLogging) { + logger.info("GLES20.glCullFace(GLES20.GL_FRONT)"); + } + Gdx.gl20.glCullFace(GL20.GL_FRONT); + break; + case FrontAndBack: + if (verboseLogging) { + logger.info("GLES20.glCullFace(GLES20.GL_FRONT_AND_BACK)"); + } + Gdx.gl20.glCullFace(GL20.GL_FRONT_AND_BACK); + break; + default: + throw new UnsupportedOperationException("Unrecognized face cull mode: " + + state.getFaceCullMode()); + } + + checkGLError(); + + context.cullMode = state.getFaceCullMode(); + } + + if (state.getBlendMode() != context.blendMode) { + if (state.getBlendMode() == RenderState.BlendMode.Off) { + if (verboseLogging) { + logger.info("GLES20.glDisable(GLES20.GL_BLEND)"); + } + Gdx.gl20.glDisable(GL20.GL_BLEND); + } else { + if (verboseLogging) { + logger.info("GLES20.glEnable(GLES20.GL_BLEND)"); + } + Gdx.gl20.glEnable(GL20.GL_BLEND); + switch (state.getBlendMode()) { + case Off: + break; + case Additive: + if (verboseLogging) { + logger.info("GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE)"); + } + Gdx.gl20.glBlendFunc(GL20.GL_ONE, GL20.GL_ONE); + break; + case AlphaAdditive: + if (verboseLogging) { + logger.info("GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE)"); + } + Gdx.gl20.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE); + break; + case Color: + if (verboseLogging) { + logger.info("GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_COLOR)"); + } + Gdx.gl20.glBlendFunc(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_COLOR); + break; + case Alpha: + if (verboseLogging) { + logger.info("GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA)"); + } + Gdx.gl20.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); + break; + case PremultAlpha: + if (verboseLogging) { + logger.info("GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA)"); + } + Gdx.gl20.glBlendFunc(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA); + break; + case Modulate: + if (verboseLogging) { + logger.info("GLES20.glBlendFunc(GLES20.GL_DST_COLOR, GLES20.GL_ZERO)"); + } + Gdx.gl20.glBlendFunc(GL20.GL_DST_COLOR, GL20.GL_ZERO); + break; + case ModulateX2: + if (verboseLogging) { + logger.info("GLES20.glBlendFunc(GLES20.GL_DST_COLOR, GLES20.GL_SRC_COLOR)"); + } + Gdx.gl20.glBlendFunc(GL20.GL_DST_COLOR, GL20.GL_SRC_COLOR); + break; + default: + throw new UnsupportedOperationException("Unrecognized blend mode: " + + state.getBlendMode()); + } + } + + checkGLError(); + + context.blendMode = state.getBlendMode(); + } + } + + /*********************************************************************\ + |* Camera and World transforms *| + \*********************************************************************/ + public void setViewPort(int x, int y, int w, int h) { + if (x != vpX || vpY != y || vpW != w || vpH != h) { + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glViewport({0}, {1}, {2}, {3})", new Object[]{x, y, w, h}); + } + Gdx.gl20.glViewport(x, y, w, h); + checkGLError(); + vpX = x; + vpY = y; + vpW = w; + vpH = h; + } + } + + public void setClipRect(int x, int y, int width, int height) { + if (!context.clipRectEnabled) { + if (verboseLogging) { + logger.info("GLES20.glEnable(GLES20.GL_SCISSOR_TEST)"); + } + Gdx.gl20.glEnable(GL20.GL_SCISSOR_TEST); + checkGLError(); + context.clipRectEnabled = true; + } + if (clipX != x || clipY != y || clipW != width || clipH != height) { + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glScissor({0}, {1}, {2}, {3})", new Object[]{x, y, width, height}); + } + Gdx.gl20.glScissor(x, y, width, height); + clipX = x; + clipY = y; + clipW = width; + clipH = height; + checkGLError(); + } + } + + public void clearClipRect() { + if (context.clipRectEnabled) { + if (verboseLogging) { + logger.info("GLES20.glDisable(GLES20.GL_SCISSOR_TEST)"); + } + Gdx.gl20.glDisable(GL20.GL_SCISSOR_TEST); + checkGLError(); + context.clipRectEnabled = false; + + clipX = 0; + clipY = 0; + clipW = 0; + clipH = 0; + } + } + + public void onFrame() { + objManager.deleteUnused(this); +// statistics.clearFrame(); + } + + public void setWorldMatrix(Matrix4f worldMatrix) { + } + + public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) { + } + + /*********************************************************************\ + |* Shaders *| + \*********************************************************************/ + protected void updateUniformLocation(Shader shader, Uniform uniform) { + stringBuf.setLength(0); + stringBuf.append(uniform.getName()).append('\0'); + updateNameBuffer(); + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glGetUniformLocation({0}, {1})", new Object[]{shader.getId(), uniform.getName()}); + } + int loc = Gdx.gl20.glGetUniformLocation(shader.getId(), uniform.getName()); + checkGLError(); + if (loc < 0) { + uniform.setLocation(-1); + // uniform is not declared in shader + if (verboseLogging) { + logger.log(Level.WARNING, "Uniform [{0}] is not declared in shader.", uniform.getName()); + } + } else { + uniform.setLocation(loc); + } + } + + protected void updateUniform(Shader shader, Uniform uniform) { + int shaderId = shader.getId(); + + assert uniform.getName() != null; + assert shader.getId() > 0; + + if (context.boundShaderProgram != shaderId) { + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glUseProgram({0})", shaderId); + } + Gdx.gl20.glUseProgram(shaderId); + checkGLError(); + statistics.onShaderUse(shader, true); + boundShader = shader; + context.boundShaderProgram = shaderId; + } else { + statistics.onShaderUse(shader, false); + } + + int loc = uniform.getLocation(); + if (loc == -1) { + if (verboseLogging) { + logger.log(Level.WARNING, "no location for uniform [{0}]", uniform.getName()); + } + return; + } + + if (loc == -2) { + // get uniform location + updateUniformLocation(shader, uniform); + if (uniform.getLocation() == -1) { + // not declared, ignore + + if (verboseLogging) { + logger.log(Level.WARNING, "not declared uniform: [{0}]", uniform.getName()); + } + + uniform.clearUpdateNeeded(); + return; + } + loc = uniform.getLocation(); + } + + if (uniform.getVarType() == null) { + logger.warning("value is not set yet."); + return; // value not set yet.. + } + + statistics.onUniformSet(); + + uniform.clearUpdateNeeded(); + FloatBuffer fb; + switch (uniform.getVarType()) { + case Float: + if (verboseLogging) { + logger.info("GLES20.glUniform1f set Float. " + uniform.getName()); + } + Float f = (Float) uniform.getValue(); + Gdx.gl20.glUniform1f(loc, f.floatValue()); + break; + case Vector2: + if (verboseLogging) { + logger.info("GLES20.glUniform2f set Vector2. " + uniform.getName()); + } + Vector2f v2 = (Vector2f) uniform.getValue(); + Gdx.gl20.glUniform2f(loc, v2.getX(), v2.getY()); + break; + case Vector3: + if (verboseLogging) { + logger.info("GLES20.glUniform3f set Vector3. " + uniform.getName()); + } + Vector3f v3 = (Vector3f) uniform.getValue(); + Gdx.gl20.glUniform3f(loc, v3.getX(), v3.getY(), v3.getZ()); + break; + case Vector4: + if (verboseLogging) { + logger.info("GLES20.glUniform4f set Vector4." + uniform.getName()); + } + Object val = uniform.getValue(); + if (val instanceof ColorRGBA) { + ColorRGBA c = (ColorRGBA) val; + Gdx.gl20.glUniform4f(loc, c.r, c.g, c.b, c.a); + } else if (val instanceof Vector4f) { + Vector4f c = (Vector4f) val; + Gdx.gl20.glUniform4f(loc, c.x, c.y, c.z, c.w); + } else { + Quaternion c = (Quaternion) uniform.getValue(); + Gdx.gl20.glUniform4f(loc, c.getX(), c.getY(), c.getZ(), c.getW()); + } + break; + case Boolean: + if (verboseLogging) { + logger.info("GLES20.glUniform1i set Boolean." + uniform.getName()); + } + Boolean b = (Boolean) uniform.getValue(); + Gdx.gl20.glUniform1i(loc, b.booleanValue() ? GL20.GL_TRUE : GL20.GL_FALSE); + break; + case Matrix3: + if (verboseLogging) { + logger.info("GLES20.glUniformMatrix3fv set Matrix3." + uniform.getName()); + } + fb = (FloatBuffer) uniform.getValue(); + assert fb.remaining() == 9; + Gdx.gl20.glUniformMatrix3fv(loc, 1, false, fb); + break; + case Matrix4: + if (verboseLogging) { + logger.info("GLES20.glUniformMatrix4fv set Matrix4." + uniform.getName()); + } + fb = (FloatBuffer) uniform.getValue(); + assert fb.remaining() == 16; + Gdx.gl20.glUniformMatrix4fv(loc, 1, false, fb); + break; + case FloatArray: + if (verboseLogging) { + logger.info("GLES20.glUniform1fv set FloatArray." + uniform.getName()); + } + fb = (FloatBuffer) uniform.getValue(); + Gdx.gl20.glUniform1fv(loc, fb.capacity(), fb); + break; + case Vector2Array: + if (verboseLogging) { + logger.info("GLES20.glUniform2fv set Vector2Array." + uniform.getName()); + } + fb = (FloatBuffer) uniform.getValue(); + Gdx.gl20.glUniform2fv(loc, fb.capacity() / 2, fb); + break; + case Vector3Array: + if (verboseLogging) { + logger.info("GLES20.glUniform3fv set Vector3Array." + uniform.getName()); + } + fb = (FloatBuffer) uniform.getValue(); + Gdx.gl20.glUniform3fv(loc, fb.capacity() / 3, fb); + break; + case Vector4Array: + if (verboseLogging) { + logger.info("GLES20.glUniform4fv set Vector4Array." + uniform.getName()); + } + fb = (FloatBuffer) uniform.getValue(); + Gdx.gl20.glUniform4fv(loc, fb.capacity() / 4, fb); + break; + case Matrix4Array: + if (verboseLogging) { + logger.info("GLES20.glUniform4fv set Matrix4Array." + uniform.getName()); + } + fb = (FloatBuffer) uniform.getValue(); + Gdx.gl20.glUniformMatrix4fv(loc, fb.capacity() / 16, false, fb); + break; + case Int: + if (verboseLogging) { + logger.info("GLES20.glUniform1i set Int." + uniform.getName()); + } + Integer i = (Integer) uniform.getValue(); + Gdx.gl20.glUniform1i(loc, i.intValue()); + break; + default: + throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType()); + } + checkGLError(); + } + + protected void updateShaderUniforms(Shader shader) { + ListMap uniforms = shader.getUniformMap(); +// for (Uniform uniform : shader.getUniforms()){ + for (int i = 0; i < uniforms.size(); i++) { + Uniform uniform = uniforms.getValue(i); + if (uniform.isUpdateNeeded()) { + updateUniform(shader, uniform); + } + } + } + + protected void resetUniformLocations(Shader shader) { + ListMap uniforms = shader.getUniformMap(); +// for (Uniform uniform : shader.getUniforms()){ + for (int i = 0; i < uniforms.size(); i++) { + Uniform uniform = uniforms.getValue(i); + uniform.reset(); // e.g check location again + } + } + + /* + * (Non-javadoc) + * Only used for fixed-function. Ignored. + */ + public void setLighting(LightList list) { + } + + public int convertShaderType(ShaderType type) { + switch (type) { + case Fragment: + return GL20.GL_FRAGMENT_SHADER; + case Vertex: + return GL20.GL_VERTEX_SHADER; +// case Geometry: +// return ARBGeometryShader4.GL_GEOMETRY_SHADER_ARB; + default: + throw new RuntimeException("Unrecognized shader type."); + } + } + + public void updateShaderSourceData(ShaderSource source, String language) { + int id = source.getId(); + if (id == -1) { + // create id + if (verboseLogging) { + logger.info("GLES20.glCreateShader(" + source.getType() + ")"); + } + id = Gdx.gl20.glCreateShader(convertShaderType(source.getType())); + checkGLError(); + if (id <= 0) { + throw new RendererException("Invalid ID received when trying to create shader."); + } + + source.setId(id); + } + + // upload shader source + // merge the defines and source code + byte[] versionData = new byte[]{};//"#version 140\n".getBytes(); +// versionData = "#define INSTANCING 1\n".getBytes(); + byte[] definesCodeData = source.getDefines().getBytes(); + byte[] sourceCodeData = source.getSource().getBytes(); + ByteBuffer codeBuf = BufferUtils.createByteBuffer(versionData.length + + definesCodeData.length + + sourceCodeData.length); + codeBuf.put(versionData); + codeBuf.put(definesCodeData); + codeBuf.put(sourceCodeData); + codeBuf.flip(); + + if (verboseLogging) { + logger.info("GLES20.glShaderSource(" + id + ")"); + } + if (source.getType().equals(ShaderType.Vertex) + && Gdx.gl20.glGetString(GL20.GL_RENDERER).indexOf("PowerVR") >= 0) { + Gdx.gl20.glShaderSource( + id, + source.getDefines() + + source.getSource()); + } else { + if (!Gdx.app.getType().equals(com.badlogic.gdx.Application.ApplicationType.Desktop)) { + Gdx.gl20.glShaderSource( + id, + "precision mediump float;\n" + + source.getDefines() + + source.getSource()); + } else { + Gdx.gl20.glShaderSource( + id, + source.getDefines() + + source.getSource()); + } + } + checkGLError(); + + if (verboseLogging) { + logger.info("GLES20.glCompileShader(" + id + ")"); + } + + Gdx.gl20.glCompileShader(id); + + checkGLError(); + + if (verboseLogging) { + logger.info("GLES20.glGetShaderiv(" + id + ", GLES20.GL_COMPILE_STATUS)"); + } + + Gdx.gl20.glGetShaderiv(id, GL20.GL_COMPILE_STATUS, intBuf1); + + checkGLError(); + + boolean compiledOK = intBuf1.get(0) == GL20.GL_TRUE; + String infoLog = null; + + if (VALIDATE_SHADER || !compiledOK) { + // even if compile succeeded, check + // log for warnings + if (verboseLogging) { + logger.info("GLES20.glGetShaderiv()"); + } + Gdx.gl20.glGetShaderiv(id, GL20.GL_INFO_LOG_LENGTH, intBuf1); + checkGLError(); + if (verboseLogging) { + logger.info("GLES20.glGetShaderInfoLog(" + id + ")"); + } + infoLog = Gdx.gl20.glGetShaderInfoLog(id); + logger.severe("Errooooooooooot(" + id + ")"); + } + + if (compiledOK) { + if (infoLog != null) { + logger.log(Level.INFO, "compile success: " + source.getName() + ", " + infoLog); + } else { + logger.log(Level.FINE, "compile success: " + source.getName()); + } + } else { + logger.log(Level.WARNING, "Bad compile of:\n{0}{1}", + new Object[]{source.getDefines(), source.getSource()}); + if (infoLog != null) { + throw new RendererException("compile error in:" + source + " error:" + infoLog); + } else { + throw new RendererException("compile error in:" + source + " error: "); + } + } + + source.clearUpdateNeeded(); + // only usable if compiled + source.setUsable(compiledOK); + if (!compiledOK) { + // make sure to dispose id cause all program's + // shaders will be cleared later. + if (verboseLogging) { + logger.info("GLES20.glDeleteShader(" + id + ")"); + } + Gdx.gl20.glDeleteShader(id); + checkGLError(); + } else { + // register for cleanup since the ID is usable + objManager.registerForCleanup(source); + } + } + + public void updateShaderData(Shader shader) { + int id = shader.getId(); + boolean needRegister = false; + if (id == -1) { + // create program + + if (verboseLogging) { + logger.info("GLES20.glCreateProgram()"); + } + + id = Gdx.gl20.glCreateProgram(); + + if (id <= 0) { + throw new RendererException("Invalid ID received when trying to create shader program."); + } + + shader.setId(id); + needRegister = true; + } + + for (ShaderSource source : shader.getSources()) { + if (source.isUpdateNeeded()) { + updateShaderSourceData(source, shader.getLanguage()); + // shader has been compiled here + } + + if (!source.isUsable()) { + // it's useless.. just forget about everything.. + shader.setUsable(false); + shader.clearUpdateNeeded(); + return; + } + if (verboseLogging) { + logger.info("GLES20.glAttachShader(" + id + ", " + source.getId() + ")"); + } + + Gdx.gl20.glAttachShader(id, source.getId()); + } + + // link shaders to program + if (verboseLogging) { + logger.info("GLES20.glLinkProgram(" + id + ")"); + } + + Gdx.gl20.glLinkProgram(id); + + + if (verboseLogging) { + logger.info("GLES20.glGetProgramiv(" + id + ")"); + } + + Gdx.gl20.glGetProgramiv(id, GL20.GL_LINK_STATUS, intBuf1); + + boolean linkOK = intBuf1.get(0) == GL20.GL_TRUE; + String infoLog = null; + + if (VALIDATE_SHADER || !linkOK) { + if (verboseLogging) { + logger.info("GLES20.glGetProgramiv(" + id + ", GLES20.GL_INFO_LOG_LENGTH, buffer)"); + } + + Gdx.gl20.glGetProgramiv(id, GL20.GL_INFO_LOG_LENGTH, intBuf1); + + int length = intBuf1.get(0); + if (length > 3) { + // get infos + + if (verboseLogging) { + logger.info("GLES20.glGetProgramInfoLog(" + id + ")"); + } + + infoLog = Gdx.gl20.glGetProgramInfoLog(id); + } + } + + if (linkOK) { + if (infoLog != null) { + logger.log(Level.INFO, "shader link success. \n{0}", infoLog); + } else { + logger.fine("shader link success"); + } + } else { + if (infoLog != null) { + throw new RendererException("Shader link failure, shader:" + shader + " info:" + infoLog); + } else { + throw new RendererException("Shader link failure, shader:" + shader + " info: "); + } + } + + shader.clearUpdateNeeded(); + if (!linkOK) { + // failure.. forget about everything + shader.resetSources(); + shader.setUsable(false); + deleteShader(shader); + } else { + shader.setUsable(true); + if (needRegister) { + objManager.registerForCleanup(shader); + statistics.onNewShader(); + } else { + // OpenGL spec: uniform locations may change after re-link + resetUniformLocations(shader); + } + } + } + + public void setShader(Shader shader) { + if (verboseLogging) { + logger.info("setShader(" + shader + ")"); + } + + if (shader == null) { + if (context.boundShaderProgram > 0) { + + if (verboseLogging) { + logger.info("GLES20.glUseProgram(0)"); + } + + Gdx.gl20.glUseProgram(0); + + statistics.onShaderUse(null, true); + context.boundShaderProgram = 0; + boundShader = null; + } + } else { + if (shader.isUpdateNeeded()) { + updateShaderData(shader); + } + + // NOTE: might want to check if any of the + // sources need an update? + + if (!shader.isUsable()) { + logger.warning("shader is not usable."); + return; + } + + assert shader.getId() > 0; + + updateShaderUniforms(shader); + if (context.boundShaderProgram != shader.getId()) { + if (VALIDATE_SHADER) { + // check if shader can be used + // with current state + if (verboseLogging) { + logger.info("GLES20.glValidateProgram(" + shader.getId() + ")"); + } + + Gdx.gl20.glValidateProgram(shader.getId()); + + if (verboseLogging) { + logger.info("GLES20.glGetProgramiv(" + shader.getId() + ", GLES20.GL_VALIDATE_STATUS, buffer)"); + } + + Gdx.gl20.glGetProgramiv(shader.getId(), GL20.GL_VALIDATE_STATUS, intBuf1); + + boolean validateOK = intBuf1.get(0) == GL20.GL_TRUE; + + if (validateOK) { + logger.fine("shader validate success"); + } else { + logger.warning("shader validate failure"); + } + } + + if (verboseLogging) { + logger.info("GLES20.glUseProgram(" + shader.getId() + ")"); + } + + Gdx.gl20.glUseProgram(shader.getId()); + + statistics.onShaderUse(shader, true); + context.boundShaderProgram = shader.getId(); + boundShader = shader; + } else { + statistics.onShaderUse(shader, false); + } + } + } + + public void setShaderWithoutUpdateUniforms(Shader shader) { + if (verboseLogging) { + logger.info("setShader(" + shader + ")"); + } + + if (shader == null) { + if (context.boundShaderProgram > 0) { + + if (verboseLogging) { + logger.info("GLES20.glUseProgram(0)"); + } + + Gdx.gl20.glUseProgram(0); + + statistics.onShaderUse(null, true); + context.boundShaderProgram = 0; + boundShader = null; + } + } else { + if (shader.isUpdateNeeded()) { + updateShaderData(shader); + } + + // NOTE: might want to check if any of the + // sources need an update? + + if (!shader.isUsable()) { + logger.warning("shader is not usable."); + return; + } + + assert shader.getId() > 0; + + if (context.boundShaderProgram != shader.getId()) { + if (VALIDATE_SHADER) { + // check if shader can be used + // with current state + if (verboseLogging) { + logger.info("GLES20.glValidateProgram(" + shader.getId() + ")"); + } + + Gdx.gl20.glValidateProgram(shader.getId()); + + if (verboseLogging) { + logger.info("GLES20.glGetProgramiv(" + shader.getId() + ", GLES20.GL_VALIDATE_STATUS, buffer)"); + } + + Gdx.gl20.glGetProgramiv(shader.getId(), GL20.GL_VALIDATE_STATUS, intBuf1); + + boolean validateOK = intBuf1.get(0) == GL20.GL_TRUE; + + if (validateOK) { + logger.fine("shader validate success"); + } else { + logger.warning("shader validate failure"); + } + } + + if (verboseLogging) { + logger.info("GLES20.glUseProgram(" + shader.getId() + ")"); + } + + Gdx.gl20.glUseProgram(shader.getId()); + + statistics.onShaderUse(shader, true); + context.boundShaderProgram = shader.getId(); + boundShader = shader; + } else { + statistics.onShaderUse(shader, false); + } + } + } + + public void deleteShaderSource(ShaderSource source) { + if (source.getId() < 0) { + logger.warning("Shader source is not uploaded to GPU, cannot delete."); + return; + } + source.setUsable(false); + source.clearUpdateNeeded(); + + if (verboseLogging) { + logger.info("GLES20.glDeleteShader(" + source.getId() + ")"); + } + + Gdx.gl20.glDeleteShader(source.getId()); + source.resetObject(); + } + + public void deleteShader(Shader shader) { + if (shader.getId() == -1) { + logger.warning("Shader is not uploaded to GPU, cannot delete."); + return; + } + for (ShaderSource source : shader.getSources()) { + if (source.getId() != -1) { + + if (verboseLogging) { + logger.info("GLES20.glDetachShader(" + shader.getId() + ", " + source.getId() + ")"); + } + + Gdx.gl20.glDetachShader(shader.getId(), source.getId()); + // the next part is done by the GLObjectManager automatically +// glDeleteShader(source.getId()); + } + } + // kill all references so sources can be collected + // if needed. + shader.resetSources(); + + if (verboseLogging) { + logger.info("GLES20.glDeleteProgram(" + shader.getId() + ")"); + } + + Gdx.gl20.glDeleteProgram(shader.getId()); + + statistics.onDeleteShader(); + } + + /*********************************************************************\ + |* Framebuffers *| + \*********************************************************************/ + public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) { + copyFrameBuffer(src, dst, true); + } + + public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) { + logger.warning("copyFrameBuffer is not supported."); + } + private void checkFrameBufferStatus(FrameBuffer fb) { + try { + checkFrameBufferError(); + } catch (IllegalStateException ex) { + logger.log(Level.SEVERE, "=== jMonkeyEngine FBO State ===\n{0}", fb); + printRealFrameBufferInfo(fb); + throw ex; + } + } + + private void checkFrameBufferError() { + int status = Gdx.gl20.glCheckFramebufferStatus(GL20.GL_FRAMEBUFFER); + switch (status) { + case GL20.GL_FRAMEBUFFER_COMPLETE: + break; + case GL20.GL_FRAMEBUFFER_UNSUPPORTED: + //Choose different formats + throw new IllegalStateException("Framebuffer object format is " + + "unsupported by the video hardware."); + case GL20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + throw new IllegalStateException("Framebuffer has erronous attachment."); + case GL20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + throw new IllegalStateException("Framebuffer doesn't have any renderbuffers attached."); + case GL20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + throw new IllegalStateException("Framebuffer attachments must have same dimensions."); +// case GLES20.GL_FRAMEBUFFER_INCOMPLETE_FORMATS: +// throw new IllegalStateException("Framebuffer attachments must have same formats."); +// case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: +// throw new IllegalStateException("Incomplete draw buffer."); +// case GLES20.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: +// throw new IllegalStateException("Incomplete read buffer."); +// case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: +// throw new IllegalStateException("Incomplete multisample buffer."); + default: + //Programming error; will fail on all hardware + throw new IllegalStateException("Some video driver error " + + "or programming error occured. " + + "Framebuffer object status is invalid: " + status); + } + } + private void printRealRenderBufferInfo(FrameBuffer fb, RenderBuffer rb, String name) { + System.out.println("== Renderbuffer " + name + " =="); + System.out.println("RB ID: " + rb.getId()); + System.out.println("Is proper? " + Gdx.gl20.glIsRenderbuffer(rb.getId())); + + int attachment = convertAttachmentSlot(rb.getSlot()); + + intBuf16.clear(); + Gdx.gl20.glGetFramebufferAttachmentParameteriv(GL20.GL_FRAMEBUFFER, + attachment, GL20.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, intBuf16); + int type = intBuf16.get(0); + + intBuf16.clear(); + Gdx.gl20.glGetFramebufferAttachmentParameteriv(GL20.GL_FRAMEBUFFER, + attachment, GL20.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, intBuf16); + int rbName = intBuf16.get(0); + + switch (type) { + case GL20.GL_NONE: + System.out.println("Type: None"); + break; + case GL20.GL_TEXTURE: + System.out.println("Type: Texture"); + break; + case GL20.GL_RENDERBUFFER: + System.out.println("Type: Buffer"); + System.out.println("RB ID: " + rbName); + break; + } + + + + } + + private void printRealFrameBufferInfo(FrameBuffer fb) { +// boolean doubleBuffer = GLES20.glGetBooleanv(GLES20.GL_DOUBLEBUFFER); + boolean doubleBuffer = false; // FIXME +// String drawBuf = getTargetBufferName(glGetInteger(GL_DRAW_BUFFER)); +// String readBuf = getTargetBufferName(glGetInteger(GL_READ_BUFFER)); + + int fbId = fb.getId(); + intBuf16.clear(); +// int curDrawBinding = GLES20.glGetIntegerv(GLES20.GL_DRAW_FRAMEBUFFER_BINDING); +// int curReadBinding = glGetInteger(ARBFramebufferObject.GL_READ_FRAMEBUFFER_BINDING); + + System.out.println("=== OpenGL FBO State ==="); + System.out.println("Context doublebuffered? " + doubleBuffer); + System.out.println("FBO ID: " + fbId); + System.out.println("Is proper? " + Gdx.gl20.glIsFramebuffer(fbId)); +// System.out.println("Is bound to draw? " + (fbId == curDrawBinding)); +// System.out.println("Is bound to read? " + (fbId == curReadBinding)); +// System.out.println("Draw buffer: " + drawBuf); +// System.out.println("Read buffer: " + readBuf); + + if (context.boundFBO != fbId) { + Gdx.gl20.glBindFramebuffer(GL20.GL_FRAMEBUFFER, fbId); + context.boundFBO = fbId; + } + + if (fb.getDepthBuffer() != null) { + printRealRenderBufferInfo(fb, fb.getDepthBuffer(), "Depth"); + } + for (int i = 0; i < fb.getNumColorBuffers(); i++) { + printRealRenderBufferInfo(fb, fb.getColorBuffer(i), "Color" + i); + } + } + + private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb) { + int id = rb.getId(); + if (id == -1) { + Gdx.gl20.glGenRenderbuffers(1, intBuf1); +// RendererUtil.checkGLError(); + + id = intBuf1.get(0); + rb.setId(id); + } + + if (context.boundRB != id) { + Gdx.gl20.glBindRenderbuffer(GL20.GL_RENDERBUFFER, id); +// RendererUtil.checkGLError(); + + context.boundRB = id; + } + + if (fb.getWidth() > maxRBSize || fb.getHeight() > maxRBSize) { + throw new RendererException("Resolution " + fb.getWidth() + + ":" + fb.getHeight() + " is not supported."); + } + + TextureUtilGdx.AndroidGLImageFormat imageFormat = TextureUtilGdx.getImageFormat(rb.getFormat()); + if (imageFormat.renderBufferStorageFormat == 0) { + throw new RendererException("The format '" + rb.getFormat() + "' cannot be used for renderbuffers."); + } + +// if (fb.getSamples() > 1 && GLContext.getCapabilities().GL_EXT_framebuffer_multisample) { + if (fb.getSamples() > 1) { +// // FIXME + throw new RendererException("Multisample FrameBuffer is not supported yet."); +// int samples = fb.getSamples(); +// if (maxFBOSamples < samples) { +// samples = maxFBOSamples; +// } +// glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, +// samples, +// glFmt.internalFormat, +// fb.getWidth(), +// fb.getHeight()); + } else { + Gdx.gl20.glRenderbufferStorage(GL20.GL_RENDERBUFFER, + imageFormat.renderBufferStorageFormat, + fb.getWidth(), + fb.getHeight()); + +// RendererUtil.checkGLError(); + } + } + private int convertAttachmentSlot(int attachmentSlot) { + // can also add support for stencil here + if (attachmentSlot == -100) { + return GL20.GL_DEPTH_ATTACHMENT; + } else if (attachmentSlot == 0) { + return GL20.GL_COLOR_ATTACHMENT0; + } else { + throw new UnsupportedOperationException("Android does not support multiple color attachments to an FBO"); + } + } + + public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) { + Texture tex = rb.getTexture(); + Image image = tex.getImage(); + if (image.isUpdateNeeded()) { + updateTexImageData(image, tex.getType(), false); + + // NOTE: For depth textures, sets nearest/no-mips mode + // Required to fix "framebuffer unsupported" + // for old NVIDIA drivers! + setupTextureParams(tex); + } + + Gdx.gl20.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, + convertAttachmentSlot(rb.getSlot()), + convertTextureType(tex.getType()), + image.getId(), + 0); + +// RendererUtil.checkGLError(); + } + + public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) { + boolean needAttach; + if (rb.getTexture() == null) { + // if it hasn't been created yet, then attach is required. + needAttach = rb.getId() == -1; + updateRenderBuffer(fb, rb); + } else { + needAttach = false; + updateRenderTexture(fb, rb); + } + if (needAttach) { + Gdx.gl20.glFramebufferRenderbuffer(GL20.GL_FRAMEBUFFER, + convertAttachmentSlot(rb.getSlot()), + GL20.GL_RENDERBUFFER, + rb.getId()); + +// RendererUtil.checkGLError(); + } + } + + public void updateFrameBuffer(FrameBuffer fb) { + int id = fb.getId(); + if (id == -1) { + intBuf1.clear(); + // create FBO + Gdx.gl20.glGenFramebuffers(1, intBuf1); +// RendererUtil.checkGLError(); + + id = intBuf1.get(0); + fb.setId(id); + objManager.registerForCleanup(fb); + + statistics.onNewFrameBuffer(); + } + + if (context.boundFBO != id) { + Gdx.gl20.glBindFramebuffer(GL20.GL_FRAMEBUFFER, id); +// RendererUtil.checkGLError(); + + // binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0 + context.boundDrawBuf = 0; + context.boundFBO = id; + } + + RenderBuffer depthBuf = fb.getDepthBuffer(); + if (depthBuf != null) { + updateFrameBufferAttachment(fb, depthBuf); + } + + for (int i = 0; i < fb.getNumColorBuffers(); i++) { + RenderBuffer colorBuf = fb.getColorBuffer(i); + updateFrameBufferAttachment(fb, colorBuf); + } + + fb.clearUpdateNeeded(); + } + + public void setMainFrameBufferOverride(FrameBuffer fb){ + mainFbOverride = fb; + } + + public void setFrameBuffer(FrameBuffer fb) { + if (fb == null && mainFbOverride != null) { + fb = mainFbOverride; + } + + if (lastFb == fb) { + if (fb == null || !fb.isUpdateNeeded()) { + return; + } + } + + // generate mipmaps for last FB if needed + if (lastFb != null) { + for (int i = 0; i < lastFb.getNumColorBuffers(); i++) { + RenderBuffer rb = lastFb.getColorBuffer(i); + Texture tex = rb.getTexture(); + if (tex != null + && tex.getMinFilter().usesMipMapLevels()) { + setTexture(0, rb.getTexture()); + +// int textureType = convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), rb.getFace()); + int textureType = convertTextureType(tex.getType()); + Gdx.gl20.glGenerateMipmap(textureType); +// RendererUtil.checkGLError(); + } + } + } + + if (fb == null) { + // unbind any fbos + if (context.boundFBO != 0) { + Gdx.gl20.glBindFramebuffer(GL20.GL_FRAMEBUFFER, 0); +// RendererUtil.checkGLError(); + + statistics.onFrameBufferUse(null, true); + + context.boundFBO = 0; + } + + /* + // select back buffer + if (context.boundDrawBuf != -1) { + glDrawBuffer(initialDrawBuf); + context.boundDrawBuf = -1; + } + if (context.boundReadBuf != -1) { + glReadBuffer(initialReadBuf); + context.boundReadBuf = -1; + } + */ + + lastFb = null; + } else { + if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null) { + throw new IllegalArgumentException("The framebuffer: " + fb + + "\nDoesn't have any color/depth buffers"); + } + + if (fb.isUpdateNeeded()) { + updateFrameBuffer(fb); + } + + if (context.boundFBO != fb.getId()) { + Gdx.gl20.glBindFramebuffer(GL20.GL_FRAMEBUFFER, fb.getId()); +// RendererUtil.checkGLError(); + + statistics.onFrameBufferUse(fb, true); + + // update viewport to reflect framebuffer's resolution + setViewPort(0, 0, fb.getWidth(), fb.getHeight()); + + context.boundFBO = fb.getId(); + } else { + statistics.onFrameBufferUse(fb, false); + } + if (fb.getNumColorBuffers() == 0) { +// // make sure to select NONE as draw buf +// // no color buffer attached. select NONE + if (context.boundDrawBuf != -2) { +// glDrawBuffer(GL_NONE); + context.boundDrawBuf = -2; + } + if (context.boundReadBuf != -2) { +// glReadBuffer(GL_NONE); + context.boundReadBuf = -2; + } + } else { + if (fb.getNumColorBuffers() > maxFBOAttachs) { + throw new RendererException("Framebuffer has more color " + + "attachments than are supported" + + " by the video hardware!"); + } + if (fb.isMultiTarget()) { + if (fb.getNumColorBuffers() > maxMRTFBOAttachs) { + throw new RendererException("Framebuffer has more" + + " multi targets than are supported" + + " by the video hardware!"); + } + + if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) { + intBuf16.clear(); + for (int i = 0; i < fb.getNumColorBuffers(); i++) { + intBuf16.put(Gdx.gl20.GL_COLOR_ATTACHMENT0 + i); + } + + intBuf16.flip(); +// glDrawBuffers(intBuf16); + context.boundDrawBuf = 100 + fb.getNumColorBuffers(); + } + } else { + RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex()); + // select this draw buffer + if (context.boundDrawBuf != rb.getSlot()) { + Gdx.gl20.glActiveTexture(convertAttachmentSlot(rb.getSlot())); +// RendererUtil.checkGLError(); + + context.boundDrawBuf = rb.getSlot(); + } + } + } + + assert fb.getId() >= 0; + assert context.boundFBO == fb.getId(); + + lastFb = fb; + + checkFrameBufferStatus(fb); + } + } + + /** + * Reads the Color Buffer from OpenGL and stores into the ByteBuffer. + * Make sure to call setViewPort with the appropriate viewport size before + * calling readFrameBuffer. + * @param fb FrameBuffer + * @param byteBuf ByteBuffer to store the Color Buffer from OpenGL + */ + public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) { + if (fb != null) { + RenderBuffer rb = fb.getColorBuffer(); + if (rb == null) { + throw new IllegalArgumentException("Specified framebuffer" + + " does not have a colorbuffer"); + } + + setFrameBuffer(fb); + } else { + setFrameBuffer(null); + } + + Gdx.gl20.glReadPixels(vpX, vpY, vpW, vpH, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, byteBuf); +// RendererUtil.checkGLError(); + } + + private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) { + intBuf1.put(0, rb.getId()); + Gdx.gl20.glDeleteRenderbuffers(1, intBuf1); +// RendererUtil.checkGLError(); + } + + public void deleteFrameBuffer(FrameBuffer fb) { + if (fb.getId() != -1) { + if (context.boundFBO == fb.getId()) { + Gdx.gl20.glBindFramebuffer(GL20.GL_FRAMEBUFFER, 0); +// RendererUtil.checkGLError(); + + context.boundFBO = 0; + } + + if (fb.getDepthBuffer() != null) { + deleteRenderBuffer(fb, fb.getDepthBuffer()); + } + if (fb.getColorBuffer() != null) { + deleteRenderBuffer(fb, fb.getColorBuffer()); + } + + intBuf1.put(0, fb.getId()); + Gdx.gl20.glDeleteFramebuffers(1, intBuf1); +// RendererUtil.checkGLError(); + + fb.resetObject(); + + statistics.onDeleteFrameBuffer(); + } + } + + /*********************************************************************\ + |* Textures *| + \*********************************************************************/ + private int convertTextureType(Texture.Type type) { + switch (type) { + case TwoDimensional: + return GL20.GL_TEXTURE_2D; + // case TwoDimensionalArray: + // return EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT; +// case ThreeDimensional: + // return GLES20.GL_TEXTURE_3D; + case CubeMap: + return GL20.GL_TEXTURE_CUBE_MAP; + default: + throw new UnsupportedOperationException("Unknown texture type: " + type); + } + } + + private int convertMagFilter(Texture.MagFilter filter) { + switch (filter) { + case Bilinear: + return GL20.GL_LINEAR; + case Nearest: + return GL20.GL_NEAREST; + default: + throw new UnsupportedOperationException("Unknown mag filter: " + filter); + } + } + + private int convertMinFilter(Texture.MinFilter filter) { + switch (filter) { + case Trilinear: + return GL20.GL_LINEAR_MIPMAP_LINEAR; + case BilinearNearestMipMap: + return GL20.GL_LINEAR_MIPMAP_NEAREST; + case NearestLinearMipMap: + return GL20.GL_NEAREST_MIPMAP_LINEAR; + case NearestNearestMipMap: + return GL20.GL_NEAREST_MIPMAP_NEAREST; + case BilinearNoMipMaps: + return GL20.GL_LINEAR; + case NearestNoMipMaps: + return GL20.GL_NEAREST; + default: + throw new UnsupportedOperationException("Unknown min filter: " + filter); + } + } + + private int convertWrapMode(Texture.WrapMode mode) { + switch (mode) { +// case BorderClamp: +// return GLES20.GL_CLAMP_TO_BORDER; +// case Clamp: +// return GLES20.GL_CLAMP; + case EdgeClamp: + return GL20.GL_CLAMP_TO_EDGE; + case Repeat: + return GL20.GL_REPEAT; + case MirroredRepeat: + return GL20.GL_MIRRORED_REPEAT; + default: + throw new UnsupportedOperationException("Unknown wrap mode: " + mode); + } + } + + /** + * setupTextureParams sets the OpenGL context texture parameters + * @param tex the Texture to set the texture parameters from + */ + private void setupTextureParams(Texture tex) { + int target = convertTextureType(tex.getType()); + + // filter things + int minFilter = convertMinFilter(tex.getMinFilter()); + int magFilter = convertMagFilter(tex.getMagFilter()); + + if (verboseLogging) { + logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_MIN_FILTER, " + minFilter + ")"); + } + + Gdx.gl20.glTexParameteri(target, GL20.GL_TEXTURE_MIN_FILTER, minFilter); + + if (verboseLogging) { + logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_MAG_FILTER, " + magFilter + ")"); + } + + Gdx.gl20.glTexParameteri(target, GL20.GL_TEXTURE_MAG_FILTER, magFilter); + + /* + if (tex.getAnisotropicFilter() > 1){ + + if (GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic){ + glTexParameterf(target, + EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, + tex.getAnisotropicFilter()); + } + + } + */ + // repeat modes + + switch (tex.getType()) { + case ThreeDimensional: + case CubeMap: // cubemaps use 3D coords + // GL_TEXTURE_WRAP_R is not available in api 8 + //GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R))); + case TwoDimensional: + case TwoDimensionalArray: + + if (verboseLogging) { + logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_WRAP_T, " + convertWrapMode(tex.getWrap(WrapAxis.T))); + } + + Gdx.gl20.glTexParameteri(target, GL20.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T))); + + // fall down here is intentional.. +// case OneDimensional: + + if (verboseLogging) { + logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_WRAP_S, " + convertWrapMode(tex.getWrap(WrapAxis.S))); + } + + Gdx.gl20.glTexParameteri(target, GL20.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S))); + break; + default: + throw new UnsupportedOperationException("Unknown texture type: " + tex.getType()); + } + + // R to Texture compare mode +/* + if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off){ + GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_MODE, GLES20.GL_COMPARE_R_TO_TEXTURE); + GLES20.glTexParameteri(target, GLES20.GL_DEPTH_TEXTURE_MODE, GLES20.GL_INTENSITY); + if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual){ + GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_FUNC, GLES20.GL_GEQUAL); + }else{ + GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_FUNC, GLES20.GL_LEQUAL); + } + } + */ + } + + /** + * updateTexImageData activates and binds the texture + * @param img + * @param type + * @param mips + */ + public void updateTexImageData(Image img, Texture.Type type, boolean mips) { + int texId = img.getId(); + if (texId == -1) { + // create texture + if (verboseLogging) { + logger.info("GLES20.glGenTexture(1, buffer)"); + } + + Gdx.gl20.glGenTextures(1, intBuf1); + texId = intBuf1.get(0); + img.setId(texId); + objManager.registerForCleanup(img); + + statistics.onNewTexture(); + } + + // bind texture + int target = convertTextureType(type); + if (context.boundTextureUnit != 0) { + if (verboseLogging) { + logger.info("GLES20.glActiveTexture(GLES20.GL_TEXTURE0)"); + } + + Gdx.gl20.glActiveTexture(GL20.GL_TEXTURE0); + context.boundTextureUnit = 0; + } + if (context.boundTextures[0] != img) { + + if (verboseLogging) { + logger.info("GLES20.glBindTexture(" + target + ", " + texId + ")"); + } + + Gdx.gl20.glBindTexture(target, texId); + context.boundTextures[0] = img; + } + + + if (target == GL20.GL_TEXTURE_CUBE_MAP) { + // Upload a cube map / sky box + // TODO +// @SuppressWarnings("unchecked") +// List bmps = (List) img.getEfficentData(); +// if (bmps != null) { +// // Native android bitmap +// if (bmps.size() != 6) { +// throw new UnsupportedOperationException("Invalid texture: " + img +// + "Cubemap textures must contain 6 data units."); +// } +// for (int i = 0; i < 6; i++) { +// TextureUtilGdx.uploadTextureBitmap(GL20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, bmps.get(i), false, powerOf2); +// } +// } else { +// // Standard jme3 image data +// List data = img.getData(); +// if (data.size() != 6) { +// logger.log(Level.WARNING, "Invalid texture: {0}\n" +// + "Cubemap textures must contain 6 data units.", img); +// return; +// } +// for (int i = 0; i < 6; i++) { +// TextureUtilGdx.uploadTexture(img, GL20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc, false, powerOf2); +// } +// } + } else { + // TODO + TextureUtilGdx.uploadTexture(img, target, 0, 0, tdc, false, powerOf2); + + if (verboseLogging) { + logger.info("GLES20.glTexParameteri(" + target + "GLES11.GL_GENERATE_MIMAP, GLES20.GL_TRUE)"); + } + + if (!img.hasMipmaps() && mips) { + // No pregenerated mips available, + // generate from base level if required + if (verboseLogging) { + logger.info("GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D)"); + } + Gdx.gl20.glGenerateMipmap(GL20.GL_TEXTURE_2D); + } + } + + img.clearUpdateNeeded(); + } + public void setTexture(int unit, Texture tex) { + Image image = tex.getImage(); + // TODO + if (image.getData().size() == 0 || image.getFormat() == null) { + return; + } + if (image.isUpdateNeeded()) { +// logger.warning("setTexture: isUpdateNeeded"); +// if (image.getEfficentData() instanceof Bitmap) { +// Bitmap bmp = (Bitmap) image.getEfficentData(); +// if (bmp != null) { +// // Check if the bitmap got recycled, can happen after wakeup/restart +// if (bmp.isRecycled()) { +// // We need to reload the bitmap +// DesktopAssetManager assetManager; +// try { +// assetManager = (DesktopAssetManager) ((AndroidHarness) JmeSystem.getActivity()).getJmeApplication().getAssetManager(); +// } catch (ClassCastException ex) { +// Application app = JmeSystem.getApplication(); +// assetManager = (DesktopAssetManager) app.getAssetManager(); +// } +// assetManager.deleteFromCache((TextureKey) tex.getKey()); +// Texture textureReloaded = assetManager.loadTexture((TextureKey) tex.getKey()); +// image.setEfficentData(textureReloaded.getImage().getEfficentData()); +// } +// } +// } + updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels()); + setupTextureParams(tex); + } + + int texId = image.getId(); + assert texId != -1; + + if (texId == -1) { + logger.warning("error: texture image has -1 id"); + } + + Image[] textures = context.boundTextures; + + int type = convertTextureType(tex.getType()); +// if (!context.textureIndexList.moveToNew(unit)) { +// if (context.boundTextureUnit != unit){ +// glActiveTexture(GL_TEXTURE0 + unit); +// context.boundTextureUnit = unit; +// } +// glEnable(type); +// } + + if (context.boundTextureUnit != unit) { + if (verboseLogging) { + logger.info("GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + " + unit + ")"); + } + } + if (textures[unit] != image) { + + if (verboseLogging) { + logger.info("GLES20.glBindTexture(" + type + ", " + texId + ")"); + } + + Gdx.gl20.glActiveTexture(GL20.GL_TEXTURE0 + unit); + context.boundTextureUnit = unit; + Gdx.gl20.glBindTexture(type, texId); + textures[unit] = image; + + statistics.onTextureUse(tex.getImage(), true); + } else { + statistics.onTextureUse(tex.getImage(), false); + } + +// setupTextureParams(tex); + } + + public void clearTextureUnits() { +// IDList textureList = context.textureIndexList; +// Image[] textures = context.boundTextures; +// for (int i = 0; i < textureList.oldLen; i++) { +// int idx = textureList.oldList[i]; +// if (context.boundTextureUnit != idx){ +// GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + idx); +// context.boundTextureUnit = idx; +// } +// GLES20.glDisable(GLES20.GL_TEXTURE_2D/*convertTextureType(textures[idx].getType())*/); +// textures[idx] = null; +// } +// context.textureIndexList.copyNewToOld(); + } + + public void deleteImage(Image image) { + int texId = image.getId(); + if (texId != -1) { + intBuf1.put(0, texId); + intBuf1.position(0).limit(1); + + if (verboseLogging) { + logger.info("GLES20.glDeleteTexture(1, buffer)"); + } + + Gdx.gl20.glDeleteTextures(1, intBuf1); + image.resetObject(); + + statistics.onDeleteTexture(); + } + } + + /*********************************************************************\ + |* Vertex Buffers and Attributes *| + \*********************************************************************/ + private int convertUsage(Usage usage) { + switch (usage) { + case Static: + return GL20.GL_STATIC_DRAW; + case Dynamic: + return GL20.GL_DYNAMIC_DRAW; + case Stream: + return GL20.GL_STREAM_DRAW; + default: + throw new RuntimeException("Unknown usage type."); + } + } + + private int convertFormat(Format format) { + switch (format) { + case Byte: + return GL20.GL_BYTE; + case UnsignedByte: + return GL20.GL_UNSIGNED_BYTE; + case Short: + return GL20.GL_SHORT; + case UnsignedShort: + return GL20.GL_UNSIGNED_SHORT; + case Int: + return GL20.GL_INT; + case UnsignedInt: + return GL20.GL_UNSIGNED_INT; + /* + case Half: + return NVHalfFloat.GL_HALF_FLOAT_NV; + // return ARBHalfFloatVertex.GL_HALF_FLOAT; + */ + case Float: + return GL20.GL_FLOAT; +// case Double: +// return GLES20.GL_DOUBLE; + default: + throw new RuntimeException("Unknown buffer format."); + + } + } + + public void updateBufferData(VertexBuffer vb) { + + if (verboseLogging) { + logger.info("updateBufferData(" + vb + ")"); + } + + int bufId = vb.getId(); + boolean created = false; + if (bufId == -1) { + // create buffer + + if (verboseLogging) { + logger.info("GLES20.glGenBuffers(" + 1 + ", buffer)"); + } + + Gdx.gl20.glGenBuffers(1, intBuf1); + bufId = intBuf1.get(0); + vb.setId(bufId); + objManager.registerForCleanup(vb); + + created = true; + } + + // bind buffer + int target; + if (vb.getBufferType() == Type.Index) { + target = GL20.GL_ELEMENT_ARRAY_BUFFER; + + if (verboseLogging) { + logger.info("vb.getBufferType() == VertexBuffer.Type.Index"); + } + + if (context.boundElementArrayVBO != bufId) { + + if (verboseLogging) { + logger.info("GLES20.glBindBuffer(" + target + ", " + bufId + ")"); + } + + Gdx.gl20.glBindBuffer(target, bufId); + context.boundElementArrayVBO = bufId; + } + } else { + if (verboseLogging) { + logger.info("vb.getBufferType() != VertexBuffer.Type.Index"); + } + + target = GL20.GL_ARRAY_BUFFER; + + if (context.boundArrayVBO != bufId) { + + if (verboseLogging) { + logger.info("GLES20.glBindBuffer(" + target + ", " + bufId + ")"); + } + + Gdx.gl20.glBindBuffer(target, bufId); + context.boundArrayVBO = bufId; + } + } + + int usage = convertUsage(vb.getUsage()); + vb.getData().clear(); + + if (created || vb.hasDataSizeChanged()) { + // upload data based on format + int size = vb.getData().capacity() * vb.getFormat().getComponentSize(); + + switch (vb.getFormat()) { + case Byte: + case UnsignedByte: + + if (verboseLogging) { + logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")"); + } + + Gdx.gl20.glBufferData(target, size, (ByteBuffer) vb.getData(), usage); + break; + // case Half: + case Short: + case UnsignedShort: + + if (verboseLogging) { + logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")"); + } + + Gdx.gl20.glBufferData(target, size, (ShortBuffer) vb.getData(), usage); + break; + case Int: + case UnsignedInt: + + if (verboseLogging) { + logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")"); + } + + Gdx.gl20.glBufferData(target, size, (IntBuffer) vb.getData(), usage); + break; + case Float: + if (verboseLogging) { + logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")"); + } + + Gdx.gl20.glBufferData(target, size, (FloatBuffer) vb.getData(), usage); + break; + case Double: + if (verboseLogging) { + logger.info("GLES20.glBufferData(" + target + ", " + size + ", (data), " + usage + ")"); + } + + Gdx.gl20.glBufferData(target, size, (DoubleBuffer) vb.getData(), usage); + break; + default: + throw new RuntimeException("Unknown buffer format."); + } + } else { + int size = vb.getData().capacity() * vb.getFormat().getComponentSize(); + + switch (vb.getFormat()) { + case Byte: + case UnsignedByte: + if (verboseLogging) { + logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))"); + } + + Gdx.gl20.glBufferSubData(target, 0, size, (ByteBuffer) vb.getData()); + break; + case Short: + case UnsignedShort: + if (verboseLogging) { + logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))"); + } + + Gdx.gl20.glBufferSubData(target, 0, size, (ShortBuffer) vb.getData()); + break; + case Int: + case UnsignedInt: + if (verboseLogging) { + logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))"); + } + + Gdx.gl20.glBufferSubData(target, 0, size, (IntBuffer) vb.getData()); + break; + case Float: + if (verboseLogging) { + logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))"); + } + + Gdx.gl20.glBufferSubData(target, 0, size, (FloatBuffer) vb.getData()); + break; + case Double: + if (verboseLogging) { + logger.info("GLES20.glBufferSubData(" + target + ", 0, " + size + ", (data))"); + } + + Gdx.gl20.glBufferSubData(target, 0, size, (DoubleBuffer) vb.getData()); + break; + default: + throw new RuntimeException("Unknown buffer format."); + } + } +// }else{ +// if (created || vb.hasDataSizeChanged()){ +// glBufferData(target, vb.getData().capacity() * vb.getFormat().getComponentSize(), usage); +// } +// +// ByteBuffer buf = glMapBuffer(target, +// GL_WRITE_ONLY, +// vb.getMappedData()); +// +// if (buf != vb.getMappedData()){ +// buf = buf.order(ByteOrder.nativeOrder()); +// vb.setMappedData(buf); +// } +// +// buf.clear(); +// +// switch (vb.getFormat()){ +// case Byte: +// case UnsignedByte: +// buf.put( (ByteBuffer) vb.getData() ); +// break; +// case Short: +// case UnsignedShort: +// buf.asShortBuffer().put( (ShortBuffer) vb.getData() ); +// break; +// case Int: +// case UnsignedInt: +// buf.asIntBuffer().put( (IntBuffer) vb.getData() ); +// break; +// case Float: +// buf.asFloatBuffer().put( (FloatBuffer) vb.getData() ); +// break; +// case Double: +// break; +// default: +// throw new RuntimeException("Unknown buffer format."); +// } +// +// glUnmapBuffer(target); +// } + + vb.clearUpdateNeeded(); + } + + public void deleteBuffer(VertexBuffer vb) { + int bufId = vb.getId(); + if (bufId != -1) { + // delete buffer + intBuf1.put(0, bufId); + intBuf1.position(0).limit(1); + if (verboseLogging) { + logger.info("GLES20.glDeleteBuffers(1, buffer)"); + } + + Gdx.gl20.glDeleteBuffers(1, intBuf1); + vb.resetObject(); + } + } + + public void clearVertexAttribs() { + IDList attribList = context.attribIndexList; + int oldLen = attribList.oldLen; + for (int i = 0; i < oldLen; i++) { + int idx = attribList.oldList[i]; + + if (verboseLogging) { + logger.info("GLES20.glDisableVertexAttribArray(" + idx + ")"); + } + if (idx != -1) { + Gdx.gl20.glDisableVertexAttribArray(idx); + context.boundAttribs[idx] = null; + } + } + context.attribIndexList.copyNewToOld(); + } + + public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) { + if (verboseLogging) { + logger.info("setVertexAttrib(" + vb + ", " + idb + ")"); + } + + if (vb.getBufferType() == Type.Index) { + throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib"); + } + + if (vb.isUpdateNeeded() && idb == null) { + updateBufferData(vb); + } + + int programId = context.boundShaderProgram; + if (programId > 0) { + Attribute attrib = boundShader.getAttribute(vb.getBufferType()); + int loc = attrib.getLocation(); + if (loc == -1) { + + if (verboseLogging) { + logger.warning("location is invalid for attrib: [" + vb.getBufferType().name() + "]"); + } + + return; // not defined + } + + if (loc == -2) { +// stringBuf.setLength(0); +// stringBuf.append("in").append(vb.getBufferType().name()).append('\0'); +// updateNameBuffer(); + + String attributeName = "in" + vb.getBufferType().name(); + + if (verboseLogging) { + logger.info("GLES20.glGetAttribLocation(" + programId + ", " + attributeName + ")"); + } + + loc = Gdx.gl20.glGetAttribLocation(programId, attributeName); + + // not really the name of it in the shader (inPosition\0) but + // the internal name of the enum (Position). + if (loc < 0) { + attrib.setLocation(-1); + + if (verboseLogging) { + logger.warning("attribute is invalid in shader: [" + vb.getBufferType().name() + "]"); + } + + return; // not available in shader. + } else { + attrib.setLocation(loc); + } + } + + VertexBuffer[] attribs = context.boundAttribs; + if (!context.attribIndexList.moveToNew(loc)) { + if (verboseLogging) { + logger.info("GLES20.glEnableVertexAttribArray(" + loc + ")"); + } + + Gdx.gl20.glEnableVertexAttribArray(loc); + //System.out.println("Enabled ATTRIB IDX: "+loc); + } + if (attribs[loc] != vb) { + // NOTE: Use id from interleaved buffer if specified + int bufId = idb != null ? idb.getId() : vb.getId(); + assert bufId != -1; + + if (bufId == -1) { + logger.warning("invalid buffer id"); + } + + if (context.boundArrayVBO != bufId) { + if (verboseLogging) { + logger.info("GLES20.glBindBuffer(" + GL20.GL_ARRAY_BUFFER + ", " + bufId + ")"); + } + Gdx.gl20.glBindBuffer(GL20.GL_ARRAY_BUFFER, bufId); + context.boundArrayVBO = bufId; + } + +// vb.getData().clear(); + + if (verboseLogging) { + logger.info("GLES20.glVertexAttribPointer(" + + "location=" + loc + ", " + + "numComponents=" + vb.getNumComponents() + ", " + + "format=" + vb.getFormat() + ", " + + "isNormalized=" + vb.isNormalized() + ", " + + "stride=" + vb.getStride() + ", " + + "data.capacity=" + vb.getData().capacity() + ")"); + } + + Gdx.gl20.glVertexAttribPointer(loc, + vb.getNumComponents(), + convertFormat(vb.getFormat()), + vb.isNormalized(), + vb.getStride(), + vb.getOffset()); + + attribs[loc] = vb; + } + } else { + throw new IllegalStateException("Cannot render mesh without shader bound"); + } + } + + public void setVertexAttrib(VertexBuffer vb) { + setVertexAttrib(vb, null); + } + + public void drawTriangleArray(Mode mode, int count, int vertCount) { + /* if (count > 1){ + ARBDrawInstanced.glDrawArraysInstancedARB(convertElementMode(mode), 0, + vertCount, count); + }else{*/ + if (verboseLogging) { + logger.info("GLES20.glDrawArrays(" + vertCount + ")"); + } + + Gdx.gl20.glDrawArrays(convertElementMode(mode), 0, vertCount); + /* + }*/ + } + + public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) { + + if (verboseLogging) { + logger.info("drawTriangleList(" + count + ")"); + } + + if (indexBuf.getBufferType() != Type.Index) { + throw new IllegalArgumentException("Only index buffers are allowed as triangle lists."); + } + + if (indexBuf.isUpdateNeeded()) { + if (verboseLogging) { + logger.info("updateBufferData for indexBuf."); + } + updateBufferData(indexBuf); + } + + int bufId = indexBuf.getId(); + assert bufId != -1; + + if (bufId == -1) { + logger.info("invalid buffer id!"); + } + + if (context.boundElementArrayVBO != bufId) { + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, {0})", bufId); + } + + Gdx.gl20.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, bufId); + context.boundElementArrayVBO = bufId; + } + + int vertCount = mesh.getVertexCount(); + boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing); + + Buffer indexData = indexBuf.getData(); + + if (mesh.getMode() == Mode.Hybrid) { + int[] modeStart = mesh.getModeStart(); + int[] elementLengths = mesh.getElementLengths(); + + int elMode = convertElementMode(Mode.Triangles); + int fmt = convertFormat(indexBuf.getFormat()); + int elSize = indexBuf.getFormat().getComponentSize(); + int listStart = modeStart[0]; + int stripStart = modeStart[1]; + int fanStart = modeStart[2]; + int curOffset = 0; + for (int i = 0; i < elementLengths.length; i++) { + if (i == stripStart) { + elMode = convertElementMode(Mode.TriangleStrip); + } else if (i == fanStart) { + elMode = convertElementMode(Mode.TriangleStrip); + } + int elementLength = elementLengths[i]; + + if (useInstancing) { + //ARBDrawInstanced. + throw new IllegalArgumentException("instancing is not supported."); + /* + GLES20.glDrawElementsInstancedARB(elMode, + elementLength, + fmt, + curOffset, + count); + */ + } else { + indexBuf.getData().position(curOffset); + if (verboseLogging) { + logger.log(Level.INFO, "glDrawElements(): {0}, {1}", new Object[]{elementLength, curOffset}); + } + + Gdx.gl20.glDrawElements(elMode, elementLength, fmt, indexBuf.getData()); + /* + glDrawRangeElements(elMode, + 0, + vertCount, + elementLength, + fmt, + curOffset); + */ + } + + curOffset += elementLength * elSize; + } + } else { + if (useInstancing) { + throw new IllegalArgumentException("instancing is not supported."); + //ARBDrawInstanced. +/* + GLES20.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()), + indexBuf.getData().capacity(), + convertFormat(indexBuf.getFormat()), + 0, + count); + */ + } else { + indexData.clear(); + + if (verboseLogging) { + logger.log(Level.INFO, "glDrawElements(), indexBuf.capacity ({0}), vertCount ({1})", new Object[]{indexBuf.getData().capacity(), vertCount}); + } + + Gdx.gl20.glDrawElements( + convertElementMode(mesh.getMode()), + indexBuf.getData().capacity(), + convertFormat(indexBuf.getFormat()), + 0); + } + } + } + + /*********************************************************************\ + |* Render Calls *| + \*********************************************************************/ + public int convertElementMode(Mode mode) { + switch (mode) { + case Points: + return GL20.GL_POINTS; + case Lines: + return GL20.GL_LINES; + case LineLoop: + return GL20.GL_LINE_LOOP; + case LineStrip: + return GL20.GL_LINE_STRIP; + case Triangles: + return GL20.GL_TRIANGLES; + case TriangleFan: + return GL20.GL_TRIANGLE_FAN; + case TriangleStrip: + return GL20.GL_TRIANGLE_STRIP; + default: + throw new UnsupportedOperationException("Unrecognized mesh mode: " + mode); + } + } + + public void updateVertexArray(Mesh mesh) { + logger.log(Level.INFO, "updateVertexArray({0})", mesh); + int id = mesh.getId(); + /* + if (id == -1){ + IntBuffer temp = intBuf1; + // ARBVertexArrayObject.glGenVertexArrays(temp); + GLES20.glGenVertexArrays(temp); + id = temp.get(0); + mesh.setId(id); + } + + if (context.boundVertexArray != id){ + // ARBVertexArrayObject.glBindVertexArray(id); + GLES20.glBindVertexArray(id); + context.boundVertexArray = id; + } + */ + VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); + if (interleavedData != null && interleavedData.isUpdateNeeded()) { + updateBufferData(interleavedData); + } + + SafeArrayList buffersList = mesh.getBufferList(); + for (int i = 0; i < buffersList.size(); i++) { + VertexBuffer vb = buffersList.get(i); + + if (vb.getBufferType() == Type.InterleavedData + || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers + || vb.getBufferType() == Type.Index) { + continue; + } + + if (vb.getStride() == 0) { + // not interleaved + setVertexAttrib(vb); + } else { + // interleaved + setVertexAttrib(vb, interleavedData); + } + } + } + + /** + * renderMeshVertexArray renders a mesh using vertex arrays + * @param mesh + * @param lod + * @param count + */ + private void renderMeshVertexArray(Mesh mesh, int lod, int count) { + if (verboseLogging) { + logger.info("renderMeshVertexArray"); + } + + // IntMap buffers = mesh.getBuffers(); + IntMap buffers = mesh.getBuffers(); + IntMap.Entry table[] = buffers.getTable(); + for (IntMap.Entry entry : table) { + if (entry == null) { + continue; + } + VertexBuffer vb = entry.getValue(); + + if (vb.getBufferType() == Type.InterleavedData + || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers + || vb.getBufferType() == Type.Index) { + continue; + } + + if (vb.getStride() == 0) { + // not interleaved + setVertexAttrib_Array(vb); + } else { + // interleaved + VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); + setVertexAttrib_Array(vb, interleavedData); + } + } + + VertexBuffer indices = null; + if (mesh.getNumLodLevels() > 0) { + indices = mesh.getLodLevel(lod); + } else { + indices = mesh.getBuffer(Type.Index);//buffers.get(Type.Index.ordinal()); + } + clearVertexAttribs(); +// clearTextureUnits(); + if (indices != null) { + drawTriangleList_Array(indices, mesh, count); + } else { + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glDrawArrays({0}, {1}, {2})", + new Object[]{mesh.getMode(), 0, mesh.getVertexCount()}); + } + + Gdx.gl20.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount()); + } + } + + private void renderMeshDefault(Mesh mesh, int lod, int count) { + if (verboseLogging) { + logger.log(Level.INFO, "renderMeshDefault({0}, {1}, {2})", + new Object[]{mesh, lod, count}); + } + VertexBuffer indices = null; + + VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); + if (interleavedData != null && interleavedData.isUpdateNeeded()) { + updateBufferData(interleavedData); + } + + //IntMap buffers = mesh.getBuffers(); ; + if (mesh.getNumLodLevels() > 0) { + indices = mesh.getLodLevel(lod); + } else { + indices = mesh.getBuffer(Type.Index);// buffers.get(Type.Index.ordinal()); + } + SafeArrayList buffersList = mesh.getBufferList(); + for (int i = 0; i < buffersList.size(); i++) { + VertexBuffer vb = buffersList.get(i); + + if (vb.getBufferType() == Type.InterleavedData + || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers + || vb.getBufferType() == Type.Index) { + continue; + } + + if (vb.getStride() == 0) { + // not interleaved + setVertexAttrib(vb); + } else { + // interleaved + setVertexAttrib(vb, interleavedData); + } + } + clearVertexAttribs(); +// clearTextureUnits(); + if (indices != null) { + drawTriangleList(indices, mesh, count); + } else { +// throw new UnsupportedOperationException("Cannot render without index buffer"); + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glDrawArrays({0}, 0, {1})", + new Object[]{convertElementMode(mesh.getMode()), mesh.getVertexCount()}); + } + + Gdx.gl20.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount()); + } + } + + public void renderMesh(Mesh mesh, int lod, int count) { + if (context.pointSize != mesh.getPointSize()) { + + if (verboseLogging) { + logger.log(Level.INFO, "GLES10.glPointSize({0})", mesh.getPointSize()); + } + // TODO + Gdx.gl10.glPointSize(mesh.getPointSize()); + context.pointSize = mesh.getPointSize(); + } + if (context.lineWidth != mesh.getLineWidth()) { + + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glLineWidth({0})", mesh.getLineWidth()); + } + + Gdx.gl20.glLineWidth(mesh.getLineWidth()); + context.lineWidth = mesh.getLineWidth(); + } + + statistics.onMeshDrawn(mesh, lod); +// if (GLContext.getCapabilities().GL_ARB_vertex_array_object){ +// renderMeshVertexArray(mesh, lod, count); +// }else{ + + if (useVBO) { + if (verboseLogging) { + logger.info("RENDERING A MESH USING VertexBufferObject"); + } + + renderMeshDefault(mesh, lod, count); + } else { + if (verboseLogging) { + logger.info("RENDERING A MESH USING VertexArray"); + } + + renderMeshVertexArray(mesh, lod, count); + } + +// } + } + + private void checkGLError() { + } + + private void checkGLError2() { + int error; + while ((error = Gdx.gl20.glGetError()) != GL20.GL_NO_ERROR) { + logger.log(Level.WARNING, "glError {0}", error); + // throw new RuntimeException("glError " + error); + } + } + + private boolean log(String message) { + logger.info(message); + return true; + } + + /** + * drawTriangleList_Array uses Vertex Array + * @param indexBuf + * @param mesh + * @param count + */ + public void drawTriangleList_Array(VertexBuffer indexBuf, Mesh mesh, int count) { + if (verboseLogging) { + logger.log(Level.INFO, "drawTriangleList_Array(Count = {0})", count); + } + + if (indexBuf.getBufferType() != Type.Index) { + throw new IllegalArgumentException("Only index buffers are allowed as triangle lists."); + } + + boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing); + if (useInstancing) { + throw new IllegalArgumentException("Caps.MeshInstancing is not supported."); + } + + int vertCount = mesh.getVertexCount(); + Buffer indexData = indexBuf.getData(); + indexData.clear(); + + if (mesh.getMode() == Mode.Hybrid) { + int[] modeStart = mesh.getModeStart(); + int[] elementLengths = mesh.getElementLengths(); + + int elMode = convertElementMode(Mode.Triangles); + int fmt = convertFormat(indexBuf.getFormat()); + int elSize = indexBuf.getFormat().getComponentSize(); + int listStart = modeStart[0]; + int stripStart = modeStart[1]; + int fanStart = modeStart[2]; + int curOffset = 0; + for (int i = 0; i < elementLengths.length; i++) { + if (i == stripStart) { + elMode = convertElementMode(Mode.TriangleStrip); + } else if (i == fanStart) { + elMode = convertElementMode(Mode.TriangleStrip); + } + int elementLength = elementLengths[i]; + + indexBuf.getData().position(curOffset); + if (verboseLogging) { + logger.log(Level.INFO, "glDrawElements(): {0}, {1}", new Object[]{elementLength, curOffset}); + } + + Gdx.gl20.glDrawElements(elMode, elementLength, fmt, indexBuf.getData()); + + curOffset += elementLength * elSize; + } + } else { + if (verboseLogging) { + logger.log(Level.INFO, "glDrawElements(), indexBuf.capacity ({0}), vertCount ({1})", new Object[]{indexBuf.getData().capacity(), vertCount}); + } + + Gdx.gl20.glDrawElements( + convertElementMode(mesh.getMode()), + indexBuf.getData().capacity(), + convertFormat(indexBuf.getFormat()), + indexBuf.getData()); + } + } + + /** + * setVertexAttrib_Array uses Vertex Array + * @param vb + * @param idb + */ + public void setVertexAttrib_Array(VertexBuffer vb, VertexBuffer idb) { + if (verboseLogging) { + logger.log(Level.INFO, "setVertexAttrib_Array({0}, {1})", new Object[]{vb, idb}); + } + + if (vb.getBufferType() == Type.Index) { + throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib"); + } + + // Get shader + int programId = context.boundShaderProgram; + if (programId > 0) { + VertexBuffer[] attribs = context.boundAttribs; + + Attribute attrib = boundShader.getAttribute(vb.getBufferType()); + int loc = attrib.getLocation(); + if (loc == -1) { + //throw new IllegalArgumentException("Location is invalid for attrib: [" + vb.getBufferType().name() + "]"); + if (verboseLogging) { + logger.log(Level.WARNING, "attribute is invalid in shader: [{0}]", vb.getBufferType().name()); + } + return; + } else if (loc == -2) { + String attributeName = "in" + vb.getBufferType().name(); + + if (verboseLogging) { + logger.log(Level.INFO, "GLES20.glGetAttribLocation({0}, {1})", new Object[]{programId, attributeName}); + } + + loc = Gdx.gl20.glGetAttribLocation(programId, attributeName); + if (loc < 0) { + attrib.setLocation(-1); + if (verboseLogging) { + logger.log(Level.WARNING, "attribute is invalid in shader: [{0}]", vb.getBufferType().name()); + } + return; // not available in shader. + } else { + attrib.setLocation(loc); + } + + } // if (loc == -2) + + if ((attribs[loc] != vb) || vb.isUpdateNeeded()) { + System.err.println("isUpdateNeeded "+vb.isUpdateNeeded()); + vb.clearUpdateNeeded(); + // NOTE: Use data from interleaved buffer if specified + VertexBuffer avb = idb != null ? idb : vb; + avb.getData().clear(); + avb.getData().position(vb.getOffset()); + + if (verboseLogging) { + logger.log(Level.INFO, + "GLES20.glVertexAttribPointer(" + + "location={0}, " + + "numComponents={1}, " + + "format={2}, " + + "isNormalized={3}, " + + "stride={4}, " + + "data.capacity={5})", + new Object[]{loc, vb.getNumComponents(), + vb.getFormat(), + vb.isNormalized(), + vb.getStride(), + avb.getData().capacity()}); + } + + + // Upload attribute data + + Gdx.gl20.glVertexAttribPointer(loc, + vb.getNumComponents(), + convertFormat(vb.getFormat()), + vb.isNormalized(), + vb.getStride(), + convBuffer(avb.getData())); + checkGLError(); + + Gdx.gl20.glEnableVertexAttribArray(loc); + + attribs[loc] = vb; + } // if (attribs[loc] != vb) + } else { + throw new IllegalStateException("Cannot render mesh without shader bound"); + } + } + private ByteBuffer convBuffer(Buffer buf) { + System.err.println("convBuffer"); + if (buf instanceof FloatBuffer) { + System.err.println("FloatBuffer"); + FloatBuffer fb = (FloatBuffer)buf; + ByteBuffer bb = BufferUtils.createByteBuffer(buf.capacity() * 4); + FloatBuffer fb2 = bb.asFloatBuffer(); + fb2.put(fb); + return bb; + } else if (buf instanceof ShortBuffer) { + System.err.println("ShortBuffer"); + + ShortBuffer sb = (ShortBuffer)buf; + ByteBuffer bb = BufferUtils.createByteBuffer(buf.capacity() * 2); + ShortBuffer sb2 = bb.asShortBuffer(); + sb2.put(sb); + return bb; + } else { + return (ByteBuffer)buf; + //throw new RuntimeException("type = "+buf.getClass().getName()); + } + } + + /** + * setVertexAttrib_Array uses Vertex Array + * @param vb + */ + public void setVertexAttrib_Array(VertexBuffer vb) { + setVertexAttrib_Array(vb, null); + } + + public void setAlphaToCoverage(boolean value) { + if (value) { + Gdx.gl20.glEnable(GL20.GL_SAMPLE_ALPHA_TO_COVERAGE); + } else { + Gdx.gl20.glDisable(GL20.GL_SAMPLE_ALPHA_TO_COVERAGE); + } + } + + @Override + public void invalidateState() { + context.reset(); + boundShader = null; + lastFb = null; + } + + public void resetBoundsTexture() { + context.boundTextures[0] = null; + if (context.boundTextureUnit != 0) { + Gdx.gl20.glActiveTexture(GL20.GL_TEXTURE0); + context.boundTextureUnit = 0; + } +// GLES20.glDisable(GLES20.GL_TEXTURE_2D); + Gdx.gl20.glBindTexture(GL20.GL_TEXTURE_2D, 0); + // context.boundTextureUnit = -2; +// context.boundElementArrayVBO = -2; +// context.boundShaderProgram = -1; +// context.boundArrayVBO = -1; +// context.boundElementArrayVBO = -1; + + if (context.boundElementArrayVBO != 0) { + + + Gdx.gl20.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0); + context.boundElementArrayVBO = 0; + } + if (context.boundArrayVBO != 0) { + Gdx.gl20.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0); + context.boundArrayVBO = 0; + } + if (context.boundShaderProgram != 0) { + Gdx.gl20.glUseProgram(0); + checkGLError(); + boundShader = null; + context.boundShaderProgram = 0; + } + } +} diff --git a/gdx/src/main/java/com/jme3/renderer/gdx/TextureUtilGdx.java b/gdx/src/main/java/com/jme3/renderer/gdx/TextureUtilGdx.java new file mode 100644 index 000000000..3e870d125 --- /dev/null +++ b/gdx/src/main/java/com/jme3/renderer/gdx/TextureUtilGdx.java @@ -0,0 +1,499 @@ +package com.jme3.renderer.gdx; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.GL10; +import com.badlogic.gdx.graphics.GL20; +import com.jme3.asset.DesktopAssetManager; +import com.jme3.math.FastMath; +import com.jme3.system.JmeSystem; +import com.jme3.texture.Image; +import com.jme3.texture.Image.Format; +import java.nio.ByteBuffer; +import java.util.logging.Logger; + +public class TextureUtilGdx { + public static boolean ENABLE_COMPRESSION = true; + private static boolean NPOT = false; + private static boolean ETC1support = false; + private static boolean DXT1 = false; + private static boolean DEPTH24_STENCIL8 = false; + private static boolean DEPTH_TEXTURE = true; + private static boolean RGBA8 = false; + + // Same constant used by both GL_ARM_rgba8 and GL_OES_rgb8_rgba8. + private static final int GL_RGBA8 = 0x8058; + + private static final int GL_DXT1 = 0x83F0; + private static final int GL_DXT1A = 0x83F1; + + private static final int GL_DEPTH_STENCIL_OES = 0x84F9; + private static final int GL_UNSIGNED_INT_24_8_OES = 0x84FA; + private static final int GL_DEPTH24_STENCIL8_OES = 0x88F0; + + public static int convertTextureFormat(Format fmt){ + switch (fmt){ + case Alpha16: + case Alpha8: + return GL10.GL_ALPHA; + case Luminance8Alpha8: + case Luminance16Alpha16: + return GL10.GL_LUMINANCE_ALPHA; + case Luminance8: + case Luminance16: + return GL10.GL_LUMINANCE; + case RGB10: + case RGB16: + case BGR8: + case RGB8: + case RGB565: + return GL10.GL_RGB; + case RGB5A1: + case RGBA16: + case RGBA8: + return GL10.GL_RGBA; + + case Depth: + return Gdx.gl20.GL_DEPTH_COMPONENT; + case Depth16: + return Gdx.gl20.GL_DEPTH_COMPONENT16; + case Depth24: + case Depth32: + case Depth32F: + throw new UnsupportedOperationException("Unsupported depth format: " + fmt); + + case DXT1A: + throw new UnsupportedOperationException("Unsupported format: " + fmt); + default: + throw new UnsupportedOperationException("Unrecognized format: " + fmt); + } + } + // TODO +// private static void buildMipmap(Bitmap bitmap) { +// int level = 0; +// int height = bitmap.getHeight(); +// int width = bitmap.getWidth(); +// +// while (height >= 1 || width >= 1) { +// //First of all, generate the texture from our bitmap and set it to the according level +// +// GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0); +////checkGLError(); +// if (height == 1 || width == 1) { +// break; +// } +// +// //Increase the mipmap level +// level++; +// +// height /= 2; +// width /= 2; +// Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true); +// +// bitmap.recycle(); +// bitmap = bitmap2; +// } +// }bitmap2 +// // TODO +// /** +// * uploadTextureBitmap uploads a native android bitmap +// * @param target +// * @param bitmap +// * @param generateMips +// * @param powerOf2 +// */ +// public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean generateMips, boolean powerOf2) { +// int MAX_RETRY_COUNT = 1; +// for(int retryCount = 0;retryCountuploadTextureBitmap uploads a native android bitmap +// * @param target +// * @param bitmap +// * @param generateMips +// * @param powerOf2 +// */ +// private static void uploadTextureBitmap2(final int target, Bitmap bitmap, boolean generateMips, boolean powerOf2) +// { +// if (bitmap.isRecycled()) { +// throw new RuntimeException("bitmap is recycled."); +// } +// if (!powerOf2) +// { +// int width = bitmap.getWidth(); +// int height = bitmap.getHeight(); +// if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height) +// /*|| width >= 512 || height >= 512*/) +// { +// // scale to power of two +// width = FastMath.nearestPowerOfTwo(width); +// height = FastMath.nearestPowerOfTwo(height); +//// while(width >= 512) { +//// width = width / 2; +//// } +//// while(height >= 512) { +//// height = height / 2; +//// } +// Logger.getLogger(TextureUtil.class.getName()).warning("texture size changed."); +// Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true); +// bitmap.recycle(); +// bitmap = bitmap2; +// } +// } +// +// if (generateMips) +// { +// buildMipmap(bitmap); +// } +// else +// { +// // TODO +// GLUtils.texImage2D(target, 0, bitmap, 0); +////checkGLError(); +////bitmap.recycle(); +// } +// } + private static void unsupportedFormat(Format fmt) { + throw new UnsupportedOperationException("The image format '" + fmt + "' is unsupported by the video hardware."); + } + + public static AndroidGLImageFormat getImageFormat(Format fmt) throws UnsupportedOperationException { + AndroidGLImageFormat imageFormat = new AndroidGLImageFormat(); + switch (fmt) { + case RGBA16: + case RGB16: + case RGB10: + case Luminance16: + case Luminance16Alpha16: + case Alpha16: + case Depth32: + case Depth32F: + throw new UnsupportedOperationException("The image format '" + + fmt + "' is not supported by OpenGL ES 2.0 specification."); + case Alpha8: + imageFormat.format = GL20.GL_ALPHA; + imageFormat.dataType = GL20.GL_UNSIGNED_BYTE; + if (RGBA8) { + imageFormat.renderBufferStorageFormat = GL_RGBA8; + } else { + // Highest precision alpha supported by vanilla OGLES2 + imageFormat.renderBufferStorageFormat = GL20.GL_RGBA4; + } + break; + case Luminance8: + imageFormat.format = GL20.GL_LUMINANCE; + imageFormat.dataType = GL20.GL_UNSIGNED_BYTE; + if (RGBA8) { + imageFormat.renderBufferStorageFormat = GL_RGBA8; + } else { + // Highest precision luminance supported by vanilla OGLES2 + imageFormat.renderBufferStorageFormat = GL20.GL_RGB565; + } + break; + case Luminance8Alpha8: + imageFormat.format = GL20.GL_LUMINANCE_ALPHA; + imageFormat.dataType = GL20.GL_UNSIGNED_BYTE; + if (RGBA8) { + imageFormat.renderBufferStorageFormat = GL_RGBA8; + } else { + imageFormat.renderBufferStorageFormat = GL20.GL_RGBA4; + } + break; + case RGB565: + imageFormat.format = GL20.GL_RGB; + imageFormat.dataType = GL20.GL_UNSIGNED_SHORT_5_6_5; + imageFormat.renderBufferStorageFormat = GL20.GL_RGB565; + break; + case ARGB4444: + imageFormat.format = GL20.GL_RGBA4; + imageFormat.dataType = GL20.GL_UNSIGNED_SHORT_4_4_4_4; + imageFormat.renderBufferStorageFormat = GL20.GL_RGBA4; + break; + case RGB5A1: + imageFormat.format = GL20.GL_RGBA; + imageFormat.dataType = GL20.GL_UNSIGNED_SHORT_5_5_5_1; + imageFormat.renderBufferStorageFormat = GL20.GL_RGB5_A1; + break; + case RGB8: + imageFormat.format = GL20.GL_RGB; + imageFormat.dataType = GL20.GL_UNSIGNED_BYTE; + if (RGBA8) { + imageFormat.renderBufferStorageFormat = GL_RGBA8; + } else { + // Fallback: Use RGB565 if RGBA8 is not available. + imageFormat.renderBufferStorageFormat = GL20.GL_RGB565; + } + break; + case BGR8: + imageFormat.format = GL20.GL_RGB; + imageFormat.dataType = GL20.GL_UNSIGNED_BYTE; + if (RGBA8) { + imageFormat.renderBufferStorageFormat = GL_RGBA8; + } else { + imageFormat.renderBufferStorageFormat = GL20.GL_RGB565; + } + break; + case RGBA8: + imageFormat.format = GL20.GL_RGBA; + imageFormat.dataType = GL20.GL_UNSIGNED_BYTE; + if (RGBA8) { + imageFormat.renderBufferStorageFormat = GL_RGBA8; + } else { + imageFormat.renderBufferStorageFormat = GL20.GL_RGBA4; + } + break; + case Depth: + case Depth16: + if (!DEPTH_TEXTURE) { + unsupportedFormat(fmt); + } + imageFormat.format = GL20.GL_DEPTH_COMPONENT; + imageFormat.dataType = GL20.GL_UNSIGNED_SHORT; + imageFormat.renderBufferStorageFormat = GL20.GL_DEPTH_COMPONENT16; + break; + case Depth24: +// case Depth24Stencil8: +// if (!DEPTH_TEXTURE) { +// unsupportedFormat(fmt); +// } +// if (DEPTH24_STENCIL8) { +// // NEW: True Depth24 + Stencil8 format. +// imageFormat.format = GL_DEPTH_STENCIL_OES; +// imageFormat.dataType = GL_UNSIGNED_INT_24_8_OES; +// imageFormat.renderBufferStorageFormat = GL_DEPTH24_STENCIL8_OES; +// } else { +// // Vanilla OGLES2, only Depth16 available. +// imageFormat.format = GLES20.GL_DEPTH_COMPONENT; +// imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT; +// imageFormat.renderBufferStorageFormat = GLES20.GL_DEPTH_COMPONENT16; +// } +// break; + case DXT1: + if (!DXT1) { + unsupportedFormat(fmt); + } + imageFormat.format = GL_DXT1; + imageFormat.dataType = GL20.GL_UNSIGNED_BYTE; + imageFormat.compress = true; + break; + case DXT1A: + if (!DXT1) { + unsupportedFormat(fmt); + } + imageFormat.format = GL_DXT1A; + imageFormat.dataType = GL20.GL_UNSIGNED_BYTE; + imageFormat.compress = true; + break; + default: + throw new UnsupportedOperationException("Unrecognized format: " + fmt); + } + return imageFormat; + } + public static class AndroidGLImageFormat { + + boolean compress = false; + int format = -1; + int renderBufferStorageFormat = -1; + int dataType = -1; + } + public static void uploadTexture( + Image img, + int target, + int index, + int border, + boolean tdc, + boolean generateMips, + boolean powerOf2){ + // TODO +// if (img.getEfficentData() instanceof Bitmap){ +// Bitmap bitmap = (Bitmap) img.getEfficentData(); +// uploadTextureBitmap(target, bitmap, generateMips, powerOf2); +// return; +// } + + Format fmt = img.getFormat(); + ByteBuffer data; + if (index >= 0 || img.getData() != null && img.getData().size() > 0){ + data = img.getData(index); + }else{ + data = null; + } + + int width = img.getWidth(); + int height = img.getHeight(); + int depth = img.getDepth(); + + boolean compress = false; + int internalFormat = -1; + int format = -1; + int dataType = -1; + + switch (fmt){ + case Alpha16: + case Alpha8: + format = GL20.GL_ALPHA; + dataType = GL20.GL_UNSIGNED_BYTE; + break; + case Luminance8: + format = GL20.GL_LUMINANCE; + dataType = GL20.GL_UNSIGNED_BYTE; + break; + case Luminance8Alpha8: + format = GL20.GL_LUMINANCE_ALPHA; + dataType = GL20.GL_UNSIGNED_BYTE; + break; + case Luminance16Alpha16: + format = GL20.GL_LUMINANCE_ALPHA; + dataType = GL20.GL_UNSIGNED_BYTE; + break; + case Luminance16: + format = GL20.GL_LUMINANCE; + dataType = GL20.GL_UNSIGNED_BYTE; + break; + case RGB565: + format = GL20.GL_RGB; + internalFormat = GL20.GL_RGB565; + dataType = GL20.GL_UNSIGNED_SHORT_5_6_5; + break; + case ARGB4444: + format = GL20.GL_RGBA; + dataType = GL20.GL_UNSIGNED_SHORT_4_4_4_4; + break; + case RGB10: + format = GL20.GL_RGB; + dataType = GL20.GL_UNSIGNED_BYTE; + break; + case RGB16: + format = GL20.GL_RGB; + dataType = GL20.GL_UNSIGNED_BYTE; + break; + case RGB5A1: + format = GL20.GL_RGBA; + internalFormat = GL20.GL_RGB5_A1; + dataType = GL20.GL_UNSIGNED_SHORT_5_5_5_1; + break; + case RGB8: + format = GL20.GL_RGB; + dataType = GL20.GL_UNSIGNED_BYTE; + break; + case BGR8: + format = GL20.GL_RGB; + dataType = GL20.GL_UNSIGNED_BYTE; + break; + case RGBA16: + format = GL20.GL_RGBA; + internalFormat = GL20.GL_RGBA4; + dataType = GL20.GL_UNSIGNED_BYTE; + break; + case RGBA8: + format = GL20.GL_RGBA; + dataType = GL20.GL_UNSIGNED_BYTE; + break; + case DXT1A: + format = GL20.GL_COMPRESSED_TEXTURE_FORMATS; + dataType = GL20.GL_UNSIGNED_BYTE; + case Depth: + format = GL20.GL_DEPTH_COMPONENT; + dataType = GL20.GL_UNSIGNED_BYTE; + break; + case Depth16: + format = GL20.GL_DEPTH_COMPONENT; + internalFormat = GL20.GL_DEPTH_COMPONENT16; + dataType = GL20.GL_UNSIGNED_BYTE; + break; + case Depth24: + case Depth32: + case Depth32F: + throw new UnsupportedOperationException("Unsupported depth format: " + fmt); + default: + throw new UnsupportedOperationException("Unrecognized format: " + fmt); + } + + if (internalFormat == -1) + { + internalFormat = format; + } + + if (data != null) + Gdx.gl20.glPixelStorei(GL20.GL_UNPACK_ALIGNMENT, 1); + + int[] mipSizes = img.getMipMapSizes(); + int pos = 0; + if (mipSizes == null){ + if (data != null) + mipSizes = new int[]{ data.capacity() }; + else + mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 }; + } + + // XXX: might want to change that when support + // of more than paletted compressions is added.. + if (compress){ + data.clear(); + Gdx.gl20.glCompressedTexImage2D(GL20.GL_TEXTURE_2D, + 1 - mipSizes.length, + format, + width, + height, + 0, + data.capacity(), + data); +//checkGLError(); +return; + } + + for (int i = 0; i < mipSizes.length; i++){ + int mipWidth = Math.max(1, width >> i); + int mipHeight = Math.max(1, height >> i); + int mipDepth = Math.max(1, depth >> i); + + if (data != null){ + data.position(pos); + data.limit(pos + mipSizes[i]); + } + + if (compress && data != null){ + Gdx.gl20.glCompressedTexImage2D(GL20.GL_TEXTURE_2D, + i, + format, + mipWidth, + mipHeight, + 0, + data.remaining(), + data); +//checkGLError(); + }else{ + Gdx.gl20.glTexImage2D(GL20.GL_TEXTURE_2D, + i, + internalFormat, + mipWidth, + mipHeight, + 0, + format, + dataType, + data); + } +//checkGLError(); + pos += mipSizes[i]; + } + } +// private static void checkGLError() { +// int error; +// while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { +// throw new RuntimeException("glError " + error); +// } +// } +} diff --git a/gdx/src/main/java/com/jme3/system/JmeSystemDelegateImpl.java b/gdx/src/main/java/com/jme3/system/JmeSystemDelegateImpl.java new file mode 100644 index 000000000..0e28644f1 --- /dev/null +++ b/gdx/src/main/java/com/jme3/system/JmeSystemDelegateImpl.java @@ -0,0 +1,133 @@ +package com.jme3.system; + +import com.jme3.app.Application; +import com.jme3.asset.*; +import com.jme3.asset.gdx.GdxAssetManager; +import com.jme3.audio.*; +import com.jme3.system.gdx.GdxAudioRenderer; +import com.jme3.system.gdx.GdxContext; +//import com.jme3.audio.DummyAudioRenderer; +import com.jme3.system.JmeContext.Type; +import com.jme3.util.JmeFormatter; + +import java.io.InputStream; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; + +import java.net.URL; + +public class JmeSystemDelegateImpl implements JmeSystemDelegate { + + private static final Logger logger = Logger.getLogger(JmeSystemDelegateImpl.class.getName()); + private boolean initialized = false; + private boolean lowPermissions = false; + private static ThreadLocal app = new ThreadLocal(); + + public void initialize(AppSettings settings) { + if (initialized) { + return; + } + + initialized = true; + try { + JmeFormatter formatter = new JmeFormatter(); + +// Handler consoleHandler = new AndroidLogHandler(); +// consoleHandler.setFormatter(formatter); + } catch (SecurityException ex) { + logger.log(Level.SEVERE, "Security error in creating log file", ex); + } + logger.log(Level.INFO, "Running on {0}", getFullName()); + } + + public String getFullName() { + return "MikuMikuStudio gdx 1.0.0"; + } + + public void setLowPermissions(boolean lowPerm) { + lowPermissions = lowPerm; + } + + public boolean isLowPermissions() { + return lowPermissions; + } + + public JmeContext newContext(AppSettings settings, Type contextType) { + initialize(settings); + if (settings.getRenderer().startsWith("LiveWallpaper")) { + + } + return new GdxContext(); + } + + // TODO + public AudioRenderer newAudioRenderer(AppSettings settings) { + return new GdxAudioRenderer(); + } + +// public static void setResources(Resources res) { +// JmeSystem.res = res; +// } + +// public static Resources getResources() { +// return res; +// } + +// public static void setActivity(Context activity) { +// JmeSystem.activity = activity; +// +// } + public void setApplication(Application app) { + JmeSystem.app.set(app); + } + public Application getApplication() { + return app.get(); + } + +// public static Context getActivity() { +// return activity; +// } + + public AssetManager newAssetManager() { + logger.log(Level.INFO, "newAssetManager()"); + AssetManager am = new GdxAssetManager(); + + return am; + } + + public AssetManager newAssetManager(URL url) { + logger.log(Level.INFO, "newAssetManager({0})", url); + AssetManager am = new GdxAssetManager(); + + return am; + } + + public boolean showSettingsDialog(AppSettings settings, boolean loadSettings) { + return true; + } + + public Platform getPlatform() { + String arch = System.getProperty("os.arch").toLowerCase(); + if (arch.contains("arm")){ + if (arch.contains("v5")){ + return Platform.Android_ARM5; + }else if (arch.contains("v6")){ + return Platform.Android_ARM6; + }else if (arch.contains("v7")){ + return Platform.Android_ARM7; + }else{ + return Platform.Android_ARM5; // unknown ARM + } + }else{ + throw new UnsupportedOperationException("Unsupported Android Platform"); + } + } + public InputStream getResourceAsStream(String name) { + return JmeSystem.class.getResourceAsStream(name); + } + + public URL getResource(String name) { + return JmeSystem.class.getResource(name); + } +} diff --git a/gdx/src/main/java/com/jme3/system/gdx/GdxAssetCache.java b/gdx/src/main/java/com/jme3/system/gdx/GdxAssetCache.java new file mode 100644 index 000000000..2e0be6b2f --- /dev/null +++ b/gdx/src/main/java/com/jme3/system/gdx/GdxAssetCache.java @@ -0,0 +1,27 @@ +package com.jme3.system.gdx; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.files.FileHandle; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + * Created by kobayasi on 2013/12/29. + */ +public class GdxAssetCache { + public static FileHandle getFileHandle(String path) { + FileHandle fileHandle = Gdx.files.local("gdxtemp/"+path); + if (!fileHandle.exists()) { + InputStream is = GdxAssetCache.class.getClassLoader().getResourceAsStream(path); + fileHandle.write(is, false); + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return fileHandle; + } +} diff --git a/gdx/src/main/java/com/jme3/system/gdx/GdxAudioData.java b/gdx/src/main/java/com/jme3/system/gdx/GdxAudioData.java new file mode 100644 index 000000000..0341eba83 --- /dev/null +++ b/gdx/src/main/java/com/jme3/system/gdx/GdxAudioData.java @@ -0,0 +1,64 @@ +package com.jme3.system.gdx; + +import com.jme3.asset.AssetKey; +import com.jme3.audio.AudioData; +import com.jme3.audio.AudioRenderer; +import com.jme3.util.NativeObject; + +/** + * Created by kobayasi on 2013/12/29. + */ +public class GdxAudioData extends AudioData{ + protected AssetKey assetKey; + protected float currentVolume = 0f; + + public GdxAudioData(){ + super(); + } + + protected GdxAudioData(int id){ + super(id); + } + + public AssetKey getAssetKey() { + return assetKey; + } + + public void setAssetKey(AssetKey assetKey) { + this.assetKey = assetKey; + } + + @Override + public DataType getDataType() { + return DataType.Buffer; + } + + @Override + public float getDuration() { + return 0; // TODO: ??? + } + + @Override + public void resetObject() { + this.id = -1; + setUpdateNeeded(); + } + + @Override + public void deleteObject(Object rendererObject) { + ((AudioRenderer)rendererObject).deleteAudioData(this); + } + + public float getCurrentVolume() { + return currentVolume; + } + + public void setCurrentVolume(float currentVolume) { + this.currentVolume = currentVolume; + } + + @Override + public NativeObject createDestructableClone() { + return new GdxAudioData(id); + } +} diff --git a/gdx/src/main/java/com/jme3/system/gdx/GdxAudioLoader.java b/gdx/src/main/java/com/jme3/system/gdx/GdxAudioLoader.java new file mode 100644 index 000000000..62dcdcf8c --- /dev/null +++ b/gdx/src/main/java/com/jme3/system/gdx/GdxAudioLoader.java @@ -0,0 +1,26 @@ +package com.jme3.system.gdx; + +import com.jme3.asset.AssetInfo; +import com.jme3.asset.AssetLoader; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Created by kobayasi on 2013/12/29. + */ +public class GdxAudioLoader implements AssetLoader { + @Override + public Object load(AssetInfo assetInfo) throws IOException + { + + InputStream in = assetInfo.openStream(); + if (in != null) + { + in.close(); + } + GdxAudioData result = new GdxAudioData(); + result.setAssetKey( assetInfo.getKey() ); + return result; + } +} diff --git a/gdx/src/main/java/com/jme3/system/gdx/GdxAudioRenderer.java b/gdx/src/main/java/com/jme3/system/gdx/GdxAudioRenderer.java new file mode 100644 index 000000000..d13a482c2 --- /dev/null +++ b/gdx/src/main/java/com/jme3/system/gdx/GdxAudioRenderer.java @@ -0,0 +1,193 @@ +package com.jme3.system.gdx; + +import com.badlogic.gdx.Application; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.audio.Music; +import com.jme3.audio.*; + +import java.util.HashMap; + +/** + * Created by kobayasi on 2013/12/28. + */ +public class GdxAudioRenderer implements AudioRenderer{ + private boolean audioDisabled = false; + private Listener listener; + private final HashMap musicMap = new HashMap(); + @Override + public void setListener(Listener listener) { + Gdx.app.log("GdxAudioRenderer", "setListener"); + if (audioDisabled) { + return; + } + + if (this.listener != null) { + // previous listener no longer associated with current + // renderer + this.listener.setRenderer(null); + } + + this.listener = listener; + this.listener.setRenderer(this); + + } + @Override + public void setEnvironment(Environment environment) { + Gdx.app.log("GdxAudioRenderer", "setEnvironment"); + } + + @Override + public void playSourceInstance(AudioNode audioNode) { + Gdx.app.log("GdxAudioRenderer", "playSourceInstance"); + GdxAudioData audioData; + audioData = (GdxAudioData) audioNode.getAudioData(); + Music music = musicMap.get(audioNode); + if (Gdx.app.getType() == Application.ApplicationType.iOS && audioData.getAssetKey().getName().endsWith(".ogg")) { + return; + } + if (music == null) { + music = Gdx.audio.newMusic(GdxAssetCache.getFileHandle(audioData.getAssetKey().getName())); + musicMap.put(audioNode, music); + } + music.stop(); + music.play(); + audioNode.setStatus(AudioNode.Status.Playing); + } + + @Override + public void playSource(AudioNode audioNode) { + Gdx.app.log("GdxAudioRenderer", "playSource"); + if (audioNode.getStatus() == AudioNode.Status.Playing) { + stopSource(audioNode); + playSourceInstance(audioNode); + } else if (audioNode.getStatus() == AudioNode.Status.Stopped) { + playSourceInstance(audioNode); + } + } + + @Override + public void pauseSource(AudioNode src) { + Gdx.app.log("GdxAudioRenderer", "pauseSource"); + if (src.getStatus() == AudioNode.Status.Playing) { + if (src.getAudioData() instanceof GdxAudioData) { + GdxAudioData audioData = (GdxAudioData) src.getAudioData(); + if (audioData.getAssetKey() instanceof AudioKey) { + AudioKey assetKey = (AudioKey) audioData.getAssetKey(); + + if (assetKey.isStream()) { + Music mp; + if (musicMap.containsKey(src)) { + mp = musicMap.get(src); + mp.pause(); + src.setStatus(AudioNode.Status.Paused); + } + } else { + assert src.getChannel() != -1; + + if (src.getChannel() > 0) { +// soundPool.pause(src.getChannel()); + src.setStatus(AudioNode.Status.Paused); + } + } + } + } + + } + } + + @Override + public void stopSource(AudioNode src) { + Gdx.app.log("GdxAudioRenderer", "stopSource"); + if (src.getStatus() != AudioNode.Status.Stopped) { + if (src.getAudioData() instanceof GdxAudioData) { + GdxAudioData audioData = (GdxAudioData) src.getAudioData(); + if (audioData.getAssetKey() instanceof AudioKey) { + AudioKey assetKey = (AudioKey) audioData.getAssetKey(); + if (assetKey.isStream()) { + Music mp; + if (musicMap.containsKey(src)) { + mp = musicMap.get(src); + mp.stop(); + src.setStatus(AudioNode.Status.Stopped); + src.setChannel(-1); + } + } else { + int chan = src.getChannel(); + assert chan != -1; // if it's not stopped, must have id + + if (src.getChannel() > 0) { +// soundPool.stop(src.getChannel()); + src.setChannel(-1); + } + + src.setStatus(AudioNode.Status.Stopped); + + if (audioData.getId() > 0) { +// soundPool.unload(audioData.getId()); + } + audioData.setId(-1); + + + + } + } + } + + } + + } + + @Override + public void updateSourceParam(AudioNode audioNode, AudioParam audioParam) { + Gdx.app.log("GdxAudioRenderer", "updateSourceParam"); + } + + @Override + public void updateListenerParam(Listener listener, ListenerParam listenerParam) { + Gdx.app.log("GdxAudioRenderer", "updateListenerParam"); + + } + + @Override + public void deleteFilter(Filter filter) { + Gdx.app.log("GdxAudioRenderer", "deleteFilter"); + + } + + @Override + public void deleteAudioData(AudioData audioData) { + Gdx.app.log("GdxAudioRenderer", "deleteAudioData"); + + } + + @Override + public void initialize() { + Gdx.app.log("GdxAudioRenderer", "initialize"); + + } + + @Override + public void update(float v) { +// Gdx.app.log("GdxAudioRenderer", "update"); + for(AudioNode src : musicMap.keySet()) { + Music music = musicMap.get(src); + if (src.getStatus() == AudioNode.Status.Playing) { + if (!music.isPlaying()) { + Gdx.app.log("GdxAudioRenderer","music Stopped"); + src.setStatus(AudioNode.Status.Stopped); + } else { + } + } + } + + } + + @Override + public void cleanup() { + Gdx.app.log("GdxAudioRenderer", "cleanup"); + for(Music music : musicMap.values()) { + music.dispose(); + } + musicMap.clear(); + } +} diff --git a/gdx/src/main/java/com/jme3/system/gdx/GdxContext.java b/gdx/src/main/java/com/jme3/system/gdx/GdxContext.java new file mode 100644 index 000000000..a661e748c --- /dev/null +++ b/gdx/src/main/java/com/jme3/system/gdx/GdxContext.java @@ -0,0 +1,145 @@ +package com.jme3.system.gdx; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.input.GestureDetector; +import com.jme3.input.*; +import com.jme3.input.dummy.DummyKeyInput; +import com.jme3.input.dummy.DummyMouseInput; +import com.jme3.renderer.Renderer; +import com.jme3.renderer.gdx.GdxRenderer; +import com.jme3.system.AppSettings; +import com.jme3.system.JmeContext; +import com.jme3.system.SystemListener; +import com.jme3.system.Timer; + +/** + * Created with IntelliJ IDEA. + * User: kobayasi + * Date: 13/10/07 + * Time: 20:59 + * To change this template use File | Settings | File Templates. + */ +public class GdxContext implements JmeContext { + private SystemListener systemListener; + private AppSettings settings; + private GdxRenderer renderer; + private boolean created = false; + private boolean renderable = false; + private boolean autoFlush = true; + private GdxTimer timer; + private boolean needInitialize = true; + + public GdxContext() { + timer = new GdxTimer(); + } + + @Override + public Type getType() { + return Type.Display; + } + + @Override + public void setSettings(AppSettings appSettings) { + System.out.println("GdxContext.setSettings"); + if (appSettings == null) { + throw new RuntimeException("GdxContext.setSettings appSettings == null"); + } + this.settings = appSettings; + } + + @Override + public void setSystemListener(SystemListener systemListener) { + System.out.println("setSystemListener"); + this.systemListener = systemListener; + } + + @Override + public AppSettings getSettings() { + return settings; + } + + @Override + public Renderer getRenderer() { + return renderer; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public MouseInput getMouseInput() { + return new DummyMouseInput(); + } + + @Override + public KeyInput getKeyInput() { + return new DummyKeyInput(); + } + + @Override + public JoyInput getJoyInput() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public TouchInput getTouchInput() { + GdxInput input = new GdxInput(); + Gdx.input.setInputProcessor(new GestureDetector(input)); + return input; + //return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public Timer getTimer() { + return timer; + } + + @Override + public void setTitle(String s) { + } + + @Override + public boolean isCreated() { + return created; + } + + @Override + public boolean isRenderable() { + return renderable; + } + + @Override + public void setAutoFlushFrames(boolean b) { + this.autoFlush = b; + } + + @Override + public void create(boolean b) { + System.out.println("GdxContext.create()"); + renderer = new GdxRenderer(); + renderable = true; + created = true; + } + + @Override + public void restart() { + } + + @Override + public void destroy(boolean b) { + renderable = false; + } + + public void onDrawFrame() { + if (needInitialize) { + renderer.initialize(); + systemListener.initialize(); + needInitialize = false; + } + if (isRenderable()) { + if (systemListener != null) { + systemListener.update(); + } + if (autoFlush) { + renderer.onFrame(); + } + } + } +} diff --git a/gdx/src/main/java/com/jme3/system/gdx/GdxInput.java b/gdx/src/main/java/com/jme3/system/gdx/GdxInput.java new file mode 100644 index 000000000..2e382cc3a --- /dev/null +++ b/gdx/src/main/java/com/jme3/system/gdx/GdxInput.java @@ -0,0 +1,426 @@ +package com.jme3.system.gdx; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.InputProcessor; +import com.badlogic.gdx.input.GestureDetector; +import com.badlogic.gdx.math.Vector2; +import com.jme3.input.RawInputListener; +import com.jme3.input.TouchInput; +import com.jme3.input.event.MouseButtonEvent; +import com.jme3.input.event.MouseMotionEvent; +import com.jme3.input.event.TouchEvent; +import com.jme3.math.Vector2f; +import com.jme3.util.RingBuffer; + +import java.util.HashMap; +import java.util.logging.Logger; + +/** + * Created by kobayasi on 2013/12/27. + */ +public class GdxInput implements TouchInput, InputProcessor, GestureDetector.GestureListener { + private static final Logger logger = Logger.getLogger(GdxInput.class.getName()); + final private static int MAX_EVENTS = 1024; + + public boolean mouseEventsEnabled = true; + public boolean mouseEventsInvertX = false; + public boolean mouseEventsInvertY = false; + public boolean keyboardEventsEnabled = false; + public boolean dontSendHistory = false; + private RawInputListener listener = null; + final private RingBuffer eventQueue = new RingBuffer(MAX_EVENTS); + final private RingBuffer eventPoolUnConsumed = new RingBuffer(MAX_EVENTS); + final private RingBuffer eventPool = new RingBuffer(MAX_EVENTS); + final private HashMap lastPositions = new HashMap(); + private int lastX = -1; + private int lastY = -1; + private boolean isInitialized = false; + + @Override + public void setSimulateMouse(boolean b) { + mouseEventsEnabled = b; + } + + @Override + public void setSimulateKeyboard(boolean b) { + keyboardEventsEnabled = b; + } + + @Override + public void setOmitHistoricEvents(boolean b) { + dontSendHistory = b; + } + + @Override + public void initialize() { + TouchEvent item; + for (int i = 0; i < MAX_EVENTS; i++) + { + item = new TouchEvent(); + eventPool.push(item); + } + isInitialized = true; + } + + @Override + public void update() { + if (listener != null) { + TouchEvent event; + MouseButtonEvent btn; + int newX; + int newY; + while (!eventQueue.isEmpty()) { + synchronized (eventQueue) { + event = eventQueue.pop(); + } + if (event != null) { + listener.onTouchEvent(event); + if (mouseEventsEnabled) { + if (mouseEventsInvertX) + newX = Gdx.graphics.getWidth() - (int) event.getX(); + else + newX = (int) event.getX(); + + if (mouseEventsInvertY) + newY = Gdx.graphics.getHeight() - (int) event.getY(); + else + newY = (int) event.getY(); + switch (event.getType()) { + case DOWN: + // Handle mouse down event + btn = new MouseButtonEvent(0, true, newX, newY); + btn.setTime(event.getTime()); + listener.onMouseButtonEvent(btn); + // Store current pos + lastX = -1; + lastY = -1; + //lastX = newX; + //lastY = newY; +// System.err.println("DOWN x = " + btn.getX() + " y = " + btn.getY()); + break; + + case UP: + // Handle mouse up event + btn = new MouseButtonEvent(0, false, newX, newY); + btn.setTime(event.getTime()); + listener.onMouseButtonEvent(btn); + // Store current pos + lastX = -1; + lastY = -1; + //lastX = newX; + //lastY = newY; +// System.err.println("UP x = " + btn.getX() + " y = " + btn.getY()); + break; + + case MOVE: + int dx; + int dy; + if (lastX != -1) { + dx = newX - lastX; + dy = newY - lastY; + } else { + dx = 1; + dy = 0; + } + MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0); + mot.setTime(event.getTime()); + listener.onMouseMotionEvent(mot); + lastX = newX; + lastY = newY; + break; + } + } + } + if (event.isConsumed() == false) { + synchronized (eventPoolUnConsumed) { + try { + eventPoolUnConsumed.push(event); + } catch(Exception ex) { + ex.printStackTrace(); + } + } + + } else { + synchronized (eventPool) { + try { + eventPool.push(event); + } catch(Exception ex) { + ex.printStackTrace(); + } + } + } + } + } + + } + + @Override + public void destroy() { + + } + + @Override + public boolean isInitialized() { + return isInitialized; + } + + @Override + public void setInputListener(RawInputListener rawInputListener) { + this.listener = rawInputListener; + } + + @Override + public long getInputTimeNanos() { + return System.nanoTime(); + } + + private void processEvent(TouchEvent event) { + synchronized (eventQueue) { + try { + eventQueue.push(event); + } catch(Exception ex) { + ex.printStackTrace(); + } + } + } + + private TouchEvent getNextFreeTouchEvent() { + return getNextFreeTouchEvent(false); + } + private TouchEvent getNextFreeTouchEvent(boolean wait) + { + TouchEvent evt = null; + synchronized(eventPoolUnConsumed) + { + int size = eventPoolUnConsumed.size(); + while (size > 0) + { + evt = eventPoolUnConsumed.pop(); + if (!evt.isConsumed()) + { + eventPoolUnConsumed.push(evt); + evt = null; + } + else + { + break; + } + size--; + } + } + + + if (evt == null) + { + if (eventPool.isEmpty() && wait) + { + logger.warning("eventPool buffer underrun"); + boolean isEmpty; + do + { + synchronized(eventPool) + { + isEmpty = eventPool.isEmpty(); + } + try { Thread.sleep(50); } catch (InterruptedException e) { } + } + while (isEmpty); + synchronized(eventPool) + { + evt = eventPool.pop(); + } + } + else if (eventPool.isEmpty()) + { + evt = new TouchEvent(); + logger.warning("eventPool buffer underrun"); + } + else + { + synchronized(eventPool) + { + evt = eventPool.pop(); + } + } + } + return evt; + } + + // InputProcessor methods. + + @Override + public boolean keyDown(int keycode) { + return false; + } + + @Override + public boolean keyUp(int keycode) { + return false; + } + + @Override + public boolean keyTyped(char character) { + return false; + } + + @Override + public boolean touchDown(int screenX, int screenY, int pointer, int button) { + //mouseMoved2(screenX, screenY); + TouchEvent event = getNextFreeTouchEvent(); + //event.set(TouchEvent.Type.MOVE, screenX, Gdx.graphics.getHeight() - screenY, 0, 0); + //event.setPointerId(0); + //event.setTime(System.nanoTime()); + //processEvent(event); + + //event = getNextFreeTouchEvent(); + event.set(TouchEvent.Type.DOWN, screenX, Gdx.graphics.getHeight() - screenY, 0, 0); + event.setPointerId(0); + event.setTime(System.nanoTime()); + processEvent(event); + + Vector2f lastPos = lastPositions.get(pointer); + if (lastPos == null) + { + lastPos = new Vector2f(screenX, Gdx.graphics.getHeight() - screenY); + lastPositions.put(pointer, lastPos); + } + lastPos.set(screenX, Gdx.graphics.getHeight() - screenY); + + System.err.println("touchDown x = " + screenX + " y = " + screenY); + return false; + } + + @Override + public boolean touchUp(int screenX, int screenY, int pointer, int button) { + TouchEvent event = getNextFreeTouchEvent(); + event.set(TouchEvent.Type.UP, screenX, Gdx.graphics.getHeight() - screenY, 0, 0); + event.setPointerId(pointer); + event.setTime(System.nanoTime()); + processEvent(event); + System.err.println("touchUp x = " + screenX + " y = " + screenY); + return false; + } + + @Override + public boolean touchDragged(int screenX, int screenY, int pointer) { + if (screenX < 0 || screenY < 0) { + return false; + } + Vector2f lastPos = lastPositions.get(pointer); + if (lastPos == null) + { + lastPos = new Vector2f(screenX, Gdx.graphics.getHeight() - screenY); + lastPositions.put(pointer, lastPos); + } + TouchEvent event = getNextFreeTouchEvent(); + event.set(TouchEvent.Type.MOVE, screenX, Gdx.graphics.getHeight() - screenY, screenX - lastPos.x, Gdx.graphics.getHeight() - screenY - lastPos.y); + event.setPointerId(pointer); + event.setTime(System.nanoTime()); + processEvent(event); + System.err.println("touchDragged x = " + screenX + " y = " + screenY); + lastPos.set(screenX, Gdx.graphics.getHeight() - screenY); + return false; + } + + @Override + public boolean mouseMoved(int screenX, int screenY) { + return mouseMoved2(screenX, screenY); + } + public boolean mouseMoved2(int screenX, int screenY) { + if (screenX < 0 || screenY < 0) { + return false; + } + Vector2f lastPos = lastPositions.get(0); + if (lastPos == null) + { + lastPos = new Vector2f(screenX, Gdx.graphics.getHeight() - screenY); + lastPositions.put(0, lastPos); + } + + + TouchEvent event = getNextFreeTouchEvent(); + event.set(TouchEvent.Type.MOVE, screenX, Gdx.graphics.getHeight() - screenY, screenX - lastPos.x, Gdx.graphics.getHeight() - screenY - lastPos.y); + event.setPointerId(0); + event.setTime(System.nanoTime()); + processEvent(event); +// System.err.println("mouseMoved x = " + screenX + " y = " + screenY); + lastPos.set(screenX, Gdx.graphics.getHeight() - screenY); + return false; + } + + @Override + public boolean scrolled(int amount) { +// System.err.println("scrolled amount = " + amount); + return false; + } + public boolean isMouseEventsInvertY() { + return mouseEventsInvertY; + } + + public void setMouseEventsInvertY(boolean mouseEventsInvertY) { + this.mouseEventsInvertY = mouseEventsInvertY; + } + + public boolean isMouseEventsInvertX() { + return mouseEventsInvertX; + } + + public void setMouseEventsInvertX(boolean mouseEventsInvertX) { + this.mouseEventsInvertX = mouseEventsInvertX; + } + + // GestureListener + + @Override + public boolean touchDown(float x, float y, int pointer, int button) { + System.err.println("touchDown2"); + return touchDown((int)x, (int)y, pointer, button); + } + + @Override + public boolean tap(float x, float y, int count, int button) { + System.err.println("tap"); + return touchUp((int)x, (int)y, count, button); + } + + @Override + public boolean longPress(float x, float y) { + System.err.println("longPress"); + return false; + } + + @Override + public boolean fling(float velocityX, float velocityY, int button) { + System.err.println("fling"); + return false; + } + + @Override + public boolean pan(float x, float y, float deltaX, float deltaY) { + System.err.println("pan"); + return touchDragged((int)x, (int)y, 0); + } + + @Override + public boolean panStop(float x, float y, int pointer, int button) { + System.err.println("panStop"); + return touchUp((int)x,(int)y,pointer, button); + } + + @Override + public boolean zoom(float initialDistance, float distance) { + System.err.println("zoom "+initialDistance+" "+distance); + TouchEvent event = getNextFreeTouchEvent(); + event.set(TouchEvent.Type.SCALE_MOVE, initialDistance, distance, 0, 0); + event.setPointerId(0); + event.setTime(System.nanoTime()); + processEvent(event); + return false; + } + + @Override + public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) { + System.err.println("pinch"); + return false; + } +} diff --git a/gdx/src/main/java/com/jme3/system/gdx/GdxTimer.java b/gdx/src/main/java/com/jme3/system/gdx/GdxTimer.java new file mode 100644 index 000000000..225b4c165 --- /dev/null +++ b/gdx/src/main/java/com/jme3/system/gdx/GdxTimer.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2003-2009 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +package com.jme3.system.gdx; + +import com.jme3.system.Timer; + +/** + * GdxTimer is a System.nanoTime implementation of Timer. + */ +public class GdxTimer extends Timer { + + //private static final long TIMER_RESOLUTION = 1000L; + //private static final float INVERSE_TIMER_RESOLUTION = 1f/1000L; + private static final long TIMER_RESOLUTION = 1000000000L; + private static final float INVERSE_TIMER_RESOLUTION = 1f/1000000000L; + + private long startTime; + private float previousTime; + private float tpf; + private float fps; + + public GdxTimer() { + //startTime = System.currentTimeMillis(); + startTime = System.nanoTime(); + } + + /** + * Returns the time in seconds. The timer starts + * at 0.0 seconds. + * + * @return the current time in seconds + */ + @Override + public float getTimeInSeconds() { + return getTime() * INVERSE_TIMER_RESOLUTION; + } + + public long getTime() { + //return System.currentTimeMillis() - startTime; + return System.nanoTime() - startTime; + } + + public long getResolution() { + return TIMER_RESOLUTION; + } + + public float getFrameRate() { + return fps; + } + + public float getTimePerFrame() { + return tpf; + } + + public void update() { + long timeNow = getTime(); + tpf = (timeNow) * (1.0f / TIMER_RESOLUTION) - previousTime; + fps = 1.0f / tpf; + previousTime = previousTime += tpf; + } + + public void reset() { + //startTime = System.currentTimeMillis(); + startTime = System.nanoTime(); + previousTime = 0;//getTime(); + } +} diff --git a/gdx/src/main/java/com/jme3/texture/plugins/gdx/GdxImageLoader.java b/gdx/src/main/java/com/jme3/texture/plugins/gdx/GdxImageLoader.java new file mode 100644 index 000000000..24edda0a5 --- /dev/null +++ b/gdx/src/main/java/com/jme3/texture/plugins/gdx/GdxImageLoader.java @@ -0,0 +1,140 @@ +package com.jme3.texture.plugins.gdx; + +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.Pixmap; +import com.jme3.asset.AssetInfo; +import com.jme3.asset.AssetLoader; +import com.jme3.asset.AssetNotFoundException; +import com.jme3.math.FastMath; +import com.jme3.texture.Image; +import com.jme3.util.BufferUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; + +/** + * Created with IntelliJ IDEA. + * User: kobayasi + * Date: 13/10/10 + * Time: 23:54 + * To change this template use File | Settings | File Templates. + */ +public class GdxImageLoader implements AssetLoader{ + public GdxImageLoader() { + } + + @Override + public Object load(AssetInfo assetInfo) throws IOException { + Pixmap pixmap = null; + InputStream is = null; + + try { + is = assetInfo.openStream(); + byte[] buf = new byte[is.available()]; + System.err.println(assetInfo.getKey()+" buf length = "+buf.length); + int i = 0; + while ( i < buf.length) { + int len = is.read(buf, i, buf.length - i); + if (len < 0) { + throw new IOException(); + } + i += len; + } + is.close(); + is = null; + pixmap = new Pixmap(buf, 0, buf.length); + Image.Format format; + ByteBuffer bb = BufferUtils.clone(pixmap.getPixels()); + int height = pixmap.getHeight(); + int width = pixmap.getWidth(); + switch(pixmap.getGLFormat()) { + case GL20.GL_RGBA: + format = Image.Format.RGBA8; + ByteBuffer bb2 = pixmap.getPixels(); + for(int y = 0;y= w1 * h1) { + index = 0; + } + for(int i=0;i<4;i++) { + out.put((x + y * w2)*4+i,bb.get(index * 4 + i)); + } + } + } + return out; + } + private static ByteBuffer resize2(int w1,int h1, int w2, int h2, ByteBuffer bb) { +// w2 = w1; +// h2 = h1; + if (w1 == w2 && h1 == h2) { + //return buf; + } + ByteBuffer out = BufferUtils.createByteBuffer(w2 * h2 * 3); + float f1 = ((float)w1) / (float)w2; + float f2 = ((float)h1) / (float)h2; + for(int x = 0;x= w1 * h1) { + index = 0; + } + for(int i=0;i<3;i++) { + out.put((x + y * w2)*3+i,bb.get(index * 3 + i)); + } + } + } + return out; + } +} diff --git a/gdx/src/main/java/com/jme3/texture/plugins/gdx/GdxTGALoader.java b/gdx/src/main/java/com/jme3/texture/plugins/gdx/GdxTGALoader.java new file mode 100644 index 000000000..8e1662802 --- /dev/null +++ b/gdx/src/main/java/com/jme3/texture/plugins/gdx/GdxTGALoader.java @@ -0,0 +1,572 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +package com.jme3.texture.plugins.gdx; + +import com.jme3.math.FastMath; +import com.jme3.asset.AssetLoader; +import com.jme3.texture.Image; +import com.jme3.util.BufferUtils; +import com.jme3.asset.AssetInfo; +import com.jme3.asset.TextureKey; +import com.jme3.texture.Image.Format; +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; + +/** + * TextureManager provides static methods for building a + * Texture object. Typically, the information supplied is the + * filename and the texture properties. + * + * @author Mark Powell + * @author Joshua Slack - cleaned, commented, added ability to read 16bit true color and color-mapped TGAs. + * @author Kirill Vainer - ported to jME3 + * @version $Id: TGALoader.java 4131 2009-03-19 20:15:28Z blaine.dev $ + */ +public final class GdxTGALoader implements AssetLoader { + + // 0 - no image data in file + public static final int TYPE_NO_IMAGE = 0; + + // 1 - uncompressed, color-mapped image + public static final int TYPE_COLORMAPPED = 1; + + // 2 - uncompressed, true-color image + public static final int TYPE_TRUECOLOR = 2; + + // 3 - uncompressed, black and white image + public static final int TYPE_BLACKANDWHITE = 3; + + // 9 - run-length encoded, color-mapped image + public static final int TYPE_COLORMAPPED_RLE = 9; + + // 10 - run-length encoded, true-color image + public static final int TYPE_TRUECOLOR_RLE = 10; + + // 11 - run-length encoded, black and white image + public static final int TYPE_BLACKANDWHITE_RLE = 11; + + public Object load(AssetInfo info) throws IOException{ + if (!(info.getKey() instanceof TextureKey)) + throw new IllegalArgumentException("Texture assets must be loaded using a TextureKey"); + + boolean flip = ((TextureKey)info.getKey()).isFlipY(); + InputStream in = null; + try { + in = info.openStream(); + Image img = load(in, flip); + return img; + } finally { + if (in != null){ + in.close(); + } + } + } + + /** + * loadImage is a manual image loader which is entirely + * independent of AWT. OUT: RGB888 or RGBA8888 Image object + * + * @return Image object that contains the + * image, either as a RGB888 or RGBA8888 + * @param flip + * Flip the image vertically + * @param exp32 + * Add a dummy Alpha channel to 24b RGB image. + * @param fis + * InputStream of an uncompressed 24b RGB or 32b RGBA TGA + * @throws java.io.IOException + */ + public static Image load(InputStream in, boolean flip) throws IOException { + boolean flipH = false; + + // open a stream to the file + DataInputStream dis = new DataInputStream(new BufferedInputStream(in)); + + // ---------- Start Reading the TGA header ---------- // + // length of the image id (1 byte) + int idLength = dis.readUnsignedByte(); + + // Type of color map (if any) included with the image + // 0 - no color map data is included + // 1 - a color map is included + int colorMapType = dis.readUnsignedByte(); + + // Type of image being read: + int imageType = dis.readUnsignedByte(); + + // Read Color Map Specification (5 bytes) + // Index of first color map entry (if we want to use it, uncomment and remove extra read.) +// short cMapStart = flipEndian(dis.readShort()); + dis.readShort(); + // number of entries in the color map + short cMapLength = flipEndian(dis.readShort()); + // number of bits per color map entry + int cMapDepth = dis.readUnsignedByte(); + + // Read Image Specification (10 bytes) + // horizontal coordinate of lower left corner of image. (if we want to use it, uncomment and remove extra read.) +// int xOffset = flipEndian(dis.readShort()); + dis.readShort(); + // vertical coordinate of lower left corner of image. (if we want to use it, uncomment and remove extra read.) +// int yOffset = flipEndian(dis.readShort()); + dis.readShort(); + // width of image - in pixels + int width = flipEndian(dis.readShort()); + // height of image - in pixels + int height = flipEndian(dis.readShort()); + // bits per pixel in image. + int pixelDepth = dis.readUnsignedByte(); + int imageDescriptor = dis.readUnsignedByte(); + if ((imageDescriptor & 32) != 0) // bit 5 : if 1, flip top/bottom ordering + flip = !flip; + if ((imageDescriptor & 16) != 0) // bit 4 : if 1, flip left/right ordering + flipH = !flipH; + + // ---------- Done Reading the TGA header ---------- // + + // Skip image ID + if (idLength > 0) + in.skip(idLength); + + ColorMapEntry[] cMapEntries = null; + if (colorMapType != 0) { + // read the color map. + int bytesInColorMap = (cMapDepth * cMapLength) >> 3; + int bitsPerColor = Math.min(cMapDepth/3 , 8); + + byte[] cMapData = new byte[bytesInColorMap]; + in.read(cMapData); + + // Only go to the trouble of constructing the color map + // table if this is declared a color mapped image. + if (imageType == TYPE_COLORMAPPED || imageType == TYPE_COLORMAPPED_RLE) { + cMapEntries = new ColorMapEntry[cMapLength]; + int alphaSize = cMapDepth - (3*bitsPerColor); + float scalar = 255f / (FastMath.pow(2, bitsPerColor) - 1); + float alphaScalar = 255f / (FastMath.pow(2, alphaSize) - 1); + for (int i = 0; i < cMapLength; i++) { + ColorMapEntry entry = new ColorMapEntry(); + int offset = cMapDepth * i; + entry.red = (byte)(int)(getBitsAsByte(cMapData, offset, bitsPerColor) * scalar); + entry.green = (byte)(int)(getBitsAsByte(cMapData, offset+bitsPerColor, bitsPerColor) * scalar); + entry.blue = (byte)(int)(getBitsAsByte(cMapData, offset+(2*bitsPerColor), bitsPerColor) * scalar); + if (alphaSize <= 0) + entry.alpha = (byte)255; + else + entry.alpha = (byte)(int)(getBitsAsByte(cMapData, offset+(3*bitsPerColor), alphaSize) * alphaScalar); + + cMapEntries[i] = entry; + } + } + } + + + // Allocate image data array + Format format; + byte[] rawData = null; + int dl; + if (pixelDepth == 32) { + rawData = new byte[width * height * 4]; + dl = 4; + } else { + rawData = new byte[width * height * 3]; + dl = 3; + } + int rawDataIndex = 0; + + if (imageType == TYPE_TRUECOLOR) { + byte red = 0; + byte green = 0; + byte blue = 0; + byte alpha = 0; + + // Faster than doing a 16-or-24-or-32 check on each individual pixel, + // just make a seperate loop for each. + if (pixelDepth == 16) { + byte[] data = new byte[2]; + float scalar = 255f/31f; + for (int i = 0; i <= (height - 1); i++) { + if (!flip) + rawDataIndex = (height - 1 - i) * width * dl; + for (int j = 0; j < width; j++) { + data[1] = dis.readByte(); + data[0] = dis.readByte(); + rawData[rawDataIndex++] = (byte)(int)(getBitsAsByte(data, 1, 5) * scalar); + rawData[rawDataIndex++] = (byte)(int)(getBitsAsByte(data, 6, 5) * scalar); + rawData[rawDataIndex++] = (byte)(int)(getBitsAsByte(data, 11, 5) * scalar); + if (dl == 4) { + // create an alpha channel + alpha = getBitsAsByte(data, 0, 1); + if (alpha == 1) alpha = (byte)255; + rawData[rawDataIndex++] = alpha; + } else { + rawData[rawDataIndex++] = (byte)0xff; + } + } + } + + format = dl == 4 ? Format.RGBA8 : Format.RGB8; + } else if (pixelDepth == 24){ + for (int y = 0; y < height; y++) { + if (!flip) + rawDataIndex = (height - 1 - y) * width * dl; + else + rawDataIndex = y * width * dl; + +// dis.readFully(rawData, rawDataIndex, width * dl); + for (int x = 0; x < width; x++) { +// read scanline + blue = dis.readByte(); + green = dis.readByte(); + red = dis.readByte(); + rawData[rawDataIndex++] = red; + rawData[rawDataIndex++] = green; + rawData[rawDataIndex++] = blue; + } + } + format = Format.RGB8; + } else if (pixelDepth == 32){ + for (int i = 0; i <= (height - 1); i++) { + if (!flip) + rawDataIndex = (height - 1 - i) * width * dl; + + for (int j = 0; j < width; j++) { + blue = dis.readByte(); + green = dis.readByte(); + red = dis.readByte(); + alpha = dis.readByte(); + rawData[rawDataIndex++] = red; + rawData[rawDataIndex++] = green; + rawData[rawDataIndex++] = blue; + rawData[rawDataIndex++] = alpha; + } + } + format = Format.RGBA8; + }else{ + throw new IOException("Unsupported TGA true color depth: "+pixelDepth); + } + } else if( imageType == TYPE_TRUECOLOR_RLE ) { + byte red = 0; + byte green = 0; + byte blue = 0; + byte alpha = 0; + // Faster than doing a 16-or-24-or-32 check on each individual pixel, + // just make a seperate loop for each. + if( pixelDepth == 32 ){ + for( int i = 0; i <= ( height - 1 ); ++i ){ + if( !flip ){ + rawDataIndex = ( height - 1 - i ) * width * dl; + } + + for( int j = 0; j < width; ++j ){ + // Get the number of pixels the next chunk covers (either packed or unpacked) + int count = dis.readByte(); + if( ( count & 0x80 ) != 0 ){ + // Its an RLE packed block - use the following 1 pixel for the next pixels + count &= 0x07f; + j += count; + blue = dis.readByte(); + green = dis.readByte(); + red = dis.readByte(); + alpha = dis.readByte(); + while( count-- >= 0 ){ + rawData[rawDataIndex++] = red; + rawData[rawDataIndex++] = green; + rawData[rawDataIndex++] = blue; + rawData[rawDataIndex++] = alpha; + } + } else{ + // Its not RLE packed, but the next pixels are raw. + j += count; + while( count-- >= 0 ){ + blue = dis.readByte(); + green = dis.readByte(); + red = dis.readByte(); + alpha = dis.readByte(); + rawData[rawDataIndex++] = red; + rawData[rawDataIndex++] = green; + rawData[rawDataIndex++] = blue; + rawData[rawDataIndex++] = alpha; + } + } + } + } + format = Format.RGBA8; + } else if( pixelDepth == 24 ){ + for( int i = 0; i <= ( height - 1 ); i++ ){ + if( !flip ){ + rawDataIndex = ( height - 1 - i ) * width * dl; + } + for( int j = 0; j < width; ++j ){ + // Get the number of pixels the next chunk covers (either packed or unpacked) + int count = dis.readByte(); + if( ( count & 0x80 ) != 0 ){ + // Its an RLE packed block - use the following 1 pixel for the next pixels + count &= 0x07f; + j += count; + blue = dis.readByte(); + green = dis.readByte(); + red = dis.readByte(); + while( count-- >= 0 ){ + rawData[rawDataIndex++] = red; + rawData[rawDataIndex++] = green; + rawData[rawDataIndex++] = blue; + } + } else{ + // Its not RLE packed, but the next pixels are raw. + j += count; + while( count-- >= 0 ){ + blue = dis.readByte(); + green = dis.readByte(); + red = dis.readByte(); + rawData[rawDataIndex++] = red; + rawData[rawDataIndex++] = green; + rawData[rawDataIndex++] = blue; + } + } + } + } + format = Format.RGB8; + } else if( pixelDepth == 16 ){ + byte[] data = new byte[ 2 ]; + float scalar = 255f / 31f; + for( int i = 0; i <= ( height - 1 ); i++ ){ + if( !flip ){ + rawDataIndex = ( height - 1 - i ) * width * dl; + } + for( int j = 0; j < width; j++ ){ + // Get the number of pixels the next chunk covers (either packed or unpacked) + int count = dis.readByte(); + if( ( count & 0x80 ) != 0 ){ + // Its an RLE packed block - use the following 1 pixel for the next pixels + count &= 0x07f; + j += count; + data[1] = dis.readByte(); + data[0] = dis.readByte(); + blue = (byte) (int) ( getBitsAsByte( data, 1, 5 ) * scalar ); + green = (byte) (int) ( getBitsAsByte( data, 6, 5 ) * scalar ); + red = (byte) (int) ( getBitsAsByte( data, 11, 5 ) * scalar ); + while( count-- >= 0 ){ + rawData[rawDataIndex++] = red; + rawData[rawDataIndex++] = green; + rawData[rawDataIndex++] = blue; + } + } else{ + // Its not RLE packed, but the next pixels are raw. + j += count; + while( count-- >= 0 ){ + data[1] = dis.readByte(); + data[0] = dis.readByte(); + blue = (byte) (int) ( getBitsAsByte( data, 1, 5 ) * scalar ); + green = (byte) (int) ( getBitsAsByte( data, 6, 5 ) * scalar ); + red = (byte) (int) ( getBitsAsByte( data, 11, 5 ) * scalar ); + rawData[rawDataIndex++] = red; + rawData[rawDataIndex++] = green; + rawData[rawDataIndex++] = blue; + } + } + } + } + format = Format.RGB8; + } else{ + throw new IOException( "Unsupported TGA true color depth: " + pixelDepth ); + } + + } else if( imageType == TYPE_COLORMAPPED ){ + int bytesPerIndex = pixelDepth / 8; + + if (bytesPerIndex == 1) { + for (int i = 0; i <= (height - 1); i++) { + if (!flip) + rawDataIndex = (height - 1 - i) * width * dl; + for (int j = 0; j < width; j++) { + int index = dis.readUnsignedByte(); + if (index >= cMapEntries.length || index < 0) + throw new IOException("TGA: Invalid color map entry referenced: "+index); + + ColorMapEntry entry = cMapEntries[index]; + rawData[rawDataIndex++] = entry.red; + rawData[rawDataIndex++] = entry.green; + rawData[rawDataIndex++] = entry.blue; + if (dl == 4) { + rawData[rawDataIndex++] = entry.alpha; + } else { + rawData[rawDataIndex++] = (byte)0xff; + } + } + } + } else if (bytesPerIndex == 2) { + for (int i = 0; i <= (height - 1); i++) { + if (!flip) + rawDataIndex = (height - 1 - i) * width * dl; + for (int j = 0; j < width; j++) { + int index = flipEndian(dis.readShort()); + if (index >= cMapEntries.length || index < 0) + throw new IOException("TGA: Invalid color map entry referenced: "+index); + + ColorMapEntry entry = cMapEntries[index]; + rawData[rawDataIndex++] = entry.red; + rawData[rawDataIndex++] = entry.green; + rawData[rawDataIndex++] = entry.blue; + if (dl == 4) { + rawData[rawDataIndex++] = entry.alpha; + } else { + rawData[rawDataIndex++] = (byte)0xff; + } + } + } + } else { + throw new IOException("TGA: unknown colormap indexing size used: "+bytesPerIndex); + } + + format = dl == 4 ? Format.RGBA8 : Format.RGB8; + } else { + throw new IOException("Grayscale TGA not supported"); + } + + + in.close(); + Image textureImage = new Image(); + textureImage.setFormat(format); + textureImage.setWidth(FastMath.nearestPowerOfTwo(width)); + textureImage.setHeight(FastMath.nearestPowerOfTwo(height)); + if (format != Format.RGBA8) { + rawData = resize2(width, height, FastMath.nearestPowerOfTwo(width), FastMath.nearestPowerOfTwo(height), rawData); + //textureImage.setFormat(Format.RGBA8); + } else { + rawData = resize(width, height, FastMath.nearestPowerOfTwo(width), FastMath.nearestPowerOfTwo(height), rawData); + } + ByteBuffer bb = BufferUtils.createByteBuffer(rawData.length); + for(int i=0;i= w1 * h1) { + index = 0; + } + for(int i=0;i<4;i++) { + out[(x + y * w2)*4+i] = buf[index * 4 + i]; + } + } + } + return out; + } + private static byte[] resize2(int w1,int h1, int w2, int h2, byte[] buf) { +// w2 = w1; +// h2 = h1; + if (w1 == w2 && h1 == h2) { + //return buf; + } + byte[] out = new byte[w2 * h2 * 3]; + float f1 = ((float)w1) / (float)w2; + float f2 = ((float)h1) / (float)h2; + for(int x = 0;x= w1 * h1) { + index = 0; + } + for(int i=0;i<3;i++) { + out[(x + y * w2)*3+i] = buf[index * 3 + i]; + } + } + } + return out; + } +} diff --git a/gdx/src/main/java/com/jme3/util/RingBuffer.java b/gdx/src/main/java/com/jme3/util/RingBuffer.java new file mode 100644 index 000000000..786417b04 --- /dev/null +++ b/gdx/src/main/java/com/jme3/util/RingBuffer.java @@ -0,0 +1,75 @@ +package com.jme3.util; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * Ring buffer (fixed size queue) implementation using a circular array (array with wrap-around). + */ +// suppress unchecked warnings in Java 1.5.0_6 and later +@SuppressWarnings("unchecked") +public class RingBuffer implements Iterable { + + private Item[] buffer; // queue elements + private int count = 0; // number of elements on queue + private int indexOut = 0; // index of first element of queue + private int indexIn = 0; // index of next available slot + + // cast needed since no generic array creation in Java + public RingBuffer(int capacity) { + buffer = (Item[]) new Object[capacity]; + } + + public boolean isEmpty() { + return count == 0; + } + + public int size() { + return count; + } + + public void push(Item item) { + if (count == buffer.length) { + throw new RuntimeException("Ring buffer overflow"); + } + buffer[indexIn] = item; + indexIn = (indexIn + 1) % buffer.length; // wrap-around + count++; + } + + public Item pop() { + if (isEmpty()) { + throw new RuntimeException("Ring buffer underflow"); + } + Item item = buffer[indexOut]; + buffer[indexOut] = null; // to help with garbage collection + count--; + indexOut = (indexOut + 1) % buffer.length; // wrap-around + return item; + } + + public Iterator iterator() { + return new RingBufferIterator(); + } + + // an iterator, doesn't implement remove() since it's optional + private class RingBufferIterator implements Iterator { + + private int i = 0; + + public boolean hasNext() { + return i < count; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public Item next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return buffer[i++]; + } + } +} diff --git a/gdx/src/main/resources/asset/Desktop.cfg b/gdx/src/main/resources/asset/Desktop.cfg new file mode 100644 index 000000000..fd3541e89 --- /dev/null +++ b/gdx/src/main/resources/asset/Desktop.cfg @@ -0,0 +1,22 @@ +LOCATOR / com.jme3.asset.plugins.ClasspathLocator + +LOADER com.jme3.audio.plugins.WAVLoader : wav +LOADER com.jme3.audio.plugins.OGGLoader : ogg +LOADER com.jme3.material.plugins.J3MLoader : j3m +LOADER com.jme3.material.plugins.J3MLoader : j3md +LOADER com.jme3.font.plugins.BitmapFontLoader : fnt +LOADER com.jme3.texture.plugins.DDSLoader : dds +LOADER com.jme3.texture.plugins.PFMLoader : pfm +LOADER com.jme3.texture.plugins.HDRLoader : hdr +LOADER com.jme3.texture.plugins.TGALoader : tga +LOADER com.jme3.export.binary.BinaryImporter : j3o +LOADER com.jme3.export.binary.BinaryImporter : j3f +LOADER com.jme3.scene.plugins.OBJLoader : obj +LOADER com.jme3.scene.plugins.MTLLoader : mtl +LOADER com.jme3.scene.plugins.ogre.MeshLoader : meshxml, mesh.xml +LOADER com.jme3.scene.plugins.ogre.SkeletonLoader : skeletonxml, skeleton.xml +LOADER com.jme3.scene.plugins.ogre.MaterialLoader : material +LOADER com.jme3.scene.plugins.ogre.SceneLoader : scene +LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, glsl, glsllib +LOADER projectkyoto.jme3.mmd.PMDLoaderGLSLSkinning2 : pmd +LOADER projectkyoto.jme3.mmd.VMDLoader : vmd \ No newline at end of file -- 2.11.0