package rajawali.materials;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.CopyOnWriteArrayList;
import rajawali.util.RajLog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.opengl.ETC1;
import android.opengl.ETC1Util;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;
/**
* @author dennis.ippel
*
*/
public class TextureManager {
private Context mContext;
private boolean mShouldValidateTextures;
private TextureInfo mCurrentValidatingTexInfo;
private Stack<TextureInfo> mTexturesToUpdate;
private boolean mShouldUpdateTextures;
private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
// Paletted texture constants
// Referenced from OpenGL ES 2.0 extension C header from Khronos Group
// http://www.khronos.org/registry/gles/api/2.0/gl2ext.h
private static final int GL_PALETTE4_RGB8_OES = 0x8B90;
private static final int GL_PALETTE4_RGBA8_OES = 0x8B91;
private static final int GL_PALETTE4_R5_G6_B5_OES = 0x8B92;
private static final int GL_PALETTE4_RGBA4_OES = 0x8B93;
private static final int GL_PALETTE4_RGB5_A1_OES = 0x8B94;
private static final int GL_PALETTE8_RGB8_OES = 0x8B95;
private static final int GL_PALETTE8_RGBA8_OES = 0x8B96;
private static final int GL_PALETTE8_R5_G6_B5_OES = 0x8B97;
private static final int GL_PALETTE8_RGBA4_OES = 0x8B98;
private static final int GL_PALETTE8_RGB5_A1_OES = 0x8B99;
// PowerVR Texture compression constants
private static final int GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00;
private static final int GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01;
private static final int GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02;
private static final int GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03;
// S3 texture compression constants
private static final int GL_COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0;
private static final int GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1;
/**
* List containing texture information objects
*/
private List<TextureInfo> mTextureInfoList;
/**
* Cube map faces
*/
private final int[] CUBE_FACES = new int[] {
GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X,
GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
};
/**
* Texture types. Can be DIFFUSE, BUMP, FRAME_BUFFER, DEPTH_BUFFER, LOOKUP, CUBE_MAP
*/
public enum TextureType {
DIFFUSE,
BUMP,
SPECULAR,
ALPHA,
FRAME_BUFFER,
DEPTH_BUFFER,
LOOKUP,
CUBE_MAP,
SPHERE_MAP,
VIDEO_TEXTURE
};
public enum CompressionType {
NONE,
ETC1,
PALETTED,
THREEDC,
ATC,
DXT1,
PVRTC
};
public enum WrapType {
CLAMP,
REPEAT
};
public enum FilterType{
NEAREST,
LINEAR
};
public enum PaletteFormat {
PALETTE4_RGB8,
PALETTE4_RGBA8,
PALETTE4_R5_G6_B5,
PALETTE4_RGBA4,
PALETTE4_RGB5_A1,
PALETTE8_RGB8,
PALETTE8_RGBA8,
PALETTE8_R5_G6_B5,
PALETTE8_RGBA4,
PALETTE8_RGB5_A1
};
public enum ThreeDcFormat {
X,
XY
};
public enum AtcFormat {
RGB,
RGBA_EXPLICIT,
RGBA_INTERPOLATED
};
public enum Dxt1Format {
RGB,
RGBA
};
public enum PvrtcFormat {
RGB_2BPP,
RGB_4BPP,
RGBA_2BPP,
RGBA_4BPP
};
public TextureManager(Context context) {
mTextureInfoList = Collections.synchronizedList(new CopyOnWriteArrayList<TextureInfo>());
mTexturesToUpdate = new Stack<TextureInfo>();
mContext = context;
}
public TextureInfo addTexture(int resID) {
return addTexture(getBitmap(resID));
}
public TextureInfo addTexture(int resID, TextureType textureType) {
return this.addTexture(getBitmap(resID), textureType, true, false, WrapType.REPEAT, FilterType.LINEAR);
}
public TextureInfo addTexture(int resID, TextureType textureType, boolean mipmap) {
return this.addTexture(getBitmap(resID), textureType, mipmap, false, WrapType.REPEAT, FilterType.LINEAR);
}
public TextureInfo addTexture(int resID, boolean mipmap, boolean recycle) {
return this.addTexture(getBitmap(resID), TextureType.DIFFUSE, mipmap, recycle, WrapType.REPEAT, FilterType.LINEAR);
}
public TextureInfo addTexture(int resID, TextureType textureType, WrapType wrapType, FilterType filterType) {
return this.addTexture(getBitmap(resID), textureType, true, false, wrapType, filterType);
}
public TextureInfo addTexture(int resID, TextureType textureType, boolean mipmap, boolean recycle) {
return this.addTexture(getBitmap(resID), textureType, mipmap, recycle, WrapType.REPEAT, FilterType.LINEAR);
}
public TextureInfo addTexture(int resID, TextureType textureType, boolean mipmap, boolean recycle, WrapType wrapType, FilterType filterType) {
final Bitmap texture = getBitmap(resID);
TextureInfo tInfo = addTexture(new ByteBuffer[0], texture, texture.getWidth(), texture.getHeight(), textureType, texture.getConfig(), mipmap, recycle, wrapType, filterType);
if(recycle && tInfo.getTextureId() > 0)
texture.recycle();
else
tInfo.setTexture(texture);
return tInfo;
}
public TextureInfo addTexture(Bitmap texture) {
return addTexture(texture, TextureType.DIFFUSE);
}
public TextureInfo addTexture(Bitmap texture, TextureType textureType) {
return this.addTexture(texture, textureType, true, false, WrapType.REPEAT, FilterType.LINEAR);
}
public TextureInfo addTexture(Bitmap texture, TextureType textureType, boolean mipmap) {
return this.addTexture(texture, textureType, mipmap, false, WrapType.REPEAT, FilterType.LINEAR);
}
public TextureInfo addTexture(Bitmap texture, boolean mipmap, boolean recycle) {
return this.addTexture(texture, TextureType.DIFFUSE, mipmap, recycle, WrapType.REPEAT, FilterType.LINEAR);
}
public TextureInfo addTexture(Bitmap texture, TextureType textureType, WrapType wrapType, FilterType filterType) {
return this.addTexture(texture, textureType, true, false, wrapType, filterType);
}
public TextureInfo addTexture(Bitmap texture, TextureType textureType, boolean mipmap, boolean recycle) {
return this.addTexture(texture, textureType, mipmap, recycle, WrapType.REPEAT, FilterType.LINEAR);
}
public TextureInfo addTexture(Bitmap texture, TextureType textureType, boolean mipmap, boolean recycle, WrapType wrapType, FilterType filterType) {
TextureInfo tInfo = addTexture(new ByteBuffer[0], texture, texture.getWidth(), texture.getHeight(), textureType, texture.getConfig(), mipmap, recycle, wrapType, filterType);
if(recycle && tInfo.getTextureId() > 0)
texture.recycle();
else
tInfo.setTexture(texture);
return tInfo;
}
public TextureInfo addTexture(ByteBuffer buffer, int width, int height) {
return addTexture(buffer, width, height, TextureType.DIFFUSE);
}
public TextureInfo addTexture(ByteBuffer buffer, int width, int height, TextureType textureType) {
return addTexture(buffer, null, width, height, textureType, Config.ARGB_8888, false, false, WrapType.REPEAT, FilterType.LINEAR);
}
public TextureInfo addTexture(ByteBuffer[] buffer, Bitmap texture, int width, int height, TextureType textureType, Config bitmapConfig, boolean mipmap, boolean recycle, WrapType wrapType, FilterType filterType) {
return addTexture(buffer, texture, width, height, textureType, bitmapConfig, mipmap, recycle, false, wrapType, filterType);
}
public TextureInfo addTexture(ByteBuffer buffer, Bitmap texture, int width, int height, TextureType textureType, Config bitmapConfig, boolean mipmap, boolean recycle, WrapType wrapType, FilterType filterType) {
return addTexture(new ByteBuffer[] { buffer }, texture, width, height, textureType, bitmapConfig, mipmap, recycle, false, wrapType, filterType);
}
public TextureInfo addTexture(TextureInfo textureInfo) {
TextureInfo newInfo;
GLES20.glDeleteTextures(1, new int[] { textureInfo.getTextureId() }, 0);
TextureInfo oldInfo = new TextureInfo(textureInfo);
if (textureInfo.getCompressionType() == CompressionType.NONE) {
if(textureInfo.getTextureType() == TextureType.CUBE_MAP) {
newInfo = addCubemapTextures(textureInfo.getTextures(), textureInfo.isMipmap(), textureInfo.shouldRecycle());
} else if(textureInfo.getTextureType() == TextureType.FRAME_BUFFER) {
newInfo = addTexture(null, textureInfo.getWidth(), textureInfo.getHeight(), TextureType.FRAME_BUFFER);
} else {
if (textureInfo.getTexture() == null) {
newInfo = addTexture(textureInfo.getBuffer(), null, textureInfo.getWidth(), textureInfo.getHeight(), textureInfo.getTextureType(), textureInfo.getBitmapConfig(), false, false, textureInfo.getWrapType(), textureInfo.getFilterType());
} else {
newInfo = addTexture(textureInfo.getTexture(), textureInfo.getTextureType(), textureInfo.isMipmap(), textureInfo.shouldRecycle(), textureInfo.getWrapType(), textureInfo.getFilterType());
}
}
} else {
newInfo = addTexture(textureInfo.getBuffer(), null, textureInfo.getWidth(), textureInfo.getHeight(), textureInfo.getTextureType(), null, false, false, false, textureInfo.getWrapType(), textureInfo.getFilterType(), textureInfo.getCompressionType(), textureInfo.getInternalFormat());
}
// remove newly added texture info because we're sticking with the old one.
mTextureInfoList.remove(newInfo);
textureInfo.setFrom(newInfo);
textureInfo.setTextureName(oldInfo.getTextureName());
return textureInfo;
}
public TextureInfo addTexture(ByteBuffer[] buffer, Bitmap texture, int width, int height, TextureType textureType, Config bitmapConfig, boolean mipmap, boolean recycle, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
return addTexture(buffer, texture, width, height, textureType, bitmapConfig, mipmap, recycle, isExistingTexture, wrapType, filterType, CompressionType.NONE, 0);
}
public TextureInfo addTexture(ByteBuffer buffer, Bitmap texture, int width, int height, TextureType textureType, Config bitmapConfig, boolean mipmap, boolean recycle, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
return addTexture(new ByteBuffer[] { buffer }, texture, width, height, textureType, bitmapConfig, mipmap, recycle, isExistingTexture, wrapType, filterType, CompressionType.NONE, 0);
}
public TextureInfo addTexture(ByteBuffer[] buffer, Bitmap texture, int width, int height, TextureType textureType, Config bitmapConfig, boolean mipmap, boolean recycle, boolean isExistingTexture, WrapType wrapType, FilterType filterType, CompressionType compressionType, int compressionFormat) {
int bitmapFormat = bitmapConfig == Config.ARGB_8888 ? GLES20.GL_RGBA : GLES20.GL_RGB;
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
int textureId = textures[0];
if(textureId > 0) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
if((mipmap && compressionType == CompressionType.NONE) ||
// Manual mipmapped textures are included
(buffer.length > 1)){
if(filterType==FilterType.LINEAR)
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR);
else
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST_MIPMAP_NEAREST);
}else{
if(filterType==FilterType.LINEAR)
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
else
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
}
if(filterType==FilterType.LINEAR)
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
else
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
if(wrapType==WrapType.REPEAT){
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
}else{
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
}
if(texture == null) {
if (compressionType == CompressionType.NONE) {
if ((buffer != null && buffer.length == 0) || buffer == null) {
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, bitmapFormat, width, height, 0, bitmapFormat, GLES20.GL_UNSIGNED_BYTE, null);
} else {
int w = width, h = height;
for (int i = 0; i < buffer.length; i++) {
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, i, bitmapFormat, w, h, 0, bitmapFormat, GLES20.GL_UNSIGNED_BYTE, buffer[i]);
w = w > 1 ? w / 2 : 1;
h = h > 1 ? h / 2 : 1;
}
}
} else {
if ((buffer != null && buffer.length == 0) || buffer == null) {
GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 0, compressionFormat, width, height, 0, 0, null);
} else {
int w = width, h = height;
for (int i = 0; i < buffer.length; i++) {
GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, i, compressionFormat, w, h, 0, buffer[i].capacity(), buffer[i]);
w = w > 1 ? w / 2 : 1;
h = h > 1 ? h / 2 : 1;
}
}
}
} else
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmapFormat, texture, 0);
if(mipmap && compressionType == CompressionType.NONE)
GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
} else {
mShouldValidateTextures = true;
}
TextureInfo textureInfo = mCurrentValidatingTexInfo == null ? new TextureInfo(textureId, textureType) : mCurrentValidatingTexInfo;
if(mCurrentValidatingTexInfo == null) {
textureInfo.setWidth(width);
textureInfo.setHeight(height);
textureInfo.setBitmapConfig(bitmapConfig);
textureInfo.setMipmap(mipmap);
textureInfo.setWrapType(wrapType);
textureInfo.setFilterType(filterType);
textureInfo.shouldRecycle(recycle);
textureInfo.setCompressionType(compressionType);
textureInfo.setBuffer(buffer);
if(compressionType != CompressionType.NONE){
textureInfo.setInternalFormat(compressionFormat);
}
if(!recycle && compressionType == CompressionType.NONE)
textureInfo.setTexture(texture);
} else {
textureInfo.setTextureId(textureId);
}
for (int i = 0; i < buffer.length; i++) {
if(buffer[i] != null) {
buffer[i].limit(0);
}
}
buffer = null;
if(!isExistingTexture && mCurrentValidatingTexInfo == null)
mTextureInfoList.add(textureInfo);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textureInfo;
}
/**
* Dynamically generates an ETC1 texture on-the-fly from given resource ID and automatically binds the newly
* generated texture.
*
* @param resID
* @param textureType
* @return
*/
public TextureInfo addEtc1Texture(int resID, TextureType textureType) {
return addEtc1Texture(getBitmap(resID), textureType);
}
/**
* Dynamically generates an ETC1 texture on-the-fly from given bitmap and automatically binds the newly generated
* texture.
*
* @param bitmap
* @param textureType
* @return
*/
public TextureInfo addEtc1Texture(Bitmap bitmap, TextureType textureType) {
int imageSize = bitmap.getRowBytes() * bitmap.getHeight();
ByteBuffer uncompressedBuffer = ByteBuffer.allocateDirect(imageSize);
bitmap.copyPixelsToBuffer(uncompressedBuffer);
uncompressedBuffer.position(0);
ByteBuffer compressedBuffer = ByteBuffer.allocateDirect(ETC1.getEncodedDataSize(bitmap.getWidth(), bitmap.getHeight())).order(ByteOrder.nativeOrder());
ETC1.encodeImage(uncompressedBuffer, bitmap.getWidth(), bitmap.getHeight(), 2, 2 * bitmap.getWidth(), compressedBuffer);
return addEtc1Texture(compressedBuffer, bitmap.getWidth(), bitmap.getHeight(), textureType);
}
/**
* Attempts to add ETC1 compressed texture and fall back to uncompressed bitmap upon failure.
* @param compressedTex InputStream of the PKM texture resource
* @param fallbackTex Uncompressed Bitmap resource to fallback on if adding ETC1 texture fails
* @return TextureInfo
*/
public TextureInfo addEtc1Texture(InputStream compressedTex, Bitmap fallbackTex) {
return addEtc1Texture(compressedTex, fallbackTex, TextureType.DIFFUSE);
}
public TextureInfo addEtc1Texture(InputStream compressedTex, Bitmap fallbackTex, TextureType textureType) {
ETC1Util.ETC1Texture texture = null;
TextureInfo textureInfo = null;
try {
texture = ETC1Util.createTexture(compressedTex);
} catch (IOException e) {
Log.e("addEtc1Texture", e.getMessage());
} finally {
if (texture == null) {
textureInfo = addTexture(fallbackTex);
Log.d("ETC1", "Falling back to uncompressed texture");
} else {
textureInfo = addEtc1Texture(texture.getData(), texture.getWidth(), texture.getHeight(), textureType);
Log.d("ETC1", "ETC1 texture load successful");
}
}
return textureInfo;
}
/**
* Add mipmap-chained ETC1 texture.
* @param context
* @param resourceIds
* @return
*/
public TextureInfo addEtc1Texture(int[] resourceIds, TextureType textureType, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
if (resourceIds == null) return null;
ByteBuffer[] mipmapChain = new ByteBuffer[resourceIds.length];
int mip_0_width = 1, mip_0_height = 1;
try {
for (int i = 0, length = resourceIds.length; i < length; i++) {
ETC1Util.ETC1Texture texture = ETC1Util.createTexture(mContext.getResources().openRawResource(resourceIds[i]));
mipmapChain[i] = texture.getData();
if (i == 0) {
mip_0_width = texture.getWidth();
mip_0_height = texture.getHeight();
}
}
} catch (IOException e) {
RajLog.e(e.getMessage());
e.printStackTrace();
}
return addTexture(mipmapChain, null, mip_0_width, mip_0_height, textureType, null, true, false, isExistingTexture, wrapType, filterType, CompressionType.ETC1, ETC1.ETC1_RGB8_OES);
}
/**
* Add mipmap-chained ETC1 texture.
* @param context
* @param resourceIds
* @return
*/
public TextureInfo addEtc1Texture(int[] resourceIds, TextureType textureType) {
return addEtc1Texture(resourceIds, textureType, false, WrapType.REPEAT, FilterType.LINEAR);
}
public TextureInfo addEtc1Texture(ByteBuffer buffer, int width, int height) {
return addEtc1Texture(buffer, width, height, TextureType.DIFFUSE);
}
/**
* Adds and binds ETC1 compressed texture. Due to limitations with compressed textures,
* automatic mipmap generation is disabled.
*
* All devices with OpenGL ES 2.0 API should be able to handle this type of compression.
*/
public TextureInfo addEtc1Texture(ByteBuffer buffer, int width, int height, TextureType textureType) {
return addEtc1Texture(buffer, width, height, textureType, false, WrapType.REPEAT, FilterType.LINEAR);
}
public TextureInfo addEtc1Texture(ByteBuffer buffer, int width, int height, TextureType textureType, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
return addTexture(new ByteBuffer[]{buffer}, null, width, height, textureType, null, false, false, isExistingTexture, wrapType, filterType, CompressionType.ETC1, ETC1.ETC1_RGB8_OES);
}
public TextureInfo addEtc1Texture(ByteBuffer[] buffer, int width, int height, TextureType textureType, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
return addTexture(buffer, null, width, height, textureType, null, false, false, isExistingTexture, wrapType, filterType, CompressionType.ETC1, ETC1.ETC1_RGB8_OES);
}
public TextureInfo addPalettedTexture(ByteBuffer buffer, int width, int height, TextureType textureType, PaletteFormat format) {
return addPalettedTexture(buffer, width, height, textureType, format, false, WrapType.REPEAT, FilterType.LINEAR);
}
/**
* Adds and binds paletted texture. Pass in multiple buffer corresponding to different mipmap levels.
*/
public TextureInfo addPalettedTexture(ByteBuffer[] buffer, int width, int height, TextureType textureType, PaletteFormat format, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
int internalformat;
switch (format) {
case PALETTE4_RGB8:
internalformat = GL_PALETTE4_RGB8_OES;
break;
case PALETTE4_RGBA8:
internalformat = GL_PALETTE4_RGBA8_OES;
break;
case PALETTE4_R5_G6_B5:
internalformat = GL_PALETTE4_R5_G6_B5_OES;
break;
case PALETTE4_RGBA4:
internalformat = GL_PALETTE4_RGBA4_OES;
break;
case PALETTE4_RGB5_A1:
internalformat = GL_PALETTE4_RGB5_A1_OES;
break;
case PALETTE8_RGB8:
internalformat = GL_PALETTE8_RGB8_OES;
break;
case PALETTE8_RGBA8:
default:
internalformat = GL_PALETTE8_RGBA8_OES;
break;
case PALETTE8_R5_G6_B5:
internalformat = GL_PALETTE8_R5_G6_B5_OES;
break;
case PALETTE8_RGBA4:
internalformat = GL_PALETTE8_RGBA4_OES;
break;
case PALETTE8_RGB5_A1:
internalformat = GL_PALETTE8_RGB5_A1_OES;
break;
}
return addTexture(buffer, null, width, height, textureType, null, false, false, isExistingTexture, wrapType, filterType, CompressionType.PALETTED, internalformat);
}
public TextureInfo addPalettedTexture(ByteBuffer buffer, int width, int height, TextureType textureType, PaletteFormat format, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
return addPalettedTexture(new ByteBuffer[]{buffer}, width, height, textureType, format, isExistingTexture, wrapType, filterType);
}
public TextureInfo add3dcTexture(ByteBuffer buffer, int width, int height, TextureType textureType, ThreeDcFormat format) {
return add3dcTexture(buffer, width, height, textureType, format, false, WrapType.REPEAT, FilterType.LINEAR);
}
/**
* Adds and binds ATI 3Dc compressed texture. This compression type is most used for
* compressing normal map textures.
*/
public TextureInfo add3dcTexture(ByteBuffer[] buffer, int width, int height, TextureType textureType, ThreeDcFormat format, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
if (format == ThreeDcFormat.X)
return addTexture(buffer, null, width, height, textureType, null, false, false, isExistingTexture, wrapType, filterType, CompressionType.THREEDC, GLES11Ext.GL_3DC_X_AMD);
else
return addTexture(buffer, null, width, height, textureType, null, false, false, isExistingTexture, wrapType, filterType, CompressionType.THREEDC, GLES11Ext.GL_3DC_XY_AMD);
}
public TextureInfo add3dcTexture(ByteBuffer buffer, int width, int height, TextureType textureType, ThreeDcFormat format, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
return add3dcTexture(new ByteBuffer[] { buffer }, width, height, textureType, format, isExistingTexture, wrapType, filterType);
}
public TextureInfo addAtcTexture(ByteBuffer buffer, int width, int height, TextureType textureType, AtcFormat format) {
return addAtcTexture(buffer, width, height, textureType, format, false, WrapType.REPEAT, FilterType.LINEAR);
}
/**
* Adds and binds AMD compressed texture. To use mipmaps, pass in more than one items in the buffer,
* starting with mipmap level 0.
*
* This method will only work on devices that support AMD texture compression. Most Adreno GPU
* based devices such as Nexus One and HTC Desire support AMD texture compression.
*/
public TextureInfo addAtcTexture(ByteBuffer[] buffer, int width, int height, TextureType textureType, AtcFormat format, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
int internalformat;
switch(format) {
case RGB:
internalformat = GLES11Ext.GL_ATC_RGB_AMD;
break;
case RGBA_EXPLICIT:
default:
internalformat = GLES11Ext.GL_ATC_RGBA_EXPLICIT_ALPHA_AMD;
break;
case RGBA_INTERPOLATED:
internalformat = GLES11Ext.GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD;
break;
}
return addTexture(buffer, null, width, height, textureType, null, false, false, isExistingTexture, wrapType, filterType, CompressionType.ATC, internalformat);
}
public TextureInfo addAtcTexture(ByteBuffer buffer, int width, int height, TextureType textureType, AtcFormat format, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
return addAtcTexture(new ByteBuffer[]{buffer}, width, height, textureType, format, isExistingTexture, wrapType, filterType);
}
public TextureInfo addDxt1Texture(ByteBuffer buffer, int width, int height, TextureType textureType, Dxt1Format format) {
return addDxt1Texture(buffer, width, height, textureType, format, false, WrapType.REPEAT, FilterType.LINEAR);
}
/**
* Adds and binds DXT1 variant of S3 compressed texture.
*
* This method will only work in devices that support S3 texture compression. Most Nvidia Tegra2 GPU
* based devices such as Motorola Xoom, and Atrix support S3 texture comrpession.
*/
public TextureInfo addDxt1Texture(ByteBuffer[] buffer, int width, int height, TextureType textureType, Dxt1Format format, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
int internalformat;
switch (format) {
case RGB:
internalformat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
break;
case RGBA:
default:
internalformat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
break;
}
return addTexture(buffer, null, width, height, textureType, null, false, false, isExistingTexture, wrapType, filterType, CompressionType.DXT1, internalformat);
}
public TextureInfo addDxt1Texture(ByteBuffer buffer, int width, int height, TextureType textureType, Dxt1Format format, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
return addDxt1Texture(new ByteBuffer[]{buffer}, width, height, textureType, format, isExistingTexture, wrapType, filterType);
}
public TextureInfo addPvrtcTexture(ByteBuffer buffer, int width, int height, TextureType textureType, PvrtcFormat format) {
return addPvrtcTexture(buffer, width, height, textureType, format, false, WrapType.REPEAT, FilterType.LINEAR);
}
/**
* Adds and binds PowerVR compressed texture. Due to limitations with compressed textures,
* automatic mipmap generation is disabled.
*
* This method will only work on devices that support PowerVR texture compression. Most PowerVR GPU
* based devices such as Nexus S and Galaxy S3 support AMD texture compression.
*/
public TextureInfo addPvrtcTexture(ByteBuffer[] buffer, int width, int height, TextureType textureType, PvrtcFormat format, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
int internalformat;
switch (format) {
case RGB_2BPP:
internalformat = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
break;
case RGB_4BPP:
internalformat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
break;
case RGBA_2BPP:
internalformat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
break;
case RGBA_4BPP:
default:
internalformat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
break;
}
return addTexture(buffer, null, width, height, textureType, null, false, false, isExistingTexture, wrapType, filterType, CompressionType.PVRTC, internalformat);
}
public TextureInfo addPvrtcTexture(ByteBuffer buffer, int width, int height, TextureType textureType, PvrtcFormat format, boolean isExistingTexture, WrapType wrapType, FilterType filterType) {
return addPvrtcTexture(new ByteBuffer[]{buffer}, width, height, textureType, format, isExistingTexture, wrapType, filterType);
}
/**
* This only works for API Level 15 and higher.
* Thanks to Lubomir Panak (@drakh)
* <p>
* How to use:
* <pre><code>
* protected void initScene() {
* super.initScene();
* mLight = new DirectionalLight(0, 0, 1);
* mCamera.setPosition(0, 0, -17);
*
* VideoMaterial material = new VideoMaterial();
* TextureInfo tInfo = mTextureManager.addVideoTexture();
*
* mTexture = new SurfaceTexture(tInfo.getTextureId());
*
* mMediaPlayer = MediaPlayer.create(getContext(), R.raw.nemo);
* mMediaPlayer.setSurface(new Surface(mTexture));
* mMediaPlayer.start();
*
* BaseObject3D cube = new Plane(2, 2, 1, 1);
* cube.setMaterial(material);
* cube.addTexture(tInfo);
* cube.addLight(mLight);
* addChild(cube);
* }
*
* public void onDrawFrame(GL10 glUnused) {
* mTexture.updateTexImage();
* super.onDrawFrame(glUnused);
* }
* </code></pre>
* @return
*/
public TextureInfo addVideoTexture() {
TextureType textureType = TextureType.VIDEO_TEXTURE;
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
int textureId = textures[0];
GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId);
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,
GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,
GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
TextureInfo textureInfo = new TextureInfo(textureId, textureType);
return textureInfo;
}
public TextureInfo addCubemapTextures(int[] textures) {
return addCubemapTextures(textures, false);
}
public TextureInfo addCubemapTextures(int[] textures, boolean mipmap) {
return addCubemapTextures(textures, false, false);
}
public TextureInfo addCubemapTextures(int[] textures, boolean mipmap, boolean recycle) {
return addCubemapTextures(textures, mipmap, recycle, false);
}
public TextureInfo addCubemapTextures(int[] textures, boolean mipmap, boolean recycle, boolean isExistingTexture) {
final Bitmap[] bitmapTextures = new Bitmap[textures.length];
for (int i=0;i<textures.length;i++)
bitmapTextures[i] = getBitmap(textures[i]);
return addCubemapTextures(bitmapTextures, mipmap, recycle, isExistingTexture);
}
public TextureInfo addCubemapTextures(Bitmap[] textures) {
return addCubemapTextures(textures, false);
}
public TextureInfo addCubemapTextures(Bitmap[] textures, boolean mipmap) {
return addCubemapTextures(textures, false, false);
}
public TextureInfo addCubemapTextures(Bitmap[] textures, boolean mipmap, boolean recycle) {
return addCubemapTextures(textures, mipmap, recycle, false);
}
public TextureInfo addCubemapTextures(Bitmap[] textures, boolean mipmap, boolean recycle, boolean isExistingTexture) {
int[] textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0);
int textureId = textureIds[0];
TextureInfo textureInfo = mCurrentValidatingTexInfo == null ? new TextureInfo(textureId) : mCurrentValidatingTexInfo;
if(!isExistingTexture && mCurrentValidatingTexInfo == null) mTextureInfoList.add(textureInfo);
if(mCurrentValidatingTexInfo == null) {
textureInfo.setWidth(textures[0].getWidth());
textureInfo.setHeight(textures[0].getHeight());
textureInfo.setTextureType(TextureType.CUBE_MAP);
textureInfo.setBitmapConfig(textures[0].getConfig());
textureInfo.setCompressionType(CompressionType.NONE);
textureInfo.setMipmap(mipmap);
textureInfo.shouldRecycle(recycle);
textureInfo.setIsCubeMap(true);
} else {
textureInfo.setTextureId(textureId);
}
if(textureId > 0) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, textureId);
if(mipmap)
GLES20.glTexParameterf(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR);
else
GLES20.glTexParameterf(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
for(int i=0; i<6; i++) {
GLES20.glHint(GLES20.GL_GENERATE_MIPMAP_HINT, GLES20.GL_NICEST);
GLUtils.texImage2D(CUBE_FACES[i], 0, textures[i], 0);
if(recycle && textureId > 0) textures[i].recycle();
}
if(mipmap) GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_CUBE_MAP);
} else {
mShouldValidateTextures = true;
}
if(!recycle)
textureInfo.setTextures(textures);
GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, textureId);
return textureInfo;
}
public void updateCubemapTextures(TextureInfo textureInfo, Bitmap[] textures)
{
textureInfo.setTextures(textures);
mShouldUpdateTextures = true;
mTexturesToUpdate.add(textureInfo);
}
public void updateCubemapTextures(TextureInfo textureInfo)
{
Bitmap[] textures = textureInfo.getTextures();
GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, textureInfo.getTextureId());
for(int i=0; i<6; i++) {
int bitmapFormat = textures[i].getConfig() == Config.ARGB_8888 ? GLES20.GL_RGBA : GLES20.GL_RGB;
GLUtils.texSubImage2D(CUBE_FACES[i], 0, 0, 0, textures[i], bitmapFormat, GLES20.GL_UNSIGNED_BYTE);
}
if(textureInfo.isMipmap())
GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_CUBE_MAP);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
public int getNumTextures() {
return mTextureInfoList.size();
}
/**
* Please use updateTexture(TextureInfo textureInfo, Bitmap texture)
* @deprecated
* @param textureId
* @param texture
*/
@Deprecated
public void updateTexture(Integer textureId, Bitmap texture) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId.intValue());
GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, texture);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
public void updateTexture(TextureInfo textureInfo, Bitmap texture) {
textureInfo.setTexture(texture);
mShouldUpdateTextures = true;
mTexturesToUpdate.add(textureInfo);
}
public void updateTexture(TextureInfo textureInfo) {
Bitmap texture = textureInfo.getTexture();
int bitmapFormat = texture.getConfig() == Config.ARGB_8888 ? GLES20.GL_RGBA : GLES20.GL_RGB;
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureInfo.getTextureId());
GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, texture, bitmapFormat, GLES20.GL_UNSIGNED_BYTE);
if(textureInfo.isMipmap())
GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
public void reload() {
TextureInfo tInfo = null;
int len = getNumTextures();
for(int i=0; i<len; i++) {
tInfo = mTextureInfoList.get(i);
tInfo.setFrom(addTexture(tInfo));
}
}
public void reset() {
int count = mTextureInfoList.size();
int[] textures = new int[count];
for(int i=0; i<count; i++)
{
TextureInfo ti = mTextureInfoList.get(i);
if(ti.getTexture() != null) ti.getTexture().recycle();
textures[i] = ti.getTextureId();
}
GLES20.glDeleteTextures(count, textures, 0);
mTextureInfoList.clear();
}
public void removeTexture(TextureInfo textureInfo) {
mTextureInfoList.remove(textureInfo);
GLES20.glDeleteTextures(1, new int[]{textureInfo.getTextureId()}, 0);
}
public void removeTextures(ArrayList<TextureInfo> textureInfoList) {
try {
int count = textureInfoList.size();
int[] textures = new int[count];
int i;
for (i = 0; i < count; ++i) {
Integer textureId = textureInfoList.get(i).getTextureId();
textures[i] = textureId.intValue();
mTextureInfoList.remove(textureInfoList.get(i));
}
textureInfoList.clear();
GLES20.glDeleteTextures(count, textures, 0);
} catch (Exception e) {
Log.e("RajawaliTextureManager", e.getMessage());
}
}
public List<TextureInfo> getTextureInfoList() {
return mTextureInfoList;
}
public void validateTextures() {
if(mShouldValidateTextures) {
// Iterating a temporary list is better for the memory
// consumption than using iterators.
List<TextureInfo> tempList = new ArrayList<TextureInfo>();
tempList.addAll(mTextureInfoList);
for (int i = 0; i < tempList.size(); ++i) {
TextureInfo inf = tempList.get(i);
if(inf.getTextureId() == 0) {
mCurrentValidatingTexInfo = inf;
addTexture(inf);
mCurrentValidatingTexInfo = null;
}
}
mShouldValidateTextures = false;
}
if(mShouldUpdateTextures) {
while(!mTexturesToUpdate.isEmpty())
{
TextureInfo tInfo = mTexturesToUpdate.pop();
if(tInfo.isCubeMap())
updateCubemapTextures(tInfo);
else
updateTexture(tInfo);
}
mShouldUpdateTextures = false;
}
}
/**
* Retrieve a bitmap for the given resource.
*
* @param resID
* @return
*/
private final Bitmap getBitmap(int resID) {
return BitmapFactory.decodeResource(mContext.getResources(), resID);
}
}