Merge pull request #264 from kostyantyn-dev/master

iOS: Fixed incorrect behaviour in J3MLoader. Added ogg loader
experimental
kostyantyn-dev 10 years ago
commit 544bbcda4e
  1. 4
      jme3-ios/src/main/java/com/jme3/asset/IOS.cfg
  2. 2573
      jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java
  3. 576
      jme3-ios/src/main/java/com/jme3/renderer/ios/TextureUtil.java
  4. 4
      jme3-ios/src/main/resources/com/jme3/asset/IOS.cfg

@ -2,7 +2,9 @@ INCLUDE com/jme3/asset/General.cfg
# IOS specific loaders
LOADER com.jme3.system.ios.IosImageLoader : jpg, bmp, gif, png, jpeg
LOADER com.jme3.material.plugins.J3MLoader : j3m, j3md
LOADER com.jme3.audio.plugins.OGGLoader : ogg
LOADER com.jme3.material.plugins.J3MLoader : j3m
LOADER com.jme3.material.plugins.J3MLoader : j3md
LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, glsl, glsllib
LOADER com.jme3.export.binary.BinaryImporter : j3o
LOADER com.jme3.font.plugins.BitmapFontLoader : fnt

@ -1,576 +0,0 @@
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);
}
}
/**
* <code>uploadTextureBitmap</code> uploads a native android bitmap
*/
/*
public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips) {
uploadTextureBitmap(target, bitmap, needMips, false, 0, 0);
}
/**
* <code>uploadTextureBitmap</code> 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 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 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 && img.isNPOT()) {
// Check if texture is POT
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 && img.isNPOT()) {
// Check if texture is POT
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];
}
}
}

@ -2,7 +2,9 @@ INCLUDE com/jme3/asset/General.cfg
# IOS specific loaders
LOADER com.jme3.system.ios.IosImageLoader : jpg, bmp, gif, png, jpeg
LOADER com.jme3.material.plugins.J3MLoader : j3m, j3md
LOADER com.jme3.audio.plugins.OGGLoader : ogg
LOADER com.jme3.material.plugins.J3MLoader : j3m
LOADER com.jme3.material.plugins.J3MLoader : j3md
LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, glsl, glsllib
LOADER com.jme3.export.binary.BinaryImporter : j3o
LOADER com.jme3.font.plugins.BitmapFontLoader : fnt

Loading…
Cancel
Save