I have been trying to reduce the memory footprint of my textures in a Android game that I wrote without too much success. Based on research that I have done it seems that a good approach is to compress my textures using ETC1 since that is the mostly wided supported format for Android devices.
I am able to create the necessary PKMs from my PNGs using Mali ARM - no problems there. I can also render these PKMs just fine using ETC1Utils - again no problems so far.
The problem comes in with trying to handle alphas. I used Mali to create a separate alpha file for my PNGs, i.e. "xxx.png" is compressed into "xxx.pkm" and "xxx_alpha.pkm". The one approach suggested to me in a different question that I asked was to use multi-texturing to combine these two textures since I can't use fragment shaders in OpenGL ES 1.1.
And this is where I am stuck. I am not too familiar with this stuff and I am not making much head way. Basically, as soon as I try to combine with my alpha texture, everything is just rendered white.
Here is a snippet of my code:
public class Texture {
GLGraphics glGraphics;
FileIO fileIO;
String fileName;
int textureId;
int minFilter;
int magFilter;
public int width;
public int height;
private boolean loaded = false;
public Texture(GLGame glGame, String fileName) {
this.glGraphics = glGame.getGLGraphics();
this.fileIO = glGame.getFileIO();
this.fileName = fileName;
load();
}
public void load() {
GL10 gl = glGraphics.getGL();
int[] textureIds = new int[2];
gl.glGenTextures(2, textureIds, 0);
textureId = textureIds[0];
InputStream inputStream = null;
try {
inputStream = fileIO.readAsset(fileName + ".pkm");
int rgbTexture = textureId;
gl.glActiveTexture(GLES10.GL_TEXTURE0);
gl.glBindTexture(GLES11.GL_TEXTURE_2D, rgbTexture);
gl.glTexEnvf(GLES11.GL_TEXTURE_ENV, GLES11.GL_TEXTURE_ENV_MODE, GLES11.GL_MODULATE);
ETC1Texture etcTexture = ETC1Util.createTexture(inputStream);
ETC1Util.loadTexture(GLES11.GL_TEXTURE_2D, 0, 0, GLES11.GL_RGB, GLES11.GL_UNSIGNED_SHORT_5_6_5, etcTexture);
int alphaTexture = textureId[1];
gl.glActiveTexture(GLES11.GL_TEXTURE1);
gl.glBindTexture(GLES11.GL_TEXTURE_2D, alphaTexture);
gl.glTexEnvf(GLES11.GL_TEXTURE_ENV, GLES11.GL_TEXTURE_ENV_MODE, GLES11.GL_COMBINE);
gl.glTexEnvf(GLES11.GL_TEXTURE_ENV, GLES11.GL_COMBINE_RGB, GLES11.GL_REPLACE);
gl.glTexEnvf(GLES11.GL_TEXTURE_ENV, GLES11.GL_SRC0_RGB, GLES11.GL_PREVIOUS);
gl.glTexEnvf(GLES11.GL_TEXTURE_ENV, GLES11.GL_OPERAND0_RGB, GLES11.GL_SRC_COLOR);
gl.glTexEnvf(GLES11.GL_TEXTURE_ENV, GLES11.GL_COMBINE_ALPHA, GLES11.GL_MODULATE);
gl.glTexEnvf(GLES11.GL_TEXTURE_ENV, GLES11.GL_SRC0_ALPHA, GLES11.GL_TEXTURE);
gl.glTexEnvf(GLES11.GL_TEXTURE_ENV, GLES11.GL_OPERAND0_ALPHA, GLES11.GL_SRC_ALPHA);
gl.glTexEnvf(GLES11.GL_TEXTURE_ENV, GLES11.GL_SRC1_ALPHA, GLES11.GL_PREVIOUS);
gl.glTexEnvf(GLES11.GL_TEXTURE_ENV, GLES11.GL_OPERAND1_ALPHA, GLES11.GL_SRC_ALPHA);
InputStream inputStreamAlpha = fileIO.readAsset(fileName + "_alpha.pkm");
ETC1Texture etcAlphaTexture = ETC1Util.createTexture(inputStreamAlpha);
ETC1Util.loadTexture(GLES11.GL_TEXTURE_2D, 0, 0, GLES11.GL_RGB, GLES11.GL_UNSIGNED_SHORT_5_6_5, etcAlphaTexture);
setFilters(GL10.GL_NEAREST, GL10.GL_NEAREST);
gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
width = etcTexture.getWidth();
height = etcTexture.getHeight();
} catch (IOException e) {
throw new RuntimeException("Couldn't load texture '" + fileName + "'", e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// do nothing
}
}
}
loaded = true;
}
public void reload() {
load();
bind();
setFilters(minFilter, magFilter);
glGraphics.getGL().glBindTexture(GL10.GL_TEXTURE_2D, 0);
}
public void setFilters(int minFilter, int magFilter) {
this.minFilter = minFilter;
this.magFilter = magFilter;
GL10 gl = glGraphics.getGL();
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, minFilter);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, magFilter);
}
public void bind() {
GL10 gl = glGraphics.getGL();
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
}
public void dispose() {
loaded = false;
GL10 gl = glGraphics.getGL();
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
int[] textureIds = { textureId };
gl.glDeleteTextures(1, textureIds, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
}
public boolean isLoaded() {
return loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
}
My main concern is the load() method. This code was put together through snippets that I have found on the web and coupled with my lack of understanding of multi-texturing in general, I have clearly gone wrong somewhere. Also note that when I render my textures I call:
GL10 gl = glGraphics.getGL();
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
gl.glEnable(GL10.GL_TEXTURE_2D);
camera.setViewportAndMatrices();
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
// call some objects that do my rendering stuff here
gl.glDisable(GL10.GL_BLEND);
gl.glDisable(GL10.GL_TEXTURE_2D);
When I render a texture I call the bind() method on my Texture class. As you can see, this binds to my global textureId variable which was used as the RGB PKMs ID when loading. I am not even sure if this is correct. Should I be binding to the RGB's ID or the alpha's ID? Or is that not even close to being on the right track? My problem may also relate to how I am loading the alphas using ETC1Utils - I have no idea if this approach is correct or not.
I am really quite stuck so any help pointing out where I have gone wrong and some sort of explanation about how multi-texturing is supposed to be implement to combine ETC1 alphas and RGBs would really be awesome.