From e5478faae05762a5169fe741963a912ae8357618 Mon Sep 17 00:00:00 2001 From: "kim..ng" Date: Sun, 29 May 2011 17:46:25 +0000 Subject: [PATCH] Android: added support for cube maps, Sky works now, use AndroidSkyFactory git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7531 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../renderer/android/OGLESShaderRenderer.java | 151 +++++++++--------- .../jme3/renderer/android/TextureUtil.java | 30 ++-- .../jme3/util/android/AndroidSkyFactory.java | 148 +++++++++++++++++ 3 files changed, 247 insertions(+), 82 deletions(-) create mode 100644 engine/src/android/com/jme3/util/android/AndroidSkyFactory.java diff --git a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java index 95cca4b3b..ce85507a1 100644 --- a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java +++ b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java @@ -1708,50 +1708,59 @@ public class OGLESShaderRenderer implements Renderer { } } - private void setupTextureParams(Texture tex){ + /** + * setupTextureParams sets the OpenGL context texture parameters + * @param tex the Texture to set the texture parameters from + */ + private void setupTextureParams(Texture tex) + { int target = convertTextureType(tex.getType()); // filter things int minFilter = convertMinFilter(tex.getMinFilter()); int magFilter = convertMagFilter(tex.getMagFilter()); - if (verboseLogging) - logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_MIN_FILTER, " + minFilter + ")"); + if (verboseLogging) + logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_MIN_FILTER, " + minFilter + ")"); GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_MIN_FILTER, minFilter); - if (verboseLogging) - logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_MAG_FILTER, " + magFilter + ")"); + if (verboseLogging) + logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_MAG_FILTER, " + magFilter + ")"); GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_MAG_FILTER, magFilter); + /* if (tex.getAnisotropicFilter() > 1){ -/* + if (GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic){ glTexParameterf(target, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, tex.getAnisotropicFilter()); } -*/ + } + */ // repeat modes switch (tex.getType()){ case ThreeDimensional: case CubeMap: // cubemaps use 3D coords -// GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R))); + // GL_TEXTURE_WRAP_R is not available in api 8 + //GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R))); case TwoDimensional: case TwoDimensionalArray: - if (verboseLogging) - logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_WRAP_T, " + convertWrapMode(tex.getWrap(WrapAxis.T))); + if (verboseLogging) + logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_WRAP_T, " + convertWrapMode(tex.getWrap(WrapAxis.T))); GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T))); + // fall down here is intentional.. -// case OneDimensional: +// case OneDimensional: - if (verboseLogging) - logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_WRAP_S, " + convertWrapMode(tex.getWrap(WrapAxis.S))); + if (verboseLogging) + logger.info("GLES20.glTexParameteri(" + target + ", GLES20.GL_TEXTURE_WRAP_S, " + convertWrapMode(tex.getWrap(WrapAxis.S))); GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S))); break; @@ -1773,13 +1782,20 @@ public class OGLESShaderRenderer implements Renderer { */ } - public void updateTexImageData(Image img, Texture.Type type, boolean mips){ + /** + * updateTexImageData activates and binds the texture + * @param img + * @param type + * @param mips + */ + public void updateTexImageData(Image img, Texture.Type type, boolean mips) + { int texId = img.getId(); - if (texId == -1){ + if (texId == -1) + { // create texture - - if (verboseLogging) - logger.info("GLES20.glGenTexture(1, buffer)"); + if (verboseLogging) + logger.info("GLES20.glGenTexture(1, buffer)"); GLES20.glGenTextures(1, intBuf1); texId = intBuf1.get(0); @@ -1791,72 +1807,56 @@ public class OGLESShaderRenderer implements Renderer { // bind texture int target = convertTextureType(type); - if (context.boundTextures[0] != img){ - if (context.boundTextureUnit != 0){ - - if (verboseLogging) - logger.info("GLES20.glActiveTexture(GLES20.GL_TEXTURE0)"); + if (context.boundTextures[0] != img) + { + if (context.boundTextureUnit != 0) + { + if (verboseLogging) + logger.info("GLES20.glActiveTexture(GLES20.GL_TEXTURE0)"); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); context.boundTextureUnit = 0; } - if (verboseLogging) - logger.info("GLES20.glBindTexture(" + target + ", " + texId + ")"); + if (verboseLogging) + logger.info("GLES20.glBindTexture(" + target + ", " + texId + ")"); GLES20.glBindTexture(target, texId); context.boundTextures[0] = img; } - if (!img.hasMipmaps() && mips){ - // No pregenerated mips available, - // generate from base level if required -// if (!GLContext.getCapabilities().GL_EXT_framebuffer_multisample){ - - if (verboseLogging) - logger.info("GLES20.glTexParameteri(" + target + "GLES11.GL_GENERATE_MIMAP, GLES20.GL_TRUE)"); - GLES20.glTexParameteri(target, GLES11.GL_GENERATE_MIPMAP, GLES20.GL_TRUE); -// } - }else{ -// glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0 ); - if (img.getMipMapSizes() != null){ -// GLES20.glTexParameteri(target, GLES11.GL_TEXTURE_MAX_LEVEL, img.getMipMapSizes().length ); + if (target == GLES20.GL_TEXTURE_CUBE_MAP) + { + // Upload a cube map / sky box + @SuppressWarnings("unchecked") + List bmps = (List)img.getEfficentData(); + if (bmps.size() != 6) + { + throw new UnsupportedOperationException("Invalid texture: " + img + + "Cubemap textures must contain 6 data units." ); + } + for (int i = 0; i < 6; i++) + { + TextureUtil.uploadTextureBitmap(GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, bmps.get(i), false, powerOf2); } } + else + { + TextureUtil.uploadTexture(img, target, 0, 0, tdc, false, powerOf2); + if (verboseLogging) + logger.info("GLES20.glTexParameteri(" + target + "GLES11.GL_GENERATE_MIMAP, GLES20.GL_TRUE)"); - if (target == GLES20.GL_TEXTURE_CUBE_MAP){ - List data = img.getData(); - if (data.size() != 6){ - logger.log(Level.WARNING, "Invalid texture: {0}\n" - + "Cubemap textures must contain 6 data units.", img); - return; - } - for (int i = 0; i < 6; i++){ - TextureUtil.uploadTexture(img, GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc, true, powerOf2); - } - }/*else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT){ - List data = img.getData(); - // -1 index specifies prepare data for 2D Array - TextureUtil.uploadTexture(img, target, -1, 0, tdc); - for (int i = 0; i < data.size(); i++){ - // upload each slice of 2D array in turn - // this time with the appropriate index - TextureUtil.uploadTexture(img, target, i, 0, tdc); + if (!img.hasMipmaps() && mips) + { + // No pregenerated mips available, + // generate from base level if required + if (verboseLogging) + logger.info("GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D)"); + GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D); } - }*/else{ - TextureUtil.uploadTexture(img, target, 0, 0, tdc, true, powerOf2); - - if (verboseLogging) - logger.info("GLES20.glTexParameteri(" + target + "GLES11.GL_GENERATE_MIMAP, GLES20.GL_TRUE)"); - - GLES20.glTexParameteri(target, GLES11.GL_GENERATE_MIPMAP, GLES20.GL_TRUE); - } - -// if (GLContext.getCapabilities().GL_EXT_framebuffer_multisample){ -// glGenerateMipmapEXT(target); -// } + } img.clearUpdateNeeded(); } @@ -1866,14 +1866,19 @@ public class OGLESShaderRenderer implements Renderer { Image image = tex.getImage(); if (image.isUpdateNeeded()) { + /* Bitmap bmp = (Bitmap)image.getEfficentData(); - // Check if the bitmap got recycled, can happen after wakeup/restart - if ( bmp.isRecycled() ) + if (bmp != null) { - // We need to reload the bitmap - Texture textureReloaded = JmeSystem.newAssetManager().loadTexture((TextureKey)tex.getKey()); - image.setEfficentData( textureReloaded.getImage().getEfficentData()); + // Check if the bitmap got recycled, can happen after wakeup/restart + if ( bmp.isRecycled() ) + { + // We need to reload the bitmap + Texture textureReloaded = JmeSystem.newAssetManager().loadTexture((TextureKey)tex.getKey()); + image.setEfficentData( textureReloaded.getImage().getEfficentData()); + } } + */ updateTexImageData(image, tex.getType(), tex.getMinFilter().usesMipMapLevels()); } diff --git a/engine/src/android/com/jme3/renderer/android/TextureUtil.java b/engine/src/android/com/jme3/renderer/android/TextureUtil.java index f1eea426d..9e34d32cb 100644 --- a/engine/src/android/com/jme3/renderer/android/TextureUtil.java +++ b/engine/src/android/com/jme3/renderer/android/TextureUtil.java @@ -62,11 +62,21 @@ public class TextureUtil { } } - private static void uploadTextureBitmap(Bitmap bitmap, boolean generateMips, boolean powerOf2){ - if (!powerOf2){ + /** + * uploadTextureBitmap uploads a native android bitmap + * @param target + * @param bitmap + * @param generateMips + * @param powerOf2 + */ + public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean generateMips, boolean powerOf2) + { + if (!powerOf2) + { int width = bitmap.getWidth(); int height = bitmap.getHeight(); - if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)){ + if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) + { // scale to power of two width = FastMath.nearestPowerOfTwo(width); height = FastMath.nearestPowerOfTwo(height); @@ -76,11 +86,14 @@ public class TextureUtil { } } - if (generateMips){ + if (generateMips) + { buildMipmap(bitmap); - }else{ - GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); - bitmap.recycle(); + } + else + { + GLUtils.texImage2D(target, 0, bitmap, 0); + //bitmap.recycle(); } } @@ -95,8 +108,7 @@ public class TextureUtil { if (img.getEfficentData() instanceof Bitmap){ Bitmap bitmap = (Bitmap) img.getEfficentData(); - uploadTextureBitmap(bitmap, generateMips, powerOf2); -// img.setEfficentData(null); + uploadTextureBitmap(target, bitmap, generateMips, powerOf2); return; } diff --git a/engine/src/android/com/jme3/util/android/AndroidSkyFactory.java b/engine/src/android/com/jme3/util/android/AndroidSkyFactory.java new file mode 100644 index 000000000..7f11d3bd5 --- /dev/null +++ b/engine/src/android/com/jme3/util/android/AndroidSkyFactory.java @@ -0,0 +1,148 @@ +package com.jme3.util.android; + +import java.util.ArrayList; +import android.graphics.Bitmap; + +import com.jme3.asset.AssetManager; +import com.jme3.asset.TextureKey; +import com.jme3.material.Material; +import com.jme3.math.Vector3f; +import com.jme3.renderer.queue.RenderQueue.Bucket; +import com.jme3.scene.Geometry; +import com.jme3.scene.Spatial; +import com.jme3.scene.shape.Sphere; +import com.jme3.texture.Image; +import com.jme3.texture.Image.Format; +import com.jme3.texture.Texture; +import com.jme3.texture.TextureCubeMap; + + +/** + * AndroidSkyFactory creates a sky box spatial + * @author larynx, derived from SkyFactory and adapted for android + * + */ +public class AndroidSkyFactory +{ + private static final Sphere sphereMesh = new Sphere(10, 10, 101f, false, true); + + public static Spatial createSky(AssetManager assetManager, Texture texture, Vector3f normalScale, boolean sphereMap) + { + Geometry sky = new Geometry("Sky", sphereMesh); + sky.setQueueBucket(Bucket.Sky); + sky.setCullHint(Spatial.CullHint.Never); + + Material skyMat = new Material(assetManager, "Common/MatDefs/Misc/Sky.j3md"); + skyMat.setVector3("NormalScale", normalScale); + if (sphereMap) + { + skyMat.setBoolean("SphereMap", sphereMap); + } + else if (!(texture instanceof TextureCubeMap)) + { + // make sure its a cubemap + Image img = texture.getImage(); + texture = new TextureCubeMap(); + texture.setImage(img); + } + skyMat.setTexture("Texture", texture); + sky.setMaterial(skyMat); + + return sky; + } + + private static void checkImage(Image image) + { + if (image.getWidth() != image.getHeight()) + throw new IllegalArgumentException("Image width and height must be the same"); + + if (image.getMultiSamples() != 1) + throw new IllegalArgumentException("Multisample textures not allowed"); + } + + private static void checkImagesForCubeMap(Image ... images) + { + if (images.length == 1) return; + + Format fmt = images[0].getFormat(); + int width = images[0].getWidth(); + int height = images[0].getHeight(); + + checkImage(images[0]); + + for (int i = 1; i < images.length; i++) + { + Image image = images[i]; + checkImage(images[i]); + if (image.getFormat() != fmt) throw new IllegalArgumentException("Images must have same format"); + if (image.getWidth() != width) throw new IllegalArgumentException("Images must have same width"); + if (image.getHeight() != height) throw new IllegalArgumentException("Images must have same height"); + } + } + + public static Spatial createSky(AssetManager assetManager, Texture west, Texture east, Texture north, Texture south, + Texture up, Texture down, Vector3f normalScale) + { + Geometry sky = new Geometry("Sky", sphereMesh); + sky.setQueueBucket(Bucket.Sky); + sky.setCullHint(Spatial.CullHint.Never); + + Image westImg = west.getImage(); + Image eastImg = east.getImage(); + Image northImg = north.getImage(); + Image southImg = south.getImage(); + Image upImg = up.getImage(); + Image downImg = down.getImage(); + + checkImagesForCubeMap(westImg, eastImg, northImg, southImg, upImg, downImg); + + Image cubeImage = new Image(westImg.getFormat(), westImg.getWidth(), westImg.getHeight(), null); + + ArrayList arrayList = new ArrayList(6); + + arrayList.add((Bitmap)westImg.getEfficentData()); + arrayList.add((Bitmap)eastImg.getEfficentData()); + + arrayList.add((Bitmap)downImg.getEfficentData()); + arrayList.add((Bitmap)upImg.getEfficentData()); + + arrayList.add((Bitmap)southImg.getEfficentData()); + arrayList.add((Bitmap)northImg.getEfficentData()); + + cubeImage.setEfficentData(arrayList); + + TextureCubeMap cubeMap = new TextureCubeMap(cubeImage); + cubeMap.setAnisotropicFilter(0); + cubeMap.setMagFilter(Texture.MagFilter.Bilinear); + cubeMap.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); + cubeMap.setWrap(Texture.WrapMode.EdgeClamp); + + + Material skyMat = new Material(assetManager, "Common/MatDefs/Misc/Sky.j3md"); + skyMat.setTexture("Texture", cubeMap); + skyMat.setVector3("NormalScale", normalScale); + sky.setMaterial(skyMat); + + return sky; + } + + public static Spatial createSky(AssetManager assetManager, Texture west, Texture east, Texture north, Texture south, + Texture up, Texture down) + { + return createSky(assetManager, west, east, north, south, up, down, Vector3f.UNIT_XYZ); + } + + public static Spatial createSky(AssetManager assetManager, Texture texture, boolean sphereMap) + { + return createSky(assetManager, texture, Vector3f.UNIT_XYZ, sphereMap); + } + + public static Spatial createSky(AssetManager assetManager, String textureName, boolean sphereMap) + { + TextureKey key = new TextureKey(textureName, true); + key.setGenerateMips(true); + key.setAsCube(!sphereMap); + Texture tex = assetManager.loadTexture(key); + return createSky(assetManager, tex, sphereMap); + } +} \ No newline at end of file