2
0
Эх сурвалжийг харах

src/android patchset: changes AndroidAssetManager, AndroidInput, OGLESContext, JmeSystem, TextureLoader

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7502 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
kim..ng 14 жил өмнө
parent
commit
a8e9d803dc

+ 287 - 0
engine/src/android/com/jme3/app/android/AndroidApplication.java

@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.app.android;
+
+import java.nio.CharBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import com.jme3.app.Application;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.input.android.AndroidInput;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial.CullHint;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeSystem;
+import com.jme3.util.FastInteger;
+
+
+/**
+ * <code>AndroidApplication</code> extends the {@link com.jme3.app.Application}
+ * class to provide default functionality like a first-person camera,
+ * and an accessible root node that is updated and rendered regularly.
+ * It will display the current frames-per-second value on-screen.
+ * 
+ *
+ */
+public abstract class AndroidApplication extends Application implements DialogInterface.OnClickListener 
+{
+
+    protected Node rootNode = new Node("Root Node");
+    protected Node guiNode = new Node("Gui Node");
+    protected float secondCounter = 0.0f;
+    protected BitmapText fpsText;
+    protected CharBuffer textBuf = CharBuffer.allocate(50);
+    protected char[] fpsBuf = new char[16];
+    protected BitmapFont guiFont;
+    
+    protected Activity activity;
+    protected AndroidInput input;
+    protected final AtomicBoolean loadingFinished;
+    
+    public AndroidApplication() 
+    {
+        this(null, null);
+    }
+
+    public AndroidApplication(Activity activity, AndroidInput input) 
+    {
+        super();
+        this.activity = activity;
+        this.input = input;
+        
+        loadingFinished = new AtomicBoolean(false);
+    }
+
+    @Override
+    public void start() 
+    {
+        // Set the correct xml parser driver for android
+        System.setProperty("org.xml.sax.driver","org.xmlpull.v1.sax2.Driver");
+        
+        if (settings == null) 
+        {
+            settings = new AppSettings(true);
+        }
+
+        // Use vertex arrays for rendering
+        settings.putBoolean("USE_VA", true);
+        // Verbose logging off
+        settings.putBoolean("VERBOSE_LOGGING", false);
+       
+        //re-setting settings they can have been merged from the registry.
+        setSettings(settings);
+        super.start();
+    }
+
+
+    /**
+     * Retrieves guiNode
+     * @return guiNode Node object
+     *
+     */
+    public Node getGuiNode() {
+        return guiNode;
+    }
+
+    /**
+     * Retrieves rootNode
+     * @return rootNode Node object
+     *
+     */
+    public Node getRootNode() {
+        return rootNode;
+    }
+
+
+    /**
+     * Attaches FPS statistics to guiNode and displays it on the screen.
+     *
+     */
+    public void loadFPSText() {
+        guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+        fpsText = new BitmapText(guiFont, false);
+        fpsText.setLocalTranslation(0, fpsText.getLineHeight(), 0);
+        fpsText.setText("Frames per second");
+        guiNode.attachChild(fpsText);
+    }
+
+    @Override
+    public void initialize() 
+    {
+        // Create a default Android assetmanager before Application can create one in super.initialize();
+        assetManager = JmeSystem.newAssetManager(null);        
+        super.initialize();
+
+        guiNode.setQueueBucket(Bucket.Gui);
+        guiNode.setCullHint(CullHint.Never);
+        loadFPSText();
+        viewPort.attachScene(rootNode);
+        guiViewPort.attachScene(guiNode);
+
+        // call user code
+        init();
+        
+        // Start thread for async load
+        Thread t = new Thread(new Runnable()
+        {
+            @Override
+            public void run ()
+            {
+                try
+                {
+                    // call user code
+                    asyncload();
+                }
+                catch (Exception e)
+                {
+                    handleError("AsyncLoad failed", e);
+                }
+                loadingFinished.set(true);
+            }
+        });
+        t.setDaemon(true);
+        t.start();
+    }
+
+    @Override
+    public void update() {
+        super.update(); // makes sure to execute AppTasks
+        if (speed == 0 || paused) {
+            return;
+        }
+
+        float tpf = timer.getTimePerFrame() * speed;
+
+        secondCounter += timer.getTimePerFrame();
+        int fps = (int) timer.getFrameRate();
+        if (secondCounter >= 5.0f) {
+            textBuf.clear();
+            textBuf.put("Frames per second: ");            
+            FastInteger.toCharArray(fps, fpsBuf);
+            textBuf.put(fpsBuf);
+            textBuf.flip();
+            fpsText.setText(textBuf);
+            secondCounter = 0.0f;
+        }
+
+        // update states
+        stateManager.update(tpf);
+
+        // simple update and root node
+        update(tpf);
+        rootNode.updateLogicalState(tpf);
+        guiNode.updateLogicalState(tpf);
+        rootNode.updateGeometricState();
+        guiNode.updateGeometricState();
+
+        // render states
+        stateManager.render(renderManager);
+        renderManager.render(tpf);
+        render(renderManager);
+        stateManager.postRender();
+    }
+
+    public abstract void init();
+
+    public void update(float tpf) {
+    }
+
+    public void render(RenderManager rm) {
+    }
+    
+    
+    /**
+     * Gets called by a different thread to allow
+     * async loading of assets.
+     * 
+     * This means that update and rendering can already
+     * happen while some assets are still loading.
+     */
+    public void asyncload()
+    {
+        
+    }
+    
+    /**
+     * Called when an error has occured. This is typically
+     * invoked when an uncought exception is thrown in the render thread.
+     * @param errorMsg The error message, if any, or null.
+     * @param t Throwable object, or null.
+     */
+    @Override
+    public void handleError(final String errorMsg, final Throwable t)
+    {
+        
+        String s = "";
+        if (t != null && t.getStackTrace() != null)
+        {
+            for (StackTraceElement ste: t.getStackTrace())
+            {
+                s +=  ste.getClassName() + "." + ste.getMethodName() + "(" + + ste.getLineNumber() + ") ";
+            }
+        }
+        final String sTrace = s;
+        activity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() 
+            {                                
+                AlertDialog dialog = new AlertDialog.Builder(activity)
+               // .setIcon(R.drawable.alert_dialog_icon)
+                .setTitle(t != null ? t.toString() : "Failed")
+                .setPositiveButton("Kill", AndroidApplication.this)
+                .setMessage((errorMsg != null ? errorMsg + ": " : "") + sTrace)
+                .create();    
+                dialog.show();                
+            }
+        });
+        
+    }
+
+    
+    /**
+     * Called by the android alert dialog, terminate the activity and OpenGL rendering
+     * @param dialog
+     * @param whichButton
+     */
+    public void onClick(DialogInterface dialog, int whichButton) 
+    {
+        this.stop();
+        activity.finish();
+    }
+
+}

+ 98 - 249
engine/src/android/com/jme3/asset/AndroidAssetManager.java

@@ -1,269 +1,118 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
 package com.jme3.asset;
 
-import com.jme3.asset.plugins.AndroidLocator;
-import com.jme3.audio.AudioData;
-import com.jme3.audio.AudioKey;
-import com.jme3.export.binary.BinaryExporter;
-import com.jme3.export.binary.BinaryImporter;
-import com.jme3.font.BitmapFont;
-import com.jme3.font.plugins.BitmapFontLoader;
-import com.jme3.material.Material;
-import com.jme3.material.plugins.J3MLoader;
-import com.jme3.scene.Spatial;
-import com.jme3.shader.Shader;
-import com.jme3.shader.ShaderKey;
-import com.jme3.shader.plugins.GLSLLoader;
-import com.jme3.texture.Image;
 import com.jme3.texture.Texture;
 import com.jme3.texture.plugins.AndroidImageLoader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.HashMap;
-import java.util.logging.Level;
+import java.net.URL;
 import java.util.logging.Logger;
 
+import com.jme3.asset.plugins.AndroidLocator;
+import com.jme3.asset.plugins.ClasspathLocator;
+
 /**
- * AssetManager for Android
+ * <code>AndroidAssetManager</code> is an implementation of DesktopAssetManager for Android
  *
- * @author Kirill Vainer
+ * @author larynx
  */
-public final class AndroidAssetManager implements AssetManager {
+public class AndroidAssetManager extends DesktopAssetManager {
 
     private static final Logger logger = Logger.getLogger(AndroidAssetManager.class.getName());
 
-    private final AndroidLocator locator = new AndroidLocator();
-    private final AndroidImageLoader imageLoader = new AndroidImageLoader();
-    private final BinaryImporter modelLoader = new BinaryImporter();
-    private final BitmapFontLoader fontLoader = new BitmapFontLoader();
-    private final J3MLoader j3mLoader = new J3MLoader();
-    private final J3MLoader j3mdLoader = new J3MLoader();
-    private final GLSLLoader glslLoader = new GLSLLoader();
-
-    private final BinaryExporter exporter = new BinaryExporter();
-    private final HashMap<AssetKey, Object> cache = new HashMap<AssetKey, Object>();
-
     public AndroidAssetManager(){
-        this(false);
-    }
-
-    public AndroidAssetManager(boolean loadDefaults){
-        if (loadDefaults){
-//            AssetConfig cfg = new AssetConfig(this);
-//            InputStream stream = AssetManager.class.getResourceAsStream("Desktop.cfg");
-//            try{
-//                cfg.loadText(stream);
-//            }catch (IOException ex){
-//                logger.log(Level.SEVERE, "Failed to load asset config", ex);
-//            }finally{
-//                if (stream != null)
-//                    try{
-//                        stream.close();
-//                    }catch (IOException ex){
-//                    }
-//            }
+        this(null);
+    }
+
+    @Deprecated
+    public AndroidAssetManager(boolean loadDefaults){    	
+        //this(Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Android.cfg"));
+    	this(null);
+    }
+
+    /**
+     * AndroidAssetManager constructor
+     * If URL == null then a default list of locators and loaders for android is set
+     * @param configFile
+     */
+    public AndroidAssetManager(URL configFile)
+    {   
+        super(configFile);
+        System.setProperty("org.xml.sax.driver","org.xmlpull.v1.sax2.Driver");
+        
+        if (configFile == null)
+        {
+        	// Set Default        	       
+	        this.registerLocator("", AndroidLocator.class);	        
+	        this.registerLocator("", ClasspathLocator.class);
+	        this.registerLoader(AndroidImageLoader.class, "jpg", "bmp", "gif", "png", "jpeg");        
+	        this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3m");
+	        this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3md");
+	        this.registerLoader(com.jme3.font.plugins.BitmapFontLoader.class, "fnt");
+	        this.registerLoader(com.jme3.texture.plugins.DDSLoader.class, "dds");
+	        this.registerLoader(com.jme3.texture.plugins.PFMLoader.class, "pfm");
+	        this.registerLoader(com.jme3.texture.plugins.HDRLoader.class, "hdr");
+	        this.registerLoader(com.jme3.texture.plugins.TGALoader.class, "tga");
+	        this.registerLoader(com.jme3.export.binary.BinaryImporter.class, "j3o");
+	        this.registerLoader(com.jme3.scene.plugins.OBJLoader.class, "obj");
+	        this.registerLoader(com.jme3.scene.plugins.MTLLoader.class, "mtl");
+	        this.registerLoader(com.jme3.scene.plugins.ogre.MeshLoader.class, "meshxml", "mesh.xml");
+	        this.registerLoader(com.jme3.scene.plugins.ogre.SkeletonLoader.class, "skeletonxml", "skeleton.xml");
+	        this.registerLoader(com.jme3.scene.plugins.ogre.MaterialLoader.class, "material");
+	        this.registerLoader(com.jme3.scene.plugins.ogre.SceneLoader.class, "scene");
+	        this.registerLoader(com.jme3.shader.plugins.GLSLLoader.class, "vert", "frag", "glsl", "glsllib");
         }
+                
         logger.info("AndroidAssetManager created.");
     }
 
-    public void registerLoader(String loaderClass, String ... extensions){
-    }
-
-    public void registerLocator(String rootPath, String locatorClass, String ... extensions){
-    }
-
-    private Object tryLoadFromHD(AssetKey key){
-        if (!key.getExtension().equals("fnt"))
-            return null;
-
-        File f = new File("/sdcard/" + key.getName() + ".opt");
-        if (!f.exists())
-            return null;
-
-        try {
-            InputStream stream = new FileInputStream(f);
-            BitmapFont font = (BitmapFont) modelLoader.load(stream, null, null);
-            stream.close();
-            return font;
-        } catch (IOException ex){
-        }
-
-        return null;
-    }
-
-    private void tryPutToHD(AssetKey key, Object data){
-        if (!key.getExtension().equals("fnt"))
-            return;
-
-        File f = new File("/sdcard/" + key.getName() + ".opt");
-
-        try {
-            BitmapFont font = (BitmapFont) data;
-            OutputStream stream = new FileOutputStream(f);
-            exporter.save(font, stream);
-            stream.close();
-        } catch (IOException ex){
-        }
-    }
-
-    public Object loadAsset(AssetKey key){
-	logger.info("loadAsset(" + key + ")");
-        Object asset;
-//        Object asset = tryLoadFromHD(key);
-//        if (asset != null)
-//            return asset;
-
-        if (key.shouldCache()){
-            asset = cache.get(key);
-            if (asset != null)
-                return key.createClonedInstance(asset);
-        }
-        // find resource
-        AssetInfo info = locator.locate(this, key);
-        if (info == null){
-            logger.log(Level.WARNING, "Cannot locate resource: "+key.getName());
-            return null;
-        }
-
-        String ex = key.getExtension();
-        logger.log(Level.INFO, "Loading asset: "+key.getName());
-        try{
-            if (ex.equals("png") || ex.equals("jpg") 
-             || ex.equals("jpeg") || ex.equals("j3i")){
-                Image image;
-                if (ex.equals("j3i")){
-                    image = (Image) modelLoader.load(info);
-                }else{
-                    image = (Image) imageLoader.load(info);
-                }
-                TextureKey tkey = (TextureKey) key;
-                asset = image;
-                Texture tex = (Texture) tkey.postProcess(asset);
-                tex.setMagFilter(Texture.MagFilter.Nearest);
-                tex.setAnisotropicFilter(0);
-                if (tex.getMinFilter().usesMipMapLevels()){
-                    tex.setMinFilter(Texture.MinFilter.NearestNearestMipMap);
-                }else{
-                    tex.setMinFilter(Texture.MinFilter.NearestNoMipMaps);
-                }
-                asset = tex;
-            }else if (ex.equals("j3o")){
-                asset = modelLoader.load(info);
-            }else if (ex.equals("fnt")){
-                asset = fontLoader.load(info);
-            }else if (ex.equals("j3md")){
-                asset = j3mdLoader.load(info);
-            }else if (ex.equals("j3m")){
-                asset = j3mLoader.load(info);
-            }else{
-		logger.info("loading asset as glsl shader ...");
-		asset = glslLoader.load(info);
-               // logger.log(Level.WARNING, "No loader registered for type: "+ex);
-               // return null;
-            }
-
-            if (key.shouldCache())
-                cache.put(key, asset);
-
-//            tryPutToHD(key, asset);
-
-            return key.createClonedInstance(asset);
-        } catch (Exception e){
-            logger.log(Level.WARNING, "Failed to load resource: "+key.getName(), e);
-        }
-        return null;
-    }
-
-    public AssetInfo locateAsset(AssetKey<?> key){
-        AssetInfo info = locator.locate(this, key);
-        if (info == null){
-            logger.log(Level.WARNING, "Cannot locate resource: "+key.getName());
-            return null;
-        }
-	return info;
-    }
-
-
-
-
-    public Object loadAsset(String name) {
-        return loadAsset(new AssetKey(name));
-    }
-
-    public Spatial loadModel(String name) {
-        return (Spatial) loadAsset(name);
-    }
-
-    public Material loadMaterial(String name) {
-        return (Material) loadAsset(name);
-    }
-
-    public BitmapFont loadFont(String name){
-        return (BitmapFont) loadAsset(name);
-    }
-
+    /**
+     * Loads a texture. 
+     *
+     * @return
+     */
+    @Override
     public Texture loadTexture(TextureKey key){
-        return (Texture) loadAsset(key);
-    }
-
-    public Texture loadTexture(String name){
-        return loadTexture(new TextureKey(name, false));
-    }
-
-	public Shader loadShader(ShaderKey key){
-		logger.info("loadShader(" + key + ")");
-
-		String vertName = key.getVertName();
-		String fragName = key.getFragName();
-
-		String vertSource = (String) loadAsset(new AssetKey(vertName));
-		String fragSource = (String) loadAsset(new AssetKey(fragName));
-
-		Shader s = new Shader(key.getLanguage());
-		s.addSource(Shader.ShaderType.Vertex,   vertName, vertSource, key.getDefines().getCompiled());
-		s.addSource(Shader.ShaderType.Fragment, fragName, fragSource, key.getDefines().getCompiled());
-
-		logger.info("returing shader: [" + s + "]");
-		return s;
-	}
-
-
-    public void registerLocator(String rootPath, String locatorClassName) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public AudioData loadAudio(AudioKey key) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public AudioData loadAudio(String name) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public Spatial loadModel(ModelKey key) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-	/* new */
-
-    private AssetEventListener eventListener = null;
-
-    public void setAssetEventListener(AssetEventListener listener){
-        eventListener = listener;
-    }
-
-    public void registerLocator(String rootPath, Class<? extends AssetLocator> locatorClass){
-	logger.warning("not implemented.");
-    }
-
-    public void registerLoader(Class<? extends AssetLoader> loader, String ... extensions){
-	logger.warning("not implemented.");
+    	Texture tex = (Texture) loadAsset(key);
+    	
+    	// Needed for Android
+        tex.setMagFilter(Texture.MagFilter.Nearest);
+        tex.setAnisotropicFilter(0);
+        if (tex.getMinFilter().usesMipMapLevels()){
+            tex.setMinFilter(Texture.MinFilter.NearestNearestMipMap);
+        }else{
+            tex.setMinFilter(Texture.MinFilter.NearestNoMipMaps);
+        }
+        return tex; 
     }
-
-  
-
-
+    
 }

+ 28 - 15
engine/src/android/com/jme3/asset/plugins/AndroidLocator.java

@@ -1,6 +1,5 @@
 package com.jme3.asset.plugins;
 
-import android.content.res.AssetManager;
 import android.content.res.Resources;
 import com.jme3.asset.AssetInfo;
 import com.jme3.asset.AssetKey;
@@ -8,20 +7,22 @@ import com.jme3.asset.AssetLocator;
 import com.jme3.system.JmeSystem;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
 public class AndroidLocator implements AssetLocator {
 
     private static final Logger logger = Logger.getLogger(AndroidLocator.class.getName());
     private Resources resources;
-    private AssetManager androidManager;
+    private android.content.res.AssetManager androidManager;
+    private String rootPath = "";
 
     private class AndroidAssetInfo extends AssetInfo {
 
         private final InputStream in;
 
-        public AndroidAssetInfo(com.jme3.asset.AssetManager manager, AssetKey key,
-                InputStream in){
+        public AndroidAssetInfo(com.jme3.asset.AssetManager manager, AssetKey<?> key, InputStream in)
+        {
             super(manager, key);
             this.in = in;
         }
@@ -33,28 +34,40 @@ public class AndroidLocator implements AssetLocator {
     }
 
 
-    public AndroidLocator(){
+    public AndroidLocator()
+    {
         resources = JmeSystem.getResources();
         androidManager = resources.getAssets();
     }
     
-    public void setRootPath(String rootPath) {
+    public void setRootPath(String rootPath) 
+    {
+        this.rootPath = rootPath;
     }
 
-    public AssetInfo locate(com.jme3.asset.AssetManager manager, AssetKey key) {
+    @SuppressWarnings("rawtypes")
+    @Override
+    public AssetInfo locate(com.jme3.asset.AssetManager manager, AssetKey key) 
+    {
         InputStream in = null;
-        try {
-            in = androidManager.open(key.getName());
+        String sAssetPath = rootPath + key.getName();
+        // Fix path issues
+        if (sAssetPath.startsWith("/"))
+        {
+            // Remove leading /
+            sAssetPath = sAssetPath.substring(1);
+        }
+        sAssetPath = sAssetPath.replace("//", "/");
+        try {      
+            in = androidManager.open(sAssetPath);
             if (in == null)
                 return null;
 
             return new AndroidAssetInfo(manager, key, in);
-        } catch (IOException ex) {
-            if (in != null)
-                try {
-                    in.close();
-                } catch (IOException ex1) {
-                }
+        } 
+        catch (IOException ex) 
+        {
+            logger.log(Level.WARNING, "Failed to locate {0} ", sAssetPath);
         }
         return null;
     }

+ 273 - 261
engine/src/android/com/jme3/input/android/AndroidInput.java

@@ -2,124 +2,45 @@ package com.jme3.input.android;
 
 import java.util.List;
 import java.util.ArrayList;
+import java.util.logging.Logger;
 
 import android.content.Context;
 import android.opengl.GLSurfaceView;
 import android.util.AttributeSet;
+import android.view.GestureDetector;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import com.jme3.input.android.TouchEvent;
 import com.jme3.input.KeyInput;
 import com.jme3.input.MouseInput;
 import com.jme3.input.RawInputListener;
 import com.jme3.input.event.KeyInputEvent;
 import com.jme3.input.event.MouseButtonEvent;
 import com.jme3.input.event.MouseMotionEvent;
+import com.jme3.math.Vector2f;
 
-public class AndroidInput extends GLSurfaceView implements KeyInput, MouseInput {
+
+public class AndroidInput extends GLSurfaceView implements KeyInput, MouseInput, 
+                                                           GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener
+{
+    private final static Logger logger = Logger.getLogger(AndroidInput.class.getName());
     
-    private RawInputListener listener;
-    private int lastX = -1, lastY = -1;
-
-    private static final char[] ANDROID_TO_JME_CHR = {
-        0x0,// unknown
-        0x0,// soft left
-        0x0,// soft right
-        0x0,// home
-        0x0,// back
-        0x0,// call
-        0x0,// endcall
-        '0',
-        '1',
-        '2',
-        '3',
-        '4',
-        '5',
-        '6',
-        '7',
-        '8',
-        '9',
-        '*',
-        '#',
-        0x0,//dpad_up
-        0x0,//dpad_down
-        0x0,//dpad_left
-        0x0,//dpad_right
-        0x0,//dpad_center
-        0x0,//volume up
-        0x0,//volume down
-        0x0,//power
-        0x0,//camera
-        0x0,//clear
-        'a',
-        'b',
-        'c',
-        'd',
-        'e',
-        'f',
-        'g',
-        'h',
-        'i',
-        'j',
-        'k',
-        'l',
-        'm',
-        'n',
-        'o',
-        'p',
-        'q',
-        'r',
-        's',
-        't',
-        'u',
-        'v',
-        'w',
-        'x',
-        'y',
-        'z',
-        ',',
-        '.',
-
-        0x0,//left alt
-        0x0,//right alt
-        0x0,//left ctrl
-        0x0,//right ctrl
-
-//        0x0,//fn
-//        0x0,//cap
-
-        '\t',
-        ' ',
-        0x0,//sym(bol)
-        0x0,//explorer
-        0x0,//envelope
-        '\n',//newline
-        0x0,//delete
-        '`',
-        '-',
-        '=',
-        '[',
-        ']',
-        '\\',//backslash
-        ';',
-        '\'',//apostrophe
-        '/',//slash
-        '@',//at
-        0x0,//num
-        0x0,//headset hook
-        0x0,//focus
-        0x0,
-        0x0,//menu
-        0x0,//notification
-        0x0,//search
-        0x0,//media play/pause
-        0x0,//media stop
-        0x0,//media next
-        0x0,//media previous
-        0x0,//media rewind
-        0x0,//media fastforward
-        0x0,//mute
-    };
+    private RawInputListener listenerRaw = null;
+    private AndroidTouchInputListener listenerTouch = null;
+    private ScaleGestureDetector scaledetector;
+    private GestureDetector detector;
+    private Vector2f lastPos = new Vector2f();
+    private boolean dragging = false;
+    
+    private List<Object> currentEvents = new ArrayList<Object>();
 
+    private final static int MAX_EVENTS = 1024;
+
+    
+    private boolean FIRE_MOUSE_EVENTS = true;
+
+   
     private static final int[] ANDROID_TO_JME = {
         0x0, // unknown
         0x0, // key code soft left
@@ -223,170 +144,170 @@ public class AndroidInput extends GLSurfaceView implements KeyInput, MouseInput
         0x0,//mute
     };
 
-//    private int[] keyMap = {
-//        0x0,
-//        KeyEvent.KEYCODE_BACK, // ESC key
-//
-//        KeyEvent.KEYCODE_1,
-//        KeyEvent.KEYCODE_2,
-//        KeyEvent.KEYCODE_3,
-//        KeyEvent.KEYCODE_4,
-//        KeyEvent.KEYCODE_5,
-//        KeyEvent.KEYCODE_6,
-//        KeyEvent.KEYCODE_7,
-//        KeyEvent.KEYCODE_8,
-//        KeyEvent.KEYCODE_9,
-//        KeyEvent.KEYCODE_0,
-//        KeyEvent.KEYCODE_MINUS,
-//        KeyEvent.KEYCODE_EQUALS,
-//        KeyEvent.KEYCODE_BACK,
-//        KeyEvent.KEYCODE_TAB,
-//        KeyEvent.KEYCODE_Q,
-//        KeyEvent.KEYCODE_W,
-//        KeyEvent.KEYCODE_E,
-//        KeyEvent.KEYCODE_R,
-//        KeyEvent.KEYCODE_T,
-//        KeyEvent.KEYCODE_Y,
-//        KeyEvent.KEYCODE_U,
-//        KeyEvent.KEYCODE_I,
-//        KeyEvent.KEYCODE_O,
-//        KeyEvent.KEYCODE_P,
-//        KeyEvent.KEYCODE_LEFT_BRACKET,
-//        KeyEvent.KEYCODE_RIGHT_BRACKET,
-//        KeyEvent.KEYCODE_ENTER,
-//        KeyEvent.KEYCODE_SOFT_LEFT, // Left Ctrl
-//        KeyEvent.KEYCODE_A,
-//        KeyEvent.KEYCODE_S,
-//        KeyEvent.KEYCODE_D,
-//        KeyEvent.KEYCODE_F,
-//        KeyEvent.KEYCODE_G,
-//        KeyEvent.KEYCODE_H,
-//        KeyEvent.KEYCODE_J,
-//        KeyEvent.KEYCODE_K,
-//        KeyEvent.KEYCODE_L,
-//        KeyEvent.KEYCODE_SEMICOLON,
-//        KeyEvent.KEYCODE_APOSTROPHE,
-//        KeyEvent.KEYCODE_GRAVE,
-//        KeyEvent.KEYCODE_SHIFT_LEFT,
-//        KeyEvent.KEYCODE_BACKSLASH,
-//        KeyEvent.KEYCODE_Z,
-//        KeyEvent.KEYCODE_X,
-//        KeyEvent.KEYCODE_C,
-//        KeyEvent.KEYCODE_V,
-//        KeyEvent.KEYCODE_B,
-//        KeyEvent.KEYCODE_N,
-//        KeyEvent.KEYCODE_M,
-//
-//        KeyEvent.KEYCODE_COMMA,
-//        KeyEvent.KEYCODE_PERIOD,
-//        KeyEvent.KEYCODE_SLASH,
-//        KeyEvent.KEYCODE_SHIFT_RIGHT,
-//        KeyEvent.KEYCODE_STAR,
-//
-//        KeyEvent.KEYCODE_ALT_LEFT,
-//        KeyEvent.KEYCODE_SPACE,
-//
-//        0x0, // no caps lock
-//
-//        0x0, // F1
-//        0x0, // F2
-//        0x0, // F3
-//        0x0, // F4
-//        0x0, // F5
-//        0x0, // F6
-//        0x0, // F7
-//        0x0, // F8
-//        0x0, // F9
-//        0x0, // F10
-//
-//        KeyEvent.KEYCODE_NUM,
-//        0x0, // scroll lock
-//
-//        0x0, // numpad7
-//        0x0, // numpad8
-//        0x0, // numpad9
-//
-//        KeyEvent.
-//    }
-
-    public AndroidInput(Context ctx, AttributeSet attribs){
+    public AndroidInput(Context ctx, AttributeSet attribs)
+    {
         super(ctx, attribs);
+        detector=new GestureDetector(this);
+        scaledetector=new ScaleGestureDetector(ctx, this);        
     }
 
-    public AndroidInput(Context ctx){
+    public AndroidInput(Context ctx)
+    {
         super(ctx);
+        detector=new GestureDetector(this);
+        scaledetector=new ScaleGestureDetector(ctx, this);
     }
 
+    /**
+     * onTouchEvent gets called from android thread on touchpad events
+     */
     @Override
-    public boolean onTouchEvent(MotionEvent motionEvent){
-	int newX = getWidth() - (int) motionEvent.getX();
-	int newY = (int) motionEvent.getY();
-
-
-        switch (motionEvent.getAction()){
+    public boolean onTouchEvent(MotionEvent event)
+    {
+        boolean bWasHandled = false;
+        MouseButtonEvent btn;
+        TouchEvent touch;
+
+        // Send the raw event
+        processEvent(event);
+        
+        // Try to detect gestures
+        this.detector.onTouchEvent(event);
+        this.scaledetector.onTouchEvent(event);
+
+        
+        switch (event.getAction())
+        {
             case MotionEvent.ACTION_DOWN:
-                MouseButtonEvent btn = new MouseButtonEvent(0, true, newX, newY);
-                btn.setTime(motionEvent.getEventTime());
-		processEvent(btn);
-               // listener.onMouseButtonEvent(btn);
-                lastX = -1;
-                lastY = -1;
-                return true;
+                
+                // Store current pos
+                lastPos.set(event.getX(),event.getY());
+                
+                if (FIRE_MOUSE_EVENTS)
+                {
+                   // Handle mouse events 
+                    btn = new MouseButtonEvent(0, true, (int)lastPos.getX(), (int)lastPos.getY());
+                    btn.setTime(event.getEventTime());
+                    processEvent(btn);
+                }
+                
+                // Handle gesture events
+                touch = new TouchEvent(TouchEvent.Type.GRABBED, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null);
+                processEvent(touch);
+                
+                bWasHandled = true;
+                break;
+                
             case MotionEvent.ACTION_UP:
-                MouseButtonEvent btn2 = new MouseButtonEvent(0, false, newX, newY);
-                btn2.setTime(motionEvent.getEventTime());
-		processEvent(btn2);
-               // listener.onMouseButtonEvent(btn2);
-                lastX = -1;
-                lastY = -1;
-                return true;
+                
+                if (FIRE_MOUSE_EVENTS)
+                {                
+                    // Handle mouse events 
+                    btn = new MouseButtonEvent(0, false, (int)event.getX(), (int)event.getY());
+                    btn.setTime(event.getEventTime());
+                    processEvent(btn);
+                }                
+                // Handle gesture events             
+                if(dragging)
+                {
+                    touch = new TouchEvent(TouchEvent.Type.DRAGGED, TouchEvent.Operation.STOPPED,event.getX(),event.getY(),event.getX()-lastPos.getX(),event.getY()-lastPos.getY(),null);
+                    processEvent(touch);
+                }
+                touch = new TouchEvent(TouchEvent.Type.RELEASED, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null);
+                processEvent(touch);
+                dragging=false;
+                bWasHandled = true;
+                break;
             case MotionEvent.ACTION_MOVE:
-               // int newX = getWidth() - (int) motionEvent.getX();
-               // int newY = (int) motionEvent.getY();
-                int dx;
-                int dy;
-                if (lastX != -1){
-                    dx = newX - lastX;
-                    dy = newY - lastY;
-                }else{
-                    dx = 0;
-                    dy = 0;
+                if(!scaledetector.isInProgress())
+                {
+                    if(!dragging)
+                        touch = new TouchEvent(TouchEvent.Type.DRAGGED, TouchEvent.Operation.STARTED,event.getX(),event.getY(),event.getX()-lastPos.getX(),event.getY()-lastPos.getY(),null);
+                    else
+                        touch = new TouchEvent(TouchEvent.Type.DRAGGED, TouchEvent.Operation.RUNNING,event.getX(),event.getY(),event.getX()-lastPos.getX(),event.getY()-lastPos.getY(),null);
+                        
+                    processEvent(touch);
+                    dragging=true;
                 }
-                lastX = newX;
-                lastY = newY;
-                MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0);
-                mot.setTime(motionEvent.getEventTime());
-		processEvent(mot);
-                //listener.onMouseMotionEvent(mot);
-                try{
-                    Thread.sleep(15);
-                } catch (InterruptedException ex) {
+                if (FIRE_MOUSE_EVENTS)
+                {
+                    int newX = getWidth() - (int) event.getX();
+                    int newY = (int) event.getY();
+                    int dx;
+                    int dy;
+                    if (lastPos.getX() != -1){
+                        dx = newX - (int)lastPos.getX();
+                        dy = newY - (int)lastPos.getY();
+                    }else{
+                        dx = 0;
+                        dy = 0;
+                    }                    
+                    MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0);
+                    mot.setTime(event.getEventTime());
+                    processEvent(mot);
                 }
-                return true;
+                bWasHandled = true;
+                break;
+                
+            // TODO: implement motion events
+            case MotionEvent.ACTION_POINTER_UP:
+                break;
+
+            case MotionEvent.ACTION_POINTER_DOWN:
+                break;
+
+            case MotionEvent.ACTION_OUTSIDE:
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+                break;
         }
-        return false;
+        return bWasHandled;        
     }
 
     @Override
     public boolean onKeyDown (int keyCode, KeyEvent event) {
+
+        // Send the raw event
+        processEvent(event);
+        
         int jmeCode  = ANDROID_TO_JME[keyCode];
-        String str =  event.getCharacters();
-        char c = str != null && str.length() > 0 ? str.charAt(0) : 0x0;
-        KeyInputEvent evt = new KeyInputEvent(jmeCode, c, true, false);
-	processEvent(evt);
-	//     listener.onKeyEvent(evt);
-        return false;
+        if (jmeCode != 0)
+        {
+            String str =  event.getCharacters();
+            char c = str != null && str.length() > 0 ? str.charAt(0) : 0x0;
+            KeyInputEvent evt = new KeyInputEvent(jmeCode, c, true, false);
+            logger.info("onKeyDown " + evt);
+            processEvent(evt);
+        }
+        // Handle all keys ourself, except the back button (4)
+        if (keyCode == 4)
+            return false;
+        else
+            return true;
     }
 
     @Override
     public boolean onKeyUp (int keyCode, KeyEvent event) {
+        
+        // Send the raw event
+        processEvent(event);
+        
         int jmeCode  = ANDROID_TO_JME[keyCode];
-        String str =  event.getCharacters();
-        char c = str != null && str.length() > 0 ? str.charAt(0) : 0x0;
-        KeyInputEvent evt = new KeyInputEvent(jmeCode, c, false, false);
-	processEvent(evt);
-        //listener.onKeyEvent(evt);
-        return false;
+        if (jmeCode != 0)
+        {            
+            String str =  event.getCharacters();
+            char c = str != null && str.length() > 0 ? str.charAt(0) : 0x0;
+            KeyInputEvent evt = new KeyInputEvent(jmeCode, c, false, false);
+            logger.info("onKeyUp " + evt);
+            processEvent(evt);
+        }
+        
+        // Handle all keys ourself, except the back button (4)
+        if (keyCode == 4)
+            return false;
+        else
+            return true;
     }
 
     public void setCursorVisible(boolean visible){
@@ -410,39 +331,130 @@ public class AndroidInput extends GLSurfaceView implements KeyInput, MouseInput
         return true;
     }
 
-	// XXX: android does not have an Event interface?
-	private List<Object> currentEvents = new ArrayList<Object>();
 
-	private final static int MAX_EVENTS = 1024;
 
-	private void processEvent(Object event) {
+	private void processEvent(Object event) 
+	{
 		synchronized (currentEvents) {
 			if (currentEvents.size() < MAX_EVENTS)
 				currentEvents.add(event);
 		}
 	}
 
+	Object event;
 	private void generateEvents() {
-		synchronized (currentEvents) {
-			for (Object event: currentEvents) {
-				if (event instanceof MouseButtonEvent) {
-              				listener.onMouseButtonEvent((MouseButtonEvent) event);
-				} else if (event instanceof MouseMotionEvent) {
-					listener.onMouseMotionEvent((MouseMotionEvent) event);
-				} else if (event instanceof KeyInputEvent) {
-					listener.onKeyEvent((KeyInputEvent) event);
-				}
-			}
-			currentEvents.clear();
-		}
+	    if (listenerRaw != null)
+	    {
+    		synchronized (currentEvents) {
+    			//for (Object event: currentEvents) {
+    			for (int i = 0; i < currentEvents.size(); i++) {
+    			    event = currentEvents.get(i);
+    				if (event instanceof MouseButtonEvent) {
+    				    listenerRaw.onMouseButtonEvent((MouseButtonEvent) event);
+    				} else if (event instanceof MouseMotionEvent) {
+    					listenerRaw.onMouseMotionEvent((MouseMotionEvent) event);
+    				} else if (event instanceof KeyInputEvent) {
+    					listenerRaw.onKeyEvent((KeyInputEvent) event);				
+                    } else if (event instanceof TouchEvent) {
+                        if (listenerTouch != null)
+                            listenerTouch.onTouchEvent((TouchEvent) event);
+                    } else if (event instanceof MotionEvent) {
+                        if (listenerTouch != null)
+                            listenerTouch.onMotionEvent((MotionEvent) event);                                                                        
+                    } else if (event instanceof KeyEvent) {
+                        if (listenerTouch != null)
+                            listenerTouch.onAndroidKeyEvent((KeyEvent) event);
+                    }    				    				    				
+    			}
+    			currentEvents.clear();
+    		}
+	    }
 	}
 
     public void setInputListener(RawInputListener listener) {
-        this.listener = listener;
+        this.listenerRaw = listener;
+    }
+    
+    public void setInputListener(AndroidTouchInputListener listener) {
+        this.listenerRaw = listener;
+        this.listenerTouch = listener;
     }
 
     public long getInputTimeNanos() {
         return System.nanoTime();
     }
 
+    
+    // --------------- Gesture detected callback events ----------------------------------
+    
+    public boolean onDown(MotionEvent event)
+    {
+        return false;
+    }
+
+    public void onLongPress(MotionEvent event)
+    {
+        TouchEvent touch = new TouchEvent(TouchEvent.Type.LONGPRESSED, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null);
+        processEvent(touch);
+    }
+
+    public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy)
+    {
+        TouchEvent touch = new TouchEvent(TouchEvent.Type.FLING, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null);
+        processEvent(touch);
+        return true;
+    }
+
+    public boolean onSingleTapConfirmed(MotionEvent event)
+    {
+        TouchEvent touch = new TouchEvent(TouchEvent.Type.TAP, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null);
+        processEvent(touch);
+        return true;
+    }
+
+    public boolean onDoubleTap(MotionEvent event)
+    {
+        TouchEvent touch = new TouchEvent(TouchEvent.Type.DOUBLETAP, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null);
+        processEvent(touch);
+        return true;
+    }
+
+    public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector)
+    {
+        TouchEvent touch = new TouchEvent(TouchEvent.Type.SCALE, TouchEvent.Operation.STARTED,scaleGestureDetector.getFocusX(),scaleGestureDetector.getFocusY(),0,0,new float[]{scaleGestureDetector.getCurrentSpan(),scaleGestureDetector.getScaleFactor()});
+        processEvent(touch);
+        return true;
+    }
+
+    public boolean onScale(ScaleGestureDetector scaleGestureDetector)
+    {        
+        TouchEvent touch = new TouchEvent(TouchEvent.Type.SCALE, TouchEvent.Operation.RUNNING,scaleGestureDetector.getFocusX(),scaleGestureDetector.getFocusY(),0,0,new float[]{scaleGestureDetector.getCurrentSpan(),scaleGestureDetector.getScaleFactor()});
+        processEvent(touch);
+        return false;
+    }
+
+    public void onScaleEnd(ScaleGestureDetector scaleGestureDetector)
+    {
+        TouchEvent touch = new TouchEvent(TouchEvent.Type.SCALE, TouchEvent.Operation.STOPPED,scaleGestureDetector.getFocusX(),scaleGestureDetector.getFocusY(),0,0,new float[]{scaleGestureDetector.getCurrentSpan(),scaleGestureDetector.getScaleFactor()});
+        processEvent(touch);        
+    }
+
+    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+            float distanceY) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    public void onShowPress(MotionEvent e) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    public boolean onSingleTapUp(MotionEvent event) 
+    {
+        TouchEvent touch = new TouchEvent(TouchEvent.Type.TAP, TouchEvent.Operation.NOP,event.getX(),event.getY(),0,0,null);
+        processEvent(touch);
+        return true;
+    }
+
 }

+ 19 - 0
engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java

@@ -0,0 +1,19 @@
+package com.jme3.input.android;
+
+import com.jme3.input.RawInputListener;
+
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+/**
+ * AndroidTouchInputListener is an inputlistener interface which defines callbacks/events for android touch screens
+ * For use with class AndroidInput
+ * @author larynx
+ *
+ */
+public interface AndroidTouchInputListener extends RawInputListener
+{
+    public void onTouchEvent(TouchEvent evt);
+    public void onMotionEvent(MotionEvent evt);
+    public void onAndroidKeyEvent(KeyEvent evt);
+}

+ 17 - 53
engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java

@@ -76,53 +76,10 @@ import java.util.EnumSet;
 import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-
-/*
-//import org.lwjgl.opengl.ARBGeometryShader4;
-//import org.lwjgl.opengl.ARBHalfFloatVertex;
-//import org.lwjgl.opengl.ARBVertexArrayObject;
-//import org.lwjgl.opengl.ARBHalfFloatVertex;
-//import org.lwjgl.opengl.ARBVertexArrayObject;
-import org.lwjgl.opengl.ARBDrawBuffers;
-//import org.lwjgl.opengl.ARBDrawInstanced;
-import org.lwjgl.opengl.ARBDrawInstanced;
-import org.lwjgl.opengl.ARBMultisample;
-import org.lwjgl.opengl.ContextCapabilities;
-import org.lwjgl.opengl.EXTTextureArray;
-import org.lwjgl.opengl.EXTTextureFilterAnisotropic;
-import org.lwjgl.opengl.GLContext;
-import org.lwjgl.opengl.NVHalfFloat;
-
-import static org.lwjgl.opengl.GL11.*;
-import static org.lwjgl.opengl.GL12.*;
-import static org.lwjgl.opengl.GL13.*;
-import static org.lwjgl.opengl.GL14.*;
-import static org.lwjgl.opengl.GL15.*;
-import static org.lwjgl.opengl.GL20.*;
-
-import static org.lwjgl.opengl.EXTFramebufferObject.*;
-import static org.lwjgl.opengl.EXTFramebufferMultisample.*;
-import static org.lwjgl.opengl.EXTFramebufferBlit.*;
-import org.lwjgl.opengl.ARBShaderObjects.*;
-import org.lwjgl.opengl.ARBVertexArrayObject;
-//import static org.lwjgl.opengl.ARBDrawInstanced.*;
-*/
-
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.egl.EGLContext;
 import javax.microedition.khronos.opengles.GL10;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.opengl.GLES10;
 import android.opengl.GLES11;
 import android.opengl.GLES20;
-import android.opengl.GLSurfaceView;
-import android.opengl.GLUtils;
-import android.opengl.Matrix;
-import android.os.SystemClock;
-import android.util.Log;
 
 
 
@@ -168,14 +125,14 @@ public class OGLESShaderRenderer implements Renderer {
     private int clipX, clipY, clipW, clipH;
 
 
-	private final GL10 gl;
+	//private final GL10 gl;
 	private boolean powerOf2 = false;
 	private boolean verboseLogging = false;
-	private boolean useVBO = true;
+	private boolean useVBO = false;
 
 
-    public OGLESShaderRenderer(GL10 gl) {
-	this.gl = gl;
+    public OGLESShaderRenderer() {
+
     }
 
 	public void setUseVA(boolean value) {
@@ -1869,7 +1826,7 @@ public class OGLESShaderRenderer implements Renderer {
                 return;
             }
             for (int i = 0; i < 6; i++){
-                TextureUtil.uploadTexture(gl, img, GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc, true, powerOf2);
+                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<ByteBuffer> data = img.getData();
@@ -1881,7 +1838,7 @@ public class OGLESShaderRenderer implements Renderer {
                  TextureUtil.uploadTexture(img, target, i, 0, tdc);
             }
         }*/else{
-            TextureUtil.uploadTexture(gl, img, target, 0, 0, tdc, true, powerOf2);
+            TextureUtil.uploadTexture(img, target, 0, 0, tdc, true, powerOf2);
 
 		if (verboseLogging)
 			logger.info("GLES20.glTexParameteri(" + target + "GLES11.GL_GENERATE_MIMAP, GLES20.GL_TRUE)");
@@ -2843,11 +2800,18 @@ public class OGLESShaderRenderer implements Renderer {
 
    public void setAlphaToCoverage(boolean value)
    {
-       // TODO Auto-generated method stub
+       if (value) {
+           GLES20.glEnable(GLES20.GL_SAMPLE_ALPHA_TO_COVERAGE);
+       } else {
+           GLES20.glDisable(GLES20.GL_SAMPLE_ALPHA_TO_COVERAGE);
+       }
    }
 
-   public void invalidateState(){
-       // TODO invalidateState
+   @Override
+   public void invalidateState() 
+   {
+       context.reset();
+       boundShader = null;
+       lastFb = null;
    }
-    
 }

+ 40 - 40
engine/src/android/com/jme3/renderer/android/TextureUtil.java

@@ -1,6 +1,7 @@
 package com.jme3.renderer.android;
 
 import android.graphics.Bitmap;
+import android.opengl.GLES20;
 import android.opengl.GLUtils;
 import com.jme3.math.FastMath;
 import com.jme3.texture.Image;
@@ -36,7 +37,7 @@ public class TextureUtil {
         }
     }
 
-    private static void buildMipmap(GL10 gl, Bitmap bitmap) {
+    private static void buildMipmap(Bitmap bitmap) {
         int level = 0;
         int height = bitmap.getHeight();
         int width = bitmap.getWidth();
@@ -61,7 +62,7 @@ public class TextureUtil {
         }
     }
 
-    private static void uploadTextureBitmap(GL10 gl, Bitmap bitmap, boolean generateMips, boolean powerOf2){
+    private static void uploadTextureBitmap(Bitmap bitmap, boolean generateMips, boolean powerOf2){
         if (!powerOf2){
             int width = bitmap.getWidth();
             int height = bitmap.getHeight();
@@ -76,7 +77,7 @@ public class TextureUtil {
         }
 
         if (generateMips){
-            buildMipmap(gl, bitmap);
+            buildMipmap(bitmap);
         }else{
             GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
             bitmap.recycle();
@@ -84,18 +85,17 @@ public class TextureUtil {
     }
 
     public static void uploadTexture(
-		GL10 gl,
                                      Image img,
                                      int target,
                                      int index,
                                      int border,
-				     boolean tdc,
+                                     boolean tdc,
                                      boolean generateMips,
                                      boolean powerOf2){
 
         if (img.getEfficentData() instanceof Bitmap){
             Bitmap bitmap = (Bitmap) img.getEfficentData();
-            uploadTextureBitmap(gl, bitmap, generateMips, powerOf2);
+            uploadTextureBitmap(bitmap, generateMips, powerOf2);
 //            img.setEfficentData(null);
             return;
         }
@@ -118,71 +118,71 @@ public class TextureUtil {
 
         switch (fmt){
             case Alpha16:
-                format = gl.GL_ALPHA;
-                dataType = gl.GL_UNSIGNED_BYTE;
+                format = GL10.GL_ALPHA;
+                dataType = GL10.GL_UNSIGNED_BYTE;
                 break;
             case Alpha8:
-                format = gl.GL_ALPHA;
-                dataType = gl.GL_UNSIGNED_BYTE;
+                format = GL10.GL_ALPHA;
+                dataType = GL10.GL_UNSIGNED_BYTE;
                 break;
             case Luminance8:
-                format = gl.GL_LUMINANCE;
-                dataType = gl.GL_UNSIGNED_BYTE;
+                format = GL10.GL_LUMINANCE;
+                dataType = GL10.GL_UNSIGNED_BYTE;
                 break;
             case Luminance8Alpha8:
-                format = gl.GL_LUMINANCE_ALPHA;
-                dataType = gl.GL_UNSIGNED_BYTE;
+                format = GL10.GL_LUMINANCE_ALPHA;
+                dataType = GL10.GL_UNSIGNED_BYTE;
                 break;
             case Luminance16Alpha16:
-                format = gl.GL_LUMINANCE_ALPHA;
-                dataType = gl.GL_UNSIGNED_BYTE;
+                format = GL10.GL_LUMINANCE_ALPHA;
+                dataType = GL10.GL_UNSIGNED_BYTE;
                 break;
             case Luminance16:
-                format = gl.GL_LUMINANCE;
-                dataType = gl.GL_UNSIGNED_BYTE;
+                format = GL10.GL_LUMINANCE;
+                dataType = GL10.GL_UNSIGNED_BYTE;
                 break;
             case RGB565:
-                format = gl.GL_RGB;
-                dataType = gl.GL_UNSIGNED_SHORT_5_6_5;
+                format = GL10.GL_RGB;
+                dataType = GL10.GL_UNSIGNED_SHORT_5_6_5;
                 break;
             case ARGB4444:
-                format = gl.GL_RGBA;
-                dataType = gl.GL_UNSIGNED_SHORT_4_4_4_4;
+                format = GL10.GL_RGBA;
+                dataType = GL10.GL_UNSIGNED_SHORT_4_4_4_4;
                 break;
             case RGB10:
-                format = gl.GL_RGB;
-                dataType = gl.GL_UNSIGNED_BYTE;
+                format = GL10.GL_RGB;
+                dataType = GL10.GL_UNSIGNED_BYTE;
                 break;
             case RGB16:
-                format = gl.GL_RGB;
-                dataType = gl.GL_UNSIGNED_BYTE;
+                format = GL10.GL_RGB;
+                dataType = GL10.GL_UNSIGNED_BYTE;
                 break;
             case RGB5A1:
-                format = gl.GL_RGBA;
-                dataType = gl.GL_UNSIGNED_SHORT_5_5_5_1;
+                format = GL10.GL_RGBA;
+                dataType = GL10.GL_UNSIGNED_SHORT_5_5_5_1;
                 break;
             case RGB8:
-                format = gl.GL_RGB;
-                dataType = gl.GL_UNSIGNED_BYTE;
+                format = GL10.GL_RGB;
+                dataType = GL10.GL_UNSIGNED_BYTE;
                 break;
             case BGR8:
-                format = gl.GL_RGB;
-                dataType = gl.GL_UNSIGNED_BYTE;
+                format = GL10.GL_RGB;
+                dataType = GL10.GL_UNSIGNED_BYTE;
                 break;
             case RGBA16:
-                format = gl.GL_RGBA;
-                dataType = gl.GL_UNSIGNED_BYTE;
+                format = GL10.GL_RGBA;
+                dataType = GL10.GL_UNSIGNED_BYTE;
                 break;
             case RGBA8:
-                format = gl.GL_RGBA;
-                dataType = gl.GL_UNSIGNED_BYTE;
+                format = GL10.GL_RGBA;
+                dataType = GL10.GL_UNSIGNED_BYTE;
                 break;
             default:
                 throw new UnsupportedOperationException("Unrecognized format: "+fmt);
         }
 
         if (data != null)
-            gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1);
+            GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
 
         int[] mipSizes = img.getMipMapSizes();
         int pos = 0;
@@ -197,7 +197,7 @@ public class TextureUtil {
         // of more than paletted compressions is added..
         if (compress){
             data.clear();
-            gl.glCompressedTexImage2D(gl.GL_TEXTURE_2D,
+            GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D,
                                       1 - mipSizes.length,
                                       format,
                                       width,
@@ -219,7 +219,7 @@ public class TextureUtil {
             }
 
             if (compress && data != null){
-                gl.glCompressedTexImage2D(gl.GL_TEXTURE_2D,
+                GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D,
                                           i,
                                           format,
                                           mipWidth,
@@ -228,7 +228,7 @@ public class TextureUtil {
                                           data.remaining(),
                                           data);
             }else{
-                gl.glTexImage2D(gl.GL_TEXTURE_2D,
+                GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D,
                                 i,
                                 format,
                                 mipWidth,

+ 25 - 9
engine/src/android/com/jme3/system/JmeSystem.java

@@ -6,8 +6,8 @@ import com.jme3.asset.AndroidAssetManager;
 import com.jme3.asset.AssetManager;
 import com.jme3.audio.AudioNode;
 import com.jme3.audio.AudioData;
-import com.jme3.audio.AudioRenderer;
 import com.jme3.audio.AudioParam;
+import com.jme3.audio.AudioRenderer;
 import com.jme3.audio.Environment;
 import com.jme3.audio.Listener;
 import com.jme3.audio.ListenerParam;
@@ -28,6 +28,7 @@ public class JmeSystem {
     private static final Logger logger = Logger.getLogger(JmeSystem.class.getName());
 
     private static boolean initialized = false;
+    private static boolean lowPermissions = false;
     private static Resources res;
 
     public static void initialize(AppSettings settings){
@@ -51,7 +52,15 @@ public class JmeSystem {
     }
 
     public static String getFullName(){
-        return "jMonkey Engine 3 ALPHA 0.50";
+        return "jMonkey Engine 3 ALPHA 0.50 Android";
+    }
+    
+    public static void setLowPermissions(boolean lowPerm){
+        lowPermissions = lowPerm;
+    }
+
+    public static boolean isLowPermissions() {
+        return lowPermissions;
     }
     
     public static JmeContext newContext(AppSettings settings, Type contextType) {
@@ -71,8 +80,15 @@ public class JmeSystem {
 			public void initialize() {}
 			public void update(float tpf) {}
 			public void cleanup() {}
-			public void updateListenerParam(Listener listener, ListenerParam parameter) {}
-			public void updateSourceParam(AudioNode node, AudioParam parameter) {}
+			public void updateListenerParam(Listener listener,
+					ListenerParam param) {
+				// TODO Auto-generated method stub
+				
+			}
+			public void updateSourceParam(AudioNode src, AudioParam param) {
+				// TODO Auto-generated method stub
+				
+			}
 		};
     }
 
@@ -85,16 +101,16 @@ public class JmeSystem {
     }
 
     public static AssetManager newAssetManager(){
-	logger.info("newAssetManager()");
-        return new AndroidAssetManager(true);
+	    logger.info("newAssetManager()");
+        return new AndroidAssetManager(null);
     }
 
     public static AssetManager newAssetManager(URL url){
-	logger.info("newAssetManager(" + url + ")");
-        return new AndroidAssetManager(true);
+	    logger.info("newAssetManager(" + url + ")");
+        return new AndroidAssetManager(url);
     }
 
-    public static boolean showSettingsDialog(AppSettings settings) {
+    public static boolean showSettingsDialog(AppSettings settings, boolean loadSettings) {
         return true;
     }
 

+ 11 - 9
engine/src/android/com/jme3/system/android/AndroidTimer.java

@@ -35,15 +35,14 @@ package com.jme3.system.android;
 import com.jme3.system.Timer;
 
 /**
- * <code>NanoTimer</code> is a System.nanoTime implementation of <code>Timer</code>.
- * This is primarily useful for headless applications running on a server.
- * 
- * @author Matthew D. Hicks
+ * <code>AndroidTimer</code> is a System.nanoTime implementation of <code>Timer</code>.
  */
 public class AndroidTimer extends Timer {
     
-    private static final long TIMER_RESOLUTION = 1000L;
-    private static final float INVERSE_TIMER_RESOLUTION = 1f/1000L;
+    //private static final long TIMER_RESOLUTION = 1000L;
+    //private static final float INVERSE_TIMER_RESOLUTION = 1f/1000L;
+    private static final long TIMER_RESOLUTION = 1000000000L;
+    private static final float INVERSE_TIMER_RESOLUTION = 1f/1000000000L;
     
     private long startTime;
     private long previousTime;
@@ -51,7 +50,8 @@ public class AndroidTimer extends Timer {
     private float fps;
     
     public AndroidTimer() {
-        startTime = System.currentTimeMillis();
+        //startTime = System.currentTimeMillis();
+        startTime = System.nanoTime();
     }
 
     /**
@@ -66,7 +66,8 @@ public class AndroidTimer extends Timer {
     }
 
     public long getTime() {
-        return System.currentTimeMillis() - startTime;
+        //return System.currentTimeMillis() - startTime;
+        return System.nanoTime() - startTime;
     }
 
     public long getResolution() {
@@ -88,7 +89,8 @@ public class AndroidTimer extends Timer {
     }
     
     public void reset() {
-        startTime = System.currentTimeMillis();
+        //startTime = System.currentTimeMillis();
+        startTime = System.nanoTime();
         previousTime = getTime();
     }
 }

+ 185 - 130
engine/src/android/com/jme3/system/android/OGLESContext.java

@@ -39,7 +39,6 @@ import com.jme3.input.JoyInput;
 import com.jme3.input.KeyInput;
 import com.jme3.input.MouseInput;
 import com.jme3.input.android.AndroidInput;
-//import com.jme3.renderer.android.OGLESRenderer;
 import com.jme3.renderer.android.OGLESShaderRenderer;
 import com.jme3.system.AppSettings;
 import com.jme3.system.JmeContext;
@@ -51,48 +50,60 @@ import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.opengles.GL10;
 
 
-public class OGLESContext implements JmeContext, GLSurfaceView.Renderer {
+public class OGLESContext implements JmeContext, GLSurfaceView.Renderer 
+{
 
     private static final Logger logger = Logger.getLogger(OGLESContext.class.getName());
 
-    protected AtomicBoolean created = new AtomicBoolean(false);
-    protected AppSettings settings = new AppSettings(true);
+    protected final AtomicBoolean created = new AtomicBoolean(false);
+    protected final AtomicBoolean renderable = new AtomicBoolean(false);
+    protected final AtomicBoolean needClose = new AtomicBoolean(false);
+    protected final Object createdLock = new Object();
+    protected final AppSettings settings = new AppSettings(true);
 
-	/* < OpenGL ES 2.0 * */
-	//protected OGLESRenderer renderer;
 	/* >= OpenGL ES 2.0 (Android 2.2+) */
 	protected OGLESShaderRenderer renderer;
 
     protected Timer timer;
     protected SystemListener listener;
 
-    protected AtomicBoolean needClose = new AtomicBoolean(false);
+    
     protected boolean wasActive = false;
-    protected int frameRate = 0;
     protected boolean autoFlush = true;
 
     protected AndroidInput view;
+    
+    private long milliStart;
+    private long milliDelta;
+    protected int frameRate = 33;
+    //protected int minFrameDuration = 1000 / frameRate;  // Set a max FPS of 33
+    protected int minFrameDuration = 0;                   // No FPS cap
 
-    public OGLESContext(){
-    }
+    public OGLESContext() { }
 
-    public Type getType() {
+    @Override
+    public Type getType() 
+    {
         return Type.Display;
     }
-
-    public GLSurfaceView createView(Activity activity){
-        view = new AndroidInput(activity);
-
-	/*
-	 * Requesting client version from GLSurfaceView which is extended by
-	 * AndroidInput.
-	 * This is required to get OpenGL ES 2.0
-	 */
-
-	logger.info("setEGLContextClientVersion(2)");
-	view.setEGLContextClientVersion(2);
-	logger.info("setEGLContextClientVersion(2) ... done.");
-
+    
+    public GLSurfaceView createView(Activity activity)
+    {
+        return createView(new AndroidInput(activity));        
+    }
+    
+        
+    public GLSurfaceView createView(AndroidInput view)
+    {
+        this.view = view;    
+
+    	/*
+    	 * Requesting client version from GLSurfaceView which is extended by
+    	 * AndroidInput.
+    	 * This is required to get OpenGL ES 2.0
+    	 */    	
+    	view.setEGLContextClientVersion(2);
+    	
         //RGB565, Depth16
         view.setEGLConfigChooser(5, 6, 5, 0, 16, 0);
         view.setFocusableInTouchMode(true);
@@ -104,12 +115,11 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer {
         return view;
 
     }
+    
 
-    protected void applySettings(AppSettings setting){
-    }
-
-    protected void initInThread(GL10 gl){
-        logger.info("Display created.");
+    protected void initInThread()
+    {
+        logger.info("OGLESContext create");
         logger.fine("Running on thread: "+Thread.currentThread().getName());
 
         Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@@ -117,175 +127,220 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer {
                 listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
             }
         });
-
-        created.set(true);
-
+        
         timer = new AndroidTimer();
 
-        renderer = new OGLESShaderRenderer(gl);
-	applySettingsToRenderer(renderer, settings);
-
+        renderer = new OGLESShaderRenderer();
+    
+        renderer.setUseVA(true);
+        renderer.setVerboseLogging(false);
+        
         renderer.initialize();
-        listener.initialize();
-
-	// OGLESShaderRenderer does not support guiView yet
-	// forcefully remove all gui nodes
-
-	if (listener instanceof com.jme3.app.SimpleApplication) {
-		((com.jme3.app.SimpleApplication) listener).getGuiNode().detachAllChildren();
-	}
+        listener.initialize();                
+        created.set(true);
+        
+        needClose.set(false);
     }
 
     /**
      * De-initialize in the OpenGL thread.
      */
-    protected void deinitInThread(){
+    protected void deinitInThread()
+    {        
+        if (renderer != null) 
+            renderer.cleanup();
+            
         listener.destroy();
-	if (renderer != null) {
-		renderer.cleanup();
-		// do android specific cleaning here
-
-		logger.info("Display destroyed.");
+        
+        // do android specific cleaning here
+		logger.info("Display destroyed.");		
+		renderable.set(false);
 		created.set(false);
 		renderer = null;
 		timer = null;
-	}
+    }
+    
+    protected void  applySettingsToRenderer(OGLESShaderRenderer renderer, AppSettings settings) 
+    {
+        logger.warning("setSettings.USE_VA: [" + settings.getBoolean("USE_VA") + "]");
+        logger.warning("setSettings.VERBOSE_LOGGING: [" + settings.getBoolean("VERBOSE_LOGGING") + "]");
+        renderer.setUseVA(settings.getBoolean("USE_VA"));
+        renderer.setVerboseLogging(settings.getBoolean("VERBOSE_LOGGING"));
+    }
+    
+    protected void applySettings(AppSettings setting)
+    {
+        if (renderer != null)
+            applySettingsToRenderer(renderer, settings);        
     }
 
-
-	protected void  applySettingsToRenderer(OGLESShaderRenderer renderer, AppSettings settings) {
-		logger.warning("setSettings.USE_VA: [" + settings.getBoolean("USE_VA") + "]");
-		logger.warning("setSettings.VERBOSE_LOGGING: [" + settings.getBoolean("VERBOSE_LOGGING") + "]");
-		renderer.setUseVA(settings.getBoolean("USE_VA"));
-		renderer.setVerboseLogging(settings.getBoolean("VERBOSE_LOGGING"));
-	}
-
-	@Override
-    public void setSettings(AppSettings settings) {
-		this.settings.copyFrom(settings);
-
-		// XXX This code should be somewhere else
-		if (renderer != null)
-			applySettingsToRenderer(renderer, this.settings);
+    @Override
+    public void setSettings(AppSettings settings) 
+    {
+        this.settings.copyFrom(settings);
     }
 
+    @Override
     public void setSystemListener(SystemListener listener){
         this.listener = listener;
     }
 
+    @Override
     public AppSettings getSettings() {
         return settings;
     }
 
+    @Override
     public com.jme3.renderer.Renderer getRenderer() {
         return renderer;
     }
 
+    @Override
     public MouseInput getMouseInput() {
         return view;
     }
 
+    @Override
     public KeyInput getKeyInput() {
         return view;
     }
-
+    
+    @Override
     public JoyInput getJoyInput() {
         return null;
     }
-
-    public Timer getTimer() {
+    
+    @Override
+    public Timer getTimer() 
+    {
         return timer;
     }
 
-    public void setTitle(String title) {
+    @Override
+    public void setTitle(String title) 
+    {
     }
-
-    public boolean isCreated(){
+    
+    @Override
+    public boolean isCreated()
+    {
         return created.get();
     }
-
-    public void setAutoFlushFrames(boolean enabled){
+    @Override
+    public void setAutoFlushFrames(boolean enabled)
+    {
         this.autoFlush = enabled;
     }
 
     // renderer:initialize
-    public void onSurfaceCreated(GL10 gl, EGLConfig cfg) {
-        logger.info("Using Android");
-        initInThread(gl);
+    @Override
+    public void onSurfaceCreated(GL10 gl, EGLConfig cfg) 
+    {
+        logger.info("GL Surface created");
+        initInThread();
+        renderable.set(true);
     }
 
     // SystemListener:reshape
-    public void onSurfaceChanged(GL10 gl, int width, int height) {
+    @Override
+    public void onSurfaceChanged(GL10 gl, int width, int height) 
+    {
         settings.setResolution(width, height);
         listener.reshape(width, height);
     }
 
     // SystemListener:update
-    public void onDrawFrame(GL10 gl) {
-        if (needClose.get()){
-            deinitInThread(); // ???
+    @Override
+    public void onDrawFrame(GL10 gl) 
+    {
+        
+        if (!created.get())
+            throw new IllegalStateException("onDrawFrame without create");
+        
+        if (needClose.get())
+        {
+            deinitInThread();
             return;
         }
-
-//        if (wasActive != Display.isActive()){
-//            if (!wasActive){
-//                listener.gainFocus();
-//                wasActive = true;
-//            }else{
-//                listener.loseFocus();
-//                wasActive = false;
-//            }
-//        }
-
-		if (!created.get())
-            throw new IllegalStateException();
-
-        listener.update();
-
-        // swap buffers
         
-        if (frameRate > 0){
-//            Display.sync(frameRate);
-            // synchronzie to framerate
-        }
+        if (renderable.get())
+        {
+            milliStart = System.currentTimeMillis();
+                    
 
-        if (autoFlush)
-            renderer.onFrame();
-    }
-
-    /**
-     * TODO: get these methods to follow the spec
-     * @param waitFor
-     */
-    public void create(boolean waitFor) {
-        if (created.get()){
-            logger.warning("create() called when display is already created!");
-            return;
+    
+            listener.update();
+            
+            
+            if (autoFlush)
+            {
+                renderer.onFrame();
+            }
+            
+            milliDelta = System.currentTimeMillis() - milliStart;
+            
+            // Enforce a FPS cap
+            if (milliDelta < minFrameDuration) 
+            {
+                //logger.log(Level.INFO, "Time per frame {0}", milliDelta);
+                try {
+                    Thread.sleep(minFrameDuration - milliDelta);
+                } catch (InterruptedException e) {
+                }
+            }
+            
         }
+        
+        
     }
-
-    public void create(){
-        create(false);
+    
+    @Override
+    public boolean isRenderable()
+    {
+        return renderable.get();
     }
-
-    public void restart() {
+    
+    @Override
+    public void create(boolean waitFor)
+    {
+        if (waitFor)
+            waitFor(true);
     }
-
-    public boolean isRenderable() {
-       // TODO isRenderable
-        return true;
+    
+    public void create()
+    {
+        create(false);
     }
     
-    /**
-     * TODO: get these methods to follow the spec
-     * @param waitFor
-     */
-    public void destroy(boolean waitFor) {
+    @Override
+    public void restart() 
+    {
+        
+    }
+    
+    @Override
+    public void destroy(boolean waitFor) 
+    {
         needClose.set(true);
+        if (waitFor)
+            waitFor(false);
     }
-
-    public void destroy(){
+           
+    public void destroy()
+    {
         destroy(false);
     }
+    
+    protected void waitFor(boolean createdVal)
+    {
+        synchronized (createdLock){
+            while (created.get() != createdVal){
+                try {
+                    createdLock.wait();
+                } catch (InterruptedException ex) {
+                }
+            }
+        }
+    }
 
 }

+ 23 - 35
engine/src/android/com/jme3/texture/plugins/AndroidImageLoader.java

@@ -2,8 +2,11 @@ package com.jme3.texture.plugins;
 
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
+
 import com.jme3.asset.AssetInfo;
 import com.jme3.asset.AssetLoader;
+import com.jme3.asset.TextureKey;
 import com.jme3.texture.Image;
 import com.jme3.texture.Image.Format;
 import com.jme3.util.BufferUtils;
@@ -11,16 +14,19 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.nio.ByteBuffer;
 
-public class AndroidImageLoader implements AssetLoader {
+public class AndroidImageLoader implements AssetLoader 
+{
 
-    public Object load2(AssetInfo info) throws IOException {
+    public Object load2(AssetInfo info) throws IOException 
+    {
         ByteBuffer bb = BufferUtils.createByteBuffer(1 * 1 * 2);
         bb.put( (byte) 0xff ).put( (byte) 0xff );
         bb.clear();
         return new Image(Format.RGB5A1, 1, 1, bb);
     }
 
-    public Object load(AssetInfo info) throws IOException {
+    public Object load(AssetInfo info) throws IOException 
+    {
         InputStream in = null;
         Bitmap bitmap = null;
         try {
@@ -36,59 +42,41 @@ public class AndroidImageLoader implements AssetLoader {
 
         int width = bitmap.getWidth();
         int height = bitmap.getHeight();
-        int bytesPerPixel = -1;
         Format fmt;
 
         switch (bitmap.getConfig()){
             case ALPHA_8:
-                bytesPerPixel = 1;
                 fmt = Format.Alpha8;
                 break;
             case ARGB_4444:
-                bytesPerPixel = 2;
                 fmt = Format.ARGB4444;
                 break;
             case ARGB_8888:
-                bytesPerPixel = 4;
                 fmt = Format.RGBA8;
                 break;
-            case RGB_565:
-                bytesPerPixel = 2;
+            case RGB_565:        
                 fmt = Format.RGB565;
                 break;
             default:
                 return null;
         }
 
-//        if (width > 128 || height > 128){
-//            if (width > height){
-//                float aspect = (float) height / width;
-//                width = 128;
-//                height = (int) (128 * aspect);
-//
-//            }else{
-//                float aspect = (float) width / height;
-//                width = (int) (128 * aspect);
-//                height = 128;
-//            }
-//            bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
-//        }
+        if ( ((TextureKey)info.getKey()).isFlipY() )
+        {
+            Bitmap newBitmap = null;
+            Matrix flipMat = new Matrix();
+            flipMat.preScale(1.0f, -1.0f);
+            newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), flipMat, false);
+            bitmap.recycle();
+            bitmap = newBitmap;
 
-//        if ( ((TextureKey)info.getKey()).isFlipY() ){
-//            Bitmap newBitmap = null;
-//            Matrix flipMat = new Matrix();
-//            flipMat.preScale(1.0f, -1.0f);
-//            newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), flipMat, false);
-//            bitmap.recycle();
-//            bitmap = newBitmap;
-//
-//            if (bitmap == null){
-//                throw new IOException("Failed to load image2: "+info.getKey().getName());
-//            }
-//        }
+            if (bitmap == null){
+                throw new IOException("Failed to flip image: "+info.getKey().getName());
+            }
+        }
 
         Image image = new Image(fmt, width, height, null);
-        image.setEfficentData(bitmap);
+        image.setEfficentData(bitmap);        
         return image;
     }
 

+ 359 - 0
engine/src/android/com/jme3/util/FastInteger.java

@@ -0,0 +1,359 @@
+package com.jme3.util;
+
+
+/**
+ * The wrapper for the primitive type {@code int}.
+ * <p>
+ * As with the specification, this implementation relies on code laid out in <a
+ * href="http://www.hackersdelight.org/">Henry S. Warren, Jr.'s Hacker's
+ * Delight, (Addison Wesley, 2002)</a> as well as <a
+ * href="http://aggregate.org/MAGIC/">The Aggregate's Magic Algorithms</a>.
+ *
+ * @see java.lang.Number
+ * @since 1.1
+ */
+public final class FastInteger {
+
+    /**
+     * Constant for the maximum {@code int} value, 2<sup>31</sup>-1.
+     */
+    public static final int MAX_VALUE = 0x7FFFFFFF;
+
+    /**
+     * Constant for the minimum {@code int} value, -2<sup>31</sup>.
+     */
+    public static final int MIN_VALUE = 0x80000000;
+
+    /**
+     * Constant for the number of bits needed to represent an {@code int} in
+     * two's complement form.
+     *
+     * @since 1.5
+     */
+    public static final int SIZE = 32;
+    
+    /*
+     * Progressively smaller decimal order of magnitude that can be represented
+     * by an instance of Integer. Used to help compute the String
+     * representation.
+     */
+    private static final int[] decimalScale = new int[] { 1000000000, 100000000,
+            10000000, 1000000, 100000, 10000, 1000, 100, 10, 1 };
+    
+    /**
+     * Converts the specified integer into its decimal string representation.
+     * The returned string is a concatenation of a minus sign if the number is
+     * negative and characters from '0' to '9'.
+     * 
+     * @param value
+     *            the integer to convert.
+     * @return the decimal string representation of {@code value}.
+     */
+    public static boolean toCharArray(int value, char[] output) {
+        if (value == 0) 
+        {
+            output[0] = '0';
+            output[1] = 0;
+            return true;
+        }
+
+        // Faster algorithm for smaller Integers
+        if (value < 1000 && value > -1000) {
+
+            int positive_value = value < 0 ? -value : value;
+            int first_digit = 0;
+            if (value < 0) {
+                output[0] = '-';
+                first_digit++;
+            }
+            int last_digit = first_digit;
+            int quot = positive_value;
+            do {
+                int res = quot / 10;
+                int digit_value = quot - ((res << 3) + (res << 1));
+                digit_value += '0';
+                output[last_digit++] = (char) digit_value;
+                quot = res;
+            } while (quot != 0);
+
+            int count = last_digit--;
+            do {
+                char tmp = output[last_digit];
+                output[last_digit--] = output[first_digit];
+                output[first_digit++] = tmp;
+            } while (first_digit < last_digit);
+            output[count] = 0;
+            return true;
+        }
+        if (value == MIN_VALUE) {
+            System.arraycopy("-2147483648".toCharArray(), 0, output, 0, 12);
+            output[12] = 0;
+            return true;
+        }
+
+
+        int positive_value = value < 0 ? -value : value;
+        byte first_digit = 0;
+        if (value < 0) {
+            output[0] = '-';
+            first_digit++;
+        }
+        byte last_digit = first_digit;
+        byte count;
+        int number;
+        boolean start = false;
+        for (int i = 0; i < 9; i++) {
+            count = 0;
+            if (positive_value < (number = decimalScale[i])) {
+                if (start) {
+                    output[last_digit++] = '0';
+                }
+                continue;
+            }
+
+            if (i > 0) {
+                number = (decimalScale[i] << 3);
+                if (positive_value >= number) {
+                    positive_value -= number;
+                    count += 8;
+                }
+                number = (decimalScale[i] << 2);
+                if (positive_value >= number) {
+                    positive_value -= number;
+                    count += 4;
+                }
+            }
+            number = (decimalScale[i] << 1);
+            if (positive_value >= number) {
+                positive_value -= number;
+                count += 2;
+            }
+            if (positive_value >= decimalScale[i]) {
+                positive_value -= decimalScale[i];
+                count++;
+            }
+            if (count > 0 && !start) {
+                start = true;
+            }
+            if (start) {
+                output[last_digit++] = (char) (count + '0');
+            }
+        }
+
+        output[last_digit++] = (char) (positive_value + '0');
+        output[last_digit] = 0;
+        count = last_digit--;
+        return true;
+    }
+
+
+    /**
+     * Determines the highest (leftmost) bit of the specified integer that is 1
+     * and returns the bit mask value for that bit. This is also referred to as
+     * the Most Significant 1 Bit. Returns zero if the specified integer is
+     * zero.
+     * 
+     * @param i
+     *            the integer to examine.
+     * @return the bit mask indicating the highest 1 bit in {@code i}.
+     * @since 1.5
+     */
+    public static int highestOneBit(int i) {
+        i |= (i >> 1);
+        i |= (i >> 2);
+        i |= (i >> 4);
+        i |= (i >> 8);
+        i |= (i >> 16);
+        return (i & ~(i >>> 1));
+    }
+
+    /**
+     * Determines the lowest (rightmost) bit of the specified integer that is 1
+     * and returns the bit mask value for that bit. This is also referred
+     * to as the Least Significant 1 Bit. Returns zero if the specified integer
+     * is zero.
+     * 
+     * @param i
+     *            the integer to examine.
+     * @return the bit mask indicating the lowest 1 bit in {@code i}.
+     * @since 1.5
+     */
+    public static int lowestOneBit(int i) {
+        return (i & (-i));
+    }
+
+    /**
+     * Determines the number of leading zeros in the specified integer prior to
+     * the {@link #highestOneBit(int) highest one bit}.
+     *
+     * @param i
+     *            the integer to examine.
+     * @return the number of leading zeros in {@code i}.
+     * @since 1.5
+     */
+    public static int numberOfLeadingZeros(int i) {
+        i |= i >> 1;
+        i |= i >> 2;
+        i |= i >> 4;
+        i |= i >> 8;
+        i |= i >> 16;
+        return bitCount(~i);
+    }
+
+    /**
+     * Determines the number of trailing zeros in the specified integer after
+     * the {@link #lowestOneBit(int) lowest one bit}.
+     *
+     * @param i
+     *            the integer to examine.
+     * @return the number of trailing zeros in {@code i}.
+     * @since 1.5
+     */
+    public static int numberOfTrailingZeros(int i) {
+        return bitCount((i & -i) - 1);
+    }
+
+    /**
+     * Counts the number of 1 bits in the specified integer; this is also
+     * referred to as population count.
+     *
+     * @param i
+     *            the integer to examine.
+     * @return the number of 1 bits in {@code i}.
+     * @since 1.5
+     */
+    public static int bitCount(int i) {
+        i -= ((i >> 1) & 0x55555555);
+        i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
+        i = (((i >> 4) + i) & 0x0F0F0F0F);
+        i += (i >> 8);
+        i += (i >> 16);
+        return (i & 0x0000003F);
+    }
+
+    /**
+     * Rotates the bits of the specified integer to the left by the specified
+     * number of bits.
+     *
+     * @param i
+     *            the integer value to rotate left.
+     * @param distance
+     *            the number of bits to rotate.
+     * @return the rotated value.
+     * @since 1.5
+     */
+    public static int rotateLeft(int i, int distance) {
+        if (distance == 0) {
+            return i;
+        }
+        /*
+         * According to JLS3, 15.19, the right operand of a shift is always
+         * implicitly masked with 0x1F, which the negation of 'distance' is
+         * taking advantage of.
+         */
+        return ((i << distance) | (i >>> (-distance)));
+    }
+
+    /**
+     * Rotates the bits of the specified integer to the right by the specified
+     * number of bits.
+     *
+     * @param i
+     *            the integer value to rotate right.
+     * @param distance
+     *            the number of bits to rotate.
+     * @return the rotated value.
+     * @since 1.5
+     */
+    public static int rotateRight(int i, int distance) {
+        if (distance == 0) {
+            return i;
+        }
+        /*
+         * According to JLS3, 15.19, the right operand of a shift is always
+         * implicitly masked with 0x1F, which the negation of 'distance' is
+         * taking advantage of.
+         */
+        return ((i >>> distance) | (i << (-distance)));
+    }
+
+    /**
+     * Reverses the order of the bytes of the specified integer.
+     * 
+     * @param i
+     *            the integer value for which to reverse the byte order.
+     * @return the reversed value.
+     * @since 1.5
+     */
+    public static int reverseBytes(int i) {
+        int b3 = i >>> 24;
+        int b2 = (i >>> 8) & 0xFF00;
+        int b1 = (i & 0xFF00) << 8;
+        int b0 = i << 24;
+        return (b0 | b1 | b2 | b3);
+    }
+
+    /**
+     * Reverses the order of the bits of the specified integer.
+     * 
+     * @param i
+     *            the integer value for which to reverse the bit order.
+     * @return the reversed value.
+     * @since 1.5
+     */
+    public static int reverse(int i) {
+        // From Hacker's Delight, 7-1, Figure 7-1
+        i = (i & 0x55555555) << 1 | (i >> 1) & 0x55555555;
+        i = (i & 0x33333333) << 2 | (i >> 2) & 0x33333333;
+        i = (i & 0x0F0F0F0F) << 4 | (i >> 4) & 0x0F0F0F0F;
+        return reverseBytes(i);
+    }
+
+    /**
+     * Returns the value of the {@code signum} function for the specified
+     * integer.
+     * 
+     * @param i
+     *            the integer value to check.
+     * @return -1 if {@code i} is negative, 1 if {@code i} is positive, 0 if
+     *         {@code i} is zero.
+     * @since 1.5
+     */
+    public static int signum(int i) {
+        return (i == 0 ? 0 : (i < 0 ? -1 : 1));
+    }
+
+    /**
+     * Returns a {@code Integer} instance for the specified integer value.
+     * <p>
+     * If it is not necessary to get a new {@code Integer} instance, it is
+     * recommended to use this method instead of the constructor, since it
+     * maintains a cache of instances which may result in better performance.
+     *
+     * @param i
+     *            the integer value to store in the instance.
+     * @return a {@code Integer} instance containing {@code i}.
+     * @since 1.5
+     */
+    public static Integer valueOf(int i) {
+        if (i < -128 || i > 127) {
+            return new Integer(i);
+        }
+        return valueOfCache.CACHE [i+128];
+
+    }
+
+   static class valueOfCache {
+        /**
+         * <p>
+         * A cache of instances used by {@link Integer#valueOf(int)} and auto-boxing.
+         */
+        static final Integer[] CACHE = new Integer[256];
+
+        static {
+            for(int i=-128; i<=127; i++) {
+                CACHE[i+128] = new Integer(i);
+            }
+        }
+    }
+}