Renderer
is responsible for taking rendering commands and
+ * executing them on the underlying video hardware.
+ *
+ * @author Kirill Vainer
+ */
+public class IGLESShaderRenderer implements Renderer {
+
+ private static final Logger logger = Logger.getLogger(IGLESShaderRenderer.class.getName());
+ private static final boolean VALIDATE_SHADER = false;
+
+ private final NativeObjectManager objManager = new NativeObjectManager();
+ private final EnumSetnull
to disable lighting.
+ *
+ * @param lights The light list to set.
+ */
+ public void setLighting(LightList lights) {
+ logger.log(Level.FINE, "IGLESShaderRenderer setLighting");
+ }
+
+ /**
+ * Sets the shader to use for rendering.
+ * If the shader has not been uploaded yet, it is compiled
+ * and linked. If it has been uploaded, then the
+ * uniform data is updated and the shader is set.
+ *
+ * @param shader The shader to use for rendering.
+ */
+ public void setShader(Shader shader) {
+ logger.log(Level.FINE, "IGLESShaderRenderer setShader");
+ if (shader == null) {
+ throw new IllegalArgumentException("Shader cannot be null");
+ } else {
+ if (shader.isUpdateNeeded()) {
+ updateShaderData(shader);
+ }
+
+ // NOTE: might want to check if any of the
+ // sources need an update?
+
+ assert shader.getId() > 0;
+
+ updateShaderUniforms(shader);
+ bindProgram(shader);
+ }
+ }
+
+ /**
+ * Deletes a shader. This method also deletes
+ * the attached shader sources.
+ *
+ * @param shader Shader to delete.
+ */
+ public void deleteShader(Shader shader) {
+ logger.log(Level.FINE, "IGLESShaderRenderer deleteShader");
+ if (shader.getId() == -1) {
+ logger.warning("Shader is not uploaded to GPU, cannot delete.");
+ return;
+ }
+
+ for (ShaderSource source : shader.getSources()) {
+ if (source.getId() != -1) {
+ JmeIosGLES.glDetachShader(shader.getId(), source.getId());
+ JmeIosGLES.checkGLError();
+
+ deleteShaderSource(source);
+ }
+ }
+
+ JmeIosGLES.glDeleteProgram(shader.getId());
+ JmeIosGLES.checkGLError();
+
+ statistics.onDeleteShader();
+ shader.resetObject();
+ }
+
+ /**
+ * Deletes the provided shader source.
+ *
+ * @param source The ShaderSource to delete.
+ */
+ public void deleteShaderSource(ShaderSource source) {
+ logger.log(Level.FINE, "IGLESShaderRenderer deleteShaderSource");
+ if (source.getId() < 0) {
+ logger.warning("Shader source is not uploaded to GPU, cannot delete.");
+ return;
+ }
+
+ source.clearUpdateNeeded();
+
+ JmeIosGLES.glDeleteShader(source.getId());
+ JmeIosGLES.checkGLError();
+
+ source.resetObject();
+ }
+
+ /**
+ * Copies contents from src to dst, scaling if necessary.
+ */
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) {
+ logger.log(Level.FINE, "IGLESShaderRenderer copyFrameBuffer");
+ copyFrameBuffer(src, dst, true);
+ }
+
+ /**
+ * Copies contents from src to dst, scaling if necessary.
+ * set copyDepth to false to only copy the color buffers.
+ */
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) {
+ logger.warning("IGLESShaderRenderer copyFrameBuffer with depth TODO");
+ throw new RendererException("Copy framebuffer not implemented yet.");
+ }
+
+ /**
+ * Sets the framebuffer that will be drawn to.
+ */
+ public void setFrameBuffer(FrameBuffer fb) {
+ logger.log(Level.FINE, "IGLESShaderRenderer setFrameBuffer");
+ 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());
+ JmeIosGLES.glGenerateMipmap(textureType);
+ JmeIosGLES.checkGLError();
+ }
+ }
+ }
+
+ if (fb == null) {
+ // unbind any fbos
+ if (context.boundFBO != 0) {
+ JmeIosGLES.glBindFramebuffer(JmeIosGLES.GL_FRAMEBUFFER, 0);
+ JmeIosGLES.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()) {
+ JmeIosGLES.glBindFramebuffer(JmeIosGLES.GL_FRAMEBUFFER, fb.getId());
+ JmeIosGLES.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 < 16; i++) {
+ intBuf16[i] = i < fb.getNumColorBuffers() ? JmeIosGLES.GL_COLOR_ATTACHMENT0 + i : 0;
+ }
+
+ //intBuf16.flip();// TODO: flip
+// glDrawBuffers(intBuf16);
+ context.boundDrawBuf = 100 + fb.getNumColorBuffers();
+ }
+ } else {
+ RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex());
+ // select this draw buffer
+ if (context.boundDrawBuf != rb.getSlot()) {
+ JmeIosGLES.glActiveTexture(convertAttachmentSlot(rb.getSlot()));
+ JmeIosGLES.checkGLError();
+
+ context.boundDrawBuf = rb.getSlot();
+ }
+ }
+ }
+
+ assert fb.getId() >= 0;
+ assert context.boundFBO == fb.getId();
+
+ lastFb = fb;
+
+ checkFrameBufferStatus(fb);
+ }
+ }
+
+ /**
+ * Set the framebuffer that will be set instead of the main framebuffer
+ * when a call to setFrameBuffer(null) is made.
+ *
+ * @param fb
+ */
+ public void setMainFrameBufferOverride(FrameBuffer fb) {
+ logger.log(Level.FINE, "IGLESShaderRenderer setMainFrameBufferOverride");
+ mainFbOverride = fb;
+ }
+
+ /**
+ * Reads the pixels currently stored in the specified framebuffer
+ * into the given ByteBuffer object.
+ * Only color pixels are transferred, the format is BGRA with 8 bits
+ * per component. The given byte buffer should have at least
+ * fb.getWidth() * fb.getHeight() * 4 bytes remaining.
+ *
+ * @param fb The framebuffer to read from
+ * @param byteBuf The bytebuffer to transfer color data to
+ */
+ public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
+ logger.log(Level.FINE, "IGLESShaderRenderer readFrameBuffer");
+ 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);
+ }
+
+ //JmeIosGLES.glReadPixels2(vpX, vpY, vpW, vpH, JmeIosGLES.GL_RGBA, JmeIosGLES.GL_UNSIGNED_BYTE, byteBuf.array(), 0, vpW * vpH * 4);
+ JmeIosGLES.glReadPixels(vpX, vpY, vpW, vpH, JmeIosGLES.GL_RGBA, JmeIosGLES.GL_UNSIGNED_BYTE, byteBuf);
+ JmeIosGLES.checkGLError();
+ }
+
+ /**
+ * Deletes a framebuffer and all attached renderbuffers
+ */
+ public void deleteFrameBuffer(FrameBuffer fb) {
+ logger.log(Level.FINE, "IGLESShaderRenderer deleteFrameBuffer");
+ if (fb.getId() != -1) {
+ if (context.boundFBO == fb.getId()) {
+ JmeIosGLES.glBindFramebuffer(JmeIosGLES.GL_FRAMEBUFFER, 0);
+ JmeIosGLES.checkGLError();
+
+ context.boundFBO = 0;
+ }
+
+ if (fb.getDepthBuffer() != null) {
+ deleteRenderBuffer(fb, fb.getDepthBuffer());
+ }
+ if (fb.getColorBuffer() != null) {
+ deleteRenderBuffer(fb, fb.getColorBuffer());
+ }
+
+ intBuf1[0] = fb.getId();
+ JmeIosGLES.glDeleteFramebuffers(1, intBuf1, 0);
+ JmeIosGLES.checkGLError();
+
+ fb.resetObject();
+
+ statistics.onDeleteFrameBuffer();
+ }
+ }
+
+ /**
+ * Sets the texture to use for the given texture unit.
+ */
+ public void setTexture(int unit, Texture tex) {
+ logger.log(Level.FINE, "IGLESShaderRenderer setTexture");
+ Image image = tex.getImage();
+ if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated()) ) {
+ updateTexImageData(image, tex.getType());
+ }
+
+ 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 (textures[unit] != image) {
+ if (context.boundTextureUnit != unit) {
+ JmeIosGLES.glActiveTexture(JmeIosGLES.GL_TEXTURE0 + unit);
+ context.boundTextureUnit = unit;
+ }
+
+ JmeIosGLES.glBindTexture(type, texId);
+ JmeIosGLES.checkGLError();
+
+ textures[unit] = image;
+
+ statistics.onTextureUse(tex.getImage(), true);
+ } else {
+ statistics.onTextureUse(tex.getImage(), false);
+ }
+
+ setupTextureParams(tex);
+ }
+
+ /**
+ * Modify the given Texture tex with the given Image. The image will be put at x and y into the texture.
+ *
+ * @param tex the Texture that will be modified
+ * @param pixels the source Image data to copy data from
+ * @param x the x position to put the image into the texture
+ * @param y the y position to put the image into the texture
+ */
+ public void modifyTexture(Texture tex, Image pixels, int x, int y) {
+ logger.log(Level.FINE, "IGLESShaderRenderer modifyTexture");
+ setTexture(0, tex);
+ TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType()), 0, x, y);
+ }
+
+ /**
+ * Deletes a texture from the GPU.
+ */
+ public void deleteImage(Image image) {
+ logger.log(Level.FINE, "IGLESShaderRenderer deleteImage");
+ int texId = image.getId();
+ if (texId != -1) {
+ intBuf1[0] = texId;
+
+ JmeIosGLES.glDeleteTextures(1, intBuf1, 0);
+ JmeIosGLES.checkGLError();
+
+ image.resetObject();
+
+ statistics.onDeleteTexture();
+ }
+ }
+
+ /**
+ * Uploads a vertex buffer to the GPU.
+ *
+ * @param vb The vertex buffer to upload
+ */
+ public void updateBufferData(VertexBuffer vb) {
+ logger.log(Level.FINE, "IGLESShaderRenderer updateBufferData");
+ int bufId = vb.getId();
+ boolean created = false;
+ if (bufId == -1) {
+ // create buffer
+ JmeIosGLES.glGenBuffers(1, intBuf1, 0);
+ JmeIosGLES.checkGLError();
+
+ bufId = intBuf1[0];
+ vb.setId(bufId);
+ objManager.registerObject(vb);
+
+ created = true;
+ }
+
+ // bind buffer
+ int target;
+ if (vb.getBufferType() == VertexBuffer.Type.Index) {
+ target = JmeIosGLES.GL_ELEMENT_ARRAY_BUFFER;
+ if (context.boundElementArrayVBO != bufId) {
+ JmeIosGLES.glBindBuffer(target, bufId);
+ JmeIosGLES.checkGLError();
+
+ context.boundElementArrayVBO = bufId;
+ }
+ } else {
+ target = JmeIosGLES.GL_ARRAY_BUFFER;
+ if (context.boundArrayVBO != bufId) {
+ JmeIosGLES.glBindBuffer(target, bufId);
+ JmeIosGLES.checkGLError();
+
+ context.boundArrayVBO = bufId;
+ }
+ }
+
+ int usage = convertUsage(vb.getUsage());
+ vb.getData().rewind();
+
+ if (created || vb.hasDataSizeChanged()) {
+ // upload data based on format
+ int size = vb.getData().limit() * vb.getFormat().getComponentSize();
+
+ switch (vb.getFormat()) {
+ case Byte:
+ case UnsignedByte:
+ JmeIosGLES.glBufferData(target, size, (ByteBuffer) vb.getData(), usage);
+ JmeIosGLES.checkGLError();
+ break;
+ case Short:
+ case UnsignedShort:
+ JmeIosGLES.glBufferData(target, size, (ShortBuffer) vb.getData(), usage);
+ JmeIosGLES.checkGLError();
+ break;
+ case Int:
+ case UnsignedInt:
+ JmeIosGLES.glBufferData(target, size, (IntBuffer) vb.getData(), usage);
+ JmeIosGLES.checkGLError();
+ break;
+ case Float:
+ JmeIosGLES.glBufferData(target, size, (FloatBuffer) vb.getData(), usage);
+ JmeIosGLES.checkGLError();
+ break;
+ default:
+ throw new RuntimeException("Unknown buffer format.");
+ }
+ } else {
+ int size = vb.getData().limit() * vb.getFormat().getComponentSize();
+
+ switch (vb.getFormat()) {
+ case Byte:
+ case UnsignedByte:
+ JmeIosGLES.glBufferSubData(target, 0, size, (ByteBuffer) vb.getData());
+ JmeIosGLES.checkGLError();
+ break;
+ case Short:
+ case UnsignedShort:
+ JmeIosGLES.glBufferSubData(target, 0, size, (ShortBuffer) vb.getData());
+ JmeIosGLES.checkGLError();
+ break;
+ case Int:
+ case UnsignedInt:
+ JmeIosGLES.glBufferSubData(target, 0, size, (IntBuffer) vb.getData());
+ JmeIosGLES.checkGLError();
+ break;
+ case Float:
+ JmeIosGLES.glBufferSubData(target, 0, size, (FloatBuffer) vb.getData());
+ JmeIosGLES.checkGLError();
+ break;
+ default:
+ throw new RuntimeException("Unknown buffer format.");
+ }
+ }
+ vb.clearUpdateNeeded();
+ }
+
+ /**
+ * Deletes a vertex buffer from the GPU.
+ * @param vb The vertex buffer to delete
+ */
+ public void deleteBuffer(VertexBuffer vb) {
+ logger.log(Level.FINE, "IGLESShaderRenderer deleteBuffer");
+ int bufId = vb.getId();
+ if (bufId != -1) {
+ // delete buffer
+ intBuf1[0] = bufId;
+
+ JmeIosGLES.glDeleteBuffers(1, intBuf1, 0);
+ JmeIosGLES.checkGLError();
+
+ vb.resetObject();
+ }
+ }
+
+ /**
+ * Renders count
meshes, with the geometry data supplied.
+ * The shader which is currently set with setShader
is
+ * responsible for transforming the input verticies into clip space
+ * and shading it based on the given vertex attributes.
+ * The int variable gl_InstanceID can be used to access the current
+ * instance of the mesh being rendered inside the vertex shader.
+ *
+ * @param mesh The mesh to render
+ * @param lod The LOD level to use, see {@link Mesh#setLodLevels(com.jme3.scene.VertexBuffer[]) }.
+ * @param count Number of mesh instances to render
+ */
+ public void renderMesh(Mesh mesh, int lod, int count) {
+ logger.log(Level.FINE, "IGLESShaderRenderer renderMesh");
+ if (mesh.getVertexCount() == 0) {
+ return;
+ }
+ /*
+ * NOTE: not supported in OpenGL ES 2.0.
+ if (context.pointSize != mesh.getPointSize()) {
+ GLES10.glPointSize(mesh.getPointSize());
+ context.pointSize = mesh.getPointSize();
+ }
+ */
+ if (context.lineWidth != mesh.getLineWidth()) {
+ JmeIosGLES.glLineWidth(mesh.getLineWidth());
+ JmeIosGLES.checkGLError();
+ context.lineWidth = mesh.getLineWidth();
+ }
+
+ statistics.onMeshDrawn(mesh, lod);
+// if (GLContext.getCapabilities().GL_ARB_vertex_array_object){
+// renderMeshVertexArray(mesh, lod, count);
+// }else{
+
+ if (useVBO) {
+ renderMeshDefault(mesh, lod, count);
+ } else {
+ renderMeshVertexArray(mesh, lod, count);
+ }
+ }
+
+ /**
+ * Resets all previously used {@link NativeObject Native Objects} on this Renderer.
+ * The state of the native objects is reset in such way, that using
+ * them again will cause the renderer to reupload them.
+ * Call this method when you know the GL context is going to shutdown.
+ *
+ * @see NativeObject#resetObject()
+ */
+ public void resetGLObjects() {
+ logger.log(Level.FINE, "IGLESShaderRenderer resetGLObjects");
+ objManager.resetObjects();
+ statistics.clearMemory();
+ boundShader = null;
+ lastFb = null;
+ context.reset();
+ }
+
+ /**
+ * Deletes all previously used {@link NativeObject Native Objects} on this Renderer, and
+ * then resets the native objects.
+ *
+ * @see #resetGLObjects()
+ * @see NativeObject#deleteObject(java.lang.Object)
+ */
+ public void cleanup() {
+ logger.log(Level.FINE, "IGLESShaderRenderer cleanup");
+ objManager.deleteAllObjects(this);
+ statistics.clearMemory();
+ }
+
+ /**
+ * Sets the alpha to coverage state.
+ * + * When alpha coverage and multi-sampling is enabled, + * each pixel will contain alpha coverage in all + * of its subsamples, which is then combined when + * other future alpha-blended objects are rendered. + *
+ *+ * Alpha-to-coverage is useful for rendering transparent objects + * without having to worry about sorting them. + *
+ */ + public void setAlphaToCoverage(boolean value) { + logger.log(Level.FINE, "IGLESShaderRenderer setAlphaToCoverage"); + if (value) { + JmeIosGLES.glEnable(JmeIosGLES.GL_SAMPLE_ALPHA_TO_COVERAGE); + JmeIosGLES.checkGLError(); + } else { + JmeIosGLES.glDisable(JmeIosGLES.GL_SAMPLE_ALPHA_TO_COVERAGE); + JmeIosGLES.checkGLError(); + } + } + + + /* ------------------------------------------------------------------------------ */ + + + public void initialize() { + Level store = logger.getLevel(); + logger.setLevel(Level.FINE); + + logger.log(Level.FINE, "Vendor: {0}", JmeIosGLES.glGetString(JmeIosGLES.GL_VENDOR)); + logger.log(Level.FINE, "Renderer: {0}", JmeIosGLES.glGetString(JmeIosGLES.GL_RENDERER)); + logger.log(Level.FINE, "Version: {0}", JmeIosGLES.glGetString(JmeIosGLES.GL_VERSION)); + logger.log(Level.FINE, "Shading Language Version: {0}", JmeIosGLES.glGetString(JmeIosGLES.GL_SHADING_LANGUAGE_VERSION)); + + /* + // Fix issue in TestRenderToMemory when GL_FRONT is the main + // buffer being used. + initialDrawBuf = glGetInteger(GL_DRAW_BUFFER); + initialReadBuf = glGetInteger(GL_READ_BUFFER); + + // XXX: This has to be GL_BACK for canvas on Mac + // Since initialDrawBuf is GL_FRONT for pbuffer, gotta + // change this value later on ... +// initialDrawBuf = GL_BACK; +// initialReadBuf = GL_BACK; + */ + + // Check OpenGL version + int openGlVer = extractVersion("OpenGL ES ", JmeIosGLES.glGetString(JmeIosGLES.GL_VERSION)); + if (openGlVer == -1) { + glslVer = -1; + throw new UnsupportedOperationException("OpenGL ES 2.0+ is required for IGLESShaderRenderer!"); + } + + // Check shader language version + glslVer = extractVersion("OpenGL ES GLSL ES ", JmeIosGLES.glGetString(JmeIosGLES.GL_SHADING_LANGUAGE_VERSION)); + switch (glslVer) { + // TODO: When new versions of OpenGL ES shader language come out, + // update this. + default: + caps.add(Caps.GLSL100); + break; + } + + JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, intBuf16, 0); + vertexTextureUnits = intBuf16[0]; + logger.log(Level.FINE, "VTF Units: {0}", vertexTextureUnits); + if (vertexTextureUnits > 0) { + caps.add(Caps.VertexTextureFetch); + } + + JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_TEXTURE_IMAGE_UNITS, intBuf16, 0); + fragTextureUnits = intBuf16[0]; + logger.log(Level.FINE, "Texture Units: {0}", fragTextureUnits); + + // Multiply vector count by 4 to get float count. + JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_VERTEX_UNIFORM_VECTORS, intBuf16, 0); + vertexUniforms = intBuf16[0] * 4; + logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms); + + JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_FRAGMENT_UNIFORM_VECTORS, intBuf16, 0); + fragUniforms = intBuf16[0] * 4; + logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms); + + JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_VARYING_VECTORS, intBuf16, 0); + int varyingFloats = intBuf16[0] * 4; + logger.log(Level.FINER, "Varying Floats: {0}", varyingFloats); + + JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_VERTEX_ATTRIBS, intBuf16, 0); + vertexAttribs = intBuf16[0]; + logger.log(Level.FINE, "Vertex Attributes: {0}", vertexAttribs); + + JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_SUBPIXEL_BITS, intBuf16, 0); + int subpixelBits = intBuf16[0]; + logger.log(Level.FINE, "Subpixel Bits: {0}", subpixelBits); + +// GLES10.glGetIntegerv(GLES10.GL_MAX_ELEMENTS_VERTICES, intBuf16); +// maxVertCount = intBuf16.get(0); +// logger.log(Level.FINER, "Preferred Batch Vertex Count: {0}", maxVertCount); +// +// GLES10.glGetIntegerv(GLES10.GL_MAX_ELEMENTS_INDICES, intBuf16); +// maxTriCount = intBuf16.get(0); +// logger.log(Level.FINER, "Preferred Batch Index Count: {0}", maxTriCount); + + JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_TEXTURE_SIZE, intBuf16, 0); + maxTexSize = intBuf16[0]; + logger.log(Level.FINE, "Maximum Texture Resolution: {0}", maxTexSize); + + JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_CUBE_MAP_TEXTURE_SIZE, intBuf16, 0); + maxCubeTexSize = intBuf16[0]; + logger.log(Level.FINE, "Maximum CubeMap Resolution: {0}", maxCubeTexSize); + + JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_RENDERBUFFER_SIZE, intBuf16, 0); + maxRBSize = intBuf16[0]; + logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize); + + /* + 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_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_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); + } + } + */ + + String extensions = JmeIosGLES.glGetString(JmeIosGLES.GL_EXTENSIONS); + logger.log(Level.FINE, "GL_EXTENSIONS: {0}", extensions); + + // Get number of compressed formats available. + JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_NUM_COMPRESSED_TEXTURE_FORMATS, intBuf16, 0); + int numCompressedFormats = intBuf16[0]; + + // Allocate buffer for compressed formats. + int[] compressedFormats = new int[numCompressedFormats]; + JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_COMPRESSED_TEXTURE_FORMATS, compressedFormats, 0); + + // Check for errors after all glGet calls. + JmeIosGLES.checkGLError(); + + // Print compressed formats. + for (int i = 0; i < numCompressedFormats; i++) { + logger.log(Level.FINE, "Compressed Texture Formats: {0}", compressedFormats[i]); + } + + TextureUtil.loadTextureFeatures(extensions); + + applyRenderState(RenderState.DEFAULT); + JmeIosGLES.glDisable(JmeIosGLES.GL_DITHER); + JmeIosGLES.checkGLError(); + + logger.log(Level.FINE, "Caps: {0}", caps); + logger.setLevel(store); + } + + + /* ------------------------------------------------------------------------------ */ + + + private int extractVersion(String prefixStr, String versionStr) { + if (versionStr != null) { + int spaceIdx = versionStr.indexOf(" ", prefixStr.length()); + if (spaceIdx >= 1) { + versionStr = versionStr.substring(prefixStr.length(), spaceIdx).trim(); + } else { + versionStr = versionStr.substring(prefixStr.length()).trim(); + } + float version = Float.parseFloat(versionStr); + return (int) (version * 100); + } else { + return -1; + } + } + + private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) { + intBuf1[0] = rb.getId(); + JmeIosGLES.glDeleteRenderbuffers(1, intBuf1, 0); + JmeIosGLES.checkGLError(); + } + + private int convertUsage(Usage usage) { + switch (usage) { + case Static: + return JmeIosGLES.GL_STATIC_DRAW; + case Dynamic: + return JmeIosGLES.GL_DYNAMIC_DRAW; + case Stream: + return JmeIosGLES.GL_STREAM_DRAW; + default: + throw new RuntimeException("Unknown usage type."); + } + } + + + protected void bindProgram(Shader shader) { + int shaderId = shader.getId(); + if (context.boundShaderProgram != shaderId) { + JmeIosGLES.glUseProgram(shaderId); + JmeIosGLES.checkGLError(); + + statistics.onShaderUse(shader, true); + boundShader = shader; + context.boundShaderProgram = shaderId; + } else { + statistics.onShaderUse(shader, false); + } + } + + protected void updateShaderUniforms(Shader shader) { + ListMapiOS GLES interface
iOS alternative to Android's GLES20 class
+ *
+ * @author Kostyantyn Hushchyn
+ */
+public class JmeIosGLES {
+ private static final Logger logger = Logger.getLogger(JmeIosGLES.class.getName());
+
+ private static boolean ENABLE_ERROR_CHECKING = true;
+
+ public static final int GL_ALPHA = 0x00001906;
+ public static final int GL_ALWAYS = 0x00000207;
+ public static final int GL_ARRAY_BUFFER = 0x00008892;
+ public static final int GL_BACK = 0x00000405;
+ public static final int GL_BLEND = 0x00000be2;
+ public static final int GL_BYTE = 0x00001400;
+ public static final int GL_CLAMP_TO_EDGE = 0x0000812f;
+ public static final int GL_COLOR_ATTACHMENT0 = 0x00008ce0;
+ public static final int GL_COLOR_BUFFER_BIT = 0x00004000;
+ public static final int GL_COMPILE_STATUS = 0x00008b81;
+ public static final int GL_COMPRESSED_TEXTURE_FORMATS = 0x000086a3;
+ public static final int GL_CULL_FACE = 0x00000b44;
+ public static final int GL_DEPTH_ATTACHMENT = 0x00008d00;
+ public static final int GL_DEPTH_BUFFER_BIT = 0x00000100;
+ public static final int GL_DEPTH_COMPONENT = 0x00001902;
+ public static final int GL_DEPTH_COMPONENT16 = 0x000081a5;
+ public static final int GL_DEPTH_TEST = 0x00000b71;
+ public static final int GL_DITHER = 0x00000bd0;
+ public static final int GL_DST_COLOR = 0x00000306;
+ public static final int GL_DYNAMIC_DRAW = 0x000088e8;
+ public static final int GL_EQUAL = 0x00000202;
+ public static final int GL_ELEMENT_ARRAY_BUFFER = 0x00008893;
+ public static final int GL_EXTENSIONS = 0x00001f03;
+ public static final int GL_FALSE = 0x00000000;
+ public static final int GL_FLOAT = 0x00001406;
+ public static final int GL_FRAGMENT_SHADER = 0x00008b30;
+ public static final int GL_FRAMEBUFFER = 0x00008d40;
+ public static final int GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = 0x00008cd1;
+ public static final int GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = 0x00008cd0;
+ public static final int GL_FRAMEBUFFER_COMPLETE = 0x00008cd5;
+ public static final int GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x00008cd6;
+ public static final int GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x00008cd9;
+ public static final int GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x00008cd7;
+ public static final int GL_FRAMEBUFFER_UNSUPPORTED = 0x00008cdd;
+ public static final int GL_FRONT = 0x00000404;
+ public static final int GL_FRONT_AND_BACK = 0x00000408;
+ public static final int GL_GEQUAL = 0x00000206;
+ public static final int GL_GREATER = 0x00000204;
+ public static final int GL_HIGH_FLOAT = 0x00008df2;
+ public static final int GL_INFO_LOG_LENGTH = 0x00008b84;
+ public static final int GL_INT = 0x00001404;
+ public static final int GL_LEQUAL = 0x00000203;
+ public static final int GL_LESS = 0x00000201;
+ public static final int GL_LINEAR = 0x00002601;
+ public static final int GL_LINEAR_MIPMAP_LINEAR = 0x00002703;
+ public static final int GL_LINEAR_MIPMAP_NEAREST = 0x00002701;
+ public static final int GL_LINES = 0x00000001;
+ public static final int GL_LINE_LOOP = 0x00000002;
+ public static final int GL_LINE_STRIP = 0x00000003;
+ public static final int GL_LINK_STATUS = 0x00008b82;
+ public static final int GL_LUMINANCE = 0x00001909;
+ public static final int GL_LUMINANCE_ALPHA = 0x0000190a;
+ public static final int GL_MAX_CUBE_MAP_TEXTURE_SIZE = 0x0000851c;
+ public static final int GL_MAX_FRAGMENT_UNIFORM_VECTORS = 0x00008dfd;
+ public static final int GL_MAX_RENDERBUFFER_SIZE = 0x000084e8;
+ public static final int GL_MAX_TEXTURE_IMAGE_UNITS = 0x00008872;
+ public static final int GL_MAX_TEXTURE_SIZE = 0x00000d33;
+ public static final int GL_MAX_VARYING_VECTORS = 0x00008dfc;
+ public static final int GL_MAX_VERTEX_ATTRIBS = 0x00008869;
+ public static final int GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x00008b4c;
+ public static final int GL_MAX_VERTEX_UNIFORM_VECTORS = 0x00008dfb;
+ public static final int GL_MIRRORED_REPEAT = 0x00008370;
+ public static final int GL_NEAREST = 0x00002600;
+ public static final int GL_NEAREST_MIPMAP_LINEAR = 0x00002702;
+ public static final int GL_NEAREST_MIPMAP_NEAREST = 0x00002700;
+ public static final int GL_NEVER = 0x00000200;
+ public static final int GL_NONE = 0x00000000;
+ public static final int GL_NOTEQUAL = 0x00000205;
+ public static final int GL_NUM_COMPRESSED_TEXTURE_FORMATS = 0x000086a2;
+ public static final int GL_ONE = 0x00000001;
+ public static final int GL_ONE_MINUS_SRC_ALPHA = 0x00000303;
+ public static final int GL_ONE_MINUS_SRC_COLOR = 0x00000301;
+ public static final int GL_POINTS = 0x00000000;
+ public static final int GL_POLYGON_OFFSET_FILL = 0x00008037;
+ public static final int GL_RENDERBUFFER = 0x00008d41;
+ public static final int GL_RENDERER = 0x00001f01;
+ public static final int GL_REPEAT = 0x00002901;
+ public static final int GL_RGB = 0x00001907;
+ public static final int GL_RGB565 = 0x00008d62;
+ public static final int GL_RGB5_A1 = 0x00008057;
+ public static final int GL_RGBA = 0x00001908;
+ public static final int GL_RGBA4 = 0x00008056;
+ public static final int GL_SAMPLE_ALPHA_TO_COVERAGE = 0x0000809e;
+ public static final int GL_SCISSOR_TEST = 0x00000c11;
+ public static final int GL_SHADING_LANGUAGE_VERSION = 0x00008b8c;
+ public static final int GL_SHORT = 0x00001402;
+ public static final int GL_SRC_COLOR = 0x00000300;
+ public static final int GL_SRC_ALPHA = 0x00000302;
+ public static final int GL_STATIC_DRAW = 0x000088e4;
+ public static final int GL_STENCIL_BUFFER_BIT = 0x00000400;
+ public static final int GL_STREAM_DRAW = 0x000088e0;
+ public static final int GL_SUBPIXEL_BITS = 0x00000d50;
+ public static final int GL_TEXTURE = 0x00001702;
+ public static final int GL_TEXTURE0 = 0x000084c0;
+ public static final int GL_TEXTURE_2D = 0x00000de1;
+ public static final int GL_TEXTURE_CUBE_MAP = 0x00008513;
+ public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x00008515;
+ public static final int GL_TEXTURE_MAG_FILTER = 0x00002800;
+ public static final int GL_TEXTURE_MIN_FILTER = 0x00002801;
+ public static final int GL_TEXTURE_WRAP_S = 0x00002802;
+ public static final int GL_TEXTURE_WRAP_T = 0x00002803;
+ public static final int GL_TRIANGLES = 0x00000004;
+ public static final int GL_TRIANGLE_FAN = 0x00000006;
+ public static final int GL_TRIANGLE_STRIP = 0x00000005;
+ public static final int GL_TRUE = 0x00000001;
+ public static final int GL_UNPACK_ALIGNMENT = 0x00000cf5;
+ public static final int GL_UNSIGNED_BYTE = 0x00001401;
+ public static final int GL_UNSIGNED_INT = 0x00001405;
+ public static final int GL_UNSIGNED_SHORT = 0x00001403;
+ public static final int GL_UNSIGNED_SHORT_4_4_4_4 = 0x00008033;
+ public static final int GL_UNSIGNED_SHORT_5_5_5_1 = 0x00008034;
+ public static final int GL_UNSIGNED_SHORT_5_6_5 = 0x00008363;
+ public static final int GL_VENDOR = 0x00001f00;
+ public static final int GL_VERSION = 0x00001f02;
+ public static final int GL_VERTEX_SHADER = 0x00008b31;
+ public static final int GL_ZERO = 0x00000000;
+
+ public static native void glActiveTexture(int texture);
+ public static native void glAttachShader(int program, int shader);
+ public static native void glBindBuffer(int target, int buffer);
+ public static native void glBindFramebuffer(int target, int framebuffer);
+ public static native void glBindRenderbuffer(int target, int renderbuffer);
+ public static native void glBindTexture(int target, int texture);
+// public static native void glBindVertexArray // TODO: Investigate this
+ public static native void glBlendFunc(int sfactor, int dfactor);
+ public static native void glBufferData(int target, int size, Buffer data, int usage);
+ public static native void glBufferData2(int target, int size, byte[] data, int offset, int usage);
+ public static native void glBufferSubData(int target, int offset, int size, Buffer data);
+ public static native void glBufferSubData2(int target, int offset, int size, byte[] data, int dataoffset);
+ public static native int glCheckFramebufferStatus(int target);
+ public static native void glClear(int mask);
+ public static native void glClearColor(float red, float green, float blue, float alpha);
+ public static native void glColorMask(boolean red, boolean green, boolean blue, boolean alpha);
+ public static native void glCompileShader(int shader);
+ public static native void glCompressedTexImage2D(int target, int level, int internalformat, int width, int height, int border, int imageSize, Buffer pixels);
+ public static native void glCompressedTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int imageSize, Buffer pixels);
+ public static native int glCreateProgram();
+ public static native int glCreateShader(int type);
+ public static native void glCullFace(int mode);
+ public static native void glDeleteBuffers(int n, int[] buffers, int offset);
+ public static native void glDeleteFramebuffers(int n, int[] framebuffers, int offset);
+ public static native void glDeleteProgram(int program);
+ public static native void glDeleteRenderbuffers(int n, int[] renderbuffers, int offset);
+ public static native void glDeleteShader(int shader);
+ public static native void glDeleteTextures(int n, int[] textures, int offset);
+ public static native void glDepthFunc(int func);
+ public static native void glDepthMask(boolean flag);
+ public static native void glDepthRangef(float zNear, float zFar);
+ public static native void glDetachShader(int program, int shader);
+ public static native void glDisableVertexAttribArray(int index);
+ public static native void glDisable(int cap);
+ public static native void glDrawArrays(int mode, int first, int count);
+ public static native void glDrawElements(int mode, int count, int type, Buffer indices);
+ public static native void glDrawElements2(int mode, int count, int type, byte[] indices, int offset);
+ public static native void glDrawElementsIndex(int mode, int count, int type, int offset);
+ public static native void glEnable(int cap);
+ public static native void glEnableVertexAttribArray(int index);
+ public static native void glFramebufferRenderbuffer(int target, int attachment, int renderbuffertarget, int renderbuffer);
+ public static native void glFramebufferTexture2D(int target, int attachment, int textarget, int texture, int level);
+ public static native void glGenBuffers(int n, int[] buffers, int offset);
+ public static native void glGenFramebuffers(int n, int[] framebuffers, int offset);
+ public static native void glGenRenderbuffers(int n, int[] renderbuffers, int offset);
+ public static native void glGenTextures(int n, int[] textures, int offset);
+ public static native void glGenerateMipmap(int target);
+ public static native int glGetAttribLocation(int program, String name);
+ public static native int glGetError();
+ public static native void glGetFramebufferAttachmentParameteriv(int target, int attachment, int pname, int[] params, int offset);
+ public static native void glGetIntegerv (int pname, int[] params, int offset);
+ public static native String glGetProgramInfoLog(int program);
+ public static native void glGetProgramiv(int program, int pname, int[] params, int offset);
+ public static native String glGetShaderInfoLog(int shader);
+ public static native void glGetShaderiv(int shader, int pname, int[] params, int offset);
+ public static native String glGetString(int name);
+ public static native int glGetUniformLocation(int program, String name);
+ public static native boolean glIsFramebuffer(int framebuffer);
+ public static native boolean glIsRenderbuffer(int renderbuffer);
+ public static native void glLineWidth(float width);
+ public static native void glLinkProgram(int program);
+ public static native void glPixelStorei(int pname, int param);
+ public static native void glPolygonOffset(float factor, float units);
+ public static native void glReadPixels(int vpX, int vpY, int vpW, int vpH, int format, int type, Buffer pixels);
+ public static native void glReadPixels2(int vpX, int vpY, int vpW, int vpH, int format, int type, byte[] pixels, int offset, int size);
+ public static native void glRenderbufferStorage(int target, int internalformat, int width, int height);
+ public static native void glScissor(int x, int y, int width, int height);
+ public static native void glShaderSource(int shader, String string);
+ public static native void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, Buffer pixels);
+ public static native void glTexParameteri(int target, int pname, int param);
+ public static native void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, Buffer pixels);
+ public static native void glUniform1f(int location, float x);
+ public static native void glUniform1fv(int location, int count, FloatBuffer v);
+ public static native void glUniform1fv2(int location, int count, float[] v, int offset);
+ public static native void glUniform1i(int location, int x);
+ public static native void glUniform1iv(int location, int count, IntBuffer v);
+ public static native void glUniform1iv2(int location, int count, int[] v, int offset);
+ public static native void glUniform2f(int location, float x, float y);
+ public static native void glUniform2fv(int location, int count, FloatBuffer v);
+ public static native void glUniform2fv2(int location, int count, float[] v, int offset);
+ public static native void glUniform3f(int location, float x, float y, float z);
+ public static native void glUniform3fv(int location, int count, FloatBuffer v);
+ public static native void glUniform3fv2(int location, int count, float[] v, int offset);
+ public static native void glUniform4f(int location, float x, float y, float z, float w);
+ public static native void glUniform4fv(int location, int count, FloatBuffer v);
+ public static native void glUniform4fv2(int location, int count, float[] v, int offset);
+ public static native void glUniformMatrix3fv(int location, int count, boolean transpose, FloatBuffer value);
+ public static native void glUniformMatrix3fv2(int location, int count, boolean transpose, float[] value, int offset);
+ public static native void glUniformMatrix4fv(int location, int count, boolean transpose, FloatBuffer value);
+ public static native void glUniformMatrix4fv2(int location, int count, boolean transpose, float[] value, int offset);
+ public static native void glUseProgram(int program);
+ //public static native void glVertexAttribPointer(int indx, int size, int type, boolean normalized, int stride, byte[] ptr, int offset);
+ public static native void glVertexAttribPointer(int indx, int size, int type, boolean normalized, int stride, Buffer ptr);
+ public static native void glVertexAttribPointer2(int indx, int size, int type, boolean normalized, int stride, int offset);
+ public static native void glViewport(int x, int y, int width, int height);
+
+
+ public static void checkGLError() {
+ if (!ENABLE_ERROR_CHECKING) {
+ return;
+ }
+ int error = glGetError();
+ if (error != 0) {
+ String message = null;//GLU.gluErrorString(error);
+ if (message == null) {
+ throw new RendererException("An unknown [" + error + "] OpenGL error has occurred.");
+ } else {
+ throw new RendererException("An OpenGL error has occurred: " + message);
+ }
+ }
+ }
+
+ /*
+ public static String gluErrorString(int error) {
+ switch (error) {
+ case GL10.GL_NO_ERROR:
+ return "no error";
+ case GL10.GL_INVALID_ENUM:
+ return "invalid enum";
+ case GL10.GL_INVALID_VALUE:
+ return "invalid value";
+ case GL10.GL_INVALID_OPERATION:
+ return "invalid operation";
+ case GL10.GL_STACK_OVERFLOW:
+ return "stack overflow";
+ case GL10.GL_STACK_UNDERFLOW:
+ return "stack underflow";
+ case GL10.GL_OUT_OF_MEMORY:
+ return "out of memory";
+ default:
+ return null;
+ }
+ }
+ */
+
+}
\ No newline at end of file
diff --git a/engine/src/ios/com/jme3/renderer/ios/TextureUtil.java b/engine/src/ios/com/jme3/renderer/ios/TextureUtil.java
new file mode 100644
index 000000000..596df0ce4
--- /dev/null
+++ b/engine/src/ios/com/jme3/renderer/ios/TextureUtil.java
@@ -0,0 +1,591 @@
+package com.jme3.renderer.ios;
+
+//import android.graphics.Bitmap;
+//import android.opengl.ETC1;
+//import android.opengl.ETC1Util.ETC1Texture;
+//import android.opengl.JmeIosGLES;
+//import android.opengl.GLUtils;
+//import com.jme3.asset.AndroidImageInfo;
+import com.jme3.renderer.ios.JmeIosGLES;
+import com.jme3.math.FastMath;
+import com.jme3.renderer.RendererException;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class TextureUtil {
+
+ private static final Logger logger = Logger.getLogger(TextureUtil.class.getName());
+ //TODO Make this configurable through appSettings
+ public static boolean ENABLE_COMPRESSION = true;
+ private static boolean NPOT = false;
+ private static boolean ETC1support = false;
+ private static boolean DXT1 = false;
+ private static boolean PVRTC = false;
+ private static boolean DEPTH24_STENCIL8 = false;
+ private static boolean DEPTH_TEXTURE = false;
+ 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 void loadTextureFeatures(String extensionString) {
+ ETC1support = extensionString.contains("GL_OES_compressed_ETC1_RGB8_texture");
+ DEPTH24_STENCIL8 = extensionString.contains("GL_OES_packed_depth_stencil");
+ NPOT = extensionString.contains("GL_IMG_texture_npot")
+ || extensionString.contains("GL_OES_texture_npot")
+ || extensionString.contains("GL_NV_texture_npot_2D_mipmap");
+
+ PVRTC = extensionString.contains("GL_IMG_texture_compression_pvrtc");
+
+ DXT1 = extensionString.contains("GL_EXT_texture_compression_dxt1");
+ DEPTH_TEXTURE = extensionString.contains("GL_OES_depth_texture");
+
+ RGBA8 = extensionString.contains("GL_ARM_rgba8") ||
+ extensionString.contains("GL_OES_rgb8_rgba8");
+
+ logger.log(Level.FINE, "Supports ETC1? {0}", ETC1support);
+ logger.log(Level.FINE, "Supports DEPTH24_STENCIL8? {0}", DEPTH24_STENCIL8);
+ logger.log(Level.FINE, "Supports NPOT? {0}", NPOT);
+ logger.log(Level.FINE, "Supports PVRTC? {0}", PVRTC);
+ logger.log(Level.FINE, "Supports DXT1? {0}", DXT1);
+ logger.log(Level.FINE, "Supports DEPTH_TEXTURE? {0}", DEPTH_TEXTURE);
+ logger.log(Level.FINE, "Supports RGBA8? {0}", RGBA8);
+ }
+
+ /*
+ private static void buildMipmap(Bitmap bitmap, boolean compress) {
+ int level = 0;
+ int height = bitmap.getHeight();
+ int width = bitmap.getWidth();
+
+ logger.log(Level.FINEST, " - Generating mipmaps for bitmap using SOFTWARE");
+
+ JmeIosGLES.glPixelStorei(JmeIosGLES.GL_UNPACK_ALIGNMENT, 1);
+
+ while (height >= 1 || width >= 1) {
+ //First of all, generate the texture from our bitmap and set it to the according level
+ if (compress) {
+ logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) with compression.", new Object[]{level, width, height});
+ uploadBitmapAsCompressed(JmeIosGLES.GL_TEXTURE_2D, level, bitmap, false, 0, 0);
+ } else {
+ logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) directly.", new Object[]{level, width, height});
+ GLUtils.texImage2D(JmeIosGLES.GL_TEXTURE_2D, level, bitmap, 0);
+ }
+
+ if (height == 1 || width == 1) {
+ break;
+ }
+
+ //Increase the mipmap level
+ height /= 2;
+ width /= 2;
+ Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
+
+ // Recycle any bitmaps created as a result of scaling the bitmap.
+ // Do not recycle the original image (mipmap level 0)
+ if (level != 0) {
+ bitmap.recycle();
+ }
+
+ bitmap = bitmap2;
+
+ level++;
+ }
+ }
+
+ private static void uploadBitmapAsCompressed(int target, int level, Bitmap bitmap, boolean subTexture, int x, int y) {
+ if (bitmap.hasAlpha()) {
+ logger.log(Level.FINEST, " - Uploading bitmap directly. Cannot compress as alpha present.");
+ if (subTexture) {
+ GLUtils.texSubImage2D(target, level, x, y, bitmap);
+ JmeIosGLES.checkGLError();
+ } else {
+ GLUtils.texImage2D(target, level, bitmap, 0);
+ JmeIosGLES.checkGLError();
+ }
+ } else {
+ // Convert to RGB565
+ int bytesPerPixel = 2;
+ Bitmap rgb565 = bitmap.copy(Bitmap.Config.RGB_565, true);
+
+ // Put texture data into ByteBuffer
+ ByteBuffer inputImage = BufferUtils.createByteBuffer(bitmap.getRowBytes() * bitmap.getHeight());
+ rgb565.copyPixelsToBuffer(inputImage);
+ inputImage.position(0);
+
+ // Delete the copied RGB565 image
+ rgb565.recycle();
+
+ // Encode the image into the output bytebuffer
+ int encodedImageSize = ETC1.getEncodedDataSize(bitmap.getWidth(), bitmap.getHeight());
+ ByteBuffer compressedImage = BufferUtils.createByteBuffer(encodedImageSize);
+ ETC1.encodeImage(inputImage, bitmap.getWidth(),
+ bitmap.getHeight(),
+ bytesPerPixel,
+ bytesPerPixel * bitmap.getWidth(),
+ compressedImage);
+
+ // Delete the input image buffer
+ BufferUtils.destroyDirectBuffer(inputImage);
+
+ // Create an ETC1Texture from the compressed image data
+ ETC1Texture etc1tex = new ETC1Texture(bitmap.getWidth(), bitmap.getHeight(), compressedImage);
+
+ // Upload the ETC1Texture
+ if (bytesPerPixel == 2) {
+ int oldSize = (bitmap.getRowBytes() * bitmap.getHeight());
+ int newSize = compressedImage.capacity();
+ logger.log(Level.FINEST, " - Uploading compressed image to GL, oldSize = {0}, newSize = {1}, ratio = {2}", new Object[]{oldSize, newSize, (float) oldSize / newSize});
+ if (subTexture) {
+ JmeIosGLES.glCompressedTexSubImage2D(target,
+ level,
+ x, y,
+ bitmap.getWidth(),
+ bitmap.getHeight(),
+ ETC1.ETC1_RGB8_OES,
+ etc1tex.getData().capacity(),
+ etc1tex.getData());
+
+ JmeIosGLES.checkGLError();
+ } else {
+ JmeIosGLES.glCompressedTexImage2D(target,
+ level,
+ ETC1.ETC1_RGB8_OES,
+ bitmap.getWidth(),
+ bitmap.getHeight(),
+ 0,
+ etc1tex.getData().capacity(),
+ etc1tex.getData());
+
+ JmeIosGLES.checkGLError();
+ }
+
+// ETC1Util.loadTexture(target, level, 0, JmeIosGLES.GL_RGB,
+// JmeIosGLES.GL_UNSIGNED_SHORT_5_6_5, etc1Texture);
+// } else if (bytesPerPixel == 3) {
+// ETC1Util.loadTexture(target, level, 0, JmeIosGLES.GL_RGB,
+// JmeIosGLES.GL_UNSIGNED_BYTE, etc1Texture);
+ }
+
+ BufferUtils.destroyDirectBuffer(compressedImage);
+ }
+ }
+
+ /**
+ * uploadTextureBitmap
uploads a native android bitmap
+ */
+ /*
+ public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips) {
+ uploadTextureBitmap(target, bitmap, needMips, false, 0, 0);
+ }
+
+ /**
+ * uploadTextureBitmap
uploads a native android bitmap
+ */
+ /*
+ public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips, boolean subTexture, int x, int y) {
+ boolean recycleBitmap = false;
+ //TODO, maybe this should raise an exception when NPOT is not supported
+
+ boolean willCompress = ENABLE_COMPRESSION && ETC1support && !bitmap.hasAlpha();
+ if (needMips && willCompress) {
+ // Image is compressed and mipmaps are desired, generate them
+ // using software.
+ buildMipmap(bitmap, willCompress);
+ } else {
+ if (willCompress) {
+ // Image is compressed but mipmaps are not desired, upload directly.
+ logger.log(Level.FINEST, " - Uploading compressed bitmap. Mipmaps are not generated.");
+ uploadBitmapAsCompressed(target, 0, bitmap, subTexture, x, y);
+
+ } else {
+ // Image is not compressed, mipmaps may or may not be desired.
+ logger.log(Level.FINEST, " - Uploading bitmap directly.{0}",
+ (needMips
+ ? " Mipmaps will be generated in HARDWARE"
+ : " Mipmaps are not generated."));
+ if (subTexture) {
+ System.err.println("x : " + x + " y :" + y + " , " + bitmap.getWidth() + "/" + bitmap.getHeight());
+ GLUtils.texSubImage2D(target, 0, x, y, bitmap);
+ JmeIosGLES.checkGLError();
+ } else {
+ GLUtils.texImage2D(target, 0, bitmap, 0);
+ JmeIosGLES.checkGLError();
+ }
+
+ if (needMips) {
+ // No pregenerated mips available,
+ // generate from base level if required
+ JmeIosGLES.glGenerateMipmap(target);
+ JmeIosGLES.checkGLError();
+ }
+ }
+ }
+
+ if (recycleBitmap) {
+ bitmap.recycle();
+ }
+ }
+ */
+
+ public static void uploadTextureAny(Image img, int target, int index, boolean needMips) {
+ /*
+ if (img.getEfficentData() instanceof AndroidImageInfo) {
+ logger.log(Level.FINEST, " === Uploading image {0}. Using BITMAP PATH === ", img);
+ // If image was loaded from asset manager, use fast path
+ AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData();
+ uploadTextureBitmap(target, imageInfo.getBitmap(), needMips);
+ } else {
+ */
+ logger.log(Level.FINEST, " === Uploading image {0}. Using BUFFER PATH === ", img);
+ boolean wantGeneratedMips = needMips && !img.hasMipmaps();
+ if (wantGeneratedMips && img.getFormat().isCompressed()) {
+ logger.log(Level.WARNING, "Generating mipmaps is only"
+ + " supported for Bitmap based or non-compressed images!");
+ }
+
+ // Upload using slower path
+ logger.log(Level.FINEST, " - Uploading bitmap directly.{0}",
+ (wantGeneratedMips
+ ? " Mipmaps will be generated in HARDWARE"
+ : " Mipmaps are not generated."));
+
+ uploadTexture(img, target, index);
+
+ // Image was uploaded using slower path, since it is not compressed,
+ // then compress it
+ if (wantGeneratedMips) {
+ // No pregenerated mips available,
+ // generate from base level if required
+ JmeIosGLES.glGenerateMipmap(target);
+ JmeIosGLES.checkGLError();
+ }
+ //}
+ }
+
+ private static void unsupportedFormat(Format fmt) {
+ throw new UnsupportedOperationException("The image format '" + fmt + "' is unsupported by the video hardware.");
+ }
+
+ public static IosGLImageFormat getImageFormat(Format fmt) throws UnsupportedOperationException {
+ IosGLImageFormat imageFormat = new IosGLImageFormat();
+ 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 = JmeIosGLES.GL_ALPHA;
+ imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
+ if (RGBA8) {
+ imageFormat.renderBufferStorageFormat = GL_RGBA8;
+ } else {
+ // Highest precision alpha supported by vanilla OGLES2
+ imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGBA4;
+ }
+ break;
+ case Luminance8:
+ imageFormat.format = JmeIosGLES.GL_LUMINANCE;
+ imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
+ if (RGBA8) {
+ imageFormat.renderBufferStorageFormat = GL_RGBA8;
+ } else {
+ // Highest precision luminance supported by vanilla OGLES2
+ imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565;
+ }
+ break;
+ case Luminance8Alpha8:
+ imageFormat.format = JmeIosGLES.GL_LUMINANCE_ALPHA;
+ imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
+ if (RGBA8) {
+ imageFormat.renderBufferStorageFormat = GL_RGBA8;
+ } else {
+ imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGBA4;
+ }
+ break;
+ case RGB565:
+ imageFormat.format = JmeIosGLES.GL_RGB;
+ imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT_5_6_5;
+ imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565;
+ break;
+ case ARGB4444:
+ imageFormat.format = JmeIosGLES.GL_RGBA4;
+ imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT_4_4_4_4;
+ imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGBA4;
+ break;
+ case RGB5A1:
+ imageFormat.format = JmeIosGLES.GL_RGBA;
+ imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT_5_5_5_1;
+ imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB5_A1;
+ break;
+ case RGB8:
+ imageFormat.format = JmeIosGLES.GL_RGB;
+ imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
+ if (RGBA8) {
+ imageFormat.renderBufferStorageFormat = GL_RGBA8;
+ } else {
+ // Fallback: Use RGB565 if RGBA8 is not available.
+ imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565;
+ }
+ break;
+ case BGR8:
+ imageFormat.format = JmeIosGLES.GL_RGB;
+ imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
+ if (RGBA8) {
+ imageFormat.renderBufferStorageFormat = GL_RGBA8;
+ } else {
+ imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565;
+ }
+ break;
+ case RGBA8:
+ imageFormat.format = JmeIosGLES.GL_RGBA;
+ imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
+ if (RGBA8) {
+ imageFormat.renderBufferStorageFormat = GL_RGBA8;
+ } else {
+ imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGBA4;
+ }
+ break;
+ case Depth:
+ case Depth16:
+ if (!DEPTH_TEXTURE) {
+ unsupportedFormat(fmt);
+ }
+ imageFormat.format = JmeIosGLES.GL_DEPTH_COMPONENT;
+ imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT;
+ imageFormat.renderBufferStorageFormat = JmeIosGLES.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 = JmeIosGLES.GL_DEPTH_COMPONENT;
+ imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT;
+ imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_DEPTH_COMPONENT16;
+ }
+ break;
+ case DXT1:
+ if (!DXT1) {
+ unsupportedFormat(fmt);
+ }
+ imageFormat.format = GL_DXT1;
+ imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
+ imageFormat.compress = true;
+ break;
+ case DXT1A:
+ if (!DXT1) {
+ unsupportedFormat(fmt);
+ }
+ imageFormat.format = GL_DXT1A;
+ imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
+ imageFormat.compress = true;
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized format: " + fmt);
+ }
+ return imageFormat;
+ }
+
+ public static class IosGLImageFormat {
+
+ boolean compress = false;
+ int format = -1;
+ int renderBufferStorageFormat = -1;
+ int dataType = -1;
+ }
+
+ private static void uploadTexture(Image img,
+ int target,
+ int index) {
+
+ /*
+ if (img.getEfficentData() instanceof AndroidImageInfo) {
+ throw new RendererException("This image uses efficient data. "
+ + "Use uploadTextureBitmap instead.");
+ }
+ */
+
+ // Otherwise upload image directly.
+ // Prefer to only use power of 2 textures here to avoid errors.
+ Image.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();
+
+ if (!NPOT) {
+ // Check if texture is POT
+ if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) {
+ throw new RendererException("Non-power-of-2 textures "
+ + "are not supported by the video hardware "
+ + "and no scaling path available for image: " + img);
+ }
+ }
+ IosGLImageFormat imageFormat = getImageFormat(fmt);
+
+ if (data != null) {
+ JmeIosGLES.glPixelStorei(JmeIosGLES.GL_UNPACK_ALIGNMENT, 1);
+ JmeIosGLES.checkGLError();
+ }
+
+ 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};
+ }
+ }
+
+ for (int i = 0; i < mipSizes.length; i++) {
+ int mipWidth = Math.max(1, width >> i);
+ int mipHeight = Math.max(1, height >> i);
+
+ if (data != null) {
+ data.position(pos);
+ data.limit(pos + mipSizes[i]);
+ }
+
+ if (imageFormat.compress && data != null) {
+ JmeIosGLES.glCompressedTexImage2D(target,
+ i,
+ imageFormat.format,
+ mipWidth,
+ mipHeight,
+ 0,
+ data.remaining(),
+ data);
+ } else {
+ JmeIosGLES.glTexImage2D(target,
+ i,
+ imageFormat.format,
+ mipWidth,
+ mipHeight,
+ 0,
+ imageFormat.format,
+ imageFormat.dataType,
+ data);
+ }
+ JmeIosGLES.checkGLError();
+
+ pos += mipSizes[i];
+ }
+ }
+
+ /**
+ * Update the texture currently bound to target at with data from the given
+ * Image at position x and y. The parameter index is used as the zoffset in
+ * case a 3d texture or texture 2d array is being updated.
+ *
+ * @param image Image with the source data (this data will be put into the
+ * texture)
+ * @param target the target texture
+ * @param index the mipmap level to update
+ * @param x the x position where to put the image in the texture
+ * @param y the y position where to put the image in the texture
+ */
+ public static void uploadSubTexture(
+ Image img,
+ int target,
+ int index,
+ int x,
+ int y) {
+ //TODO:
+ /*
+ if (img.getEfficentData() instanceof AndroidImageInfo) {
+ AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData();
+ uploadTextureBitmap(target, imageInfo.getBitmap(), true, true, x, y);
+ return;
+ }
+ */
+
+ // Otherwise upload image directly.
+ // Prefer to only use power of 2 textures here to avoid errors.
+ Image.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();
+
+ if (!NPOT) {
+ // Check if texture is POT
+ if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) {
+ throw new RendererException("Non-power-of-2 textures "
+ + "are not supported by the video hardware "
+ + "and no scaling path available for image: " + img);
+ }
+ }
+ IosGLImageFormat imageFormat = getImageFormat(fmt);
+
+ if (data != null) {
+ JmeIosGLES.glPixelStorei(JmeIosGLES.GL_UNPACK_ALIGNMENT, 1);
+ JmeIosGLES.checkGLError();
+ }
+
+ 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};
+ }
+ }
+
+ for (int i = 0; i < mipSizes.length; i++) {
+ int mipWidth = Math.max(1, width >> i);
+ int mipHeight = Math.max(1, height >> i);
+
+ if (data != null) {
+ data.position(pos);
+ data.limit(pos + mipSizes[i]);
+ }
+
+ if (imageFormat.compress && data != null) {
+ JmeIosGLES.glCompressedTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, data.remaining(), data);
+ JmeIosGLES.checkGLError();
+ } else {
+ JmeIosGLES.glTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, imageFormat.dataType, data);
+ JmeIosGLES.checkGLError();
+ }
+
+ pos += mipSizes[i];
+ }
+ }
+}