diff options
67 files changed, 5347 insertions, 4808 deletions
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 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt */
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 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt 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 class AndroidHarness extends Activity implements TouchListener, DialogInt }
}
}
+
+ 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.audio.plugins.AndroidAudioLoader; 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 @@ public class AndroidAssetManager extends DesktopAssetManager { //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 @@ public class AndroidAssetManager extends DesktopAssetManager { 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.asset.*; 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 @@ public class AndroidLocator implements AssetLocator { 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.asset.AssetLoader; 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.RawInputListener; 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.texture.Texture.WrapAxis; 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 @@ public class OGLESShaderRenderer implements Renderer { } 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 @@ public class OGLESShaderRenderer implements Renderer { 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 @@ public class OGLESShaderRenderer implements Renderer { 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 @@ public class TextureUtil { width /= 2; Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true); - bitmap.recycle(); + //bitmap.recycle(); bitmap = bitmap2; } } @@ -94,7 +94,7 @@ public class TextureUtil { 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 @@ package com.jme3.system.android; 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 @@ import javax.microedition.khronos.egl.EGLDisplay; /** * 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 @@ public class AndroidConfigChooser implements EGLConfigChooser 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 (type == ConfigType.BEST) { + ComponentSizeChooser compChooser = new ComponentSizeChooser(8, 8, 8, 8, 32, 0); + choosenConfig = compChooser.chooseConfig(egl, display); - if (choosenConfig == null) - { + 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 @@ public class AndroidConfigChooser implements EGLConfigChooser 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 @@ public class AndroidConfigChooser implements EGLConfigChooser 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 @@ public class AndroidConfigChooser implements EGLConfigChooser } 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.EGLContext; 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 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer { 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 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer { 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 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer { 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 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer { // 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 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer { } } + JmeSystem.setSoftTextDialogInput(this); + needClose.set(false); renderable.set(true); } @@ -268,6 +282,13 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer { 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 class OGLESContext implements JmeContext, GLSurfaceView.Renderer { 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 android.util.Log; 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 @@ public class AndroidLogHandler extends Handler { 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.Iterator; 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 @@ public class RingBuffer<Item> implements Iterable<Item> { 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 @@ public class RingBuffer<Item> implements Iterable<Item> { 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 @@ public class RingBuffer<Item> implements Iterable<Item> { 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.TextureHelper; 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 @@ public class MeshHelper extends AbstractBlenderHelper { // 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 @@ public class TextureBlenderAWT extends AbstractTextureBlender { 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 @@ vec2 computeLighting(in vec3 wvNorm, in vec3 wvViewDir, in vec3 wvLightDir){ 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 @@ MaterialDef Point Sprite { 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 @@ MaterialDef Point Sprite { }
}
+ 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.RenderManager; 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 @@ import com.jme3.texture.Texture2D; */ 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 @@ public final class TranslucentBucketFilter extends Filter { 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 @@ public final class TranslucentBucketFilter extends Filter { } @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 @@ public final class TranslucentBucketFilter extends Filter { if (renderManager != null) { renderManager.setHandleTranslucentBucket(true); } + + initSoftParticles(viewPort, false); } @Override @@ -76,5 +124,32 @@ public final class TranslucentBucketFilter extends Filter { 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 @@ public final class OBJLoader implements AssetLoader { 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 @@ public final class OBJLoader implements AssetLoader { } 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 @@ public class Application implements SystemListener { 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 @@ public class Application implements SystemListener { }
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 @@ public abstract class SimpleApplication extends Application { 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 @@ public class StatsView extends Node implements Control { 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 @@ public class AppStateManager { 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 @@ public class AppStateManager { 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 @@ public interface TouchInput extends Input { * @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.Renderer; 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 @@ public abstract class Filter implements Savable { * 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 @@ public abstract class Filter implements Savable { protected void cleanUpFilter(Renderer r) {
}
- ;
-
/**
* Must return the material used for this filter.
* this method is called every frame.
@@ -278,6 +277,14 @@ public abstract class Filter implements Savable { * @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 @@ public class FilterPostProcessor implements SceneProcessor, Savable { private int originalHeight;
private int lastFilterIndex = -1;
private boolean cameraInit = false;
-
+
/**
* Create a FilterProcessor
* @param assetManager the assetManager
@@ -98,8 +98,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable { * @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 @@ public class FilterPostProcessor implements SceneProcessor, Savable { * @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 @@ public class FilterPostProcessor implements SceneProcessor, Savable { } 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 @@ public class FilterPostProcessor implements SceneProcessor, Savable { //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 @@ public class FilterPostProcessor implements SceneProcessor, Savable { * 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.ArrayList; 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 @@ public class BatchNode extends Node implements Savable { } 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 @@ public class BatchNode extends Node implements Savable { 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.Matrix4f; 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 com.jme3.util.SafeArrayList; 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 @@ public class Mesh implements Savable, Cloneable { 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 @@ public class Mesh implements Savable, Cloneable { * 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 @@ public class Mesh implements Savable, Cloneable { * 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 @@ public class Mesh implements Savable, Cloneable { * 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 @@ public class Mesh implements Savable, Cloneable { * 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 @@ public class Mesh implements Savable, Cloneable { } /** + * 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 @@ public class Mesh implements Savable, Cloneable { * @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 class Mesh implements Savable, Cloneable { } 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 class Mesh implements Savable, Cloneable { } 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 class Mesh implements Savable, Cloneable { } 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.ArrayList; 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 @@ public final class BufferUtils { } } + 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 final class BufferUtils { * */ 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 @@ public final class BufferUtils { 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 @@ import java.util.Map; * @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 final class IntMap<T> implements Iterable<Entry<T>>, Cloneable { } 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 @@ public class TangentBinormalGenerator { } } 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 @@ public class TangentBinormalGenerator { lineMesh.setBuffer(Type.Color, 4, lineColor); lineMesh.setStatic(); - lineMesh.setInterleaved(); + //lineMesh.setInterleaved(); return lineMesh; } @@ -733,7 +739,7 @@ public class TangentBinormalGenerator { 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 class AWTLoader implements AssetLoader { 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/NiftyQuadGrad.vert b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.vert new file mode 100644 index 0000000..0ecaee4 --- /dev/null +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.vert @@ -0,0 +1,14 @@ +uniform mat4 g_WorldViewProjectionMatrix;
+
+attribute vec4 inPosition;
+attribute vec4 inColor;
+attribute vec4 inIndex;
+
+varying vec4 color;
+
+void main() {
+ vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;
+ gl_Position = vec4(pos, 0.0, 1.0);
+
+ 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 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 @@ MaterialDef Default GUI { 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 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.nio.FloatBuffer; 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){
+
+ 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()));
+ 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){
+
+ 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 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 @@ public class RenderDeviceJme implements RenderDevice { * @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 @@ public class RenderDeviceJme implements RenderDevice { 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 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } 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 @@ package com.jme3.terrain.geomipmap; 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 @@ public class LODGeomap extends GeoMap { 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 @@ public class LODGeomap extends GeoMap { 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 @@ public class LODGeomap extends GeoMap { }
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 @@ public class LODGeomap extends GeoMap { 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 @@ public class TerrainPatch extends Geometry { 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 @@ public class TerrainPatch extends Geometry { 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 @@ public class TerrainPatch extends Geometry { // 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 @@ public class TerrainPatch extends Geometry { 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 @@ public class TerrainQuad extends Node implements Terrain { }
}
}
- 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 @@ public class HelloMaterial extends SimpleApplication { /** 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 @@ public class HelloMaterial extends SimpleApplication { 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 @@ public class ModelConverter { 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 @@ public class ModelConverter { 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 @@ public class GeometryBatchFactory { } } - 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 @@ public class GeometryBatchFactory { 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 @@ public class GeometryBatchFactory { 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); } |