commit
0594e5dc7e
@ -1,686 +0,0 @@ |
|||||||
package com.jme3.input.android; |
|
||||||
|
|
||||||
import android.view.*; |
|
||||||
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.system.AppSettings; |
|
||||||
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 implements |
|
||||||
TouchInput, |
|
||||||
View.OnTouchListener, |
|
||||||
View.OnKeyListener, |
|
||||||
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 View view; |
|
||||||
private ScaleGestureDetector scaledetector; |
|
||||||
private boolean scaleInProgress = false; |
|
||||||
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() { |
|
||||||
} |
|
||||||
|
|
||||||
public void setView(View view) { |
|
||||||
this.view = view; |
|
||||||
if (view != null) { |
|
||||||
detector = new GestureDetector(null, this, null, false); |
|
||||||
scaledetector = new ScaleGestureDetector(view.getContext(), this); |
|
||||||
view.setOnTouchListener(this); |
|
||||||
view.setOnKeyListener(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; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* onTouch gets called from android thread on touchpad events |
|
||||||
*/ |
|
||||||
public boolean onTouch(View view, MotionEvent event) { |
|
||||||
if (view != this.view) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
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); |
|
||||||
Vector2f lastPos = lastPositions.get(pointerId); |
|
||||||
|
|
||||||
// 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), view.getHeight() - event.getY(pointerIndex), 0, 0); |
|
||||||
touch.setPointerId(pointerId); |
|
||||||
touch.setTime(event.getEventTime()); |
|
||||||
touch.setPressure(event.getPressure(pointerIndex)); |
|
||||||
processEvent(touch); |
|
||||||
|
|
||||||
lastPos = new Vector2f(event.getX(pointerIndex), view.getHeight() - event.getY(pointerIndex)); |
|
||||||
lastPositions.put(pointerId, lastPos); |
|
||||||
|
|
||||||
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), view.getHeight() - event.getY(pointerIndex), 0, 0); |
|
||||||
touch.setPointerId(pointerId); |
|
||||||
touch.setTime(event.getEventTime()); |
|
||||||
touch.setPressure(event.getPressure(pointerIndex)); |
|
||||||
processEvent(touch); |
|
||||||
lastPositions.remove(pointerId); |
|
||||||
|
|
||||||
bWasHandled = true; |
|
||||||
break; |
|
||||||
case MotionEvent.ACTION_MOVE: |
|
||||||
// Convert all pointers into events
|
|
||||||
for (int p = 0; p < event.getPointerCount(); p++) { |
|
||||||
lastPos = lastPositions.get(event.getPointerId(p)); |
|
||||||
if (lastPos == null) { |
|
||||||
lastPos = new Vector2f(event.getX(p), view.getHeight() - event.getY(p)); |
|
||||||
lastPositions.put(event.getPointerId(p), lastPos); |
|
||||||
} |
|
||||||
|
|
||||||
float dX = event.getX(p) - lastPos.x; |
|
||||||
float dY = view.getHeight() - event.getY(p) - lastPos.y; |
|
||||||
if (dX != 0 || dY != 0) { |
|
||||||
touch = getNextFreeTouchEvent(); |
|
||||||
touch.set(Type.MOVE, event.getX(p), view.getHeight() - event.getY(p), dX, dY); |
|
||||||
touch.setPointerId(event.getPointerId(p)); |
|
||||||
touch.setTime(event.getEventTime()); |
|
||||||
touch.setPressure(event.getPressure(p)); |
|
||||||
touch.setScaleSpanInProgress(scaleInProgress); |
|
||||||
processEvent(touch); |
|
||||||
lastPos.set(event.getX(p), view.getHeight() - event.getY(p)); |
|
||||||
} |
|
||||||
} |
|
||||||
bWasHandled = true; |
|
||||||
break; |
|
||||||
case MotionEvent.ACTION_OUTSIDE: |
|
||||||
break; |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
// Try to detect gestures
|
|
||||||
this.detector.onTouchEvent(event); |
|
||||||
this.scaledetector.onTouchEvent(event); |
|
||||||
|
|
||||||
return bWasHandled; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* onKey gets called from android thread on key events |
|
||||||
*/ |
|
||||||
public boolean onKey(View view, int keyCode, KeyEvent event) { |
|
||||||
if (view != this.view) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
if (event.getAction() == KeyEvent.ACTION_DOWN) { |
|
||||||
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; |
|
||||||
} |
|
||||||
} else if (event.getAction() == KeyEvent.ACTION_UP) { |
|
||||||
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; |
|
||||||
} |
|
||||||
} else { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void loadSettings(AppSettings settings) { |
|
||||||
mouseEventsEnabled = settings.isEmulateMouse(); |
|
||||||
mouseEventsInvertX = settings.isEmulateMouseFlipX(); |
|
||||||
mouseEventsInvertY = settings.isEmulateMouseFlipY(); |
|
||||||
} |
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
// 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(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
this.view = null; |
|
||||||
} |
|
||||||
|
|
||||||
@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) { |
|
||||||
//Discarding events when the ring buffer is full to avoid buffer overflow.
|
|
||||||
if(eventQueue.size()< MAX_EVENTS){ |
|
||||||
eventQueue.push(event); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// --------------- INSIDE GLThread ---------------
|
|
||||||
@Override |
|
||||||
public void update() { |
|
||||||
generateEvents(); |
|
||||||
} |
|
||||||
|
|
||||||
private void generateEvents() { |
|
||||||
if (listener != null) { |
|
||||||
TouchEvent event; |
|
||||||
MouseButtonEvent btn; |
|
||||||
MouseMotionEvent mot; |
|
||||||
int newX; |
|
||||||
int newY; |
|
||||||
|
|
||||||
while (!eventQueue.isEmpty()) { |
|
||||||
synchronized (eventQueue) { |
|
||||||
event = eventQueue.pop(); |
|
||||||
} |
|
||||||
if (event != null) { |
|
||||||
listener.onTouchEvent(event); |
|
||||||
|
|
||||||
if (mouseEventsEnabled) { |
|
||||||
if (mouseEventsInvertX) { |
|
||||||
newX = view.getWidth() - (int) event.getX(); |
|
||||||
} else { |
|
||||||
newX = (int) event.getX(); |
|
||||||
} |
|
||||||
|
|
||||||
if (mouseEventsInvertY) { |
|
||||||
newY = view.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 SCALE_MOVE: |
|
||||||
if (lastX != -1 && lastY != -1) { |
|
||||||
newX = lastX; |
|
||||||
newY = lastY; |
|
||||||
} |
|
||||||
int wheel = (int) (event.getScaleSpan() / 4f); // scale to match mouse wheel
|
|
||||||
int dwheel = (int) (event.getDeltaScaleSpan() / 4f); // scale to match mouse wheel
|
|
||||||
mot = new MouseMotionEvent(newX, newX, 0, 0, wheel, dwheel); |
|
||||||
mot.setTime(event.getTime()); |
|
||||||
listener.onMouseMotionEvent(mot); |
|
||||||
lastX = newX; |
|
||||||
lastY = newY; |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case MOVE: |
|
||||||
if (event.isScaleSpanInProgress()) { |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
int dx; |
|
||||||
int dy; |
|
||||||
if (lastX != -1) { |
|
||||||
dx = newX - lastX; |
|
||||||
dy = newY - lastY; |
|
||||||
} else { |
|
||||||
dx = 0; |
|
||||||
dy = 0; |
|
||||||
} |
|
||||||
|
|
||||||
mot = new MouseMotionEvent(newX, newY, dx, dy, (int)event.getScaleSpan(), (int)event.getDeltaScaleSpan()); |
|
||||||
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(), view.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(), view.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(), view.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) { |
|
||||||
scaleInProgress = true; |
|
||||||
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.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan()); |
|
||||||
touch.setScaleFactor(scaleGestureDetector.getScaleFactor()); |
|
||||||
touch.setScaleSpanInProgress(scaleInProgress); |
|
||||||
processEvent(touch); |
|
||||||
// System.out.println("scaleBegin");
|
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
public boolean onScale(ScaleGestureDetector scaleGestureDetector) { |
|
||||||
TouchEvent touch = getNextFreeTouchEvent(); |
|
||||||
touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), view.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f); |
|
||||||
touch.setPointerId(0); |
|
||||||
touch.setTime(scaleGestureDetector.getEventTime()); |
|
||||||
touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); |
|
||||||
touch.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan()); |
|
||||||
touch.setScaleFactor(scaleGestureDetector.getScaleFactor()); |
|
||||||
touch.setScaleSpanInProgress(scaleInProgress); |
|
||||||
processEvent(touch); |
|
||||||
// System.out.println("scale");
|
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) { |
|
||||||
scaleInProgress = false; |
|
||||||
TouchEvent touch = getNextFreeTouchEvent(); |
|
||||||
touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), view.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f); |
|
||||||
touch.setPointerId(0); |
|
||||||
touch.setTime(scaleGestureDetector.getEventTime()); |
|
||||||
touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); |
|
||||||
touch.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan()); |
|
||||||
touch.setScaleFactor(scaleGestureDetector.getScaleFactor()); |
|
||||||
touch.setScaleSpanInProgress(scaleInProgress); |
|
||||||
processEvent(touch); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { |
|
||||||
TouchEvent touch = getNextFreeTouchEvent(); |
|
||||||
touch.set(Type.SCROLL, e1.getX(), view.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(), view.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(), view.getHeight() - event.getY(), 0f, 0f); |
|
||||||
touch.setPointerId(0); |
|
||||||
touch.setTime(event.getEventTime()); |
|
||||||
processEvent(touch); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setSimulateKeyboard(boolean simulate) { |
|
||||||
keyboardEventsEnabled = simulate; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setOmitHistoricEvents(boolean dontSendHistory) { |
|
||||||
this.dontSendHistory = dontSendHistory; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated Use {@link #getSimulateMouse()}; |
|
||||||
*/ |
|
||||||
@Deprecated |
|
||||||
public boolean isMouseEventsEnabled() { |
|
||||||
return mouseEventsEnabled; |
|
||||||
} |
|
||||||
|
|
||||||
@Deprecated |
|
||||||
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; |
|
||||||
} |
|
||||||
|
|
||||||
public void setSimulateMouse(boolean simulate) { |
|
||||||
mouseEventsEnabled = simulate; |
|
||||||
} |
|
||||||
|
|
||||||
public boolean getSimulateMouse() { |
|
||||||
return isSimulateMouse(); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean isSimulateMouse() { |
|
||||||
return mouseEventsEnabled; |
|
||||||
} |
|
||||||
|
|
||||||
public boolean isSimulateKeyboard() { |
|
||||||
return keyboardEventsEnabled; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,159 @@ |
|||||||
|
/* |
||||||
|
* 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.android; |
||||||
|
|
||||||
|
import android.opengl.GLSurfaceView; |
||||||
|
import android.view.InputDevice; |
||||||
|
import android.view.KeyEvent; |
||||||
|
import android.view.MotionEvent; |
||||||
|
import android.view.View; |
||||||
|
import java.util.logging.Level; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* <code>AndroidInputHandler14</code> extends <code>AndroidInputHandler</code> to |
||||||
|
* add the onHover and onGenericMotion events that where added in Android rev 14 (Android 4.0).</br> |
||||||
|
* The onGenericMotion events are the main interface to Joystick axes. They |
||||||
|
* were actually released in Android rev 12. |
||||||
|
* |
||||||
|
* @author iwgeric |
||||||
|
*/ |
||||||
|
public class AndroidInputHandler14 extends AndroidInputHandler implements View.OnHoverListener, |
||||||
|
View.OnGenericMotionListener { |
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(AndroidInputHandler14.class.getName()); |
||||||
|
|
||||||
|
public AndroidInputHandler14() { |
||||||
|
touchInput = new AndroidTouchInput14(this); |
||||||
|
joyInput = new AndroidJoyInput14(this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void removeListeners(GLSurfaceView view) { |
||||||
|
super.removeListeners(view); |
||||||
|
view.setOnHoverListener(null); |
||||||
|
view.setOnGenericMotionListener(null); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void addListeners(GLSurfaceView view) { |
||||||
|
super.addListeners(view); |
||||||
|
view.setOnHoverListener(this); |
||||||
|
view.setOnGenericMotionListener(this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean onHover(View view, MotionEvent event) { |
||||||
|
if (view != getView()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
boolean consumed = false; |
||||||
|
|
||||||
|
int source = event.getSource(); |
||||||
|
// logger.log(Level.INFO, "onTouch source: {0}", source);
|
||||||
|
|
||||||
|
boolean isTouch = ((source & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN); |
||||||
|
// logger.log(Level.INFO, "onTouch source: {0}, isTouch: {1}",
|
||||||
|
// new Object[]{source, isTouch});
|
||||||
|
|
||||||
|
if (isTouch && touchInput != null) { |
||||||
|
// send the event to the touch processor
|
||||||
|
consumed = ((AndroidTouchInput14)touchInput).onHover(event); |
||||||
|
} |
||||||
|
|
||||||
|
return consumed; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean onGenericMotion(View view, MotionEvent event) { |
||||||
|
if (view != getView()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
boolean consumed = false; |
||||||
|
|
||||||
|
int source = event.getSource(); |
||||||
|
// logger.log(Level.INFO, "onGenericMotion source: {0}", source);
|
||||||
|
|
||||||
|
boolean isJoystick = |
||||||
|
((source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) || |
||||||
|
((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK); |
||||||
|
|
||||||
|
if (isJoystick && joyInput != null) { |
||||||
|
// logger.log(Level.INFO, "onGenericMotion source: {0}, isJoystick: {1}",
|
||||||
|
// new Object[]{source, isJoystick});
|
||||||
|
// send the event to the touch processor
|
||||||
|
consumed = consumed || ((AndroidJoyInput14)joyInput).onGenericMotion(event); |
||||||
|
} |
||||||
|
|
||||||
|
return consumed; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean onKey(View view, int keyCode, KeyEvent event) { |
||||||
|
if (view != getView()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
boolean consumed = false; |
||||||
|
|
||||||
|
// logger.log(Level.INFO, "onKey keyCode: {0}, action: {1}, event: {2}",
|
||||||
|
// new Object[]{KeyEvent.keyCodeToString(keyCode), event.getAction(), event});
|
||||||
|
int source = event.getSource(); |
||||||
|
// logger.log(Level.INFO, "onKey source: {0}", source);
|
||||||
|
|
||||||
|
boolean isTouch = |
||||||
|
((source & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) || |
||||||
|
((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD); |
||||||
|
boolean isJoystick = |
||||||
|
((source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) || |
||||||
|
((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK); |
||||||
|
|
||||||
|
if (isTouch && touchInput != null) { |
||||||
|
// logger.log(Level.INFO, "onKey source: {0}, isTouch: {1}",
|
||||||
|
// new Object[]{source, isTouch});
|
||||||
|
consumed = touchInput.onKey(event); |
||||||
|
} |
||||||
|
if (isJoystick && joyInput != null) { |
||||||
|
// logger.log(Level.INFO, "onKey source: {0}, isJoystick: {1}",
|
||||||
|
// new Object[]{source, isJoystick});
|
||||||
|
// use inclusive OR to make sure the onKey method is called.
|
||||||
|
consumed = consumed | ((AndroidJoyInput14)joyInput).onKey(event); |
||||||
|
} |
||||||
|
|
||||||
|
return consumed; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,108 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 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.android; |
||||||
|
|
||||||
|
import android.view.KeyEvent; |
||||||
|
import android.view.MotionEvent; |
||||||
|
import com.jme3.input.InputManager; |
||||||
|
import com.jme3.input.Joystick; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* <code>AndroidJoyInput14</code> extends <code>AndroidJoyInput</code> |
||||||
|
* to include support for physical joysticks/gamepads.</br> |
||||||
|
* |
||||||
|
* @author iwgeric |
||||||
|
*/ |
||||||
|
public class AndroidJoyInput14 extends AndroidJoyInput { |
||||||
|
private static final Logger logger = Logger.getLogger(AndroidJoyInput14.class.getName()); |
||||||
|
|
||||||
|
private AndroidJoystickJoyInput14 joystickJoyInput; |
||||||
|
|
||||||
|
public AndroidJoyInput14(AndroidInputHandler inputHandler) { |
||||||
|
super(inputHandler); |
||||||
|
joystickJoyInput = new AndroidJoystickJoyInput14(this); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Pauses the joystick device listeners to save battery life if they are not needed. |
||||||
|
* Used to pause when the activity pauses |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void pauseJoysticks() { |
||||||
|
super.pauseJoysticks(); |
||||||
|
|
||||||
|
if (joystickJoyInput != null) { |
||||||
|
joystickJoyInput.pauseJoysticks(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resumes the joystick device listeners. |
||||||
|
* Used to resume when the activity comes to the top of the stack |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void resumeJoysticks() { |
||||||
|
super.resumeJoysticks(); |
||||||
|
if (joystickJoyInput != null) { |
||||||
|
joystickJoyInput.resumeJoysticks(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void destroy() { |
||||||
|
super.destroy(); |
||||||
|
if (joystickJoyInput != null) { |
||||||
|
joystickJoyInput.destroy(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Joystick[] loadJoysticks(InputManager inputManager) { |
||||||
|
// load the simulated joystick for device orientation
|
||||||
|
super.loadJoysticks(inputManager); |
||||||
|
// load physical gamepads/joysticks
|
||||||
|
joystickList.addAll(joystickJoyInput.loadJoysticks(joystickList.size(), inputManager)); |
||||||
|
// return the list of joysticks back to InputManager
|
||||||
|
return joystickList.toArray( new Joystick[joystickList.size()] ); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean onGenericMotion(MotionEvent event) { |
||||||
|
return joystickJoyInput.onGenericMotion(event); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean onKey(KeyEvent event) { |
||||||
|
return joystickJoyInput.onKey(event); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,416 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 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.android; |
||||||
|
|
||||||
|
import android.view.InputDevice; |
||||||
|
import android.view.InputDevice.MotionRange; |
||||||
|
import android.view.KeyCharacterMap; |
||||||
|
import android.view.KeyEvent; |
||||||
|
import android.view.MotionEvent; |
||||||
|
import com.jme3.input.AbstractJoystick; |
||||||
|
import com.jme3.input.DefaultJoystickAxis; |
||||||
|
import com.jme3.input.DefaultJoystickButton; |
||||||
|
import com.jme3.input.InputManager; |
||||||
|
import com.jme3.input.JoyInput; |
||||||
|
import com.jme3.input.Joystick; |
||||||
|
import com.jme3.input.JoystickAxis; |
||||||
|
import com.jme3.input.JoystickButton; |
||||||
|
import com.jme3.input.JoystickCompatibilityMappings; |
||||||
|
import com.jme3.input.event.JoyAxisEvent; |
||||||
|
import com.jme3.input.event.JoyButtonEvent; |
||||||
|
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; |
||||||
|
|
||||||
|
/** |
||||||
|
* Main class that creates and manages Android inputs for physical gamepads/joysticks. |
||||||
|
* |
||||||
|
* @author iwgeric |
||||||
|
*/ |
||||||
|
public class AndroidJoystickJoyInput14 { |
||||||
|
private static final Logger logger = Logger.getLogger(AndroidJoystickJoyInput14.class.getName()); |
||||||
|
|
||||||
|
private boolean loaded = false; |
||||||
|
private AndroidJoyInput joyInput; |
||||||
|
private Map<Integer, AndroidJoystick> joystickIndex = new HashMap<Integer, AndroidJoystick>(); |
||||||
|
|
||||||
|
private static int[] AndroidGamepadButtons = { |
||||||
|
// Dpad buttons
|
||||||
|
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN, |
||||||
|
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT, |
||||||
|
KeyEvent.KEYCODE_DPAD_CENTER, |
||||||
|
|
||||||
|
// pressing joystick down
|
||||||
|
KeyEvent.KEYCODE_BUTTON_THUMBL, KeyEvent.KEYCODE_BUTTON_THUMBR, |
||||||
|
|
||||||
|
// buttons
|
||||||
|
KeyEvent.KEYCODE_BUTTON_A, KeyEvent.KEYCODE_BUTTON_B, |
||||||
|
KeyEvent.KEYCODE_BUTTON_X, KeyEvent.KEYCODE_BUTTON_Y, |
||||||
|
|
||||||
|
// buttons on back of device
|
||||||
|
KeyEvent.KEYCODE_BUTTON_L1, KeyEvent.KEYCODE_BUTTON_R1, |
||||||
|
KeyEvent.KEYCODE_BUTTON_L2, KeyEvent.KEYCODE_BUTTON_R2, |
||||||
|
|
||||||
|
// start / select buttons
|
||||||
|
KeyEvent.KEYCODE_BUTTON_START, KeyEvent.KEYCODE_BUTTON_SELECT, |
||||||
|
KeyEvent.KEYCODE_BUTTON_MODE, |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
public AndroidJoystickJoyInput14(AndroidJoyInput joyInput) { |
||||||
|
this.joyInput = joyInput; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void pauseJoysticks() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void resumeJoysticks() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void destroy() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public List<Joystick> loadJoysticks(int joyId, InputManager inputManager) { |
||||||
|
logger.log(Level.INFO, "loading Joystick devices"); |
||||||
|
ArrayList<Joystick> joysticks = new ArrayList<Joystick>(); |
||||||
|
joysticks.clear(); |
||||||
|
joystickIndex.clear(); |
||||||
|
|
||||||
|
ArrayList gameControllerDeviceIds = new ArrayList(); |
||||||
|
int[] deviceIds = InputDevice.getDeviceIds(); |
||||||
|
for (int deviceId : deviceIds) { |
||||||
|
InputDevice dev = InputDevice.getDevice(deviceId); |
||||||
|
int sources = dev.getSources(); |
||||||
|
logger.log(Level.FINE, "deviceId[{0}] sources: {1}", new Object[]{deviceId, sources}); |
||||||
|
|
||||||
|
// Verify that the device has gamepad buttons, control sticks, or both.
|
||||||
|
if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) || |
||||||
|
((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) { |
||||||
|
// This device is a game controller. Store its device ID.
|
||||||
|
if (!gameControllerDeviceIds.contains(deviceId)) { |
||||||
|
gameControllerDeviceIds.add(deviceId); |
||||||
|
logger.log(Level.FINE, "Attempting to create joystick for device: {0}", dev); |
||||||
|
// Create an AndroidJoystick and store the InputDevice so we
|
||||||
|
// can later correspond the input from the InputDevice to the
|
||||||
|
// appropriate jME Joystick event
|
||||||
|
AndroidJoystick joystick = new AndroidJoystick(inputManager, |
||||||
|
joyInput, |
||||||
|
dev, |
||||||
|
joyId+joysticks.size(), |
||||||
|
dev.getName()); |
||||||
|
joystickIndex.put(deviceId, joystick); |
||||||
|
joysticks.add(joystick); |
||||||
|
|
||||||
|
// Each analog input is reported as a MotionRange
|
||||||
|
// The axis number corresponds to the type of axis
|
||||||
|
// The AndroidJoystick.addAxis(MotionRange) converts the axis
|
||||||
|
// type reported by Android into the jME Joystick axis
|
||||||
|
List<MotionRange> motionRanges = dev.getMotionRanges(); |
||||||
|
for (MotionRange motionRange: motionRanges) { |
||||||
|
logger.log(Level.INFO, "motion range: {0}", motionRange.toString()); |
||||||
|
logger.log(Level.INFO, "axis: {0}", motionRange.getAxis()); |
||||||
|
JoystickAxis axis = joystick.addAxis(motionRange); |
||||||
|
logger.log(Level.INFO, "added axis: {0}", axis); |
||||||
|
} |
||||||
|
|
||||||
|
// InputDevice has a method for determining if a keyCode is
|
||||||
|
// supported (InputDevice public boolean[] hasKeys (int... keys)).
|
||||||
|
// But this method wasn't added until rev 19 (Android 4.4)
|
||||||
|
// Therefore, we only can query the entire device and see if
|
||||||
|
// any InputDevice supports the keyCode. This may result in
|
||||||
|
// buttons being configured that don't exist on the specific
|
||||||
|
// device, but I haven't found a better way yet.
|
||||||
|
for (int keyCode: AndroidGamepadButtons) { |
||||||
|
logger.log(Level.INFO, "button[{0}]: {1}", |
||||||
|
new Object[]{keyCode, KeyCharacterMap.deviceHasKey(keyCode)}); |
||||||
|
if (KeyCharacterMap.deviceHasKey(keyCode)) { |
||||||
|
// add button even though we aren't sure if the button
|
||||||
|
// actually exists on this InputDevice
|
||||||
|
logger.log(Level.INFO, "button[{0}] exists somewhere", keyCode); |
||||||
|
JoystickButton button = joystick.addButton(keyCode); |
||||||
|
logger.log(Level.INFO, "added button: {0}", button); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
loaded = true; |
||||||
|
return joysticks; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean onGenericMotion(MotionEvent event) { |
||||||
|
boolean consumed = false; |
||||||
|
// logger.log(Level.INFO, "onGenericMotion event: {0}", event);
|
||||||
|
event.getDeviceId(); |
||||||
|
event.getSource(); |
||||||
|
// logger.log(Level.INFO, "deviceId: {0}, source: {1}", new Object[]{event.getDeviceId(), event.getSource()});
|
||||||
|
AndroidJoystick joystick = joystickIndex.get(event.getDeviceId()); |
||||||
|
if (joystick != null) { |
||||||
|
for (int androidAxis: joystick.getAndroidAxes()) { |
||||||
|
String axisName = MotionEvent.axisToString(androidAxis); |
||||||
|
float value = event.getAxisValue(androidAxis); |
||||||
|
int action = event.getAction(); |
||||||
|
if (action == MotionEvent.ACTION_MOVE) { |
||||||
|
// logger.log(Level.INFO, "MOVE axis num: {0}, axisName: {1}, value: {2}",
|
||||||
|
// new Object[]{androidAxis, axisName, value});
|
||||||
|
JoystickAxis axis = joystick.getAxis(androidAxis); |
||||||
|
if (axis != null) { |
||||||
|
// logger.log(Level.INFO, "MOVE axis num: {0}, axisName: {1}, value: {2}, deadzone: {3}",
|
||||||
|
// new Object[]{androidAxis, axisName, value, axis.getDeadZone()});
|
||||||
|
JoyAxisEvent axisEvent = new JoyAxisEvent(axis, value); |
||||||
|
joyInput.addEvent(axisEvent); |
||||||
|
consumed = true; |
||||||
|
} else { |
||||||
|
// logger.log(Level.INFO, "axis was null for axisName: {0}", axisName);
|
||||||
|
} |
||||||
|
} else { |
||||||
|
// logger.log(Level.INFO, "action: {0}", action);
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return consumed; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean onKey(KeyEvent event) { |
||||||
|
boolean consumed = false; |
||||||
|
// logger.log(Level.INFO, "onKey event: {0}", event);
|
||||||
|
|
||||||
|
event.getDeviceId(); |
||||||
|
event.getSource(); |
||||||
|
AndroidJoystick joystick = joystickIndex.get(event.getDeviceId()); |
||||||
|
if (joystick != null) { |
||||||
|
JoystickButton button = joystick.getButton(event.getKeyCode()); |
||||||
|
boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN; |
||||||
|
if (button != null) { |
||||||
|
JoyButtonEvent buttonEvent = new JoyButtonEvent(button, pressed); |
||||||
|
joyInput.addEvent(buttonEvent); |
||||||
|
consumed = true; |
||||||
|
} else { |
||||||
|
JoystickButton newButton = joystick.addButton(event.getKeyCode()); |
||||||
|
JoyButtonEvent buttonEvent = new JoyButtonEvent(newButton, pressed); |
||||||
|
joyInput.addEvent(buttonEvent); |
||||||
|
consumed = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return consumed; |
||||||
|
} |
||||||
|
|
||||||
|
protected class AndroidJoystick extends AbstractJoystick { |
||||||
|
|
||||||
|
private JoystickAxis nullAxis; |
||||||
|
private InputDevice device; |
||||||
|
private JoystickAxis xAxis; |
||||||
|
private JoystickAxis yAxis; |
||||||
|
private JoystickAxis povX; |
||||||
|
private JoystickAxis povY; |
||||||
|
private Map<Integer, JoystickAxis> axisIndex = new HashMap<Integer, JoystickAxis>(); |
||||||
|
private Map<Integer, JoystickButton> buttonIndex = new HashMap<Integer, JoystickButton>(); |
||||||
|
|
||||||
|
public AndroidJoystick( InputManager inputManager, JoyInput joyInput, InputDevice device, |
||||||
|
int joyId, String name ) { |
||||||
|
super( inputManager, joyInput, joyId, name ); |
||||||
|
|
||||||
|
this.device = device; |
||||||
|
|
||||||
|
this.nullAxis = new DefaultJoystickAxis( getInputManager(), this, -1, |
||||||
|
"Null", "null", false, false, 0 ); |
||||||
|
this.xAxis = nullAxis; |
||||||
|
this.yAxis = nullAxis; |
||||||
|
this.povX = nullAxis; |
||||||
|
this.povY = nullAxis; |
||||||
|
} |
||||||
|
|
||||||
|
protected JoystickAxis getAxis(int androidAxis) { |
||||||
|
return axisIndex.get(androidAxis); |
||||||
|
} |
||||||
|
|
||||||
|
protected Set<Integer> getAndroidAxes() { |
||||||
|
return axisIndex.keySet(); |
||||||
|
} |
||||||
|
|
||||||
|
protected JoystickButton getButton(int keyCode) { |
||||||
|
return buttonIndex.get(keyCode); |
||||||
|
} |
||||||
|
|
||||||
|
protected JoystickButton addButton( int keyCode ) { |
||||||
|
|
||||||
|
// logger.log(Level.FINE, "Adding button: {0}", keyCode);
|
||||||
|
|
||||||
|
String name = KeyEvent.keyCodeToString(keyCode); |
||||||
|
String original = KeyEvent.keyCodeToString(keyCode); |
||||||
|
// A/B/X/Y buttons
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BUTTON_Y) { |
||||||
|
original = JoystickButton.BUTTON_0; |
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_B) { |
||||||
|
original = JoystickButton.BUTTON_1; |
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_A) { |
||||||
|
original = JoystickButton.BUTTON_2; |
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_X) { |
||||||
|
original = JoystickButton.BUTTON_3; |
||||||
|
// Front buttons Some of these have the top ones and the bottoms ones flipped.
|
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_L1) { |
||||||
|
original = JoystickButton.BUTTON_4; |
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_R1) { |
||||||
|
original = JoystickButton.BUTTON_5; |
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_L2) { |
||||||
|
original = JoystickButton.BUTTON_6; |
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_R2) { |
||||||
|
original = JoystickButton.BUTTON_7; |
||||||
|
// // Dpad buttons
|
||||||
|
// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
|
||||||
|
// original = JoystickButton.BUTTON_8;
|
||||||
|
// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
|
||||||
|
// original = JoystickButton.BUTTON_9;
|
||||||
|
// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
|
||||||
|
// original = JoystickButton.BUTTON_8;
|
||||||
|
// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
|
||||||
|
// original = JoystickButton.BUTTON_9;
|
||||||
|
// Select and start buttons
|
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_SELECT) { |
||||||
|
original = JoystickButton.BUTTON_8; |
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_START) { |
||||||
|
original = JoystickButton.BUTTON_9; |
||||||
|
// Joystick push buttons
|
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_THUMBL) { |
||||||
|
original = JoystickButton.BUTTON_10; |
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_THUMBR) { |
||||||
|
original = JoystickButton.BUTTON_11; |
||||||
|
} |
||||||
|
|
||||||
|
String logicalId = JoystickCompatibilityMappings.remapComponent( getName(), original ); |
||||||
|
if( logicalId == null ? original != null : !logicalId.equals(original) ) { |
||||||
|
logger.log(Level.FINE, "Remapped: {0} to: {1}", |
||||||
|
new Object[]{original, logicalId}); |
||||||
|
} |
||||||
|
|
||||||
|
JoystickButton button = new DefaultJoystickButton( getInputManager(), this, getButtonCount(), |
||||||
|
name, logicalId ); |
||||||
|
addButton(button); |
||||||
|
buttonIndex.put( keyCode, button ); |
||||||
|
return button; |
||||||
|
} |
||||||
|
|
||||||
|
protected JoystickAxis addAxis(MotionRange motionRange) { |
||||||
|
|
||||||
|
String name = MotionEvent.axisToString(motionRange.getAxis()); |
||||||
|
|
||||||
|
String original = MotionEvent.axisToString(motionRange.getAxis()); |
||||||
|
if (motionRange.getAxis() == MotionEvent.AXIS_X) { |
||||||
|
original = JoystickAxis.X_AXIS; |
||||||
|
} else if (motionRange.getAxis() == MotionEvent.AXIS_Y) { |
||||||
|
original = JoystickAxis.Y_AXIS; |
||||||
|
} else if (motionRange.getAxis() == MotionEvent.AXIS_Z) { |
||||||
|
original = JoystickAxis.Z_AXIS; |
||||||
|
} else if (motionRange.getAxis() == MotionEvent.AXIS_RZ) { |
||||||
|
original = JoystickAxis.Z_ROTATION; |
||||||
|
} else if (motionRange.getAxis() == MotionEvent.AXIS_HAT_X) { |
||||||
|
original = JoystickAxis.POV_X; |
||||||
|
} else if (motionRange.getAxis() == MotionEvent.AXIS_HAT_Y) { |
||||||
|
original = JoystickAxis.POV_Y; |
||||||
|
} |
||||||
|
String logicalId = JoystickCompatibilityMappings.remapComponent( getName(), original ); |
||||||
|
if( logicalId == null ? original != null : !logicalId.equals(original) ) { |
||||||
|
logger.log(Level.FINE, "Remapped: {0} to: {1}", |
||||||
|
new Object[]{original, logicalId}); |
||||||
|
} |
||||||
|
|
||||||
|
JoystickAxis axis = new DefaultJoystickAxis(getInputManager(), |
||||||
|
this, |
||||||
|
getAxisCount(), |
||||||
|
name, |
||||||
|
logicalId, |
||||||
|
true, |
||||||
|
true, |
||||||
|
motionRange.getFlat()); |
||||||
|
|
||||||
|
if (motionRange.getAxis() == MotionEvent.AXIS_X) { |
||||||
|
xAxis = axis; |
||||||
|
} |
||||||
|
if (motionRange.getAxis() == MotionEvent.AXIS_Y) { |
||||||
|
yAxis = axis; |
||||||
|
} |
||||||
|
if (motionRange.getAxis() == MotionEvent.AXIS_HAT_X) { |
||||||
|
povX = axis; |
||||||
|
} |
||||||
|
if (motionRange.getAxis() == MotionEvent.AXIS_HAT_Y) { |
||||||
|
povY = axis; |
||||||
|
} |
||||||
|
|
||||||
|
addAxis(axis); |
||||||
|
axisIndex.put(motionRange.getAxis(), axis); |
||||||
|
return axis; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JoystickAxis getXAxis() { |
||||||
|
return xAxis; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JoystickAxis getYAxis() { |
||||||
|
return yAxis; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JoystickAxis getPovXAxis() { |
||||||
|
return povX; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JoystickAxis getPovYAxis() { |
||||||
|
return povY; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getXAxisIndex(){ |
||||||
|
return xAxis.getAxisId(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getYAxisIndex(){ |
||||||
|
return yAxis.getAxisId(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,140 +0,0 @@ |
|||||||
/* |
|
||||||
* 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.android; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import android.view.KeyEvent; |
|
||||||
import android.view.View; |
|
||||||
import android.view.inputmethod.InputMethodManager; |
|
||||||
import com.jme3.input.event.KeyInputEvent; |
|
||||||
import com.jme3.input.event.TouchEvent; |
|
||||||
import java.util.logging.Logger; |
|
||||||
|
|
||||||
/** |
|
||||||
* AndroidKeyHandler recieves onKey events from the Android system and creates |
|
||||||
* the jME KeyEvents. onKey is used by Android to receive keys from the keyboard |
|
||||||
* or device buttons. All key events are consumed by jME except for the Volume |
|
||||||
* buttons and menu button. |
|
||||||
* |
|
||||||
* This class also provides the functionality to display or hide the soft keyboard |
|
||||||
* for inputing single key events. Use OGLESContext to display an dialog to type |
|
||||||
* in complete strings. |
|
||||||
* |
|
||||||
* @author iwgeric |
|
||||||
*/ |
|
||||||
public class AndroidKeyHandler implements View.OnKeyListener { |
|
||||||
private static final Logger logger = Logger.getLogger(AndroidKeyHandler.class.getName()); |
|
||||||
|
|
||||||
private AndroidInputHandler androidInput; |
|
||||||
private boolean sendKeyEvents = true; |
|
||||||
|
|
||||||
public AndroidKeyHandler(AndroidInputHandler androidInput) { |
|
||||||
this.androidInput = androidInput; |
|
||||||
} |
|
||||||
|
|
||||||
public void initialize() { |
|
||||||
} |
|
||||||
|
|
||||||
public void destroy() { |
|
||||||
} |
|
||||||
|
|
||||||
public void setView(View view) { |
|
||||||
if (view != null) { |
|
||||||
view.setOnKeyListener(this); |
|
||||||
} else { |
|
||||||
androidInput.getView().setOnKeyListener(null); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* onKey gets called from android thread on key events |
|
||||||
*/ |
|
||||||
public boolean onKey(View view, int keyCode, KeyEvent event) { |
|
||||||
if (androidInput.isInitialized() && view != androidInput.getView()) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
TouchEvent evt; |
|
||||||
// TODO: get touch event from pool
|
|
||||||
if (event.getAction() == KeyEvent.ACTION_DOWN) { |
|
||||||
evt = new TouchEvent(); |
|
||||||
evt.set(TouchEvent.Type.KEY_DOWN); |
|
||||||
evt.setKeyCode(keyCode); |
|
||||||
evt.setCharacters(event.getCharacters()); |
|
||||||
evt.setTime(event.getEventTime()); |
|
||||||
|
|
||||||
// Send the event
|
|
||||||
androidInput.addEvent(evt); |
|
||||||
|
|
||||||
} else if (event.getAction() == KeyEvent.ACTION_UP) { |
|
||||||
evt = new TouchEvent(); |
|
||||||
evt.set(TouchEvent.Type.KEY_UP); |
|
||||||
evt.setKeyCode(keyCode); |
|
||||||
evt.setCharacters(event.getCharacters()); |
|
||||||
evt.setTime(event.getEventTime()); |
|
||||||
|
|
||||||
// Send the event
|
|
||||||
androidInput.addEvent(evt); |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
if (androidInput.isSimulateKeyboard()) { |
|
||||||
KeyInputEvent kie; |
|
||||||
char unicodeChar = (char)event.getUnicodeChar(); |
|
||||||
int jmeKeyCode = AndroidKeyMapping.getJmeKey(keyCode); |
|
||||||
|
|
||||||
boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN; |
|
||||||
boolean repeating = pressed && event.getRepeatCount() > 0; |
|
||||||
|
|
||||||
kie = new KeyInputEvent(jmeKeyCode, unicodeChar, pressed, repeating); |
|
||||||
kie.setTime(event.getEventTime()); |
|
||||||
androidInput.addEvent(kie); |
|
||||||
// logger.log(Level.FINE, "onKey keyCode: {0}, jmeKeyCode: {1}, pressed: {2}, repeating: {3}",
|
|
||||||
// new Object[]{keyCode, jmeKeyCode, pressed, repeating});
|
|
||||||
// logger.log(Level.FINE, "creating KeyInputEvent: {0}", kie);
|
|
||||||
} |
|
||||||
|
|
||||||
// consume all keys ourself except Volume Up/Down and Menu
|
|
||||||
// Don't do Menu so that typical Android Menus can be created and used
|
|
||||||
// by the user in MainActivity
|
|
||||||
if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || |
|
||||||
(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) || |
|
||||||
(keyCode == KeyEvent.KEYCODE_MENU)) { |
|
||||||
return false; |
|
||||||
} else { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,257 +0,0 @@ |
|||||||
/* |
|
||||||
* 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.android; |
|
||||||
|
|
||||||
import android.view.MotionEvent; |
|
||||||
import android.view.View; |
|
||||||
import com.jme3.input.event.InputEvent; |
|
||||||
import com.jme3.input.event.MouseButtonEvent; |
|
||||||
import com.jme3.input.event.MouseMotionEvent; |
|
||||||
import com.jme3.input.event.TouchEvent; |
|
||||||
import static com.jme3.input.event.TouchEvent.Type.DOWN; |
|
||||||
import static com.jme3.input.event.TouchEvent.Type.MOVE; |
|
||||||
import static com.jme3.input.event.TouchEvent.Type.UP; |
|
||||||
import com.jme3.math.Vector2f; |
|
||||||
import java.util.HashMap; |
|
||||||
import java.util.logging.Logger; |
|
||||||
|
|
||||||
/** |
|
||||||
* AndroidTouchHandler is the base class that receives touch inputs from the |
|
||||||
* Android system and creates the TouchEvents for jME. This class is designed |
|
||||||
* to handle the base touch events for Android rev 9 (Android 2.3). This is |
|
||||||
* extended by other classes to add features that were introducted after |
|
||||||
* Android rev 9. |
|
||||||
* |
|
||||||
* @author iwgeric |
|
||||||
*/ |
|
||||||
public class AndroidTouchHandler implements View.OnTouchListener { |
|
||||||
private static final Logger logger = Logger.getLogger(AndroidTouchHandler.class.getName()); |
|
||||||
|
|
||||||
final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>(); |
|
||||||
|
|
||||||
protected int numPointers = 0; |
|
||||||
|
|
||||||
protected AndroidInputHandler androidInput; |
|
||||||
protected AndroidGestureHandler gestureHandler; |
|
||||||
|
|
||||||
public AndroidTouchHandler(AndroidInputHandler androidInput, AndroidGestureHandler gestureHandler) { |
|
||||||
this.androidInput = androidInput; |
|
||||||
this.gestureHandler = gestureHandler; |
|
||||||
} |
|
||||||
|
|
||||||
public void initialize() { |
|
||||||
} |
|
||||||
|
|
||||||
public void destroy() { |
|
||||||
setView(null); |
|
||||||
} |
|
||||||
|
|
||||||
public void setView(View view) { |
|
||||||
if (view != null) { |
|
||||||
view.setOnTouchListener(this); |
|
||||||
} else { |
|
||||||
androidInput.getView().setOnTouchListener(null); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
protected int getPointerIndex(MotionEvent event) { |
|
||||||
return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) |
|
||||||
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT; |
|
||||||
} |
|
||||||
|
|
||||||
protected int getPointerId(MotionEvent event) { |
|
||||||
return event.getPointerId(getPointerIndex(event)); |
|
||||||
} |
|
||||||
|
|
||||||
protected int getAction(MotionEvent event) { |
|
||||||
return event.getAction() & MotionEvent.ACTION_MASK; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* onTouch gets called from android thread on touch events |
|
||||||
*/ |
|
||||||
public boolean onTouch(View view, MotionEvent event) { |
|
||||||
if (!androidInput.isInitialized() || view != androidInput.getView()) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
boolean bWasHandled = false; |
|
||||||
TouchEvent touch = null; |
|
||||||
// System.out.println("native : " + event.getAction());
|
|
||||||
int action = getAction(event); |
|
||||||
int pointerIndex = getPointerIndex(event); |
|
||||||
int pointerId = getPointerId(event); |
|
||||||
Vector2f lastPos = lastPositions.get(pointerId); |
|
||||||
float jmeX; |
|
||||||
float jmeY; |
|
||||||
|
|
||||||
numPointers = event.getPointerCount(); |
|
||||||
|
|
||||||
// final int historySize = event.getHistorySize();
|
|
||||||
//final int pointerCount = event.getPointerCount();
|
|
||||||
switch (getAction(event)) { |
|
||||||
case MotionEvent.ACTION_POINTER_DOWN: |
|
||||||
case MotionEvent.ACTION_DOWN: |
|
||||||
jmeX = androidInput.getJmeX(event.getX(pointerIndex)); |
|
||||||
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex))); |
|
||||||
touch = androidInput.getFreeTouchEvent(); |
|
||||||
touch.set(TouchEvent.Type.DOWN, jmeX, jmeY, 0, 0); |
|
||||||
touch.setPointerId(pointerId); |
|
||||||
touch.setTime(event.getEventTime()); |
|
||||||
touch.setPressure(event.getPressure(pointerIndex)); |
|
||||||
|
|
||||||
lastPos = new Vector2f(jmeX, jmeY); |
|
||||||
lastPositions.put(pointerId, lastPos); |
|
||||||
|
|
||||||
processEvent(touch); |
|
||||||
|
|
||||||
bWasHandled = true; |
|
||||||
break; |
|
||||||
case MotionEvent.ACTION_POINTER_UP: |
|
||||||
case MotionEvent.ACTION_CANCEL: |
|
||||||
case MotionEvent.ACTION_UP: |
|
||||||
jmeX = androidInput.getJmeX(event.getX(pointerIndex)); |
|
||||||
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex))); |
|
||||||
touch = androidInput.getFreeTouchEvent(); |
|
||||||
touch.set(TouchEvent.Type.UP, jmeX, jmeY, 0, 0); |
|
||||||
touch.setPointerId(pointerId); |
|
||||||
touch.setTime(event.getEventTime()); |
|
||||||
touch.setPressure(event.getPressure(pointerIndex)); |
|
||||||
lastPositions.remove(pointerId); |
|
||||||
|
|
||||||
processEvent(touch); |
|
||||||
|
|
||||||
bWasHandled = true; |
|
||||||
break; |
|
||||||
case MotionEvent.ACTION_MOVE: |
|
||||||
// Convert all pointers into events
|
|
||||||
for (int p = 0; p < event.getPointerCount(); p++) { |
|
||||||
jmeX = androidInput.getJmeX(event.getX(p)); |
|
||||||
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(p))); |
|
||||||
lastPos = lastPositions.get(event.getPointerId(p)); |
|
||||||
if (lastPos == null) { |
|
||||||
lastPos = new Vector2f(jmeX, jmeY); |
|
||||||
lastPositions.put(event.getPointerId(p), lastPos); |
|
||||||
} |
|
||||||
|
|
||||||
float dX = jmeX - lastPos.x; |
|
||||||
float dY = jmeY - lastPos.y; |
|
||||||
if (dX != 0 || dY != 0) { |
|
||||||
touch = androidInput.getFreeTouchEvent(); |
|
||||||
touch.set(TouchEvent.Type.MOVE, jmeX, jmeY, dX, dY); |
|
||||||
touch.setPointerId(event.getPointerId(p)); |
|
||||||
touch.setTime(event.getEventTime()); |
|
||||||
touch.setPressure(event.getPressure(p)); |
|
||||||
lastPos.set(jmeX, jmeY); |
|
||||||
|
|
||||||
processEvent(touch); |
|
||||||
|
|
||||||
bWasHandled = true; |
|
||||||
} |
|
||||||
} |
|
||||||
break; |
|
||||||
case MotionEvent.ACTION_OUTSIDE: |
|
||||||
break; |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
// Try to detect gestures
|
|
||||||
if (gestureHandler != null) { |
|
||||||
gestureHandler.detectGesture(event); |
|
||||||
} |
|
||||||
|
|
||||||
return bWasHandled; |
|
||||||
} |
|
||||||
|
|
||||||
protected void processEvent(TouchEvent event) { |
|
||||||
// Add the touch event
|
|
||||||
androidInput.addEvent(event); |
|
||||||
// MouseEvents do not support multi-touch, so only evaluate 1 finger pointer events
|
|
||||||
if (androidInput.isSimulateMouse() && numPointers == 1) { |
|
||||||
InputEvent mouseEvent = generateMouseEvent(event); |
|
||||||
if (mouseEvent != null) { |
|
||||||
// Add the mouse event
|
|
||||||
androidInput.addEvent(mouseEvent); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
// TODO: Ring Buffer for mouse events?
|
|
||||||
protected InputEvent generateMouseEvent(TouchEvent event) { |
|
||||||
InputEvent inputEvent = null; |
|
||||||
int newX; |
|
||||||
int newY; |
|
||||||
int newDX; |
|
||||||
int newDY; |
|
||||||
|
|
||||||
if (androidInput.isMouseEventsInvertX()) { |
|
||||||
newX = (int) (androidInput.invertX(event.getX())); |
|
||||||
newDX = (int)event.getDeltaX() * -1; |
|
||||||
} else { |
|
||||||
newX = (int) event.getX(); |
|
||||||
newDX = (int)event.getDeltaX(); |
|
||||||
} |
|
||||||
|
|
||||||
if (androidInput.isMouseEventsInvertY()) { |
|
||||||
newY = (int) (androidInput.invertY(event.getY())); |
|
||||||
newDY = (int)event.getDeltaY() * -1; |
|
||||||
} else { |
|
||||||
newY = (int) event.getY(); |
|
||||||
newDY = (int)event.getDeltaY(); |
|
||||||
} |
|
||||||
|
|
||||||
switch (event.getType()) { |
|
||||||
case DOWN: |
|
||||||
// Handle mouse down event
|
|
||||||
inputEvent = new MouseButtonEvent(0, true, newX, newY); |
|
||||||
inputEvent.setTime(event.getTime()); |
|
||||||
break; |
|
||||||
|
|
||||||
case UP: |
|
||||||
// Handle mouse up event
|
|
||||||
inputEvent = new MouseButtonEvent(0, false, newX, newY); |
|
||||||
inputEvent.setTime(event.getTime()); |
|
||||||
break; |
|
||||||
|
|
||||||
case HOVER_MOVE: |
|
||||||
case MOVE: |
|
||||||
inputEvent = new MouseMotionEvent(newX, newY, newDX, newDY, (int)event.getScaleSpan(), (int)event.getDeltaScaleSpan()); |
|
||||||
inputEvent.setTime(event.getTime()); |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
return inputEvent; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,475 @@ |
|||||||
|
/* |
||||||
|
* 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.android; |
||||||
|
|
||||||
|
import android.view.GestureDetector; |
||||||
|
import android.view.KeyEvent; |
||||||
|
import android.view.MotionEvent; |
||||||
|
import android.view.ScaleGestureDetector; |
||||||
|
import com.jme3.input.RawInputListener; |
||||||
|
import com.jme3.input.TouchInput; |
||||||
|
import com.jme3.input.event.InputEvent; |
||||||
|
import com.jme3.input.event.KeyInputEvent; |
||||||
|
import com.jme3.input.event.MouseButtonEvent; |
||||||
|
import com.jme3.input.event.MouseMotionEvent; |
||||||
|
import com.jme3.input.event.TouchEvent; |
||||||
|
import static com.jme3.input.event.TouchEvent.Type.DOWN; |
||||||
|
import static com.jme3.input.event.TouchEvent.Type.MOVE; |
||||||
|
import static com.jme3.input.event.TouchEvent.Type.UP; |
||||||
|
import com.jme3.math.Vector2f; |
||||||
|
import com.jme3.system.AppSettings; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue; |
||||||
|
import java.util.logging.Level; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* AndroidTouchInput is the base class that receives touch inputs from the |
||||||
|
* Android system and creates the TouchEvents for jME. This class is designed |
||||||
|
* to handle the base touch events for Android rev 9 (Android 2.3). This is |
||||||
|
* extended by other classes to add features that were introducted after |
||||||
|
* Android rev 9. |
||||||
|
* |
||||||
|
* @author iwgeric |
||||||
|
*/ |
||||||
|
public class AndroidTouchInput implements TouchInput { |
||||||
|
private static final Logger logger = Logger.getLogger(AndroidTouchInput.class.getName()); |
||||||
|
|
||||||
|
private boolean mouseEventsEnabled = true; |
||||||
|
private boolean mouseEventsInvertX = false; |
||||||
|
private boolean mouseEventsInvertY = false; |
||||||
|
private boolean keyboardEventsEnabled = false; |
||||||
|
private boolean dontSendHistory = false; |
||||||
|
|
||||||
|
protected int numPointers = 0; |
||||||
|
final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>(); |
||||||
|
final private ConcurrentLinkedQueue<InputEvent> inputEventQueue = new ConcurrentLinkedQueue<InputEvent>(); |
||||||
|
private final static int MAX_TOUCH_EVENTS = 1024; |
||||||
|
private final TouchEventPool touchEventPool = new TouchEventPool(MAX_TOUCH_EVENTS); |
||||||
|
private float scaleX = 1f; |
||||||
|
private float scaleY = 1f; |
||||||
|
|
||||||
|
private boolean initialized = false; |
||||||
|
private RawInputListener listener = null; |
||||||
|
|
||||||
|
private GestureDetector gestureDetector; |
||||||
|
private ScaleGestureDetector scaleDetector; |
||||||
|
|
||||||
|
protected AndroidInputHandler androidInput; |
||||||
|
|
||||||
|
public AndroidTouchInput(AndroidInputHandler androidInput) { |
||||||
|
this.androidInput = androidInput; |
||||||
|
} |
||||||
|
|
||||||
|
public GestureDetector getGestureDetector() { |
||||||
|
return gestureDetector; |
||||||
|
} |
||||||
|
|
||||||
|
public void setGestureDetector(GestureDetector gestureDetector) { |
||||||
|
this.gestureDetector = gestureDetector; |
||||||
|
} |
||||||
|
|
||||||
|
public ScaleGestureDetector getScaleDetector() { |
||||||
|
return scaleDetector; |
||||||
|
} |
||||||
|
|
||||||
|
public void setScaleDetector(ScaleGestureDetector scaleDetector) { |
||||||
|
this.scaleDetector = scaleDetector; |
||||||
|
} |
||||||
|
|
||||||
|
public float invertX(float origX) { |
||||||
|
return getJmeX(androidInput.getView().getWidth()) - origX; |
||||||
|
} |
||||||
|
|
||||||
|
public float invertY(float origY) { |
||||||
|
return getJmeY(androidInput.getView().getHeight()) - origY; |
||||||
|
} |
||||||
|
|
||||||
|
public float getJmeX(float origX) { |
||||||
|
return origX * scaleX; |
||||||
|
} |
||||||
|
|
||||||
|
public float getJmeY(float origY) { |
||||||
|
return origY * scaleY; |
||||||
|
} |
||||||
|
|
||||||
|
public void loadSettings(AppSettings settings) { |
||||||
|
keyboardEventsEnabled = settings.isEmulateKeyboard(); |
||||||
|
mouseEventsEnabled = settings.isEmulateMouse(); |
||||||
|
mouseEventsInvertX = settings.isEmulateMouseFlipX(); |
||||||
|
mouseEventsInvertY = settings.isEmulateMouseFlipY(); |
||||||
|
|
||||||
|
// view width and height are 0 until the view is displayed on the screen
|
||||||
|
if (androidInput.getView().getWidth() != 0 && androidInput.getView().getHeight() != 0) { |
||||||
|
scaleX = (float)settings.getWidth() / (float)androidInput.getView().getWidth(); |
||||||
|
scaleY = (float)settings.getHeight() / (float)androidInput.getView().getHeight(); |
||||||
|
} |
||||||
|
logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}", |
||||||
|
new Object[]{scaleX, scaleY}); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
protected int getPointerIndex(MotionEvent event) { |
||||||
|
return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) |
||||||
|
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT; |
||||||
|
} |
||||||
|
|
||||||
|
protected int getPointerId(MotionEvent event) { |
||||||
|
return event.getPointerId(getPointerIndex(event)); |
||||||
|
} |
||||||
|
|
||||||
|
protected int getAction(MotionEvent event) { |
||||||
|
return event.getAction() & MotionEvent.ACTION_MASK; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean onTouch(MotionEvent event) { |
||||||
|
if (!isInitialized()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
boolean bWasHandled = false; |
||||||
|
TouchEvent touch = null; |
||||||
|
// System.out.println("native : " + event.getAction());
|
||||||
|
int action = getAction(event); |
||||||
|
int pointerIndex = getPointerIndex(event); |
||||||
|
int pointerId = getPointerId(event); |
||||||
|
Vector2f lastPos = lastPositions.get(pointerId); |
||||||
|
float jmeX; |
||||||
|
float jmeY; |
||||||
|
|
||||||
|
numPointers = event.getPointerCount(); |
||||||
|
|
||||||
|
// final int historySize = event.getHistorySize();
|
||||||
|
//final int pointerCount = event.getPointerCount();
|
||||||
|
switch (getAction(event)) { |
||||||
|
case MotionEvent.ACTION_POINTER_DOWN: |
||||||
|
case MotionEvent.ACTION_DOWN: |
||||||
|
jmeX = getJmeX(event.getX(pointerIndex)); |
||||||
|
jmeY = invertY(getJmeY(event.getY(pointerIndex))); |
||||||
|
touch = getFreeTouchEvent(); |
||||||
|
touch.set(TouchEvent.Type.DOWN, jmeX, jmeY, 0, 0); |
||||||
|
touch.setPointerId(pointerId); |
||||||
|
touch.setTime(event.getEventTime()); |
||||||
|
touch.setPressure(event.getPressure(pointerIndex)); |
||||||
|
|
||||||
|
lastPos = new Vector2f(jmeX, jmeY); |
||||||
|
lastPositions.put(pointerId, lastPos); |
||||||
|
|
||||||
|
addEvent(touch); |
||||||
|
addEvent(generateMouseEvent(touch)); |
||||||
|
|
||||||
|
bWasHandled = true; |
||||||
|
break; |
||||||
|
case MotionEvent.ACTION_POINTER_UP: |
||||||
|
case MotionEvent.ACTION_CANCEL: |
||||||
|
case MotionEvent.ACTION_UP: |
||||||
|
jmeX = getJmeX(event.getX(pointerIndex)); |
||||||
|
jmeY = invertY(getJmeY(event.getY(pointerIndex))); |
||||||
|
touch = getFreeTouchEvent(); |
||||||
|
touch.set(TouchEvent.Type.UP, jmeX, jmeY, 0, 0); |
||||||
|
touch.setPointerId(pointerId); |
||||||
|
touch.setTime(event.getEventTime()); |
||||||
|
touch.setPressure(event.getPressure(pointerIndex)); |
||||||
|
lastPositions.remove(pointerId); |
||||||
|
|
||||||
|
addEvent(touch); |
||||||
|
addEvent(generateMouseEvent(touch)); |
||||||
|
|
||||||
|
bWasHandled = true; |
||||||
|
break; |
||||||
|
case MotionEvent.ACTION_MOVE: |
||||||
|
// Convert all pointers into events
|
||||||
|
for (int p = 0; p < event.getPointerCount(); p++) { |
||||||
|
jmeX = getJmeX(event.getX(p)); |
||||||
|
jmeY = invertY(getJmeY(event.getY(p))); |
||||||
|
lastPos = lastPositions.get(event.getPointerId(p)); |
||||||
|
if (lastPos == null) { |
||||||
|
lastPos = new Vector2f(jmeX, jmeY); |
||||||
|
lastPositions.put(event.getPointerId(p), lastPos); |
||||||
|
} |
||||||
|
|
||||||
|
float dX = jmeX - lastPos.x; |
||||||
|
float dY = jmeY - lastPos.y; |
||||||
|
if (dX != 0 || dY != 0) { |
||||||
|
touch = getFreeTouchEvent(); |
||||||
|
touch.set(TouchEvent.Type.MOVE, jmeX, jmeY, dX, dY); |
||||||
|
touch.setPointerId(event.getPointerId(p)); |
||||||
|
touch.setTime(event.getEventTime()); |
||||||
|
touch.setPressure(event.getPressure(p)); |
||||||
|
lastPos.set(jmeX, jmeY); |
||||||
|
|
||||||
|
addEvent(touch); |
||||||
|
addEvent(generateMouseEvent(touch)); |
||||||
|
|
||||||
|
bWasHandled = true; |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
case MotionEvent.ACTION_OUTSIDE: |
||||||
|
break; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// Try to detect gestures
|
||||||
|
if (gestureDetector != null) { |
||||||
|
gestureDetector.onTouchEvent(event); |
||||||
|
} |
||||||
|
if (scaleDetector != null) { |
||||||
|
scaleDetector.onTouchEvent(event); |
||||||
|
} |
||||||
|
|
||||||
|
return bWasHandled; |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: Ring Buffer for mouse events?
|
||||||
|
public InputEvent generateMouseEvent(TouchEvent event) { |
||||||
|
InputEvent inputEvent = null; |
||||||
|
int newX; |
||||||
|
int newY; |
||||||
|
int newDX; |
||||||
|
int newDY; |
||||||
|
|
||||||
|
// MouseEvents do not support multi-touch, so only evaluate 1 finger pointer events
|
||||||
|
if (!isSimulateMouse() || numPointers > 1) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
if (isMouseEventsInvertX()) { |
||||||
|
newX = (int) (invertX(event.getX())); |
||||||
|
newDX = (int)event.getDeltaX() * -1; |
||||||
|
} else { |
||||||
|
newX = (int) event.getX(); |
||||||
|
newDX = (int)event.getDeltaX(); |
||||||
|
} |
||||||
|
|
||||||
|
if (isMouseEventsInvertY()) { |
||||||
|
newY = (int) (invertY(event.getY())); |
||||||
|
newDY = (int)event.getDeltaY() * -1; |
||||||
|
} else { |
||||||
|
newY = (int) event.getY(); |
||||||
|
newDY = (int)event.getDeltaY(); |
||||||
|
} |
||||||
|
|
||||||
|
switch (event.getType()) { |
||||||
|
case DOWN: |
||||||
|
// Handle mouse down event
|
||||||
|
inputEvent = new MouseButtonEvent(0, true, newX, newY); |
||||||
|
inputEvent.setTime(event.getTime()); |
||||||
|
break; |
||||||
|
|
||||||
|
case UP: |
||||||
|
// Handle mouse up event
|
||||||
|
inputEvent = new MouseButtonEvent(0, false, newX, newY); |
||||||
|
inputEvent.setTime(event.getTime()); |
||||||
|
break; |
||||||
|
|
||||||
|
case HOVER_MOVE: |
||||||
|
case MOVE: |
||||||
|
inputEvent = new MouseMotionEvent(newX, newY, newDX, newDY, (int)event.getScaleSpan(), (int)event.getDeltaScaleSpan()); |
||||||
|
inputEvent.setTime(event.getTime()); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return inputEvent; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public boolean onKey(KeyEvent event) { |
||||||
|
if (!isInitialized()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
TouchEvent evt; |
||||||
|
// TODO: get touch event from pool
|
||||||
|
if (event.getAction() == KeyEvent.ACTION_DOWN) { |
||||||
|
evt = new TouchEvent(); |
||||||
|
evt.set(TouchEvent.Type.KEY_DOWN); |
||||||
|
evt.setKeyCode(event.getKeyCode()); |
||||||
|
evt.setCharacters(event.getCharacters()); |
||||||
|
evt.setTime(event.getEventTime()); |
||||||
|
|
||||||
|
// Send the event
|
||||||
|
addEvent(evt); |
||||||
|
|
||||||
|
} else if (event.getAction() == KeyEvent.ACTION_UP) { |
||||||
|
evt = new TouchEvent(); |
||||||
|
evt.set(TouchEvent.Type.KEY_UP); |
||||||
|
evt.setKeyCode(event.getKeyCode()); |
||||||
|
evt.setCharacters(event.getCharacters()); |
||||||
|
evt.setTime(event.getEventTime()); |
||||||
|
|
||||||
|
// Send the event
|
||||||
|
addEvent(evt); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
if (isSimulateKeyboard()) { |
||||||
|
KeyInputEvent kie; |
||||||
|
char unicodeChar = (char)event.getUnicodeChar(); |
||||||
|
int jmeKeyCode = AndroidKeyMapping.getJmeKey(event.getKeyCode()); |
||||||
|
|
||||||
|
boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN; |
||||||
|
boolean repeating = pressed && event.getRepeatCount() > 0; |
||||||
|
|
||||||
|
kie = new KeyInputEvent(jmeKeyCode, unicodeChar, pressed, repeating); |
||||||
|
kie.setTime(event.getEventTime()); |
||||||
|
addEvent(kie); |
||||||
|
// logger.log(Level.FINE, "onKey keyCode: {0}, jmeKeyCode: {1}, pressed: {2}, repeating: {3}",
|
||||||
|
// new Object[]{event.getKeyCode(), jmeKeyCode, pressed, repeating});
|
||||||
|
// logger.log(Level.FINE, "creating KeyInputEvent: {0}", kie);
|
||||||
|
} |
||||||
|
|
||||||
|
// consume all keys ourself except Volume Up/Down and Menu
|
||||||
|
// Don't do Menu so that typical Android Menus can be created and used
|
||||||
|
// by the user in MainActivity
|
||||||
|
if ((event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) || |
||||||
|
(event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) || |
||||||
|
(event.getKeyCode() == KeyEvent.KEYCODE_MENU)) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
// JME3 Input interface
|
||||||
|
@Override |
||||||
|
public void initialize() { |
||||||
|
touchEventPool.initialize(); |
||||||
|
|
||||||
|
initialized = true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void destroy() { |
||||||
|
initialized = false; |
||||||
|
|
||||||
|
touchEventPool.destroy(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isInitialized() { |
||||||
|
return initialized; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setInputListener(RawInputListener listener) { |
||||||
|
this.listener = listener; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getInputTimeNanos() { |
||||||
|
return System.nanoTime(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void update() { |
||||||
|
if (listener != null) { |
||||||
|
InputEvent inputEvent; |
||||||
|
|
||||||
|
while ((inputEvent = inputEventQueue.poll()) != null) { |
||||||
|
if (inputEvent instanceof TouchEvent) { |
||||||
|
listener.onTouchEvent((TouchEvent)inputEvent); |
||||||
|
} else if (inputEvent instanceof MouseButtonEvent) { |
||||||
|
listener.onMouseButtonEvent((MouseButtonEvent)inputEvent); |
||||||
|
} else if (inputEvent instanceof MouseMotionEvent) { |
||||||
|
listener.onMouseMotionEvent((MouseMotionEvent)inputEvent); |
||||||
|
} else if (inputEvent instanceof KeyInputEvent) { |
||||||
|
listener.onKeyEvent((KeyInputEvent)inputEvent); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
public TouchEvent getFreeTouchEvent() { |
||||||
|
return touchEventPool.getNextFreeEvent(); |
||||||
|
} |
||||||
|
|
||||||
|
public void addEvent(InputEvent event) { |
||||||
|
if (event == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
logger.log(Level.INFO, "event: {0}", event); |
||||||
|
|
||||||
|
inputEventQueue.add(event); |
||||||
|
if (event instanceof TouchEvent) { |
||||||
|
touchEventPool.storeEvent((TouchEvent)event); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setSimulateMouse(boolean simulate) { |
||||||
|
this.mouseEventsEnabled = simulate; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isSimulateMouse() { |
||||||
|
return mouseEventsEnabled; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isMouseEventsInvertX() { |
||||||
|
return mouseEventsInvertX; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isMouseEventsInvertY() { |
||||||
|
return mouseEventsInvertY; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setSimulateKeyboard(boolean simulate) { |
||||||
|
this.keyboardEventsEnabled = simulate; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isSimulateKeyboard() { |
||||||
|
return keyboardEventsEnabled; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setOmitHistoricEvents(boolean dontSendHistory) { |
||||||
|
this.dontSendHistory = dontSendHistory; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 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.opengl; |
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.lang.reflect.Proxy; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Comparator; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
public class GLTiming implements InvocationHandler { |
||||||
|
|
||||||
|
private final Object obj; |
||||||
|
private final GLTimingState state; |
||||||
|
|
||||||
|
public GLTiming(Object obj, GLTimingState state) { |
||||||
|
this.obj = obj; |
||||||
|
this.state = state; |
||||||
|
} |
||||||
|
|
||||||
|
public static Object createGLTiming(Object glInterface, GLTimingState state, Class<?> ... glInterfaceClasses) { |
||||||
|
return Proxy.newProxyInstance(glInterface.getClass().getClassLoader(), |
||||||
|
glInterfaceClasses, |
||||||
|
new GLTiming(glInterface, state)); |
||||||
|
} |
||||||
|
|
||||||
|
private static class CallTimingComparator implements Comparator<Map.Entry<String, Long>> { |
||||||
|
@Override |
||||||
|
public int compare(Map.Entry<String, Long> o1, Map.Entry<String, Long> o2) { |
||||||
|
return (int) (o2.getValue() - o1.getValue()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
||||||
|
String methodName = method.getName(); |
||||||
|
if (methodName.equals("resetStats")) { |
||||||
|
if (state.lastPrintOutTime + 1000000000 <= System.nanoTime() && state.sampleCount > 0) { |
||||||
|
state.timeSpentInGL /= state.sampleCount; |
||||||
|
System.out.println("--- TOTAL TIME SPENT IN GL CALLS: " + (state.timeSpentInGL/1000) + "us"); |
||||||
|
|
||||||
|
Map.Entry<String, Long>[] callTimes = new Map.Entry[state.callTiming.size()]; |
||||||
|
int i = 0; |
||||||
|
for (Map.Entry<String, Long> callTime : state.callTiming.entrySet()) { |
||||||
|
callTimes[i++] = callTime; |
||||||
|
} |
||||||
|
Arrays.sort(callTimes, new CallTimingComparator()); |
||||||
|
int limit = 10; |
||||||
|
for (Map.Entry<String, Long> callTime : callTimes) { |
||||||
|
long val = callTime.getValue() / state.sampleCount; |
||||||
|
String name = callTime.getKey(); |
||||||
|
String pad = " ".substring(0, 30 - name.length()); |
||||||
|
System.out.println("\t" + callTime.getKey() + pad + (val/1000) + "us"); |
||||||
|
if (limit-- == 0) break; |
||||||
|
} |
||||||
|
for (Map.Entry<String, Long> callTime : callTimes) { |
||||||
|
state.callTiming.put(callTime.getKey(), Long.valueOf(0)); |
||||||
|
} |
||||||
|
|
||||||
|
state.sampleCount = 0; |
||||||
|
state.timeSpentInGL = 0; |
||||||
|
state.lastPrintOutTime = System.nanoTime(); |
||||||
|
} else { |
||||||
|
state.sampleCount++; |
||||||
|
} |
||||||
|
return null; |
||||||
|
} else { |
||||||
|
Long currentTimeObj = state.callTiming.get(methodName); |
||||||
|
long currentTime = 0; |
||||||
|
if (currentTimeObj != null) currentTime = currentTimeObj; |
||||||
|
|
||||||
|
|
||||||
|
long startTime = System.nanoTime(); |
||||||
|
Object result = method.invoke(obj, args); |
||||||
|
long delta = System.nanoTime() - startTime; |
||||||
|
|
||||||
|
currentTime += delta; |
||||||
|
state.timeSpentInGL += delta; |
||||||
|
|
||||||
|
state.callTiming.put(methodName, currentTime); |
||||||
|
|
||||||
|
if (delta > 1000000 && !methodName.equals("glClear")) { |
||||||
|
// More than 1ms
|
||||||
|
// Ignore glClear as it cannot be avoided.
|
||||||
|
System.out.println("GL call " + methodName + " took " + (delta/1000) + "us to execute!"); |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 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.opengl; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
|
||||||
|
public class GLTimingState { |
||||||
|
long timeSpentInGL = 0; |
||||||
|
int sampleCount = 0; |
||||||
|
long lastPrintOutTime = 0; |
||||||
|
final HashMap<String, Long> callTiming = new HashMap<String, Long>(); |
||||||
|
} |
@ -0,0 +1,140 @@ |
|||||||
|
/* |
||||||
|
* 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.util; |
||||||
|
|
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.texture.Image; |
||||||
|
import com.jme3.texture.Image.Format; |
||||||
|
import com.jme3.texture.image.ImageRaster; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
public class MipMapGenerator { |
||||||
|
|
||||||
|
private MipMapGenerator() { |
||||||
|
} |
||||||
|
|
||||||
|
public static Image scaleImage(Image inputImage, int outputWidth, int outputHeight) { |
||||||
|
int size = outputWidth * outputHeight * inputImage.getFormat().getBitsPerPixel() / 8; |
||||||
|
ByteBuffer buffer = BufferUtils.createByteBuffer(size); |
||||||
|
Image outputImage = new Image(inputImage.getFormat(), |
||||||
|
outputWidth, |
||||||
|
outputHeight, |
||||||
|
buffer, |
||||||
|
inputImage.getColorSpace()); |
||||||
|
|
||||||
|
ImageRaster input = ImageRaster.create(inputImage, 0, 0, false); |
||||||
|
ImageRaster output = ImageRaster.create(outputImage, 0, 0, false); |
||||||
|
|
||||||
|
float xRatio = ((float)(input.getWidth() - 1)) / output.getWidth(); |
||||||
|
float yRatio = ((float)(input.getHeight() - 1)) / output.getHeight(); |
||||||
|
|
||||||
|
ColorRGBA outputColor = new ColorRGBA(); |
||||||
|
ColorRGBA bottomLeft = new ColorRGBA(); |
||||||
|
ColorRGBA bottomRight = new ColorRGBA(); |
||||||
|
ColorRGBA topLeft = new ColorRGBA(); |
||||||
|
ColorRGBA topRight = new ColorRGBA(); |
||||||
|
|
||||||
|
for (int y = 0; y < outputHeight; y++) { |
||||||
|
for (int x = 0; x < outputWidth; x++) { |
||||||
|
float x2f = x * xRatio; |
||||||
|
float y2f = y * yRatio; |
||||||
|
|
||||||
|
int x2 = (int)x2f; |
||||||
|
int y2 = (int)y2f; |
||||||
|
|
||||||
|
float xDiff = x2f - x2; |
||||||
|
float yDiff = y2f - y2; |
||||||
|
|
||||||
|
input.getPixel(x2, y2, bottomLeft); |
||||||
|
input.getPixel(x2 + 1, y2, bottomRight); |
||||||
|
input.getPixel(x2, y2 + 1, topLeft); |
||||||
|
input.getPixel(x2 + 1, y2 + 1, topRight); |
||||||
|
|
||||||
|
bottomLeft.multLocal( (1f - xDiff) * (1f - yDiff) ); |
||||||
|
bottomRight.multLocal( (xDiff) * (1f - yDiff) ); |
||||||
|
topLeft.multLocal( (1f - xDiff) * (yDiff) ); |
||||||
|
topRight.multLocal( (xDiff) * (yDiff) ); |
||||||
|
|
||||||
|
outputColor.set(bottomLeft).addLocal(bottomRight) |
||||||
|
.addLocal(topLeft).addLocal(topRight); |
||||||
|
|
||||||
|
output.setPixel(x, y, outputColor); |
||||||
|
} |
||||||
|
} |
||||||
|
return outputImage; |
||||||
|
} |
||||||
|
|
||||||
|
public static Image resizeToPowerOf2(Image original){ |
||||||
|
int potWidth = FastMath.nearestPowerOfTwo(original.getWidth()); |
||||||
|
int potHeight = FastMath.nearestPowerOfTwo(original.getHeight()); |
||||||
|
return scaleImage(original, potWidth, potHeight); |
||||||
|
} |
||||||
|
|
||||||
|
public static void generateMipMaps(Image image){ |
||||||
|
int width = image.getWidth(); |
||||||
|
int height = image.getHeight(); |
||||||
|
|
||||||
|
Image current = image; |
||||||
|
ArrayList<ByteBuffer> output = new ArrayList<ByteBuffer>(); |
||||||
|
int totalSize = 0; |
||||||
|
|
||||||
|
while (height >= 1 || width >= 1){ |
||||||
|
output.add(current.getData(0)); |
||||||
|
totalSize += current.getData(0).capacity(); |
||||||
|
|
||||||
|
if (height == 1 || width == 1) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
height /= 2; |
||||||
|
width /= 2; |
||||||
|
|
||||||
|
current = scaleImage(current, width, height); |
||||||
|
} |
||||||
|
|
||||||
|
ByteBuffer combinedData = BufferUtils.createByteBuffer(totalSize); |
||||||
|
int[] mipSizes = new int[output.size()]; |
||||||
|
for (int i = 0; i < output.size(); i++){ |
||||||
|
ByteBuffer data = output.get(i); |
||||||
|
data.clear(); |
||||||
|
combinedData.put(data); |
||||||
|
mipSizes[i] = data.capacity(); |
||||||
|
} |
||||||
|
combinedData.flip(); |
||||||
|
|
||||||
|
// insert mip data into image
|
||||||
|
image.setData(0, combinedData); |
||||||
|
image.setMipMapSizes(mipSizes); |
||||||
|
} |
||||||
|
} |
@ -1,152 +1,152 @@ |
|||||||
/* |
/* |
||||||
* Copyright (c) 2009-2012 jMonkeyEngine |
* Copyright (c) 2009-2012 jMonkeyEngine |
||||||
* All rights reserved. |
* All rights reserved. |
||||||
* |
* |
||||||
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
||||||
* modification, are permitted provided that the following conditions are |
* modification, are permitted provided that the following conditions are |
||||||
* met: |
* met: |
||||||
* |
* |
||||||
* * Redistributions of source code must retain the above copyright |
* * Redistributions of source code must retain the above copyright |
||||||
* notice, this list of conditions and the following disclaimer. |
* notice, this list of conditions and the following disclaimer. |
||||||
* |
* |
||||||
* * Redistributions in binary form must reproduce the above copyright |
* * Redistributions in binary form must reproduce the above copyright |
||||||
* notice, this list of conditions and the following disclaimer in the |
* notice, this list of conditions and the following disclaimer in the |
||||||
* documentation and/or other materials provided with the distribution. |
* documentation and/or other materials provided with the distribution. |
||||||
* |
* |
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
* may be used to endorse or promote products derived from this software |
* may be used to endorse or promote products derived from this software |
||||||
* without specific prior written permission. |
* without specific prior written permission. |
||||||
* |
* |
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
*/ |
*/ |
||||||
package jme3test.bullet; |
package jme3test.bullet; |
||||||
|
|
||||||
import com.jme3.app.SimpleApplication; |
import com.jme3.app.SimpleApplication; |
||||||
import com.jme3.bullet.BulletAppState; |
import com.jme3.bullet.BulletAppState; |
||||||
import com.jme3.bullet.PhysicsSpace; |
import com.jme3.bullet.PhysicsSpace; |
||||||
import com.jme3.bullet.collision.shapes.BoxCollisionShape; |
import com.jme3.bullet.collision.shapes.BoxCollisionShape; |
||||||
import com.jme3.bullet.collision.shapes.MeshCollisionShape; |
import com.jme3.bullet.collision.shapes.MeshCollisionShape; |
||||||
import com.jme3.bullet.collision.shapes.SphereCollisionShape; |
import com.jme3.bullet.collision.shapes.SphereCollisionShape; |
||||||
import com.jme3.bullet.control.RigidBodyControl; |
import com.jme3.bullet.control.RigidBodyControl; |
||||||
import com.jme3.input.MouseInput; |
import com.jme3.input.MouseInput; |
||||||
import com.jme3.input.controls.ActionListener; |
import com.jme3.input.controls.ActionListener; |
||||||
import com.jme3.input.controls.MouseButtonTrigger; |
import com.jme3.input.controls.MouseButtonTrigger; |
||||||
import com.jme3.material.Material; |
import com.jme3.material.Material; |
||||||
import com.jme3.math.ColorRGBA; |
import com.jme3.math.ColorRGBA; |
||||||
import com.jme3.math.Vector3f; |
import com.jme3.math.Vector3f; |
||||||
import com.jme3.renderer.RenderManager; |
import com.jme3.renderer.RenderManager; |
||||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode; |
import com.jme3.renderer.queue.RenderQueue.ShadowMode; |
||||||
import com.jme3.scene.Geometry; |
import com.jme3.scene.Geometry; |
||||||
import com.jme3.scene.Node; |
import com.jme3.scene.Node; |
||||||
import com.jme3.scene.shape.Box; |
import com.jme3.scene.shape.Box; |
||||||
import com.jme3.scene.shape.Sphere; |
import com.jme3.scene.shape.Sphere; |
||||||
import com.jme3.scene.shape.Sphere.TextureMode; |
import com.jme3.scene.shape.Sphere.TextureMode; |
||||||
|
|
||||||
/** |
/** |
||||||
* |
* |
||||||
* @author normenhansen |
* @author normenhansen |
||||||
*/ |
*/ |
||||||
public class TestCcd extends SimpleApplication implements ActionListener { |
public class TestCcd extends SimpleApplication implements ActionListener { |
||||||
|
|
||||||
private Material mat; |
private Material mat; |
||||||
private Material mat2; |
private Material mat2; |
||||||
private Sphere bullet; |
private Sphere bullet; |
||||||
private SphereCollisionShape bulletCollisionShape; |
private SphereCollisionShape bulletCollisionShape; |
||||||
private BulletAppState bulletAppState; |
private BulletAppState bulletAppState; |
||||||
|
|
||||||
public static void main(String[] args) { |
public static void main(String[] args) { |
||||||
TestCcd app = new TestCcd(); |
TestCcd app = new TestCcd(); |
||||||
app.start(); |
app.start(); |
||||||
} |
} |
||||||
|
|
||||||
private void setupKeys() { |
private void setupKeys() { |
||||||
inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); |
inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); |
||||||
inputManager.addMapping("shoot2", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)); |
inputManager.addMapping("shoot2", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)); |
||||||
inputManager.addListener(this, "shoot"); |
inputManager.addListener(this, "shoot"); |
||||||
inputManager.addListener(this, "shoot2"); |
inputManager.addListener(this, "shoot2"); |
||||||
} |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
public void simpleInitApp() { |
public void simpleInitApp() { |
||||||
bulletAppState = new BulletAppState(); |
bulletAppState = new BulletAppState(); |
||||||
stateManager.attach(bulletAppState); |
stateManager.attach(bulletAppState); |
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager); |
bulletAppState.setDebugEnabled(true); |
||||||
bullet = new Sphere(32, 32, 0.4f, true, false); |
bullet = new Sphere(32, 32, 0.4f, true, false); |
||||||
bullet.setTextureMode(TextureMode.Projected); |
bullet.setTextureMode(TextureMode.Projected); |
||||||
bulletCollisionShape = new SphereCollisionShape(0.1f); |
bulletCollisionShape = new SphereCollisionShape(0.1f); |
||||||
setupKeys(); |
setupKeys(); |
||||||
|
|
||||||
mat = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); |
mat = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
mat.getAdditionalRenderState().setWireframe(true); |
mat.getAdditionalRenderState().setWireframe(true); |
||||||
mat.setColor("Color", ColorRGBA.Green); |
mat.setColor("Color", ColorRGBA.Green); |
||||||
|
|
||||||
mat2 = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); |
mat2 = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
mat2.getAdditionalRenderState().setWireframe(true); |
mat2.getAdditionalRenderState().setWireframe(true); |
||||||
mat2.setColor("Color", ColorRGBA.Red); |
mat2.setColor("Color", ColorRGBA.Red); |
||||||
|
|
||||||
// An obstacle mesh, does not move (mass=0)
|
// An obstacle mesh, does not move (mass=0)
|
||||||
Node node2 = new Node(); |
Node node2 = new Node(); |
||||||
node2.setName("mesh"); |
node2.setName("mesh"); |
||||||
node2.setLocalTranslation(new Vector3f(2.5f, 0, 0f)); |
node2.setLocalTranslation(new Vector3f(2.5f, 0, 0f)); |
||||||
node2.addControl(new RigidBodyControl(new MeshCollisionShape(new Box(Vector3f.ZERO, 4, 4, 0.1f)), 0)); |
node2.addControl(new RigidBodyControl(new MeshCollisionShape(new Box(Vector3f.ZERO, 4, 4, 0.1f)), 0)); |
||||||
rootNode.attachChild(node2); |
rootNode.attachChild(node2); |
||||||
getPhysicsSpace().add(node2); |
getPhysicsSpace().add(node2); |
||||||
|
|
||||||
// The floor, does not move (mass=0)
|
// The floor, does not move (mass=0)
|
||||||
Node node3 = new Node(); |
Node node3 = new Node(); |
||||||
node3.setLocalTranslation(new Vector3f(0f, -6, 0f)); |
node3.setLocalTranslation(new Vector3f(0f, -6, 0f)); |
||||||
node3.addControl(new RigidBodyControl(new BoxCollisionShape(new Vector3f(100, 1, 100)), 0)); |
node3.addControl(new RigidBodyControl(new BoxCollisionShape(new Vector3f(100, 1, 100)), 0)); |
||||||
rootNode.attachChild(node3); |
rootNode.attachChild(node3); |
||||||
getPhysicsSpace().add(node3); |
getPhysicsSpace().add(node3); |
||||||
|
|
||||||
} |
} |
||||||
|
|
||||||
private PhysicsSpace getPhysicsSpace() { |
private PhysicsSpace getPhysicsSpace() { |
||||||
return bulletAppState.getPhysicsSpace(); |
return bulletAppState.getPhysicsSpace(); |
||||||
} |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
public void simpleUpdate(float tpf) { |
public void simpleUpdate(float tpf) { |
||||||
//TODO: add update code
|
//TODO: add update code
|
||||||
} |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
public void simpleRender(RenderManager rm) { |
public void simpleRender(RenderManager rm) { |
||||||
//TODO: add render code
|
//TODO: add render code
|
||||||
} |
} |
||||||
|
|
||||||
public void onAction(String binding, boolean value, float tpf) { |
public void onAction(String binding, boolean value, float tpf) { |
||||||
if (binding.equals("shoot") && !value) { |
if (binding.equals("shoot") && !value) { |
||||||
Geometry bulletg = new Geometry("bullet", bullet); |
Geometry bulletg = new Geometry("bullet", bullet); |
||||||
bulletg.setMaterial(mat); |
bulletg.setMaterial(mat); |
||||||
bulletg.setName("bullet"); |
bulletg.setName("bullet"); |
||||||
bulletg.setLocalTranslation(cam.getLocation()); |
bulletg.setLocalTranslation(cam.getLocation()); |
||||||
bulletg.setShadowMode(ShadowMode.CastAndReceive); |
bulletg.setShadowMode(ShadowMode.CastAndReceive); |
||||||
bulletg.addControl(new RigidBodyControl(bulletCollisionShape, 1)); |
bulletg.addControl(new RigidBodyControl(bulletCollisionShape, 1)); |
||||||
bulletg.getControl(RigidBodyControl.class).setCcdMotionThreshold(0.1f); |
bulletg.getControl(RigidBodyControl.class).setCcdMotionThreshold(0.1f); |
||||||
bulletg.getControl(RigidBodyControl.class).setLinearVelocity(cam.getDirection().mult(40)); |
bulletg.getControl(RigidBodyControl.class).setLinearVelocity(cam.getDirection().mult(40)); |
||||||
rootNode.attachChild(bulletg); |
rootNode.attachChild(bulletg); |
||||||
getPhysicsSpace().add(bulletg); |
getPhysicsSpace().add(bulletg); |
||||||
} else if (binding.equals("shoot2") && !value) { |
} else if (binding.equals("shoot2") && !value) { |
||||||
Geometry bulletg = new Geometry("bullet", bullet); |
Geometry bulletg = new Geometry("bullet", bullet); |
||||||
bulletg.setMaterial(mat2); |
bulletg.setMaterial(mat2); |
||||||
bulletg.setName("bullet"); |
bulletg.setName("bullet"); |
||||||
bulletg.setLocalTranslation(cam.getLocation()); |
bulletg.setLocalTranslation(cam.getLocation()); |
||||||
bulletg.setShadowMode(ShadowMode.CastAndReceive); |
bulletg.setShadowMode(ShadowMode.CastAndReceive); |
||||||
bulletg.addControl(new RigidBodyControl(bulletCollisionShape, 1)); |
bulletg.addControl(new RigidBodyControl(bulletCollisionShape, 1)); |
||||||
bulletg.getControl(RigidBodyControl.class).setLinearVelocity(cam.getDirection().mult(40)); |
bulletg.getControl(RigidBodyControl.class).setLinearVelocity(cam.getDirection().mult(40)); |
||||||
rootNode.attachChild(bulletg); |
rootNode.attachChild(bulletg); |
||||||
getPhysicsSpace().add(bulletg); |
getPhysicsSpace().add(bulletg); |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -1,98 +1,98 @@ |
|||||||
/* |
/* |
||||||
* Copyright (c) 2009-2012 jMonkeyEngine |
* Copyright (c) 2009-2012 jMonkeyEngine |
||||||
* All rights reserved. |
* All rights reserved. |
||||||
* |
* |
||||||
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
||||||
* modification, are permitted provided that the following conditions are |
* modification, are permitted provided that the following conditions are |
||||||
* met: |
* met: |
||||||
* |
* |
||||||
* * Redistributions of source code must retain the above copyright |
* * Redistributions of source code must retain the above copyright |
||||||
* notice, this list of conditions and the following disclaimer. |
* notice, this list of conditions and the following disclaimer. |
||||||
* |
* |
||||||
* * Redistributions in binary form must reproduce the above copyright |
* * Redistributions in binary form must reproduce the above copyright |
||||||
* notice, this list of conditions and the following disclaimer in the |
* notice, this list of conditions and the following disclaimer in the |
||||||
* documentation and/or other materials provided with the distribution. |
* documentation and/or other materials provided with the distribution. |
||||||
* |
* |
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
* may be used to endorse or promote products derived from this software |
* may be used to endorse or promote products derived from this software |
||||||
* without specific prior written permission. |
* without specific prior written permission. |
||||||
* |
* |
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
*/ |
*/ |
||||||
|
|
||||||
package jme3test.bullet; |
package jme3test.bullet; |
||||||
|
|
||||||
import com.jme3.app.SimpleApplication; |
import com.jme3.app.SimpleApplication; |
||||||
import com.jme3.bullet.BulletAppState; |
import com.jme3.bullet.BulletAppState; |
||||||
import com.jme3.bullet.PhysicsSpace; |
import com.jme3.bullet.PhysicsSpace; |
||||||
import com.jme3.bullet.collision.PhysicsCollisionEvent; |
import com.jme3.bullet.collision.PhysicsCollisionEvent; |
||||||
import com.jme3.bullet.collision.PhysicsCollisionListener; |
import com.jme3.bullet.collision.PhysicsCollisionListener; |
||||||
import com.jme3.bullet.collision.shapes.SphereCollisionShape; |
import com.jme3.bullet.collision.shapes.SphereCollisionShape; |
||||||
import com.jme3.renderer.RenderManager; |
import com.jme3.renderer.RenderManager; |
||||||
import com.jme3.scene.shape.Sphere; |
import com.jme3.scene.shape.Sphere; |
||||||
import com.jme3.scene.shape.Sphere.TextureMode; |
import com.jme3.scene.shape.Sphere.TextureMode; |
||||||
|
|
||||||
/** |
/** |
||||||
* |
* |
||||||
* @author normenhansen |
* @author normenhansen |
||||||
*/ |
*/ |
||||||
public class TestCollisionListener extends SimpleApplication implements PhysicsCollisionListener { |
public class TestCollisionListener extends SimpleApplication implements PhysicsCollisionListener { |
||||||
|
|
||||||
private BulletAppState bulletAppState; |
private BulletAppState bulletAppState; |
||||||
private Sphere bullet; |
private Sphere bullet; |
||||||
private SphereCollisionShape bulletCollisionShape; |
private SphereCollisionShape bulletCollisionShape; |
||||||
|
|
||||||
public static void main(String[] args) { |
public static void main(String[] args) { |
||||||
TestCollisionListener app = new TestCollisionListener(); |
TestCollisionListener app = new TestCollisionListener(); |
||||||
app.start(); |
app.start(); |
||||||
} |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
public void simpleInitApp() { |
public void simpleInitApp() { |
||||||
bulletAppState = new BulletAppState(); |
bulletAppState = new BulletAppState(); |
||||||
stateManager.attach(bulletAppState); |
stateManager.attach(bulletAppState); |
||||||
bulletAppState.getPhysicsSpace().enableDebug(assetManager); |
bulletAppState.setDebugEnabled(true); |
||||||
bullet = new Sphere(32, 32, 0.4f, true, false); |
bullet = new Sphere(32, 32, 0.4f, true, false); |
||||||
bullet.setTextureMode(TextureMode.Projected); |
bullet.setTextureMode(TextureMode.Projected); |
||||||
bulletCollisionShape = new SphereCollisionShape(0.4f); |
bulletCollisionShape = new SphereCollisionShape(0.4f); |
||||||
|
|
||||||
PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace()); |
PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace()); |
||||||
PhysicsTestHelper.createBallShooter(this, rootNode, bulletAppState.getPhysicsSpace()); |
PhysicsTestHelper.createBallShooter(this, rootNode, bulletAppState.getPhysicsSpace()); |
||||||
|
|
||||||
// add ourselves as collision listener
|
// add ourselves as collision listener
|
||||||
getPhysicsSpace().addCollisionListener(this); |
getPhysicsSpace().addCollisionListener(this); |
||||||
} |
} |
||||||
|
|
||||||
private PhysicsSpace getPhysicsSpace(){ |
private PhysicsSpace getPhysicsSpace(){ |
||||||
return bulletAppState.getPhysicsSpace(); |
return bulletAppState.getPhysicsSpace(); |
||||||
} |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
public void simpleUpdate(float tpf) { |
public void simpleUpdate(float tpf) { |
||||||
//TODO: add update code
|
//TODO: add update code
|
||||||
} |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
public void simpleRender(RenderManager rm) { |
public void simpleRender(RenderManager rm) { |
||||||
//TODO: add render code
|
//TODO: add render code
|
||||||
} |
} |
||||||
|
|
||||||
public void collision(PhysicsCollisionEvent event) { |
public void collision(PhysicsCollisionEvent event) { |
||||||
if ("Box".equals(event.getNodeA().getName()) || "Box".equals(event.getNodeB().getName())) { |
if ("Box".equals(event.getNodeA().getName()) || "Box".equals(event.getNodeB().getName())) { |
||||||
if ("bullet".equals(event.getNodeA().getName()) || "bullet".equals(event.getNodeB().getName())) { |
if ("bullet".equals(event.getNodeA().getName()) || "bullet".equals(event.getNodeB().getName())) { |
||||||
fpsText.setText("You hit the box!"); |
fpsText.setText("You hit the box!"); |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
} |
} |
||||||
|
@ -0,0 +1,83 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 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.model.anim; |
||||||
|
|
||||||
|
import com.jme3.animation.AnimChannel; |
||||||
|
import com.jme3.animation.AnimControl; |
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.export.binary.BinaryExporter; |
||||||
|
import com.jme3.light.DirectionalLight; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
|
||||||
|
public class TestModelExportingCloning extends SimpleApplication { |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
TestModelExportingCloning app = new TestModelExportingCloning(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
cam.setLocation(new Vector3f(10f, 3f, 40f)); |
||||||
|
cam.lookAtDirection(Vector3f.UNIT_Z.negate(), Vector3f.UNIT_Y); |
||||||
|
|
||||||
|
DirectionalLight dl = new DirectionalLight(); |
||||||
|
dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal()); |
||||||
|
dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f)); |
||||||
|
rootNode.addLight(dl); |
||||||
|
|
||||||
|
AnimControl control; |
||||||
|
AnimChannel channel; |
||||||
|
|
||||||
|
Spatial originalModel = assetManager.loadModel("Models/Oto/Oto.mesh.xml"); |
||||||
|
control = originalModel.getControl(AnimControl.class); |
||||||
|
channel = control.createChannel(); |
||||||
|
channel.setAnim("Walk"); |
||||||
|
rootNode.attachChild(originalModel); |
||||||
|
|
||||||
|
Spatial clonedModel = originalModel.clone(); |
||||||
|
clonedModel.move(10, 0, 0); |
||||||
|
control = clonedModel.getControl(AnimControl.class); |
||||||
|
channel = control.createChannel(); |
||||||
|
channel.setAnim("push"); |
||||||
|
rootNode.attachChild(clonedModel); |
||||||
|
|
||||||
|
Spatial exportedModel = BinaryExporter.saveAndLoad(assetManager, originalModel); |
||||||
|
exportedModel.move(20, 0, 0); |
||||||
|
control = exportedModel.getControl(AnimControl.class); |
||||||
|
channel = control.createChannel(); |
||||||
|
channel.setAnim("pull"); |
||||||
|
rootNode.attachChild(exportedModel); |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -1,67 +0,0 @@ |
|||||||
package com.jme3.audio.android; |
|
||||||
|
|
||||||
import com.jme3.asset.AssetKey; |
|
||||||
import com.jme3.audio.AudioData; |
|
||||||
import com.jme3.audio.AudioRenderer; |
|
||||||
import com.jme3.util.NativeObject; |
|
||||||
|
|
||||||
public class AndroidAudioData extends AudioData { |
|
||||||
|
|
||||||
protected AssetKey<?> assetKey; |
|
||||||
protected float currentVolume = 0f; |
|
||||||
|
|
||||||
public AndroidAudioData(){ |
|
||||||
super(); |
|
||||||
} |
|
||||||
|
|
||||||
protected AndroidAudioData(int id){ |
|
||||||
super(id); |
|
||||||
} |
|
||||||
|
|
||||||
public AssetKey<?> getAssetKey() { |
|
||||||
return assetKey; |
|
||||||
} |
|
||||||
|
|
||||||
public void setAssetKey(AssetKey<?> assetKey) { |
|
||||||
this.assetKey = assetKey; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public DataType getDataType() { |
|
||||||
return DataType.Buffer; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public float getDuration() { |
|
||||||
return 0; // TODO: ???
|
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void resetObject() { |
|
||||||
this.id = -1; |
|
||||||
setUpdateNeeded(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void deleteObject(Object rendererObject) { |
|
||||||
((AudioRenderer)rendererObject).deleteAudioData(this); |
|
||||||
} |
|
||||||
|
|
||||||
public float getCurrentVolume() { |
|
||||||
return currentVolume; |
|
||||||
} |
|
||||||
|
|
||||||
public void setCurrentVolume(float currentVolume) { |
|
||||||
this.currentVolume = currentVolume; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public NativeObject createDestructableClone() { |
|
||||||
return new AndroidAudioData(id); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public long getUniqueId() { |
|
||||||
return ((long)OBJTYPE_AUDIOBUFFER << 32) | ((long)id); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,53 @@ |
|||||||
|
package com.jme3.audio.ios; |
||||||
|
|
||||||
|
import com.jme3.audio.openal.AL; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.nio.FloatBuffer; |
||||||
|
import java.nio.IntBuffer; |
||||||
|
|
||||||
|
public final class IosAL implements AL { |
||||||
|
|
||||||
|
public IosAL() { |
||||||
|
} |
||||||
|
|
||||||
|
public native String alGetString(int parameter); |
||||||
|
|
||||||
|
public native int alGenSources(); |
||||||
|
|
||||||
|
public native int alGetError(); |
||||||
|
|
||||||
|
public native void alDeleteSources(int numSources, IntBuffer sources); |
||||||
|
|
||||||
|
public native void alGenBuffers(int numBuffers, IntBuffer buffers); |
||||||
|
|
||||||
|
public native void alDeleteBuffers(int numBuffers, IntBuffer buffers); |
||||||
|
|
||||||
|
public native void alSourceStop(int source); |
||||||
|
|
||||||
|
public native void alSourcei(int source, int param, int value); |
||||||
|
|
||||||
|
public native void alBufferData(int buffer, int format, ByteBuffer data, int size, int frequency); |
||||||
|
|
||||||
|
public native void alSourcePlay(int source); |
||||||
|
|
||||||
|
public native void alSourcePause(int source); |
||||||
|
|
||||||
|
public native void alSourcef(int source, int param, float value); |
||||||
|
|
||||||
|
public native void alSource3f(int source, int param, float value1, float value2, float value3); |
||||||
|
|
||||||
|
public native int alGetSourcei(int source, int param); |
||||||
|
|
||||||
|
public native void alSourceUnqueueBuffers(int source, int numBuffers, IntBuffer buffers); |
||||||
|
|
||||||
|
public native void alSourceQueueBuffers(int source, int numBuffers, IntBuffer buffers); |
||||||
|
|
||||||
|
public native void alListener(int param, FloatBuffer data); |
||||||
|
|
||||||
|
public native void alListenerf(int param, float value); |
||||||
|
|
||||||
|
public native void alListener3f(int param, float value1, float value2, float value3); |
||||||
|
|
||||||
|
public native void alSource3i(int source, int param, int value1, int value2, int value3); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
package com.jme3.audio.ios; |
||||||
|
|
||||||
|
import com.jme3.audio.openal.ALC; |
||||||
|
import java.nio.IntBuffer; |
||||||
|
|
||||||
|
public final class IosALC implements ALC { |
||||||
|
|
||||||
|
public IosALC() { |
||||||
|
} |
||||||
|
|
||||||
|
public native void createALC(); |
||||||
|
|
||||||
|
public native void destroyALC(); |
||||||
|
|
||||||
|
public native boolean isCreated(); |
||||||
|
|
||||||
|
public native String alcGetString(int parameter); |
||||||
|
|
||||||
|
public native boolean alcIsExtensionPresent(String extension); |
||||||
|
|
||||||
|
public native void alcGetInteger(int param, IntBuffer buffer, int size); |
||||||
|
|
||||||
|
public native void alcDevicePauseSOFT(); |
||||||
|
|
||||||
|
public native void alcDeviceResumeSOFT(); |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.jme3.audio.ios; |
||||||
|
|
||||||
|
import com.jme3.audio.openal.EFX; |
||||||
|
import java.nio.IntBuffer; |
||||||
|
|
||||||
|
public class IosEFX implements EFX { |
||||||
|
|
||||||
|
public IosEFX() { |
||||||
|
} |
||||||
|
|
||||||
|
public native void alGenAuxiliaryEffectSlots(int numSlots, IntBuffer buffers); |
||||||
|
|
||||||
|
public native void alGenEffects(int numEffects, IntBuffer buffers); |
||||||
|
|
||||||
|
public native void alEffecti(int effect, int param, int value); |
||||||
|
|
||||||
|
public native void alAuxiliaryEffectSloti(int effectSlot, int param, int value); |
||||||
|
|
||||||
|
public native void alDeleteEffects(int numEffects, IntBuffer buffers); |
||||||
|
|
||||||
|
public native void alDeleteAuxiliaryEffectSlots(int numEffectSlots, IntBuffer buffers); |
||||||
|
|
||||||
|
public native void alGenFilters(int numFilters, IntBuffer buffers); |
||||||
|
|
||||||
|
public native void alFilteri(int filter, int param, int value); |
||||||
|
|
||||||
|
public native void alFilterf(int filter, int param, float value); |
||||||
|
|
||||||
|
public native void alDeleteFilters(int numFilters, IntBuffer buffers); |
||||||
|
|
||||||
|
public native void alEffectf(int effect, int param, float value); |
||||||
|
} |
@ -1,20 +0,0 @@ |
|||||||
package com.jme3.audio.plugins; |
|
||||||
|
|
||||||
import com.jme3.asset.AssetInfo; |
|
||||||
import com.jme3.asset.AssetLoader; |
|
||||||
import com.jme3.audio.android.AndroidAudioData; |
|
||||||
import java.io.IOException; |
|
||||||
|
|
||||||
/** |
|
||||||
* <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 { |
|
||||||
AndroidAudioData result = new AndroidAudioData(); |
|
||||||
result.setAssetKey(assetInfo.getKey()); |
|
||||||
return result; |
|
||||||
} |
|
||||||
} |
|
File diff suppressed because it is too large
Load Diff
@ -1,576 +0,0 @@ |
|||||||
package com.jme3.renderer.ios; |
|
||||||
|
|
||||||
//import android.graphics.Bitmap;
|
|
||||||
//import android.opengl.ETC1;
|
|
||||||
//import android.opengl.ETC1Util.ETC1Texture;
|
|
||||||
//import android.opengl.JmeIosGLES;
|
|
||||||
//import android.opengl.GLUtils;
|
|
||||||
//import com.jme3.asset.AndroidImageInfo;
|
|
||||||
import com.jme3.renderer.ios.JmeIosGLES; |
|
||||||
import com.jme3.math.FastMath; |
|
||||||
import com.jme3.renderer.RendererException; |
|
||||||
import com.jme3.texture.Image; |
|
||||||
import com.jme3.texture.Image.Format; |
|
||||||
import com.jme3.util.BufferUtils; |
|
||||||
import java.nio.ByteBuffer; |
|
||||||
import java.util.logging.Level; |
|
||||||
import java.util.logging.Logger; |
|
||||||
|
|
||||||
public class TextureUtil { |
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(TextureUtil.class.getName()); |
|
||||||
//TODO Make this configurable through appSettings
|
|
||||||
public static boolean ENABLE_COMPRESSION = true; |
|
||||||
private static boolean NPOT = false; |
|
||||||
private static boolean ETC1support = false; |
|
||||||
private static boolean DXT1 = false; |
|
||||||
private static boolean PVRTC = false; |
|
||||||
private static boolean DEPTH24_STENCIL8 = false; |
|
||||||
private static boolean DEPTH_TEXTURE = false; |
|
||||||
private static boolean RGBA8 = false; |
|
||||||
|
|
||||||
// Same constant used by both GL_ARM_rgba8 and GL_OES_rgb8_rgba8.
|
|
||||||
private static final int GL_RGBA8 = 0x8058; |
|
||||||
|
|
||||||
private static final int GL_DXT1 = 0x83F0; |
|
||||||
private static final int GL_DXT1A = 0x83F1; |
|
||||||
|
|
||||||
private static final int GL_DEPTH_STENCIL_OES = 0x84F9; |
|
||||||
private static final int GL_UNSIGNED_INT_24_8_OES = 0x84FA; |
|
||||||
private static final int GL_DEPTH24_STENCIL8_OES = 0x88F0; |
|
||||||
|
|
||||||
public static void loadTextureFeatures(String extensionString) { |
|
||||||
ETC1support = extensionString.contains("GL_OES_compressed_ETC1_RGB8_texture"); |
|
||||||
DEPTH24_STENCIL8 = extensionString.contains("GL_OES_packed_depth_stencil"); |
|
||||||
NPOT = extensionString.contains("GL_IMG_texture_npot") |
|
||||||
|| extensionString.contains("GL_OES_texture_npot") |
|
||||||
|| extensionString.contains("GL_NV_texture_npot_2D_mipmap"); |
|
||||||
|
|
||||||
PVRTC = extensionString.contains("GL_IMG_texture_compression_pvrtc"); |
|
||||||
|
|
||||||
DXT1 = extensionString.contains("GL_EXT_texture_compression_dxt1"); |
|
||||||
DEPTH_TEXTURE = extensionString.contains("GL_OES_depth_texture"); |
|
||||||
|
|
||||||
RGBA8 = extensionString.contains("GL_ARM_rgba8") || |
|
||||||
extensionString.contains("GL_OES_rgb8_rgba8"); |
|
||||||
|
|
||||||
logger.log(Level.FINE, "Supports ETC1? {0}", ETC1support); |
|
||||||
logger.log(Level.FINE, "Supports DEPTH24_STENCIL8? {0}", DEPTH24_STENCIL8); |
|
||||||
logger.log(Level.FINE, "Supports NPOT? {0}", NPOT); |
|
||||||
logger.log(Level.FINE, "Supports PVRTC? {0}", PVRTC); |
|
||||||
logger.log(Level.FINE, "Supports DXT1? {0}", DXT1); |
|
||||||
logger.log(Level.FINE, "Supports DEPTH_TEXTURE? {0}", DEPTH_TEXTURE); |
|
||||||
logger.log(Level.FINE, "Supports RGBA8? {0}", RGBA8); |
|
||||||
} |
|
||||||
|
|
||||||
/* |
|
||||||
private static void buildMipmap(Bitmap bitmap, boolean compress) { |
|
||||||
int level = 0; |
|
||||||
int height = bitmap.getHeight(); |
|
||||||
int width = bitmap.getWidth(); |
|
||||||
|
|
||||||
logger.log(Level.FINEST, " - Generating mipmaps for bitmap using SOFTWARE"); |
|
||||||
|
|
||||||
JmeIosGLES.glPixelStorei(JmeIosGLES.GL_UNPACK_ALIGNMENT, 1); |
|
||||||
|
|
||||||
while (height >= 1 || width >= 1) { |
|
||||||
//First of all, generate the texture from our bitmap and set it to the according level
|
|
||||||
if (compress) { |
|
||||||
logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) with compression.", new Object[]{level, width, height}); |
|
||||||
uploadBitmapAsCompressed(JmeIosGLES.GL_TEXTURE_2D, level, bitmap, false, 0, 0); |
|
||||||
} else { |
|
||||||
logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) directly.", new Object[]{level, width, height}); |
|
||||||
GLUtils.texImage2D(JmeIosGLES.GL_TEXTURE_2D, level, bitmap, 0); |
|
||||||
} |
|
||||||
|
|
||||||
if (height == 1 || width == 1) { |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
//Increase the mipmap level
|
|
||||||
height /= 2; |
|
||||||
width /= 2; |
|
||||||
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true); |
|
||||||
|
|
||||||
// Recycle any bitmaps created as a result of scaling the bitmap.
|
|
||||||
// Do not recycle the original image (mipmap level 0)
|
|
||||||
if (level != 0) { |
|
||||||
bitmap.recycle(); |
|
||||||
} |
|
||||||
|
|
||||||
bitmap = bitmap2; |
|
||||||
|
|
||||||
level++; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static void uploadBitmapAsCompressed(int target, int level, Bitmap bitmap, boolean subTexture, int x, int y) { |
|
||||||
if (bitmap.hasAlpha()) { |
|
||||||
logger.log(Level.FINEST, " - Uploading bitmap directly. Cannot compress as alpha present."); |
|
||||||
if (subTexture) { |
|
||||||
GLUtils.texSubImage2D(target, level, x, y, bitmap); |
|
||||||
JmeIosGLES.checkGLError(); |
|
||||||
} else { |
|
||||||
GLUtils.texImage2D(target, level, bitmap, 0); |
|
||||||
JmeIosGLES.checkGLError(); |
|
||||||
} |
|
||||||
} else { |
|
||||||
// Convert to RGB565
|
|
||||||
int bytesPerPixel = 2; |
|
||||||
Bitmap rgb565 = bitmap.copy(Bitmap.Config.RGB_565, true); |
|
||||||
|
|
||||||
// Put texture data into ByteBuffer
|
|
||||||
ByteBuffer inputImage = BufferUtils.createByteBuffer(bitmap.getRowBytes() * bitmap.getHeight()); |
|
||||||
rgb565.copyPixelsToBuffer(inputImage); |
|
||||||
inputImage.position(0); |
|
||||||
|
|
||||||
// Delete the copied RGB565 image
|
|
||||||
rgb565.recycle(); |
|
||||||
|
|
||||||
// Encode the image into the output bytebuffer
|
|
||||||
int encodedImageSize = ETC1.getEncodedDataSize(bitmap.getWidth(), bitmap.getHeight()); |
|
||||||
ByteBuffer compressedImage = BufferUtils.createByteBuffer(encodedImageSize); |
|
||||||
ETC1.encodeImage(inputImage, bitmap.getWidth(), |
|
||||||
bitmap.getHeight(), |
|
||||||
bytesPerPixel, |
|
||||||
bytesPerPixel * bitmap.getWidth(), |
|
||||||
compressedImage); |
|
||||||
|
|
||||||
// Delete the input image buffer
|
|
||||||
BufferUtils.destroyDirectBuffer(inputImage); |
|
||||||
|
|
||||||
// Create an ETC1Texture from the compressed image data
|
|
||||||
ETC1Texture etc1tex = new ETC1Texture(bitmap.getWidth(), bitmap.getHeight(), compressedImage); |
|
||||||
|
|
||||||
// Upload the ETC1Texture
|
|
||||||
if (bytesPerPixel == 2) { |
|
||||||
int oldSize = (bitmap.getRowBytes() * bitmap.getHeight()); |
|
||||||
int newSize = compressedImage.capacity(); |
|
||||||
logger.log(Level.FINEST, " - Uploading compressed image to GL, oldSize = {0}, newSize = {1}, ratio = {2}", new Object[]{oldSize, newSize, (float) oldSize / newSize}); |
|
||||||
if (subTexture) { |
|
||||||
JmeIosGLES.glCompressedTexSubImage2D(target, |
|
||||||
level, |
|
||||||
x, y, |
|
||||||
bitmap.getWidth(), |
|
||||||
bitmap.getHeight(), |
|
||||||
ETC1.ETC1_RGB8_OES, |
|
||||||
etc1tex.getData().capacity(), |
|
||||||
etc1tex.getData()); |
|
||||||
|
|
||||||
JmeIosGLES.checkGLError(); |
|
||||||
} else { |
|
||||||
JmeIosGLES.glCompressedTexImage2D(target, |
|
||||||
level, |
|
||||||
ETC1.ETC1_RGB8_OES, |
|
||||||
bitmap.getWidth(), |
|
||||||
bitmap.getHeight(), |
|
||||||
0, |
|
||||||
etc1tex.getData().capacity(), |
|
||||||
etc1tex.getData()); |
|
||||||
|
|
||||||
JmeIosGLES.checkGLError(); |
|
||||||
} |
|
||||||
|
|
||||||
// ETC1Util.loadTexture(target, level, 0, JmeIosGLES.GL_RGB,
|
|
||||||
// JmeIosGLES.GL_UNSIGNED_SHORT_5_6_5, etc1Texture);
|
|
||||||
// } else if (bytesPerPixel == 3) {
|
|
||||||
// ETC1Util.loadTexture(target, level, 0, JmeIosGLES.GL_RGB,
|
|
||||||
// JmeIosGLES.GL_UNSIGNED_BYTE, etc1Texture);
|
|
||||||
} |
|
||||||
|
|
||||||
BufferUtils.destroyDirectBuffer(compressedImage); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* <code>uploadTextureBitmap</code> uploads a native android bitmap |
|
||||||
*/ |
|
||||||
/* |
|
||||||
public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips) { |
|
||||||
uploadTextureBitmap(target, bitmap, needMips, false, 0, 0); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* <code>uploadTextureBitmap</code> uploads a native android bitmap |
|
||||||
*/ |
|
||||||
/* |
|
||||||
public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips, boolean subTexture, int x, int y) { |
|
||||||
boolean recycleBitmap = false; |
|
||||||
//TODO, maybe this should raise an exception when NPOT is not supported
|
|
||||||
|
|
||||||
boolean willCompress = ENABLE_COMPRESSION && ETC1support && !bitmap.hasAlpha(); |
|
||||||
if (needMips && willCompress) { |
|
||||||
// Image is compressed and mipmaps are desired, generate them
|
|
||||||
// using software.
|
|
||||||
buildMipmap(bitmap, willCompress); |
|
||||||
} else { |
|
||||||
if (willCompress) { |
|
||||||
// Image is compressed but mipmaps are not desired, upload directly.
|
|
||||||
logger.log(Level.FINEST, " - Uploading compressed bitmap. Mipmaps are not generated."); |
|
||||||
uploadBitmapAsCompressed(target, 0, bitmap, subTexture, x, y); |
|
||||||
|
|
||||||
} else { |
|
||||||
// Image is not compressed, mipmaps may or may not be desired.
|
|
||||||
logger.log(Level.FINEST, " - Uploading bitmap directly.{0}", |
|
||||||
(needMips |
|
||||||
? " Mipmaps will be generated in HARDWARE" |
|
||||||
: " Mipmaps are not generated.")); |
|
||||||
if (subTexture) { |
|
||||||
System.err.println("x : " + x + " y :" + y + " , " + bitmap.getWidth() + "/" + bitmap.getHeight()); |
|
||||||
GLUtils.texSubImage2D(target, 0, x, y, bitmap); |
|
||||||
JmeIosGLES.checkGLError(); |
|
||||||
} else { |
|
||||||
GLUtils.texImage2D(target, 0, bitmap, 0); |
|
||||||
JmeIosGLES.checkGLError(); |
|
||||||
} |
|
||||||
|
|
||||||
if (needMips) { |
|
||||||
// No pregenerated mips available,
|
|
||||||
// generate from base level if required
|
|
||||||
JmeIosGLES.glGenerateMipmap(target); |
|
||||||
JmeIosGLES.checkGLError(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (recycleBitmap) { |
|
||||||
bitmap.recycle(); |
|
||||||
} |
|
||||||
} |
|
||||||
*/ |
|
||||||
|
|
||||||
public static void uploadTextureAny(Image img, int target, int index, boolean needMips) { |
|
||||||
/* |
|
||||||
if (img.getEfficentData() instanceof AndroidImageInfo) { |
|
||||||
logger.log(Level.FINEST, " === Uploading image {0}. Using BITMAP PATH === ", img); |
|
||||||
// If image was loaded from asset manager, use fast path
|
|
||||||
AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData(); |
|
||||||
uploadTextureBitmap(target, imageInfo.getBitmap(), needMips); |
|
||||||
} else { |
|
||||||
*/ |
|
||||||
logger.log(Level.FINEST, " === Uploading image {0}. Using BUFFER PATH === ", img); |
|
||||||
boolean wantGeneratedMips = needMips && !img.hasMipmaps(); |
|
||||||
if (wantGeneratedMips && img.getFormat().isCompressed()) { |
|
||||||
logger.log(Level.WARNING, "Generating mipmaps is only" |
|
||||||
+ " supported for Bitmap based or non-compressed images!"); |
|
||||||
} |
|
||||||
|
|
||||||
// Upload using slower path
|
|
||||||
logger.log(Level.FINEST, " - Uploading bitmap directly.{0}", |
|
||||||
(wantGeneratedMips |
|
||||||
? " Mipmaps will be generated in HARDWARE" |
|
||||||
: " Mipmaps are not generated.")); |
|
||||||
|
|
||||||
uploadTexture(img, target, index); |
|
||||||
|
|
||||||
// Image was uploaded using slower path, since it is not compressed,
|
|
||||||
// then compress it
|
|
||||||
if (wantGeneratedMips) { |
|
||||||
// No pregenerated mips available,
|
|
||||||
// generate from base level if required
|
|
||||||
JmeIosGLES.glGenerateMipmap(target); |
|
||||||
JmeIosGLES.checkGLError(); |
|
||||||
} |
|
||||||
//}
|
|
||||||
} |
|
||||||
|
|
||||||
private static void unsupportedFormat(Format fmt) { |
|
||||||
throw new UnsupportedOperationException("The image format '" + fmt + "' is unsupported by the video hardware."); |
|
||||||
} |
|
||||||
|
|
||||||
public static IosGLImageFormat getImageFormat(Format fmt) throws UnsupportedOperationException { |
|
||||||
IosGLImageFormat imageFormat = new IosGLImageFormat(); |
|
||||||
switch (fmt) { |
|
||||||
case Depth32: |
|
||||||
case Depth32F: |
|
||||||
throw new UnsupportedOperationException("The image format '" |
|
||||||
+ fmt + "' is not supported by OpenGL ES 2.0 specification."); |
|
||||||
case Alpha8: |
|
||||||
imageFormat.format = JmeIosGLES.GL_ALPHA; |
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE; |
|
||||||
if (RGBA8) { |
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8; |
|
||||||
} else { |
|
||||||
// Highest precision alpha supported by vanilla OGLES2
|
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGBA4; |
|
||||||
} |
|
||||||
break; |
|
||||||
case Luminance8: |
|
||||||
imageFormat.format = JmeIosGLES.GL_LUMINANCE; |
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE; |
|
||||||
if (RGBA8) { |
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8; |
|
||||||
} else { |
|
||||||
// Highest precision luminance supported by vanilla OGLES2
|
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565; |
|
||||||
} |
|
||||||
break; |
|
||||||
case Luminance8Alpha8: |
|
||||||
imageFormat.format = JmeIosGLES.GL_LUMINANCE_ALPHA; |
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE; |
|
||||||
if (RGBA8) { |
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8; |
|
||||||
} else { |
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGBA4; |
|
||||||
} |
|
||||||
break; |
|
||||||
case RGB565: |
|
||||||
imageFormat.format = JmeIosGLES.GL_RGB; |
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT_5_6_5; |
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565; |
|
||||||
break; |
|
||||||
case RGB5A1: |
|
||||||
imageFormat.format = JmeIosGLES.GL_RGBA; |
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT_5_5_5_1; |
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB5_A1; |
|
||||||
break; |
|
||||||
case RGB8: |
|
||||||
imageFormat.format = JmeIosGLES.GL_RGB; |
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE; |
|
||||||
if (RGBA8) { |
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8; |
|
||||||
} else { |
|
||||||
// Fallback: Use RGB565 if RGBA8 is not available.
|
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565; |
|
||||||
} |
|
||||||
break; |
|
||||||
case BGR8: |
|
||||||
imageFormat.format = JmeIosGLES.GL_RGB; |
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE; |
|
||||||
if (RGBA8) { |
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8; |
|
||||||
} else { |
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565; |
|
||||||
} |
|
||||||
break; |
|
||||||
case RGBA8: |
|
||||||
imageFormat.format = JmeIosGLES.GL_RGBA; |
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE; |
|
||||||
if (RGBA8) { |
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8; |
|
||||||
} else { |
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGBA4; |
|
||||||
} |
|
||||||
break; |
|
||||||
case Depth: |
|
||||||
case Depth16: |
|
||||||
if (!DEPTH_TEXTURE) { |
|
||||||
unsupportedFormat(fmt); |
|
||||||
} |
|
||||||
imageFormat.format = JmeIosGLES.GL_DEPTH_COMPONENT; |
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT; |
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_DEPTH_COMPONENT16; |
|
||||||
break; |
|
||||||
case Depth24: |
|
||||||
case Depth24Stencil8: |
|
||||||
if (!DEPTH_TEXTURE) { |
|
||||||
unsupportedFormat(fmt); |
|
||||||
} |
|
||||||
if (DEPTH24_STENCIL8) { |
|
||||||
// NEW: True Depth24 + Stencil8 format.
|
|
||||||
imageFormat.format = GL_DEPTH_STENCIL_OES; |
|
||||||
imageFormat.dataType = GL_UNSIGNED_INT_24_8_OES; |
|
||||||
imageFormat.renderBufferStorageFormat = GL_DEPTH24_STENCIL8_OES; |
|
||||||
} else { |
|
||||||
// Vanilla OGLES2, only Depth16 available.
|
|
||||||
imageFormat.format = JmeIosGLES.GL_DEPTH_COMPONENT; |
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT; |
|
||||||
imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_DEPTH_COMPONENT16; |
|
||||||
} |
|
||||||
break; |
|
||||||
case DXT1: |
|
||||||
if (!DXT1) { |
|
||||||
unsupportedFormat(fmt); |
|
||||||
} |
|
||||||
imageFormat.format = GL_DXT1; |
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE; |
|
||||||
imageFormat.compress = true; |
|
||||||
break; |
|
||||||
case DXT1A: |
|
||||||
if (!DXT1) { |
|
||||||
unsupportedFormat(fmt); |
|
||||||
} |
|
||||||
imageFormat.format = GL_DXT1A; |
|
||||||
imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE; |
|
||||||
imageFormat.compress = true; |
|
||||||
break; |
|
||||||
default: |
|
||||||
throw new UnsupportedOperationException("Unrecognized format: " + fmt); |
|
||||||
} |
|
||||||
return imageFormat; |
|
||||||
} |
|
||||||
|
|
||||||
public static class IosGLImageFormat { |
|
||||||
|
|
||||||
boolean compress = false; |
|
||||||
int format = -1; |
|
||||||
int renderBufferStorageFormat = -1; |
|
||||||
int dataType = -1; |
|
||||||
} |
|
||||||
|
|
||||||
private static void uploadTexture(Image img, |
|
||||||
int target, |
|
||||||
int index) { |
|
||||||
|
|
||||||
/* |
|
||||||
if (img.getEfficentData() instanceof AndroidImageInfo) { |
|
||||||
throw new RendererException("This image uses efficient data. " |
|
||||||
+ "Use uploadTextureBitmap instead."); |
|
||||||
} |
|
||||||
*/ |
|
||||||
|
|
||||||
// Otherwise upload image directly.
|
|
||||||
// Prefer to only use power of 2 textures here to avoid errors.
|
|
||||||
Image.Format fmt = img.getFormat(); |
|
||||||
ByteBuffer data; |
|
||||||
if (index >= 0 || img.getData() != null && img.getData().size() > 0) { |
|
||||||
data = img.getData(index); |
|
||||||
} else { |
|
||||||
data = null; |
|
||||||
} |
|
||||||
|
|
||||||
int width = img.getWidth(); |
|
||||||
int height = img.getHeight(); |
|
||||||
|
|
||||||
if (!NPOT && img.isNPOT()) { |
|
||||||
// Check if texture is POT
|
|
||||||
throw new RendererException("Non-power-of-2 textures " |
|
||||||
+ "are not supported by the video hardware " |
|
||||||
+ "and no scaling path available for image: " + img); |
|
||||||
} |
|
||||||
IosGLImageFormat imageFormat = getImageFormat(fmt); |
|
||||||
|
|
||||||
if (data != null) { |
|
||||||
JmeIosGLES.glPixelStorei(JmeIosGLES.GL_UNPACK_ALIGNMENT, 1); |
|
||||||
JmeIosGLES.checkGLError(); |
|
||||||
} |
|
||||||
|
|
||||||
int[] mipSizes = img.getMipMapSizes(); |
|
||||||
int pos = 0; |
|
||||||
if (mipSizes == null) { |
|
||||||
if (data != null) { |
|
||||||
mipSizes = new int[]{data.capacity()}; |
|
||||||
} else { |
|
||||||
mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8}; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
for (int i = 0; i < mipSizes.length; i++) { |
|
||||||
int mipWidth = Math.max(1, width >> i); |
|
||||||
int mipHeight = Math.max(1, height >> i); |
|
||||||
|
|
||||||
if (data != null) { |
|
||||||
data.position(pos); |
|
||||||
data.limit(pos + mipSizes[i]); |
|
||||||
} |
|
||||||
|
|
||||||
if (imageFormat.compress && data != null) { |
|
||||||
JmeIosGLES.glCompressedTexImage2D(target, |
|
||||||
i, |
|
||||||
imageFormat.format, |
|
||||||
mipWidth, |
|
||||||
mipHeight, |
|
||||||
0, |
|
||||||
data.remaining(), |
|
||||||
data); |
|
||||||
} else { |
|
||||||
JmeIosGLES.glTexImage2D(target, |
|
||||||
i, |
|
||||||
imageFormat.format, |
|
||||||
mipWidth, |
|
||||||
mipHeight, |
|
||||||
0, |
|
||||||
imageFormat.format, |
|
||||||
imageFormat.dataType, |
|
||||||
data); |
|
||||||
} |
|
||||||
JmeIosGLES.checkGLError(); |
|
||||||
|
|
||||||
pos += mipSizes[i]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Update the texture currently bound to target at with data from the given |
|
||||||
* Image at position x and y. The parameter index is used as the zoffset in |
|
||||||
* case a 3d texture or texture 2d array is being updated. |
|
||||||
* |
|
||||||
* @param image Image with the source data (this data will be put into the |
|
||||||
* texture) |
|
||||||
* @param target the target texture |
|
||||||
* @param index the mipmap level to update |
|
||||||
* @param x the x position where to put the image in the texture |
|
||||||
* @param y the y position where to put the image in the texture |
|
||||||
*/ |
|
||||||
public static void uploadSubTexture( |
|
||||||
Image img, |
|
||||||
int target, |
|
||||||
int index, |
|
||||||
int x, |
|
||||||
int y) { |
|
||||||
//TODO:
|
|
||||||
/* |
|
||||||
if (img.getEfficentData() instanceof AndroidImageInfo) { |
|
||||||
AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData(); |
|
||||||
uploadTextureBitmap(target, imageInfo.getBitmap(), true, true, x, y); |
|
||||||
return; |
|
||||||
} |
|
||||||
*/ |
|
||||||
|
|
||||||
// Otherwise upload image directly.
|
|
||||||
// Prefer to only use power of 2 textures here to avoid errors.
|
|
||||||
Image.Format fmt = img.getFormat(); |
|
||||||
ByteBuffer data; |
|
||||||
if (index >= 0 || img.getData() != null && img.getData().size() > 0) { |
|
||||||
data = img.getData(index); |
|
||||||
} else { |
|
||||||
data = null; |
|
||||||
} |
|
||||||
|
|
||||||
int width = img.getWidth(); |
|
||||||
int height = img.getHeight(); |
|
||||||
|
|
||||||
if (!NPOT && img.isNPOT()) { |
|
||||||
// Check if texture is POT
|
|
||||||
throw new RendererException("Non-power-of-2 textures " |
|
||||||
+ "are not supported by the video hardware " |
|
||||||
+ "and no scaling path available for image: " + img); |
|
||||||
} |
|
||||||
IosGLImageFormat imageFormat = getImageFormat(fmt); |
|
||||||
|
|
||||||
if (data != null) { |
|
||||||
JmeIosGLES.glPixelStorei(JmeIosGLES.GL_UNPACK_ALIGNMENT, 1); |
|
||||||
JmeIosGLES.checkGLError(); |
|
||||||
} |
|
||||||
|
|
||||||
int[] mipSizes = img.getMipMapSizes(); |
|
||||||
int pos = 0; |
|
||||||
if (mipSizes == null) { |
|
||||||
if (data != null) { |
|
||||||
mipSizes = new int[]{data.capacity()}; |
|
||||||
} else { |
|
||||||
mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8}; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
for (int i = 0; i < mipSizes.length; i++) { |
|
||||||
int mipWidth = Math.max(1, width >> i); |
|
||||||
int mipHeight = Math.max(1, height >> i); |
|
||||||
|
|
||||||
if (data != null) { |
|
||||||
data.position(pos); |
|
||||||
data.limit(pos + mipSizes[i]); |
|
||||||
} |
|
||||||
|
|
||||||
if (imageFormat.compress && data != null) { |
|
||||||
JmeIosGLES.glCompressedTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, data.remaining(), data); |
|
||||||
JmeIosGLES.checkGLError(); |
|
||||||
} else { |
|
||||||
JmeIosGLES.glTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, imageFormat.dataType, data); |
|
||||||
JmeIosGLES.checkGLError(); |
|
||||||
} |
|
||||||
|
|
||||||
pos += mipSizes[i]; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue