New drop of the jmonkeyengine library

A new drop of the jmonkeyengine library sources, based on a
2012-03-05 snapshot. Fixes a few unnecessary memory allocations in
the main rendering loop.

Change-Id: I51ac0942fe87204df102cfdce746b59a5cb5ff85
diff --git a/engine/src/android/com/jme3/app/AndroidHarness.java b/engine/src/android/com/jme3/app/AndroidHarness.java
index 6631003..0ad2ba1 100644
--- a/engine/src/android/com/jme3/app/AndroidHarness.java
+++ b/engine/src/android/com/jme3/app/AndroidHarness.java
@@ -70,6 +70,10 @@
      */

     protected boolean mouseEventsInvertY = true;

     /**

+     * if true finish this activity when the jme app is stopped

+     */

+    protected boolean finishOnAppStop = true;

+    /**

      * Title of the exit dialog, default is "Do you want to exit?"

      */

     protected String exitDialogTitle = "Do you want to exit?";

@@ -131,7 +135,7 @@
         boolean bIsLogFormatSet = false;

         do {

             if (log.getHandlers().length == 0) {

-                log = logger.getParent();

+                log = log.getParent();

                 if (log != null) {

                     for (Handler h : log.getHandlers()) {

                         //h.setFormatter(new SimpleFormatter());

@@ -395,4 +399,10 @@
             }

         }

     }

+

+    public boolean isFinishOnAppStop() {

+        return finishOnAppStop;

+    }

+    

+    

 }

diff --git a/engine/src/android/com/jme3/asset/AndroidAssetManager.java b/engine/src/android/com/jme3/asset/AndroidAssetManager.java
index c6f0b17..b2140dd 100644
--- a/engine/src/android/com/jme3/asset/AndroidAssetManager.java
+++ b/engine/src/android/com/jme3/asset/AndroidAssetManager.java
@@ -37,6 +37,7 @@
 import com.jme3.texture.Texture;

 import com.jme3.texture.plugins.AndroidImageLoader;

 import java.net.URL;

+import java.util.logging.Level;

 import java.util.logging.Logger;

 

 /**

@@ -57,6 +58,14 @@
         //this(Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Android.cfg"));

         this(null);

     }

+    

+    private void registerLoaderSafe(Class<? extends AssetLoader> loaderClass, String ... extensions) {

+        try {

+            registerLoader(loaderClass, extensions);

+        } catch (Exception e){

+            logger.log(Level.WARNING, "Failed to load AssetLoader", e);

+        }

+    }

 

     /**

      * AndroidAssetManager constructor

@@ -67,25 +76,29 @@
         System.setProperty("org.xml.sax.driver", "org.xmlpull.v1.sax2.Driver");

 

         // Set Default Android config        	       

-        this.registerLocator("", AndroidLocator.class);

-        this.registerLocator("", ClasspathLocator.class);

-        this.registerLoader(AndroidImageLoader.class, "jpg", "bmp", "gif", "png", "jpeg");

-        this.registerLoader(AndroidAudioLoader.class, "ogg", "mp3", "wav");

-        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");

+        registerLocator("", AndroidLocator.class);

+        registerLocator("", ClasspathLocator.class);

+        

+        registerLoader(AndroidImageLoader.class, "jpg", "bmp", "gif", "png", "jpeg");

+        registerLoader(AndroidAudioLoader.class, "ogg", "mp3", "wav");

+        registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3m");

+        registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3md");

+        registerLoader(com.jme3.shader.plugins.GLSLLoader.class, "vert", "frag", "glsl", "glsllib");

+        registerLoader(com.jme3.export.binary.BinaryImporter.class, "j3o");

+        registerLoader(com.jme3.font.plugins.BitmapFontLoader.class, "fnt");

+        

+        // Less common loaders (especially on Android)

+        registerLoaderSafe(com.jme3.texture.plugins.DDSLoader.class, "dds");

+        registerLoaderSafe(com.jme3.texture.plugins.PFMLoader.class, "pfm");

+        registerLoaderSafe(com.jme3.texture.plugins.HDRLoader.class, "hdr");

+        registerLoaderSafe(com.jme3.texture.plugins.TGALoader.class, "tga");

+        registerLoaderSafe(com.jme3.scene.plugins.OBJLoader.class, "obj");

+        registerLoaderSafe(com.jme3.scene.plugins.MTLLoader.class, "mtl");

+        registerLoaderSafe(com.jme3.scene.plugins.ogre.MeshLoader.class, "mesh.xml");

+        registerLoaderSafe(com.jme3.scene.plugins.ogre.SkeletonLoader.class, "skeleton.xml");

+        registerLoaderSafe(com.jme3.scene.plugins.ogre.MaterialLoader.class, "material");

+        registerLoaderSafe(com.jme3.scene.plugins.ogre.SceneLoader.class, "scene");

+        

 

         logger.info("AndroidAssetManager created.");

     }

diff --git a/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java b/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java
index 01b1cab..225faf3 100644
--- a/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java
+++ b/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java
@@ -4,9 +4,6 @@
 import com.jme3.system.android.JmeAndroidSystem;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.logging.Level;
 import java.util.logging.Logger;
 
 public class AndroidLocator implements AssetLocator {
@@ -80,7 +77,7 @@
         assetPath = assetPath.replace("//", "/");
         try {
             return create(manager, key, assetPath);
-        }catch (IOException ex){
+        } catch (IOException ex) {
             // This is different handling than URL locator
             // since classpath locating would return null at the getResource() 
             // call, otherwise there's a more critical error...
diff --git a/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java b/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java
index fc9c03e..a11425b 100644
--- a/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java
+++ b/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java
@@ -5,15 +5,16 @@
 import com.jme3.audio.android.AndroidAudioData;

 import java.io.IOException;

 

-public class AndroidAudioLoader implements AssetLoader 

-{

+/**

+ * <code>AndroidAudioLoader</code> will create an 

+ * {@link AndroidAudioData} object with the specified asset key.

+ */

+public class AndroidAudioLoader implements AssetLoader {

 

     @Override

-    public Object load(AssetInfo assetInfo) throws IOException 

-    {

+    public Object load(AssetInfo assetInfo) throws IOException {

         AndroidAudioData result = new AndroidAudioData();

-        result.setAssetKey( assetInfo.getKey() );

+        result.setAssetKey(assetInfo.getKey());

         return result;

     }

-

 }

diff --git a/engine/src/android/com/jme3/input/android/AndroidInput.java b/engine/src/android/com/jme3/input/android/AndroidInput.java
index ca1141d..9bbbe4e 100644
--- a/engine/src/android/com/jme3/input/android/AndroidInput.java
+++ b/engine/src/android/com/jme3/input/android/AndroidInput.java
@@ -1,619 +1,620 @@
-package com.jme3.input.android;
-
-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.KeyInput;
-import com.jme3.input.RawInputListener;
-import com.jme3.input.TouchInput;
-import com.jme3.input.event.MouseButtonEvent;
-import com.jme3.input.event.MouseMotionEvent;
-import com.jme3.input.event.TouchEvent;
-import com.jme3.input.event.TouchEvent.Type;
-import com.jme3.math.Vector2f;
-import com.jme3.util.RingBuffer;
-import java.util.HashMap;
-import java.util.logging.Logger;
-
-/**
- * <code>AndroidInput</code> is one of the main components that connect jme with android. Is derived from GLSurfaceView and handles all Inputs
- * @author larynx
- *
- */
-public class AndroidInput extends GLSurfaceView implements TouchInput,
-        GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, ScaleGestureDetector.OnScaleGestureListener {
-
-    final private static int MAX_EVENTS = 1024;
-    // Custom settings
-    public boolean mouseEventsEnabled = true;
-    public boolean mouseEventsInvertX = false;
-    public boolean mouseEventsInvertY = false;
-    public boolean keyboardEventsEnabled = false;
-    public boolean dontSendHistory = false;
-    // Used to transfer events from android thread to GLThread
-    final private RingBuffer<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS);
-    final private RingBuffer<TouchEvent> eventPoolUnConsumed = new RingBuffer<TouchEvent>(MAX_EVENTS);
-    final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS);
-    final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();
-    // Internal
-    private ScaleGestureDetector scaledetector;
-    private GestureDetector detector;
-    private int lastX;
-    private int lastY;
-    private final static Logger logger = Logger.getLogger(AndroidInput.class.getName());
-    private boolean isInitialized = false;
-    private RawInputListener listener = null;
-    private static final int[] ANDROID_TO_JME = {
-        0x0, // unknown
-        0x0, // key code soft left
-        0x0, // key code soft right
-        KeyInput.KEY_HOME,
-        KeyInput.KEY_ESCAPE, // key back
-        0x0, // key call
-        0x0, // key endcall
-        KeyInput.KEY_0,
-        KeyInput.KEY_1,
-        KeyInput.KEY_2,
-        KeyInput.KEY_3,
-        KeyInput.KEY_4,
-        KeyInput.KEY_5,
-        KeyInput.KEY_6,
-        KeyInput.KEY_7,
-        KeyInput.KEY_8,
-        KeyInput.KEY_9,
-        KeyInput.KEY_MULTIPLY,
-        0x0, // key pound
-        KeyInput.KEY_UP,
-        KeyInput.KEY_DOWN,
-        KeyInput.KEY_LEFT,
-        KeyInput.KEY_RIGHT,
-        KeyInput.KEY_RETURN, // dpad center
-        0x0, // volume up
-        0x0, // volume down
-        KeyInput.KEY_POWER, // power (?)
-        0x0, // camera
-        0x0, // clear
-        KeyInput.KEY_A,
-        KeyInput.KEY_B,
-        KeyInput.KEY_C,
-        KeyInput.KEY_D,
-        KeyInput.KEY_E,
-        KeyInput.KEY_F,
-        KeyInput.KEY_G,
-        KeyInput.KEY_H,
-        KeyInput.KEY_I,
-        KeyInput.KEY_J,
-        KeyInput.KEY_K,
-        KeyInput.KEY_L,
-        KeyInput.KEY_M,
-        KeyInput.KEY_N,
-        KeyInput.KEY_O,
-        KeyInput.KEY_P,
-        KeyInput.KEY_Q,
-        KeyInput.KEY_R,
-        KeyInput.KEY_S,
-        KeyInput.KEY_T,
-        KeyInput.KEY_U,
-        KeyInput.KEY_V,
-        KeyInput.KEY_W,
-        KeyInput.KEY_X,
-        KeyInput.KEY_Y,
-        KeyInput.KEY_Z,
-        KeyInput.KEY_COMMA,
-        KeyInput.KEY_PERIOD,
-        KeyInput.KEY_LMENU,
-        KeyInput.KEY_RMENU,
-        KeyInput.KEY_LSHIFT,
-        KeyInput.KEY_RSHIFT,
-        //        0x0, // fn
-        //        0x0, // cap (?)
-
-        KeyInput.KEY_TAB,
-        KeyInput.KEY_SPACE,
-        0x0, // sym (?) symbol
-        0x0, // explorer
-        0x0, // envelope
-        KeyInput.KEY_RETURN, // newline/enter
-        KeyInput.KEY_DELETE,
-        KeyInput.KEY_GRAVE,
-        KeyInput.KEY_MINUS,
-        KeyInput.KEY_EQUALS,
-        KeyInput.KEY_LBRACKET,
-        KeyInput.KEY_RBRACKET,
-        KeyInput.KEY_BACKSLASH,
-        KeyInput.KEY_SEMICOLON,
-        KeyInput.KEY_APOSTROPHE,
-        KeyInput.KEY_SLASH,
-        KeyInput.KEY_AT, // at (@)
-        KeyInput.KEY_NUMLOCK, //0x0, // num
-        0x0, //headset hook
-        0x0, //focus
-        KeyInput.KEY_ADD,
-        KeyInput.KEY_LMETA, //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
-    };
-
-    public AndroidInput(Context ctx, AttributeSet attribs) {
-        super(ctx, attribs);
-        detector = new GestureDetector(null, this, null, false);
-        scaledetector = new ScaleGestureDetector(ctx, this);
-
-    }
-
-    public AndroidInput(Context ctx) {
-        super(ctx);
-        detector = new GestureDetector(null, this, null, false);
-        scaledetector = new ScaleGestureDetector(ctx, this);
-    }
-
-    private TouchEvent getNextFreeTouchEvent() {
-        return getNextFreeTouchEvent(false);
-    }
-
-    /**
-     * Fetches a touch event from the reuse pool
-     * @param wait if true waits for a reusable event to get available/released by an other thread, if false returns a new one if needed
-     * @return a usable TouchEvent
-     */
-    private TouchEvent getNextFreeTouchEvent(boolean wait) {
-        TouchEvent evt = null;
-        synchronized (eventPoolUnConsumed) {
-            int size = eventPoolUnConsumed.size();
-            while (size > 0) {
-                evt = eventPoolUnConsumed.pop();
-                if (!evt.isConsumed()) {
-                    eventPoolUnConsumed.push(evt);
-                    evt = null;
-                } else {
-                    break;
-                }
-                size--;
-            }
-        }
-
-
-        if (evt == null) {
-            if (eventPool.isEmpty() && wait) {
-                logger.warning("eventPool buffer underrun");
-                boolean isEmpty;
-                do {
-                    synchronized (eventPool) {
-                        isEmpty = eventPool.isEmpty();
-                    }
-                    try {
-                        Thread.sleep(50);
-                    } catch (InterruptedException e) {
-                    }
-                } while (isEmpty);
-                synchronized (eventPool) {
-                    evt = eventPool.pop();
-                }
-            } else if (eventPool.isEmpty()) {
-                evt = new TouchEvent();
-                logger.warning("eventPool buffer underrun");
-            } else {
-                synchronized (eventPool) {
-                    evt = eventPool.pop();
-                }
-            }
-        }
-        return evt;
-    }
-
-    /**
-     * onTouchEvent gets called from android thread on touchpad events
-     */
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        boolean bWasHandled = false;
-        TouchEvent touch;
-        //    System.out.println("native : " + event.getAction());
-        int action = event.getAction() & MotionEvent.ACTION_MASK;
-        int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
-                >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-        int pointerId = event.getPointerId(pointerIndex);
-
-        // final int historySize = event.getHistorySize();
-        //final int pointerCount = event.getPointerCount();
-
-
-        switch (action) {
-
-            case MotionEvent.ACTION_POINTER_DOWN:
-            case MotionEvent.ACTION_DOWN:
-
-
-                touch = getNextFreeTouchEvent();
-                touch.set(Type.DOWN, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);
-                touch.setPointerId(pointerId);
-                touch.setTime(event.getEventTime());
-                touch.setPressure(event.getPressure(pointerIndex));
-                processEvent(touch);
-
-                bWasHandled = true;
-                break;
-
-            case MotionEvent.ACTION_POINTER_UP:
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-
-                touch = getNextFreeTouchEvent();
-                touch.set(Type.UP, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);
-                touch.setPointerId(pointerId);
-                touch.setTime(event.getEventTime());
-                touch.setPressure(event.getPressure(pointerIndex));
-                processEvent(touch);
-
-
-                bWasHandled = true;
-                break;
-            case MotionEvent.ACTION_MOVE:
-
-
-                // Convert all pointers into events
-                for (int p = 0; p < event.getPointerCount(); p++) {
-                    Vector2f lastPos = lastPositions.get(pointerIndex);
-                    if (lastPos == null) {
-                        lastPos = new Vector2f(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));
-                        lastPositions.put(pointerId, lastPos);
-                    }
-                    touch = getNextFreeTouchEvent();
-                    touch.set(Type.MOVE, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), event.getX(pointerIndex) - lastPos.x, this.getHeight() - event.getY(pointerIndex) - lastPos.y);
-                    touch.setPointerId(pointerId);
-                    touch.setTime(event.getEventTime());
-                    touch.setPressure(event.getPressure(pointerIndex));
-                    processEvent(touch);
-                    lastPos.set(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));
-                }
-                bWasHandled = true;
-                break;
-
-
-            case MotionEvent.ACTION_OUTSIDE:
-                break;
-
-        }
-
-        // Try to detect gestures        
-        this.detector.onTouchEvent(event);
-        this.scaledetector.onTouchEvent(event);
-
-        return bWasHandled;
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        TouchEvent evt;
-        evt = getNextFreeTouchEvent();
-        evt.set(TouchEvent.Type.KEY_DOWN);
-        evt.setKeyCode(keyCode);
-        evt.setCharacters(event.getCharacters());
-        evt.setTime(event.getEventTime());
-
-        // Send the event
-        processEvent(evt);
-
-        // Handle all keys ourself except Volume Up/Down
-        if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
-            return false;
-        } else {
-            return true;
-        }
-
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        TouchEvent evt;
-        evt = getNextFreeTouchEvent();
-        evt.set(TouchEvent.Type.KEY_UP);
-        evt.setKeyCode(keyCode);
-        evt.setCharacters(event.getCharacters());
-        evt.setTime(event.getEventTime());
-
-        // Send the event
-        processEvent(evt);
-
-        // Handle all keys ourself except Volume Up/Down
-        if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
-            return false;
-        } else {
-            return true;
-        }
-    }
-
-    // -----------------------------------------
-    // JME3 Input interface
-    @Override
-    public void initialize() {
-        TouchEvent item;
-        for (int i = 0; i < MAX_EVENTS; i++) {
-            item = new TouchEvent();
-            eventPool.push(item);
-        }
-        isInitialized = true;
-    }
-
-    @Override
-    public void destroy() {
-        isInitialized = false;
-
-        // Clean up queues
-        while (!eventPool.isEmpty()) {
-            eventPool.pop();
-        }
-        while (!eventQueue.isEmpty()) {
-            eventQueue.pop();
-        }
-    }
-
-    @Override
-    public boolean isInitialized() {
-        return isInitialized;
-    }
-
-    @Override
-    public void setInputListener(RawInputListener listener) {
-        this.listener = listener;
-    }
-
-    @Override
-    public long getInputTimeNanos() {
-        return System.nanoTime();
-    }
-    // -----------------------------------------
-
-    private void processEvent(TouchEvent event) {
-        synchronized (eventQueue) {
-            eventQueue.push(event);
-        }
-    }
-
-    //  ---------------  INSIDE GLThread  --------------- 
-    @Override
-    public void update() {
-        generateEvents();
-    }
-
-    private void generateEvents() {
-        if (listener != null) {
-            TouchEvent event;
-            MouseButtonEvent btn;
-            int newX;
-            int newY;
-
-            while (!eventQueue.isEmpty()) {
-                synchronized (eventQueue) {
-                    event = eventQueue.pop();
-                }
-                if (event != null) {
-                    listener.onTouchEvent(event);
-
-                    if (mouseEventsEnabled) {
-                        if (mouseEventsInvertX) {
-                            newX = this.getWidth() - (int) event.getX();
-                        } else {
-                            newX = (int) event.getX();
-                        }
-
-                        if (mouseEventsInvertY) {
-                            newY = this.getHeight() - (int) event.getY();
-                        } else {
-                            newY = (int) event.getY();
-                        }
-
-                        switch (event.getType()) {
-                            case DOWN:
-                                // Handle mouse down event 
-                                btn = new MouseButtonEvent(0, true, newX, newY);
-                                btn.setTime(event.getTime());
-                                listener.onMouseButtonEvent(btn);
-                                // Store current pos
-                                lastX = -1;
-                                lastY = -1;
-                                break;
-
-                            case UP:
-                                // Handle mouse up event 
-                                btn = new MouseButtonEvent(0, false, newX, newY);
-                                btn.setTime(event.getTime());
-                                listener.onMouseButtonEvent(btn);
-                                // Store current pos
-                                lastX = -1;
-                                lastY = -1;
-                                break;
-
-                            case MOVE:
-                                int dx;
-                                int dy;
-                                if (lastX != -1) {
-                                    dx = newX - lastX;
-                                    dy = newY - lastY;
-                                } else {
-                                    dx = 0;
-                                    dy = 0;
-                                }
-                                MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0);
-                                mot.setTime(event.getTime());
-                                listener.onMouseMotionEvent(mot);
-                                lastX = newX;
-                                lastY = newY;
-                                break;
-                        }
-
-
-                    }
-                }
-
-                if (event.isConsumed() == false) {
-                    synchronized (eventPoolUnConsumed) {
-                        eventPoolUnConsumed.push(event);
-                    }
-
-                } else {
-                    synchronized (eventPool) {
-                        eventPool.push(event);
-                    }
-                }
-            }
-
-        }
-    }
-    //  --------------- ENDOF INSIDE GLThread  --------------- 
-
-    // --------------- Gesture detected callback events  --------------- 
-    public boolean onDown(MotionEvent event) {
-        return false;
-    }
-
-    public void onLongPress(MotionEvent event) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.LONGPRESSED, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(event.getEventTime());
-        processEvent(touch);
-    }
-
-    public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.FLING, event.getX(), this.getHeight() - event.getY(), vx, vy);
-        touch.setPointerId(0);
-        touch.setTime(event.getEventTime());
-        processEvent(touch);
-
-        return true;
-    }
-
-    public boolean onSingleTapConfirmed(MotionEvent event) {
-        //Nothing to do here the tap has already been detected.
-        return false;
-    }
-
-    public boolean onDoubleTap(MotionEvent event) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.DOUBLETAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(event.getEventTime());
-        processEvent(touch);
-        return true;
-    }
-
-    public boolean onDoubleTapEvent(MotionEvent event) {
-        return false;
-    }
-
-    public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.SCALE_START, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(scaleGestureDetector.getEventTime());
-        touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
-        touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
-        processEvent(touch);
-        //    System.out.println("scaleBegin");
-
-        return true;
-    }
-
-    public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(scaleGestureDetector.getEventTime());
-        touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
-        touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
-        processEvent(touch);
-        //   System.out.println("scale");
-
-        return false;
-    }
-
-    public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(scaleGestureDetector.getEventTime());
-        touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
-        touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
-        processEvent(touch);
-    }
-
-    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.SCROLL, e1.getX(), this.getHeight() - e1.getY(), distanceX, distanceY * (-1));
-        touch.setPointerId(0);
-        touch.setTime(e1.getEventTime());
-        processEvent(touch);
-        //System.out.println("scroll " + e1.getPointerCount());
-        return false;
-    }
-
-    public void onShowPress(MotionEvent event) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.SHOWPRESS, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(event.getEventTime());
-        processEvent(touch);
-    }
-
-    public boolean onSingleTapUp(MotionEvent event) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(event.getEventTime());
-        processEvent(touch);
-        return true;
-    }
-
-    @Override
-    public void setSimulateMouse(boolean simulate) {
-        mouseEventsEnabled = simulate;
-    }
-
-    @Override
-    public void setSimulateKeyboard(boolean simulate) {
-        keyboardEventsEnabled = simulate;
-    }
-
-    @Override
-    public void setOmitHistoricEvents(boolean dontSendHistory) {
-        this.dontSendHistory = dontSendHistory;
-    }
-
-    // TODO: move to TouchInput
-    public boolean isMouseEventsEnabled() {
-        return mouseEventsEnabled;
-    }
-
-    public void setMouseEventsEnabled(boolean mouseEventsEnabled) {
-        this.mouseEventsEnabled = mouseEventsEnabled;
-    }
-
-    public boolean isMouseEventsInvertY() {
-        return mouseEventsInvertY;
-    }
-
-    public void setMouseEventsInvertY(boolean mouseEventsInvertY) {
-        this.mouseEventsInvertY = mouseEventsInvertY;
-    }
-
-    public boolean isMouseEventsInvertX() {
-        return mouseEventsInvertX;
-    }
-
-    public void setMouseEventsInvertX(boolean mouseEventsInvertX) {
-        this.mouseEventsInvertX = mouseEventsInvertX;
-    }
-}
+package com.jme3.input.android;

+

+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.KeyInput;

+import com.jme3.input.RawInputListener;

+import com.jme3.input.TouchInput;

+import com.jme3.input.event.MouseButtonEvent;

+import com.jme3.input.event.MouseMotionEvent;

+import com.jme3.input.event.TouchEvent;

+import com.jme3.input.event.TouchEvent.Type;

+import com.jme3.math.Vector2f;

+import com.jme3.util.RingBuffer;

+import java.util.HashMap;

+import java.util.logging.Logger;

+

+/**

+ * <code>AndroidInput</code> is one of the main components that connect jme with android. Is derived from GLSurfaceView and handles all Inputs

+ * @author larynx

+ *

+ */

+public class AndroidInput extends GLSurfaceView implements 

+        TouchInput,

+        GestureDetector.OnGestureListener, 

+        GestureDetector.OnDoubleTapListener, 

+        ScaleGestureDetector.OnScaleGestureListener {

+

+    final private static int MAX_EVENTS = 1024;

+    // Custom settings

+    public boolean mouseEventsEnabled = true;

+    public boolean mouseEventsInvertX = false;

+    public boolean mouseEventsInvertY = false;

+    public boolean keyboardEventsEnabled = false;

+    public boolean dontSendHistory = false;

+    // Used to transfer events from android thread to GLThread

+    final private RingBuffer<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS);

+    final private RingBuffer<TouchEvent> eventPoolUnConsumed = new RingBuffer<TouchEvent>(MAX_EVENTS);

+    final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS);

+    final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();

+    // Internal

+    private ScaleGestureDetector scaledetector;

+    private GestureDetector detector;

+    private int lastX;

+    private int lastY;

+    private final static Logger logger = Logger.getLogger(AndroidInput.class.getName());

+    private boolean isInitialized = false;

+    private RawInputListener listener = null;

+    private static final int[] ANDROID_TO_JME = {

+        0x0, // unknown

+        0x0, // key code soft left

+        0x0, // key code soft right

+        KeyInput.KEY_HOME,

+        KeyInput.KEY_ESCAPE, // key back

+        0x0, // key call

+        0x0, // key endcall

+        KeyInput.KEY_0,

+        KeyInput.KEY_1,

+        KeyInput.KEY_2,

+        KeyInput.KEY_3,

+        KeyInput.KEY_4,

+        KeyInput.KEY_5,

+        KeyInput.KEY_6,

+        KeyInput.KEY_7,

+        KeyInput.KEY_8,

+        KeyInput.KEY_9,

+        KeyInput.KEY_MULTIPLY,

+        0x0, // key pound

+        KeyInput.KEY_UP,

+        KeyInput.KEY_DOWN,

+        KeyInput.KEY_LEFT,

+        KeyInput.KEY_RIGHT,

+        KeyInput.KEY_RETURN, // dpad center

+        0x0, // volume up

+        0x0, // volume down

+        KeyInput.KEY_POWER, // power (?)

+        0x0, // camera

+        0x0, // clear

+        KeyInput.KEY_A,

+        KeyInput.KEY_B,

+        KeyInput.KEY_C,

+        KeyInput.KEY_D,

+        KeyInput.KEY_E,

+        KeyInput.KEY_F,

+        KeyInput.KEY_G,

+        KeyInput.KEY_H,

+        KeyInput.KEY_I,

+        KeyInput.KEY_J,

+        KeyInput.KEY_K,

+        KeyInput.KEY_L,

+        KeyInput.KEY_M,

+        KeyInput.KEY_N,

+        KeyInput.KEY_O,

+        KeyInput.KEY_P,

+        KeyInput.KEY_Q,

+        KeyInput.KEY_R,

+        KeyInput.KEY_S,

+        KeyInput.KEY_T,

+        KeyInput.KEY_U,

+        KeyInput.KEY_V,

+        KeyInput.KEY_W,

+        KeyInput.KEY_X,

+        KeyInput.KEY_Y,

+        KeyInput.KEY_Z,

+        KeyInput.KEY_COMMA,

+        KeyInput.KEY_PERIOD,

+        KeyInput.KEY_LMENU,

+        KeyInput.KEY_RMENU,

+        KeyInput.KEY_LSHIFT,

+        KeyInput.KEY_RSHIFT,

+        //        0x0, // fn

+        //        0x0, // cap (?)

+

+        KeyInput.KEY_TAB,

+        KeyInput.KEY_SPACE,

+        0x0, // sym (?) symbol

+        0x0, // explorer

+        0x0, // envelope

+        KeyInput.KEY_RETURN, // newline/enter

+        KeyInput.KEY_DELETE,

+        KeyInput.KEY_GRAVE,

+        KeyInput.KEY_MINUS,

+        KeyInput.KEY_EQUALS,

+        KeyInput.KEY_LBRACKET,

+        KeyInput.KEY_RBRACKET,

+        KeyInput.KEY_BACKSLASH,

+        KeyInput.KEY_SEMICOLON,

+        KeyInput.KEY_APOSTROPHE,

+        KeyInput.KEY_SLASH,

+        KeyInput.KEY_AT, // at (@)

+        KeyInput.KEY_NUMLOCK, //0x0, // num

+        0x0, //headset hook

+        0x0, //focus

+        KeyInput.KEY_ADD,

+        KeyInput.KEY_LMETA, //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

+    };

+

+    public AndroidInput(Context ctx, AttributeSet attribs) {

+        super(ctx, attribs);

+        detector = new GestureDetector(null, this, null, false);

+        scaledetector = new ScaleGestureDetector(ctx, this);

+

+    }

+

+    public AndroidInput(Context ctx) {

+        super(ctx);

+        detector = new GestureDetector(null, this, null, false);

+        scaledetector = new ScaleGestureDetector(ctx, this);

+    }

+

+    private TouchEvent getNextFreeTouchEvent() {

+        return getNextFreeTouchEvent(false);

+    }

+

+    /**

+     * Fetches a touch event from the reuse pool

+     * @param wait if true waits for a reusable event to get available/released 

+     * by an other thread, if false returns a new one if needed.

+     * 

+     * @return a usable TouchEvent

+     */

+    private TouchEvent getNextFreeTouchEvent(boolean wait) {

+        TouchEvent evt = null;

+        synchronized (eventPoolUnConsumed) {

+            int size = eventPoolUnConsumed.size();

+            while (size > 0) {

+                evt = eventPoolUnConsumed.pop();

+                if (!evt.isConsumed()) {

+                    eventPoolUnConsumed.push(evt);

+                    evt = null;

+                } else {

+                    break;

+                }

+                size--;

+            }

+        }

+

+        if (evt == null) {

+            if (eventPool.isEmpty() && wait) {

+                logger.warning("eventPool buffer underrun");

+                boolean isEmpty;

+                do {

+                    synchronized (eventPool) {

+                        isEmpty = eventPool.isEmpty();

+                    }

+                    try {

+                        Thread.sleep(50);

+                    } catch (InterruptedException e) {

+                    }

+                } while (isEmpty);

+                synchronized (eventPool) {

+                    evt = eventPool.pop();

+                }

+            } else if (eventPool.isEmpty()) {

+                evt = new TouchEvent();

+                logger.warning("eventPool buffer underrun");

+            } else {

+                synchronized (eventPool) {

+                    evt = eventPool.pop();

+                }

+            }

+        }

+        return evt;

+    }

+

+    /**

+     * onTouchEvent gets called from android thread on touchpad events

+     */

+    @Override

+    public boolean onTouchEvent(MotionEvent event) {

+        boolean bWasHandled = false;

+        TouchEvent touch;

+        //    System.out.println("native : " + event.getAction());

+        int action = event.getAction() & MotionEvent.ACTION_MASK;

+        int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)

+                >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;

+        int pointerId = event.getPointerId(pointerIndex);

+

+        // final int historySize = event.getHistorySize();

+        //final int pointerCount = event.getPointerCount();

+

+        switch (action) {

+            case MotionEvent.ACTION_POINTER_DOWN:

+            case MotionEvent.ACTION_DOWN:

+                touch = getNextFreeTouchEvent();

+                touch.set(Type.DOWN, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);

+                touch.setPointerId(pointerId);

+                touch.setTime(event.getEventTime());

+                touch.setPressure(event.getPressure(pointerIndex));

+                processEvent(touch);

+

+                bWasHandled = true;

+                break;

+

+            case MotionEvent.ACTION_POINTER_UP:

+            case MotionEvent.ACTION_CANCEL:

+            case MotionEvent.ACTION_UP:

+

+                touch = getNextFreeTouchEvent();

+                touch.set(Type.UP, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);

+                touch.setPointerId(pointerId);

+                touch.setTime(event.getEventTime());

+                touch.setPressure(event.getPressure(pointerIndex));

+                processEvent(touch);

+

+

+                bWasHandled = true;

+                break;

+            case MotionEvent.ACTION_MOVE:

+

+

+                // Convert all pointers into events

+                for (int p = 0; p < event.getPointerCount(); p++) {

+                    Vector2f lastPos = lastPositions.get(pointerIndex);

+                    if (lastPos == null) {

+                        lastPos = new Vector2f(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));

+                        lastPositions.put(pointerId, lastPos);

+                    }

+                    touch = getNextFreeTouchEvent();

+                    touch.set(Type.MOVE, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), event.getX(pointerIndex) - lastPos.x, this.getHeight() - event.getY(pointerIndex) - lastPos.y);

+                    touch.setPointerId(pointerId);

+                    touch.setTime(event.getEventTime());

+                    touch.setPressure(event.getPressure(pointerIndex));

+                    processEvent(touch);

+                    lastPos.set(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));

+                }

+                bWasHandled = true;

+                break;

+            case MotionEvent.ACTION_OUTSIDE:

+                break;

+

+        }

+

+        // Try to detect gestures        

+        this.detector.onTouchEvent(event);

+        this.scaledetector.onTouchEvent(event);

+

+        return bWasHandled;

+    }

+

+    @Override

+    public boolean onKeyDown(int keyCode, KeyEvent event) {

+        TouchEvent evt;

+        evt = getNextFreeTouchEvent();

+        evt.set(TouchEvent.Type.KEY_DOWN);

+        evt.setKeyCode(keyCode);

+        evt.setCharacters(event.getCharacters());

+        evt.setTime(event.getEventTime());

+

+        // Send the event

+        processEvent(evt);

+

+        // Handle all keys ourself except Volume Up/Down

+        if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {

+            return false;

+        } else {

+            return true;

+        }

+    }

+

+    @Override

+    public boolean onKeyUp(int keyCode, KeyEvent event) {

+        TouchEvent evt;

+        evt = getNextFreeTouchEvent();

+        evt.set(TouchEvent.Type.KEY_UP);

+        evt.setKeyCode(keyCode);

+        evt.setCharacters(event.getCharacters());

+        evt.setTime(event.getEventTime());

+

+        // Send the event

+        processEvent(evt);

+

+        // Handle all keys ourself except Volume Up/Down

+        if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {

+            return false;

+        } else {

+            return true;

+        }

+    }

+

+    // -----------------------------------------

+    // JME3 Input interface

+    @Override

+    public void initialize() {

+        TouchEvent item;

+        for (int i = 0; i < MAX_EVENTS; i++) {

+            item = new TouchEvent();

+            eventPool.push(item);

+        }

+        isInitialized = true;

+    }

+

+    @Override

+    public void destroy() {

+        isInitialized = false;

+

+        // Clean up queues

+        while (!eventPool.isEmpty()) {

+            eventPool.pop();

+        }

+        while (!eventQueue.isEmpty()) {

+            eventQueue.pop();

+        }

+    }

+

+    @Override

+    public boolean isInitialized() {

+        return isInitialized;

+    }

+

+    @Override

+    public void setInputListener(RawInputListener listener) {

+        this.listener = listener;

+    }

+

+    @Override

+    public long getInputTimeNanos() {

+        return System.nanoTime();

+    }

+    // -----------------------------------------

+

+    private void processEvent(TouchEvent event) {

+        synchronized (eventQueue) {

+            eventQueue.push(event);

+        }

+    }

+

+    //  ---------------  INSIDE GLThread  --------------- 

+    @Override

+    public void update() {

+        generateEvents();

+    }

+

+    private void generateEvents() {

+        if (listener != null) {

+            TouchEvent event;

+            MouseButtonEvent btn;

+            int newX;

+            int newY;

+

+            while (!eventQueue.isEmpty()) {

+                synchronized (eventQueue) {

+                    event = eventQueue.pop();

+                }

+                if (event != null) {

+                    listener.onTouchEvent(event);

+

+                    if (mouseEventsEnabled) {

+                        if (mouseEventsInvertX) {

+                            newX = this.getWidth() - (int) event.getX();

+                        } else {

+                            newX = (int) event.getX();

+                        }

+

+                        if (mouseEventsInvertY) {

+                            newY = this.getHeight() - (int) event.getY();

+                        } else {

+                            newY = (int) event.getY();

+                        }

+

+                        switch (event.getType()) {

+                            case DOWN:

+                                // Handle mouse down event 

+                                btn = new MouseButtonEvent(0, true, newX, newY);

+                                btn.setTime(event.getTime());

+                                listener.onMouseButtonEvent(btn);

+                                // Store current pos

+                                lastX = -1;

+                                lastY = -1;

+                                break;

+

+                            case UP:

+                                // Handle mouse up event 

+                                btn = new MouseButtonEvent(0, false, newX, newY);

+                                btn.setTime(event.getTime());

+                                listener.onMouseButtonEvent(btn);

+                                // Store current pos

+                                lastX = -1;

+                                lastY = -1;

+                                break;

+

+                            case MOVE:

+                                int dx;

+                                int dy;

+                                if (lastX != -1) {

+                                    dx = newX - lastX;

+                                    dy = newY - lastY;

+                                } else {

+                                    dx = 0;

+                                    dy = 0;

+                                }

+                                MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0);

+                                mot.setTime(event.getTime());

+                                listener.onMouseMotionEvent(mot);

+                                lastX = newX;

+                                lastY = newY;

+                                break;

+                        }

+

+

+                    }

+                }

+

+                if (event.isConsumed() == false) {

+                    synchronized (eventPoolUnConsumed) {

+                        eventPoolUnConsumed.push(event);

+                    }

+

+                } else {

+                    synchronized (eventPool) {

+                        eventPool.push(event);

+                    }

+                }

+            }

+

+        }

+    }

+    //  --------------- ENDOF INSIDE GLThread  --------------- 

+

+    // --------------- Gesture detected callback events  --------------- 

+    public boolean onDown(MotionEvent event) {

+        return false;

+    }

+

+    public void onLongPress(MotionEvent event) {

+        TouchEvent touch = getNextFreeTouchEvent();

+        touch.set(Type.LONGPRESSED, event.getX(), this.getHeight() - event.getY(), 0f, 0f);

+        touch.setPointerId(0);

+        touch.setTime(event.getEventTime());

+        processEvent(touch);

+    }

+

+    public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) {

+        TouchEvent touch = getNextFreeTouchEvent();

+        touch.set(Type.FLING, event.getX(), this.getHeight() - event.getY(), vx, vy);

+        touch.setPointerId(0);

+        touch.setTime(event.getEventTime());

+        processEvent(touch);

+

+        return true;

+    }

+

+    public boolean onSingleTapConfirmed(MotionEvent event) {

+        //Nothing to do here the tap has already been detected.

+        return false;

+    }

+

+    public boolean onDoubleTap(MotionEvent event) {

+        TouchEvent touch = getNextFreeTouchEvent();

+        touch.set(Type.DOUBLETAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);

+        touch.setPointerId(0);

+        touch.setTime(event.getEventTime());

+        processEvent(touch);

+        return true;

+    }

+

+    public boolean onDoubleTapEvent(MotionEvent event) {

+        return false;

+    }

+

+    public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {

+        TouchEvent touch = getNextFreeTouchEvent();

+        touch.set(Type.SCALE_START, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f);

+        touch.setPointerId(0);

+        touch.setTime(scaleGestureDetector.getEventTime());

+        touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());

+        touch.setScaleFactor(scaleGestureDetector.getScaleFactor());

+        processEvent(touch);

+        //    System.out.println("scaleBegin");

+

+        return true;

+    }

+

+    public boolean onScale(ScaleGestureDetector scaleGestureDetector) {

+        TouchEvent touch = getNextFreeTouchEvent();

+        touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);

+        touch.setPointerId(0);

+        touch.setTime(scaleGestureDetector.getEventTime());

+        touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());

+        touch.setScaleFactor(scaleGestureDetector.getScaleFactor());

+        processEvent(touch);

+        //   System.out.println("scale");

+

+        return false;

+    }

+

+    public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {

+        TouchEvent touch = getNextFreeTouchEvent();

+        touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);

+        touch.setPointerId(0);

+        touch.setTime(scaleGestureDetector.getEventTime());

+        touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());

+        touch.setScaleFactor(scaleGestureDetector.getScaleFactor());

+        processEvent(touch);

+    }

+

+    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

+        TouchEvent touch = getNextFreeTouchEvent();

+        touch.set(Type.SCROLL, e1.getX(), this.getHeight() - e1.getY(), distanceX, distanceY * (-1));

+        touch.setPointerId(0);

+        touch.setTime(e1.getEventTime());

+        processEvent(touch);

+        //System.out.println("scroll " + e1.getPointerCount());

+        return false;

+    }

+

+    public void onShowPress(MotionEvent event) {

+        TouchEvent touch = getNextFreeTouchEvent();

+        touch.set(Type.SHOWPRESS, event.getX(), this.getHeight() - event.getY(), 0f, 0f);

+        touch.setPointerId(0);

+        touch.setTime(event.getEventTime());

+        processEvent(touch);

+    }

+

+    public boolean onSingleTapUp(MotionEvent event) {

+        TouchEvent touch = getNextFreeTouchEvent();

+        touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);

+        touch.setPointerId(0);

+        touch.setTime(event.getEventTime());

+        processEvent(touch);

+        return true;

+    }

+

+    @Override

+    public void setSimulateMouse(boolean simulate) {

+        mouseEventsEnabled = simulate;

+    }

+    @Override

+    public boolean getSimulateMouse() {

+        return mouseEventsEnabled;

+    }

+

+    @Override

+    public void setSimulateKeyboard(boolean simulate) {

+        keyboardEventsEnabled = simulate;

+    }

+

+    @Override

+    public void setOmitHistoricEvents(boolean dontSendHistory) {

+        this.dontSendHistory = dontSendHistory;

+    }

+

+    // TODO: move to TouchInput

+    public boolean isMouseEventsEnabled() {

+        return mouseEventsEnabled;

+    }

+

+    public void setMouseEventsEnabled(boolean mouseEventsEnabled) {

+        this.mouseEventsEnabled = mouseEventsEnabled;

+    }

+

+    public boolean isMouseEventsInvertY() {

+        return mouseEventsInvertY;

+    }

+

+    public void setMouseEventsInvertY(boolean mouseEventsInvertY) {

+        this.mouseEventsInvertY = mouseEventsInvertY;

+    }

+

+    public boolean isMouseEventsInvertX() {

+        return mouseEventsInvertX;

+    }

+

+    public void setMouseEventsInvertX(boolean mouseEventsInvertX) {

+        this.mouseEventsInvertX = mouseEventsInvertX;

+    }

+}

diff --git a/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java b/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java
index 34e4592..c8b2228 100644
--- a/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java
+++ b/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java
@@ -6,14 +6,17 @@
 import com.jme3.input.event.TouchEvent;
 
 /**
- * AndroidTouchInputListener is an inputlistener interface which defines callbacks/events for android touch screens
- * For use with class AndroidInput
+ * 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 interface AndroidTouchInputListener extends RawInputListener {
+
     public void onTouchEvent(TouchEvent evt);
+
     public void onMotionEvent(MotionEvent evt);
+
     public void onAndroidKeyEvent(KeyEvent evt);
 }
diff --git a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java
index b5a8c14..7e7af29 100644
--- a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java
+++ b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java
@@ -31,9 +31,7 @@
  */
 package com.jme3.renderer.android;
 
-import android.graphics.Bitmap;
 import android.opengl.GLES10;
-import android.opengl.GLES11;
 import android.opengl.GLES20;
 import android.os.Build;
 import com.jme3.asset.AndroidImageInfo;
@@ -60,14 +58,11 @@
 import com.jme3.util.BufferUtils;
 import com.jme3.util.ListMap;
 import com.jme3.util.NativeObjectManager;
-import com.jme3.util.SafeArrayList;
 import java.nio.*;
-import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import javax.microedition.khronos.opengles.GL10;
 
 public class OGLESShaderRenderer implements Renderer {
 
@@ -360,25 +355,10 @@
         }
 
         applyRenderState(RenderState.DEFAULT);
-//        GLES20.glClearDepthf(1.0f);
-
-        if (verboseLogging) {
-            logger.info("GLES20.glDisable(GL10.GL_DITHER)");
-        }
-
-        GLES20.glDisable(GL10.GL_DITHER);
+        GLES20.glDisable(GLES20.GL_DITHER);
 
         checkGLError();
 
-        if (verboseLogging) {
-            logger.info("GLES20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST)");
-        }
-         
-        //It seems that GL10.GL_PERSPECTIVE_CORRECTION_HINT gives invalid_enum error on android.        
-//        GLES20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
-
-//	checkGLError();
-
         useVBO = false;
         
         // NOTE: SDK_INT is only available since 1.6, 
@@ -1779,10 +1759,8 @@
 
     private int convertWrapMode(Texture.WrapMode mode) {
         switch (mode) {
-//            case BorderClamp:
-//                return GLES20.GL_CLAMP_TO_BORDER;
-//            case Clamp:
-//                return GLES20.GL_CLAMP;
+            case BorderClamp:
+            case Clamp:
             case EdgeClamp:
                 return GLES20.GL_CLAMP_TO_EDGE;
             case Repeat:
@@ -2557,7 +2535,7 @@
                     logger.log(Level.INFO, "glDrawElements(), indexBuf.capacity ({0}), vertCount ({1})", new Object[]{indexBuf.getData().capacity(), vertCount});
                 }
 
-                GLES11.glDrawElements(
+                GLES20.glDrawElements(
                         convertElementMode(mesh.getMode()),
                         indexBuf.getData().capacity(),
                         convertFormat(indexBuf.getFormat()),
diff --git a/engine/src/android/com/jme3/renderer/android/TextureUtil.java b/engine/src/android/com/jme3/renderer/android/TextureUtil.java
index 53b96b4..4a60d6b 100644
--- a/engine/src/android/com/jme3/renderer/android/TextureUtil.java
+++ b/engine/src/android/com/jme3/renderer/android/TextureUtil.java
@@ -70,7 +70,7 @@
             width /= 2;
             Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
 
-            bitmap.recycle();
+            //bitmap.recycle();
             bitmap = bitmap2;
         }
     }
@@ -94,7 +94,7 @@
                 width = FastMath.nearestPowerOfTwo(width);
                 height = FastMath.nearestPowerOfTwo(height);
                 Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
-                bitmap.recycle();
+                //bitmap.recycle();
                 bitmap = bitmap2;
             }
         }
diff --git a/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java b/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java
index e55fa55..56ed415 100644
--- a/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java
+++ b/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java
@@ -2,6 +2,7 @@
 
 import android.graphics.PixelFormat;
 import android.opengl.GLSurfaceView.EGLConfigChooser;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.microedition.khronos.egl.EGL10;
 import javax.microedition.khronos.egl.EGLConfig;
@@ -9,26 +10,23 @@
 
 /**
  * AndroidConfigChooser is used to determine the best suited EGL Config
- * @author larynx
  *
+ * @author larynx
  */
-public class AndroidConfigChooser implements EGLConfigChooser 
-{
+public class AndroidConfigChooser implements EGLConfigChooser {
+
     private static final Logger logger = Logger.getLogger(AndroidConfigChooser.class.getName());
-    
     protected int clientOpenGLESVersion = 0;
     protected EGLConfig bestConfig = null;
     protected EGLConfig fastestConfig = null;
     protected EGLConfig choosenConfig = null;
     protected ConfigType type;
     protected int pixelFormat;
-    
     protected boolean verbose = false;
-    
     private final static int EGL_OPENGL_ES2_BIT = 4;
 
-    public enum ConfigType 
-    {
+    public enum ConfigType {
+
         /**
          * RGB565, 0 alpha, 16 depth, 0 stencil
          */
@@ -39,94 +37,79 @@
         BEST,
         /**
          * Turn off config chooser and use hardcoded
-         * setEGLContextClientVersion(2);
-         * setEGLConfigChooser(5, 6, 5, 0, 16, 0);
+         * setEGLContextClientVersion(2); setEGLConfigChooser(5, 6, 5, 0, 16,
+         * 0);
          */
         LEGACY
     }
-    
-    public AndroidConfigChooser(ConfigType type, boolean verbose)
-    {
+
+    public AndroidConfigChooser(ConfigType type) {
         this.type = type;
-        this.verbose = verbose;
     }
-        
+
     /**
      * Gets called by the GLSurfaceView class to return the best config
-     */    
+     */
     @Override
-    public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display)
-    {
+    public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
         logger.info("GLSurfaceView asks for egl config, returning: ");
         logEGLConfig(choosenConfig, display, egl);
         return choosenConfig;
     }
-    
+
     /**
      * findConfig is used to locate the best config and init the chooser with
+     *
      * @param egl
      * @param display
      * @return true if successfull, false if no config was found
      */
-    public boolean findConfig(EGL10 egl, EGLDisplay display)
-    {           
-        
-        if (type == ConfigType.BEST)
-        {        	
-        	ComponentSizeChooser compChooser = new ComponentSizeChooser(8, 8, 8, 8, 32, 0);
-        	choosenConfig = compChooser.chooseConfig(egl, display);
+    public boolean findConfig(EGL10 egl, EGLDisplay display) {
 
-        	if (choosenConfig == null)
-        	{
+        if (type == ConfigType.BEST) {
+            ComponentSizeChooser compChooser = new ComponentSizeChooser(8, 8, 8, 8, 32, 0);
+            choosenConfig = compChooser.chooseConfig(egl, display);
+
+            if (choosenConfig == null) {
                 compChooser = new ComponentSizeChooser(8, 8, 8, 0, 32, 0);
                 choosenConfig = compChooser.chooseConfig(egl, display);
-                if (choosenConfig == null)
-                {
+                if (choosenConfig == null) {
                     compChooser = new ComponentSizeChooser(8, 8, 8, 8, 16, 0);
                     choosenConfig = compChooser.chooseConfig(egl, display);
-                    if (choosenConfig == null)
-                    {
+                    if (choosenConfig == null) {
                         compChooser = new ComponentSizeChooser(8, 8, 8, 0, 16, 0);
                         choosenConfig = compChooser.chooseConfig(egl, display);
                     }
                 }
-        	}
-        	
+            }
+
             logger.info("JME3 using best EGL configuration available here: ");
-        }
-        else
-        {
-        	ComponentSizeChooser compChooser = new ComponentSizeChooser(5, 6, 5, 0, 16, 0);
-        	choosenConfig = compChooser.chooseConfig(egl, display);
+        } else {
+            ComponentSizeChooser compChooser = new ComponentSizeChooser(5, 6, 5, 0, 16, 0);
+            choosenConfig = compChooser.chooseConfig(egl, display);
             logger.info("JME3 using fastest EGL configuration available here: ");
         }
-        
-        if (choosenConfig != null)
-        {
-            logger.info("JME3 using choosen config: "); 
-            logEGLConfig(choosenConfig, display, egl);               
+
+        if (choosenConfig != null) {
+            logger.info("JME3 using choosen config: ");
+            logEGLConfig(choosenConfig, display, egl);
             pixelFormat = getPixelFormat(choosenConfig, display, egl);
             clientOpenGLESVersion = getOpenGLVersion(choosenConfig, display, egl);
             return true;
-        }
-        else
-        {
+        } else {
             logger.severe("###ERROR### Unable to get a valid OpenGL ES 2.0 config, nether Fastest nor Best found! Bug. Please report this.");
             clientOpenGLESVersion = 1;
             pixelFormat = PixelFormat.UNKNOWN;
             return false;
-        }        
+        }
     }
-    
-    
-    private int getPixelFormat(EGLConfig conf, EGLDisplay display, EGL10 egl)
-    {
+
+    private int getPixelFormat(EGLConfig conf, EGLDisplay display, EGL10 egl) {
         int[] value = new int[1];
-        int result = PixelFormat.RGB_565;                
+        int result = PixelFormat.RGB_565;
 
         egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RED_SIZE, value);
-        if (value[0] == 8)
-        {
+        if (value[0] == 8) {
             result = PixelFormat.RGBA_8888;
             /*
             egl.eglGetConfigAttrib(display, conf, EGL10.EGL_ALPHA_SIZE, value);
@@ -139,165 +122,163 @@
                 result = PixelFormat.RGB_888;
             }*/
         }
-        
-        if (verbose)
-        {
-            logger.info("Using PixelFormat " + result);                            
+
+        if (verbose) {
+            logger.log(Level.INFO, "Using PixelFormat {0}", result);
         }
-    
+
         //return result; TODO Test pixelformat
         return PixelFormat.TRANSPARENT;
     }
-    
-    private int getOpenGLVersion(EGLConfig conf, EGLDisplay display, EGL10 egl)
-    {
+
+    private int getOpenGLVersion(EGLConfig conf, EGLDisplay display, EGL10 egl) {
         int[] value = new int[1];
         int result = 1;
-                        
+
         egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RENDERABLE_TYPE, value);
         // Check if conf is OpenGL ES 2.0
-        if ((value[0] & EGL_OPENGL_ES2_BIT) != 0)
-        {
+        if ((value[0] & EGL_OPENGL_ES2_BIT) != 0) {
             result = 2;
         }
 
-        return result;                    
+        return result;
     }
-    
+
     /**
      * log output with egl config details
+     *
      * @param conf
      * @param display
      * @param egl
      */
-    public void logEGLConfig(EGLConfig conf, EGLDisplay display, EGL10 egl)
-    {
+    public void logEGLConfig(EGLConfig conf, EGLDisplay display, EGL10 egl) {
         int[] value = new int[1];
 
         egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RED_SIZE, value);
-        logger.info(String.format("EGL_RED_SIZE  = %d", value[0] ) );
-        
+        logger.info(String.format("EGL_RED_SIZE  = %d", value[0]));
+
         egl.eglGetConfigAttrib(display, conf, EGL10.EGL_GREEN_SIZE, value);
-        logger.info(String.format("EGL_GREEN_SIZE  = %d", value[0] ) );
-        
+        logger.info(String.format("EGL_GREEN_SIZE  = %d", value[0]));
+
         egl.eglGetConfigAttrib(display, conf, EGL10.EGL_BLUE_SIZE, value);
-        logger.info(String.format("EGL_BLUE_SIZE  = %d", value[0] ) );
-        
+        logger.info(String.format("EGL_BLUE_SIZE  = %d", value[0]));
+
         egl.eglGetConfigAttrib(display, conf, EGL10.EGL_ALPHA_SIZE, value);
-        logger.info(String.format("EGL_ALPHA_SIZE  = %d", value[0] ) );
-        
+        logger.info(String.format("EGL_ALPHA_SIZE  = %d", value[0]));
+
         egl.eglGetConfigAttrib(display, conf, EGL10.EGL_DEPTH_SIZE, value);
-        logger.info(String.format("EGL_DEPTH_SIZE  = %d", value[0] ) );
-                
+        logger.info(String.format("EGL_DEPTH_SIZE  = %d", value[0]));
+
         egl.eglGetConfigAttrib(display, conf, EGL10.EGL_STENCIL_SIZE, value);
-        logger.info(String.format("EGL_STENCIL_SIZE  = %d", value[0] ) );
+        logger.info(String.format("EGL_STENCIL_SIZE  = %d", value[0]));
 
         egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RENDERABLE_TYPE, value);
-        logger.info(String.format("EGL_RENDERABLE_TYPE  = %d", value[0] ) );
-        
+        logger.info(String.format("EGL_RENDERABLE_TYPE  = %d", value[0]));
+
         egl.eglGetConfigAttrib(display, conf, EGL10.EGL_SURFACE_TYPE, value);
-        logger.info(String.format("EGL_SURFACE_TYPE  = %d", value[0] ) );               
+        logger.info(String.format("EGL_SURFACE_TYPE  = %d", value[0]));
     }
-        
-    public int getClientOpenGLESVersion() 
-    {
+
+    public int getClientOpenGLESVersion() {
         return clientOpenGLESVersion;
     }
 
-    public void setClientOpenGLESVersion(int clientOpenGLESVersion) 
-    {
+    public void setClientOpenGLESVersion(int clientOpenGLESVersion) {
         this.clientOpenGLESVersion = clientOpenGLESVersion;
     }
-    
-    public int getPixelFormat() 
-    {
+
+    public int getPixelFormat() {
         return pixelFormat;
     }
-    
-    
-    
-    private abstract class BaseConfigChooser implements EGLConfigChooser 
-    {
+
+    private abstract class BaseConfigChooser implements EGLConfigChooser {
+
         private boolean bClientOpenGLESVersionSet;
-        
-		public BaseConfigChooser(int[] configSpec) 
-		{
-		    bClientOpenGLESVersionSet = false;
-		    mConfigSpec = filterConfigSpec(configSpec);
-		}
-		
-		public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) 
-		{
-		    int[] num_config = new int[1];
-		    if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
-		            num_config)) {
-		        throw new IllegalArgumentException("eglChooseConfig failed");
-		    }
-		
-		    int numConfigs = num_config[0];
-		
-		    if (numConfigs <= 0) 
-		    {
-		        //throw new IllegalArgumentException("No configs match configSpec");
-		        
-		        return null;
-		    }
-		
-		    EGLConfig[] configs = new EGLConfig[numConfigs];
-		    if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
-		            num_config)) {
-		        throw new IllegalArgumentException("eglChooseConfig#2 failed");
-		    }
-		    EGLConfig config = chooseConfig(egl, display, configs);
-		    //if (config == null) {
-		    //    throw new IllegalArgumentException("No config chosen");
-		    //}
-		    return config;
-		}
-		
-		abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
-		        EGLConfig[] configs);
-		
-		protected int[] mConfigSpec;
-		
-		private int[] filterConfigSpec(int[] configSpec) 
-		{
-		    if (bClientOpenGLESVersionSet == true) {
-		        return configSpec;
-		    }
-		    /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
-		     * And we know the configSpec is well formed.
-		     */
-		    int len = configSpec.length;
-		    int[] newConfigSpec = new int[len + 2];
-		    System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
-		    newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
-		    newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
-		    newConfigSpec[len+1] = EGL10.EGL_NONE;
-		    
-		    bClientOpenGLESVersionSet = true;
-		    
-		    return newConfigSpec;
-		}
+
+        public BaseConfigChooser(int[] configSpec) {
+            bClientOpenGLESVersionSet = false;
+            mConfigSpec = filterConfigSpec(configSpec);
+        }
+
+        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+            int[] num_config = new int[1];
+            if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
+                    num_config)) {
+                throw new IllegalArgumentException("eglChooseConfig failed");
+            }
+
+            int numConfigs = num_config[0];
+
+            if (numConfigs <= 0) {
+                //throw new IllegalArgumentException("No configs match configSpec");
+
+                return null;
+            }
+
+            EGLConfig[] configs = new EGLConfig[numConfigs];
+            if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
+                    num_config)) {
+                throw new IllegalArgumentException("eglChooseConfig#2 failed");
+            }
+            EGLConfig config = chooseConfig(egl, display, configs);
+            //if (config == null) {
+            //    throw new IllegalArgumentException("No config chosen");
+            //}
+            return config;
+        }
+
+        abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+                EGLConfig[] configs);
+        protected int[] mConfigSpec;
+
+        private int[] filterConfigSpec(int[] configSpec) {
+            if (bClientOpenGLESVersionSet == true) {
+                return configSpec;
+            }
+            /*
+             * We know none of the subclasses define EGL_RENDERABLE_TYPE. And we
+             * know the configSpec is well formed.
+             */
+            int len = configSpec.length;
+            int[] newConfigSpec = new int[len + 2];
+            System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);
+            newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;
+            newConfigSpec[len] = 4; /*
+             * EGL_OPENGL_ES2_BIT
+             */
+            newConfigSpec[len + 1] = EGL10.EGL_NONE;
+
+            bClientOpenGLESVersionSet = true;
+
+            return newConfigSpec;
+        }
     }
-    
+
     /**
-     * Choose a configuration with exactly the specified r,g,b,a sizes,
-     * and at least the specified depth and stencil sizes.
+     * Choose a configuration with exactly the specified r,g,b,a sizes, and at
+     * least the specified depth and stencil sizes.
      */
-    private class ComponentSizeChooser extends BaseConfigChooser 
-    {
+    private class ComponentSizeChooser extends BaseConfigChooser {
+
+        private int[] mValue;
+        // Subclasses can adjust these values:
+        protected int mRedSize;
+        protected int mGreenSize;
+        protected int mBlueSize;
+        protected int mAlphaSize;
+        protected int mDepthSize;
+        protected int mStencilSize;
+        
         public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
-                int alphaSize, int depthSize, int stencilSize) 
-        {
-            super(new int[] {
-                    EGL10.EGL_RED_SIZE, redSize,
-                    EGL10.EGL_GREEN_SIZE, greenSize,
-                    EGL10.EGL_BLUE_SIZE, blueSize,
-                    EGL10.EGL_ALPHA_SIZE, alphaSize,
-                    EGL10.EGL_DEPTH_SIZE, depthSize,
-                    EGL10.EGL_STENCIL_SIZE, stencilSize,
-                    EGL10.EGL_NONE});
+                int alphaSize, int depthSize, int stencilSize) {
+            super(new int[]{
+                        EGL10.EGL_RED_SIZE, redSize,
+                        EGL10.EGL_GREEN_SIZE, greenSize,
+                        EGL10.EGL_BLUE_SIZE, blueSize,
+                        EGL10.EGL_ALPHA_SIZE, alphaSize,
+                        EGL10.EGL_DEPTH_SIZE, depthSize,
+                        EGL10.EGL_STENCIL_SIZE, stencilSize,
+                        EGL10.EGL_NONE});
             mValue = new int[1];
             mRedSize = redSize;
             mGreenSize = greenSize;
@@ -305,25 +286,22 @@
             mAlphaSize = alphaSize;
             mDepthSize = depthSize;
             mStencilSize = stencilSize;
-       }
+        }
 
         @Override
-        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) 
-        {
-            for (EGLConfig config : configs) 
-            {
+        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
+            for (EGLConfig config : configs) {
                 int d = findConfigAttrib(egl, display, config,
                         EGL10.EGL_DEPTH_SIZE, 0);
                 int s = findConfigAttrib(egl, display, config,
                         EGL10.EGL_STENCIL_SIZE, 0);
-                if ((d >= mDepthSize) && (s >= mStencilSize)) 
-                {
+                if ((d >= mDepthSize) && (s >= mStencilSize)) {
                     int r = findConfigAttrib(egl, display, config,
                             EGL10.EGL_RED_SIZE, 0);
                     int g = findConfigAttrib(egl, display, config,
-                             EGL10.EGL_GREEN_SIZE, 0);
+                            EGL10.EGL_GREEN_SIZE, 0);
                     int b = findConfigAttrib(egl, display, config,
-                              EGL10.EGL_BLUE_SIZE, 0);
+                            EGL10.EGL_BLUE_SIZE, 0);
                     int a = findConfigAttrib(egl, display, config,
                             EGL10.EGL_ALPHA_SIZE, 0);
                     if ((r == mRedSize) && (g == mGreenSize)
@@ -336,27 +314,12 @@
         }
 
         private int findConfigAttrib(EGL10 egl, EGLDisplay display,
-                EGLConfig config, int attribute, int defaultValue) 
-        {
+                EGLConfig config, int attribute, int defaultValue) {
 
-            if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) 
-            {
+            if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
                 return mValue[0];
             }
             return defaultValue;
         }
-
-        private int[] mValue;
-        // Subclasses can adjust these values:
-        protected int mRedSize;
-        protected int mGreenSize;
-        protected int mBlueSize;
-        protected int mAlphaSize;
-        protected int mDepthSize;
-        protected int mStencilSize;
     }
-    
-    
-    
-    
 }
diff --git a/engine/src/android/com/jme3/system/android/OGLESContext.java b/engine/src/android/com/jme3/system/android/OGLESContext.java
index 668b68d..1ecf2e1 100644
--- a/engine/src/android/com/jme3/system/android/OGLESContext.java
+++ b/engine/src/android/com/jme3/system/android/OGLESContext.java
@@ -32,26 +32,37 @@
 package com.jme3.system.android;
 
 import android.app.Activity;
+import android.app.AlertDialog;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.opengl.GLSurfaceView;
+import android.text.InputType;
+import android.view.Gravity;
 import android.view.SurfaceHolder;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.EditText;
+import android.widget.FrameLayout;
 import com.jme3.app.AndroidHarness;
 import com.jme3.app.Application;
 import com.jme3.input.JoyInput;
 import com.jme3.input.KeyInput;
 import com.jme3.input.MouseInput;
+import com.jme3.input.SoftTextDialogInput;
 import com.jme3.input.TouchInput;
 import com.jme3.input.android.AndroidInput;
+import com.jme3.input.controls.SoftTextDialogInputListener;
 import com.jme3.input.controls.TouchTrigger;
 import com.jme3.input.dummy.DummyKeyInput;
 import com.jme3.input.dummy.DummyMouseInput;
 import com.jme3.renderer.android.OGLESShaderRenderer;
 import com.jme3.system.AppSettings;
 import com.jme3.system.JmeContext;
+import com.jme3.system.JmeSystem;
 import com.jme3.system.SystemListener;
 import com.jme3.system.Timer;
 import com.jme3.system.android.AndroidConfigChooser.ConfigType;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.microedition.khronos.egl.EGL10;
 import javax.microedition.khronos.egl.EGLConfig;
@@ -59,7 +70,7 @@
 import javax.microedition.khronos.egl.EGLDisplay;
 import javax.microedition.khronos.opengles.GL10;
 
-public class OGLESContext implements JmeContext, GLSurfaceView.Renderer {
+public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTextDialogInput {
 
     private static final Logger logger = Logger.getLogger(OGLESContext.class.getName());
     protected final AtomicBoolean created = new AtomicBoolean(false);
@@ -76,7 +87,6 @@
     protected boolean autoFlush = true;
     protected AndroidInput view;
     private boolean firstDrawFrame = true;
-
     //protected int minFrameDuration = 1000 / frameRate;  // Set a max FPS of 33
     protected int minFrameDuration = 0;                   // No FPS cap
     /**
@@ -145,12 +155,12 @@
 
             int[] version = new int[2];
             if (egl.eglInitialize(display, version) == true) {
-                logger.info("Display EGL Version: " + version[0] + "." + version[1]);
+                logger.log(Level.INFO, "Display EGL Version: {0}.{1}", new Object[]{version[0], version[1]});
             }
 
             try {
                 // Create a config chooser
-                AndroidConfigChooser configChooser = new AndroidConfigChooser(configType, eglConfigVerboseLogging);
+                AndroidConfigChooser configChooser = new AndroidConfigChooser(configType);
                 // Init chooser
                 if (!configChooser.findConfig(egl, display)) {
                     listener.handleError("Unable to find suitable EGL config", null);
@@ -162,7 +172,7 @@
                     listener.handleError("OpenGL ES 2.0 is not supported on this device", null);
                     return null;
                 }
-                
+
                 // Requesting client version from GLSurfaceView which is extended by
                 // AndroidInput.
                 view.setEGLContextClientVersion(clientOpenGLESVersion);
@@ -210,12 +220,14 @@
         // Setup unhandled Exception Handler
         if (ctx instanceof AndroidHarness) {
             Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+
                 public void uncaughtException(Thread thread, Throwable thrown) {
                     ((AndroidHarness) ctx).handleError("Exception thrown in " + thread.toString(), thrown);
                 }
             });
         } else {
             Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+
                 public void uncaughtException(Thread thread, Throwable thrown) {
                     listener.handleError("Exception thrown in " + thread.toString(), thrown);
                 }
@@ -244,6 +256,8 @@
             }
         }
 
+        JmeSystem.setSoftTextDialogInput(this);
+
         needClose.set(false);
         renderable.set(true);
     }
@@ -268,6 +282,13 @@
             logger.info("Display destroyed.");
 
             renderable.set(false);
+            final Context ctx = this.view.getContext();
+            if (ctx instanceof AndroidHarness) {
+                AndroidHarness harness = (AndroidHarness) ctx;
+                if (harness.isFinishOnAppStop()) {
+                    harness.finish();
+                }
+            }
         }
     }
 
@@ -442,4 +463,67 @@
     public int getClientOpenGLESVersion() {
         return clientOpenGLESVersion;
     }
+
+    public void requestDialog(final int id, final String title, final String initialValue, final SoftTextDialogInputListener listener) {
+        logger.log(Level.INFO, "requestDialog: title: {0}, initialValue: {1}",
+                new Object[]{title, initialValue});
+
+        JmeAndroidSystem.getActivity().runOnUiThread(new Runnable() {
+
+            @Override
+            public void run() {
+
+                final FrameLayout layoutTextDialogInput = new FrameLayout(JmeAndroidSystem.getActivity());
+                final EditText editTextDialogInput = new EditText(JmeAndroidSystem.getActivity());
+                editTextDialogInput.setWidth(LayoutParams.FILL_PARENT);
+                editTextDialogInput.setHeight(LayoutParams.FILL_PARENT);
+                editTextDialogInput.setPadding(20, 20, 20, 20);
+                editTextDialogInput.setGravity(Gravity.FILL_HORIZONTAL);
+
+                editTextDialogInput.setText(initialValue);
+
+                switch (id) {
+                    case SoftTextDialogInput.TEXT_ENTRY_DIALOG:
+
+                        editTextDialogInput.setInputType(InputType.TYPE_CLASS_TEXT);
+                        break;
+
+                    case SoftTextDialogInput.NUMERIC_ENTRY_DIALOG:
+
+                        editTextDialogInput.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL | InputType.TYPE_NUMBER_FLAG_SIGNED);
+                        break;
+
+                    case SoftTextDialogInput.NUMERIC_KEYPAD_DIALOG:
+
+                        editTextDialogInput.setInputType(InputType.TYPE_CLASS_PHONE);
+                        break;
+
+                    default:
+                        break;
+                }
+
+                layoutTextDialogInput.addView(editTextDialogInput);
+
+                AlertDialog dialogTextInput = new AlertDialog.Builder(JmeAndroidSystem.getActivity()).setTitle(title).setView(layoutTextDialogInput).setPositiveButton("OK",
+                        new DialogInterface.OnClickListener() {
+
+                            public void onClick(DialogInterface dialog, int whichButton) {
+                                /* User clicked OK, send COMPLETE action
+                                 * and text */
+                                listener.onSoftText(SoftTextDialogInputListener.COMPLETE, editTextDialogInput.getText().toString());
+                            }
+                        }).setNegativeButton("Cancel",
+                        new DialogInterface.OnClickListener() {
+
+                            public void onClick(DialogInterface dialog, int whichButton) {
+                                /* User clicked CANCEL, send CANCEL action
+                                 * and text */
+                                listener.onSoftText(SoftTextDialogInputListener.CANCEL, editTextDialogInput.getText().toString());
+                            }
+                        }).create();
+
+                dialogTextInput.show();
+            }
+        });
+    }
 }
diff --git a/engine/src/android/com/jme3/util/AndroidLogHandler.java b/engine/src/android/com/jme3/util/AndroidLogHandler.java
index 8fb21c2..1da1299 100644
--- a/engine/src/android/com/jme3/util/AndroidLogHandler.java
+++ b/engine/src/android/com/jme3/util/AndroidLogHandler.java
@@ -4,7 +4,12 @@
 import java.util.logging.Handler;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
+import java.util.logging.Logger;
 
+/**
+ * Converts from Java based logging ({@link Logger} to Android based
+ * logging {@link Log}.
+ */
 public class AndroidLogHandler extends Handler {
 
     @Override
@@ -21,17 +26,16 @@
         String clsName = record.getSourceClassName();
         String msg = record.getMessage();
         Throwable t = record.getThrown();
-        if (level == Level.INFO){
+        if (level == Level.INFO) {
             Log.i(clsName, msg, t);
-        }else if (level == Level.SEVERE){
+        } else if (level == Level.SEVERE) {
             Log.e(clsName, msg, t);
-        }else if (level == Level.WARNING){
+        } else if (level == Level.WARNING) {
             Log.w(clsName, msg, t);
-        }else if (level == Level.CONFIG){
+        } else if (level == Level.CONFIG) {
             Log.d(clsName, msg, t);
-        }else if (level == Level.FINE || level == Level.FINER || level == Level.FINEST){
+        } else if (level == Level.FINE || level == Level.FINER || level == Level.FINEST) {
             Log.v(clsName, msg, t);
         }
     }
-
 }
diff --git a/engine/src/android/com/jme3/util/RingBuffer.java b/engine/src/android/com/jme3/util/RingBuffer.java
index 786417b..1d3c22d 100644
--- a/engine/src/android/com/jme3/util/RingBuffer.java
+++ b/engine/src/android/com/jme3/util/RingBuffer.java
@@ -4,20 +4,21 @@
 import java.util.NoSuchElementException;
 
 /**
- * Ring buffer (fixed size queue) implementation using a circular array (array with wrap-around).
+ * Ring buffer (fixed size queue) implementation using a circular array (array
+ * with wrap-around).
  */
 // suppress unchecked warnings in Java 1.5.0_6 and later
 @SuppressWarnings("unchecked")
-public class RingBuffer<Item> implements Iterable<Item> {
+public class RingBuffer<T> implements Iterable<T> {
 
-    private Item[] buffer;          // queue elements
+    private T[] buffer;          // queue elements
     private int count = 0;          // number of elements on queue
     private int indexOut = 0;       // index of first element of queue
     private int indexIn = 0;       // index of next available slot
 
     // cast needed since no generic array creation in Java
     public RingBuffer(int capacity) {
-        buffer = (Item[]) new Object[capacity];
+        buffer = (T[]) new Object[capacity];
     }
 
     public boolean isEmpty() {
@@ -28,7 +29,7 @@
         return count;
     }
 
-    public void push(Item item) {
+    public void push(T item) {
         if (count == buffer.length) {
             throw new RuntimeException("Ring buffer overflow");
         }
@@ -37,23 +38,23 @@
         count++;
     }
 
-    public Item pop() {
+    public T pop() {
         if (isEmpty()) {
             throw new RuntimeException("Ring buffer underflow");
         }
-        Item item = buffer[indexOut];
+        T item = buffer[indexOut];
         buffer[indexOut] = null;                  // to help with garbage collection
         count--;
         indexOut = (indexOut + 1) % buffer.length; // wrap-around
         return item;
     }
 
-    public Iterator<Item> iterator() {
+    public Iterator<T> iterator() {
         return new RingBufferIterator();
     }
 
     // an iterator, doesn't implement remove() since it's optional
-    private class RingBufferIterator implements Iterator<Item> {
+    private class RingBufferIterator implements Iterator<T> {
 
         private int i = 0;
 
@@ -65,7 +66,7 @@
             throw new UnsupportedOperationException();
         }
 
-        public Item next() {
+        public T next() {
             if (!hasNext()) {
                 throw new NoSuchElementException();
             }
diff --git a/engine/src/android/res/layout/about.xml b/engine/src/android/res/layout/about.xml
index 1d8c2b0..ca249e7 100644
--- a/engine/src/android/res/layout/about.xml
+++ b/engine/src/android/res/layout/about.xml
@@ -1,29 +1,29 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <LinearLayout
-	xmlns:android="http://schemas.android.com/apk/res/android"
-	android:orientation="vertical"
-	android:layout_width="fill_parent"
-	android:layout_height="fill_parent"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
 >
 
     <LinearLayout
-		android:id="@+id/buttonsContainer"
-		xmlns:android="http://schemas.android.com/apk/res/android"
-		android:orientation="vertical"
-		android:layout_width="fill_parent"
-		android:layout_height="fill_parent"
-	>
+        android:id="@+id/buttonsContainer"
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+    >
         <TextView  
-			android:layout_width="fill_parent" 
-			android:layout_height="wrap_content" 
-			android:text="copyright (c) 2009-2010 JMonkeyEngine"
+            android:layout_width="fill_parent" 
+            android:layout_height="wrap_content" 
+            android:text="copyright (c) 2009-2010 JMonkeyEngine"
 		/>
 
         <TextView  
-			android:layout_width="fill_parent" 
-			android:layout_height="wrap_content" 
-			android:text="http://www.jmonkeyengine.org"
+            android:layout_width="fill_parent" 
+            android:layout_height="wrap_content" 
+            android:text="http://www.jmonkeyengine.org"
 		/>
 
     </LinearLayout>
diff --git a/engine/src/android/res/layout/tests.xml b/engine/src/android/res/layout/tests.xml
index 70bd5ec..f38e233 100644
--- a/engine/src/android/res/layout/tests.xml
+++ b/engine/src/android/res/layout/tests.xml
@@ -1,17 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <LinearLayout
-	xmlns:android="http://schemas.android.com/apk/res/android"
-	android:orientation="vertical"
-	android:layout_width="fill_parent"
-	android:layout_height="fill_parent"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
 >
     <LinearLayout
-		android:id="@+id/buttonsContainer"
-		xmlns:android="http://schemas.android.com/apk/res/android"
-		android:orientation="vertical"
-		android:layout_width="fill_parent"
-		android:layout_height="fill_parent">
+        android:id="@+id/buttonsContainer"
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
 <!--
 		<Button
 			android:id="@+id/SimpleTextured"
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
index 484e942..7f9e8ac 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
@@ -31,6 +31,14 @@
  */

 package com.jme3.scene.plugins.blender.meshes;

 

+import java.nio.ByteBuffer;

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.LinkedList;

+import java.util.List;

+import java.util.Map;

+import java.util.Map.Entry;

+

 import com.jme3.asset.BlenderKey.FeaturesToLoad;

 import com.jme3.material.Material;

 import com.jme3.math.FastMath;

@@ -57,10 +65,6 @@
 import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator;

 import com.jme3.texture.Texture;

 import com.jme3.util.BufferUtils;

-import java.nio.ByteBuffer;

-import java.nio.FloatBuffer;

-import java.util.*;

-import java.util.Map.Entry;

 

 /**

  * A class that is used in mesh calculations.

@@ -300,12 +304,13 @@
         // generating meshes

         //FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors);

         ByteBuffer verticesColorsBuffer = createByteBuffer(verticesColors);

+        verticesAmount = vertexList.size();

         for (Entry<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {

             Mesh mesh = new Mesh();

 

             // creating vertices indices for this mesh

             List<Integer> indexList = meshEntry.getValue();

-            if(verticesAmount < Short.MAX_VALUE * 2) {

+            if(verticesAmount <= Short.MAX_VALUE) {

             	short[] indices = new short[indexList.size()];

                 for (int i = 0; i < indexList.size(); ++i) {

                     indices[i] = indexList.get(i).shortValue();

diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
index 75fc0c5..918072e 100644
--- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
@@ -56,8 +56,8 @@
 		float[] resultPixel = new float[4];

 		int dataIndex = 0;

 		while (data.hasRemaining()) {

-			float tin = this.setupMaterialColor(data, format, neg, pixelColor);

-			this.blendPixel(resultPixel, materialColor, color, tin, blendType, blenderContext);

+			this.setupMaterialColor(data, format, neg, pixelColor);

+			this.blendPixel(resultPixel, materialColor, pixelColor, affectFactor, blendType, blenderContext);

 			newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));

 			newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));

 			newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));

diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag
index 76f0e15..eb1c3fd 100644
--- a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag
+++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag
@@ -126,6 +126,10 @@
     float att = vLightDir.w;

    #endif

 

+   if (m_Shininess <= 1.0) {

+       specularFactor = 0.0; // should be one instruction on most cards ..

+   }

+

    specularFactor *= diffuseFactor;

 

    return vec2(diffuseFactor, specularFactor) * vec2(att);

diff --git a/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md b/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md
index e28d41a..73674f9 100644
--- a/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md
+++ b/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md
@@ -4,6 +4,10 @@
         Texture2D Texture

         Float Quadratic

         Boolean PointSprite

+        

+        //only used for soft particles

+        Texture2D DepthTexture

+        Float Softness

 

         // Texture of the glowing parts of the material

         Texture2D GlowMap

@@ -58,6 +62,28 @@
         }

     }

 

+    Technique SoftParticles{

+

+        VertexShader   GLSL100 : Common/MatDefs/Misc/SoftParticle.vert

+        FragmentShader GLSL100 : Common/MatDefs/Misc/SoftParticle.frag

+

+        WorldParameters {

+            WorldViewProjectionMatrix

+            WorldViewMatrix

+            WorldMatrix

+            CameraPosition

+        }

+

+        RenderState {

+            Blend AlphaAdditive

+            DepthWrite Off

+        }

+

+        Defines {

+            USE_TEXTURE : Texture

+        }

+    }

+

     Technique FixedFunc {

         RenderState {

             Blend AlphaAdditive

diff --git a/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.frag b/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.frag
new file mode 100644
index 0000000..d3108b5
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.frag
@@ -0,0 +1,50 @@
+uniform sampler2D m_DepthTexture;

+uniform float m_Softness; // Power used in the contrast function

+varying vec2 vPos; // Position of the pixel

+varying vec2 projPos;// z and w valus in projection space

+

+#ifdef USE_TEXTURE

+uniform sampler2D m_Texture;

+varying vec4 texCoord;

+#endif

+

+varying vec4 color;

+

+float Contrast(float d){

+    float val = clamp( 2.0*( (d > 0.5) ? 1.0-d : d ), 0.0, 1.0);

+    float a = 0.5 * pow(val, m_Softness);

+    return (d > 0.5) ? 1.0 - a : a;

+}

+

+float stdDiff(float d){   

+    return clamp((d)*m_Softness,0.0,1.0);

+}

+

+

+void main(){

+    if (color.a <= 0.01)

+        discard;

+

+    vec4 c = vec4(1.0,1.0,1.0,1.0);//color;

+    #ifdef USE_TEXTURE

+        #ifdef POINT_SPRITE

+            vec2 uv = mix(texCoord.xy, texCoord.zw, gl_PointCoord.xy);

+        #else

+            vec2 uv = texCoord.xy;

+        #endif

+        c = texture2D(m_Texture, uv) * color;

+    #endif

+

+

+    float depthv = texture2D(m_DepthTexture, vPos).x*2.0-1.0; // Scene depth

+    depthv*=projPos.y;   

+    float particleDepth = projPos.x;

+	

+    float zdiff =depthv-particleDepth;

+    if(zdiff<=0.0){

+        discard;

+    }

+    // Computes alpha based on the particles distance to the rest of the scene

+    c.a = c.a * stdDiff(zdiff);// Contrast(zdiff);

+    gl_FragColor =c;

+}
\ No newline at end of file
diff --git a/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.vert b/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.vert
new file mode 100644
index 0000000..4f0dfd5
--- /dev/null
+++ b/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.vert
@@ -0,0 +1,53 @@
+uniform mat4 g_WorldViewProjectionMatrix;

+

+attribute vec3 inPosition;

+attribute vec4 inColor;

+attribute vec4 inTexCoord;

+

+varying vec4 color;

+// z and w values in projection space

+varying vec2 projPos;

+varying vec2 vPos; // Position of the pixel in clip space

+

+

+

+#ifdef USE_TEXTURE

+varying vec4 texCoord;

+#endif

+

+#ifdef POINT_SPRITE

+uniform mat4 g_WorldViewMatrix;

+uniform mat4 g_WorldMatrix;

+uniform vec3 g_CameraPosition;

+uniform float m_Quadratic;

+const float SIZE_MULTIPLIER = 4.0;

+attribute float inSize;

+#endif

+

+void main(){

+    vec4 pos = vec4(inPosition, 1.0);

+

+    gl_Position = g_WorldViewProjectionMatrix * pos;

+    color = inColor;

+

+    projPos = gl_Position.zw;

+   // projPos.x = 0.5 * (projPos.x) + 0.5;

+

+    // Transforms the vPosition data to the range [0,1]

+    vPos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0;

+

+    #ifdef USE_TEXTURE

+        texCoord = inTexCoord;

+    #endif

+

+    #ifdef POINT_SPRITE

+        vec4 worldPos = g_WorldMatrix * pos;

+        float d = distance(g_CameraPosition.xyz, worldPos.xyz);

+        gl_PointSize = max(1.0, (inSize * SIZE_MULTIPLIER * m_Quadratic) / d);

+

+        //vec4 worldViewPos = g_WorldViewMatrix * pos;

+        //gl_PointSize = (inSize * SIZE_MULTIPLIER * m_Quadratic)*100.0 / worldViewPos.z;

+

+        color.a *= min(gl_PointSize, 1.0);

+    #endif

+}
\ No newline at end of file
diff --git a/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java b/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java
index 47be413..c377a58 100644
--- a/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java
+++ b/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java
@@ -5,6 +5,7 @@
 package com.jme3.post.filters;
 
 import com.jme3.asset.AssetManager;
+import com.jme3.effect.ParticleEmitter;
 import com.jme3.material.Material;
 import com.jme3.math.ColorRGBA;
 import com.jme3.post.Filter;
@@ -12,8 +13,14 @@
 import com.jme3.renderer.Renderer;
 import com.jme3.renderer.ViewPort;
 import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.scene.Node;
+
+import com.jme3.scene.Spatial;
 import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Texture;
 import com.jme3.texture.Texture2D;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * A filter to handle translucent objects when rendering a scene with filters that uses depth like WaterFilter and SSAOFilter
@@ -22,11 +29,25 @@
  */
 public final class TranslucentBucketFilter extends Filter {
 
+    private final static Logger logger = Logger.getLogger(TranslucentBucketFilter.class.getName());
     private RenderManager renderManager;
+    private boolean enabledSoftParticles = false;
+    private Texture depthTexture;
+    private ViewPort viewPort;
+
+    public TranslucentBucketFilter() {
+        super("TranslucentBucketFilter");
+    }
+
+    public TranslucentBucketFilter(boolean enabledSoftParticles) {
+        this();
+        this.enabledSoftParticles = enabledSoftParticles;
+    }
 
     @Override
     protected void initFilter(AssetManager manager, RenderManager rm, ViewPort vp, int w, int h) {
         this.renderManager = rm;
+        this.viewPort = vp;
         material = new Material(manager, "Common/MatDefs/Post/Overlay.j3md");
         material.setColor("Color", ColorRGBA.White);
         Texture2D tex = processor.getFilterTexture();
@@ -37,6 +58,26 @@
             material.clearParam("NumSamples");
         }
         renderManager.setHandleTranslucentBucket(false);
+        if (enabledSoftParticles && depthTexture != null) {
+            initSoftParticles(vp, true);
+        }
+    }
+
+    private void initSoftParticles(ViewPort vp, boolean enabledSP) {
+        if (depthTexture != null) {
+            for (Spatial scene : vp.getScenes()) {
+                makeSoftParticleEmitter(scene, enabledSP && enabled);
+            }
+        }
+
+    }
+
+    @Override
+    protected void setDepthTexture(Texture depthTexture) {
+        this.depthTexture = depthTexture;
+        if (enabledSoftParticles && depthTexture != null) {
+            initSoftParticles(viewPort, true);
+        }
     }
 
     /**
@@ -49,6 +90,11 @@
     }
 
     @Override
+    protected boolean isRequiresDepthTexture() {
+        return enabledSoftParticles;
+    }
+
+    @Override
     protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
         renderManager.setCamera(viewPort.getCamera(), false);
         if (prevFilterBuffer != sceneBuffer) {
@@ -63,6 +109,8 @@
         if (renderManager != null) {
             renderManager.setHandleTranslucentBucket(true);
         }
+
+        initSoftParticles(viewPort, false);
     }
 
     @Override
@@ -76,5 +124,32 @@
         if (renderManager != null) {
             renderManager.setHandleTranslucentBucket(!enabled);
         }
+        initSoftParticles(viewPort, enabledSoftParticles);
+    }
+
+    private void makeSoftParticleEmitter(Spatial scene, boolean enabled) {
+        if (scene instanceof Node) {
+            Node n = (Node) scene;
+            for (Spatial child : n.getChildren()) {
+                makeSoftParticleEmitter(child, enabled);
+            }
+        }
+        if (scene instanceof ParticleEmitter) {
+            ParticleEmitter emitter = (ParticleEmitter) scene;
+            if (enabled) {
+                enabledSoftParticles = enabled;
+
+                emitter.getMaterial().selectTechnique("SoftParticles", renderManager);
+                emitter.getMaterial().setTexture("DepthTexture", processor.getDepthTexture());
+                emitter.setQueueBucket(RenderQueue.Bucket.Translucent);
+
+                logger.log(Level.INFO, "Made particle Emitter {0} soft.", emitter.getName());
+            } else {
+                emitter.getMaterial().clearParam("DepthTexture");
+                emitter.getMaterial().selectTechnique("Default", renderManager);
+               // emitter.setQueueBucket(RenderQueue.Bucket.Transparent);
+                logger.log(Level.INFO, "Particle Emitter {0} is not soft anymore.", emitter.getName());
+            }
+        }
     }
 }
diff --git a/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java~ b/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java~
deleted file mode 100644
index f5fbfd1..0000000
--- a/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java~
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * 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.plugins;
-
-import com.jme3.asset.AssetInfo;
-import com.jme3.asset.AssetKey;
-import com.jme3.asset.AssetLocator;
-import com.jme3.asset.AssetManager;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CoderResult;
-import java.util.HashMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.zip.Inflater;
-import java.util.zip.InflaterInputStream;
-import java.util.zip.ZipEntry;
-
-public class HttpZipLocator implements AssetLocator {
-
-    private static final Logger logger = Logger.getLogger(HttpZipLocator.class.getName());
-
-    private URL zipUrl;
-    private String rootPath = "";
-    private int numEntries;
-    private int tableOffset;
-    private int tableLength;
-    private HashMap<String, ZipEntry2> entries;
-    
-    private static final ByteBuffer byteBuf = ByteBuffer.allocate(250);
-    private static final CharBuffer charBuf = CharBuffer.allocate(250);
-    private static final CharsetDecoder utf8Decoder;
-    
-    static {
-        Charset utf8 = Charset.forName("UTF-8");
-        utf8Decoder = utf8.newDecoder();
-    }
-
-    private static class ZipEntry2 {
-        String name;
-        int length;
-        int offset;
-        int compSize;
-        long crc;
-        boolean deflate;
-
-        @Override
-        public String toString(){
-            return "ZipEntry[name=" + name +
-                         ",  length=" + length +
-                         ",  compSize=" + compSize +
-                         ",  offset=" + offset + "]";
-        }
-    }
-
-    private static int get16(byte[] b, int off) {
-	return  (b[off++] & 0xff) |
-               ((b[off]   & 0xff) << 8);
-    }
-
-    private static int get32(byte[] b, int off) {
-	return  (b[off++] & 0xff) |
-               ((b[off++] & 0xff) << 8) |
-               ((b[off++] & 0xff) << 16) |
-               ((b[off] & 0xff) << 24);
-    }
-
-    private static long getu32(byte[] b, int off) throws IOException{
-        return (b[off++]&0xff) |
-              ((b[off++]&0xff) << 8) |
-              ((b[off++]&0xff) << 16) |
-             (((long)(b[off]&0xff)) << 24);
-    }
-
-    private static String getUTF8String(byte[] b, int off, int len) throws CharacterCodingException {
-        StringBuilder sb = new StringBuilder();
-        
-        int read = 0;
-        while (read < len){
-            // Either read n remaining bytes in b or 250 if n is higher.
-            int toRead = Math.min(len - read, byteBuf.capacity());
-            
-            boolean endOfInput = toRead < byteBuf.capacity();
-            
-            // read 'toRead' bytes into byteBuf
-            byteBuf.put(b, off + read, toRead);
-            
-            // set limit to position and set position to 0
-            // so data can be decoded
-            byteBuf.flip();
-            
-            // decode data in byteBuf
-            CoderResult result = utf8Decoder.decode(byteBuf, charBuf, endOfInput); 
-            
-            // if the result is not an underflow its an error
-            // that cannot be handled.
-            // if the error is an underflow and its the end of input
-            // then the decoder expects more bytes but there are no more => error
-            if (!result.isUnderflow() || !endOfInput){
-                result.throwException();
-            }
-            
-            // flip the char buf to get the string just decoded
-            charBuf.flip();
-            
-            // append the decoded data into the StringBuilder
-            sb.append(charBuf.toString());
-            
-            // clear buffers for next use
-            byteBuf.clear();
-            charBuf.clear();
-            
-            read += toRead;
-        }
-        
-        return sb.toString();
-    }
-
-    private InputStream readData(int offset, int length) throws IOException{
-        HttpURLConnection conn = (HttpURLConnection) zipUrl.openConnection();
-        conn.setDoOutput(false);
-        conn.setUseCaches(false);
-        conn.setInstanceFollowRedirects(false);
-        String range = "-";
-        if (offset != Integer.MAX_VALUE){
-            range = offset + range;
-        }
-        if (length != Integer.MAX_VALUE){
-            if (offset != Integer.MAX_VALUE){
-                range = range + (offset + length - 1);
-            }else{
-                range = range + length;
-            }
-        }
-
-        conn.setRequestProperty("Range", "bytes=" + range);
-        conn.connect();
-        if (conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL){
-            return conn.getInputStream();
-        }else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK){
-            throw new IOException("Your server does not support HTTP feature Content-Range. Please contact your server administrator.");
-        }else{
-            throw new IOException(conn.getResponseCode() + " " + conn.getResponseMessage());
-        }
-    }
-
-    private int readTableEntry(byte[] table, int offset) throws IOException{
-        if (get32(table, offset) != ZipEntry.CENSIG){
-            throw new IOException("Central directory error, expected 'PK12'");
-        }
-
-        int nameLen = get16(table, offset + ZipEntry.CENNAM);
-        int extraLen = get16(table, offset + ZipEntry.CENEXT);
-        int commentLen = get16(table, offset + ZipEntry.CENCOM);
-        int newOffset = offset + ZipEntry.CENHDR + nameLen + extraLen + commentLen;
-
-        int flags = get16(table, offset + ZipEntry.CENFLG);
-        if ((flags & 1) == 1){
-            // ignore this entry, it uses encryption
-            return newOffset;
-        }
-            
-        int method = get16(table, offset + ZipEntry.CENHOW);
-        if (method != ZipEntry.DEFLATED && method != ZipEntry.STORED){
-            // ignore this entry, it uses unknown compression method
-            return newOffset;
-        }
-
-        String name = getUTF8String(table, offset + ZipEntry.CENHDR, nameLen);
-        if (name.charAt(name.length()-1) == '/'){
-            // ignore this entry, it is directory node
-            // or it has no name (?)
-            return newOffset;
-        }
-
-        ZipEntry2 entry = new ZipEntry2();
-        entry.name     = name;
-        entry.deflate  = (method == ZipEntry.DEFLATED);
-        entry.crc      = getu32(table, offset + ZipEntry.CENCRC);
-        entry.length   = get32(table, offset + ZipEntry.CENLEN);
-        entry.compSize = get32(table, offset + ZipEntry.CENSIZ);
-        entry.offset   = get32(table, offset + ZipEntry.CENOFF);
-
-        // we want offset directly into file data ..
-        // move the offset forward to skip the LOC header
-        entry.offset += ZipEntry.LOCHDR + nameLen + extraLen;
-
-        entries.put(entry.name, entry);
-        
-        return newOffset;
-    }
-
-    private void fillByteArray(byte[] array, InputStream source) throws IOException{
-        int total = 0;
-        int length = array.length;
-	while (total < length) {
-	    int read = source.read(array, total, length - total);
-            if (read < 0)
-                throw new IOException("Failed to read entire array");
-
-	    total += read;
-	}
-    }
-
-    private void readCentralDirectory() throws IOException{
-        InputStream in = readData(tableOffset, tableLength);
-        byte[] header = new byte[tableLength];
-
-        // Fix for "PK12 bug in town.zip": sometimes
-        // not entire byte array will be read with InputStream.read()
-        // (especially for big headers)
-        fillByteArray(header, in);
-
-//        in.read(header);
-        in.close();
-
-        entries = new HashMap<String, ZipEntry2>(numEntries);
-        int offset = 0;
-        for (int i = 0; i < numEntries; i++){
-            offset = readTableEntry(header, offset);
-        }
-    }
-
-    private void readEndHeader() throws IOException{
-
-//        InputStream in = readData(Integer.MAX_VALUE, ZipEntry.ENDHDR);
-//        byte[] header = new byte[ZipEntry.ENDHDR];
-//        fillByteArray(header, in);
-//        in.close();
-//
-//        if (get32(header, 0) != ZipEntry.ENDSIG){
-//            throw new IOException("End header error, expected 'PK56'");
-//        }
-
-        // Fix for "PK56 bug in town.zip":
-        // If there's a zip comment inside the end header,
-        // PK56 won't appear in the -22 position relative to the end of the
-        // file!
-        // In that case, we have to search for it.
-        // Increase search space to 200 bytes
-
-        InputStream in = readData(Integer.MAX_VALUE, 200);
-        byte[] header = new byte[200];
-        fillByteArray(header, in);
-        in.close();
-
-        int offset = -1;
-        for (int i = 200 - 22; i >= 0; i--){
-            if (header[i] == (byte) (ZipEntry.ENDSIG & 0xff)
-              && get32(header, i) == ZipEntry.ENDSIG){
-                // found location
-                offset = i;
-                break;
-            }
-        }
-        if (offset == -1)
-            throw new IOException("Cannot find Zip End Header in file!");
-
-        numEntries  = get16(header, offset + ZipEntry.ENDTOT);
-        tableLength = get32(header, offset + ZipEntry.ENDSIZ);
-        tableOffset = get32(header, offset + ZipEntry.ENDOFF);
-    }
-
-    public void load(URL url) throws IOException {
-        if (!url.getProtocol().equals("http"))
-            throw new UnsupportedOperationException();
-
-        zipUrl = url;
-        readEndHeader();
-        readCentralDirectory();
-    }
-
-    private InputStream openStream(ZipEntry2 entry) throws IOException{
-        InputStream in = readData(entry.offset, entry.compSize);
-        if (entry.deflate){
-            return new InflaterInputStream(in, new Inflater(true));
-        }
-        return in;
-    }
-
-    public InputStream openStream(String name) throws IOException{
-        ZipEntry2 entry = entries.get(name);
-        if (entry == null)
-            throw new RuntimeException("Entry not found: "+name);
-
-        return openStream(entry);
-    }
-
-    public void setRootPath(String path){
-        if (!rootPath.equals(path)){
-            rootPath = path;
-            try {
-                load(new URL(path));
-            } catch (IOException ex) {
-                logger.log(Level.WARNING, "Failed to set root path "+path, ex);
-            }
-        }
-    }
-
-    public AssetInfo locate(AssetManager manager, AssetKey key){
-        final ZipEntry2 entry = entries.get(key.getName());
-        if (entry == null)
-            return null;
-
-        return new AssetInfo(manager, key){
-            @Override
-            public InputStream openStream() {
-                try {
-                    return HttpZipLocator.this.openStream(entry);
-                } catch (IOException ex) {
-                    logger.log(Level.WARNING, "Error retrieving "+entry.name, ex);
-                    return null;
-                }
-            }
-        };
-    }
-
-}
diff --git a/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java b/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java
index 3ce7f52..47165f6 100644
--- a/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java
+++ b/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java
@@ -454,9 +454,11 @@
 
         if (hasNormals){
             normBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3);
+            m.setBuffer(VertexBuffer.Type.Normal, 3, normBuf);
         }
         if (hasTexCoord){
             tcBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 2);
+            m.setBuffer(VertexBuffer.Type.TexCoord, 2, tcBuf);
         }
 
         IndexBuffer indexBuf = null;
@@ -517,9 +519,7 @@
         }
 
         m.setBuffer(VertexBuffer.Type.Position, 3, posBuf);
-        m.setBuffer(VertexBuffer.Type.Normal,   3, normBuf);
-        m.setBuffer(VertexBuffer.Type.TexCoord, 2, tcBuf);
-        // index buffer was set on creation
+        // index buffer and others were set on creation
 
         m.setStatic();
         m.updateBound();
diff --git a/engine/src/core/com/jme3/app/Application.java b/engine/src/core/com/jme3/app/Application.java
index 517ec61..5100725 100644
--- a/engine/src/core/com/jme3/app/Application.java
+++ b/engine/src/core/com/jme3/app/Application.java
@@ -290,6 +290,10 @@
 

     private void initStateManager(){

         stateManager = new AppStateManager(this);

+        

+        // Always register a ResetStatsState to make sure

+        // that the stats are cleared every frame

+        stateManager.attach(new ResetStatsState());

     }

 

     /**

@@ -580,6 +584,17 @@
             }

             task.invoke();

         } while (((task = taskQueue.poll()) != null));

+        

+        /* I think the above is really just doing this:

+        AppTask<?> task;

+        while( (task = taskQueue.poll()) != null ) {

+            if (!task.isCancelled()) {

+                task.invoke();

+            }

+        }

+        //...but it's hard to say for sure.  It's so twisted

+        //up that I don't trust my eyes.  -pspeed

+        */ 

     

         if (speed == 0 || paused)

             return;

diff --git a/engine/src/core/com/jme3/app/ResetStatsState.java b/engine/src/core/com/jme3/app/ResetStatsState.java
new file mode 100644
index 0000000..03f3013
--- /dev/null
+++ b/engine/src/core/com/jme3/app/ResetStatsState.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2012 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;
+
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.renderer.RenderManager;
+
+
+/**
+ *  Resets (clearFrame()) the render's stats object every frame
+ *  during AppState.render().  This state is registered once
+ *  with Application to ensure that the stats are cleared once
+ *  a frame.  Using this makes sure that any Appliction based
+ *  application that properly runs its state manager will have
+ *  stats reset no matter how many views it has or if it even
+ *  has views.
+ *
+ *  @author    Paul Speed
+ */
+public class ResetStatsState extends AbstractAppState {
+
+    public ResetStatsState() {
+    }    
+
+    @Override
+    public void render(RenderManager rm) {
+        super.render(rm);
+        rm.getRenderer().getStatistics().clearFrame();        
+    } 
+
+}
diff --git a/engine/src/core/com/jme3/app/SimpleApplication.java b/engine/src/core/com/jme3/app/SimpleApplication.java
index c79ce83..aa8e749 100644
--- a/engine/src/core/com/jme3/app/SimpleApplication.java
+++ b/engine/src/core/com/jme3/app/SimpleApplication.java
@@ -241,13 +241,6 @@
         rootNode.updateGeometricState();
         guiNode.updateGeometricState();
 
-        // Moving this here to make sure it is always done.
-        // Now the sets are cleared every frame (guaranteed)
-        // and more than one viewer can access the data.  This
-        // used to be cleared by StatsView but then only StatsView
-        // could get accurate counts.
-        renderer.getStatistics().clearFrame();        
-                
         // render states
         stateManager.render(renderManager);
         renderManager.render(tpf, context.isRenderable());
diff --git a/engine/src/core/com/jme3/app/StatsView.java b/engine/src/core/com/jme3/app/StatsView.java
index 49eeb13..f2b2ba5 100644
--- a/engine/src/core/com/jme3/app/StatsView.java
+++ b/engine/src/core/com/jme3/app/StatsView.java
@@ -105,7 +105,7 @@
             labels[i].setText(stringBuilder);
         }
         
-        // Moved to SimpleApplication to make sure it is
+        // Moved to ResetStatsState to make sure it is
         // done even if there is no StatsView or the StatsView
         // is disable.
         //statistics.clearFrame();
diff --git a/engine/src/core/com/jme3/app/state/AppStateManager.java b/engine/src/core/com/jme3/app/state/AppStateManager.java
index 81228af..92a00f5 100644
--- a/engine/src/core/com/jme3/app/state/AppStateManager.java
+++ b/engine/src/core/com/jme3/app/state/AppStateManager.java
@@ -202,6 +202,9 @@
 

     protected void initializePending(){

         AppState[] array = getInitializing();

+        if (array.length == 0)

+            return;

+            

         synchronized( states ) {

             // Move the states that will be initialized

             // into the active array.  In all but one case the

@@ -219,6 +222,9 @@
     

     protected void terminatePending(){

         AppState[] array = getTerminating();

+        if (array.length == 0)

+            return;

+            

         for (AppState state : array) {

             state.cleanup();

         }        

diff --git a/engine/src/core/com/jme3/input/InputManager.java b/engine/src/core/com/jme3/input/InputManager.java
index 23f2988..a0c8788 100644
--- a/engine/src/core/com/jme3/input/InputManager.java
+++ b/engine/src/core/com/jme3/input/InputManager.java
@@ -1,881 +1,892 @@
-/*
- * Copyright (c) 2009-2012 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.input;
-
-import com.jme3.app.Application;
-import com.jme3.input.controls.*;
-import com.jme3.input.event.*;
-import com.jme3.math.FastMath;
-import com.jme3.math.Vector2f;
-import com.jme3.util.IntMap;
-import com.jme3.util.IntMap.Entry;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * The <code>InputManager</code> is responsible for converting input events
- * received from the Key, Mouse and Joy Input implementations into an
- * abstract, input device independent representation that user code can use.
- * <p>
- * By default an <code>InputManager</code> is included with every Application instance for use
- * in user code to query input, unless the Application is created as headless
- * or with input explicitly disabled.
- * <p>
- * The input manager has two concepts, a {@link Trigger} and a mapping.
- * A trigger represents a specific input trigger, such as a key button, 
- * or a mouse axis. A mapping represents a link onto one or several triggers, 
- * when the appropriate trigger is activated (e.g. a key is pressed), the 
- * mapping will be invoked. Any listeners registered to receive an event
- * from the mapping will have an event raised.
- * <p>
- * There are two types of events that {@link InputListener input listeners}
- * can receive, one is {@link ActionListener#onAction(java.lang.String, boolean, float) action}
- * events and another is {@link AnalogListener#onAnalog(java.lang.String, float, float) analog}
- * events. 
- * <p>
- * <code>onAction</code> events are raised when the specific input
- * activates or deactivates. For a digital input such as key press, the <code>onAction()</code>
- * event will be raised with the <code>isPressed</code> argument equal to true,
- * when the key is released, <code>onAction</code> is called again but this time
- * with the <code>isPressed</code> argument set to false.
- * For analog inputs, the <code>onAction</code> method will be called any time
- * the input is non-zero, however an exception to this is for joystick axis inputs,
- * which are only called when the input is above the {@link InputManager#setAxisDeadZone(float) dead zone}.
- * <p>
- * <code>onAnalog</code> events are raised every frame while the input is activated.
- * For digital inputs, every frame that the input is active will cause the
- * <code>onAnalog</code> method to be called, the argument <code>value</code>
- * argument will equal to the frame's time per frame (TPF) value but only
- * for digital inputs. For analog inputs however, the <code>value</code> argument
- * will equal the actual analog value.
- */
-public class InputManager implements RawInputListener {
-
-    private static final Logger logger = Logger.getLogger(InputManager.class.getName());
-    private final KeyInput keys;
-    private final MouseInput mouse;
-    private final JoyInput joystick;
-    private final TouchInput touch;
-    private float frameTPF;
-    private long lastLastUpdateTime = 0;
-    private long lastUpdateTime = 0;
-    private long frameDelta = 0;
-    private long firstTime = 0;
-    private boolean eventsPermitted = false;
-    private boolean mouseVisible = true;
-    private boolean safeMode = false;
-    private float axisDeadZone = 0.05f;
-    private Vector2f cursorPos = new Vector2f();
-    private Joystick[] joysticks;
-    private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>();
-    private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>();
-    private final IntMap<Long> pressedButtons = new IntMap<Long>();
-    private final IntMap<Float> axisValues = new IntMap<Float>();
-    private ArrayList<RawInputListener> rawListeners = new ArrayList<RawInputListener>();
-    private RawInputListener[] rawListenerArray = null;
-    private ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();
-
-    private static class Mapping {
-
-        private final String name;
-        private final ArrayList<Integer> triggers = new ArrayList<Integer>();
-        private final ArrayList<InputListener> listeners = new ArrayList<InputListener>();
-
-        public Mapping(String name) {
-            this.name = name;
-        }
-    }
-
-    /**
-     * Initializes the InputManager.
-     * 
-     * <p>This should only be called internally in {@link Application}.
-     *
-     * @param mouse
-     * @param keys
-     * @param joystick
-     * @param touch
-     * @throws IllegalArgumentException If either mouseInput or keyInput are null.
-     */
-    public InputManager(MouseInput mouse, KeyInput keys, JoyInput joystick, TouchInput touch) {
-        if (keys == null || mouse == null) {
-            throw new NullPointerException("Mouse or keyboard cannot be null");
-        }
-
-        this.keys = keys;
-        this.mouse = mouse;
-        this.joystick = joystick;
-        this.touch = touch;
-
-        keys.setInputListener(this);
-        mouse.setInputListener(this);
-        if (joystick != null) {
-            joystick.setInputListener(this);
-            joysticks = joystick.loadJoysticks(this);
-        }
-        if (touch != null) {
-            touch.setInputListener(this);
-        }
-
-        firstTime = keys.getInputTimeNanos();
-    }
-
-    private void invokeActions(int hash, boolean pressed) {
-        ArrayList<Mapping> maps = bindings.get(hash);
-        if (maps == null) {
-            return;
-        }
-
-        int size = maps.size();
-        for (int i = size - 1; i >= 0; i--) {
-            Mapping mapping = maps.get(i);
-            ArrayList<InputListener> listeners = mapping.listeners;
-            int listenerSize = listeners.size();
-            for (int j = listenerSize - 1; j >= 0; j--) {
-                InputListener listener = listeners.get(j);
-                if (listener instanceof ActionListener) {
-                    ((ActionListener) listener).onAction(mapping.name, pressed, frameTPF);
-                }
-            }
-        }
-    }
-
-    private float computeAnalogValue(long timeDelta) {
-        if (safeMode || frameDelta == 0) {
-            return 1f;
-        } else {
-            return FastMath.clamp((float) timeDelta / (float) frameDelta, 0, 1);
-        }
-    }
-
-    private void invokeTimedActions(int hash, long time, boolean pressed) {
-        if (!bindings.containsKey(hash)) {
-            return;
-        }
-
-        if (pressed) {
-            pressedButtons.put(hash, time);
-        } else {
-            Long pressTimeObj = pressedButtons.remove(hash);
-            if (pressTimeObj == null) {
-                return; // under certain circumstances it can be null, ignore
-            }                        // the event then.
-
-            long pressTime = pressTimeObj;
-            long lastUpdate = lastLastUpdateTime;
-            long releaseTime = time;
-            long timeDelta = releaseTime - Math.max(pressTime, lastUpdate);
-
-            if (timeDelta > 0) {
-                invokeAnalogs(hash, computeAnalogValue(timeDelta), false);
-            }
-        }
-    }
-
-    private void invokeUpdateActions() {
-        for (Entry<Long> pressedButton : pressedButtons) {
-            int hash = pressedButton.getKey();
-
-            long pressTime = pressedButton.getValue();
-            long timeDelta = lastUpdateTime - Math.max(lastLastUpdateTime, pressTime);
-
-            if (timeDelta > 0) {
-                invokeAnalogs(hash, computeAnalogValue(timeDelta), false);
-            }
-        }
-
-        for (Entry<Float> axisValue : axisValues) {
-            int hash = axisValue.getKey();
-            float value = axisValue.getValue();
-            invokeAnalogs(hash, value * frameTPF, true);
-        }
-    }
-
-    private void invokeAnalogs(int hash, float value, boolean isAxis) {
-        ArrayList<Mapping> maps = bindings.get(hash);
-        if (maps == null) {
-            return;
-        }
-
-        if (!isAxis) {
-            value *= frameTPF;
-        }
-
-        int size = maps.size();
-        for (int i = size - 1; i >= 0; i--) {
-            Mapping mapping = maps.get(i);
-            ArrayList<InputListener> listeners = mapping.listeners;
-            int listenerSize = listeners.size();
-            for (int j = listenerSize - 1; j >= 0; j--) {
-                InputListener listener = listeners.get(j);
-                if (listener instanceof AnalogListener) {
-                    // NOTE: multiply by TPF for any button bindings
-                    ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);
-                }
-            }
-        }
-    }
-
-    private void invokeAnalogsAndActions(int hash, float value, boolean applyTpf) {
-        if (value < axisDeadZone) {
-            invokeAnalogs(hash, value, !applyTpf);
-            return;
-        }
-
-        ArrayList<Mapping> maps = bindings.get(hash);
-        if (maps == null) {
-            return;
-        }
-
-        boolean valueChanged = !axisValues.containsKey(hash);
-        if (applyTpf) {
-            value *= frameTPF;
-        }
-
-        int size = maps.size();
-        for (int i = size - 1; i >= 0; i--) {
-            Mapping mapping = maps.get(i);
-            ArrayList<InputListener> listeners = mapping.listeners;
-            int listenerSize = listeners.size();
-            for (int j = listenerSize - 1; j >= 0; j--) {
-                InputListener listener = listeners.get(j);
-
-                if (listener instanceof ActionListener && valueChanged) {
-                    ((ActionListener) listener).onAction(mapping.name, true, frameTPF);
-                }
-
-                if (listener instanceof AnalogListener) {
-                    ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);
-                }
-
-            }
-        }
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void beginInput() {
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void endInput() {
-    }
-
-    private void onJoyAxisEventQueued(JoyAxisEvent evt) {
-//        for (int i = 0; i < rawListeners.size(); i++){
-//            rawListeners.get(i).onJoyAxisEvent(evt);
-//        }
-
-        int joyId = evt.getJoyIndex();
-        int axis = evt.getAxisIndex();
-        float value = evt.getValue();
-        if (value < axisDeadZone && value > -axisDeadZone) {
-            int hash1 = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
-            int hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
-
-            Float val1 = axisValues.get(hash1);
-            Float val2 = axisValues.get(hash2);
-
-            if (val1 != null && val1.floatValue() > axisDeadZone) {
-                invokeActions(hash1, false);
-            }
-            if (val2 != null && val2.floatValue() > axisDeadZone) {
-                invokeActions(hash2, false);
-            }
-
-            axisValues.remove(hash1);
-            axisValues.remove(hash2);
-
-        } else if (value < 0) {
-            int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
-            int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
-            invokeAnalogsAndActions(hash, -value, true);
-            axisValues.put(hash, -value);
-            axisValues.remove(otherHash);
-        } else {
-            int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
-            int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
-            invokeAnalogsAndActions(hash, value, true);
-            axisValues.put(hash, value);
-            axisValues.remove(otherHash);
-        }
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void onJoyAxisEvent(JoyAxisEvent evt) {
-        if (!eventsPermitted) {
-            throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");
-        }
-
-        inputQueue.add(evt);
-    }
-
-    private void onJoyButtonEventQueued(JoyButtonEvent evt) {
-//        for (int i = 0; i < rawListeners.size(); i++){
-//            rawListeners.get(i).onJoyButtonEvent(evt);
-//        }
-
-        int hash = JoyButtonTrigger.joyButtonHash(evt.getJoyIndex(), evt.getButtonIndex());
-        invokeActions(hash, evt.isPressed());
-        invokeTimedActions(hash, evt.getTime(), evt.isPressed());
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void onJoyButtonEvent(JoyButtonEvent evt) {
-        if (!eventsPermitted) {
-            throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");
-        }
-
-        inputQueue.add(evt);
-    }
-
-    private void onMouseMotionEventQueued(MouseMotionEvent evt) {
-//        for (int i = 0; i < rawListeners.size(); i++){
-//            rawListeners.get(i).onMouseMotionEvent(evt);
-//        }
-
-        if (evt.getDX() != 0) {
-            float val = Math.abs(evt.getDX()) / 1024f;
-            invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_X, evt.getDX() < 0), val, false);
-        }
-        if (evt.getDY() != 0) {
-            float val = Math.abs(evt.getDY()) / 1024f;
-            invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_Y, evt.getDY() < 0), val, false);
-        }
-        if (evt.getDeltaWheel() != 0) {
-            float val = Math.abs(evt.getDeltaWheel()) / 100f;
-            invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_WHEEL, evt.getDeltaWheel() < 0), val, false);
-        }
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void onMouseMotionEvent(MouseMotionEvent evt) {
-        if (!eventsPermitted) {
-            throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");
-        }
-
-        cursorPos.set(evt.getX(), evt.getY());
-        inputQueue.add(evt);
-    }
-
-    private void onMouseButtonEventQueued(MouseButtonEvent evt) {
-        int hash = MouseButtonTrigger.mouseButtonHash(evt.getButtonIndex());
-        invokeActions(hash, evt.isPressed());
-        invokeTimedActions(hash, evt.getTime(), evt.isPressed());
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void onMouseButtonEvent(MouseButtonEvent evt) {
-        if (!eventsPermitted) {
-            throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");
-        }
-        //updating cursor pos on click, so that non android touch events can properly update cursor position.
-        cursorPos.set(evt.getX(), evt.getY());
-        inputQueue.add(evt);
-    }
-
-    private void onKeyEventQueued(KeyInputEvent evt) {
-        if (evt.isRepeating()) {
-            return; // repeat events not used for bindings
-        }
-        
-        int hash = KeyTrigger.keyHash(evt.getKeyCode());
-        invokeActions(hash, evt.isPressed());
-        invokeTimedActions(hash, evt.getTime(), evt.isPressed());
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void onKeyEvent(KeyInputEvent evt) {
-        if (!eventsPermitted) {
-            throw new UnsupportedOperationException("KeyInput has raised an event at an illegal time.");
-        }
-
-        inputQueue.add(evt);
-    }
-
-    /**
-     * Set the deadzone for joystick axes.
-     * 
-     * <p>{@link ActionListener#onAction(java.lang.String, boolean, float) }
-     * events will only be raised if the joystick axis value is greater than
-     * the <code>deadZone</code>.
-     * 
-     * @param deadZone the deadzone for joystick axes. 
-     */
-    public void setAxisDeadZone(float deadZone) {
-        this.axisDeadZone = deadZone;
-    }
-
-    /**
-     * Returns the deadzone for joystick axes.
-     * 
-     * @return the deadzone for joystick axes.
-     */
-    public float getAxisDeadZone() {
-        return axisDeadZone;
-    }
-    
-    /**
-     * Adds a new listener to receive events on the given mappings.
-     * 
-     * <p>The given InputListener will be registered to receive events
-     * on the specified mapping names. When a mapping raises an event, the
-     * listener will have its appropriate method invoked, either
-     * {@link ActionListener#onAction(java.lang.String, boolean, float) }
-     * or {@link AnalogListener#onAnalog(java.lang.String, float, float) }
-     * depending on which interface the <code>listener</code> implements. 
-     * If the listener implements both interfaces, then it will receive the
-     * appropriate event for each method.
-     * 
-     * @param listener The listener to register to receive input events.
-     * @param mappingNames The mapping names which the listener will receive
-     * events from.
-     * 
-     * @see InputManager#removeListener(com.jme3.input.controls.InputListener) 
-     */
-    public void addListener(InputListener listener, String... mappingNames) {
-        for (String mappingName : mappingNames) {
-            Mapping mapping = mappings.get(mappingName);
-            if (mapping == null) {
-                mapping = new Mapping(mappingName);
-                mappings.put(mappingName, mapping);
-            }
-            if (!mapping.listeners.contains(listener)) {
-                mapping.listeners.add(listener);
-            }
-        }
-    }
-
-    /**
-     * Removes a listener from receiving events.
-     * 
-     * <p>This will unregister the listener from any mappings that it
-     * was previously registered with via 
-     * {@link InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) }.
-     * 
-     * @param listener The listener to unregister.
-     * 
-     * @see InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) 
-     */
-    public void removeListener(InputListener listener) {
-        for (Mapping mapping : mappings.values()) {
-            mapping.listeners.remove(listener);
-        }
-    }
-
-    /**
-     * Create a new mapping to the given triggers.
-     * 
-     * <p>
-     * The given mapping will be assigned to the given triggers, when
-     * any of the triggers given raise an event, the listeners
-     * registered to the mappings will receive appropriate events.
-     * 
-     * @param mappingName The mapping name to assign.
-     * @param triggers The triggers to which the mapping is to be registered.
-     * 
-     * @see InputManager#deleteMapping(java.lang.String) 
-     */
-    public void addMapping(String mappingName, Trigger... triggers) {
-        Mapping mapping = mappings.get(mappingName);
-        if (mapping == null) {
-            mapping = new Mapping(mappingName);
-            mappings.put(mappingName, mapping);
-        }
-
-        for (Trigger trigger : triggers) {
-            int hash = trigger.triggerHashCode();
-            ArrayList<Mapping> names = bindings.get(hash);
-            if (names == null) {
-                names = new ArrayList<Mapping>();
-                bindings.put(hash, names);
-            }
-            if (!names.contains(mapping)) {
-                names.add(mapping);
-                mapping.triggers.add(hash);
-            } else {
-                logger.log(Level.WARNING, "Attempted to add mapping \"{0}\" twice to trigger.", mappingName);
-            }
-        }
-    }
-
-    /**
-     * Returns true if this InputManager has a mapping registered
-     * for the given mappingName.
-     *
-     * @param mappingName The mapping name to check.
-     *
-     * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) 
-     * @see InputManager#deleteMapping(java.lang.String) 
-     */ 
-    public boolean hasMapping(String mappingName) {
-        return mappings.containsKey(mappingName);
-    }
-    
-    /**
-     * Deletes a mapping from receiving trigger events.
-     * 
-     * <p>
-     * The given mapping will no longer be assigned to receive trigger
-     * events.
-     * 
-     * @param mappingName The mapping name to unregister.
-     * 
-     * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) 
-     */
-    public void deleteMapping(String mappingName) {
-        Mapping mapping = mappings.remove(mappingName);
-        if (mapping == null) {
-            throw new IllegalArgumentException("Cannot find mapping: " + mappingName);
-        }
-
-        ArrayList<Integer> triggers = mapping.triggers;
-        for (int i = triggers.size() - 1; i >= 0; i--) {
-            int hash = triggers.get(i);
-            ArrayList<Mapping> maps = bindings.get(hash);
-            maps.remove(mapping);
-        }
-    }
-
-    /**
-     * Deletes a specific trigger registered to a mapping.
-     * 
-     * <p>
-     * The given mapping will no longer receive events raised by the 
-     * trigger.
-     * 
-     * @param mappingName The mapping name to cease receiving events from the 
-     * trigger.
-     * @param trigger The trigger to no longer invoke events on the mapping.
-     */
-    public void deleteTrigger(String mappingName, Trigger trigger) {
-        Mapping mapping = mappings.get(mappingName);
-        if (mapping == null) {
-            throw new IllegalArgumentException("Cannot find mapping: " + mappingName);
-        }
-
-        ArrayList<Mapping> maps = bindings.get(trigger.triggerHashCode());
-        maps.remove(mapping);
-
-    }
-
-    /**
-     * Clears all the input mappings from this InputManager. 
-     * Consequently, also clears all of the
-     * InputListeners as well.
-     */
-    public void clearMappings() {
-        mappings.clear();
-        bindings.clear();
-        reset();
-    }
-
-    /**
-     * Do not use.
-     * Called to reset pressed keys or buttons when focus is restored.
-     */
-    public void reset() {
-        pressedButtons.clear();
-        axisValues.clear();
-    }
-
-    /**
-     * Returns whether the mouse cursor is visible or not.
-     * 
-     * <p>By default the cursor is visible.
-     * 
-     * @return whether the mouse cursor is visible or not.
-     * 
-     * @see InputManager#setCursorVisible(boolean) 
-     */
-    public boolean isCursorVisible() {
-        return mouseVisible;
-    }
-
-    /**
-     * Set whether the mouse cursor should be visible or not.
-     * 
-     * @param visible whether the mouse cursor should be visible or not.
-     */
-    public void setCursorVisible(boolean visible) {
-        if (mouseVisible != visible) {
-            mouseVisible = visible;
-            mouse.setCursorVisible(mouseVisible);
-        }
-    }
-
-    /**
-     * Returns the current cursor position. The position is relative to the
-     * bottom-left of the screen and is in pixels.
-     * 
-     * @return the current cursor position
-     */
-    public Vector2f getCursorPosition() {
-        return cursorPos;
-    }
-
-    /**
-     * Returns an array of all joysticks installed on the system.
-     * 
-     * @return an array of all joysticks installed on the system.
-     */
-    public Joystick[] getJoysticks() {
-        return joysticks;
-    }
-
-    /**
-     * Adds a {@link RawInputListener} to receive raw input events.
-     * 
-     * <p>
-     * Any raw input listeners registered to this <code>InputManager</code>
-     * will receive raw input events first, before they get handled
-     * by the <code>InputManager</code> itself. The listeners are 
-     * each processed in the order they were added, e.g. FIFO.
-     * <p>
-     * If a raw input listener has handled the event and does not wish
-     * other listeners down the list to process the event, it may set the
-     * {@link InputEvent#setConsumed() consumed flag} to indicate the 
-     * event was consumed and shouldn't be processed any further.
-     * The listener may do this either at each of the event callbacks 
-     * or at the {@link RawInputListener#endInput() } method.
-     * 
-     * @param listener A listener to receive raw input events.
-     * 
-     * @see RawInputListener
-     */
-    public void addRawInputListener(RawInputListener listener) {
-        rawListeners.add(listener);
-        rawListenerArray = null;
-    }
-
-    /**
-     * Removes a {@link RawInputListener} so that it no longer
-     * receives raw input events.
-     * 
-     * @param listener The listener to cease receiving raw input events.
-     * 
-     * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) 
-     */
-    public void removeRawInputListener(RawInputListener listener) {
-        rawListeners.remove(listener);
-        rawListenerArray = null;
-    }
-
-    /**
-     * Clears all {@link RawInputListener}s.
-     * 
-     * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) 
-     */
-    public void clearRawInputListeners() {
-        rawListeners.clear();
-        rawListenerArray = null;
-    }
-
-    private RawInputListener[] getRawListenerArray() {
-        if (rawListenerArray == null) 
-            rawListenerArray = rawListeners.toArray(new RawInputListener[rawListeners.size()]);
-        return rawListenerArray;
-    }
-    
-    /**
-     * Enable simulation of mouse events. Used for touchscreen input only.
-     * 
-     * @param value True to enable simulation of mouse events
-     */
-    public void setSimulateMouse(boolean value) {
-        if (touch != null) {
-            touch.setSimulateMouse(value);
-        }
-    }
-    
-    /**
-     * Enable simulation of keyboard events. Used for touchscreen input only.
-     * 
-     * @param value True to enable simulation of keyboard events
-     */
-    public void setSimulateKeyboard(boolean value) {
-        if (touch != null) {
-            touch.setSimulateKeyboard(value);
-        }
-    }
-
-    private void processQueue() {
-        int queueSize = inputQueue.size();
-        RawInputListener[] array = getRawListenerArray();
- 
-        for (RawInputListener listener : array) {
-            listener.beginInput();
-
-            for (int j = 0; j < queueSize; j++) {
-                InputEvent event = inputQueue.get(j);
-                if (event.isConsumed()) {
-                    continue;
-                }
-
-                if (event instanceof MouseMotionEvent) {
-                    listener.onMouseMotionEvent((MouseMotionEvent) event);
-                } else if (event instanceof KeyInputEvent) {
-                    listener.onKeyEvent((KeyInputEvent) event);
-                } else if (event instanceof MouseButtonEvent) {
-                    listener.onMouseButtonEvent((MouseButtonEvent) event);
-                } else if (event instanceof JoyAxisEvent) {
-                    listener.onJoyAxisEvent((JoyAxisEvent) event);
-                } else if (event instanceof JoyButtonEvent) {
-                    listener.onJoyButtonEvent((JoyButtonEvent) event);
-                } else if (event instanceof TouchEvent) {
-                    listener.onTouchEvent((TouchEvent) event);
-                } else {
-                    assert false;
-                }
-            }
-
-            listener.endInput();
-        }
-
-        for (int i = 0; i < queueSize; i++) {
-            InputEvent event = inputQueue.get(i);
-            if (event.isConsumed()) {
-                continue;
-            }
-
-            if (event instanceof MouseMotionEvent) {
-                onMouseMotionEventQueued((MouseMotionEvent) event);
-            } else if (event instanceof KeyInputEvent) {
-                onKeyEventQueued((KeyInputEvent) event);
-            } else if (event instanceof MouseButtonEvent) {
-                onMouseButtonEventQueued((MouseButtonEvent) event);
-            } else if (event instanceof JoyAxisEvent) {
-                onJoyAxisEventQueued((JoyAxisEvent) event);
-            } else if (event instanceof JoyButtonEvent) {
-                onJoyButtonEventQueued((JoyButtonEvent) event);
-            } else if (event instanceof TouchEvent) {
-                onTouchEventQueued((TouchEvent) event);
-            } else {
-                assert false;
-            }
-            // larynx, 2011.06.10 - flag event as reusable because
-            // the android input uses a non-allocating ringbuffer which
-            // needs to know when the event is not anymore in inputQueue
-            // and therefor can be reused.
-            event.setConsumed();
-        }
-
-        inputQueue.clear();
-    }
-
-    /**
-     * Updates the <code>InputManager</code>. 
-     * This will query current input devices and send
-     * appropriate events to registered listeners.
-     *
-     * @param tpf Time per frame value.
-     */
-    public void update(float tpf) {
-        frameTPF = tpf;
-        
-        // Activate safemode if the TPF value is so small
-        // that rounding errors are inevitable
-        safeMode = tpf < 0.015f;
-        
-        long currentTime = keys.getInputTimeNanos();
-        frameDelta = currentTime - lastUpdateTime;
-
-        eventsPermitted = true;
-
-        keys.update();
-        mouse.update();
-        if (joystick != null) {
-            joystick.update();
-        }
-        if (touch != null) {
-            touch.update();
-        }
-
-        eventsPermitted = false;
-
-        processQueue();
-        invokeUpdateActions();
-
-        lastLastUpdateTime = lastUpdateTime;
-        lastUpdateTime = currentTime;
-    }
-
-    /**
-     * Dispatches touch events to touch listeners
-     * @param evt The touch event to be dispatched to all onTouch listeners
-     */
-    public void onTouchEventQueued(TouchEvent evt) { 
-        ArrayList<Mapping> maps = bindings.get(TouchTrigger.touchHash(evt.getKeyCode()));
-        if (maps == null) {
-            return;
-        }
-
-        int size = maps.size();
-        for (int i = size - 1; i >= 0; i--) {
-            Mapping mapping = maps.get(i);
-            ArrayList<InputListener> listeners = mapping.listeners;
-            int listenerSize = listeners.size();
-            for (int j = listenerSize - 1; j >= 0; j--) {
-                InputListener listener = listeners.get(j);
-                if (listener instanceof TouchListener) {
-                    ((TouchListener) listener).onTouch(mapping.name, evt, frameTPF); 
-                }
-            }
-        }               
-    }
-    
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    @Override
-    public void onTouchEvent(TouchEvent evt) {
-        if (!eventsPermitted) {
-            throw new UnsupportedOperationException("TouchInput has raised an event at an illegal time.");
-        }
-        inputQueue.add(evt);         
-    }
-}
+/*

+ * Copyright (c) 2009-2012 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.input;

+

+import com.jme3.app.Application;

+import com.jme3.input.controls.*;

+import com.jme3.input.event.*;

+import com.jme3.math.FastMath;

+import com.jme3.math.Vector2f;

+import com.jme3.util.IntMap;

+import com.jme3.util.IntMap.Entry;

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.logging.Level;

+import java.util.logging.Logger;

+

+/**

+ * The <code>InputManager</code> is responsible for converting input events

+ * received from the Key, Mouse and Joy Input implementations into an

+ * abstract, input device independent representation that user code can use.

+ * <p>

+ * By default an <code>InputManager</code> is included with every Application instance for use

+ * in user code to query input, unless the Application is created as headless

+ * or with input explicitly disabled.

+ * <p>

+ * The input manager has two concepts, a {@link Trigger} and a mapping.

+ * A trigger represents a specific input trigger, such as a key button, 

+ * or a mouse axis. A mapping represents a link onto one or several triggers, 

+ * when the appropriate trigger is activated (e.g. a key is pressed), the 

+ * mapping will be invoked. Any listeners registered to receive an event

+ * from the mapping will have an event raised.

+ * <p>

+ * There are two types of events that {@link InputListener input listeners}

+ * can receive, one is {@link ActionListener#onAction(java.lang.String, boolean, float) action}

+ * events and another is {@link AnalogListener#onAnalog(java.lang.String, float, float) analog}

+ * events. 

+ * <p>

+ * <code>onAction</code> events are raised when the specific input

+ * activates or deactivates. For a digital input such as key press, the <code>onAction()</code>

+ * event will be raised with the <code>isPressed</code> argument equal to true,

+ * when the key is released, <code>onAction</code> is called again but this time

+ * with the <code>isPressed</code> argument set to false.

+ * For analog inputs, the <code>onAction</code> method will be called any time

+ * the input is non-zero, however an exception to this is for joystick axis inputs,

+ * which are only called when the input is above the {@link InputManager#setAxisDeadZone(float) dead zone}.

+ * <p>

+ * <code>onAnalog</code> events are raised every frame while the input is activated.

+ * For digital inputs, every frame that the input is active will cause the

+ * <code>onAnalog</code> method to be called, the argument <code>value</code>

+ * argument will equal to the frame's time per frame (TPF) value but only

+ * for digital inputs. For analog inputs however, the <code>value</code> argument

+ * will equal the actual analog value.

+ */

+public class InputManager implements RawInputListener {

+

+    private static final Logger logger = Logger.getLogger(InputManager.class.getName());

+    private final KeyInput keys;

+    private final MouseInput mouse;

+    private final JoyInput joystick;

+    private final TouchInput touch;

+    private float frameTPF;

+    private long lastLastUpdateTime = 0;

+    private long lastUpdateTime = 0;

+    private long frameDelta = 0;

+    private long firstTime = 0;

+    private boolean eventsPermitted = false;

+    private boolean mouseVisible = true;

+    private boolean safeMode = false;

+    private float axisDeadZone = 0.05f;

+    private Vector2f cursorPos = new Vector2f();

+    private Joystick[] joysticks;

+    private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>();

+    private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>();

+    private final IntMap<Long> pressedButtons = new IntMap<Long>();

+    private final IntMap<Float> axisValues = new IntMap<Float>();

+    private ArrayList<RawInputListener> rawListeners = new ArrayList<RawInputListener>();

+    private RawInputListener[] rawListenerArray = null;

+    private ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();

+

+    private static class Mapping {

+

+        private final String name;

+        private final ArrayList<Integer> triggers = new ArrayList<Integer>();

+        private final ArrayList<InputListener> listeners = new ArrayList<InputListener>();

+

+        public Mapping(String name) {

+            this.name = name;

+        }

+    }

+

+    /**

+     * Initializes the InputManager.

+     * 

+     * <p>This should only be called internally in {@link Application}.

+     *

+     * @param mouse

+     * @param keys

+     * @param joystick

+     * @param touch

+     * @throws IllegalArgumentException If either mouseInput or keyInput are null.

+     */

+    public InputManager(MouseInput mouse, KeyInput keys, JoyInput joystick, TouchInput touch) {

+        if (keys == null || mouse == null) {

+            throw new NullPointerException("Mouse or keyboard cannot be null");

+        }

+

+        this.keys = keys;

+        this.mouse = mouse;

+        this.joystick = joystick;

+        this.touch = touch;

+

+        keys.setInputListener(this);

+        mouse.setInputListener(this);

+        if (joystick != null) {

+            joystick.setInputListener(this);

+            joysticks = joystick.loadJoysticks(this);

+        }

+        if (touch != null) {

+            touch.setInputListener(this);

+        }

+

+        firstTime = keys.getInputTimeNanos();

+    }

+

+    private void invokeActions(int hash, boolean pressed) {

+        ArrayList<Mapping> maps = bindings.get(hash);

+        if (maps == null) {

+            return;

+        }

+

+        int size = maps.size();

+        for (int i = size - 1; i >= 0; i--) {

+            Mapping mapping = maps.get(i);

+            ArrayList<InputListener> listeners = mapping.listeners;

+            int listenerSize = listeners.size();

+            for (int j = listenerSize - 1; j >= 0; j--) {

+                InputListener listener = listeners.get(j);

+                if (listener instanceof ActionListener) {

+                    ((ActionListener) listener).onAction(mapping.name, pressed, frameTPF);

+                }

+            }

+        }

+    }

+

+    private float computeAnalogValue(long timeDelta) {

+        if (safeMode || frameDelta == 0) {

+            return 1f;

+        } else {

+            return FastMath.clamp((float) timeDelta / (float) frameDelta, 0, 1);

+        }

+    }

+

+    private void invokeTimedActions(int hash, long time, boolean pressed) {

+        if (!bindings.containsKey(hash)) {

+            return;

+        }

+

+        if (pressed) {

+            pressedButtons.put(hash, time);

+        } else {

+            Long pressTimeObj = pressedButtons.remove(hash);

+            if (pressTimeObj == null) {

+                return; // under certain circumstances it can be null, ignore

+            }                        // the event then.

+

+            long pressTime = pressTimeObj;

+            long lastUpdate = lastLastUpdateTime;

+            long releaseTime = time;

+            long timeDelta = releaseTime - Math.max(pressTime, lastUpdate);

+

+            if (timeDelta > 0) {

+                invokeAnalogs(hash, computeAnalogValue(timeDelta), false);

+            }

+        }

+    }

+

+    private void invokeUpdateActions() {

+      if (pressedButtons.size() > 0) for (Entry<Long> pressedButton : pressedButtons) {

+            int hash = pressedButton.getKey();

+

+            long pressTime = pressedButton.getValue();

+            long timeDelta = lastUpdateTime - Math.max(lastLastUpdateTime, pressTime);

+

+            if (timeDelta > 0) {

+                invokeAnalogs(hash, computeAnalogValue(timeDelta), false);

+            }

+        }

+

+      if (axisValues.size() > 0) for (Entry<Float> axisValue : axisValues) {

+            int hash = axisValue.getKey();

+            float value = axisValue.getValue();

+            invokeAnalogs(hash, value * frameTPF, true);

+        }

+    }

+

+    private void invokeAnalogs(int hash, float value, boolean isAxis) {

+        ArrayList<Mapping> maps = bindings.get(hash);

+        if (maps == null) {

+            return;

+        }

+

+        if (!isAxis) {

+            value *= frameTPF;

+        }

+

+        int size = maps.size();

+        for (int i = size - 1; i >= 0; i--) {

+            Mapping mapping = maps.get(i);

+            ArrayList<InputListener> listeners = mapping.listeners;

+            int listenerSize = listeners.size();

+            for (int j = listenerSize - 1; j >= 0; j--) {

+                InputListener listener = listeners.get(j);

+                if (listener instanceof AnalogListener) {

+                    // NOTE: multiply by TPF for any button bindings

+                    ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);

+                }

+            }

+        }

+    }

+

+    private void invokeAnalogsAndActions(int hash, float value, boolean applyTpf) {

+        if (value < axisDeadZone) {

+            invokeAnalogs(hash, value, !applyTpf);

+            return;

+        }

+

+        ArrayList<Mapping> maps = bindings.get(hash);

+        if (maps == null) {

+            return;

+        }

+

+        boolean valueChanged = !axisValues.containsKey(hash);

+        if (applyTpf) {

+            value *= frameTPF;

+        }

+

+        int size = maps.size();

+        for (int i = size - 1; i >= 0; i--) {

+            Mapping mapping = maps.get(i);

+            ArrayList<InputListener> listeners = mapping.listeners;

+            int listenerSize = listeners.size();

+            for (int j = listenerSize - 1; j >= 0; j--) {

+                InputListener listener = listeners.get(j);

+

+                if (listener instanceof ActionListener && valueChanged) {

+                    ((ActionListener) listener).onAction(mapping.name, true, frameTPF);

+                }

+

+                if (listener instanceof AnalogListener) {

+                    ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);

+                }

+

+            }

+        }

+    }

+

+    /**

+     * Callback from RawInputListener. Do not use.

+     */

+    public void beginInput() {

+    }

+

+    /**

+     * Callback from RawInputListener. Do not use.

+     */

+    public void endInput() {

+    }

+

+    private void onJoyAxisEventQueued(JoyAxisEvent evt) {

+//        for (int i = 0; i < rawListeners.size(); i++){

+//            rawListeners.get(i).onJoyAxisEvent(evt);

+//        }

+

+        int joyId = evt.getJoyIndex();

+        int axis = evt.getAxisIndex();

+        float value = evt.getValue();

+        if (value < axisDeadZone && value > -axisDeadZone) {

+            int hash1 = JoyAxisTrigger.joyAxisHash(joyId, axis, true);

+            int hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false);

+

+            Float val1 = axisValues.get(hash1);

+            Float val2 = axisValues.get(hash2);

+

+            if (val1 != null && val1.floatValue() > axisDeadZone) {

+                invokeActions(hash1, false);

+            }

+            if (val2 != null && val2.floatValue() > axisDeadZone) {

+                invokeActions(hash2, false);

+            }

+

+            axisValues.remove(hash1);

+            axisValues.remove(hash2);

+

+        } else if (value < 0) {

+            int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);

+            int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);

+            invokeAnalogsAndActions(hash, -value, true);

+            axisValues.put(hash, -value);

+            axisValues.remove(otherHash);

+        } else {

+            int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);

+            int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);

+            invokeAnalogsAndActions(hash, value, true);

+            axisValues.put(hash, value);

+            axisValues.remove(otherHash);

+        }

+    }

+

+    /**

+     * Callback from RawInputListener. Do not use.

+     */

+    public void onJoyAxisEvent(JoyAxisEvent evt) {

+        if (!eventsPermitted) {

+            throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");

+        }

+

+        inputQueue.add(evt);

+    }

+

+    private void onJoyButtonEventQueued(JoyButtonEvent evt) {

+//        for (int i = 0; i < rawListeners.size(); i++){

+//            rawListeners.get(i).onJoyButtonEvent(evt);

+//        }

+

+        int hash = JoyButtonTrigger.joyButtonHash(evt.getJoyIndex(), evt.getButtonIndex());

+        invokeActions(hash, evt.isPressed());

+        invokeTimedActions(hash, evt.getTime(), evt.isPressed());

+    }

+

+    /**

+     * Callback from RawInputListener. Do not use.

+     */

+    public void onJoyButtonEvent(JoyButtonEvent evt) {

+        if (!eventsPermitted) {

+            throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");

+        }

+

+        inputQueue.add(evt);

+    }

+

+    private void onMouseMotionEventQueued(MouseMotionEvent evt) {

+//        for (int i = 0; i < rawListeners.size(); i++){

+//            rawListeners.get(i).onMouseMotionEvent(evt);

+//        }

+

+        if (evt.getDX() != 0) {

+            float val = Math.abs(evt.getDX()) / 1024f;

+            invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_X, evt.getDX() < 0), val, false);

+        }

+        if (evt.getDY() != 0) {

+            float val = Math.abs(evt.getDY()) / 1024f;

+            invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_Y, evt.getDY() < 0), val, false);

+        }

+        if (evt.getDeltaWheel() != 0) {

+            float val = Math.abs(evt.getDeltaWheel()) / 100f;

+            invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_WHEEL, evt.getDeltaWheel() < 0), val, false);

+        }

+    }

+

+    /**

+     * Callback from RawInputListener. Do not use.

+     */

+    public void onMouseMotionEvent(MouseMotionEvent evt) {

+        if (!eventsPermitted) {

+            throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");

+        }

+

+        cursorPos.set(evt.getX(), evt.getY());

+        inputQueue.add(evt);

+    }

+

+    private void onMouseButtonEventQueued(MouseButtonEvent evt) {

+        int hash = MouseButtonTrigger.mouseButtonHash(evt.getButtonIndex());

+        invokeActions(hash, evt.isPressed());

+        invokeTimedActions(hash, evt.getTime(), evt.isPressed());

+    }

+

+    /**

+     * Callback from RawInputListener. Do not use.

+     */

+    public void onMouseButtonEvent(MouseButtonEvent evt) {

+        if (!eventsPermitted) {

+            throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");

+        }

+        //updating cursor pos on click, so that non android touch events can properly update cursor position.

+        cursorPos.set(evt.getX(), evt.getY());

+        inputQueue.add(evt);

+    }

+

+    private void onKeyEventQueued(KeyInputEvent evt) {

+        if (evt.isRepeating()) {

+            return; // repeat events not used for bindings

+        }

+        

+        int hash = KeyTrigger.keyHash(evt.getKeyCode());

+        invokeActions(hash, evt.isPressed());

+        invokeTimedActions(hash, evt.getTime(), evt.isPressed());

+    }

+

+    /**

+     * Callback from RawInputListener. Do not use.

+     */

+    public void onKeyEvent(KeyInputEvent evt) {

+        if (!eventsPermitted) {

+            throw new UnsupportedOperationException("KeyInput has raised an event at an illegal time.");

+        }

+

+        inputQueue.add(evt);

+    }

+

+    /**

+     * Set the deadzone for joystick axes.

+     * 

+     * <p>{@link ActionListener#onAction(java.lang.String, boolean, float) }

+     * events will only be raised if the joystick axis value is greater than

+     * the <code>deadZone</code>.

+     * 

+     * @param deadZone the deadzone for joystick axes. 

+     */

+    public void setAxisDeadZone(float deadZone) {

+        this.axisDeadZone = deadZone;

+    }

+

+    /**

+     * Returns the deadzone for joystick axes.

+     * 

+     * @return the deadzone for joystick axes.

+     */

+    public float getAxisDeadZone() {

+        return axisDeadZone;

+    }

+    

+    /**

+     * Adds a new listener to receive events on the given mappings.

+     * 

+     * <p>The given InputListener will be registered to receive events

+     * on the specified mapping names. When a mapping raises an event, the

+     * listener will have its appropriate method invoked, either

+     * {@link ActionListener#onAction(java.lang.String, boolean, float) }

+     * or {@link AnalogListener#onAnalog(java.lang.String, float, float) }

+     * depending on which interface the <code>listener</code> implements. 

+     * If the listener implements both interfaces, then it will receive the

+     * appropriate event for each method.

+     * 

+     * @param listener The listener to register to receive input events.

+     * @param mappingNames The mapping names which the listener will receive

+     * events from.

+     * 

+     * @see InputManager#removeListener(com.jme3.input.controls.InputListener) 

+     */

+    public void addListener(InputListener listener, String... mappingNames) {

+        for (String mappingName : mappingNames) {

+            Mapping mapping = mappings.get(mappingName);

+            if (mapping == null) {

+                mapping = new Mapping(mappingName);

+                mappings.put(mappingName, mapping);

+            }

+            if (!mapping.listeners.contains(listener)) {

+                mapping.listeners.add(listener);

+            }

+        }

+    }

+

+    /**

+     * Removes a listener from receiving events.

+     * 

+     * <p>This will unregister the listener from any mappings that it

+     * was previously registered with via 

+     * {@link InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) }.

+     * 

+     * @param listener The listener to unregister.

+     * 

+     * @see InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) 

+     */

+    public void removeListener(InputListener listener) {

+        for (Mapping mapping : mappings.values()) {

+            mapping.listeners.remove(listener);

+        }

+    }

+

+    /**

+     * Create a new mapping to the given triggers.

+     * 

+     * <p>

+     * The given mapping will be assigned to the given triggers, when

+     * any of the triggers given raise an event, the listeners

+     * registered to the mappings will receive appropriate events.

+     * 

+     * @param mappingName The mapping name to assign.

+     * @param triggers The triggers to which the mapping is to be registered.

+     * 

+     * @see InputManager#deleteMapping(java.lang.String) 

+     */

+    public void addMapping(String mappingName, Trigger... triggers) {

+        Mapping mapping = mappings.get(mappingName);

+        if (mapping == null) {

+            mapping = new Mapping(mappingName);

+            mappings.put(mappingName, mapping);

+        }

+

+        for (Trigger trigger : triggers) {

+            int hash = trigger.triggerHashCode();

+            ArrayList<Mapping> names = bindings.get(hash);

+            if (names == null) {

+                names = new ArrayList<Mapping>();

+                bindings.put(hash, names);

+            }

+            if (!names.contains(mapping)) {

+                names.add(mapping);

+                mapping.triggers.add(hash);

+            } else {

+                logger.log(Level.WARNING, "Attempted to add mapping \"{0}\" twice to trigger.", mappingName);

+            }

+        }

+    }

+

+    /**

+     * Returns true if this InputManager has a mapping registered

+     * for the given mappingName.

+     *

+     * @param mappingName The mapping name to check.

+     *

+     * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) 

+     * @see InputManager#deleteMapping(java.lang.String) 

+     */ 

+    public boolean hasMapping(String mappingName) {

+        return mappings.containsKey(mappingName);

+    }

+    

+    /**

+     * Deletes a mapping from receiving trigger events.

+     * 

+     * <p>

+     * The given mapping will no longer be assigned to receive trigger

+     * events.

+     * 

+     * @param mappingName The mapping name to unregister.

+     * 

+     * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) 

+     */

+    public void deleteMapping(String mappingName) {

+        Mapping mapping = mappings.remove(mappingName);

+        if (mapping == null) {

+            throw new IllegalArgumentException("Cannot find mapping: " + mappingName);

+        }

+

+        ArrayList<Integer> triggers = mapping.triggers;

+        for (int i = triggers.size() - 1; i >= 0; i--) {

+            int hash = triggers.get(i);

+            ArrayList<Mapping> maps = bindings.get(hash);

+            maps.remove(mapping);

+        }

+    }

+

+    /**

+     * Deletes a specific trigger registered to a mapping.

+     * 

+     * <p>

+     * The given mapping will no longer receive events raised by the 

+     * trigger.

+     * 

+     * @param mappingName The mapping name to cease receiving events from the 

+     * trigger.

+     * @param trigger The trigger to no longer invoke events on the mapping.

+     */

+    public void deleteTrigger(String mappingName, Trigger trigger) {

+        Mapping mapping = mappings.get(mappingName);

+        if (mapping == null) {

+            throw new IllegalArgumentException("Cannot find mapping: " + mappingName);

+        }

+

+        ArrayList<Mapping> maps = bindings.get(trigger.triggerHashCode());

+        maps.remove(mapping);

+

+    }

+

+    /**

+     * Clears all the input mappings from this InputManager. 

+     * Consequently, also clears all of the

+     * InputListeners as well.

+     */

+    public void clearMappings() {

+        mappings.clear();

+        bindings.clear();

+        reset();

+    }

+

+    /**

+     * Do not use.

+     * Called to reset pressed keys or buttons when focus is restored.

+     */

+    public void reset() {

+        pressedButtons.clear();

+        axisValues.clear();

+    }

+

+    /**

+     * Returns whether the mouse cursor is visible or not.

+     * 

+     * <p>By default the cursor is visible.

+     * 

+     * @return whether the mouse cursor is visible or not.

+     * 

+     * @see InputManager#setCursorVisible(boolean) 

+     */

+    public boolean isCursorVisible() {

+        return mouseVisible;

+    }

+

+    /**

+     * Set whether the mouse cursor should be visible or not.

+     * 

+     * @param visible whether the mouse cursor should be visible or not.

+     */

+    public void setCursorVisible(boolean visible) {

+        if (mouseVisible != visible) {

+            mouseVisible = visible;

+            mouse.setCursorVisible(mouseVisible);

+        }

+    }

+

+    /**

+     * Returns the current cursor position. The position is relative to the

+     * bottom-left of the screen and is in pixels.

+     * 

+     * @return the current cursor position

+     */

+    public Vector2f getCursorPosition() {

+        return cursorPos;

+    }

+

+    /**

+     * Returns an array of all joysticks installed on the system.

+     * 

+     * @return an array of all joysticks installed on the system.

+     */

+    public Joystick[] getJoysticks() {

+        return joysticks;

+    }

+

+    /**

+     * Adds a {@link RawInputListener} to receive raw input events.

+     * 

+     * <p>

+     * Any raw input listeners registered to this <code>InputManager</code>

+     * will receive raw input events first, before they get handled

+     * by the <code>InputManager</code> itself. The listeners are 

+     * each processed in the order they were added, e.g. FIFO.

+     * <p>

+     * If a raw input listener has handled the event and does not wish

+     * other listeners down the list to process the event, it may set the

+     * {@link InputEvent#setConsumed() consumed flag} to indicate the 

+     * event was consumed and shouldn't be processed any further.

+     * The listener may do this either at each of the event callbacks 

+     * or at the {@link RawInputListener#endInput() } method.

+     * 

+     * @param listener A listener to receive raw input events.

+     * 

+     * @see RawInputListener

+     */

+    public void addRawInputListener(RawInputListener listener) {

+        rawListeners.add(listener);

+        rawListenerArray = null;

+    }

+

+    /**

+     * Removes a {@link RawInputListener} so that it no longer

+     * receives raw input events.

+     * 

+     * @param listener The listener to cease receiving raw input events.

+     * 

+     * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) 

+     */

+    public void removeRawInputListener(RawInputListener listener) {

+        rawListeners.remove(listener);

+        rawListenerArray = null;

+    }

+

+    /**

+     * Clears all {@link RawInputListener}s.

+     * 

+     * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) 

+     */

+    public void clearRawInputListeners() {

+        rawListeners.clear();

+        rawListenerArray = null;

+    }

+

+    private RawInputListener[] getRawListenerArray() {

+        if (rawListenerArray == null) 

+            rawListenerArray = rawListeners.toArray(new RawInputListener[rawListeners.size()]);

+        return rawListenerArray;

+    }

+    

+    /**

+     * Enable simulation of mouse events. Used for touchscreen input only.

+     * 

+     * @param value True to enable simulation of mouse events

+     */

+    public void setSimulateMouse(boolean value) {

+        if (touch != null) {

+            touch.setSimulateMouse(value);

+        }

+    }

+    /**

+     * Returns state of simulation of mouse events. Used for touchscreen input only.

+     *

+     */

+    public boolean getSimulateMouse() {

+        if (touch != null) {           

+            return touch.getSimulateMouse();

+        } else {

+            return false;

+        }

+    }

+    

+    /**

+     * Enable simulation of keyboard events. Used for touchscreen input only.

+     * 

+     * @param value True to enable simulation of keyboard events

+     */

+    public void setSimulateKeyboard(boolean value) {

+        if (touch != null) {

+            touch.setSimulateKeyboard(value);

+        }

+    }

+

+    private void processQueue() {

+        int queueSize = inputQueue.size();

+        RawInputListener[] array = getRawListenerArray();

+ 

+        for (RawInputListener listener : array) {

+            listener.beginInput();

+

+            for (int j = 0; j < queueSize; j++) {

+                InputEvent event = inputQueue.get(j);

+                if (event.isConsumed()) {

+                    continue;

+                }

+

+                if (event instanceof MouseMotionEvent) {

+                    listener.onMouseMotionEvent((MouseMotionEvent) event);

+                } else if (event instanceof KeyInputEvent) {

+                    listener.onKeyEvent((KeyInputEvent) event);

+                } else if (event instanceof MouseButtonEvent) {

+                    listener.onMouseButtonEvent((MouseButtonEvent) event);

+                } else if (event instanceof JoyAxisEvent) {

+                    listener.onJoyAxisEvent((JoyAxisEvent) event);

+                } else if (event instanceof JoyButtonEvent) {

+                    listener.onJoyButtonEvent((JoyButtonEvent) event);

+                } else if (event instanceof TouchEvent) {

+                    listener.onTouchEvent((TouchEvent) event);

+                } else {

+                    assert false;

+                }

+            }

+

+            listener.endInput();

+        }

+

+        for (int i = 0; i < queueSize; i++) {

+            InputEvent event = inputQueue.get(i);

+            if (event.isConsumed()) {

+                continue;

+            }

+

+            if (event instanceof MouseMotionEvent) {

+                onMouseMotionEventQueued((MouseMotionEvent) event);

+            } else if (event instanceof KeyInputEvent) {

+                onKeyEventQueued((KeyInputEvent) event);

+            } else if (event instanceof MouseButtonEvent) {

+                onMouseButtonEventQueued((MouseButtonEvent) event);

+            } else if (event instanceof JoyAxisEvent) {

+                onJoyAxisEventQueued((JoyAxisEvent) event);

+            } else if (event instanceof JoyButtonEvent) {

+                onJoyButtonEventQueued((JoyButtonEvent) event);

+            } else if (event instanceof TouchEvent) {

+                onTouchEventQueued((TouchEvent) event);

+            } else {

+                assert false;

+            }

+            // larynx, 2011.06.10 - flag event as reusable because

+            // the android input uses a non-allocating ringbuffer which

+            // needs to know when the event is not anymore in inputQueue

+            // and therefor can be reused.

+            event.setConsumed();

+        }

+

+        inputQueue.clear();

+    }

+

+    /**

+     * Updates the <code>InputManager</code>. 

+     * This will query current input devices and send

+     * appropriate events to registered listeners.

+     *

+     * @param tpf Time per frame value.

+     */

+    public void update(float tpf) {

+        frameTPF = tpf;

+        

+        // Activate safemode if the TPF value is so small

+        // that rounding errors are inevitable

+        safeMode = tpf < 0.015f;

+        

+        long currentTime = keys.getInputTimeNanos();

+        frameDelta = currentTime - lastUpdateTime;

+

+        eventsPermitted = true;

+

+        keys.update();

+        mouse.update();

+        if (joystick != null) {

+            joystick.update();

+        }

+        if (touch != null) {

+            touch.update();

+        }

+

+        eventsPermitted = false;

+

+        processQueue();

+        invokeUpdateActions();

+

+        lastLastUpdateTime = lastUpdateTime;

+        lastUpdateTime = currentTime;

+    }

+

+    /**

+     * Dispatches touch events to touch listeners

+     * @param evt The touch event to be dispatched to all onTouch listeners

+     */

+    public void onTouchEventQueued(TouchEvent evt) { 

+        ArrayList<Mapping> maps = bindings.get(TouchTrigger.touchHash(evt.getKeyCode()));

+        if (maps == null) {

+            return;

+        }

+

+        int size = maps.size();

+        for (int i = size - 1; i >= 0; i--) {

+            Mapping mapping = maps.get(i);

+            ArrayList<InputListener> listeners = mapping.listeners;

+            int listenerSize = listeners.size();

+            for (int j = listenerSize - 1; j >= 0; j--) {

+                InputListener listener = listeners.get(j);

+                if (listener instanceof TouchListener) {

+                    ((TouchListener) listener).onTouch(mapping.name, evt, frameTPF); 

+                }

+            }

+        }               

+    }

+    

+    /**

+     * Callback from RawInputListener. Do not use.

+     */

+    @Override

+    public void onTouchEvent(TouchEvent evt) {

+        if (!eventsPermitted) {

+            throw new UnsupportedOperationException("TouchInput has raised an event at an illegal time.");

+        }

+        inputQueue.add(evt);         

+    }

+}

diff --git a/engine/src/core/com/jme3/input/SoftTextDialogInput.java b/engine/src/core/com/jme3/input/SoftTextDialogInput.java
new file mode 100644
index 0000000..d166b97
--- /dev/null
+++ b/engine/src/core/com/jme3/input/SoftTextDialogInput.java
@@ -0,0 +1,13 @@
+package com.jme3.input;

+

+import com.jme3.input.controls.SoftTextDialogInputListener;

+

+public interface SoftTextDialogInput {

+

+    public static int TEXT_ENTRY_DIALOG = 0;

+    public static int NUMERIC_ENTRY_DIALOG = 1;

+    public static int NUMERIC_KEYPAD_DIALOG = 2;

+

+    public void requestDialog(int id, String title, String initialValue, SoftTextDialogInputListener listener);

+

+}

diff --git a/engine/src/core/com/jme3/input/TouchInput.java b/engine/src/core/com/jme3/input/TouchInput.java
index 2f45b44..e69c4b3 100644
--- a/engine/src/core/com/jme3/input/TouchInput.java
+++ b/engine/src/core/com/jme3/input/TouchInput.java
@@ -73,6 +73,12 @@
      * @param simulate if mouse events should be generated
      */
     public void setSimulateMouse(boolean simulate);
+    
+    /**
+     * Get if mouse events are generated
+     *
+     */
+    public boolean getSimulateMouse();
 
     /**
      * Set if keyboard events should be generated
diff --git a/engine/src/core/com/jme3/input/controls/SoftTextDialogInputListener.java b/engine/src/core/com/jme3/input/controls/SoftTextDialogInputListener.java
new file mode 100644
index 0000000..d33844d
--- /dev/null
+++ b/engine/src/core/com/jme3/input/controls/SoftTextDialogInputListener.java
@@ -0,0 +1,44 @@
+/*
+ * 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.input.controls;
+
+/**
+ *
+ * @author potterec (aka iwgeric)
+ */
+public interface SoftTextDialogInputListener {
+
+    public static int COMPLETE = 0;
+    public static int CANCEL = 1;
+
+    public void onSoftText(int action, String text);
+}
diff --git a/engine/src/core/com/jme3/math/Transform.java b/engine/src/core/com/jme3/math/Transform.java
index 7ccd847..3573d8c 100644
--- a/engine/src/core/com/jme3/math/Transform.java
+++ b/engine/src/core/com/jme3/math/Transform.java
@@ -1,318 +1,318 @@
-/*

- * 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.math;

-

-import com.jme3.export.*;

-import java.io.IOException;

-

-/**

- * Started Date: Jul 16, 2004<br><br>

- * Represents a translation, rotation and scale in one object.

- * 

- * @author Jack Lindamood

- * @author Joshua Slack

- */

-public final class Transform implements Savable, Cloneable, java.io.Serializable {

-

-    static final long serialVersionUID = 1;

-

-    public static final Transform IDENTITY = new Transform();

-

-    private Quaternion rot = new Quaternion();

-    private Vector3f translation = new Vector3f();

-    private Vector3f scale = new Vector3f(1,1,1);

-

-    public Transform(Vector3f translation, Quaternion rot){

-        this.translation.set(translation);

-        this.rot.set(rot);

-    }

-    

-    public Transform(Vector3f translation, Quaternion rot, Vector3f scale){

-        this(translation, rot);

-        this.scale.set(scale);

-    }

-

-    public Transform(Vector3f translation){

-        this(translation, Quaternion.IDENTITY);

-    }

-

-    public Transform(Quaternion rot){

-        this(Vector3f.ZERO, rot);

-    }

-

-    public Transform(){

-        this(Vector3f.ZERO, Quaternion.IDENTITY);

-    }

-

-    /**

-     * Sets this rotation to the given Quaternion value.

-     * @param rot The new rotation for this matrix.

-     * @return this

-     */

-    public Transform setRotation(Quaternion rot) {

-        this.rot.set(rot);

-        return this;

-    }

-

-    /**

-     * Sets this translation to the given value.

-     * @param trans The new translation for this matrix.

-     * @return this

-     */

-    public Transform setTranslation(Vector3f trans) {

-        this.translation.set(trans);

-        return this;

-    }

-

-    /**

-     * Return the translation vector in this matrix.

-     * @return translation vector.

-     */

-    public Vector3f getTranslation() {

-        return translation;

-    }

-

-    /**

-     * Sets this scale to the given value.

-     * @param scale The new scale for this matrix.

-     * @return this

-     */

-    public Transform setScale(Vector3f scale) {

-        this.scale.set(scale);

-        return this;

-    }

-

-    /**

-     * Sets this scale to the given value.

-     * @param scale The new scale for this matrix.

-     * @return this

-     */

-    public Transform setScale(float scale) {

-        this.scale.set(scale, scale, scale);

-        return this;

-    }

-

-    /**

-     * Return the scale vector in this matrix.

-     * @return scale vector.

-     */

-    public Vector3f getScale() {

-        return scale;

-    }

-

-    /**

-     * Stores this translation value into the given vector3f.  If trans is null, a new vector3f is created to

-     * hold the value.  The value, once stored, is returned.

-     * @param trans The store location for this matrix's translation.

-     * @return The value of this matrix's translation.

-     */

-    public Vector3f getTranslation(Vector3f trans) {

-        if (trans==null) trans=new Vector3f();

-        trans.set(this.translation);

-        return trans;

-    }

-

-    /**

-     * Stores this rotation value into the given Quaternion.  If quat is null, a new Quaternion is created to

-     * hold the value.  The value, once stored, is returned.

-     * @param quat The store location for this matrix's rotation.

-     * @return The value of this matrix's rotation.

-     */

-    public Quaternion getRotation(Quaternion quat) {

-        if (quat==null) quat=new Quaternion();

-        quat.set(rot);

-        return quat;

-    }

-    

-    /**

-     * Return the rotation quaternion in this matrix.

-     * @return rotation quaternion.

-     */

-    public Quaternion getRotation() {

-        return rot;

-    } 

-    

-    /**

-     * Stores this scale value into the given vector3f.  If scale is null, a new vector3f is created to

-     * hold the value.  The value, once stored, is returned.

-     * @param scale The store location for this matrix's scale.

-     * @return The value of this matrix's scale.

-     */

-    public Vector3f getScale(Vector3f scale) {

-        if (scale==null) scale=new Vector3f();

-        scale.set(this.scale);

-        return scale;

-    }

-

-    /**

-     * Sets this matrix to the interpolation between the first matrix and the second by delta amount.

-     * @param t1 The begining transform.

-     * @param t2 The ending transform.

-     * @param delta An amount between 0 and 1 representing how far to interpolate from t1 to t2.

-     */

-    public void interpolateTransforms(Transform t1, Transform t2, float delta) {

-        this.rot.slerp(t1.rot,t2.rot,delta);

-        this.translation.interpolate(t1.translation,t2.translation,delta);

-        this.scale.interpolate(t1.scale,t2.scale,delta);

-    }

-

-    /**

-     * Changes the values of this matrix acording to it's parent.  Very similar to the concept of Node/Spatial transforms.

-     * @param parent The parent matrix.

-     * @return This matrix, after combining.

-     */

-    public Transform combineWithParent(Transform parent) {

-        scale.multLocal(parent.scale);

-//        rot.multLocal(parent.rot);

-        parent.rot.mult(rot, rot);

-

-        // This here, is evil code

-//        parent

-//            .rot

-//            .multLocal(translation)

-//            .multLocal(parent.scale)

-//            .addLocal(parent.translation);

-

-        translation.multLocal(parent.scale);

-        parent

-            .rot

-            .multLocal(translation)

-            .addLocal(parent.translation);

-        return this;

-    }

-

-    /**

-     * Sets this matrix's translation to the given x,y,z values.

-     * @param x This matrix's new x translation.

-     * @param y This matrix's new y translation.

-     * @param z This matrix's new z translation.

-     * @return this

-     */

-    public Transform setTranslation(float x,float y, float z) {

-        translation.set(x,y,z);

-        return this;

-    }

-

-    /**

-     * Sets this matrix's scale to the given x,y,z values.

-     * @param x This matrix's new x scale.

-     * @param y This matrix's new y scale.

-     * @param z This matrix's new z scale.

-     * @return this

-     */

-    public Transform setScale(float x, float y, float z) {

-        scale.set(x,y,z);

-        return this;

-    }

-

-    public Vector3f transformVector(final Vector3f in, Vector3f store){

-        if (store == null)

-            store = new Vector3f();

-

-        // multiply with scale first, then rotate, finally translate (cf.

-        // Eberly)

-        return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation);

-    }

-

-    public Vector3f transformInverseVector(final Vector3f in, Vector3f store){

-        if (store == null)

-            store = new Vector3f();

-

-        // The author of this code should look above and take the inverse of that

-        // But for some reason, they didnt ..

-//        in.subtract(translation, store).divideLocal(scale);

-//        rot.inverse().mult(store, store);

-

-        in.subtract(translation, store);

-        rot.inverse().mult(store, store);

-        store.divideLocal(scale);

-

-        return store;

-    }

-

-    /**

-     * Loads the identity.  Equal to translation=1,1,1 scale=0,0,0 rot=0,0,0,1.

-     */

-    public void loadIdentity() {

-        translation.set(0,0,0);

-        scale.set(1,1,1);

-        rot.set(0,0,0,1);

-    }

-

-    @Override

-    public String toString(){

-        return getClass().getSimpleName() + "[ " + translation.x + ", " + translation.y + ", " + translation.z + "]\n"

-                                          + "[ " + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "]\n"

-                                          + "[ " + scale.x + " , " + scale.y + ", " + scale.z + "]";

-    }

-

-    /**

-     * Sets this matrix to be equal to the given matrix.

-     * @param matrixQuat The matrix to be equal to.

-     * @return this

-     */

-    public Transform set(Transform matrixQuat) {

-        this.translation.set(matrixQuat.translation);

-        this.rot.set(matrixQuat.rot);

-        this.scale.set(matrixQuat.scale);

-        return this;

-    }

-

-    public void write(JmeExporter e) throws IOException {

-        OutputCapsule capsule = e.getCapsule(this);

-        capsule.write(rot, "rot", new Quaternion());

-        capsule.write(translation, "translation", Vector3f.ZERO);

-        capsule.write(scale, "scale", Vector3f.UNIT_XYZ);

-    }

-

-    public void read(JmeImporter e) throws IOException {

-        InputCapsule capsule = e.getCapsule(this);

-        

-        rot = (Quaternion)capsule.readSavable("rot", new Quaternion());

-        translation = (Vector3f)capsule.readSavable("translation", Vector3f.ZERO);

-        scale = (Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ);

-    }

-    

-    @Override

-    public Transform clone() {

-        try {

-            Transform tq = (Transform) super.clone();

-            tq.rot = rot.clone();

-            tq.scale = scale.clone();

-            tq.translation = translation.clone();

-            return tq;

-        } catch (CloneNotSupportedException e) {

-            throw new AssertionError();

-        }

-    }

-}

+/*
+ * 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.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+
+/**
+ * Started Date: Jul 16, 2004<br><br>
+ * Represents a translation, rotation and scale in one object.
+ * 
+ * @author Jack Lindamood
+ * @author Joshua Slack
+ */
+public final class Transform implements Savable, Cloneable, java.io.Serializable {
+
+    static final long serialVersionUID = 1;
+
+    public static final Transform IDENTITY = new Transform();
+
+    private Quaternion rot = new Quaternion();
+    private Vector3f translation = new Vector3f();
+    private Vector3f scale = new Vector3f(1,1,1);
+
+    public Transform(Vector3f translation, Quaternion rot){
+        this.translation.set(translation);
+        this.rot.set(rot);
+    }
+    
+    public Transform(Vector3f translation, Quaternion rot, Vector3f scale){
+        this(translation, rot);
+        this.scale.set(scale);
+    }
+
+    public Transform(Vector3f translation){
+        this(translation, Quaternion.IDENTITY);
+    }
+
+    public Transform(Quaternion rot){
+        this(Vector3f.ZERO, rot);
+    }
+
+    public Transform(){
+        this(Vector3f.ZERO, Quaternion.IDENTITY);
+    }
+
+    /**
+     * Sets this rotation to the given Quaternion value.
+     * @param rot The new rotation for this matrix.
+     * @return this
+     */
+    public Transform setRotation(Quaternion rot) {
+        this.rot.set(rot);
+        return this;
+    }
+
+    /**
+     * Sets this translation to the given value.
+     * @param trans The new translation for this matrix.
+     * @return this
+     */
+    public Transform setTranslation(Vector3f trans) {
+        this.translation.set(trans);
+        return this;
+    }
+
+    /**
+     * Return the translation vector in this matrix.
+     * @return translation vector.
+     */
+    public Vector3f getTranslation() {
+        return translation;
+    }
+
+    /**
+     * Sets this scale to the given value.
+     * @param scale The new scale for this matrix.
+     * @return this
+     */
+    public Transform setScale(Vector3f scale) {
+        this.scale.set(scale);
+        return this;
+    }
+
+    /**
+     * Sets this scale to the given value.
+     * @param scale The new scale for this matrix.
+     * @return this
+     */
+    public Transform setScale(float scale) {
+        this.scale.set(scale, scale, scale);
+        return this;
+    }
+
+    /**
+     * Return the scale vector in this matrix.
+     * @return scale vector.
+     */
+    public Vector3f getScale() {
+        return scale;
+    }
+
+    /**
+     * Stores this translation value into the given vector3f.  If trans is null, a new vector3f is created to
+     * hold the value.  The value, once stored, is returned.
+     * @param trans The store location for this matrix's translation.
+     * @return The value of this matrix's translation.
+     */
+    public Vector3f getTranslation(Vector3f trans) {
+        if (trans==null) trans=new Vector3f();
+        trans.set(this.translation);
+        return trans;
+    }
+
+    /**
+     * Stores this rotation value into the given Quaternion.  If quat is null, a new Quaternion is created to
+     * hold the value.  The value, once stored, is returned.
+     * @param quat The store location for this matrix's rotation.
+     * @return The value of this matrix's rotation.
+     */
+    public Quaternion getRotation(Quaternion quat) {
+        if (quat==null) quat=new Quaternion();
+        quat.set(rot);
+        return quat;
+    }
+    
+    /**
+     * Return the rotation quaternion in this matrix.
+     * @return rotation quaternion.
+     */
+    public Quaternion getRotation() {
+        return rot;
+    } 
+    
+    /**
+     * Stores this scale value into the given vector3f.  If scale is null, a new vector3f is created to
+     * hold the value.  The value, once stored, is returned.
+     * @param scale The store location for this matrix's scale.
+     * @return The value of this matrix's scale.
+     */
+    public Vector3f getScale(Vector3f scale) {
+        if (scale==null) scale=new Vector3f();
+        scale.set(this.scale);
+        return scale;
+    }
+
+    /**
+     * Sets this matrix to the interpolation between the first matrix and the second by delta amount.
+     * @param t1 The begining transform.
+     * @param t2 The ending transform.
+     * @param delta An amount between 0 and 1 representing how far to interpolate from t1 to t2.
+     */
+    public void interpolateTransforms(Transform t1, Transform t2, float delta) {
+        this.rot.slerp(t1.rot,t2.rot,delta);
+        this.translation.interpolate(t1.translation,t2.translation,delta);
+        this.scale.interpolate(t1.scale,t2.scale,delta);
+    }
+
+    /**
+     * Changes the values of this matrix acording to it's parent.  Very similar to the concept of Node/Spatial transforms.
+     * @param parent The parent matrix.
+     * @return This matrix, after combining.
+     */
+    public Transform combineWithParent(Transform parent) {
+        scale.multLocal(parent.scale);
+//        rot.multLocal(parent.rot);
+        parent.rot.mult(rot, rot);
+
+        // This here, is evil code
+//        parent
+//            .rot
+//            .multLocal(translation)
+//            .multLocal(parent.scale)
+//            .addLocal(parent.translation);
+
+        translation.multLocal(parent.scale);
+        parent
+            .rot
+            .multLocal(translation)
+            .addLocal(parent.translation);
+        return this;
+    }
+
+    /**
+     * Sets this matrix's translation to the given x,y,z values.
+     * @param x This matrix's new x translation.
+     * @param y This matrix's new y translation.
+     * @param z This matrix's new z translation.
+     * @return this
+     */
+    public Transform setTranslation(float x,float y, float z) {
+        translation.set(x,y,z);
+        return this;
+    }
+
+    /**
+     * Sets this matrix's scale to the given x,y,z values.
+     * @param x This matrix's new x scale.
+     * @param y This matrix's new y scale.
+     * @param z This matrix's new z scale.
+     * @return this
+     */
+    public Transform setScale(float x, float y, float z) {
+        scale.set(x,y,z);
+        return this;
+    }
+
+    public Vector3f transformVector(final Vector3f in, Vector3f store){
+        if (store == null)
+            store = new Vector3f();
+
+        // multiply with scale first, then rotate, finally translate (cf.
+        // Eberly)
+        return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation);
+    }
+
+    public Vector3f transformInverseVector(final Vector3f in, Vector3f store){
+        if (store == null)
+            store = new Vector3f();
+
+        // The author of this code should look above and take the inverse of that
+        // But for some reason, they didnt ..
+//        in.subtract(translation, store).divideLocal(scale);
+//        rot.inverse().mult(store, store);
+
+        in.subtract(translation, store);
+        rot.inverse().mult(store, store);
+        store.divideLocal(scale);
+
+        return store;
+    }
+
+    /**
+     * Loads the identity.  Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1.
+     */
+    public void loadIdentity() {
+        translation.set(0,0,0);
+        scale.set(1,1,1);
+        rot.set(0,0,0,1);
+    }
+
+    @Override
+    public String toString(){
+        return getClass().getSimpleName() + "[ " + translation.x + ", " + translation.y + ", " + translation.z + "]\n"
+                                          + "[ " + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "]\n"
+                                          + "[ " + scale.x + " , " + scale.y + ", " + scale.z + "]";
+    }
+
+    /**
+     * Sets this matrix to be equal to the given matrix.
+     * @param matrixQuat The matrix to be equal to.
+     * @return this
+     */
+    public Transform set(Transform matrixQuat) {
+        this.translation.set(matrixQuat.translation);
+        this.rot.set(matrixQuat.rot);
+        this.scale.set(matrixQuat.scale);
+        return this;
+    }
+
+    public void write(JmeExporter e) throws IOException {
+        OutputCapsule capsule = e.getCapsule(this);
+        capsule.write(rot, "rot", new Quaternion());
+        capsule.write(translation, "translation", Vector3f.ZERO);
+        capsule.write(scale, "scale", Vector3f.UNIT_XYZ);
+    }
+
+    public void read(JmeImporter e) throws IOException {
+        InputCapsule capsule = e.getCapsule(this);
+        
+        rot = (Quaternion)capsule.readSavable("rot", new Quaternion());
+        translation = (Vector3f)capsule.readSavable("translation", Vector3f.ZERO);
+        scale = (Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ);
+    }
+    
+    @Override
+    public Transform clone() {
+        try {
+            Transform tq = (Transform) super.clone();
+            tq.rot = rot.clone();
+            tq.scale = scale.clone();
+            tq.translation = translation.clone();
+            return tq;
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError();
+        }
+    }
+}
diff --git a/engine/src/core/com/jme3/post/Filter.java b/engine/src/core/com/jme3/post/Filter.java
index ef9a6ff..12babf5 100644
--- a/engine/src/core/com/jme3/post/Filter.java
+++ b/engine/src/core/com/jme3/post/Filter.java
@@ -40,6 +40,7 @@
 import com.jme3.renderer.ViewPort;

 import com.jme3.texture.FrameBuffer;

 import com.jme3.texture.Image.Format;

+import com.jme3.texture.Texture;

 import com.jme3.texture.Texture2D;

 import java.io.IOException;

 import java.util.Collection;

@@ -236,7 +237,7 @@
      * cleanup this filter

      * @param r

      */

-    protected final void cleanup(Renderer r) {

+    protected final void cleanup(Renderer r) {   

         processor = null;

         if (defaultPass != null) {

             defaultPass.cleanup(r);

@@ -269,8 +270,6 @@
     protected void cleanUpFilter(Renderer r) {

     }

 

-    ;

-

     /**

      * Must return the material used for this filter.

      * this method is called every frame.

@@ -278,6 +277,14 @@
      * @return the material used for this filter.

      */

     protected abstract Material getMaterial();

+    

+    /**

+     * Override if you want to do something special with the depth texture;

+     * @param depthTexture 

+     */

+    protected void setDepthTexture(Texture depthTexture){

+        getMaterial().setTexture("DepthTexture", depthTexture);

+    }

 

     /**

      * Override this method if you want to make a pre pass, before the actual rendering of the frame

diff --git a/engine/src/core/com/jme3/post/FilterPostProcessor.java b/engine/src/core/com/jme3/post/FilterPostProcessor.java
index 2e48f0f..d97e8e9 100644
--- a/engine/src/core/com/jme3/post/FilterPostProcessor.java
+++ b/engine/src/core/com/jme3/post/FilterPostProcessor.java
@@ -77,7 +77,7 @@
     private int originalHeight;

     private int lastFilterIndex = -1;

     private boolean cameraInit = false;

-

+    

     /**

      * Create a FilterProcessor 

      * @param assetManager the assetManager

@@ -98,8 +98,7 @@
      * @param filter the filter to add

      */

     public void addFilter(Filter filter) {

-        filters.add(filter);

-        filter.setProcessor(this);

+        filters.add(filter);        

 

         if (isInitialized()) {

             initFilter(filter, viewPort);

@@ -148,14 +147,17 @@
      * @param vp 

      */

     private void initFilter(Filter filter, ViewPort vp) {

-        filter.init(assetManager, renderManager, vp, width, height);

+        filter.setProcessor(this);

         if (filter.isRequiresDepthTexture()) {

-            if (!computeDepth && renderFrameBuffer != null) {

+            if (!computeDepth && renderFrameBuffer != null) {                

                 depthTexture = new Texture2D(width, height, Format.Depth24);

                 renderFrameBuffer.setDepthTexture(depthTexture);

             }

             computeDepth = true;

-            filter.getMaterial().setTexture("DepthTexture", depthTexture);

+            filter.init(assetManager, renderManager, vp, width, height);

+            filter.setDepthTexture(depthTexture);

+        } else {

+            filter.init(assetManager, renderManager, vp, width, height);

         }

     }

 

@@ -281,9 +283,9 @@
         } else if (renderFrameBufferMS != null) {

             sceneBuffer = renderFrameBufferMS;

         }

-        renderFilterChain(renderer, sceneBuffer);        

+        renderFilterChain(renderer, sceneBuffer);

         renderer.setFrameBuffer(outputBuffer);

-        

+

         //viewport can be null if no filters are enabled

         if (viewPort != null) {

             renderManager.setCamera(viewPort.getCamera(), false);

@@ -356,8 +358,11 @@
             //reseting the viewport camera viewport to its initial value

             viewPort.getCamera().resize(originalWidth, originalHeight, true);

             viewPort.getCamera().setViewPort(left, right, bottom, top);

-            viewPort.setOutputFrameBuffer(outputBuffer);          

+            viewPort.setOutputFrameBuffer(outputBuffer);            

             viewPort = null;

+            for (Filter filter : filters) {

+                filter.cleanup(renderer);

+            }

         }

 

     }

@@ -484,7 +489,7 @@
      * For internal use only<br>

      * returns the depth texture of the scene

      * @return 

-     */

+     */    

     public Texture2D getDepthTexture() {

         return depthTexture;

     }

diff --git a/engine/src/core/com/jme3/renderer/RenderManager.java b/engine/src/core/com/jme3/renderer/RenderManager.java
index 1d58d22..a0f5fdf 100644
--- a/engine/src/core/com/jme3/renderer/RenderManager.java
+++ b/engine/src/core/com/jme3/renderer/RenderManager.java
@@ -1,1170 +1,1178 @@
-/*
- * Copyright (c) 2009-2012 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.renderer;
-
-import com.jme3.material.Material;
-import com.jme3.material.MaterialDef;
-import com.jme3.material.RenderState;
-import com.jme3.material.Technique;
-import com.jme3.math.*;
-import com.jme3.post.SceneProcessor;
-import com.jme3.renderer.queue.GeometryList;
-import com.jme3.renderer.queue.RenderQueue;
-import com.jme3.renderer.queue.RenderQueue.Bucket;
-import com.jme3.renderer.queue.RenderQueue.ShadowMode;
-import com.jme3.scene.*;
-import com.jme3.shader.Uniform;
-import com.jme3.shader.UniformBinding;
-import com.jme3.shader.VarType;
-import com.jme3.system.NullRenderer;
-import com.jme3.system.Timer;
-import com.jme3.util.IntMap.Entry;
-import com.jme3.util.TempVars;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.logging.Logger;
-
-/**
- * <code>RenderManager</code> is a high-level rendering interface that is
- * above the Renderer implementation. RenderManager takes care
- * of rendering the scene graphs attached to each viewport and
- * handling SceneProcessors.
- *
- * @see SceneProcessor
- * @see ViewPort
- * @see Spatial
- */
-public class RenderManager {
-
-    private static final Logger logger = Logger.getLogger(RenderManager.class.getName());
-    
-    private Renderer renderer;
-    private Timer timer;
-    private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>();
-    private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
-    private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>();
-    private Camera prevCam = null;
-    private Material forcedMaterial = null;
-    private String forcedTechnique = null;
-    private RenderState forcedRenderState = null;
-    private boolean shader;
-    private int viewX, viewY, viewWidth, viewHeight;
-    private float near, far;
-    private Matrix4f orthoMatrix = new Matrix4f();
-    private Matrix4f viewMatrix = new Matrix4f();
-    private Matrix4f projMatrix = new Matrix4f();
-    private Matrix4f viewProjMatrix = new Matrix4f();
-    private Matrix4f worldMatrix = new Matrix4f();
-    private Vector3f camUp = new Vector3f(),
-            camLeft = new Vector3f(),
-            camDir = new Vector3f(),
-            camLoc = new Vector3f();
-    //temp technique
-    private String tmpTech;
-    private boolean handleTranlucentBucket = true;
-
-    /**
-     * Create a high-level rendering interface over the
-     * low-level rendering interface.
-     * @param renderer
-     */
-    public RenderManager(Renderer renderer) {
-        this.renderer = renderer;
-        //this.shader = renderer.getCaps().contains(Caps.GLSL100);
-    }
-
-    /**
-     * Returns the pre ViewPort with the given name.
-     * 
-     * @param viewName The name of the pre ViewPort to look up
-     * @return The ViewPort, or null if not found.
-     * 
-     * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) 
-     */
-    public ViewPort getPreView(String viewName) {
-        for (int i = 0; i < preViewPorts.size(); i++) {
-            if (preViewPorts.get(i).getName().equals(viewName)) {
-                return preViewPorts.get(i);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Removes the specified pre ViewPort.
-     * 
-     * @param view The pre ViewPort to remove
-     * @return True if the ViewPort was removed successfully.
-     * 
-     * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) 
-     */
-    public boolean removePreView(ViewPort view) {
-        return preViewPorts.remove(view);
-    }
-
-    /**
-     * Returns the main ViewPort with the given name.
-     * 
-     * @param viewName The name of the main ViewPort to look up
-     * @return The ViewPort, or null if not found.
-     * 
-     * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) 
-     */
-    public ViewPort getMainView(String viewName) {
-        for (int i = 0; i < viewPorts.size(); i++) {
-            if (viewPorts.get(i).getName().equals(viewName)) {
-                return viewPorts.get(i);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Removes the main ViewPort with the specified name.
-     * 
-     * @param viewName The main ViewPort name to remove
-     * @return True if the ViewPort was removed successfully.
-     * 
-     * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) 
-     */
-    public boolean removeMainView(String viewName) {
-        for (int i = 0; i < viewPorts.size(); i++) {
-            if (viewPorts.get(i).getName().equals(viewName)) {
-                viewPorts.remove(i);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Removes the specified main ViewPort.
-     * 
-     * @param view The main ViewPort to remove
-     * @return True if the ViewPort was removed successfully.
-     * 
-     * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) 
-     */
-    public boolean removeMainView(ViewPort view) {
-        return viewPorts.remove(view);
-    }
-
-    /**
-     * Returns the post ViewPort with the given name.
-     * 
-     * @param viewName The name of the post ViewPort to look up
-     * @return The ViewPort, or null if not found.
-     * 
-     * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) 
-     */
-    public ViewPort getPostView(String viewName) {
-        for (int i = 0; i < postViewPorts.size(); i++) {
-            if (postViewPorts.get(i).getName().equals(viewName)) {
-                return postViewPorts.get(i);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Removes the post ViewPort with the specified name.
-     * 
-     * @param viewName The post ViewPort name to remove
-     * @return True if the ViewPort was removed successfully.
-     * 
-     * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) 
-     */
-    public boolean removePostView(String viewName) {
-        for (int i = 0; i < postViewPorts.size(); i++) {
-            if (postViewPorts.get(i).getName().equals(viewName)) {
-                postViewPorts.remove(i);
-
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Removes the specified post ViewPort.
-     * 
-     * @param view The post ViewPort to remove
-     * @return True if the ViewPort was removed successfully.
-     * 
-     * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) 
-     */
-    public boolean removePostView(ViewPort view) {
-        return postViewPorts.remove(view);
-    }
-
-    /**
-     * Returns a read-only list of all pre ViewPorts
-     * @return a read-only list of all pre ViewPorts
-     * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) 
-     */
-    public List<ViewPort> getPreViews() {
-        return Collections.unmodifiableList(preViewPorts);
-    }
-
-    /**
-     * Returns a read-only list of all main ViewPorts
-     * @return a read-only list of all main ViewPorts
-     * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) 
-     */
-    public List<ViewPort> getMainViews() {
-        return Collections.unmodifiableList(viewPorts);
-    }
-
-    /**
-     * Returns a read-only list of all post ViewPorts
-     * @return a read-only list of all post ViewPorts
-     * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) 
-     */
-    public List<ViewPort> getPostViews() {
-        return Collections.unmodifiableList(postViewPorts);
-    }
-
-    /**
-     * Creates a new pre ViewPort, to display the given camera's content.
-     * <p>
-     * The view will be processed before the main and post viewports.
-     */
-    public ViewPort createPreView(String viewName, Camera cam) {
-        ViewPort vp = new ViewPort(viewName, cam);
-        preViewPorts.add(vp);
-        return vp;
-    }
-
-    /**
-     * Creates a new main ViewPort, to display the given camera's content.
-     * <p>
-     * The view will be processed before the post viewports but after
-     * the pre viewports.
-     */
-    public ViewPort createMainView(String viewName, Camera cam) {
-        ViewPort vp = new ViewPort(viewName, cam);
-        viewPorts.add(vp);
-        return vp;
-    }
-
-    /**
-     * Creates a new post ViewPort, to display the given camera's content.
-     * <p>
-     * The view will be processed after the pre and main viewports.
-     */
-    public ViewPort createPostView(String viewName, Camera cam) {
-        ViewPort vp = new ViewPort(viewName, cam);
-        postViewPorts.add(vp);
-        return vp;
-    }
-
-    private void notifyReshape(ViewPort vp, int w, int h) {
-        List<SceneProcessor> processors = vp.getProcessors();
-        for (SceneProcessor proc : processors) {
-            if (!proc.isInitialized()) {
-                proc.initialize(this, vp);
-            } else {
-                proc.reshape(vp, w, h);
-            }
-        }
-    }
-
-    /**
-     * Internal use only.
-     * Updates the resolution of all on-screen cameras to match
-     * the given width and height.
-     */
-    public void notifyReshape(int w, int h) {
-        for (ViewPort vp : preViewPorts) {
-            if (vp.getOutputFrameBuffer() == null) {
-                Camera cam = vp.getCamera();
-                cam.resize(w, h, true);
-            }
-            notifyReshape(vp, w, h);
-        }
-        for (ViewPort vp : viewPorts) {
-            if (vp.getOutputFrameBuffer() == null) {
-                Camera cam = vp.getCamera();
-                cam.resize(w, h, true);
-            }
-            notifyReshape(vp, w, h);
-        }
-        for (ViewPort vp : postViewPorts) {
-            if (vp.getOutputFrameBuffer() == null) {
-                Camera cam = vp.getCamera();
-                cam.resize(w, h, true);
-            }
-            notifyReshape(vp, w, h);
-        }
-    }
-
-    /**
-     * Internal use only.
-     * Updates the given list of uniforms with {@link UniformBinding uniform bindings}
-     * based on the current world state.
-     */
-    public void updateUniformBindings(List<Uniform> params) {
-        // assums worldMatrix is properly set.
-        TempVars vars = TempVars.get();
-
-        Matrix4f tempMat4 = vars.tempMat4;
-        Matrix3f tempMat3 = vars.tempMat3;
-        Vector2f tempVec2 = vars.vect2d;
-        Quaternion tempVec4 = vars.quat1;
-
-        for (int i = 0; i < params.size(); i++) {
-            Uniform u = params.get(i);
-            switch (u.getBinding()) {
-                case WorldMatrix:
-                    u.setValue(VarType.Matrix4, worldMatrix);
-                    break;
-                case ViewMatrix:
-                    u.setValue(VarType.Matrix4, viewMatrix);
-                    break;
-                case ProjectionMatrix:
-                    u.setValue(VarType.Matrix4, projMatrix);
-                    break;
-                case ViewProjectionMatrix:
-                    u.setValue(VarType.Matrix4, viewProjMatrix);
-                    break;
-                case WorldViewMatrix:
-                    tempMat4.set(viewMatrix);
-                    tempMat4.multLocal(worldMatrix);
-                    u.setValue(VarType.Matrix4, tempMat4);
-                    break;
-                case NormalMatrix:
-                    tempMat4.set(viewMatrix);
-                    tempMat4.multLocal(worldMatrix);
-                    tempMat4.toRotationMatrix(tempMat3);
-                    tempMat3.invertLocal();
-                    tempMat3.transposeLocal();
-                    u.setValue(VarType.Matrix3, tempMat3);
-                    break;
-                case WorldViewProjectionMatrix:
-                    tempMat4.set(viewProjMatrix);
-                    tempMat4.multLocal(worldMatrix);
-                    u.setValue(VarType.Matrix4, tempMat4);
-                    break;
-                case WorldMatrixInverse:
-                    tempMat4.multLocal(worldMatrix);
-                    tempMat4.invertLocal();
-                    u.setValue(VarType.Matrix4, tempMat4);
-                    break;
-                case ViewMatrixInverse:
-                    tempMat4.set(viewMatrix);
-                    tempMat4.invertLocal();
-                    u.setValue(VarType.Matrix4, tempMat4);
-                    break;
-                case ProjectionMatrixInverse:
-                    tempMat4.set(projMatrix);
-                    tempMat4.invertLocal();
-                    u.setValue(VarType.Matrix4, tempMat4);
-                    break;
-                case ViewProjectionMatrixInverse:
-                    tempMat4.set(viewProjMatrix);
-                    tempMat4.invertLocal();
-                    u.setValue(VarType.Matrix4, tempMat4);
-                    break;
-                case WorldViewMatrixInverse:
-                    tempMat4.set(viewMatrix);
-                    tempMat4.multLocal(worldMatrix);
-                    tempMat4.invertLocal();
-                    u.setValue(VarType.Matrix4, tempMat4);
-                    break;
-                case NormalMatrixInverse:
-                    tempMat4.set(viewMatrix);
-                    tempMat4.multLocal(worldMatrix);
-                    tempMat4.toRotationMatrix(tempMat3);
-                    tempMat3.invertLocal();
-                    tempMat3.transposeLocal();
-                    tempMat3.invertLocal();
-                    u.setValue(VarType.Matrix3, tempMat3);
-                    break;
-                case WorldViewProjectionMatrixInverse:
-                    tempMat4.set(viewProjMatrix);
-                    tempMat4.multLocal(worldMatrix);
-                    tempMat4.invertLocal();
-                    u.setValue(VarType.Matrix4, tempMat4);
-                    break;
-                case ViewPort:
-                    tempVec4.set(viewX, viewY, viewWidth, viewHeight);
-                    u.setValue(VarType.Vector4, tempVec4);
-                    break;
-                case Resolution:
-                    tempVec2.set(viewWidth, viewHeight);
-                    u.setValue(VarType.Vector2, tempVec2);
-                    break;
-                case Aspect:
-                    float aspect = ((float) viewWidth) / viewHeight;
-                    u.setValue(VarType.Float, aspect);
-                    break;
-                case FrustumNearFar:
-                    tempVec2.set(near, far);
-                    u.setValue(VarType.Vector2, tempVec2);
-                    break;
-                case CameraPosition:
-                    u.setValue(VarType.Vector3, camLoc);
-                    break;
-                case CameraDirection:
-                    u.setValue(VarType.Vector3, camDir);
-                    break;
-                case CameraLeft:
-                    u.setValue(VarType.Vector3, camLeft);
-                    break;
-                case CameraUp:
-                    u.setValue(VarType.Vector3, camUp);
-                    break;
-                case Time:
-                    u.setValue(VarType.Float, timer.getTimeInSeconds());
-                    break;
-                case Tpf:
-                    u.setValue(VarType.Float, timer.getTimePerFrame());
-                    break;
-                case FrameRate:
-                    u.setValue(VarType.Float, timer.getFrameRate());
-                    break;
-            }
-        }
-
-        vars.release();
-    }
-
-    /**
-     * Set the material to use to render all future objects.
-     * This overrides the material set on the geometry and renders
-     * with the provided material instead.
-     * Use null to clear the material and return renderer to normal
-     * functionality.
-     * @param mat The forced material to set, or null to return to normal
-     */
-    public void setForcedMaterial(Material mat) {
-        forcedMaterial = mat;
-    }
-
-    /**
-     * Returns the forced render state previously set with 
-     * {@link #setForcedRenderState(com.jme3.material.RenderState) }.
-     * @return the forced render state
-     */
-    public RenderState getForcedRenderState() {
-        return forcedRenderState;
-    }
-
-    /**
-     * Set the render state to use for all future objects.
-     * This overrides the render state set on the material and instead
-     * forces this render state to be applied for all future materials
-     * rendered. Set to null to return to normal functionality.
-     * 
-     * @param forcedRenderState The forced render state to set, or null
-     * to return to normal
-     */
-    public void setForcedRenderState(RenderState forcedRenderState) {
-        this.forcedRenderState = forcedRenderState;
-    }
-
-    /**
-     * Set the timer that should be used to query the time based
-     * {@link UniformBinding}s for material world parameters.
-     * 
-     * @param timer The timer to query time world parameters
-     */
-    public void setTimer(Timer timer) {
-        this.timer = timer;
-    }
-
-    /**
-     * Returns the forced technique name set.
-     * 
-     * @return the forced technique name set.
-     * 
-     * @see #setForcedTechnique(java.lang.String) 
-     */
-    public String getForcedTechnique() {
-        return forcedTechnique;
-    }
-
-    /**
-     * Sets the forced technique to use when rendering geometries.
-     * <p>
-     * If the specified technique name is available on the geometry's
-     * material, then it is used, otherwise, the 
-     * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used.
-     * If a forced material is not set and the forced technique name cannot
-     * be found on the material, the geometry will <em>not</em> be rendered.
-     * 
-     * @param forcedTechnique The forced technique name to use, set to null
-     * to return to normal functionality.
-     * 
-     * @see #renderGeometry(com.jme3.scene.Geometry) 
-     */
-    public void setForcedTechnique(String forcedTechnique) {
-        this.forcedTechnique = forcedTechnique;
-    }
-
-    /**
-     * Enable or disable alpha-to-coverage. 
-     * <p>
-     * When alpha to coverage is enabled and the renderer implementation
-     * supports it, then alpha blending will be replaced with alpha dissolve
-     * if multi-sampling is also set on the renderer.
-     * This feature allows avoiding of alpha blending artifacts due to
-     * lack of triangle-level back-to-front sorting.
-     * 
-     * @param value True to enable alpha-to-coverage, false otherwise.
-     */
-    public void setAlphaToCoverage(boolean value) {
-        renderer.setAlphaToCoverage(value);
-    }
-
-    /**
-     * True if the translucent bucket should automatically be rendered
-     * by the RenderManager.
-     * 
-     * @return Whether or not the translucent bucket is rendered.
-     * 
-     * @see #setHandleTranslucentBucket(boolean) 
-     */
-    public boolean isHandleTranslucentBucket() {
-        return handleTranlucentBucket;
-    }
-
-    /**
-     * Enable or disable rendering of the 
-     * {@link Bucket#Translucent translucent bucket}
-     * by the RenderManager. The default is enabled.
-     * 
-     * @param handleTranslucentBucket Whether or not the translucent bucket should
-     * be rendered.
-     */
-    public void setHandleTranslucentBucket(boolean handleTranslucentBucket) {
-        this.handleTranlucentBucket = handleTranslucentBucket;
-    }
-
-    /**
-     * Internal use only. Sets the world matrix to use for future
-     * rendering. This has no effect unless objects are rendered manually
-     * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }.
-     * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will 
-     * override this value.
-     * 
-     * @param mat The world matrix to set
-     */
-    public void setWorldMatrix(Matrix4f mat) {
-        if (shader) {
-            worldMatrix.set(mat);
-        } else {
-            renderer.setWorldMatrix(mat);
-        }
-    }
-
-    /**
-     * Renders the given geometry.
-     * <p>
-     * First the proper world matrix is set, if 
-     * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform}
-     * feature is enabled, the identity world matrix is used, otherwise, the 
-     * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used. 
-     * <p>
-     * Once the world matrix is applied, the proper material is chosen for rendering.
-     * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is
-     * set on this RenderManager, then it is used for rendering the geometry,
-     * otherwise, the {@link Geometry#getMaterial() geometry's material} is used.
-     * <p>
-     * If a {@link #setForcedTechnique(java.lang.String) forced technique} is
-     * set on this RenderManager, then it is selected automatically
-     * on the geometry's material and is used for rendering. Otherwise, one
-     * of the {@link MaterialDef#getDefaultTechniques() default techniques} is
-     * used.
-     * <p>
-     * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced
-     * render state} is set on this RenderManager, then it is used
-     * for rendering the material, and the material's own render state is ignored.
-     * Otherwise, the material's render state is used as intended.
-     * 
-     * @param g The geometry to render
-     * 
-     * @see Technique
-     * @see RenderState
-     * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) 
-     * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) 
-     */
-    public void renderGeometry(Geometry g) {
-        if (g.isIgnoreTransform()) {
-            setWorldMatrix(Matrix4f.IDENTITY);
-        } else {
-            setWorldMatrix(g.getWorldMatrix());
-        }
-
-        //if forcedTechnique we try to force it for render,
-        //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null
-        //else the geom is not rendered
-        if (forcedTechnique != null) {
-            if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) {
-                tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default";
-                g.getMaterial().selectTechnique(forcedTechnique, this);
-                // use geometry's material
-                g.getMaterial().render(g, this);
-                g.getMaterial().selectTechnique(tmpTech, this);
-                //Reverted this part from revision 6197
-                //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered
-            } else if (forcedMaterial != null) {
-                // use forced material
-                forcedMaterial.render(g, this);
-            }
-        } else if (forcedMaterial != null) {
-            // use forced material
-            forcedMaterial.render(g, this);
-        } else {
-            g.getMaterial().render(g, this);
-        }
-    }
-
-    /**
-     * Renders the given GeometryList.
-     * <p>
-     * For every geometry in the list, the 
-     * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called.
-     * 
-     * @param gl The geometry list to render.
-     * 
-     * @see GeometryList
-     * @see #renderGeometry(com.jme3.scene.Geometry) 
-     */
-    public void renderGeometryList(GeometryList gl) {
-        for (int i = 0; i < gl.size(); i++) {
-            renderGeometry(gl.get(i));
-        }
-    }
-
-    /**
-     * If a spatial is not inside the eye frustum, it
-     * is still rendered in the shadow frustum (shadow casting queue)
-     * through this recursive method.
-     */
-    private void renderShadow(Spatial s, RenderQueue rq) {
-        if (s instanceof Node) {
-            Node n = (Node) s;
-            List<Spatial> children = n.getChildren();
-            for (int i = 0; i < children.size(); i++) {
-                renderShadow(children.get(i), rq);
-            }
-        } else if (s instanceof Geometry) {
-            Geometry gm = (Geometry) s;
-
-            RenderQueue.ShadowMode shadowMode = s.getShadowMode();
-            if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) {
-                //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue
-                rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast);
-            }
-        }
-    }
-
-    /**
-     * Preloads a scene for rendering.
-     * <p>
-     * After invocation of this method, the underlying
-     * renderer would have uploaded any textures, shaders and meshes
-     * used by the given scene to the video driver. 
-     * Using this method is useful when wishing to avoid the initial pause
-     * when rendering a scene for the first time. Note that it is not 
-     * guaranteed that the underlying renderer will actually choose to upload
-     * the data to the GPU so some pause is still to be expected.
-     * 
-     * @param scene The scene to preload
-     */
-    public void preloadScene(Spatial scene) {
-        if (scene instanceof Node) {
-            // recurse for all children
-            Node n = (Node) scene;
-            List<Spatial> children = n.getChildren();
-            for (int i = 0; i < children.size(); i++) {
-                preloadScene(children.get(i));
-            }
-        } else if (scene instanceof Geometry) {
-            // add to the render queue
-            Geometry gm = (Geometry) scene;
-            if (gm.getMaterial() == null) {
-                throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
-            }
-
-            gm.getMaterial().preload(this);
-            Mesh mesh = gm.getMesh();
-            if (mesh != null) {
-                for (Entry<VertexBuffer> entry : mesh.getBuffers()) {
-                    VertexBuffer buf = entry.getValue();
-                    if (buf.getData() != null) {
-                        renderer.updateBufferData(buf);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Flattens the given scene graph into the ViewPort's RenderQueue,
-     * checking for culling as the call goes down the graph recursively.
-     * <p>
-     * First, the scene is checked for culling based on the <code>Spatial</code>s
-     * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint},
-     * if the camera frustum contains the scene, then this method is recursively
-     * called on its children.
-     * <p>
-     * When the scene's leaves or {@link Geometry geometries} are reached,
-     * they are each enqueued into the 
-     * {@link ViewPort#getQueue() ViewPort's render queue}.
-     * <p>
-     * In addition to enqueuing the visible geometries, this method
-     * also scenes which cast or receive shadows, by putting them into the
-     * RenderQueue's 
-     * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode) 
-     * shadow queue}. Each Spatial which has its 
-     * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}
-     * set to not off, will be put into the appropriate shadow queue, note that
-     * this process does not check for frustum culling on any 
-     * {@link ShadowMode#Cast shadow casters}, as they don't have to be
-     * in the eye camera frustum to cast shadows on objects that are inside it.
-     * 
-     * @param scene The scene to flatten into the queue
-     * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera}
-     * used for culling and the {@link ViewPort#getQueue() queue} used to 
-     * contain the flattened scene graph.
-     */
-    public void renderScene(Spatial scene, ViewPort vp) {
-        if (scene.getParent() == null) {
-            vp.getCamera().setPlaneState(0);
-        }
-        // check culling first.
-        if (!scene.checkCulling(vp.getCamera())) {
-            // move on to shadow-only render
-            if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) {
-                renderShadow(scene, vp.getQueue());
-            }
-            return;
-        }
-
-        scene.runControlRender(this, vp);
-        if (scene instanceof Node) {
-            // recurse for all children
-            Node n = (Node) scene;
-            List<Spatial> children = n.getChildren();
-            //saving cam state for culling
-            int camState = vp.getCamera().getPlaneState();
-            for (int i = 0; i < children.size(); i++) {
-                //restoring cam state before proceeding children recusively
-                vp.getCamera().setPlaneState(camState);
-                renderScene(children.get(i), vp);
-
-            }
-        } else if (scene instanceof Geometry) {
-
-            // add to the render queue
-            Geometry gm = (Geometry) scene;
-            if (gm.getMaterial() == null) {
-                throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
-            }
-
-            vp.getQueue().addToQueue(gm, scene.getQueueBucket());
-
-            // add to shadow queue if needed
-            RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
-            if (shadowMode != RenderQueue.ShadowMode.Off) {
-                vp.getQueue().addToShadowQueue(gm, shadowMode);
-            }
-        }
-    }
-
-    /**
-     * Returns the camera currently used for rendering.
-     * <p>
-     * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }.
-     * 
-     * @return the camera currently used for rendering.
-     */
-    public Camera getCurrentCamera() {
-        return prevCam;
-    }
-
-    /**
-     * The renderer implementation used for rendering operations.
-     * 
-     * @return The renderer implementation
-     * 
-     * @see #RenderManager(com.jme3.renderer.Renderer) 
-     * @see Renderer
-     */
-    public Renderer getRenderer() {
-        return renderer;
-    }
-
-    /**
-     * Flushes the ViewPort's {@link ViewPort#getQueue() render queue}
-     * by rendering each of its visible buckets.
-     * By default the queues will automatically be cleared after rendering,
-     * so there's no need to clear them manually.
-     * 
-     * @param vp The ViewPort of which the queue will be flushed
-     * 
-     * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera) 
-     * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList) 
-     */
-    public void flushQueue(ViewPort vp) {
-        renderViewPortQueues(vp, true);
-    }
-
-    /**
-     * Clears the queue of the given ViewPort.
-     * Simply calls {@link RenderQueue#clear() } on the ViewPort's 
-     * {@link ViewPort#getQueue() render queue}.
-     * 
-     * @param vp The ViewPort of which the queue will be cleared.
-     * 
-     * @see RenderQueue#clear()
-     * @see ViewPort#getQueue()
-     */
-    public void clearQueue(ViewPort vp) {
-        vp.getQueue().clear();
-    }
-
-    /**
-     * Render the given viewport queues.
-     * <p>
-     * Changes the {@link Renderer#setDepthRange(float, float) depth range}
-     * appropriately as expected by each queue and then calls 
-     * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) }
-     * on the queue. Makes sure to restore the depth range to [0, 1] 
-     * at the end of the call.
-     * Note that the {@link Bucket#Translucent translucent bucket} is NOT
-     * rendered by this method. Instead the user should call 
-     * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) }
-     * after this call.
-     * 
-     * @param vp the viewport of which queue should be rendered
-     * @param flush If true, the queues will be cleared after
-     * rendering.
-     * 
-     * @see RenderQueue
-     * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort) 
-     */
-    public void renderViewPortQueues(ViewPort vp, boolean flush) {
-        RenderQueue rq = vp.getQueue();
-        Camera cam = vp.getCamera();
-        boolean depthRangeChanged = false;
-
-        // render opaque objects with default depth range
-        // opaque objects are sorted front-to-back, reducing overdraw
-        rq.renderQueue(Bucket.Opaque, this, cam, flush);
-
-        // render the sky, with depth range set to the farthest
-        if (!rq.isQueueEmpty(Bucket.Sky)) {
-            renderer.setDepthRange(1, 1);
-            rq.renderQueue(Bucket.Sky, this, cam, flush);
-            depthRangeChanged = true;
-        }
-
-
-        // transparent objects are last because they require blending with the
-        // rest of the scene's objects. Consequently, they are sorted
-        // back-to-front.
-        if (!rq.isQueueEmpty(Bucket.Transparent)) {
-            if (depthRangeChanged) {
-                renderer.setDepthRange(0, 1);
-                depthRangeChanged = false;
-            }
-
-            rq.renderQueue(Bucket.Transparent, this, cam, flush);
-        }
-
-        if (!rq.isQueueEmpty(Bucket.Gui)) {
-            renderer.setDepthRange(0, 0);
-            setCamera(cam, true);
-            rq.renderQueue(Bucket.Gui, this, cam, flush);
-            setCamera(cam, false);
-            depthRangeChanged = true;
-        }
-
-        // restore range to default
-        if (depthRangeChanged) {
-            renderer.setDepthRange(0, 1);
-        }
-    }
-
-    /**
-     * Renders the {@link Bucket#Translucent translucent queue} on the viewPort.
-     * <p>
-     * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) }
-     * is set to true. This method clears the translucent queue after rendering
-     * it.
-     * 
-     * @param vp The viewport of which the translucent queue should be rendered.
-     * 
-     * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) 
-     * @see #setHandleTranslucentBucket(boolean) 
-     */
-    public void renderTranslucentQueue(ViewPort vp) {
-        RenderQueue rq = vp.getQueue();
-        if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) {
-            rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true);
-        }
-    }
-
-    private void setViewPort(Camera cam) {
-        // this will make sure to update viewport only if needed
-        if (cam != prevCam || cam.isViewportChanged()) {
-            viewX = (int) (cam.getViewPortLeft() * cam.getWidth());
-            viewY = (int) (cam.getViewPortBottom() * cam.getHeight());
-            viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth());
-            viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight());
-            renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);
-            renderer.setClipRect(viewX, viewY, viewWidth, viewHeight);
-            cam.clearViewportChanged();
-            prevCam = cam;
-
-//            float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX);
-//            float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY);
-//            float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX);
-//            float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY);
-//            
-//            orthoMatrix.loadIdentity();
-//            orthoMatrix.setTranslation(translateX, translateY, 0);
-//            orthoMatrix.setScale(scaleX, scaleY, 0); 
-
-            orthoMatrix.loadIdentity();
-            orthoMatrix.setTranslation(-1f, -1f, 0f);
-            orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f);
-        }
-    }
-
-    private void setViewProjection(Camera cam, boolean ortho) {
-        if (shader) {
-            if (ortho) {
-                viewMatrix.set(Matrix4f.IDENTITY);
-                projMatrix.set(orthoMatrix);
-                viewProjMatrix.set(orthoMatrix);
-            } else {
-                viewMatrix.set(cam.getViewMatrix());
-                projMatrix.set(cam.getProjectionMatrix());
-                viewProjMatrix.set(cam.getViewProjectionMatrix());
-            }
-
-            camLoc.set(cam.getLocation());
-            cam.getLeft(camLeft);
-            cam.getUp(camUp);
-            cam.getDirection(camDir);
-
-            near = cam.getFrustumNear();
-            far = cam.getFrustumFar();
-        } else {
-            if (ortho) {
-                renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix);
-            } else {
-                renderer.setViewProjectionMatrices(cam.getViewMatrix(),
-                        cam.getProjectionMatrix());
-            }
-
-        }
-    }
-
-    /**
-     * Set the camera to use for rendering.
-     * <p>
-     * First, the camera's 
-     * {@link Camera#setViewPort(float, float, float, float) view port parameters}
-     * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and 
-     * {@link Camera#getProjectionMatrix() projection} matrices are set
-     * on the renderer. If <code>ortho</code> is <code>true</code>, then
-     * instead of using the camera's view and projection matrices, an ortho
-     * matrix is computed and used instead of the view projection matrix. 
-     * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1)
-     * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1).
-     * 
-     * @param cam The camera to set
-     * @param ortho True if to use orthographic projection (for GUI rendering),
-     * false if to use the camera's view and projection matrices.
-     */
-    public void setCamera(Camera cam, boolean ortho) {
-        setViewPort(cam);
-        setViewProjection(cam, ortho);
-    }
-
-    /**
-     * Draws the viewport but without notifying {@link SceneProcessor scene
-     * processors} of any rendering events.
-     * 
-     * @param vp The ViewPort to render
-     * 
-     * @see #renderViewPort(com.jme3.renderer.ViewPort, float) 
-     */
-    public void renderViewPortRaw(ViewPort vp) {
-        setCamera(vp.getCamera(), false);
-        List<Spatial> scenes = vp.getScenes();
-        for (int i = scenes.size() - 1; i >= 0; i--) {
-            renderScene(scenes.get(i), vp);
-        }
-        flushQueue(vp);
-    }
-
-    /**
-     * Renders the {@link ViewPort}.
-     * <p>
-     * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method
-     * returns immediately. Otherwise, the ViewPort is rendered by 
-     * the following process:<br>
-     * <ul>
-     * <li>All {@link SceneProcessor scene processors} that are attached
-     * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}.
-     * </li>
-     * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method 
-     * is called.</li>
-     * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer}
-     * is set on the Renderer</li>
-     * <li>The camera is set on the renderer, including its view port parameters.
-     * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li>
-     * <li>Any buffers that the ViewPort requests to be cleared are cleared
-     * and the {@link ViewPort#getBackgroundColor() background color} is set</li>
-     * <li>Every scene that is attached to the ViewPort is flattened into 
-     * the ViewPort's render queue 
-     * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) })
-     * </li>
-     * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) }
-     * method is called.</li>
-     * <li>The render queue is sorted and then flushed, sending
-     * rendering commands to the underlying Renderer implementation. 
-     * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li>
-     * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) }
-     * method is called.</li>
-     * <li>The translucent queue of the ViewPort is sorted and then flushed
-     * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>
-     * <li>If any objects remained in the render queue, they are removed
-     * from the queue. This is generally objects added to the 
-     * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) 
-     * shadow queue}
-     * which were not rendered because of a missing shadow renderer.</li>
-     * </ul>
-     * 
-     * @param vp
-     * @param tpf 
-     */
-    public void renderViewPort(ViewPort vp, float tpf) {
-        if (!vp.isEnabled()) {
-            return;
-        }
-        List<SceneProcessor> processors = vp.getProcessors();
-        if (processors.isEmpty()) {
-            processors = null;
-        }
-
-        if (processors != null) {
-            for (SceneProcessor proc : processors) {
-                if (!proc.isInitialized()) {
-                    proc.initialize(this, vp);
-                }
-                proc.preFrame(tpf);
-            }
-        }
-
-        renderer.setFrameBuffer(vp.getOutputFrameBuffer());
-        setCamera(vp.getCamera(), false);
-        if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) {
-            if (vp.isClearColor()) {
-                renderer.setBackgroundColor(vp.getBackgroundColor());
-            }
-            renderer.clearBuffers(vp.isClearColor(),
-                    vp.isClearDepth(),
-                    vp.isClearStencil());
-        }
-
-        List<Spatial> scenes = vp.getScenes();
-        for (int i = scenes.size() - 1; i >= 0; i--) {
-            renderScene(scenes.get(i), vp);
-        }
-
-        if (processors != null) {
-            for (SceneProcessor proc : processors) {
-                proc.postQueue(vp.getQueue());
-            }
-        }
-
-        flushQueue(vp);
-
-        if (processors != null) {
-            for (SceneProcessor proc : processors) {
-                proc.postFrame(vp.getOutputFrameBuffer());
-            }
-        }
-        //renders the translucent objects queue after processors have been rendered
-        renderTranslucentQueue(vp);
-        // clear any remaining spatials that were not rendered.
-        clearQueue(vp);
-    }
-
-    /**
-     * Called by the application to render any ViewPorts
-     * added to this RenderManager.
-     * <p>
-     * Renders any viewports that were added using the following methods:
-     * <ul>
-     * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li>
-     * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li>
-     * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li>
-     * </ul>
-     * 
-     * @param tpf Time per frame value
-     */
-    public void render(float tpf, boolean mainFrameBufferActive) {
-        if (renderer instanceof NullRenderer) {
-            return;
-        }
-
-        this.shader = renderer.getCaps().contains(Caps.GLSL100);
-
-        for (int i = 0; i < preViewPorts.size(); i++) {
-            ViewPort vp = preViewPorts.get(i);
-            if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
-                renderViewPort(vp, tpf);
-            }
-        }
-        for (int i = 0; i < viewPorts.size(); i++) {
-            ViewPort vp = viewPorts.get(i);
-            if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
-                renderViewPort(vp, tpf);
-            }
-        }
-        for (int i = 0; i < postViewPorts.size(); i++) {
-            ViewPort vp = postViewPorts.get(i);
-            if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
-                renderViewPort(vp, tpf);
-            }
-        }
-    }
-}
+/*

+ * Copyright (c) 2009-2012 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.renderer;

+

+import com.jme3.material.Material;

+import com.jme3.material.MaterialDef;

+import com.jme3.material.RenderState;

+import com.jme3.material.Technique;

+import com.jme3.math.*;

+import com.jme3.post.SceneProcessor;

+import com.jme3.renderer.queue.GeometryList;

+import com.jme3.renderer.queue.RenderQueue;

+import com.jme3.renderer.queue.RenderQueue.Bucket;

+import com.jme3.renderer.queue.RenderQueue.ShadowMode;

+import com.jme3.scene.*;

+import com.jme3.shader.Uniform;

+import com.jme3.shader.UniformBinding;

+import com.jme3.shader.VarType;

+import com.jme3.system.NullRenderer;

+import com.jme3.system.Timer;

+import com.jme3.util.IntMap.Entry;

+import com.jme3.util.TempVars;

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.List;

+import java.util.logging.Logger;

+

+/**

+ * <code>RenderManager</code> is a high-level rendering interface that is

+ * above the Renderer implementation. RenderManager takes care

+ * of rendering the scene graphs attached to each viewport and

+ * handling SceneProcessors.

+ *

+ * @see SceneProcessor

+ * @see ViewPort

+ * @see Spatial

+ */

+public class RenderManager {

+

+    private static final Logger logger = Logger.getLogger(RenderManager.class.getName());

+    

+    private Renderer renderer;

+    private Timer timer;

+    private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>();

+    private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();

+    private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>();

+    private Camera prevCam = null;

+    private Material forcedMaterial = null;

+    private String forcedTechnique = null;

+    private RenderState forcedRenderState = null;

+    private boolean shader;

+    private int viewX, viewY, viewWidth, viewHeight;

+    private float near, far;

+    private Matrix4f orthoMatrix = new Matrix4f();

+    private Matrix4f viewMatrix = new Matrix4f();

+    private Matrix4f projMatrix = new Matrix4f();

+    private Matrix4f viewProjMatrix = new Matrix4f();

+    private Matrix4f worldMatrix = new Matrix4f();

+    private Vector3f camUp = new Vector3f(),

+            camLeft = new Vector3f(),

+            camDir = new Vector3f(),

+            camLoc = new Vector3f();

+    //temp technique

+    private String tmpTech;

+    private boolean handleTranlucentBucket = true;

+

+    /**

+     * Create a high-level rendering interface over the

+     * low-level rendering interface.

+     * @param renderer

+     */

+    public RenderManager(Renderer renderer) {

+        this.renderer = renderer;

+        //this.shader = renderer.getCaps().contains(Caps.GLSL100);

+    }

+

+    /**

+     * Returns the pre ViewPort with the given name.

+     * 

+     * @param viewName The name of the pre ViewPort to look up

+     * @return The ViewPort, or null if not found.

+     * 

+     * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) 

+     */

+    public ViewPort getPreView(String viewName) {

+        for (int i = 0; i < preViewPorts.size(); i++) {

+            if (preViewPorts.get(i).getName().equals(viewName)) {

+                return preViewPorts.get(i);

+            }

+        }

+        return null;

+    }

+

+    /**

+     * Removes the specified pre ViewPort.

+     * 

+     * @param view The pre ViewPort to remove

+     * @return True if the ViewPort was removed successfully.

+     * 

+     * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) 

+     */

+    public boolean removePreView(ViewPort view) {

+        return preViewPorts.remove(view);

+    }

+

+    /**

+     * Returns the main ViewPort with the given name.

+     * 

+     * @param viewName The name of the main ViewPort to look up

+     * @return The ViewPort, or null if not found.

+     * 

+     * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) 

+     */

+    public ViewPort getMainView(String viewName) {

+        for (int i = 0; i < viewPorts.size(); i++) {

+            if (viewPorts.get(i).getName().equals(viewName)) {

+                return viewPorts.get(i);

+            }

+        }

+        return null;

+    }

+

+    /**

+     * Removes the main ViewPort with the specified name.

+     * 

+     * @param viewName The main ViewPort name to remove

+     * @return True if the ViewPort was removed successfully.

+     * 

+     * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) 

+     */

+    public boolean removeMainView(String viewName) {

+        for (int i = 0; i < viewPorts.size(); i++) {

+            if (viewPorts.get(i).getName().equals(viewName)) {

+                viewPorts.remove(i);

+                return true;

+            }

+        }

+        return false;

+    }

+

+    /**

+     * Removes the specified main ViewPort.

+     * 

+     * @param view The main ViewPort to remove

+     * @return True if the ViewPort was removed successfully.

+     * 

+     * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) 

+     */

+    public boolean removeMainView(ViewPort view) {

+        return viewPorts.remove(view);

+    }

+

+    /**

+     * Returns the post ViewPort with the given name.

+     * 

+     * @param viewName The name of the post ViewPort to look up

+     * @return The ViewPort, or null if not found.

+     * 

+     * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) 

+     */

+    public ViewPort getPostView(String viewName) {

+        for (int i = 0; i < postViewPorts.size(); i++) {

+            if (postViewPorts.get(i).getName().equals(viewName)) {

+                return postViewPorts.get(i);

+            }

+        }

+        return null;

+    }

+

+    /**

+     * Removes the post ViewPort with the specified name.

+     * 

+     * @param viewName The post ViewPort name to remove

+     * @return True if the ViewPort was removed successfully.

+     * 

+     * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) 

+     */

+    public boolean removePostView(String viewName) {

+        for (int i = 0; i < postViewPorts.size(); i++) {

+            if (postViewPorts.get(i).getName().equals(viewName)) {

+                postViewPorts.remove(i);

+

+                return true;

+            }

+        }

+        return false;

+    }

+

+    /**

+     * Removes the specified post ViewPort.

+     * 

+     * @param view The post ViewPort to remove

+     * @return True if the ViewPort was removed successfully.

+     * 

+     * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) 

+     */

+    public boolean removePostView(ViewPort view) {

+        return postViewPorts.remove(view);

+    }

+

+    /**

+     * Returns a read-only list of all pre ViewPorts

+     * @return a read-only list of all pre ViewPorts

+     * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) 

+     */

+    public List<ViewPort> getPreViews() {

+        return Collections.unmodifiableList(preViewPorts);

+    }

+

+    /**

+     * Returns a read-only list of all main ViewPorts

+     * @return a read-only list of all main ViewPorts

+     * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) 

+     */

+    public List<ViewPort> getMainViews() {

+        return Collections.unmodifiableList(viewPorts);

+    }

+

+    /**

+     * Returns a read-only list of all post ViewPorts

+     * @return a read-only list of all post ViewPorts

+     * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) 

+     */

+    public List<ViewPort> getPostViews() {

+        return Collections.unmodifiableList(postViewPorts);

+    }

+

+    /**

+     * Creates a new pre ViewPort, to display the given camera's content.

+     * <p>

+     * The view will be processed before the main and post viewports.

+     */

+    public ViewPort createPreView(String viewName, Camera cam) {

+        ViewPort vp = new ViewPort(viewName, cam);

+        preViewPorts.add(vp);

+        return vp;

+    }

+

+    /**

+     * Creates a new main ViewPort, to display the given camera's content.

+     * <p>

+     * The view will be processed before the post viewports but after

+     * the pre viewports.

+     */

+    public ViewPort createMainView(String viewName, Camera cam) {

+        ViewPort vp = new ViewPort(viewName, cam);

+        viewPorts.add(vp);

+        return vp;

+    }

+

+    /**

+     * Creates a new post ViewPort, to display the given camera's content.

+     * <p>

+     * The view will be processed after the pre and main viewports.

+     */

+    public ViewPort createPostView(String viewName, Camera cam) {

+        ViewPort vp = new ViewPort(viewName, cam);

+        postViewPorts.add(vp);

+        return vp;

+    }

+

+    private void notifyReshape(ViewPort vp, int w, int h) {

+        List<SceneProcessor> processors = vp.getProcessors();

+        for (SceneProcessor proc : processors) {

+            if (!proc.isInitialized()) {

+                proc.initialize(this, vp);

+            } else {

+                proc.reshape(vp, w, h);

+            }

+        }

+    }

+

+    /**

+     * Internal use only.

+     * Updates the resolution of all on-screen cameras to match

+     * the given width and height.

+     */

+    public void notifyReshape(int w, int h) {

+        for (ViewPort vp : preViewPorts) {

+            if (vp.getOutputFrameBuffer() == null) {

+                Camera cam = vp.getCamera();

+                cam.resize(w, h, true);

+            }

+            notifyReshape(vp, w, h);

+        }

+        for (ViewPort vp : viewPorts) {

+            if (vp.getOutputFrameBuffer() == null) {

+                Camera cam = vp.getCamera();

+                cam.resize(w, h, true);

+            }

+            notifyReshape(vp, w, h);

+        }

+        for (ViewPort vp : postViewPorts) {

+            if (vp.getOutputFrameBuffer() == null) {

+                Camera cam = vp.getCamera();

+                cam.resize(w, h, true);

+            }

+            notifyReshape(vp, w, h);

+        }

+    }

+

+    /**

+     * Internal use only.

+     * Updates the given list of uniforms with {@link UniformBinding uniform bindings}

+     * based on the current world state.

+     */

+    public void updateUniformBindings(List<Uniform> params) {

+        // assums worldMatrix is properly set.

+        TempVars vars = TempVars.get();

+

+        Matrix4f tempMat4 = vars.tempMat4;

+        Matrix3f tempMat3 = vars.tempMat3;

+        Vector2f tempVec2 = vars.vect2d;

+        Quaternion tempVec4 = vars.quat1;

+

+        for (int i = 0; i < params.size(); i++) {

+            Uniform u = params.get(i);

+            switch (u.getBinding()) {

+                case WorldMatrix:

+                    u.setValue(VarType.Matrix4, worldMatrix);

+                    break;

+                case ViewMatrix:

+                    u.setValue(VarType.Matrix4, viewMatrix);

+                    break;

+                case ProjectionMatrix:

+                    u.setValue(VarType.Matrix4, projMatrix);

+                    break;

+                case ViewProjectionMatrix:

+                    u.setValue(VarType.Matrix4, viewProjMatrix);

+                    break;

+                case WorldViewMatrix:

+                    tempMat4.set(viewMatrix);

+                    tempMat4.multLocal(worldMatrix);

+                    u.setValue(VarType.Matrix4, tempMat4);

+                    break;

+                case NormalMatrix:

+                    tempMat4.set(viewMatrix);

+                    tempMat4.multLocal(worldMatrix);

+                    tempMat4.toRotationMatrix(tempMat3);

+                    tempMat3.invertLocal();

+                    tempMat3.transposeLocal();

+                    u.setValue(VarType.Matrix3, tempMat3);

+                    break;

+                case WorldViewProjectionMatrix:

+                    tempMat4.set(viewProjMatrix);

+                    tempMat4.multLocal(worldMatrix);

+                    u.setValue(VarType.Matrix4, tempMat4);

+                    break;

+                case WorldMatrixInverse:

+                    tempMat4.set(worldMatrix);

+                    tempMat4.invertLocal();

+                    u.setValue(VarType.Matrix4, tempMat4);

+                    break;

+                case WorldMatrixInverseTranspose:

+                    worldMatrix.toRotationMatrix(tempMat3);

+                    tempMat3.invertLocal().transposeLocal();

+                    u.setValue(VarType.Matrix3, tempMat3);

+                    break;

+                case ViewMatrixInverse:

+                    tempMat4.set(viewMatrix);

+                    tempMat4.invertLocal();

+                    u.setValue(VarType.Matrix4, tempMat4);

+                    break;

+                case ProjectionMatrixInverse:

+                    tempMat4.set(projMatrix);

+                    tempMat4.invertLocal();

+                    u.setValue(VarType.Matrix4, tempMat4);

+                    break;

+                case ViewProjectionMatrixInverse:

+                    tempMat4.set(viewProjMatrix);

+                    tempMat4.invertLocal();

+                    u.setValue(VarType.Matrix4, tempMat4);

+                    break;

+                case WorldViewMatrixInverse:

+                    tempMat4.set(viewMatrix);

+                    tempMat4.multLocal(worldMatrix);

+                    tempMat4.invertLocal();

+                    u.setValue(VarType.Matrix4, tempMat4);

+                    break;

+                case NormalMatrixInverse:

+                    tempMat4.set(viewMatrix);

+                    tempMat4.multLocal(worldMatrix);

+                    tempMat4.toRotationMatrix(tempMat3);

+                    tempMat3.invertLocal();

+                    tempMat3.transposeLocal();

+                    tempMat3.invertLocal();

+                    u.setValue(VarType.Matrix3, tempMat3);

+                    break;

+                case WorldViewProjectionMatrixInverse:

+                    tempMat4.set(viewProjMatrix);

+                    tempMat4.multLocal(worldMatrix);

+                    tempMat4.invertLocal();

+                    u.setValue(VarType.Matrix4, tempMat4);

+                    break;

+                case ViewPort:

+                    tempVec4.set(viewX, viewY, viewWidth, viewHeight);

+                    u.setValue(VarType.Vector4, tempVec4);

+                    break;

+                case Resolution:

+                    tempVec2.set(viewWidth, viewHeight);

+                    u.setValue(VarType.Vector2, tempVec2);

+                    break;

+                case ResolutionInverse:

+                    tempVec2.set(1f / viewWidth, 1f / viewHeight);

+                    u.setValue(VarType.Vector2, tempVec2);

+                    break;

+                case Aspect:

+                    float aspect = ((float) viewWidth) / viewHeight;

+                    u.setValue(VarType.Float, aspect);

+                    break;

+                case FrustumNearFar:

+                    tempVec2.set(near, far);

+                    u.setValue(VarType.Vector2, tempVec2);

+                    break;

+                case CameraPosition:

+                    u.setValue(VarType.Vector3, camLoc);

+                    break;

+                case CameraDirection:

+                    u.setValue(VarType.Vector3, camDir);

+                    break;

+                case CameraLeft:

+                    u.setValue(VarType.Vector3, camLeft);

+                    break;

+                case CameraUp:

+                    u.setValue(VarType.Vector3, camUp);

+                    break;

+                case Time:

+                    u.setValue(VarType.Float, timer.getTimeInSeconds());

+                    break;

+                case Tpf:

+                    u.setValue(VarType.Float, timer.getTimePerFrame());

+                    break;

+                case FrameRate:

+                    u.setValue(VarType.Float, timer.getFrameRate());

+                    break;

+            }

+        }

+

+        vars.release();

+    }

+

+    /**

+     * Set the material to use to render all future objects.

+     * This overrides the material set on the geometry and renders

+     * with the provided material instead.

+     * Use null to clear the material and return renderer to normal

+     * functionality.

+     * @param mat The forced material to set, or null to return to normal

+     */

+    public void setForcedMaterial(Material mat) {

+        forcedMaterial = mat;

+    }

+

+    /**

+     * Returns the forced render state previously set with 

+     * {@link #setForcedRenderState(com.jme3.material.RenderState) }.

+     * @return the forced render state

+     */

+    public RenderState getForcedRenderState() {

+        return forcedRenderState;

+    }

+

+    /**

+     * Set the render state to use for all future objects.

+     * This overrides the render state set on the material and instead

+     * forces this render state to be applied for all future materials

+     * rendered. Set to null to return to normal functionality.

+     * 

+     * @param forcedRenderState The forced render state to set, or null

+     * to return to normal

+     */

+    public void setForcedRenderState(RenderState forcedRenderState) {

+        this.forcedRenderState = forcedRenderState;

+    }

+

+    /**

+     * Set the timer that should be used to query the time based

+     * {@link UniformBinding}s for material world parameters.

+     * 

+     * @param timer The timer to query time world parameters

+     */

+    public void setTimer(Timer timer) {

+        this.timer = timer;

+    }

+

+    /**

+     * Returns the forced technique name set.

+     * 

+     * @return the forced technique name set.

+     * 

+     * @see #setForcedTechnique(java.lang.String) 

+     */

+    public String getForcedTechnique() {

+        return forcedTechnique;

+    }

+

+    /**

+     * Sets the forced technique to use when rendering geometries.

+     * <p>

+     * If the specified technique name is available on the geometry's

+     * material, then it is used, otherwise, the 

+     * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used.

+     * If a forced material is not set and the forced technique name cannot

+     * be found on the material, the geometry will <em>not</em> be rendered.

+     * 

+     * @param forcedTechnique The forced technique name to use, set to null

+     * to return to normal functionality.

+     * 

+     * @see #renderGeometry(com.jme3.scene.Geometry) 

+     */

+    public void setForcedTechnique(String forcedTechnique) {

+        this.forcedTechnique = forcedTechnique;

+    }

+

+    /**

+     * Enable or disable alpha-to-coverage. 

+     * <p>

+     * When alpha to coverage is enabled and the renderer implementation

+     * supports it, then alpha blending will be replaced with alpha dissolve

+     * if multi-sampling is also set on the renderer.

+     * This feature allows avoiding of alpha blending artifacts due to

+     * lack of triangle-level back-to-front sorting.

+     * 

+     * @param value True to enable alpha-to-coverage, false otherwise.

+     */

+    public void setAlphaToCoverage(boolean value) {

+        renderer.setAlphaToCoverage(value);

+    }

+

+    /**

+     * True if the translucent bucket should automatically be rendered

+     * by the RenderManager.

+     * 

+     * @return Whether or not the translucent bucket is rendered.

+     * 

+     * @see #setHandleTranslucentBucket(boolean) 

+     */

+    public boolean isHandleTranslucentBucket() {

+        return handleTranlucentBucket;

+    }

+

+    /**

+     * Enable or disable rendering of the 

+     * {@link Bucket#Translucent translucent bucket}

+     * by the RenderManager. The default is enabled.

+     * 

+     * @param handleTranslucentBucket Whether or not the translucent bucket should

+     * be rendered.

+     */

+    public void setHandleTranslucentBucket(boolean handleTranslucentBucket) {

+        this.handleTranlucentBucket = handleTranslucentBucket;

+    }

+

+    /**

+     * Internal use only. Sets the world matrix to use for future

+     * rendering. This has no effect unless objects are rendered manually

+     * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }.

+     * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will 

+     * override this value.

+     * 

+     * @param mat The world matrix to set

+     */

+    public void setWorldMatrix(Matrix4f mat) {

+        if (shader) {

+            worldMatrix.set(mat);

+        } else {

+            renderer.setWorldMatrix(mat);

+        }

+    }

+

+    /**

+     * Renders the given geometry.

+     * <p>

+     * First the proper world matrix is set, if 

+     * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform}

+     * feature is enabled, the identity world matrix is used, otherwise, the 

+     * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used. 

+     * <p>

+     * Once the world matrix is applied, the proper material is chosen for rendering.

+     * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is

+     * set on this RenderManager, then it is used for rendering the geometry,

+     * otherwise, the {@link Geometry#getMaterial() geometry's material} is used.

+     * <p>

+     * If a {@link #setForcedTechnique(java.lang.String) forced technique} is

+     * set on this RenderManager, then it is selected automatically

+     * on the geometry's material and is used for rendering. Otherwise, one

+     * of the {@link MaterialDef#getDefaultTechniques() default techniques} is

+     * used.

+     * <p>

+     * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced

+     * render state} is set on this RenderManager, then it is used

+     * for rendering the material, and the material's own render state is ignored.

+     * Otherwise, the material's render state is used as intended.

+     * 

+     * @param g The geometry to render

+     * 

+     * @see Technique

+     * @see RenderState

+     * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) 

+     * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) 

+     */

+    public void renderGeometry(Geometry g) {

+        if (g.isIgnoreTransform()) {

+            setWorldMatrix(Matrix4f.IDENTITY);

+        } else {

+            setWorldMatrix(g.getWorldMatrix());

+        }

+

+        //if forcedTechnique we try to force it for render,

+        //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null

+        //else the geom is not rendered

+        if (forcedTechnique != null) {

+            if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) {

+                tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default";

+                g.getMaterial().selectTechnique(forcedTechnique, this);

+                // use geometry's material

+                g.getMaterial().render(g, this);

+                g.getMaterial().selectTechnique(tmpTech, this);

+                //Reverted this part from revision 6197

+                //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered

+            } else if (forcedMaterial != null) {

+                // use forced material

+                forcedMaterial.render(g, this);

+            }

+        } else if (forcedMaterial != null) {

+            // use forced material

+            forcedMaterial.render(g, this);

+        } else {

+            g.getMaterial().render(g, this);

+        }

+    }

+

+    /**

+     * Renders the given GeometryList.

+     * <p>

+     * For every geometry in the list, the 

+     * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called.

+     * 

+     * @param gl The geometry list to render.

+     * 

+     * @see GeometryList

+     * @see #renderGeometry(com.jme3.scene.Geometry) 

+     */

+    public void renderGeometryList(GeometryList gl) {

+        for (int i = 0; i < gl.size(); i++) {

+            renderGeometry(gl.get(i));

+        }

+    }

+

+    /**

+     * If a spatial is not inside the eye frustum, it

+     * is still rendered in the shadow frustum (shadow casting queue)

+     * through this recursive method.

+     */

+    private void renderShadow(Spatial s, RenderQueue rq) {

+        if (s instanceof Node) {

+            Node n = (Node) s;

+            List<Spatial> children = n.getChildren();

+            for (int i = 0; i < children.size(); i++) {

+                renderShadow(children.get(i), rq);

+            }

+        } else if (s instanceof Geometry) {

+            Geometry gm = (Geometry) s;

+

+            RenderQueue.ShadowMode shadowMode = s.getShadowMode();

+            if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) {

+                //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue

+                rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast);

+            }

+        }

+    }

+

+    /**

+     * Preloads a scene for rendering.

+     * <p>

+     * After invocation of this method, the underlying

+     * renderer would have uploaded any textures, shaders and meshes

+     * used by the given scene to the video driver. 

+     * Using this method is useful when wishing to avoid the initial pause

+     * when rendering a scene for the first time. Note that it is not 

+     * guaranteed that the underlying renderer will actually choose to upload

+     * the data to the GPU so some pause is still to be expected.

+     * 

+     * @param scene The scene to preload

+     */

+    public void preloadScene(Spatial scene) {

+        if (scene instanceof Node) {

+            // recurse for all children

+            Node n = (Node) scene;

+            List<Spatial> children = n.getChildren();

+            for (int i = 0; i < children.size(); i++) {

+                preloadScene(children.get(i));

+            }

+        } else if (scene instanceof Geometry) {

+            // add to the render queue

+            Geometry gm = (Geometry) scene;

+            if (gm.getMaterial() == null) {

+                throw new IllegalStateException("No material is set for Geometry: " + gm.getName());

+            }

+

+            gm.getMaterial().preload(this);

+            Mesh mesh = gm.getMesh();

+            if (mesh != null) {

+                for (VertexBuffer vb : mesh.getBufferList().getArray()) {

+                    if (vb.getData() != null) {

+                        renderer.updateBufferData(vb);

+                    }

+                }

+            }

+        }

+    }

+

+    /**

+     * Flattens the given scene graph into the ViewPort's RenderQueue,

+     * checking for culling as the call goes down the graph recursively.

+     * <p>

+     * First, the scene is checked for culling based on the <code>Spatial</code>s

+     * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint},

+     * if the camera frustum contains the scene, then this method is recursively

+     * called on its children.

+     * <p>

+     * When the scene's leaves or {@link Geometry geometries} are reached,

+     * they are each enqueued into the 

+     * {@link ViewPort#getQueue() ViewPort's render queue}.

+     * <p>

+     * In addition to enqueuing the visible geometries, this method

+     * also scenes which cast or receive shadows, by putting them into the

+     * RenderQueue's 

+     * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode) 

+     * shadow queue}. Each Spatial which has its 

+     * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}

+     * set to not off, will be put into the appropriate shadow queue, note that

+     * this process does not check for frustum culling on any 

+     * {@link ShadowMode#Cast shadow casters}, as they don't have to be

+     * in the eye camera frustum to cast shadows on objects that are inside it.

+     * 

+     * @param scene The scene to flatten into the queue

+     * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera}

+     * used for culling and the {@link ViewPort#getQueue() queue} used to 

+     * contain the flattened scene graph.

+     */

+    public void renderScene(Spatial scene, ViewPort vp) {

+        if (scene.getParent() == null) {

+            vp.getCamera().setPlaneState(0);

+        }

+        // check culling first.

+        if (!scene.checkCulling(vp.getCamera())) {

+            // move on to shadow-only render

+            if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) {

+                renderShadow(scene, vp.getQueue());

+            }

+            return;

+        }

+

+        scene.runControlRender(this, vp);

+        if (scene instanceof Node) {

+            // recurse for all children

+            Node n = (Node) scene;

+            List<Spatial> children = n.getChildren();

+            //saving cam state for culling

+            int camState = vp.getCamera().getPlaneState();

+            for (int i = 0; i < children.size(); i++) {

+                //restoring cam state before proceeding children recusively

+                vp.getCamera().setPlaneState(camState);

+                renderScene(children.get(i), vp);

+

+            }

+        } else if (scene instanceof Geometry) {

+

+            // add to the render queue

+            Geometry gm = (Geometry) scene;

+            if (gm.getMaterial() == null) {

+                throw new IllegalStateException("No material is set for Geometry: " + gm.getName());

+            }

+

+            vp.getQueue().addToQueue(gm, scene.getQueueBucket());

+

+            // add to shadow queue if needed

+            RenderQueue.ShadowMode shadowMode = scene.getShadowMode();

+            if (shadowMode != RenderQueue.ShadowMode.Off) {

+                vp.getQueue().addToShadowQueue(gm, shadowMode);

+            }

+        }

+    }

+

+    /**

+     * Returns the camera currently used for rendering.

+     * <p>

+     * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }.

+     * 

+     * @return the camera currently used for rendering.

+     */

+    public Camera getCurrentCamera() {

+        return prevCam;

+    }

+

+    /**

+     * The renderer implementation used for rendering operations.

+     * 

+     * @return The renderer implementation

+     * 

+     * @see #RenderManager(com.jme3.renderer.Renderer) 

+     * @see Renderer

+     */

+    public Renderer getRenderer() {

+        return renderer;

+    }

+

+    /**

+     * Flushes the ViewPort's {@link ViewPort#getQueue() render queue}

+     * by rendering each of its visible buckets.

+     * By default the queues will automatically be cleared after rendering,

+     * so there's no need to clear them manually.

+     * 

+     * @param vp The ViewPort of which the queue will be flushed

+     * 

+     * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera) 

+     * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList) 

+     */

+    public void flushQueue(ViewPort vp) {

+        renderViewPortQueues(vp, true);

+    }

+

+    /**

+     * Clears the queue of the given ViewPort.

+     * Simply calls {@link RenderQueue#clear() } on the ViewPort's 

+     * {@link ViewPort#getQueue() render queue}.

+     * 

+     * @param vp The ViewPort of which the queue will be cleared.

+     * 

+     * @see RenderQueue#clear()

+     * @see ViewPort#getQueue()

+     */

+    public void clearQueue(ViewPort vp) {

+        vp.getQueue().clear();

+    }

+

+    /**

+     * Render the given viewport queues.

+     * <p>

+     * Changes the {@link Renderer#setDepthRange(float, float) depth range}

+     * appropriately as expected by each queue and then calls 

+     * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) }

+     * on the queue. Makes sure to restore the depth range to [0, 1] 

+     * at the end of the call.

+     * Note that the {@link Bucket#Translucent translucent bucket} is NOT

+     * rendered by this method. Instead the user should call 

+     * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) }

+     * after this call.

+     * 

+     * @param vp the viewport of which queue should be rendered

+     * @param flush If true, the queues will be cleared after

+     * rendering.

+     * 

+     * @see RenderQueue

+     * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort) 

+     */

+    public void renderViewPortQueues(ViewPort vp, boolean flush) {

+        RenderQueue rq = vp.getQueue();

+        Camera cam = vp.getCamera();

+        boolean depthRangeChanged = false;

+

+        // render opaque objects with default depth range

+        // opaque objects are sorted front-to-back, reducing overdraw

+        rq.renderQueue(Bucket.Opaque, this, cam, flush);

+

+        // render the sky, with depth range set to the farthest

+        if (!rq.isQueueEmpty(Bucket.Sky)) {

+            renderer.setDepthRange(1, 1);

+            rq.renderQueue(Bucket.Sky, this, cam, flush);

+            depthRangeChanged = true;

+        }

+

+

+        // transparent objects are last because they require blending with the

+        // rest of the scene's objects. Consequently, they are sorted

+        // back-to-front.

+        if (!rq.isQueueEmpty(Bucket.Transparent)) {

+            if (depthRangeChanged) {

+                renderer.setDepthRange(0, 1);

+                depthRangeChanged = false;

+            }

+

+            rq.renderQueue(Bucket.Transparent, this, cam, flush);

+        }

+

+        if (!rq.isQueueEmpty(Bucket.Gui)) {

+            renderer.setDepthRange(0, 0);

+            setCamera(cam, true);

+            rq.renderQueue(Bucket.Gui, this, cam, flush);

+            setCamera(cam, false);

+            depthRangeChanged = true;

+        }

+

+        // restore range to default

+        if (depthRangeChanged) {

+            renderer.setDepthRange(0, 1);

+        }

+    }

+

+    /**

+     * Renders the {@link Bucket#Translucent translucent queue} on the viewPort.

+     * <p>

+     * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) }

+     * is set to true. This method clears the translucent queue after rendering

+     * it.

+     * 

+     * @param vp The viewport of which the translucent queue should be rendered.

+     * 

+     * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) 

+     * @see #setHandleTranslucentBucket(boolean) 

+     */

+    public void renderTranslucentQueue(ViewPort vp) {

+        RenderQueue rq = vp.getQueue();

+        if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) {

+            rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true);

+        }

+    }

+

+    private void setViewPort(Camera cam) {

+        // this will make sure to update viewport only if needed

+        if (cam != prevCam || cam.isViewportChanged()) {

+            viewX = (int) (cam.getViewPortLeft() * cam.getWidth());

+            viewY = (int) (cam.getViewPortBottom() * cam.getHeight());

+            viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth());

+            viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight());

+            renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);

+            renderer.setClipRect(viewX, viewY, viewWidth, viewHeight);

+            cam.clearViewportChanged();

+            prevCam = cam;

+

+//            float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX);

+//            float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY);

+//            float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX);

+//            float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY);

+//            

+//            orthoMatrix.loadIdentity();

+//            orthoMatrix.setTranslation(translateX, translateY, 0);

+//            orthoMatrix.setScale(scaleX, scaleY, 0); 

+

+            orthoMatrix.loadIdentity();

+            orthoMatrix.setTranslation(-1f, -1f, 0f);

+            orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f);

+        }

+    }

+

+    private void setViewProjection(Camera cam, boolean ortho) {

+        if (shader) {

+            if (ortho) {

+                viewMatrix.set(Matrix4f.IDENTITY);

+                projMatrix.set(orthoMatrix);

+                viewProjMatrix.set(orthoMatrix);

+            } else {

+                viewMatrix.set(cam.getViewMatrix());

+                projMatrix.set(cam.getProjectionMatrix());

+                viewProjMatrix.set(cam.getViewProjectionMatrix());

+            }

+

+            camLoc.set(cam.getLocation());

+            cam.getLeft(camLeft);

+            cam.getUp(camUp);

+            cam.getDirection(camDir);

+

+            near = cam.getFrustumNear();

+            far = cam.getFrustumFar();

+        } else {

+            if (ortho) {

+                renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix);

+            } else {

+                renderer.setViewProjectionMatrices(cam.getViewMatrix(),

+                        cam.getProjectionMatrix());

+            }

+

+        }

+    }

+

+    /**

+     * Set the camera to use for rendering.

+     * <p>

+     * First, the camera's 

+     * {@link Camera#setViewPort(float, float, float, float) view port parameters}

+     * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and 

+     * {@link Camera#getProjectionMatrix() projection} matrices are set

+     * on the renderer. If <code>ortho</code> is <code>true</code>, then

+     * instead of using the camera's view and projection matrices, an ortho

+     * matrix is computed and used instead of the view projection matrix. 

+     * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1)

+     * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1).

+     * 

+     * @param cam The camera to set

+     * @param ortho True if to use orthographic projection (for GUI rendering),

+     * false if to use the camera's view and projection matrices.

+     */

+    public void setCamera(Camera cam, boolean ortho) {

+        setViewPort(cam);

+        setViewProjection(cam, ortho);

+    }

+

+    /**

+     * Draws the viewport but without notifying {@link SceneProcessor scene

+     * processors} of any rendering events.

+     * 

+     * @param vp The ViewPort to render

+     * 

+     * @see #renderViewPort(com.jme3.renderer.ViewPort, float) 

+     */

+    public void renderViewPortRaw(ViewPort vp) {

+        setCamera(vp.getCamera(), false);

+        List<Spatial> scenes = vp.getScenes();

+        for (int i = scenes.size() - 1; i >= 0; i--) {

+            renderScene(scenes.get(i), vp);

+        }

+        flushQueue(vp);

+    }

+

+    /**

+     * Renders the {@link ViewPort}.

+     * <p>

+     * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method

+     * returns immediately. Otherwise, the ViewPort is rendered by 

+     * the following process:<br>

+     * <ul>

+     * <li>All {@link SceneProcessor scene processors} that are attached

+     * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}.

+     * </li>

+     * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method 

+     * is called.</li>

+     * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer}

+     * is set on the Renderer</li>

+     * <li>The camera is set on the renderer, including its view port parameters.

+     * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li>

+     * <li>Any buffers that the ViewPort requests to be cleared are cleared

+     * and the {@link ViewPort#getBackgroundColor() background color} is set</li>

+     * <li>Every scene that is attached to the ViewPort is flattened into 

+     * the ViewPort's render queue 

+     * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) })

+     * </li>

+     * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) }

+     * method is called.</li>

+     * <li>The render queue is sorted and then flushed, sending

+     * rendering commands to the underlying Renderer implementation. 

+     * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li>

+     * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) }

+     * method is called.</li>

+     * <li>The translucent queue of the ViewPort is sorted and then flushed

+     * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>

+     * <li>If any objects remained in the render queue, they are removed

+     * from the queue. This is generally objects added to the 

+     * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) 

+     * shadow queue}

+     * which were not rendered because of a missing shadow renderer.</li>

+     * </ul>

+     * 

+     * @param vp

+     * @param tpf 

+     */

+    public void renderViewPort(ViewPort vp, float tpf) {

+        if (!vp.isEnabled()) {

+            return;

+        }

+        List<SceneProcessor> processors = vp.getProcessors();

+        if (processors.isEmpty()) {

+            processors = null;

+        }

+

+        if (processors != null) {

+            for (SceneProcessor proc : processors) {

+                if (!proc.isInitialized()) {

+                    proc.initialize(this, vp);

+                }

+                proc.preFrame(tpf);

+            }

+        }

+

+        renderer.setFrameBuffer(vp.getOutputFrameBuffer());

+        setCamera(vp.getCamera(), false);

+        if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) {

+            if (vp.isClearColor()) {

+                renderer.setBackgroundColor(vp.getBackgroundColor());

+            }

+            renderer.clearBuffers(vp.isClearColor(),

+                    vp.isClearDepth(),

+                    vp.isClearStencil());

+        }

+

+        List<Spatial> scenes = vp.getScenes();

+        for (int i = scenes.size() - 1; i >= 0; i--) {

+            renderScene(scenes.get(i), vp);

+        }

+

+        if (processors != null) {

+            for (SceneProcessor proc : processors) {

+                proc.postQueue(vp.getQueue());

+            }

+        }

+

+        flushQueue(vp);

+

+        if (processors != null) {

+            for (SceneProcessor proc : processors) {

+                proc.postFrame(vp.getOutputFrameBuffer());

+            }

+        }

+        //renders the translucent objects queue after processors have been rendered

+        renderTranslucentQueue(vp);

+        // clear any remaining spatials that were not rendered.

+        clearQueue(vp);

+    }

+

+    /**

+     * Called by the application to render any ViewPorts

+     * added to this RenderManager.

+     * <p>

+     * Renders any viewports that were added using the following methods:

+     * <ul>

+     * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li>

+     * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li>

+     * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li>

+     * </ul>

+     * 

+     * @param tpf Time per frame value

+     */

+    public void render(float tpf, boolean mainFrameBufferActive) {

+        if (renderer instanceof NullRenderer) {

+            return;

+        }

+

+        this.shader = renderer.getCaps().contains(Caps.GLSL100);

+

+        for (int i = 0; i < preViewPorts.size(); i++) {

+            ViewPort vp = preViewPorts.get(i);

+            if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){

+                renderViewPort(vp, tpf);

+            }

+        }

+        for (int i = 0; i < viewPorts.size(); i++) {

+            ViewPort vp = viewPorts.get(i);

+            if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){

+                renderViewPort(vp, tpf);

+            }

+        }

+        for (int i = 0; i < postViewPorts.size(); i++) {

+            ViewPort vp = postViewPorts.get(i);

+            if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){

+                renderViewPort(vp, tpf);

+            }

+        }

+    }

+}

diff --git a/engine/src/core/com/jme3/scene/BatchNode.java b/engine/src/core/com/jme3/scene/BatchNode.java
index bc1b2cb..303de7a 100644
--- a/engine/src/core/com/jme3/scene/BatchNode.java
+++ b/engine/src/core/com/jme3/scene/BatchNode.java
@@ -46,6 +46,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -190,10 +191,10 @@
             }
             batches.clear();
         }
-        
-        for (Material material : matMap.keySet()) {
+        for (Map.Entry<Material, List<Geometry>> entry : matMap.entrySet()) {
             Mesh m = new Mesh();
-            List<Geometry> list = matMap.get(material);
+            Material material = entry.getKey();
+            List<Geometry> list = entry.getValue();
             nbGeoms += list.size();
             if (!needsFullRebatch) {
                 list.add(batches.get(material).geometry);
@@ -408,9 +409,9 @@
                     throw new UnsupportedOperationException();
             }
 
-            for (Entry<VertexBuffer> entry : geom.getMesh().getBuffers()) {
-                compsForBuf[entry.getKey()] = entry.getValue().getNumComponents();
-                formatForBuf[entry.getKey()] = entry.getValue().getFormat();
+            for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) {
+                compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents();
+                formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat();
             }
 
             if (mode != null && mode != listMode) {
diff --git a/engine/src/core/com/jme3/scene/Mesh.java b/engine/src/core/com/jme3/scene/Mesh.java
index 6c587f2..036e525 100644
--- a/engine/src/core/com/jme3/scene/Mesh.java
+++ b/engine/src/core/com/jme3/scene/Mesh.java
@@ -43,7 +43,6 @@
 import com.jme3.math.Triangle;
 import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
-import com.jme3.scene.VertexBuffer;
 import com.jme3.scene.VertexBuffer.Format;
 import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.VertexBuffer.Usage;
@@ -55,8 +54,6 @@
 import java.io.IOException;
 import java.nio.*;
 import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
 
 /**
  * <code>Mesh</code> is used to store rendering data.
@@ -237,9 +234,9 @@
 
             clone.buffers = new IntMap<VertexBuffer>();
             clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class);
-            for (Entry<VertexBuffer> ent : buffers){
-                VertexBuffer bufClone = ent.getValue().clone();
-                clone.buffers.put(ent.getKey(), bufClone);
+            for (VertexBuffer vb : buffersList.getArray()){
+                VertexBuffer bufClone = vb.clone();
+                clone.buffers.put(vb.getBufferType().ordinal(), bufClone);
                 clone.buffersList.add(bufClone);
             }
             
@@ -540,8 +537,8 @@
      * for all {@link VertexBuffer vertex buffers} on this Mesh.
      */
     public void setStatic() {
-        for (Entry<VertexBuffer> entry : buffers){
-            entry.getValue().setUsage(Usage.Static);
+        for (VertexBuffer vb : buffersList.getArray()){
+            vb.setUsage(Usage.Static);
         }
     }
 
@@ -551,8 +548,8 @@
      * for all {@link VertexBuffer vertex buffers} on this Mesh.
      */
     public void setDynamic() {
-        for (Entry<VertexBuffer> entry : buffers){
-            entry.getValue().setUsage(Usage.Dynamic);
+        for (VertexBuffer vb : buffersList.getArray()){
+            vb.setUsage(Usage.Dynamic);
         }
     }
 
@@ -562,8 +559,8 @@
      * for all {@link VertexBuffer vertex buffers} on this Mesh.
      */
     public void setStreamed(){
-        for (Entry<VertexBuffer> entry : buffers){
-            entry.getValue().setUsage(Usage.Stream);
+        for (VertexBuffer vb : buffersList.getArray()){
+            vb.setUsage(Usage.Stream);
         }
     }
 
@@ -572,11 +569,11 @@
      * Some GPUs may prefer the data in this format, however it is a good idea
      * to <em>avoid</em> using this method as it disables some engine features.
      */
+    @Deprecated
     public void setInterleaved(){
         ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>();
-        for (Entry<VertexBuffer> entry : buffers){
-            vbs.add(entry.getValue());
-        }
+        vbs.addAll(buffersList);
+        
 //        ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(buffers.values());
         // index buffer not included when interleaving
         vbs.remove(getBuffer(Type.Index));
@@ -860,6 +857,65 @@
     }
 
     /**
+     * Sets the {@link VertexBuffer} on the mesh.
+     * This will update the vertex/triangle counts if needed.
+     * 
+     * @param vb The buffer to set
+     * @throws IllegalArgumentException If the buffer type is already set
+     */
+    public void setBuffer(VertexBuffer vb){
+        if (buffers.containsKey(vb.getBufferType().ordinal()))
+            throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType());
+
+        buffers.put(vb.getBufferType().ordinal(), vb);
+        buffersList.add(vb);
+        updateCounts();
+    }
+    
+    /**
+     * Unsets the {@link VertexBuffer} set on this mesh
+     * with the given type. Does nothing if the vertex buffer type is not set 
+     * initially.
+     * 
+     * @param type The buffer type to remove
+     */
+    public void clearBuffer(VertexBuffer.Type type){
+        VertexBuffer vb = buffers.remove(type.ordinal());
+        if (vb != null){
+            buffersList.remove(vb);
+            updateCounts();
+        }
+    }
+    
+    /**
+     * Creates a {@link VertexBuffer} for the mesh or modifies
+     * the existing one per the parameters given.
+     * 
+     * @param type The type of the buffer
+     * @param components Number of components
+     * @param format Data format
+     * @param buf The buffer data
+     * 
+     * @throws UnsupportedOperationException If the buffer already set is 
+     * incompatible with the parameters given.
+     */
+    public void setBuffer(Type type, int components, Format format, Buffer buf){
+        VertexBuffer vb = buffers.get(type.ordinal());
+        if (vb == null){
+            vb = new VertexBuffer(type);
+            vb.setupData(Usage.Dynamic, components, format, buf);
+            setBuffer(vb);
+        }else{
+            if (vb.getNumComponents() != components || vb.getFormat() != format){
+                throw new UnsupportedOperationException("The buffer already set "
+                        + "is incompatible with the given parameters");
+            }
+            vb.updateData(buf);
+            updateCounts();
+        }
+    }
+    
+    /**
      * Set a floating point {@link VertexBuffer} on the mesh. 
      * 
      * @param type The type of {@link VertexBuffer}, 
@@ -871,21 +927,7 @@
      * @param buf The floating point data to contain
      */
     public void setBuffer(Type type, int components, FloatBuffer buf) {
-//        VertexBuffer vb = buffers.get(type);
-        VertexBuffer vb = buffers.get(type.ordinal());
-        if (vb == null){
-            if (buf == null)
-                return;
-
-            vb = new VertexBuffer(type);
-            vb.setupData(Usage.Dynamic, components, Format.Float, buf);
-//            buffers.put(type, vb);
-            buffers.put(type.ordinal(), vb);
-            buffersList.add(vb);
-        }else{
-            vb.setupData(Usage.Dynamic, components, Format.Float, buf);
-        }
-        updateCounts();
+        setBuffer(type, components, Format.Float, buf);
     }
 
     public void setBuffer(Type type, int components, float[] buf){
@@ -893,14 +935,7 @@
     }
 
     public void setBuffer(Type type, int components, IntBuffer buf) {
-        VertexBuffer vb = buffers.get(type.ordinal());
-        if (vb == null){
-            vb = new VertexBuffer(type);
-            vb.setupData(Usage.Dynamic, components, Format.UnsignedInt, buf);
-            buffers.put(type.ordinal(), vb);
-            buffersList.add(vb);
-            updateCounts();
-        }
+        setBuffer(type, components, Format.UnsignedInt, buf);
     }
 
     public void setBuffer(Type type, int components, int[] buf){
@@ -908,14 +943,7 @@
     }
 
     public void setBuffer(Type type, int components, ShortBuffer buf) {
-        VertexBuffer vb = buffers.get(type.ordinal());
-        if (vb == null){
-            vb = new VertexBuffer(type);
-            vb.setupData(Usage.Dynamic, components, Format.UnsignedShort, buf);
-            buffers.put(type.ordinal(), vb);
-            buffersList.add(vb);
-            updateCounts();
-        }
+        setBuffer(type, components, Format.UnsignedShort, buf);
     }
 
     public void setBuffer(Type type, int components, byte[] buf){
@@ -923,38 +951,7 @@
     }
 
     public void setBuffer(Type type, int components, ByteBuffer buf) {
-        VertexBuffer vb = buffers.get(type.ordinal());
-        if (vb == null){
-            vb = new VertexBuffer(type);
-            vb.setupData(Usage.Dynamic, components, Format.UnsignedByte, buf);
-            buffers.put(type.ordinal(), vb);
-            buffersList.add(vb);
-            updateCounts();
-        }
-    }
-
-    public void setBuffer(VertexBuffer vb){
-        if (buffers.containsKey(vb.getBufferType().ordinal()))
-            throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType());
-
-        buffers.put(vb.getBufferType().ordinal(), vb);
-        buffersList.add(vb);
-        updateCounts();
-    }
-
-    /**
-     * Clears or unsets the {@link VertexBuffer} set on this mesh
-     * with the given type.
-     * Does nothing if the vertex buffer type is not set initially
-     * 
-     * @param type The type to remove
-     */
-    public void clearBuffer(VertexBuffer.Type type){
-        VertexBuffer vb = buffers.remove(type.ordinal());
-        if (vb != null){
-            buffersList.remove(vb);
-            updateCounts();
-        }
+        setBuffer(type, components, Format.UnsignedByte, buf);
     }
 
     public void setBuffer(Type type, int components, short[] buf){
diff --git a/engine/src/core/com/jme3/shader/UniformBinding.java b/engine/src/core/com/jme3/shader/UniformBinding.java
index bdca35b..6bd4ca9 100644
--- a/engine/src/core/com/jme3/shader/UniformBinding.java
+++ b/engine/src/core/com/jme3/shader/UniformBinding.java
@@ -1,162 +1,176 @@
-/*
- * 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.shader;
-
-public enum UniformBinding {
-
-    /**
-     * The world matrix. Converts Model space to World space.
-     * Type: mat4
-     */
-    WorldMatrix,
-
-    /**
-     * The view matrix. Converts World space to View space.
-     * Type: mat4
-     */
-    ViewMatrix,
-
-    /**
-     * The projection matrix. Converts View space to Clip/Projection space.
-     * Type: mat4
-     */
-    ProjectionMatrix,
-
-    /**
-     * The world view matrix. Converts Model space to View space.
-     * Type: mat4
-     */
-    WorldViewMatrix,
-
-    /**
-     * The normal matrix. The inverse transpose of the worldview matrix.
-     * Converts normals from model space to view space.
-     * Type: mat3
-     */
-    NormalMatrix,
-
-    /**
-     * The world view projection matrix. Converts Model space to Clip/Projection
-     * space.
-     * Type: mat4
-     */
-    WorldViewProjectionMatrix,
-
-    /**
-     * The view projection matrix. Converts Model space to Clip/Projection
-     * space.
-     * Type: mat4
-     */
-    ViewProjectionMatrix,
-
-
-    WorldMatrixInverse,
-    ViewMatrixInverse,
-    ProjectionMatrixInverse,
-    ViewProjectionMatrixInverse,
-    WorldViewMatrixInverse,
-    NormalMatrixInverse,
-    WorldViewProjectionMatrixInverse,
-
-    /**
-     * Contains the four viewport parameters in this order:
-     * X = Left,
-     * Y = Top,
-     * Z = Right,
-     * W = Bottom.
-     * Type: vec4
-     */
-    ViewPort,
-
-    /**
-     * The near and far values for the camera frustum.
-     * X = Near
-     * Y = Far.
-     * Type: vec2
-     */
-    FrustumNearFar,
-    
-    /**
-     * The width and height of the camera.
-     * Type: vec2
-     */
-    Resolution,
-
-    /**
-     * Aspect ratio of the resolution currently set. Width/Height.
-     * Type: float
-     */
-    Aspect,
-
-    /**
-     * Camera position in world space.
-     * Type: vec3
-     */
-    CameraPosition,
-
-    /**
-     * Direction of the camera.
-     * Type: vec3
-     */
-    CameraDirection,
-
-    /**
-     * Left vector of the camera.
-     * Type: vec3
-     */
-    CameraLeft,
-
-    /**
-     * Up vector of the camera.
-     * Type: vec3
-     */
-    CameraUp,
-
-    /**
-     * Time in seconds since the application was started.
-     * Type: float
-     */
-    Time,
-
-    /**
-     * Time in seconds that the last frame took.
-     * Type: float
-     */
-    Tpf,
-
-    /**
-     * Frames per second.
-     * Type: float
-     */
-    FrameRate,
-}
+/*

+ * 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.shader;

+

+public enum UniformBinding {

+

+    /**

+     * The world matrix. Converts Model space to World space.

+     * Type: mat4

+     */

+    WorldMatrix,

+

+    /**

+     * The view matrix. Converts World space to View space.

+     * Type: mat4

+     */

+    ViewMatrix,

+

+    /**

+     * The projection matrix. Converts View space to Clip/Projection space.

+     * Type: mat4

+     */

+    ProjectionMatrix,

+

+    /**

+     * The world view matrix. Converts Model space to View space.

+     * Type: mat4

+     */

+    WorldViewMatrix,

+

+    /**

+     * The normal matrix. The inverse transpose of the worldview matrix.

+     * Converts normals from model space to view space.

+     * Type: mat3

+     */

+    NormalMatrix,

+

+    /**

+     * The world view projection matrix. Converts Model space to Clip/Projection

+     * space.

+     * Type: mat4

+     */

+    WorldViewProjectionMatrix,

+

+    /**

+     * The view projection matrix. Converts World space to Clip/Projection

+     * space.

+     * Type: mat4

+     */

+    ViewProjectionMatrix,

+

+    /**

+     * The world matrix inverse transpose. Converts a normals from Model space

+     * to world space.

+     * Type: mat3

+     */

+    WorldMatrixInverseTranspose,      

+

+

+

+    WorldMatrixInverse,

+    ViewMatrixInverse,

+    ProjectionMatrixInverse,

+    ViewProjectionMatrixInverse,

+    WorldViewMatrixInverse,

+    NormalMatrixInverse,

+    WorldViewProjectionMatrixInverse,

+

+    /**

+     * Contains the four viewport parameters in this order:

+     * X = Left,

+     * Y = Top,

+     * Z = Right,

+     * W = Bottom.

+     * Type: vec4

+     */

+    ViewPort,

+

+    /**

+     * The near and far values for the camera frustum.

+     * X = Near

+     * Y = Far.

+     * Type: vec2

+     */

+    FrustumNearFar,

+    

+    /**

+     * The width and height of the camera.

+     * Type: vec2

+     */

+    Resolution,

+    

+    /**

+     * The inverse of the resolution, 1/width and 1/height. 

+     * Type: vec2

+     */

+    ResolutionInverse,

+

+    /**

+     * Aspect ratio of the resolution currently set. Width/Height.

+     * Type: float

+     */

+    Aspect,

+

+    /**

+     * Camera position in world space.

+     * Type: vec3

+     */

+    CameraPosition,

+

+    /**

+     * Direction of the camera.

+     * Type: vec3

+     */

+    CameraDirection,

+

+    /**

+     * Left vector of the camera.

+     * Type: vec3

+     */

+    CameraLeft,

+

+    /**

+     * Up vector of the camera.

+     * Type: vec3

+     */

+    CameraUp,

+

+    /**

+     * Time in seconds since the application was started.

+     * Type: float

+     */

+    Time,

+

+    /**

+     * Time in seconds that the last frame took.

+     * Type: float

+     */

+    Tpf,

+

+    /**

+     * Frames per second.

+     * Type: float

+     */

+    FrameRate,

+}

diff --git a/engine/src/core/com/jme3/system/JmeSystem.java b/engine/src/core/com/jme3/system/JmeSystem.java
index 1a13a42..2e2d79a 100644
--- a/engine/src/core/com/jme3/system/JmeSystem.java
+++ b/engine/src/core/com/jme3/system/JmeSystem.java
@@ -1,136 +1,147 @@
-/*
- * 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.system;
-
-import com.jme3.asset.AssetManager;
-import com.jme3.audio.AudioRenderer;
-import java.io.File;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public class JmeSystem {
-
-    private static JmeSystemDelegate systemDelegate;
-
-    public static void setSystemDelegate(JmeSystemDelegate systemDelegate) {
-        JmeSystem.systemDelegate = systemDelegate;
-    }
-    
-    public static synchronized File getStorageFolder() {
-        checkDelegate();
-        return systemDelegate.getStorageFolder();
-    }
-
-    public static String getFullName() {
-        checkDelegate();
-        return systemDelegate.getFullName();
-    }
-
-    public static InputStream getResourceAsStream(String name) {
-        checkDelegate();
-        return systemDelegate.getResourceAsStream(name);
-    }
-
-    public static URL getResource(String name) {
-        checkDelegate();
-        return systemDelegate.getResource(name);
-    }
-
-    public static boolean trackDirectMemory() {
-        checkDelegate();
-        return systemDelegate.trackDirectMemory();
-    }
-
-    public static void setLowPermissions(boolean lowPerm) {
-        checkDelegate();
-        systemDelegate.setLowPermissions(lowPerm);
-    }
-
-    public static boolean isLowPermissions() {
-        checkDelegate();
-        return systemDelegate.isLowPermissions();
-    }
-
-    public static AssetManager newAssetManager(URL configFile) {
-        checkDelegate();
-        return systemDelegate.newAssetManager(configFile);
-    }
-
-    public static AssetManager newAssetManager() {
-        checkDelegate();
-        return systemDelegate.newAssetManager();
-    }
-
-    public static boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) {
-        checkDelegate();
-        return systemDelegate.showSettingsDialog(sourceSettings, loadFromRegistry);
-    }
-
-    public static Platform getPlatform() {
-        checkDelegate();
-        return systemDelegate.getPlatform();
-    }
-
-    public static JmeContext newContext(AppSettings settings, JmeContext.Type contextType) {
-        checkDelegate();
-        return systemDelegate.newContext(settings, contextType);
-    }
-
-    public static AudioRenderer newAudioRenderer(AppSettings settings) {
-        checkDelegate();
-        return systemDelegate.newAudioRenderer(settings);
-    }
-
-    public static void initialize(AppSettings settings) {
-        checkDelegate();
-        systemDelegate.initialize(settings);
-    }
-
-    @SuppressWarnings("unchecked")
-    private static void checkDelegate() {
-        if (systemDelegate == null) {
-            Class<JmeSystemDelegate> systemDelegateClass;
-            try {
-                systemDelegateClass = (Class<JmeSystemDelegate>) Class.forName("com.jme3.system.JmeDesktopSystem");
-                systemDelegate = systemDelegateClass.newInstance();
-            } catch (InstantiationException ex) {
-                Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);
-            } catch (IllegalAccessException ex) {
-                Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);
-            } catch (ClassNotFoundException ex) {
-                Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);
-            }
-        }
-    }
-}
+/*

+ * 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.system;

+

+import com.jme3.asset.AssetManager;

+import com.jme3.audio.AudioRenderer;

+import com.jme3.input.SoftTextDialogInput;

+import java.io.File;

+import java.io.InputStream;

+import java.net.URL;

+import java.util.logging.Level;

+import java.util.logging.Logger;

+

+public class JmeSystem {

+

+    private static JmeSystemDelegate systemDelegate;

+

+    public static void setSystemDelegate(JmeSystemDelegate systemDelegate) {

+        JmeSystem.systemDelegate = systemDelegate;

+    }

+    

+    public static synchronized File getStorageFolder() {

+        checkDelegate();

+        return systemDelegate.getStorageFolder();

+    }

+

+    public static String getFullName() {

+        checkDelegate();

+        return systemDelegate.getFullName();

+    }

+

+    public static InputStream getResourceAsStream(String name) {

+        checkDelegate();

+        return systemDelegate.getResourceAsStream(name);

+    }

+

+    public static URL getResource(String name) {

+        checkDelegate();

+        return systemDelegate.getResource(name);

+    }

+

+    public static boolean trackDirectMemory() {

+        checkDelegate();

+        return systemDelegate.trackDirectMemory();

+    }

+

+    public static void setLowPermissions(boolean lowPerm) {

+        checkDelegate();

+        systemDelegate.setLowPermissions(lowPerm);

+    }

+

+    public static boolean isLowPermissions() {

+        checkDelegate();

+        return systemDelegate.isLowPermissions();

+    }

+

+    public static void setSoftTextDialogInput(SoftTextDialogInput input) {

+        checkDelegate();

+        systemDelegate.setSoftTextDialogInput(input);

+    }

+

+    public static SoftTextDialogInput getSoftTextDialogInput() {

+        checkDelegate();

+        return systemDelegate.getSoftTextDialogInput();

+    }

+

+    public static AssetManager newAssetManager(URL configFile) {

+        checkDelegate();

+        return systemDelegate.newAssetManager(configFile);

+    }

+

+    public static AssetManager newAssetManager() {

+        checkDelegate();

+        return systemDelegate.newAssetManager();

+    }

+

+    public static boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) {

+        checkDelegate();

+        return systemDelegate.showSettingsDialog(sourceSettings, loadFromRegistry);

+    }

+

+    public static Platform getPlatform() {

+        checkDelegate();

+        return systemDelegate.getPlatform();

+    }

+

+    public static JmeContext newContext(AppSettings settings, JmeContext.Type contextType) {

+        checkDelegate();

+        return systemDelegate.newContext(settings, contextType);

+    }

+

+    public static AudioRenderer newAudioRenderer(AppSettings settings) {

+        checkDelegate();

+        return systemDelegate.newAudioRenderer(settings);

+    }

+

+    public static void initialize(AppSettings settings) {

+        checkDelegate();

+        systemDelegate.initialize(settings);

+    }

+

+    @SuppressWarnings("unchecked")

+    private static void checkDelegate() {

+        if (systemDelegate == null) {

+            Class<JmeSystemDelegate> systemDelegateClass;

+            try {

+                systemDelegateClass = (Class<JmeSystemDelegate>) Class.forName("com.jme3.system.JmeDesktopSystem");

+                systemDelegate = systemDelegateClass.newInstance();

+            } catch (InstantiationException ex) {

+                Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);

+            } catch (IllegalAccessException ex) {

+                Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);

+            } catch (ClassNotFoundException ex) {

+                Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);

+            }

+        }

+    }

+}

diff --git a/engine/src/core/com/jme3/system/JmeSystemDelegate.java b/engine/src/core/com/jme3/system/JmeSystemDelegate.java
index 60265ae..e0f6fa5 100644
--- a/engine/src/core/com/jme3/system/JmeSystemDelegate.java
+++ b/engine/src/core/com/jme3/system/JmeSystemDelegate.java
@@ -1,140 +1,149 @@
-/*
- * 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.system;
-
-import com.jme3.asset.AssetManager;
-import com.jme3.audio.AudioRenderer;
-import java.io.File;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.logging.Logger;
-
-/**
- *
- * @author Kirill Vainer, normenhansen
- */
-public abstract class JmeSystemDelegate {
-
-    protected final Logger logger = Logger.getLogger(JmeSystem.class.getName());
-    protected boolean initialized = false;
-    protected boolean lowPermissions = false;
-    protected File storageFolder = null;
-
-    public synchronized File getStorageFolder() {
-        if (lowPermissions) {
-            throw new UnsupportedOperationException("File system access restricted");
-        }
-        if (storageFolder == null) {
-            // Initialize storage folder
-            storageFolder = new File(System.getProperty("user.home"), ".jme3");
-            if (!storageFolder.exists()) {
-                storageFolder.mkdir();
-            }
-        }
-        return storageFolder;
-    }
-    
-    public String getFullName() {
-        return JmeVersion.FULL_NAME;
-    }
-
-    public InputStream getResourceAsStream(String name) {
-        return this.getClass().getResourceAsStream(name);
-    }
-
-    public URL getResource(String name) {
-        return this.getClass().getResource(name);
-    }
-
-    public boolean trackDirectMemory() {
-        return false;
-    }
-
-    public void setLowPermissions(boolean lowPerm) {
-        lowPermissions = lowPerm;
-    }
-
-    public boolean isLowPermissions() {
-        return lowPermissions;
-    }
-
-    public abstract AssetManager newAssetManager(URL configFile);
-
-    public abstract AssetManager newAssetManager();
-
-    public abstract boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry);
-
-    private boolean is64Bit(String arch) {
-        if (arch.equals("x86")) {
-            return false;
-        } else if (arch.equals("amd64")) {
-            return true;
-        } else if (arch.equals("x86_64")) {
-            return true;
-        } else if (arch.equals("ppc") || arch.equals("PowerPC")) {
-            return false;
-        } else if (arch.equals("ppc64")) {
-            return true;
-        } else if (arch.equals("i386") || arch.equals("i686")) {
-            return false;
-        } else if (arch.equals("universal")) {
-            return false;
-        } else {
-            throw new UnsupportedOperationException("Unsupported architecture: " + arch);
-        }
-    }
-
-    public Platform getPlatform() {
-        String os = System.getProperty("os.name").toLowerCase();
-        String arch = System.getProperty("os.arch").toLowerCase();
-        boolean is64 = is64Bit(arch);
-        if (os.contains("windows")) {
-            return is64 ? Platform.Windows64 : Platform.Windows32;
-        } else if (os.contains("linux") || os.contains("freebsd") || os.contains("sunos")) {
-            return is64 ? Platform.Linux64 : Platform.Linux32;
-        } else if (os.contains("mac os x") || os.contains("darwin")) {
-            if (arch.startsWith("ppc")) {
-                return is64 ? Platform.MacOSX_PPC64 : Platform.MacOSX_PPC32;
-            } else {
-                return is64 ? Platform.MacOSX64 : Platform.MacOSX32;
-            }
-        } else {
-            throw new UnsupportedOperationException("The specified platform: " + os + " is not supported.");
-        }
-    }
-
-    public abstract JmeContext newContext(AppSettings settings, JmeContext.Type contextType);
-
-    public abstract AudioRenderer newAudioRenderer(AppSettings settings);
-
-    public abstract void initialize(AppSettings settings);
-}
+/*

+ * 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.system;

+

+import com.jme3.asset.AssetManager;

+import com.jme3.audio.AudioRenderer;

+import com.jme3.input.SoftTextDialogInput;

+import java.io.File;

+import java.io.InputStream;

+import java.net.URL;

+import java.util.logging.Logger;

+

+/**

+ *

+ * @author Kirill Vainer, normenhansen

+ */

+public abstract class JmeSystemDelegate {

+

+    protected final Logger logger = Logger.getLogger(JmeSystem.class.getName());

+    protected boolean initialized = false;

+    protected boolean lowPermissions = false;

+    protected File storageFolder = null;

+    protected SoftTextDialogInput softTextDialogInput = null;

+

+    public synchronized File getStorageFolder() {

+        if (lowPermissions) {

+            throw new UnsupportedOperationException("File system access restricted");

+        }

+        if (storageFolder == null) {

+            // Initialize storage folder

+            storageFolder = new File(System.getProperty("user.home"), ".jme3");

+            if (!storageFolder.exists()) {

+                storageFolder.mkdir();

+            }

+        }

+        return storageFolder;

+    }

+    

+    public String getFullName() {

+        return JmeVersion.FULL_NAME;

+    }

+

+    public InputStream getResourceAsStream(String name) {

+        return this.getClass().getResourceAsStream(name);

+    }

+

+    public URL getResource(String name) {

+        return this.getClass().getResource(name);

+    }

+

+    public boolean trackDirectMemory() {

+        return false;

+    }

+

+    public void setLowPermissions(boolean lowPerm) {

+        lowPermissions = lowPerm;

+    }

+

+    public boolean isLowPermissions() {

+        return lowPermissions;

+    }

+

+    public void setSoftTextDialogInput(SoftTextDialogInput input) {

+        softTextDialogInput = input;

+    }

+    public SoftTextDialogInput getSoftTextDialogInput() {

+        return softTextDialogInput;

+    }

+

+    public abstract AssetManager newAssetManager(URL configFile);

+

+    public abstract AssetManager newAssetManager();

+

+    public abstract boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry);

+

+    private boolean is64Bit(String arch) {

+        if (arch.equals("x86")) {

+            return false;

+        } else if (arch.equals("amd64")) {

+            return true;

+        } else if (arch.equals("x86_64")) {

+            return true;

+        } else if (arch.equals("ppc") || arch.equals("PowerPC")) {

+            return false;

+        } else if (arch.equals("ppc64")) {

+            return true;

+        } else if (arch.equals("i386") || arch.equals("i686")) {

+            return false;

+        } else if (arch.equals("universal")) {

+            return false;

+        } else {

+            throw new UnsupportedOperationException("Unsupported architecture: " + arch);

+        }

+    }

+

+    public Platform getPlatform() {

+        String os = System.getProperty("os.name").toLowerCase();

+        String arch = System.getProperty("os.arch").toLowerCase();

+        boolean is64 = is64Bit(arch);

+        if (os.contains("windows")) {

+            return is64 ? Platform.Windows64 : Platform.Windows32;

+        } else if (os.contains("linux") || os.contains("freebsd") || os.contains("sunos")) {

+            return is64 ? Platform.Linux64 : Platform.Linux32;

+        } else if (os.contains("mac os x") || os.contains("darwin")) {

+            if (arch.startsWith("ppc")) {

+                return is64 ? Platform.MacOSX_PPC64 : Platform.MacOSX_PPC32;

+            } else {

+                return is64 ? Platform.MacOSX64 : Platform.MacOSX32;

+            }

+        } else {

+            throw new UnsupportedOperationException("The specified platform: " + os + " is not supported.");

+        }

+    }

+

+    public abstract JmeContext newContext(AppSettings settings, JmeContext.Type contextType);

+

+    public abstract AudioRenderer newAudioRenderer(AppSettings settings);

+

+    public abstract void initialize(AppSettings settings);

+}

diff --git a/engine/src/core/com/jme3/util/BufferUtils.java b/engine/src/core/com/jme3/util/BufferUtils.java
index f0cc698..2e59e97 100644
--- a/engine/src/core/com/jme3/util/BufferUtils.java
+++ b/engine/src/core/com/jme3/util/BufferUtils.java
@@ -42,6 +42,7 @@
 import java.util.Collections;
 import java.util.Map;
 import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -1144,6 +1145,47 @@
         }
     }
     
+    private static final AtomicBoolean loadedMethods = new AtomicBoolean(false);
+    private static Method cleanerMethod = null;
+    private static Method cleanMethod = null;
+    private static Method viewedBufferMethod = null;
+    private static Method freeMethod = null;
+    
+    private static Method loadMethod(String className, String methodName){
+        try {
+            Method method = Class.forName(className).getMethod(methodName);
+            method.setAccessible(true);
+            return method;
+        } catch (NoSuchMethodException ex) {
+            return null; // the method was not found
+        } catch (SecurityException ex) {
+            return null; // setAccessible not allowed by security policy
+        } catch (ClassNotFoundException ex) {
+            return null; // the direct buffer implementation was not found
+        }
+    }
+    
+    private static void loadCleanerMethods() {
+        // If its already true, exit, if not, set it to true.
+        if (loadedMethods.getAndSet(true)) {
+            return;
+        }
+        // This could potentially be called many times if used from multiple
+        // threads
+        synchronized (loadedMethods) {
+            // Oracle JRE / OpenJDK
+            cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner");
+            cleanMethod = loadMethod("sun.misc.Cleaner", "clean");
+            viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer");
+            
+            // Apache Harmony
+            freeMethod = loadMethod("org.apache.harmony.nio.internal.DirectBuffer", "free");
+            
+            // GUN Classpath (not likely)
+            //finalizeMethod = loadMethod("java.nio.DirectByteBufferImpl", "finalize");
+        }
+    }
+    
     /**
     * Direct buffers are garbage collected by using a phantom reference and a
     * reference queue. Every once a while, the JVM checks the reference queue and
@@ -1157,27 +1199,27 @@
     *          
     */
     public static void destroyDirectBuffer(Buffer toBeDestroyed) {
-    
         if (!toBeDestroyed.isDirect()) {
             return;
         }
+        
+        loadCleanerMethods();
+        
         try {
-            Method cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner");
-            cleanerMethod.setAccessible(true);
-            Object cleaner = cleanerMethod.invoke(toBeDestroyed);
-            if (cleaner != null) {
-                Method cleanMethod = cleaner.getClass().getMethod("clean");
-                cleanMethod.setAccessible(true);
-                cleanMethod.invoke(cleaner);
+            if (freeMethod != null) {
+                freeMethod.invoke(toBeDestroyed);
             } else {
-                // Try the alternate approach of getting the viewed buffer
-                Method viewedBufferMethod = toBeDestroyed.getClass().getMethod("viewedBuffer");
-                viewedBufferMethod.setAccessible(true);
-                Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed);
-                if (viewedBuffer != null) {
-                    destroyDirectBuffer( (Buffer)viewedBuffer );
+                Object cleaner = cleanerMethod.invoke(toBeDestroyed);
+                if (cleaner != null) {
+                    cleanMethod.invoke(cleaner);
                 } else {
-                    Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed);
+                    // Try the alternate approach of getting the viewed buffer first
+                    Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed);
+                    if (viewedBuffer != null) {
+                        destroyDirectBuffer((Buffer) viewedBuffer);
+                    } else {
+                        Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed);
+                    }
                 }
             }
         } catch (IllegalAccessException ex) {
@@ -1186,11 +1228,8 @@
             Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
         } catch (InvocationTargetException ex) {
             Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
-        } catch (NoSuchMethodException ex) {
-            Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
         } catch (SecurityException ex) {
             Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
         }
     }
-
 }
diff --git a/engine/src/core/com/jme3/util/IntMap.java b/engine/src/core/com/jme3/util/IntMap.java
index edf659b..1b91119 100644
--- a/engine/src/core/com/jme3/util/IntMap.java
+++ b/engine/src/core/com/jme3/util/IntMap.java
@@ -44,8 +44,6 @@
  * @author Nate 
  */
 public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
-
-    private final IntMapIterator iterator = new IntMapIterator();
             
     private Entry[] table;
     private final float loadFactor;
@@ -200,8 +198,9 @@
     }
 
     public Iterator<Entry<T>> iterator() {
-        iterator.beginUse();
-        return iterator;
+        IntMapIterator it = new IntMapIterator();
+        it.beginUse();
+        return it;
     }
 
     final class IntMapIterator implements Iterator<Entry<T>> {
diff --git a/engine/src/core/com/jme3/util/TangentBinormalGenerator.java b/engine/src/core/com/jme3/util/TangentBinormalGenerator.java
index 88f6822..8816b91 100644
--- a/engine/src/core/com/jme3/util/TangentBinormalGenerator.java
+++ b/engine/src/core/com/jme3/util/TangentBinormalGenerator.java
@@ -117,7 +117,13 @@
             }
         } else {
             Geometry geom = (Geometry) scene;
-            generate(geom.getMesh());
+            Mesh mesh = geom.getMesh();
+            
+            // Check to ensure mesh has texcoords and normals before generating
+            if (mesh.getBuffer(Type.TexCoord) != null 
+             && mesh.getBuffer(Type.Normal) != null){
+                generate(geom.getMesh());
+            }
         }
     }
     
@@ -640,7 +646,7 @@
         lineMesh.setBuffer(Type.Color, 4, lineColor);
         
         lineMesh.setStatic();
-        lineMesh.setInterleaved();
+        //lineMesh.setInterleaved();
         return lineMesh;
     }
     
@@ -733,7 +739,7 @@
         lineMesh.setBuffer(Type.Color, 4, lineColor);
         
         lineMesh.setStatic();
-        lineMesh.setInterleaved();
+        //lineMesh.setInterleaved();
         return lineMesh;
     }
 }
diff --git a/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java b/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java
index 3f471f7..9513bcf 100644
--- a/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java
+++ b/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java
@@ -33,6 +33,7 @@
 package com.jme3.texture.plugins;
 
 import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetLoadException;
 import com.jme3.asset.AssetLoader;
 import com.jme3.asset.TextureKey;
 import com.jme3.texture.Image;
@@ -185,27 +186,30 @@
     public Image load(InputStream in, boolean flipY) throws IOException{
         ImageIO.setUseCache(false);
         BufferedImage img = ImageIO.read(in);
-        if (img == null)
+        if (img == null){
             return null;
-
+        }
         return load(img, flipY);
     }
 
     public Object load(AssetInfo info) throws IOException {
-        if (ImageIO.getImageWritersBySuffix(info.getKey().getExtension()) != null){
-            
+        if (ImageIO.getImageReadersBySuffix(info.getKey().getExtension()) != null){
             boolean flip = ((TextureKey) info.getKey()).isFlipY();
             InputStream in = null;
             try {
                 in = info.openStream();
                 Image img = load(in, flip);
+                if (img == null){
+                    throw new AssetLoadException("The given image cannot be loaded " + info.getKey());
+                }
                 return img;
             } finally {
                 if (in != null){
                     in.close();
                 }
             }
+        }else{
+            throw new AssetLoadException("The extension " + info.getKey().getExtension() + " is not supported");
         }
-        return null;
     }
 }
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag b/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag
deleted file mode 100644
index 5e77548..0000000
--- a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag
+++ /dev/null
@@ -1,13 +0,0 @@
-uniform bool m_UseTex;

-uniform sampler2D m_Texture;

-uniform vec4 m_Color;

-

-varying vec2 texCoord;

-varying vec4 color;

-

-void main() {

-    vec4 texVal = texture2D(m_Texture, texCoord);

-    texVal = m_UseTex ? texVal : vec4(1.0);

-    gl_FragColor = texVal * color * m_Color;

-}

-

diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.frag b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.frag
new file mode 100644
index 0000000..31f49d6
--- /dev/null
+++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.frag
@@ -0,0 +1,6 @@
+uniform vec4 m_Color;

+

+void main() { 

+    gl_FragColor = m_Color;

+}

+

diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.j3md b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.j3md
new file mode 100644
index 0000000..d0213e9
--- /dev/null
+++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.j3md
@@ -0,0 +1,19 @@
+MaterialDef Default GUI {

+

+    MaterialParameters {      

+        Vector4 Color (Color)

+    }

+

+    Technique {

+        VertexShader GLSL100:   Common/MatDefs/Nifty/NiftyQuad.vert

+        FragmentShader GLSL100: Common/MatDefs/Nifty/NiftyQuad.frag

+

+        WorldParameters {

+            WorldViewProjectionMatrix

+        }   

+    }

+

+    Technique FixedFunc {

+    }

+

+}
\ No newline at end of file
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.vert b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.vert
new file mode 100644
index 0000000..1eb1616
--- /dev/null
+++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.vert
@@ -0,0 +1,9 @@
+uniform mat4 g_WorldViewProjectionMatrix;

+

+attribute vec4 inPosition;

+

+

+void main() {

+    vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;

+    gl_Position = vec4(pos, 0.0, 1.0);

+}
\ No newline at end of file
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.frag b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.frag
new file mode 100644
index 0000000..1f0a645
--- /dev/null
+++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.frag
@@ -0,0 +1,6 @@
+varying vec4 color;

+

+void main() { 

+    gl_FragColor = color;

+}

+

diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.j3md b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.j3md
new file mode 100644
index 0000000..4b3b22d
--- /dev/null
+++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.j3md
@@ -0,0 +1,18 @@
+MaterialDef Default GUI {

+

+    MaterialParameters {              

+    }

+

+    Technique {

+        VertexShader GLSL100:   Common/MatDefs/Nifty/NiftyQuadGrad.vert

+        FragmentShader GLSL100: Common/MatDefs/Nifty/NiftyQuadGrad.frag

+

+        WorldParameters {

+            WorldViewProjectionMatrix

+        }   

+    }

+

+    Technique FixedFunc {

+    }

+

+}
\ No newline at end of file
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.vert
similarity index 70%
copy from engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert
copy to engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.vert
index 67c864d..0ecaee4 100644
--- a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert
+++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.vert
@@ -2,15 +2,13 @@
 

 attribute vec4 inPosition;

 attribute vec4 inColor;

-attribute vec2 inTexCoord;

+attribute vec4 inIndex;

 

-varying vec2 texCoord;

 varying vec4 color;

 

 void main() {

     vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;

     gl_Position = vec4(pos, 0.0, 1.0);

 

-    texCoord = inTexCoord;

-    color = inColor;

+    color = inIndex;

 }
\ No newline at end of file
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.frag b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.frag
new file mode 100644
index 0000000..b2b4b95
--- /dev/null
+++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.frag
@@ -0,0 +1,10 @@
+uniform sampler2D m_Texture;

+uniform vec4 m_Color; 

+

+varying vec2 texCoord;

+

+void main() {

+    vec4 texVal = texture2D(m_Texture, texCoord);

+    gl_FragColor = texVal * m_Color ;

+}

+

diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.j3md
similarity index 62%
rename from engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md
rename to engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.j3md
index 9ba39b1..07f5e92 100644
--- a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md
+++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.j3md
@@ -2,13 +2,12 @@
 

     MaterialParameters {

         Texture2D Texture

-        Boolean UseTex

         Vector4 Color (Color)

     }

 

     Technique {

-        VertexShader GLSL100:   Common/MatDefs/Nifty/Nifty.vert

-        FragmentShader GLSL100: Common/MatDefs/Nifty/Nifty.frag

+        VertexShader GLSL100:   Common/MatDefs/Nifty/NiftyTex.vert

+        FragmentShader GLSL100: Common/MatDefs/Nifty/NiftyTex.frag

 

         WorldParameters {

             WorldViewProjectionMatrix

diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.vert
similarity index 72%
rename from engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert
rename to engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.vert
index 67c864d..c5c3dc1 100644
--- a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert
+++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.vert
@@ -1,16 +1,13 @@
 uniform mat4 g_WorldViewProjectionMatrix;

 

 attribute vec4 inPosition;

-attribute vec4 inColor;

 attribute vec2 inTexCoord;

 

 varying vec2 texCoord;

-varying vec4 color;

 

 void main() {

     vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;

     gl_Position = vec4(pos, 0.0, 1.0);

 

-    texCoord = inTexCoord;

-    color = inColor;

+    texCoord = inTexCoord;    

 }
\ No newline at end of file
diff --git a/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java b/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java
index 0b80131..01c2ac7 100644
--- a/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java
+++ b/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java
@@ -1,233 +1,274 @@
-/*
- * 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.niftygui;
-
-import com.jme3.input.InputManager;
-import com.jme3.input.KeyInput;
-import com.jme3.input.RawInputListener;
-import com.jme3.input.event.*;
-import de.lessvoid.nifty.Nifty;
-import de.lessvoid.nifty.NiftyInputConsumer;
-import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader;
-import de.lessvoid.nifty.input.keyboard.KeyboardInputEvent;
-import de.lessvoid.nifty.spi.input.InputSystem;
-import java.util.ArrayList;
-
-public class InputSystemJme implements InputSystem, RawInputListener {
-
-    private final ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();
-
-    private InputManager inputManager;
-
-    private boolean isDragging = false, niftyOwnsDragging = false;
-    private boolean pressed = false;
-    private int buttonIndex;
-    private int x, y;
-    private int height;
-
-    private boolean shiftDown = false;
-    private boolean ctrlDown  = false;
-
-    private Nifty nifty;
-
-    public InputSystemJme(InputManager inputManager){
-        this.inputManager = inputManager;
-    }
-
-    public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) {
-    }
-
-    public void setNifty(Nifty nifty) {
-        this.nifty = nifty;
-    }
-
-    /**
-     * @param height The height of the viewport. Used to convert
-     * buttom-left origin to upper-left origin.
-     */
-    public void setHeight(int height){
-        this.height = height;
-    }
-
-    public void setMousePosition(int x, int y){
-    }
-
-    public void beginInput(){
-    }
-
-    public void endInput(){
-        boolean result = nifty.update();
-    }
-
-    private void onTouchEventQueued(TouchEvent evt, NiftyInputConsumer nic) {  
-        boolean consumed = false;
-
-        x = (int) evt.getX();
-        y = (int) (height - evt.getY());
-
-        switch (evt.getType()) {
-           case DOWN:
-               consumed = nic.processMouseEvent(x, y, 0, 0, false);
-               isDragging = true;
-               niftyOwnsDragging = consumed;
-               if (consumed){
-                   evt.setConsumed();
-               }
-
-               break;
-
-           case UP:
-               if (niftyOwnsDragging){
-                   consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed);
-                   if (consumed){
-                       evt.setConsumed();
-                   }
-               }
-
-               isDragging = false;
-               niftyOwnsDragging = false;
-               break;
-       }
-    }
-    
-    private void onMouseMotionEventQueued(MouseMotionEvent evt, NiftyInputConsumer nic) {
-        x = evt.getX();
-        y = height - evt.getY();
-        nic.processMouseEvent(x, y, evt.getDeltaWheel(), buttonIndex, pressed);
-//        if (nic.processMouseEvent(niftyEvt) /*|| nifty.getCurrentScreen().isMouseOverElement()*/){
-            // Do not consume motion events
-            //evt.setConsumed();
-//        }
-    }
-
-    private void onMouseButtonEventQueued(MouseButtonEvent evt, NiftyInputConsumer nic) {
-        boolean wasPressed = pressed;
-        boolean forwardToNifty = true;
-        
-        buttonIndex = evt.getButtonIndex();
-        pressed = evt.isPressed();
-        
-        // Mouse button raised. End dragging
-        if (wasPressed && !pressed){
-            if (!niftyOwnsDragging){
-                forwardToNifty = false;
-            }
-            isDragging = false;
-            niftyOwnsDragging = false;
-        }
-
-        boolean consumed = false;
-        if (forwardToNifty){
-            consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed);
-            if (consumed){
-                evt.setConsumed();
-            }
-        }
-        
-        // Mouse button pressed. Begin dragging
-        if (!wasPressed && pressed){
-            isDragging = true;
-            niftyOwnsDragging = consumed;
-        }
-    }
-
-    private void onKeyEventQueued(KeyInputEvent evt, NiftyInputConsumer nic) {
-        int code = evt.getKeyCode();
-
-        if (code == KeyInput.KEY_LSHIFT || code == KeyInput.KEY_RSHIFT) {
-            shiftDown = evt.isPressed();
-        } else if (code == KeyInput.KEY_LCONTROL || code == KeyInput.KEY_RCONTROL) {
-            ctrlDown = evt.isPressed();
-        }
-        
-        KeyboardInputEvent keyEvt = new KeyboardInputEvent(code,
-                                                           evt.getKeyChar(),
-                                                           evt.isPressed(),
-                                                           shiftDown,
-                                                           ctrlDown);
-
-        if (nic.processKeyboardEvent(keyEvt)){
-            evt.setConsumed();
-        }
-    }
-    
-    public void onMouseMotionEvent(MouseMotionEvent evt) {
-        // Only forward the event if there's actual motion involved.
-        if (inputManager.isCursorVisible() && (evt.getDX() != 0 ||
-                                               evt.getDY() != 0 ||
-                                               evt.getDeltaWheel() != 0)){
-            inputQueue.add(evt);
-        }
-    }
-
-    public void onMouseButtonEvent(MouseButtonEvent evt) {
-        if (inputManager.isCursorVisible() && evt.getButtonIndex() >= 0 && evt.getButtonIndex() <= 2){
-            inputQueue.add(evt);
-        }
-    }
-    
-    public void onJoyAxisEvent(JoyAxisEvent evt) {
-    }
-
-    public void onJoyButtonEvent(JoyButtonEvent evt) {
-    }
-    
-    public void onKeyEvent(KeyInputEvent evt) {
-        inputQueue.add(evt);
-    }
-    
-    public void onTouchEvent(TouchEvent evt) {     
-        inputQueue.add(evt);
-    }
-
-    public void forwardEvents(NiftyInputConsumer nic) {
-        int queueSize = inputQueue.size();
-
-        for (int i = 0; i < queueSize; i++){
-            InputEvent evt = inputQueue.get(i);
-            if (evt instanceof MouseMotionEvent){
-                onMouseMotionEventQueued( (MouseMotionEvent)evt, nic);
-            }else if (evt instanceof MouseButtonEvent){
-                onMouseButtonEventQueued( (MouseButtonEvent)evt, nic);
-            }else if (evt instanceof KeyInputEvent){
-                onKeyEventQueued( (KeyInputEvent)evt, nic);
-            }else if (evt instanceof TouchEvent){
-                onTouchEventQueued( (TouchEvent)evt, nic);
-            }
-        }
-
-        inputQueue.clear();
-    }
-    
-    
-}
+/*

+ * 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.niftygui;

+

+import com.jme3.input.InputManager;

+import com.jme3.input.KeyInput;

+import com.jme3.input.RawInputListener;

+import com.jme3.input.SoftTextDialogInput;

+import com.jme3.input.controls.SoftTextDialogInputListener;

+import com.jme3.input.event.*;

+import com.jme3.system.JmeSystem;

+import de.lessvoid.nifty.Nifty;

+import de.lessvoid.nifty.NiftyInputConsumer;

+import de.lessvoid.nifty.controls.TextField;

+import de.lessvoid.nifty.controls.nullobjects.TextFieldNull;

+import de.lessvoid.nifty.elements.Element;

+import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader;

+import de.lessvoid.nifty.input.keyboard.KeyboardInputEvent;

+import de.lessvoid.nifty.spi.input.InputSystem;

+import java.util.ArrayList;

+import java.util.logging.Level;

+import java.util.logging.Logger;

+

+public class InputSystemJme implements InputSystem, RawInputListener {

+

+    private final ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();

+    private InputManager inputManager;

+    private boolean isDragging = false, niftyOwnsDragging = false;

+    private boolean pressed = false;

+    private int buttonIndex;

+    private int x, y;

+    private int height;

+    private boolean shiftDown = false;

+    private boolean ctrlDown = false;

+    private Nifty nifty;

+

+    public InputSystemJme(InputManager inputManager) {

+        this.inputManager = inputManager;

+    }

+

+    public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) {

+    }

+

+    public void setNifty(Nifty nifty) {

+        this.nifty = nifty;

+    }

+

+    /**

+     * @param height The height of the viewport. Used to convert

+     * buttom-left origin to upper-left origin.

+     */

+    public void setHeight(int height) {

+        this.height = height;

+    }

+

+    public void setMousePosition(int x, int y) {

+    }

+

+    public void beginInput() {

+    }

+

+    public void endInput() {

+        boolean result = nifty.update();

+    }

+

+    private void onTouchEventQueued(TouchEvent evt, NiftyInputConsumer nic) {

+        boolean consumed = false;

+

+        x = (int) evt.getX();

+        y = (int) (height - evt.getY());

+

+        if (!inputManager.getSimulateMouse()) {            

+            switch (evt.getType()) {

+                case DOWN:

+                    consumed = nic.processMouseEvent(x, y, 0, 0, true);

+                    isDragging = true;

+                    niftyOwnsDragging = consumed;

+                    if (consumed) {

+                        evt.setConsumed();

+                    }

+

+                    break;

+

+                case UP:

+                    if (niftyOwnsDragging) {

+                        consumed = nic.processMouseEvent(x, y, 0, 0, false);

+                        if (consumed) {

+                            evt.setConsumed();

+                        }

+                    }

+

+                    isDragging = false;

+                    niftyOwnsDragging = false;

+

+                    if (consumed) {

+                        processSoftKeyboard();

+                    }

+

+                    break;

+            }

+        }

+    }

+

+    private void onMouseMotionEventQueued(MouseMotionEvent evt, NiftyInputConsumer nic) {

+        x = evt.getX();

+        y = height - evt.getY();

+        nic.processMouseEvent(x, y, evt.getDeltaWheel(), buttonIndex, pressed);

+//        if (nic.processMouseEvent(niftyEvt) /*|| nifty.getCurrentScreen().isMouseOverElement()*/){

+        // Do not consume motion events

+        //evt.setConsumed();

+//        }

+    }

+

+    private void onMouseButtonEventQueued(MouseButtonEvent evt, NiftyInputConsumer nic) {

+        boolean wasPressed = pressed;

+        boolean forwardToNifty = true;

+

+        buttonIndex = evt.getButtonIndex();

+        pressed = evt.isPressed();

+

+        // Mouse button raised. End dragging

+        if (wasPressed && !pressed) {

+            if (!niftyOwnsDragging) {

+                forwardToNifty = false;

+            }

+            isDragging = false;

+            niftyOwnsDragging = false;

+        }

+

+        boolean consumed = false;

+        if (forwardToNifty) {

+            consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed);

+            if (consumed) {

+                evt.setConsumed();

+            }

+        }

+

+        // Mouse button pressed. Begin dragging

+        if (!wasPressed && pressed) {

+            isDragging = true;

+            niftyOwnsDragging = consumed;

+        }

+

+        if (consumed && pressed) {

+            processSoftKeyboard();

+        }

+

+    }

+

+    private void onKeyEventQueued(KeyInputEvent evt, NiftyInputConsumer nic) {

+        int code = evt.getKeyCode();

+

+        if (code == KeyInput.KEY_LSHIFT || code == KeyInput.KEY_RSHIFT) {

+            shiftDown = evt.isPressed();

+        } else if (code == KeyInput.KEY_LCONTROL || code == KeyInput.KEY_RCONTROL) {

+            ctrlDown = evt.isPressed();

+        }

+

+        KeyboardInputEvent keyEvt = new KeyboardInputEvent(code,

+                evt.getKeyChar(),

+                evt.isPressed(),

+                shiftDown,

+                ctrlDown);

+

+        if (nic.processKeyboardEvent(keyEvt)) {

+            evt.setConsumed();

+        }

+    }

+

+    public void onMouseMotionEvent(MouseMotionEvent evt) {

+        // Only forward the event if there's actual motion involved.

+        if (inputManager.isCursorVisible() && (evt.getDX() != 0

+                || evt.getDY() != 0

+                || evt.getDeltaWheel() != 0)) {

+            inputQueue.add(evt);

+        }

+    }

+

+    public void onMouseButtonEvent(MouseButtonEvent evt) {

+        if (inputManager.isCursorVisible() && evt.getButtonIndex() >= 0 && evt.getButtonIndex() <= 2) {

+            inputQueue.add(evt);

+        }

+    }

+

+    public void onJoyAxisEvent(JoyAxisEvent evt) {

+    }

+

+    public void onJoyButtonEvent(JoyButtonEvent evt) {

+    }

+

+    public void onKeyEvent(KeyInputEvent evt) {

+        inputQueue.add(evt);

+    }

+

+    public void onTouchEvent(TouchEvent evt) {

+        inputQueue.add(evt);

+    }

+

+    public void forwardEvents(NiftyInputConsumer nic) {

+        int queueSize = inputQueue.size();

+

+        for (int i = 0; i < queueSize; i++) {

+            InputEvent evt = inputQueue.get(i);

+            if (evt instanceof MouseMotionEvent) {

+                onMouseMotionEventQueued((MouseMotionEvent) evt, nic);

+            } else if (evt instanceof MouseButtonEvent) {

+                onMouseButtonEventQueued((MouseButtonEvent) evt, nic);

+            } else if (evt instanceof KeyInputEvent) {

+                onKeyEventQueued((KeyInputEvent) evt, nic);

+            } else if (evt instanceof TouchEvent) {

+                onTouchEventQueued((TouchEvent) evt, nic);

+            }

+        }

+

+        inputQueue.clear();

+    }

+

+    private void processSoftKeyboard() {

+        SoftTextDialogInput softTextDialogInput = JmeSystem.getSoftTextDialogInput();

+        if (softTextDialogInput != null) {

+

+            Element element = nifty.getCurrentScreen().getFocusHandler().getKeyboardFocusElement();

+            if (element != null) {

+                final TextField textField = element.getNiftyControl(TextField.class);

+                if (textField != null && !(textField instanceof TextFieldNull)) {

+                    Logger.getLogger(InputSystemJme.class.getName()).log(Level.INFO, "Current TextField: {0}", textField.getId());

+                    String initialValue = textField.getText();

+                    if (initialValue == null) {

+                        initialValue = "";

+                    }

+

+                    softTextDialogInput.requestDialog(SoftTextDialogInput.TEXT_ENTRY_DIALOG, "Enter Text", initialValue, new SoftTextDialogInputListener() {

+

+                        public void onSoftText(int action, String text) {

+                            if (action == SoftTextDialogInputListener.COMPLETE) {

+                                textField.setText(text);

+                            }

+                        }

+                    });

+                }

+            }

+        }

+

+    }

+}

diff --git a/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java b/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java
index 8977ed6..1f36a97 100644
--- a/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java
+++ b/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java
@@ -29,7 +29,6 @@
  * 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.niftygui;

 

 import com.jme3.font.BitmapText;

@@ -60,140 +59,148 @@
 import java.util.HashMap;

 

 public class RenderDeviceJme implements RenderDevice {

-

+    

     private NiftyJmeDisplay display;

     private RenderManager rm;

     private Renderer r;

-    

     private HashMap<String, BitmapText> textCacheLastFrame = new HashMap<String, BitmapText>();

     private HashMap<String, BitmapText> textCacheCurrentFrame = new HashMap<String, BitmapText>();

-

     private final Quad quad = new Quad(1, -1, true);

     private final Geometry quadGeom = new Geometry("nifty-quad", quad);

     private final Material niftyMat;

-

+    private final Material niftyQuadMat;

+    private final Material niftyQuadGradMat;    

     private boolean clipWasSet = false;

     private BlendMode blendMode = null;

-

     private VertexBuffer quadDefaultTC = quad.getBuffer(Type.TexCoord);

     private VertexBuffer quadModTC = quadDefaultTC.clone();

     private VertexBuffer quadColor;

-

     private Matrix4f tempMat = new Matrix4f();

     private ColorRGBA tempColor = new ColorRGBA();

-

-    public RenderDeviceJme(NiftyJmeDisplay display){

+    

+    public RenderDeviceJme(NiftyJmeDisplay display) {

         this.display = display;

-

+        

         quadColor = new VertexBuffer(Type.Color);

         quadColor.setNormalized(true);

         ByteBuffer bb = BufferUtils.createByteBuffer(4 * 4);

         quadColor.setupData(Usage.Stream, 4, Format.UnsignedByte, bb);

         quad.setBuffer(quadColor);

-

+        

         quadModTC.setUsage(Usage.Stream);

-

-        niftyMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/Nifty.j3md");

+        

+        //Color + texture color material for text and images

+        niftyMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/NiftyTex.j3md");

         niftyMat.getAdditionalRenderState().setDepthTest(false);

+        //Color material for uniform colored quads

+        niftyQuadMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/NiftyQuad.j3md");

+        niftyQuadMat.getAdditionalRenderState().setDepthTest(false);        

+        

+        //vertex color only for gradient quads (although i didn't find a way in nifty to make a gradient using vertex color)

+        niftyQuadGradMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/NiftyQuadGrad.j3md");

+        niftyQuadGradMat.getAdditionalRenderState().setDepthTest(false);        

+        

     }

-

+    

     public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) {

     }

-

-    public void setRenderManager(RenderManager rm){

+    

+    public void setRenderManager(RenderManager rm) {

         this.rm = rm;

         this.r = rm.getRenderer();

     }

 

     // TODO: Cursor support

-    public MouseCursor createMouseCursor(String str, int x, int y){

+    public MouseCursor createMouseCursor(String str, int x, int y) {

         return new MouseCursor() {

+            

             public void dispose() {

             }

         };

     }

-

-    public void enableMouseCursor(MouseCursor cursor){

+    

+    public void enableMouseCursor(MouseCursor cursor) {

     }

-

-    public void disableMouseCursor(){

+    

+    public void disableMouseCursor() {

     }

-

+    

     public RenderImage createImage(String filename, boolean linear) {

         return new RenderImageJme(filename, linear, display);

     }

-

+    

     public RenderFont createFont(String filename) {

         return new RenderFontJme(filename, display);

     }

-

+    

     public void beginFrame() {

     }

-

+    

     public void endFrame() {

         HashMap<String, BitmapText> temp = textCacheLastFrame;

         textCacheLastFrame = textCacheCurrentFrame;

         textCacheCurrentFrame = temp;

         textCacheCurrentFrame.clear();

-        

+

 //        System.exit(1);

     }

-

+    

     public int getWidth() {

         return display.getWidth();

     }

-

+    

     public int getHeight() {

         return display.getHeight();

     }

-

+    

     public void clear() {

     }

-

+    

     public void setBlendMode(BlendMode blendMode) {

-        if (this.blendMode != blendMode){

+        if (this.blendMode != blendMode) {

             this.blendMode = blendMode;

         }

     }

-

-    private RenderState.BlendMode convertBlend(){

-        if (blendMode == null)

+    

+    private RenderState.BlendMode convertBlend() {

+        if (blendMode == null) {

             return RenderState.BlendMode.Off;

-        else if (blendMode == BlendMode.BLEND)

+        } else if (blendMode == BlendMode.BLEND) {

             return RenderState.BlendMode.Alpha;

-        else if (blendMode == BlendMode.MULIPLY)

+        } else if (blendMode == BlendMode.MULIPLY) {

             return RenderState.BlendMode.Modulate;

-        else

+        } else {

             throw new UnsupportedOperationException();

-    }

-

-    private int convertColor(Color color){

-        int color2 = 0;

-        color2 |= ((int)(255.0 * color.getAlpha())) << 24;

-        color2 |= ((int)(255.0 * color.getBlue())) << 16;

-        color2 |= ((int)(255.0 * color.getGreen())) << 8;

-        color2 |= ((int)(255.0 * color.getRed()));

-        return color2;

-    }

-

-    private ColorRGBA convertColor(Color inColor, ColorRGBA outColor){

-        return outColor.set(inColor.getRed(), inColor.getGreen(), inColor.getBlue(), inColor.getAlpha());

-    }

-

-    private void setColor(Color color){

-        ByteBuffer buf = (ByteBuffer) quadColor.getData();

-        buf.rewind();

-

-        int color2 = convertColor(color);

-        buf.putInt(color2);

-        buf.putInt(color2);

-        buf.putInt(color2);

-        buf.putInt(color2);

-

-        buf.flip();

-        quadColor.updateData(buf);

+        }

     }

     

+    private int convertColor(Color color) {

+        int color2 = 0;

+        color2 |= ((int) (255.0 * color.getAlpha())) << 24;

+        color2 |= ((int) (255.0 * color.getBlue())) << 16;

+        color2 |= ((int) (255.0 * color.getGreen())) << 8;

+        color2 |= ((int) (255.0 * color.getRed()));

+        return color2;

+    }

+    

+    private ColorRGBA convertColor(Color inColor, ColorRGBA outColor) {

+        return outColor.set(inColor.getRed(), inColor.getGreen(), inColor.getBlue(), inColor.getAlpha());

+    }

+    

+//    private void setColor(Color color) {

+//        ByteBuffer buf = (ByteBuffer) quadColor.getData();

+//        buf.rewind();

+//        

+//        int color2 = convertColor(color);

+//        buf.putInt(color2);

+//        buf.putInt(color2);

+//        buf.putInt(color2);

+//        buf.putInt(color2);

+//        

+//        buf.flip();

+//        quadColor.updateData(buf);

+//    }

+

     /**

      * 

      * @param font

@@ -205,22 +212,23 @@
      * @deprecated use renderFont(RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY) instead

      */

     @Deprecated

-    public void renderFont(RenderFont font, String str, int x, int y, Color color, float size){        

+    public void renderFont(RenderFont font, String str, int x, int y, Color color, float size) {        

         renderFont(font, str, x, y, color, size, size);

     }

- 

+    

     @Override

-    public void renderFont(RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY){        

-        //TODO find out what the f1 param is for

-        if (str.length() == 0)

+    public void renderFont(RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY) {        

+        if (str.length() == 0) {

             return;

-

-        if (font instanceof RenderFontNull)

+        }

+        

+        if (font instanceof RenderFontNull) {

             return;

-

+        }

+        

         RenderFontJme jmeFont = (RenderFontJme) font;

         

-        String key = font+str+color.getColorString();

+        String key = font + str + color.getColorString();

         BitmapText text = textCacheLastFrame.get(key);

         if (text == null) {

             text = jmeFont.createText();

@@ -228,166 +236,149 @@
             text.updateLogicalState(0);

         }

         textCacheCurrentFrame.put(key, text);

-

-        niftyMat.setColor("Color", convertColor(color, tempColor));

-        niftyMat.setBoolean("UseTex", true);

+        

+        niftyMat.setColor("Color", convertColor(color, tempColor));        

         niftyMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);

 //        niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());

         text.setMaterial(niftyMat);

-

-        float width = text.getLineWidth();

-        float height = text.getLineHeight();

-

-        float x0 = x + 0.5f * width  * (1f - sizeX);

-        float y0 = y + 0.5f * height * (1f - sizeY);

-

+        

         tempMat.loadIdentity();

-        tempMat.setTranslation(x0, getHeight() - y0, 0);

+        tempMat.setTranslation(x, getHeight() - y, 0);

         tempMat.setScale(sizeX, sizeY, 0);

-

+        

         rm.setWorldMatrix(tempMat);

         text.render(rm);

-        

+

 //        System.out.println("renderFont");

     }

-

+    

     public void renderImage(RenderImage image, int x, int y, int w, int h,

-                            int srcX, int srcY, int srcW, int srcH,

-                            Color color, float scale,

-                            int centerX, int centerY){

+            int srcX, int srcY, int srcW, int srcH,

+            Color color, float scale,

+            int centerX, int centerY) {

         RenderImageJme jmeImage = (RenderImageJme) image;

         Texture2D texture = jmeImage.getTexture();

-

+        

         niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());

-        niftyMat.setColor("Color", ColorRGBA.White);

-        niftyMat.setTexture("Texture", texture);

-        niftyMat.setBoolean("UseTex", true);

-        setColor(color);

-

-        float imageWidth  = jmeImage.getWidth();

+        niftyMat.setColor("Color", convertColor(color, tempColor));

+        niftyMat.setTexture("Texture", texture);        

+        //setColor(color);

+        

+        float imageWidth = jmeImage.getWidth();

         float imageHeight = jmeImage.getHeight();

         FloatBuffer texCoords = (FloatBuffer) quadModTC.getData();

-

+        

         float startX = srcX / imageWidth;

         float startY = srcY / imageHeight;

-        float endX   = startX + (srcW / imageWidth);

-        float endY   = startY + (srcH / imageHeight);

-

+        float endX = startX + (srcW / imageWidth);

+        float endY = startY + (srcH / imageHeight);

+        

         startY = 1f - startY;

-        endY   = 1f - endY;

-

+        endY = 1f - endY;

+        

         texCoords.rewind();

         texCoords.put(startX).put(startY);

-        texCoords.put(endX)  .put(startY);

-        texCoords.put(endX)  .put(endY);

+        texCoords.put(endX).put(startY);

+        texCoords.put(endX).put(endY);

         texCoords.put(startX).put(endY);

         texCoords.flip();

         quadModTC.updateData(texCoords);

-

+        

         quad.clearBuffer(Type.TexCoord);

         quad.setBuffer(quadModTC);

-

+        

         float x0 = centerX + (x - centerX) * scale;

         float y0 = centerY + (y - centerY) * scale;

-

+        

         tempMat.loadIdentity();

         tempMat.setTranslation(x0, getHeight() - y0, 0);

         tempMat.setScale(w * scale, h * scale, 0);

-

+        

         rm.setWorldMatrix(tempMat);

         niftyMat.render(quadGeom, rm);

 //        

 //        System.out.println("renderImage (Sub)");

     }

-

+    

     public void renderImage(RenderImage image, int x, int y, int width, int height,

-                       Color color, float imageScale){

-

+            Color color, float imageScale) {

+        

         RenderImageJme jmeImage = (RenderImageJme) image;

-

+        

         niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());

-        niftyMat.setColor("Color", ColorRGBA.White);

-        niftyMat.setTexture("Texture", jmeImage.getTexture());

-        niftyMat.setBoolean("UseTex", true);

-        setColor(color);

-

+        niftyMat.setColor("Color", convertColor(color, tempColor));

+        niftyMat.setTexture("Texture", jmeImage.getTexture());        

+        //setColor(color);

+        

         quad.clearBuffer(Type.TexCoord);

         quad.setBuffer(quadDefaultTC);

-

-        float x0 = x + 0.5f * width  * (1f - imageScale);

+        

+        float x0 = x + 0.5f * width * (1f - imageScale);

         float y0 = y + 0.5f * height * (1f - imageScale);

-

+        

         tempMat.loadIdentity();

         tempMat.setTranslation(x0, getHeight() - y0, 0);

         tempMat.setScale(width * imageScale, height * imageScale, 0);

-

+        

         rm.setWorldMatrix(tempMat);

         niftyMat.render(quadGeom, rm);

 //        

 //        System.out.println("renderImage");

     }

-

-    public void renderQuad(int x, int y, int width, int height, Color color){

-        niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());

-        niftyMat.setColor("Color", ColorRGBA.White);

-        niftyMat.clearParam("Texture");

-        niftyMat.setBoolean("UseTex", false);

-        setColor(color);

-

-        tempMat.loadIdentity();

-        tempMat.setTranslation(x, getHeight() - y, 0);

-        tempMat.setScale(width, height, 0);

-

-        rm.setWorldMatrix(tempMat);

-        niftyMat.render(quadGeom, rm);

-        

+    

+    public void renderQuad(int x, int y, int width, int height, Color color) {

+        if (color.getAlpha() > 0) {

+            niftyQuadMat.getAdditionalRenderState().setBlendMode(convertBlend());

+            niftyQuadMat.setColor("Color", convertColor(color, tempColor));                        

+            

+            tempMat.loadIdentity();

+            tempMat.setTranslation(x, getHeight() - y, 0);

+            tempMat.setScale(width, height, 0);

+            

+            rm.setWorldMatrix(tempMat);

+            niftyQuadMat.render(quadGeom, rm);

+        }

 //        System.out.println("renderQuad (Solid)");

     }

-

+    

     public void renderQuad(int x, int y, int width, int height,

-                           Color topLeft, Color topRight, Color bottomRight, Color bottomLeft) {

+            Color topLeft, Color topRight, Color bottomRight, Color bottomLeft) {

         

         ByteBuffer buf = (ByteBuffer) quadColor.getData();

         buf.rewind();

         

         buf.putInt(convertColor(topRight));

         buf.putInt(convertColor(topLeft));

-

+        

         buf.putInt(convertColor(bottomLeft));

         buf.putInt(convertColor(bottomRight));

         

         buf.flip();

         quadColor.updateData(buf);

         

-        niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());

-        niftyMat.setColor("Color", ColorRGBA.White);

-        niftyMat.clearParam("Texture");

-        niftyMat.setBoolean("UseTex", false);

-

+        niftyQuadGradMat.getAdditionalRenderState().setBlendMode(convertBlend());      

+        

         tempMat.loadIdentity();

         tempMat.setTranslation(x, getHeight() - y, 0);

         tempMat.setScale(width, height, 0);

-

+        

         rm.setWorldMatrix(tempMat);

-        niftyMat.render(quadGeom, rm);

+        niftyQuadGradMat.render(quadGeom, rm);

 //        

 //        System.out.println("renderQuad (Grad)");

     }

-

-    public void enableClip(int x0, int y0, int x1, int y1){

+    

+    public void enableClip(int x0, int y0, int x1, int y1) {

 //        System.out.println("enableClip");

         clipWasSet = true;

         r.setClipRect(x0, getHeight() - y1, x1 - x0, y1 - y0);

     }

-

+    

     public void disableClip() {

 //        System.out.println("disableClip");

-        if (clipWasSet){

+        if (clipWasSet) {

             r.clearClipRect();

             clipWasSet = false;

         }

     }

-

-    

-

 }

diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java
index e7f4b10..fc8cc6b 100644
--- a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java
@@ -712,9 +712,8 @@
         } else if (qName.equals("geometry")
                 || qName.equals("sharedgeometry")) {
             // finish writing to buffers
-            IntMap<VertexBuffer> bufs = mesh.getBuffers();
-            for (Entry<VertexBuffer> entry : bufs) {
-                Buffer data = entry.getValue().getData();
+            for (VertexBuffer buf : mesh.getBufferList().getArray()) {
+                Buffer data = buf.getData();
                 if (data.position() != 0) {
                     data.flip();
                 }
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java b/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java
index 3944b98..3348b48 100644
--- a/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java
@@ -34,6 +34,7 @@
 import com.jme3.export.JmeExporter;

 import com.jme3.export.JmeImporter;

 import com.jme3.math.FastMath;

+import com.jme3.math.Plane;

 import com.jme3.math.Triangle;

 import com.jme3.math.Vector2f;

 import com.jme3.math.Vector3f;

@@ -87,7 +88,7 @@
         IntBuffer ib = writeIndexArrayLodDiff(null, lod, rightLod, topLod, leftLod, bottomLod);

         FloatBuffer bb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);

         FloatBuffer tanb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);

-        writeTangentArray(tanb, bb, texb, scale);

+        writeTangentArray(nb, tanb, bb, texb, scale);

         Mesh m = new Mesh();

         m.setMode(Mode.TriangleStrip);

         m.setBuffer(Type.Position, 3, pb);

@@ -627,7 +628,7 @@
         return num;

     }

 

-    public FloatBuffer[] writeTangentArray(FloatBuffer tangentStore, FloatBuffer binormalStore, FloatBuffer textureBuffer, Vector3f scale) {

+    public FloatBuffer[] writeTangentArray(FloatBuffer normalBuffer, FloatBuffer tangentStore, FloatBuffer binormalStore, FloatBuffer textureBuffer, Vector3f scale) {

         if (!isLoaded()) {

             throw new NullPointerException();

         }

@@ -650,19 +651,31 @@
         }

         binormalStore.rewind();

 

+        Vector3f normal = new Vector3f();

         Vector3f tangent = new Vector3f();

         Vector3f binormal = new Vector3f();

-        Vector3f v1 = new Vector3f();

+        /*Vector3f v1 = new Vector3f();

         Vector3f v2 = new Vector3f();

         Vector3f v3 = new Vector3f();

         Vector2f t1 = new Vector2f();

         Vector2f t2 = new Vector2f();

-        Vector2f t3 = new Vector2f();

-

-        //scale = Vector3f.UNIT_XYZ;

+        Vector2f t3 = new Vector2f();*/

 

         for (int r = 0; r < getHeight(); r++) {

             for (int c = 0; c < getWidth(); c++) {

+                

+                int idx = (r * getWidth() + c) * 3;

+                normal.set(normalBuffer.get(idx), normalBuffer.get(idx+1), normalBuffer.get(idx+2));

+                tangent.set(normal.cross(new Vector3f(0,0,1)));

+                binormal.set(new Vector3f(1,0,0).cross(normal));

+                

+                BufferUtils.setInBuffer(tangent.normalizeLocal(), tangentStore, (r * getWidth() + c)); // save the tangent

+                BufferUtils.setInBuffer(binormal.normalizeLocal(), binormalStore, (r * getWidth() + c)); // save the binormal

+            }

+        }

+

+/*        for (int r = 0; r < getHeight(); r++) {

+            for (int c = 0; c < getWidth(); c++) {

 

                 int texIdx = ((getHeight() - 1 - r) * getWidth() + c) * 2; // pull from the end

                 int texIdxAbove = ((getHeight() - 1 - (r - 1)) * getWidth() + c) * 2; // pull from the end

@@ -702,7 +715,7 @@
                 BufferUtils.setInBuffer(binormal, binormalStore, (r * getWidth() + c)); // save the binormal

             }

         }

-

+        */

         return new FloatBuffer[]{tangentStore, binormalStore};

     }

 

diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
index 40cf190..83537af 100644
--- a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
@@ -314,13 +314,25 @@
         getMesh().getBuffer(Type.Normal).updateData(newNormalBuffer);

         FloatBuffer newTangentBuffer = null;

         FloatBuffer newBinormalBuffer = null;

-        FloatBuffer[] tb = geomap.writeTangentArray(newTangentBuffer, newBinormalBuffer, (FloatBuffer)getMesh().getBuffer(Type.TexCoord).getData(), getWorldScale());

+        FloatBuffer[] tb = geomap.writeTangentArray(newNormalBuffer, newTangentBuffer, newBinormalBuffer, (FloatBuffer)getMesh().getBuffer(Type.TexCoord).getData(), getWorldScale());

         newTangentBuffer = tb[0];

         newBinormalBuffer = tb[1];

         getMesh().getBuffer(Type.Tangent).updateData(newTangentBuffer);

         getMesh().getBuffer(Type.Binormal).updateData(newBinormalBuffer);

     }

 

+    private void setInBuffer(Mesh mesh, int index, Vector3f normal, Vector3f tangent, Vector3f binormal) {

+        VertexBuffer NB = mesh.getBuffer(Type.Normal);

+        VertexBuffer TB = mesh.getBuffer(Type.Tangent);

+        VertexBuffer BB = mesh.getBuffer(Type.Binormal);

+        BufferUtils.setInBuffer(normal, (FloatBuffer)NB.getData(), index);

+        BufferUtils.setInBuffer(tangent, (FloatBuffer)TB.getData(), index);

+        BufferUtils.setInBuffer(binormal, (FloatBuffer)BB.getData(), index);

+        NB.setUpdateNeeded();

+        TB.setUpdateNeeded();

+        BB.setUpdateNeeded();

+    }

+    

     /**

      * Matches the normals along the edge of the patch with the neighbours.

      * Computes the normals for the right, bottom, left, and top edges of the

@@ -357,158 +369,122 @@
         Vector3f binormal = new Vector3f();

         Vector3f normal = new Vector3f();

 

+        

         int s = this.getSize()-1;

         

         if (right != null) { // right side,    works its way down

             for (int i=0; i<s+1; i++) {

-                rootPoint.set(s, this.getHeightmapHeight(s,i), i);

-                leftPoint.set(s-1, this.getHeightmapHeight(s-1,i), i);

-                rightPoint.set(s+1, right.getHeightmapHeight(1,i), i);

+                rootPoint.set(0, this.getHeightmapHeight(s,i), 0);

+                leftPoint.set(-1, this.getHeightmapHeight(s-1,i), 0);

+                rightPoint.set(1, right.getHeightmapHeight(1,i), 0);

 

                 if (i == 0) { // top point

+                    bottomPoint.set(0, this.getHeightmapHeight(s,i+1), 1);

+                    

                     if (top == null) {

-                        bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);

-                        

-                        averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);

-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);

-                        VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), s);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), 0);

+                        averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint,  normal, tangent, binormal);

+                        setInBuffer(this.getMesh(), s, normal, tangent, binormal);

+                        setInBuffer(right.getMesh(), 0, normal, tangent, binormal);

                     } else {

-                        topPoint.set(s, top.getHeightmapHeight(s,s-1), i-1);

-                        bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);

+                        topPoint.set(0, top.getHeightmapHeight(s,s-1), -1);

                         

-                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);

-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);

-                        VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), s);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), 0);

+                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint,normal, tangent, binormal);

+                        setInBuffer(this.getMesh(), s, normal, tangent, binormal);

+                        setInBuffer(right.getMesh(), 0, normal, tangent, binormal);

+                        setInBuffer(top.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);

                         

                         if (topRight != null) {

-                            VertexBuffer topRightNB = topRight.getMesh().getBuffer(Type.Normal);

-                            BufferUtils.setInBuffer(normal, (FloatBuffer)topRightNB.getData(), (s+1)*s);

-                            topRightNB.setUpdateNeeded();

+                    //        setInBuffer(topRight.getMesh(), (s+1)*s, normal, tangent, binormal);

                         }

                     }

                 } else if (i == s) { // bottom point

+                    topPoint.set(0, this.getHeightmapHeight(s,s-1), -1);

+                    

                     if (bottom == null) {

-                        topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);

-                        

-                        averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, null, null, null, null, null, normal, tangent, binormal);

-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);

-                        VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i+1)-1);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*(s));

+                        averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal);

+                        setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);

+                        setInBuffer(right.getMesh(), (s+1)*(s), normal, tangent, binormal);

                     } else {

-                        topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);

-                        bottomPoint.set(s, bottom.getHeightmapHeight(s,1), i+1);

-                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);

-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);

-                        VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);

-                        VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);

-

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s+1)-1);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*s);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), s);

+                        bottomPoint.set(0, bottom.getHeightmapHeight(s,1), 1);

+                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);

+                        setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);

+                        setInBuffer(right.getMesh(), (s+1)*s, normal, tangent, binormal);

+                        setInBuffer(bottom.getMesh(), s, normal, tangent, binormal);

                         

                         if (bottomRight != null) {

-                            VertexBuffer bottomRightNB = bottomRight.getMesh().getBuffer(Type.Normal);

-                            BufferUtils.setInBuffer(normal, (FloatBuffer)bottomRightNB.getData(), 0);

-                            bottomRightNB.setUpdateNeeded();

+                   //         setInBuffer(bottomRight.getMesh(), 0, normal, tangent, binormal);

                         }

-                        downNB.setUpdateNeeded();

                     }

                 } else { // all in the middle

-                    topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);

-                    bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);

-                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);

-                    VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);

-                    VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);

-                    BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i+1)-1);

-                    BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*(i));

+                    topPoint.set(0, this.getHeightmapHeight(s,i-1), -1);

+                    bottomPoint.set(0, this.getHeightmapHeight(s,i+1), 1);

+                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);

+                    setInBuffer(this.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal);

+                    setInBuffer(right.getMesh(), (s+1)*(i), normal, tangent, binormal);

                 }

             }

-            right.getMesh().getBuffer(Type.Normal).setUpdateNeeded();

         }

 

         if (left != null) { // left side,    works its way down

             for (int i=0; i<s+1; i++) {

-                rootPoint.set(0, this.getHeightmapHeight(0,i), i);

-                leftPoint.set(-1, left.getHeightmapHeight(s-1,i), i);

-                rightPoint.set(1, this.getHeightmapHeight(1,i), i);

+                rootPoint.set(0, this.getHeightmapHeight(0,i), 0);

+                leftPoint.set(-1, left.getHeightmapHeight(s-1,i), 0);

+                rightPoint.set(1, this.getHeightmapHeight(1,i), 0);

                 

                 if (i == 0) { // top point

+                    bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1);

+                    

                     if (top == null) {

-                        bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);

-                        averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);

-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);

-                        VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), 0);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), s);

+                        averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);

+                        setInBuffer(this.getMesh(), 0, normal, tangent, binormal);

+                        setInBuffer(left.getMesh(), s, normal, tangent, binormal);

                     } else {

-                        topPoint.set(0, top.getHeightmapHeight(0,s-1), i-1);

-                        bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);

+                        topPoint.set(0, top.getHeightmapHeight(0,s-1), -1);

                         

-                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);

-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);

-                        VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), 0);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), s);

+                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);

+                        setInBuffer(this.getMesh(), 0, normal, tangent, binormal);

+                        setInBuffer(left.getMesh(), s, normal, tangent, binormal);

+                        setInBuffer(top.getMesh(), (s+1)*s, normal, tangent, binormal);

                         

                         if (topLeft != null) {

-                            VertexBuffer topLeftNB = topLeft.getMesh().getBuffer(Type.Normal);

-                            BufferUtils.setInBuffer(normal, (FloatBuffer)topLeftNB.getData(), (s+1)*(s+1)-1);

-                            topLeftNB.setUpdateNeeded();

+                     //       setInBuffer(topLeft.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);

                         }

                     }

                 } else if (i == s) { // bottom point

+                    topPoint.set(0, this.getHeightmapHeight(0,i-1), -1);

+                    

                     if (bottom == null) {

-                        topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1);

-                        

-                        averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, null, null, null, null, null, normal, tangent, binormal);

-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);

-                        VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s));

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1);

+                        averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal);

+                        setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal);

+                        setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);

                     } else {

-                        topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1);

-                        bottomPoint.set(0, bottom.getHeightmapHeight(0,1), i+1);

+                        bottomPoint.set(0, bottom.getHeightmapHeight(0,1), 1);

                         

-                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);

-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);

-                        VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);

-                        VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);

-

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s));

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1);

-                        BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), 0);

+                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);

+                        setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal);

+                        setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);

+                        setInBuffer(bottom.getMesh(), 0, normal, tangent, binormal);

                         

                         if (bottomLeft != null) {

-                            VertexBuffer bottomLeftNB = bottomLeft.getMesh().getBuffer(Type.Normal);

-                            BufferUtils.setInBuffer(normal, (FloatBuffer)bottomLeftNB.getData(), s);

-                            bottomLeftNB.setUpdateNeeded();

+                     //       setInBuffer(bottomLeft.getMesh(), s, normal, tangent, binormal);

                         }

-                        downNB.setUpdateNeeded();

                     }

                 } else { // all in the middle

-                    topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1);

-                    bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);

+                    topPoint.set(0, this.getHeightmapHeight(0,i-1), -1);

+                    bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1);

                     

-                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);

-                    VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);

-                    VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);

-                    BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i));

-                    BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1);

+                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);

+                    setInBuffer(this.getMesh(), (s+1)*(i), normal, tangent, binormal);

+                    setInBuffer(left.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal);

                 }

             }

-            left.getMesh().getBuffer(Type.Normal).setUpdateNeeded();

         }

-        

+

         if (top != null) { // top side,    works its way right

             for (int i=0; i<s+1; i++) {

-                rootPoint.set(i, this.getHeightmapHeight(i,0), 0);

-                topPoint.set(i, top.getHeightmapHeight(i,s-1), -1);

-                bottomPoint.set(i, this.getHeightmapHeight(i,1), 1);

+                rootPoint.set(0, this.getHeightmapHeight(i,0), 0);

+                topPoint.set(0, top.getHeightmapHeight(i,s-1), -1);

+                bottomPoint.set(0, this.getHeightmapHeight(i,1), 1);

                 

                 if (i == 0) { // left corner

                     // handled by left side pass

@@ -518,49 +494,39 @@
                     // handled by this patch when it does its right side

                     

                 } else { // all in the middle

-                    leftPoint.set(i-1, this.getHeightmapHeight(i-1,0), 0);

-                    rightPoint.set(i+1, this.getHeightmapHeight(i+1,0), 0);

-                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);

-                    VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);

-                    BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), i);

-                    VertexBuffer topNB = top.getMesh().getBuffer(Type.Normal);

-                    BufferUtils.setInBuffer(normal, (FloatBuffer)topNB.getData(), (s+1)*(s)+i);

+                    leftPoint.set(-1, this.getHeightmapHeight(i-1,0), 0);

+                    rightPoint.set(1, this.getHeightmapHeight(i+1,0), 0);

+                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);

+                    setInBuffer(this.getMesh(), i, normal, tangent, binormal);

+                    setInBuffer(top.getMesh(), (s+1)*(s)+i, normal, tangent, binormal);

                 }

             }

-            top.getMesh().getBuffer(Type.Normal).setUpdateNeeded();

             

         }

         

         if (bottom != null) { // bottom side,    works its way right

             for (int i=0; i<s+1; i++) {

-                rootPoint.set(i, this.getHeightmapHeight(i,s), s);

-                topPoint.set(i, this.getHeightmapHeight(i,s-1), s-1);

-                bottomPoint.set(i, bottom.getHeightmapHeight(i,1), s+1);

+                rootPoint.set(0, this.getHeightmapHeight(i,s), 0);

+                topPoint.set(0, this.getHeightmapHeight(i,s-1), -1);

+                bottomPoint.set(0, bottom.getHeightmapHeight(i,1), 1);

 

                 if (i == 0) { // left

                     // handled by the left side pass

                     

                 } else if (i == s) { // right

                     

-                    // handled by this patch when it does its right side

+                    // handled by the right side pass

                     

                 } else { // all in the middle

-                    leftPoint.set(i-1, this.getHeightmapHeight(i-1,s), s);

-                    rightPoint.set(i+1, this.getHeightmapHeight(i+1,s), s);

-                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);

-                    VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);

-                    BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s)+i);

-                    VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);

-                    BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), i);

+                    leftPoint.set(-1, this.getHeightmapHeight(i-1,s), 0);

+                    rightPoint.set(1, this.getHeightmapHeight(i+1,s), 0);

+                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);

+                    setInBuffer(this.getMesh(), (s+1)*(s)+i, normal, tangent, binormal);

+                    setInBuffer(bottom.getMesh(), i, normal, tangent, binormal);

                 }

             }

-            bottom.getMesh().getBuffer(Type.Normal).setUpdateNeeded();

             

         }

-

-        this.getMesh().getBuffer(Type.Normal).setUpdateNeeded();

-        this.getMesh().getBuffer(Type.Tangent).setUpdateNeeded();

-        this.getMesh().getBuffer(Type.Binormal).setUpdateNeeded();

     }

 

     protected void averageNormalsTangents(

@@ -569,38 +535,36 @@
             Vector3f leftPoint, 

             Vector3f bottomPoint, 

             Vector3f rightPoint,

-            Vector2f topTex,

-            Vector2f rootTex,

-            Vector2f leftTex,

-            Vector2f bottomTex,

-            Vector2f rightTex,

             Vector3f normal,

             Vector3f tangent,

             Vector3f binormal)

     {

         Vector3f scale = getWorldScale();

         

-        Vector3f n1 = Vector3f.ZERO;

+        Vector3f n1 = new Vector3f(0,0,0);

         if (topPoint != null && leftPoint != null) {

-            n1 = calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale));

+            n1.set(calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale)));

         }

-        Vector3f n2 = Vector3f.ZERO;

+        Vector3f n2 = new Vector3f(0,0,0);

         if (leftPoint != null && bottomPoint != null) {

-            n2 = calculateNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale));

+            n2.set(calculateNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale)));

         }

-        Vector3f n3 = Vector3f.ZERO;

+        Vector3f n3 = new Vector3f(0,0,0);

         if (rightPoint != null && bottomPoint != null) {

-            n3 = calculateNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale));

+            n3.set(calculateNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale)));

         }

-        Vector3f n4 = Vector3f.ZERO;

+        Vector3f n4 = new Vector3f(0,0,0);

         if (rightPoint != null && topPoint != null) {

-            n4 = calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale));

+            n4.set(calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale)));

         }

         

-        if (bottomPoint != null && rightPoint != null && rootTex != null && rightTex != null && bottomTex != null)

-            LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),bottomPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,bottomTex}, tangent, binormal);

+        //if (bottomPoint != null && rightPoint != null && rootTex != null && rightTex != null && bottomTex != null)

+        //    LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),bottomPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,bottomTex}, tangent, binormal);

 

-        normal.set(n1.add(n2).add(n3).add(n4).normalizeLocal());

+        normal.set(n1.add(n2).add(n3).add(n4).normalize());

+        

+        tangent.set(normal.cross(new Vector3f(0,0,1)).normalize());

+        binormal.set(new Vector3f(1,0,0).cross(normal).normalize());

     }

 

     private Vector3f calculateNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint) {

diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java
index 0b9b218..1086c71 100644
--- a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java
+++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java
@@ -1675,8 +1675,9 @@
                             }

                         }

                     }

-                    else

+                    else if (children.get(i) instanceof TerrainQuad) {

                         ((TerrainQuad) children.get(i)).findPick(toTest, results);

+                    }

                 }

             }

         }

diff --git a/engine/src/test/jme3test/app/TestAppStateLifeCycle.java b/engine/src/test/jme3test/app/TestAppStateLifeCycle.java
new file mode 100644
index 0000000..f224242
--- /dev/null
+++ b/engine/src/test/jme3test/app/TestAppStateLifeCycle.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2009-2012 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 jme3test.app;
+
+import com.jme3.app.Application;
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+
+
+/**
+ *  Tests the app state lifecycles.
+ *
+ *  @author    Paul Speed
+ */
+public class TestAppStateLifeCycle extends SimpleApplication {
+
+    public static void main(String[] args){
+        TestAppStateLifeCycle app = new TestAppStateLifeCycle();
+        app.start();
+    }
+
+    @Override
+    public void simpleInitApp() {
+        Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+        Geometry geom = new Geometry("Box", b);
+        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+        geom.setMaterial(mat);
+        rootNode.attachChild(geom);
+
+        System.out.println("Attaching test state.");
+        stateManager.attach(new TestState());        
+    }
+
+    @Override
+    public void simpleUpdate(float tpf) {
+    
+        if(stateManager.getState(TestState.class) != null) {
+            System.out.println("Detaching test state."); 
+            stateManager.detach(stateManager.getState(TestState.class));
+            System.out.println("Done"); 
+        }        
+    }
+    
+    public class TestState extends AbstractAppState {
+ 
+        @Override
+        public void initialize(AppStateManager stateManager, Application app) {
+            super.initialize(stateManager, app);
+            System.out.println("Initialized");
+        }
+ 
+        @Override
+        public void stateAttached(AppStateManager stateManager) {
+            super.stateAttached(stateManager);
+            System.out.println("Attached");
+        }
+ 
+        @Override
+        public void update(float tpf) {
+            super.update(tpf);
+            System.out.println("update");
+        }
+
+        @Override
+        public void render(RenderManager rm) {
+            super.render(rm);
+            System.out.println("render");
+        }
+
+        @Override
+        public void postRender() {
+            super.postRender();
+            System.out.println("postRender");
+        }
+
+        @Override
+        public void stateDetached(AppStateManager stateManager) {
+            super.stateDetached(stateManager);
+            System.out.println("Detached");
+        }
+ 
+        @Override
+        public void cleanup() {
+            super.cleanup();
+            System.out.println("Cleanup"); 
+        }
+
+    }    
+}
diff --git a/engine/src/test/jme3test/effect/TestSoftParticles.java b/engine/src/test/jme3test/effect/TestSoftParticles.java
new file mode 100644
index 0000000..d779a6f
--- /dev/null
+++ b/engine/src/test/jme3test/effect/TestSoftParticles.java
@@ -0,0 +1,130 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3test.effect;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.ParticleMesh;
+import com.jme3.effect.shapes.EmitterSphereShape;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.TranslucentBucketFilter;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+
+/**
+ *
+ * @author Nehon
+ */
+public class TestSoftParticles extends SimpleApplication {
+
+    private boolean softParticles = true;
+    private FilterPostProcessor fpp;
+    private TranslucentBucketFilter tbf;
+
+    public static void main(String[] args) {
+        TestSoftParticles app = new TestSoftParticles();
+        app.start();
+    }
+
+    @Override
+    public void simpleInitApp() {
+
+        cam.setLocation(new Vector3f(-7.2221026f, 4.1183004f, 7.759811f));
+        cam.setRotation(new Quaternion(0.06152846f, 0.91236454f, -0.1492115f, 0.37621948f));
+
+        flyCam.setMoveSpeed(10);
+
+
+        // -------- floor
+        Box b = new Box(Vector3f.ZERO, 10, 0.1f, 10);
+        Geometry geom = new Geometry("Box", b);
+        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        mat.setColor("Color", ColorRGBA.Gray);
+        mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+        geom.setMaterial(mat);
+        rootNode.attachChild(geom);
+
+        Box b2 = new Box(Vector3f.ZERO, 1, 1, 1);
+        Geometry geom2 = new Geometry("Box", b2);
+        Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        mat2.setColor("Color", ColorRGBA.DarkGray);
+        geom2.setMaterial(mat2);
+        rootNode.attachChild(geom2);
+        geom2.setLocalScale(0.1f, 0.2f, 1);
+
+        fpp = new FilterPostProcessor(assetManager);        
+        tbf = new TranslucentBucketFilter(true);
+        fpp.addFilter(tbf);
+        viewPort.addProcessor(fpp);
+
+
+        Material material = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+        material.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flame.png"));
+        
+        material.setFloat("Softness", 3f); // 
+
+
+        //Fire
+        ParticleEmitter fire = new ParticleEmitter("Fire", ParticleMesh.Type.Triangle, 30);
+        fire.setMaterial(material);
+        fire.setShape(new EmitterSphereShape(Vector3f.ZERO, 0.1f));
+        fire.setImagesX(2);
+        fire.setImagesY(2); // 2x2 texture animation
+        fire.setEndColor(new ColorRGBA(1f, 0f, 0f, 1f)); // red
+        fire.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
+        fire.setStartSize(0.6f);
+        fire.setEndSize(0.01f);
+        fire.setGravity(0, -0.3f, 0);
+        fire.setLowLife(0.5f);
+        fire.setHighLife(3f);
+        fire.setLocalTranslation(0, 0.2f, 0);
+
+        rootNode.attachChild(fire);
+        
+        
+        ParticleEmitter smoke = new ParticleEmitter("Smoke", ParticleMesh.Type.Triangle, 30);
+        smoke.setMaterial(material);
+        smoke.setShape(new EmitterSphereShape(Vector3f.ZERO, 5));
+        smoke.setImagesX(1);
+        smoke.setImagesY(1); // 2x2 texture animation
+        smoke.setStartColor(new ColorRGBA(0.1f, 0.1f, 0.1f,1f)); // dark gray
+        smoke.setEndColor(new ColorRGBA(0.5f, 0.5f, 0.5f, 0.3f)); // gray      
+        smoke.setStartSize(3f);
+        smoke.setEndSize(5f);
+        smoke.setGravity(0, -0.001f, 0);
+        smoke.setLowLife(100f);
+        smoke.setHighLife(100f);
+        smoke.setLocalTranslation(0, 0.1f, 0);        
+        smoke.emitAllParticles();
+        
+        rootNode.attachChild(smoke);
+
+        
+        inputManager.addListener(new ActionListener() {
+
+            public void onAction(String name, boolean isPressed, float tpf) {
+                if(isPressed && name.equals("toggle")){
+               //     tbf.setEnabled(!tbf.isEnabled());     
+                    softParticles = !softParticles;
+                    if(softParticles){
+                        viewPort.addProcessor(fpp);
+                    }else{
+                        viewPort.removeProcessor(fpp);
+                    }
+                }
+            }
+        }, "toggle");
+        inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
+    }
+    
+    
+}
diff --git a/engine/src/test/jme3test/helloworld/HelloMaterial.java b/engine/src/test/jme3test/helloworld/HelloMaterial.java
index e930c91..8ef6f56 100644
--- a/engine/src/test/jme3test/helloworld/HelloMaterial.java
+++ b/engine/src/test/jme3test/helloworld/HelloMaterial.java
@@ -80,7 +80,7 @@
     /** A cube with its base color "leaking" through a partially transparent texture */
     Box boxshape4 = new Box(new Vector3f(3f,-1f,0f), 1f,1f,1f);
     Geometry cube_leak = new Geometry("Leak-through color cube", boxshape4);
-    Material mat_tl = new Material(assetManager, "Common/MatDefs/Misc/ColoredTextured.j3md");
+    Material mat_tl = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
     mat_tl.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png"));
     mat_tl.setColor("Color", new ColorRGBA(1f,0f,1f, 1f)); // purple
     cube_leak.setMaterial(mat_tl);
@@ -103,6 +103,7 @@
     shiny_rock.setLocalTranslation(0,2,-2); // Move it a bit
     shiny_rock.rotate(1.6f, 0, 0);          // Rotate it a bit
     rootNode.attachChild(shiny_rock);
+    
     /** Must add a light to make the lit object visible! */
     DirectionalLight sun = new DirectionalLight();
     sun.setDirection(new Vector3f(1,0,-2).normalizeLocal());
diff --git a/engine/src/test/jme3test/model/shape/TestExpandingTorus.java b/engine/src/test/jme3test/model/shape/TestExpandingTorus.java
new file mode 100644
index 0000000..9e76b3f
--- /dev/null
+++ b/engine/src/test/jme3test/model/shape/TestExpandingTorus.java
@@ -0,0 +1,45 @@
+/*

+ * To change this template, choose Tools | Templates

+ * and open the template in the editor.

+ */

+package jme3test.model.shape;

+

+import com.jme3.app.SimpleApplication;

+import com.jme3.material.Material;

+import com.jme3.scene.Geometry;

+import com.jme3.scene.shape.Torus;

+

+public class TestExpandingTorus extends SimpleApplication {

+

+    private float outerRadius = 1.5f;

+    private float rate = 1;

+    private Torus torus;

+    private Geometry geom;

+    

+    public static void main(String[] args) {

+        TestExpandingTorus app = new TestExpandingTorus();

+        app.start();

+    }

+

+    @Override

+    public void simpleInitApp() {

+        torus = new Torus(30, 10, .5f, 1f);

+        geom = new Geometry("Torus", torus);

+        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");

+        geom.setMaterial(mat);

+        rootNode.attachChild(geom);

+    }

+    

+    @Override

+    public void simpleUpdate(float tpf){

+        if (outerRadius > 2.5f){

+            outerRadius = 2.5f;

+            rate = -rate;

+        }else if (outerRadius < 1f){

+            outerRadius = 1f;

+            rate = -rate;

+        }

+        outerRadius += rate * tpf;

+        torus.updateGeometry(30, 10, .5f, outerRadius);

+    }

+}
\ No newline at end of file
diff --git a/engine/src/tools/jme3tools/converters/model/ModelConverter.java b/engine/src/tools/jme3tools/converters/model/ModelConverter.java
index 2539574..6383d43 100644
--- a/engine/src/tools/jme3tools/converters/model/ModelConverter.java
+++ b/engine/src/tools/jme3tools/converters/model/ModelConverter.java
@@ -69,7 +69,7 @@
         ts.setListsOnly(listOnly);
         ts.setMinStripSize(minStripSize);
 
-        IndexBuffer ib = mesh.getIndexBuffer();
+        IndexBuffer ib = mesh.getIndicesAsList();
         int[] indices = new int[ib.size()];
         for (int i = 0; i < indices.length; i++)
             indices[i] = ib.get(i);
@@ -157,7 +157,7 @@
                 mesh.setBuffer(vb);
             }
         }
-        mesh.setInterleaved();
+        //mesh.setInterleaved();
     }
 
     private static void optimizeScene(Spatial source, boolean toFixed){
diff --git a/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java b/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java
index ae6ad8c..121d75c 100644
--- a/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java
+++ b/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java
@@ -62,26 +62,27 @@
         }
     }
     
-    private static void doTransformTangents(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform) {
+    private static void doTransformTangents(FloatBuffer inBuf, int offset, int components, FloatBuffer outBuf, Matrix4f transform) {
         Vector3f tan = new Vector3f();
-        float handedness = 0;
+        
         // offset is given in element units
         // convert to be in component units
-        offset *= 4;
+        offset *= components;
 
-        for (int i = 0; i < inBuf.capacity() / 4; i++) {
-            tan.x = inBuf.get(i * 4 + 0);
-            tan.y = inBuf.get(i * 4 + 1);
-            tan.z = inBuf.get(i * 4 + 2);
-            handedness = inBuf.get(i * 4 + 3);
+        for (int i = 0; i < inBuf.capacity() / components; i++) {
+            tan.x = inBuf.get(i * components + 0);
+            tan.y = inBuf.get(i * components + 1);
+            tan.z = inBuf.get(i * components + 2);
 
             transform.multNormal(tan, tan);
 
-            outBuf.put(offset + i * 4 + 0, tan.x);
-            outBuf.put(offset + i * 4 + 1, tan.y);
-            outBuf.put(offset + i * 4 + 2, tan.z);
-            outBuf.put(offset + i * 4 + 3, handedness);
-             
+            outBuf.put(offset + i * components + 0, tan.x);
+            outBuf.put(offset + i * components + 1, tan.y);
+            outBuf.put(offset + i * components + 2, tan.z);
+            
+            if (components == 4){
+                outBuf.put(offset + i * components + 3, inBuf.get(i * components + 3));
+            }
         }
     }
 
@@ -129,9 +130,9 @@
                     throw new UnsupportedOperationException();
             }
 
-            for (Entry<VertexBuffer> entry : geom.getMesh().getBuffers()) {
-                compsForBuf[entry.getKey()] = entry.getValue().getNumComponents();
-                formatForBuf[entry.getKey()] = entry.getValue().getFormat();
+            for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()){
+                compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents();
+                formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat();
             }
 
             if (mode != null && mode != listMode) {
@@ -208,10 +209,11 @@
                     FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
                     FloatBuffer outPos = (FloatBuffer) outBuf.getData();
                     doTransformNorms(inPos, globalVertIndex, outPos, worldMatrix);
-                }else if(Type.Tangent.ordinal() == bufType){                    
+                }else if(Type.Tangent.ordinal() == bufType){                
                     FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
                     FloatBuffer outPos = (FloatBuffer) outBuf.getData();
-                    doTransformTangents(inPos, globalVertIndex, outPos, worldMatrix);
+                    int components = inBuf.getNumComponents();
+                    doTransformTangents(inPos, globalVertIndex, components, outPos, worldMatrix);
                 } else {
                     inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount);
                 }