Adding support for defining external (or internal in Android's case) sensors. This is still a work in progress. Major task yet to complete is defining the coordinate system to return the sensor data. 3 sensor types are defined: Magnetic, Accelerometer, Orientation. Right now the sensor data is returned in device coordinates for Magnetic and Acceleration, and World (Earth) coordinates for Orientation. Sensors use the Input Manager to define triggers and listeners like all other input types. Only Android has an implementation for SensorInput at this time. See forum post http://jmonkeyengine.org/groups/android/forum/topic/creating-engine-support-for-android-sensor-input/ for details of the operation and current status.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9610 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
pot..om 12 years ago
parent 1fbc0cc406
commit bb631ab13a
  1. 58
      engine/src/android/com/jme3/app/AndroidHarness.java
  2. 584
      engine/src/android/com/jme3/input/android/AndroidSensorInput.java
  3. 25
      engine/src/android/com/jme3/system/android/OGLESContext.java
  4. 102
      engine/src/core/com/jme3/app/Application.java
  5. 59
      engine/src/core/com/jme3/input/InputManager.java
  6. 53
      engine/src/core/com/jme3/input/RawInputListener.java
  7. 146
      engine/src/core/com/jme3/input/SensorInput.java
  8. 58
      engine/src/core/com/jme3/input/controls/MotionSensorListener.java
  9. 83
      engine/src/core/com/jme3/input/controls/SensorTrigger.java
  10. 118
      engine/src/core/com/jme3/input/event/MotionSensorEvent.java
  11. 20
      engine/src/core/com/jme3/system/JmeContext.java
  12. 11
      engine/src/core/com/jme3/system/NullContext.java
  13. 49
      engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java
  14. 17
      engine/src/lwjgl/com/jme3/system/lwjgl/LwjglAbstractDisplay.java
  15. 17
      engine/src/lwjgl/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java
  16. 3
      engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java
  17. 33
      engine/src/test/jme3test/gui/TestBitmapFont.java
  18. 6
      engine/src/test/jme3test/gui/TestSoftwareMouse.java

@ -15,7 +15,9 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.jme3.audio.AudioRenderer;
import com.jme3.audio.android.AndroidAudioRenderer;
import com.jme3.input.SensorInput;
import com.jme3.input.TouchInput;
import com.jme3.input.android.AndroidSensorInput;
import com.jme3.input.controls.TouchListener;
import com.jme3.input.controls.TouchTrigger;
import com.jme3.input.event.TouchEvent;
@ -39,64 +41,64 @@ import java.util.logging.Logger;
public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener, SystemListener {
protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName());
/**
* The application class to start
*/
protected String appClass = "jme3test.android.Test";
/**
* The jme3 application object
*/
protected Application app = null;
/**
* ConfigType.FASTEST is RGB565, GLSurfaceView default ConfigType.BEST is
* RGBA8888 or better if supported by the hardware
*/
protected ConfigType eglConfigType = ConfigType.FASTEST;
/**
* If true all valid and not valid egl configs are logged
*/
protected boolean eglConfigVerboseLogging = false;
/**
* If true MouseEvents are generated from TouchEvents
*/
protected boolean mouseEventsEnabled = true;
/**
* Flip X axis
*/
protected boolean mouseEventsInvertX = true;
/**
* Flip Y axis
*/
protected boolean mouseEventsInvertY = true;
/**
* if true finish this activity when the jme app is stopped
*/
protected boolean finishOnAppStop = true;
/**
* set to false if you don't want the harness to handle the exit hook
*/
protected boolean handleExitHook = true;
/**
* Title of the exit dialog, default is "Do you want to exit?"
*/
protected String exitDialogTitle = "Do you want to exit?";
/**
* Message of the exit dialog, default is "Use your home key to bring this
* app into the background or exit to terminate it."
*/
protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it.";
/**
* Set the screen window mode. If screenFullSize is true, then the
* notification bar and title bar are removed and the screen covers the
@ -105,20 +107,20 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
* false, then the title bar is also displayed under the notification bar.
*/
protected boolean screenFullScreen = true;
/**
* if screenShowTitle is true while screenFullScreen is false, then the
* title bar is also displayed under the notification bar
*/
protected boolean screenShowTitle = true;
/**
* Splash Screen picture Resource ID. If a Splash Screen is desired, set
* splashPicID to the value of the Resource ID (i.e. R.drawable.picname). If
* splashPicID = 0, then no splash screen will be displayed.
*/
protected int splashPicID = 0;
/**
* Set the screen orientation, default is SENSOR
* ActivityInfo.SCREEN_ORIENTATION_* constants package
@ -142,7 +144,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
private class DataObject {
protected Application app = null;
}
@Override
public Object onRetainNonConfigurationInstance() {
logger.log(Level.INFO, "onRetainNonConfigurationInstance called");
@ -156,7 +158,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
logger.log(Level.INFO, "inConfigChange: {0}", inConfigChange);
return data;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -257,6 +259,16 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
renderer.resumeAll();
}
}
//resume the sensors
if (app.getInputManager() != null) {
SensorInput sensorInput = app.getInputManager().getSensorInput();
if (sensorInput != null) {
logger.log(Level.INFO, "resume: {0}", sensorInput.getClass().getSimpleName());
if (sensorInput instanceof AndroidSensorInput) {
((AndroidSensorInput)sensorInput).resumeSensors();
}
}
}
}
isGLThreadPaused = false;
@ -280,6 +292,16 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
renderer.pauseAll();
}
}
//pause the sensors
if (app.getInputManager() != null) {
SensorInput sensorInput = app.getInputManager().getSensorInput();
if (sensorInput != null) {
logger.log(Level.INFO, "pause: {0}", sensorInput.getClass().getSimpleName());
if (sensorInput instanceof AndroidSensorInput) {
((AndroidSensorInput)sensorInput).resumeSensors();
}
}
}
}
isGLThreadPaused = true;
logger.info("onPause");
@ -406,7 +428,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
((ViewGroup)view.getParent()).removeView(view);
}
frameLayout.addView(view);
if (splashImageView.getParent() != null) {
((ViewGroup)splashImageView.getParent()).removeView(splashImageView);
}

@ -0,0 +1,584 @@
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.input.android;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
import com.jme3.input.RawInputListener;
import com.jme3.input.SensorInput;
import com.jme3.input.event.MotionSensorEvent;
import com.jme3.math.Vector3f;
import com.jme3.system.android.JmeAndroidSystem;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Android specific implementation of SensorInput.
*
* @author iwgeric
*/
public class AndroidSensorInput implements SensorInput, SensorEventListener {
private final static Logger logger = Logger.getLogger(AndroidSensorInput.class.getName());
private SensorManager sensorManager = null;
private RawInputListener listener = null;
private Map<Integer, SensorData> sensors = new HashMap<Integer, SensorData>();
private boolean initialized = false;
private WindowManager window;
private Display disp;
private final float[] curAccValues = new float[3];
private final float[] curMagValues = new float[3];
private final float[] curInclination = new float[9];
private final float[] curRotation = new float[9];
private final float[] rotatedRotation = new float[9];
private final ArrayList<MotionSensorEvent> eventQueue = new ArrayList<MotionSensorEvent>();
/**
* Internal class to enclose data for each sensor.
*/
private class SensorData {
int androidSensorType = -1;
int androidSensorSpeed = SensorManager.SENSOR_DELAY_UI;
Sensor sensor = null;
Vector3f lastValues = new Vector3f();
float minChangePercent = 0f;
boolean enabled = false;
boolean paused = false;
public SensorData(int androidSensorType, Sensor sensor) {
this.androidSensorType = androidSensorType;
this.sensor = sensor;
}
}
/**
* Pauses the active sensors to save battery. Mostly used internally so that
* the sensors can be deactivated while the game Activity is
* in the background to save battery life
*/
public void pauseSensors() {
for (Entry<Integer, SensorData> entry : sensors.entrySet()) {
SensorData sensorData = entry.getValue();
if (sensorData.sensor != null) {
unRegisterListener(entry.getKey());
sensorData.paused = true;
}
}
}
/**
* Resumes paused sensors. Mostly used internally so that
* the sensors can be reactivated when the game Activity is
* placed back onto the forefront.
*/
public void resumeSensors() {
for (Entry<Integer, SensorData> entry : sensors.entrySet()) {
SensorData sensorData = entry.getValue();
if (sensorData.sensor != null && sensorData.paused) {
if (registerListener(entry.getKey())) {
sensorData.paused = false;
}
}
}
}
/**
* Used internally by the context to reset the Sensor Manager on device rotations.
* Necessary because a new Activity is created on a device rotation, so the
* Sensor Manager needs to be reset with the new Activity.
*/
public void resetSensorManager() {
initSensorManager();
}
private void initSensorManager() {
window = JmeAndroidSystem.getActivity().getWindowManager();
disp = window.getDefaultDisplay();
sensorManager = (SensorManager) JmeAndroidSystem.getActivity().getSystemService(Context.SENSOR_SERVICE);
initSensor(SensorInput.SENSOR_TYPE_MAGNETIC_FIELD);
initSensor(SensorInput.SENSOR_TYPE_ACCELEROMETER);
initSensor(SensorInput.SENSOR_TYPE_ORIENTATION);
}
private boolean initSensor(int sensorType) {
boolean result = false;
boolean previouslyActive = false;
SensorData sensorData = sensors.get((Integer)sensorType);
if (sensorData != null) {
if (sensorData.enabled) {
previouslyActive = true;
}
unRegisterListener(sensorType);
} else {
sensorData = new SensorData(sensorType, null);
sensors.put(sensorType, sensorData);
}
switch (sensorType) {
case SensorInput.SENSOR_TYPE_MAGNETIC_FIELD:
sensorData.androidSensorType = Sensor.TYPE_MAGNETIC_FIELD;
sensorData.sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
break;
case SensorInput.SENSOR_TYPE_ACCELEROMETER:
sensorData.androidSensorType = Sensor.TYPE_ACCELEROMETER;
sensorData.sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
break;
case SensorInput.SENSOR_TYPE_ORIENTATION:
sensorData.androidSensorType = Sensor.TYPE_ORIENTATION;
//Orientation is not a sensor anymore but rather a call to SensorMangaer
// to get the current orientation based on the Magnetic and Accelerometer sensor data
sensorData.sensor = null;
break;
default:
throw new IllegalArgumentException("Invalid Sensor Type.");
}
if (sensorData.sensor != null || sensorType == SensorInput.SENSOR_TYPE_ORIENTATION) {
logger.log(Level.INFO, "Sensor Type {0} found.", sensorType);
if (previouslyActive) {
logger.log(Level.INFO, "Reactivating Sensor Type {0}.", sensorType);
registerListener(sensorType);
}
result = true;
}
return result;
}
private boolean registerListener(int sensorType) {
SensorData sensorData = sensors.get((Integer)sensorType);
if (sensorData != null) {
if (sensorData.enabled) {
logger.log(Level.INFO, "Sensor Already Active: SensorType: {0}, active: {1}",
new Object[]{sensorType, sensorData.enabled});
return true;
}
if (sensorData.sensor != null) {
if (sensorManager.registerListener(this, sensorData.sensor, sensorData.androidSensorSpeed)) {
sensorData.enabled = true;
logger.log(Level.INFO, "SensorType: {0}, active: {1}",
new Object[]{sensorType, sensorData.enabled});
logger.log(Level.INFO, "Sensor Type {0} activated.", sensorType);
return true;
} else {
sensorData.enabled = false;
logger.log(Level.INFO, "Sensor Type {0} activation failed.", sensorType);
}
} else if (sensorType == SensorInput.SENSOR_TYPE_ORIENTATION) {
logger.log(Level.INFO, "Sensor is Orientation");
if (registerListener(SensorInput.SENSOR_TYPE_MAGNETIC_FIELD) && registerListener(SensorInput.SENSOR_TYPE_ACCELEROMETER)) {
sensorData.enabled = true;
logger.log(Level.INFO, "Magnetic and Acceleration Sensors Registered and Orientation Sensor being simulated.");
return true;
}
}
sensorData.lastValues = null;
}
return false;
}
private void unRegisterListener(int sensorType) {
SensorData sensorData = sensors.get((Integer)sensorType);
if (sensorData != null) {
if (sensorData.sensor != null) {
sensorManager.unregisterListener(this, sensorData.sensor);
} else if (sensorType == SensorInput.SENSOR_TYPE_ORIENTATION) {
logger.log(Level.INFO, "Mangetic and Acceleration Sensors are being deactivated with Orientation Sensor.");
unRegisterListener(SensorInput.SENSOR_TYPE_MAGNETIC_FIELD);
unRegisterListener(SensorInput.SENSOR_TYPE_ACCELEROMETER);
}
sensorData.enabled = false;
logger.log(Level.INFO, "SensorType: {0}, active: {1}",
new Object[]{sensorType, sensorData.enabled});
logger.log(Level.INFO, "Sensor Type {0} deactivated.", sensorType);
}
}
/*
* Android remapCoordinateSystem from the Android docs
* remapCoordinateSystem(float[] inR, int X, int Y, float[] outR)
*
* @param inR the rotation matrix to be transformed. Usually it is the matrix
* returned by getRotationMatrix(float[], float[], float[], float[]).
*
* @param outR the transformed rotation matrix. inR and outR can be the same
* array, but it is not recommended for performance reason.
*
* X defines on which world axis and direction the X axis of the device is mapped.
* Y defines on which world axis and direction the Y axis of the device is mapped.
*
* @return True if successful
*/
private boolean remapCoordinates(float[] inR, float[] outR) {
int xDir = SensorManager.AXIS_X;
int yDir = SensorManager.AXIS_Y;
// logger.log(Level.INFO, "Screen Rotation: {0}", getScreenRotation());
if (getScreenRotation() == Surface.ROTATION_0) {
xDir = SensorManager.AXIS_X;
yDir = SensorManager.AXIS_Y;
}
if (getScreenRotation() == Surface.ROTATION_90) {
xDir = SensorManager.AXIS_MINUS_Y;
yDir = SensorManager.AXIS_MINUS_X;
}
if (getScreenRotation() == Surface.ROTATION_180) {
xDir = SensorManager.AXIS_MINUS_X;
yDir = SensorManager.AXIS_MINUS_Y;
}
if (getScreenRotation() == Surface.ROTATION_270) {
xDir = SensorManager.AXIS_Y;
yDir = SensorManager.AXIS_MINUS_X;
}
return SensorManager.remapCoordinateSystem(inR, xDir, yDir, outR);
}
/**
* Returns the current device rotation.
* Surface.ROTATION_0 = device in natural default rotation
* Surface.ROTATION_90 = device in rotated 90deg counterclockwise
* Surface.ROTATION_180 = device in rotated 180deg counterclockwise
* Surface.ROTATION_270 = device in rotated 270deg counterclockwise
*
* When the Manifest locks the orientation, this value will not change during
* gametime, but if the orientation of the screen is based off the sensor,
* this value will change as the device is rotated.
* @return Current device rotation amount
*/
private int getScreenRotation() {
return disp.getRotation();
}
private Integer getAndroidSensorSpeed(int sensorInputSpeed) {
Integer androidSpeed = null;
switch (sensorInputSpeed) {
case SensorInput.SENSOR_SPEED_SLOW:
androidSpeed = SensorManager.SENSOR_DELAY_UI;
break;
case SensorInput.SENSOR_SPEED_MEDIUM:
androidSpeed = SensorManager.SENSOR_DELAY_NORMAL;
break;
case SensorInput.SENSOR_SPEED_FAST:
androidSpeed = SensorManager.SENSOR_DELAY_GAME;
break;
default:
throw new IllegalArgumentException("Invalid Sensor Speed.");
}
return androidSpeed;
}
/**
* Calculates the device orientation based off the data recieved from the
* Acceleration Sensor and Mangetic Field sensor
* Values are returned relative to the Earth.
*
* From the Android Doc
*
* Computes the device's orientation based on the rotation matrix. When it returns, the array values is filled with the result:
* values[0]: azimuth, rotation around the Z axis.
* values[1]: pitch, rotation around the X axis.
* values[2]: roll, rotation around the Y axis.
*
* The reference coordinate-system used is different from the world
* coordinate-system defined for the rotation matrix:
* X is defined as the vector product Y.Z (It is tangential to the ground at the device's current location and roughly points West).
* Y is tangential to the ground at the device's current location and points towards the magnetic North Pole.
* Z points towards the center of the Earth and is perpendicular to the ground.
*
* @return
*/
private boolean updateOrientation() {
SensorData sensorData;
sensorData = sensors.get((Integer)SensorInput.SENSOR_TYPE_MAGNETIC_FIELD);
if (sensorData == null || !sensorData.enabled) {
return false;
}
sensorData = sensors.get((Integer)SensorInput.SENSOR_TYPE_ACCELEROMETER);
if (sensorData == null || !sensorData.enabled) {
return false;
}
sensorData = sensors.get((Integer)SensorInput.SENSOR_TYPE_ORIENTATION);
if (sensorData != null && sensorData.enabled) {
// create new copies so they don't get updated during the getRotationMatrix call
final float[] accValues = new float[3];
final float[] magValues = new float[3];
synchronized(curAccValues) {
accValues[0] = curAccValues[0];
accValues[1] = curAccValues[1];
accValues[2] = curAccValues[2];
}
synchronized(curMagValues) {
magValues[0] = curMagValues[0];
magValues[1] = curMagValues[1];
magValues[2] = curMagValues[2];
}
if (SensorManager.getRotationMatrix(curRotation, curInclination, accValues, magValues)) {
final float [] orientValues = new float[3];
if (remapCoordinates(curRotation, rotatedRotation)) {
SensorManager.getOrientation(rotatedRotation, orientValues);
// logger.log(Level.INFO, "Orientation Values: {0}, {1}, {2}",
// new Object[]{orientValues[0], orientValues[1], orientValues[2]});
updateEventQueue(SensorInput.SENSOR_TYPE_ORIENTATION,
orientValues[0], orientValues[1], orientValues[2], System.nanoTime());
return true;
} else {
//logger.log(Level.INFO, "remapCoordinateSystem failed");
}
} else {
//logger.log(Level.INFO, "getRotationMatrix returned false");
}
} else {
if (!sensorData.enabled) {
//logger.log(Level.INFO, "Orientation is not active");
}
}
return false;
}
private void updateEventQueue(int sensorType, float x, float y, float z, long timestamp) {
// logger.log(Level.INFO, "updateEventQueue for {0}: values: {1}, {2}, {3}",
// new Object[]{sensorType, x, y, z});
float lastX, lastY, lastZ;
float dX, dY, dZ;
SensorData sensorData = sensors.get((Integer)sensorType);
if (sensorData != null) {
// if lastValues is null, then this is the first scan after a registerListener
// so set lastValues to the current values so dX,dY,dZ are zero this pass
if (sensorData.lastValues == null) {
sensorData.lastValues = new Vector3f(x, y, z);
}
lastX = sensorData.lastValues.x;
lastY = sensorData.lastValues.y;
lastZ = sensorData.lastValues.z;
dX = x - lastX;
dY = y - lastY;
dZ = z - lastZ;
if (dX != 0 && dY != 0 && dZ != 0) {
MotionSensorEvent motionEvent = new MotionSensorEvent(sensorType, x, y, z, dX, dY, dZ);
motionEvent.setTime(timestamp);
sensorData.lastValues.x = x;
sensorData.lastValues.y = y;
sensorData.lastValues.z = z;
synchronized (eventQueue){
eventQueue.add(motionEvent);
}
} else {
//logger.log(Level.INFO, "No change in Sensor Data for: {0}", sensorType);
}
} else {
//logger.log(Level.INFO, "Sensor Data is null for: {0}", sensorType);
}
}
// Start of methods from SensorInput
public boolean isEnabled(int sensorType) {
logger.log(Level.INFO, "Checking isEnabled for type: {0}", sensorType);
SensorData sensorData = sensors.get((Integer)sensorType);
if (sensorData == null) {
// logger.log(Level.INFO, "sensor data is null, sensors size is: {0}", sensors.size());
return false;
}
return sensors.get((Integer)sensorType).enabled;
}
public void setEnable(boolean enable) {
for (Integer sensorType: sensors.keySet()) {
setEnable(sensorType, enable);
}
}
public void setEnable(int sensorType, boolean enable) {
logger.log(Level.INFO, "Setting Sensor {0} Enable to {1}",
new Object[]{sensorType, enable});
if (enable) {
// registerListener(sensorType, true);
registerListener(sensorType);
} else {
unRegisterListener(sensorType);
}
}
public void setSensorFrequency(int sensorType, int updateSpeed) {
SensorData sensorData = sensors.get((Integer)sensorType);
if (sensorData == null || sensorData.enabled) {
throw new IllegalArgumentException("Sensor Type Not Configured or is already active.");
}
sensorData.androidSensorSpeed = getAndroidSensorSpeed(updateSpeed);
}
public Set<Integer> getSensorTypes() {
return Collections.unmodifiableSet(sensors.keySet());
}
public void setSensorMinChange(int sensorType, float minChangePercent) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void initialize() {
logger.log(Level.INFO, "Doing Initialize.");
initSensorManager();
initialized = true;
}
public void update() {
updateOrientation();
synchronized (eventQueue){
// flush events to listener
for (int i = 0; i < eventQueue.size(); i++){
listener.onMotionSensorEvent(eventQueue.get(i));
}
eventQueue.clear();
}
}
public void destroy() {
for (Integer i: sensors.keySet()) {
unRegisterListener(i);
}
logger.log(Level.INFO, "Doing Destroy");
if (sensorManager != null) {
sensorManager.unregisterListener(this);
}
sensors.clear();
eventQueue.clear();
}
public boolean isInitialized() {
return initialized;
}
public void setInputListener(RawInputListener listener) {
this.listener = listener;
}
public long getInputTimeNanos() {
return System.nanoTime();
}
// End of methods from SensorInput
// Start of methods from SensorEventListener
public void onSensorChanged(SensorEvent se) {
// logger.log(Level.INFO, "onSensorChanged for {0}: values: {1}, {2}, {3}",
// new Object[]{se.sensor.getType(), se.values[0], se.values[1], se.values[2]});
SensorData sensorData;
int sensorType;
for (Entry<Integer, SensorData> entry : sensors.entrySet()) {
// if (entry.getValue().sensor == null) {
// logger.log(Level.INFO, "Sensor is null for SensorType: {0}", entry.getKey());
// }
if (entry.getValue().sensor != null && entry.getValue().sensor.equals(se.sensor)) {
sensorType = entry.getKey();
sensorData = entry.getValue();
updateEventQueue(sensorType, se.values[0], se.values[1], se.values[2], se.timestamp);
if (sensorType == SensorInput.SENSOR_TYPE_MAGNETIC_FIELD) {
synchronized(curMagValues) {
curMagValues[0] = se.values[0];
curMagValues[1] = se.values[1];
curMagValues[2] = se.values[2];
}
}
if (sensorType == SensorInput.SENSOR_TYPE_ACCELEROMETER) {
synchronized(curAccValues) {
curAccValues[0] = se.values[0];
curAccValues[1] = se.values[1];
curAccValues[2] = se.values[2];
}
}
break;
}
}
}
public void onAccuracyChanged(Sensor sensor, int i) {
logger.log(Level.INFO, "onAccuracyChanged for {0}: accuracy: {1}",
new Object[]{sensor.toString(), i});
}
// End of methods from SensorEventListener
}

@ -13,8 +13,8 @@
* 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
* * 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
@ -32,7 +32,6 @@
package com.jme3.system.android;
import com.jme3.renderer.android.AndroidGLSurfaceView;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.opengl.GLSurfaceView;
@ -44,6 +43,7 @@ import android.widget.EditText;
import android.widget.FrameLayout;
import com.jme3.input.*;
import com.jme3.input.android.AndroidInput;
import com.jme3.input.android.AndroidSensorInput;
import com.jme3.input.controls.SoftTextDialogInputListener;
import com.jme3.input.dummy.DummyKeyInput;
import com.jme3.input.dummy.DummyMouseInput;
@ -75,6 +75,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
protected SystemListener listener;
protected boolean autoFlush = true;
protected AndroidInput androidInput;
protected AndroidSensorInput androidSensorInput;
protected AndroidGLSurfaceView view;
protected int minFrameDuration = 0; // No FPS cap
/**
@ -92,15 +93,15 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
}
/**
* <code>createView</code> creates the GLSurfaceView that the
* <code>createView</code> creates the GLSurfaceView that the
* renderer will draw to.
* <p>
* The result GLSurfaceView will receive input events and forward
* them to the Application. Any rendering will be done into
* the GLSurfaceView. Only one GLSurfaceView can be created at this time.
* The given configType specifies how to determine the display configuration.
*
*
*
*
* @param configType ConfigType.FASTEST (Default) | ConfigType.LEGACY |
* ConfigType.BEST
* @param eglConfigVerboseLogging if true show all found configs
@ -114,6 +115,13 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
} else {
androidInput.setView(view);
}
if (androidSensorInput == null) {
logger.log(Level.INFO, "Creating New SensorInput");
androidSensorInput = new AndroidSensorInput();
} else {
logger.log(Level.INFO, "Resetting SensorInput");
androidSensorInput.resetSensorManager();
}
if (configType == ConfigType.LEGACY) {
// Hardcoded egl setup
clientOpenGLESVersion = 2;
@ -277,6 +285,11 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
return androidInput;
}
@Override
public SensorInput getSensorInput() {
return androidSensorInput;
}
@Override
public Timer getTimer() {
return timer;

@ -62,14 +62,14 @@ import java.util.logging.Logger;
*
* jME3 applications should extend this class and call start() to begin the
* application.
*
*
*/
public class Application implements SystemListener {
private static final Logger logger = Logger.getLogger(Application.class.getName());
protected AssetManager assetManager;
protected AudioRenderer audioRenderer;
protected Renderer renderer;
protected RenderManager renderManager;
@ -90,6 +90,7 @@ public class Application implements SystemListener {
protected KeyInput keyInput;
protected JoyInput joyInput;
protected TouchInput touchInput;
protected SensorInput sensorInput;
protected InputManager inputManager;
protected AppStateManager stateManager;
@ -104,10 +105,10 @@ public class Application implements SystemListener {
/**
* Returns true if pause on lost focus is enabled, false otherwise.
*
*
* @return true if pause on lost focus is enabled
*
* @see #setPauseOnLostFocus(boolean)
*
* @see #setPauseOnLostFocus(boolean)
*/
public boolean isPauseOnLostFocus() {
return pauseOnFocus;
@ -117,13 +118,13 @@ public class Application implements SystemListener {
* Enable or disable pause on lost focus.
* <p>
* By default, pause on lost focus is enabled.
* If enabled, the application will stop updating
* when it loses focus or becomes inactive (e.g. alt-tab).
* If enabled, the application will stop updating
* when it loses focus or becomes inactive (e.g. alt-tab).
* For online or real-time applications, this might not be preferable,
* so this feature should be set to disabled. For other applications,
* it is best to keep it on so that CPU usage is not used when
* not necessary.
*
* not necessary.
*
* @param pauseOnLostFocus True to enable pause on lost focus, false
* otherwise.
*/
@ -199,30 +200,30 @@ public class Application implements SystemListener {
*/
public void setTimer(Timer timer){
this.timer = timer;
if (timer != null) {
timer.reset();
}
if (renderManager != null) {
renderManager.setTimer(timer);
}
}
public Timer getTimer(){
return timer;
}
}
private void initDisplay(){
// aquire important objects
// from the context
settings = context.getSettings();
// Only reset the timer if a user has not already provided one
// Only reset the timer if a user has not already provided one
if (timer == null) {
timer = context.getTimer();
}
renderer = context.getRenderer();
}
@ -274,7 +275,7 @@ public class Application implements SystemListener {
keyInput = context.getKeyInput();
if (keyInput != null)
keyInput.initialize();
touchInput = context.getTouchInput();
if (touchInput != null)
touchInput.initialize();
@ -285,12 +286,16 @@ public class Application implements SystemListener {
joyInput.initialize();
}
inputManager = new InputManager(mouseInput, keyInput, joyInput, touchInput);
sensorInput = context.getSensorInput();
if (sensorInput != null)
sensorInput.initialize();
inputManager = new InputManager(mouseInput, keyInput, joyInput, touchInput, sensorInput);
}
private void initStateManager(){
stateManager = new AppStateManager(this);
// Always register a ResetStatsState to make sure
// that the stats are cleared every frame
stateManager.attach(new ResetStatsState());
@ -361,15 +366,15 @@ public class Application implements SystemListener {
/**
* Starts the application in {@link Type#Display display} mode.
*
* @see #start(com.jme3.system.JmeContext.Type)
*
* @see #start(com.jme3.system.JmeContext.Type)
*/
public void start(){
start(JmeContext.Type.Display);
}
/**
* Starts the application.
* Starts the application.
* Creating a rendering context and executing
* the main loop in a separate thread.
*/
@ -382,7 +387,7 @@ public class Application implements SystemListener {
if (settings == null){
settings = new AppSettings(true);
}
logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
context = JmeSystem.newContext(settings, contextType);
context.setSystemListener(this);
@ -392,15 +397,15 @@ public class Application implements SystemListener {
/**
* Initializes the application's canvas for use.
* <p>
* After calling this method, cast the {@link #getContext() context} to
* After calling this method, cast the {@link #getContext() context} to
* {@link JmeCanvasContext},
* then acquire the canvas with {@link JmeCanvasContext#getCanvas() }
* and attach it to an AWT/Swing Frame.
* The rendering thread will start when the canvas becomes visible on
* screen, however if you wish to start the context immediately you
* may call {@link #startCanvas() } to force the rendering thread
* to start.
*
* to start.
*
* @see JmeCanvasContext
* @see Type#Canvas
*/
@ -423,8 +428,8 @@ public class Application implements SystemListener {
* Starts the rendering thread after createCanvas() has been called.
* <p>
* Same as calling startCanvas(false)
*
* @see #startCanvas(boolean)
*
* @see #startCanvas(boolean)
*/
public void startCanvas(){
startCanvas(false);
@ -435,8 +440,8 @@ public class Application implements SystemListener {
* <p>
* Calling this method is optional, the canvas will start automatically
* when it becomes visible.
*
* @param waitFor If true, the current thread will block until the
*
* @param waitFor If true, the current thread will block until the
* rendering thread is running
*/
public void startCanvas(boolean waitFor){
@ -444,7 +449,7 @@ public class Application implements SystemListener {
}
/**
* Internal use only.
* Internal use only.
*/
public void reshape(int w, int h){
renderManager.notifyReshape(w, h);
@ -453,7 +458,7 @@ public class Application implements SystemListener {
/**
* Restarts the context, applying any changed settings.
* <p>
* Changes to the {@link AppSettings} of this Application are not
* Changes to the {@link AppSettings} of this Application are not
* applied immediately; calling this method forces the context
* to restart, applying the new settings.
*/
@ -465,10 +470,10 @@ public class Application implements SystemListener {
/**
* Requests the context to close, shutting down the main loop
* and making necessary cleanup operations.
*
*
* Same as calling stop(false)
*
* @see #stop(boolean)
*
* @see #stop(boolean)
*/
public void stop(){
stop(false);
@ -476,7 +481,7 @@ public class Application implements SystemListener {
/**
* Requests the context to close, shutting down the main loop
* and making necessary cleanup operations.
* and making necessary cleanup operations.
* After the application has stopped, it cannot be used anymore.
*/
public void stop(boolean waitFor){
@ -501,7 +506,7 @@ public class Application implements SystemListener {
initDisplay();
initCamera();
if (inputEnabled){
initInput();
}
@ -522,12 +527,12 @@ public class Application implements SystemListener {
logger.log(Level.SEVERE, errMsg, t);
// Display error message on screen
if (t != null) {
JmeSystem.showErrorDialog(errMsg + "\n" + t.getClass().getSimpleName() +
JmeSystem.showErrorDialog(errMsg + "\n" + t.getClass().getSimpleName() +
(t.getMessage() != null ? ": " + t.getMessage() : ""));
} else {
JmeSystem.showErrorDialog(errMsg);
}
stop(); // stop the application
}
@ -563,7 +568,7 @@ public class Application implements SystemListener {
/**
* Enqueues a task/callable object to execute in the jME3
* rendering thread.
* rendering thread.
* <p>
* Callables are executed right at the beginning of the main loop.
* They are executed even if the application is currently paused
@ -585,7 +590,7 @@ public class Application implements SystemListener {
task.invoke();
}
}
}
}
/**
* Do not call manually.
@ -594,9 +599,9 @@ public class Application implements SystemListener {
public void update(){
// Make sure the audio renderer is available to callables
AudioContext.setAudioRenderer(audioRenderer);
runQueuedTasks();
if (speed == 0 || paused)
return;
@ -622,9 +627,12 @@ public class Application implements SystemListener {
if (joyInput != null)
joyInput.destroy();
if (touchInput != null)
touchInput.destroy();
touchInput.destroy();
if (sensorInput != null)
sensorInput.destroy();
inputManager = null;
}
@ -635,11 +643,11 @@ public class Application implements SystemListener {
*/
public void destroy(){
stateManager.cleanup();
destroyInput();
if (audioRenderer != null)
audioRenderer.cleanup();
timer.reset();
}

@ -88,6 +88,7 @@ public class InputManager implements RawInputListener {
private final MouseInput mouse;
private final JoyInput joystick;
private final TouchInput touch;
private final SensorInput sensor;
private float frameTPF;
private long lastLastUpdateTime = 0;
private long lastUpdateTime = 0;
@ -127,9 +128,10 @@ public class InputManager implements RawInputListener {
* @param keys
* @param joystick
* @param touch
* @param sensor
* @throws IllegalArgumentException If either mouseInput or keyInput are null.
*/
public InputManager(MouseInput mouse, KeyInput keys, JoyInput joystick, TouchInput touch) {
public InputManager(MouseInput mouse, KeyInput keys, JoyInput joystick, TouchInput touch, SensorInput sensor) {
if (keys == null || mouse == null) {
throw new NullPointerException("Mouse or keyboard cannot be null");
}
@ -138,6 +140,7 @@ public class InputManager implements RawInputListener {
this.mouse = mouse;
this.joystick = joystick;
this.touch = touch;
this.sensor = sensor;
keys.setInputListener(this);
mouse.setInputListener(this);
@ -148,6 +151,9 @@ public class InputManager implements RawInputListener {
if (touch != null) {
touch.setInputListener(this);
}
if (sensor != null) {
sensor.setInputListener(this);
}
firstTime = keys.getInputTimeNanos();
}
@ -442,6 +448,50 @@ public class InputManager implements RawInputListener {
inputQueue.add(evt);
}
private void onMotionSensorEventQueued(MotionSensorEvent evt) {
int hash = SensorTrigger.sensorHash(evt.getSensorType());
ArrayList<Mapping> maps = bindings.get(hash);
if (maps == null) {
return;
}
int size = maps.size();
for (int i = size - 1; i >= 0; i--) {
Mapping mapping = maps.get(i);
ArrayList<InputListener> listeners = mapping.listeners;
int listenerSize = listeners.size();
for (int j = listenerSize - 1; j >= 0; j--) {
InputListener listener = listeners.get(j);
if (listener instanceof MotionSensorListener) {
((MotionSensorListener) listener).onMotionSensorChange(mapping.name, evt.getSensorType(), evt.getX(), evt.getY(), evt.getZ(), evt.getDX(), evt.getDY(), evt.getDZ());
}
}
}
}
/**
* Callback from RawInputListener. Do not use.
*/
public void onMotionSensorEvent(MotionSensorEvent evt) {
if (!eventsPermitted) {
throw new UnsupportedOperationException("SensorInput has raised an event at an illegal time.");
}
inputQueue.add(evt);
}
/**
* Returns the SensorInput implementation. Use this as an entry point to
* enable and disable various sensors as well as setting other sensor settings.
* @return The SensorInput implementation.
*/
public SensorInput getSensorInput() {
return sensor;
}
/**
* Set the deadzone for joystick axes.
*
@ -784,6 +834,8 @@ public class InputManager implements RawInputListener {
listener.onJoyButtonEvent((JoyButtonEvent) event);
} else if (event instanceof TouchEvent) {
listener.onTouchEvent((TouchEvent) event);
} else if (event instanceof MotionSensorEvent) {
listener.onMotionSensorEvent((MotionSensorEvent) event);
} else {
assert false;
}
@ -810,6 +862,8 @@ public class InputManager implements RawInputListener {
onJoyButtonEventQueued((JoyButtonEvent) event);
} else if (event instanceof TouchEvent) {
onTouchEventQueued((TouchEvent) event);
} else if (event instanceof MotionSensorEvent) {
onMotionSensorEventQueued((MotionSensorEvent) event);
} else {
assert false;
}
@ -850,6 +904,9 @@ public class InputManager implements RawInputListener {
if (touch != null) {
touch.update();
}
if (sensor != null) {
sensor.update();
}
eventsPermitted = false;

@ -40,15 +40,15 @@ import com.jme3.input.event.*;
public interface RawInputListener {
/**
* Called before a batch of input will be sent to this
* <code>RawInputListener</code>.
* Called before a batch of input will be sent to this
* <code>RawInputListener</code>.
*/
public void beginInput();
/**
* Called after a batch of input was sent to this
* <code>RawInputListener</code>.
*
* <code>RawInputListener</code>.
*
* The listener should set the {@link InputEvent#setConsumed() consumed flag}
* on any events that have been consumed either at this call or previous calls.
*/
@ -56,44 +56,51 @@ public interface RawInputListener {
/**
* Invoked on joystick axis events.
*
* @param evt
*
* @param evt
*/
public void onJoyAxisEvent(JoyAxisEvent evt);
/**
* Invoked on joystick button presses.
*
* @param evt
*
* @param evt
*/
public void onJoyButtonEvent(JoyButtonEvent evt);
/**
* Invoked on mouse movement/motion events.
*
* @param evt
*
* @param evt
*/
public void onMouseMotionEvent(MouseMotionEvent evt);
/**
* Invoked on mouse button events.
*
* @param evt
*
* @param evt
*/
public void onMouseButtonEvent(MouseButtonEvent evt);
/**
* Invoked on keyboard key press or release events.
*
* @param evt
*
* @param evt
*/
public void onKeyEvent(KeyInputEvent evt);
/**
* Invoked on touchscreen touch events.
*
* @param evt
*
* @param evt
*/
public void onTouchEvent(TouchEvent evt);
/**
* Invoked on motion sensor events.
*
* @param evt
*/
public void onMotionSensorEvent(MotionSensorEvent evt);
}

@ -0,0 +1,146 @@
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.input;
import java.util.Set;
/**
* A specific API for interfacing with sensors.
*
* In order to conserve battery power for handheld devices, sensors must be
* enabled before data will be sent. Use the setEnable method to enable or disable
* the sensors.
*
* Sensor speed can also be set. Constants in this class are used so that each
* platform implementation can decide what absolute update rate to use. Use the
* setSensorFrequency method to set the desired update rate.
*
* In order to minimize the amount of data sent to the application, there is a
* method available to set how much change is required between sensor readings
* before the new updated data is sent. Use the setSensorMinChange method to set
* a minimum data change percentage (percentage of max sensor range). Data will
* not be sent to the application until the data has changed by this amount.
*
*
*
*
*
* @author iwgeric
*/
public interface SensorInput extends Input {
/**
* Orientation Sensor. Values returned in the onMotionSensorChanged event
* are in radians.
*/
public static final int SENSOR_TYPE_ORIENTATION = 0;
/**
* Accelerometer Sensor. Values returned in the onMotionSensorChanged event
* are in m/s^2. Values include gravity. To get true device acceleration,
* gravity must be removed.
*/
public static final int SENSOR_TYPE_ACCELEROMETER = 1;
/**
* Magnetic Field Sensor. Values returned in the onMotionSensorChanged event
* are in micro-Tesla (uT).
*/
public static final int SENSOR_TYPE_MAGNETIC_FIELD = 2;
/**
* Slowest Sensor Update Speed
*/
public static final int SENSOR_SPEED_SLOW = 0;
/**
* Medium Sensor Update Speed
*/
public static final int SENSOR_SPEED_MEDIUM = 1;
/**
* Fastest Sensor Update Speed
*/
public static final int SENSOR_SPEED_FAST = 2;
/**
* Returns whether a sensor is enabled or not.
*
* @param sensorType The sensor type.
* @return whether a sensor is enabled or not.
*/
public boolean isEnabled(int sensorType);
/**
* Sets enable/disable for a specific sensor type.
*
* @param sensorType The sensor type.
* @param enable True to enable, False to disable.
*/
public void setEnable(int sensorType, boolean enable);
/**
* Sets enable/disable for all sensor types.
*
* @param enable True to enable, False to disable.
*/
public void setEnable(boolean enable);
/**
* Returns a list of available sensor types.
*
* @return a list of available sensor types.
*/
public Set<Integer> getSensorTypes();
/**
* Set the minimum amount of change that is required before an event
* is created for the sensor. minChangePercent is defined as a percentage
* of the maximum sensor range.
*
* @param sensorType The sensor type.
* @param minChangePercent Percentage of changed required before creating an event.
*/
public void setSensorMinChange(int sensorType, float minChangePercent);
/**
* Set the update frequency for the sensor. Use the defined constants in
* SensorInput for setting the speed becuase the actual update frequency is
* platform dependant.
*
* @param sensorType The sensor type.
* @param updateSpeed Target update speed as a constant (do not use absolute values)
*/
public void setSensorFrequency(int sensorType, int updateSpeed);
}

@ -0,0 +1,58 @@
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.input.controls;
import com.jme3.input.SensorInput;
/**
* <code>MotionSensorListener</code> is used to receive events from Sensors
*
* @author iwgeric
*/
public interface MotionSensorListener extends InputListener {
/**
* Called when data from a sensor has been updated.
*
* @param name The name of the mapping that was invoked
* @param sensorType Sensor Type value from {@link SensorInput}.
* @param x X component of the new sensor data based on the sensor type.
* @param y Y component of the new sensor data based on the sensor type.
* @param z Z component of the new sensor data based on the sensor type.
* @param dX Change in the x component from the last update.
* @param dY Change in the y component from the last update.
* @param dZ Change in the z component from the last update.
*/
public void onMotionSensorChange(String name, int sensorType, float x, float y, float z, float dX, float dY, float dZ);
}

@ -0,0 +1,83 @@
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.input.controls;
import com.jme3.input.SensorInput;
/**
* A <code>SensorTrigger</code> is used as a mapping to receive events
* from a sensor.
*
* @author Kirill Vainer
*/
public class SensorTrigger implements Trigger {
// private final SensorInput.Type sensorType;
private final int sensorType;
/**
* Create a new <code>SensorTrigger</code> to receive sensor events.
*
* @param Sensor Type. See {@link SensorInput}.
*/
// public SensorTrigger(SensorInput.Type sensorType) {
public SensorTrigger(int sensorType) {
// if (sensorType == null)
if (sensorType < 0 || sensorType > 255)
throw new IllegalArgumentException("Invalide Sensor Type");
this.sensorType = sensorType;
}
// public SensorInput.Type getSensorType() {
public int getSensorType() {
return sensorType;
}
public String getName() {
return sensorType + " Sensor";
}
// public static int sensorHash(SensorInput.Type sensorType){
public static int sensorHash(int sensorType){
// assert sensorType != null;
// return 256 | (sensorType.ordinal() & 0xff);
assert sensorType >= 0 && sensorType <= 255;
return 1024 | (sensorType & 0xff);
}
public int triggerHashCode() {
return sensorHash(sensorType);
}
}

@ -0,0 +1,118 @@
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.input.event;
/**
* Motion Sensor event.
*
* @author iwgeric
*/
public class MotionSensorEvent extends InputEvent {
private int sensorType;
private float x, y, z, dX, dY, dZ;
public MotionSensorEvent(int sensorType, float x, float y, float z, float dX, float dY, float dZ) {
this.sensorType = sensorType;
this.x = x;
this.y = y;
this.z = z;
this.dX = dX;
this.dY = dY;
this.dZ = dZ;
}
/**
* Sensor Type
* @return Sensor Type
*/
public int getSensorType() {
return sensorType;
}
/**
* Current X coordinate
* @return Current X coordinate
*/
public float getX() {
return x;
}
/**
* Current Y coordinate
* @return Current Y coordinate
*/
public float getY() {
return y;
}
/**
* Current Z coordinate
* @return Current Z coordinate
*/
public float getZ() {
return z;
}
/**
* The change in X coordinate
* @return change in X coordinate
*/
public float getDX() {
return dX;
}
/**
* The change in Y coordinate
*
* @return change in Y coordinate
*/
public float getDY() {
return dY;
}
/**
* The change in Z coordinate
*
* @return change in Z coordinate
*/
public float getDZ() {
return dZ;
}
@Override
public String toString(){
return "MotionSensor(Type="+sensorType+", X="+x+", Y="+y+", Z="+z+", DX="+dX+", DY="+dY+", DZ="+dZ+")";
}
}

@ -35,6 +35,7 @@ package com.jme3.system;
import com.jme3.input.JoyInput;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.SensorInput;
import com.jme3.input.TouchInput;
import com.jme3.renderer.Renderer;
@ -56,7 +57,7 @@ public interface JmeContext {
* display with the windowing system.
*/
Display,
/**
* A canvas type context makes a rendering surface available as an
* AWT {@link java.awt.Canvas} object that can be embedded in a Swing/AWT
@ -64,7 +65,7 @@ public interface JmeContext {
* to {@link JmeCanvasContext}.
*/
Canvas,
/**
* An <code>OffscreenSurface</code> is a context that is not visible
* by the user. The application can use the offscreen surface to do
@ -85,7 +86,7 @@ public interface JmeContext {
* @return The type of the context.
*/
public Type getType();
/**
* @param settings the display settings to use for the created context. If
* the context has already been created, then <code>restart()</code> must be called
@ -100,7 +101,7 @@ public interface JmeContext {
public void setSystemListener(SystemListener listener);
/**
* @return The current display settings. Note that they might be
* @return The current display settings. Note that they might be
* different from the ones set with setDisplaySettings() if the context
* was restarted or the settings changed internally.
*/
@ -125,17 +126,22 @@ public interface JmeContext {
* @return Joystick input implementation. May be null if not available.
*/
public JoyInput getJoyInput();
/**
* @return Touch device input implementation. May be null if not available.
*/
public TouchInput getTouchInput();
/**
* @return Sensor device input implementation. May be null if not available.
*/
public SensorInput getSensorInput();
/**
* @return The timer for this context, or null if not created yet.
*/
public Timer getTimer();
/**
* Sets the title of the display (if available). This does nothing
* for fullscreen, headless, or canvas contexts.

@ -35,6 +35,7 @@ package com.jme3.system;
import com.jme3.input.JoyInput;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.SensorInput;
import com.jme3.input.TouchInput;
import com.jme3.input.dummy.DummyKeyInput;
import com.jme3.input.dummy.DummyMouseInput;
@ -135,7 +136,7 @@ public class NullContext implements JmeContext, Runnable {
}
deinitInThread();
logger.info("NullContext destroyed.");
}
@ -173,11 +174,15 @@ public class NullContext implements JmeContext, Runnable {
public JoyInput getJoyInput() {
return null;
}
public TouchInput getTouchInput() {
return null;
}
public SensorInput getSensorInput() {
return null;
}
public void setTitle(String title) {
}
@ -225,6 +230,6 @@ public class NullContext implements JmeContext, Runnable {
public boolean isRenderable() {
return true; // Doesn't really matter if true or false. Either way
// RenderManager won't render anything.
// RenderManager won't render anything.
}
}

@ -3,6 +3,7 @@ package com.jme3.system.awt;
import com.jme3.input.JoyInput;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.SensorInput;
import com.jme3.input.TouchInput;
import com.jme3.input.awt.AwtKeyInput;
import com.jme3.input.awt.AwtMouseInput;
@ -17,12 +18,12 @@ public class AwtPanelsContext implements JmeContext {
protected SystemListener listener;
protected ArrayList<AwtPanel> panels = new ArrayList<AwtPanel>();
protected AwtPanel inputSource;
protected AwtMouseInput mouseInput = new AwtMouseInput();
protected AwtKeyInput keyInput = new AwtKeyInput();
protected boolean lastThrottleState = false;
private class AwtPanelsListener implements SystemListener {
public void initialize() {
@ -60,20 +61,20 @@ public class AwtPanelsContext implements JmeContext {
destroyInThread();
}
}
public void setInputSource(AwtPanel panel){
if (!panels.contains(panel))
throw new IllegalArgumentException();
inputSource = panel;
mouseInput.setInputSource(panel);
keyInput.setInputSource(panel);
}
public Type getType() {
return Type.OffscreenSurface;
}
public void setSystemListener(SystemListener listener) {
this.listener = listener;
}
@ -102,6 +103,10 @@ public class AwtPanelsContext implements JmeContext {
return null;
}
public SensorInput getSensorInput() {
return null;
}
public Timer getTimer() {
return actualContext.getTimer();
}
@ -113,31 +118,31 @@ public class AwtPanelsContext implements JmeContext {
public boolean isRenderable() {
return actualContext != null && actualContext.isRenderable();
}
public AwtPanelsContext(){
}
public AwtPanel createPanel(PaintMode paintMode){
AwtPanel panel = new AwtPanel(paintMode);
panels.add(panel);
return panel;
}
private void initInThread(){
listener.initialize();
}
private void updateInThread(){
// Check if throttle required
boolean needThrottle = true;
for (AwtPanel panel : panels){
if (panel.isActiveDrawing()){
needThrottle = false;
break;
}
}
if (lastThrottleState != needThrottle){
lastThrottleState = needThrottle;
if (lastThrottleState){
@ -146,21 +151,21 @@ public class AwtPanelsContext implements JmeContext {
System.out.println("OGL: Ceased throttling update loop.");
}
}
if (needThrottle){
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
}
}
listener.update();
}
private void destroyInThread(){
listener.destroy();
}
public void setSettings(AppSettings settings) {
this.settings.copyFrom(settings);
this.settings.setRenderer(AppSettings.LWJGL_OPENGL2);
@ -173,7 +178,7 @@ public class AwtPanelsContext implements JmeContext {
if (actualContext != null){
throw new IllegalStateException("Already created");
}
actualContext = JmeSystem.newContext(settings, Type.OffscreenSurface);
actualContext.setSystemListener(new AwtPanelsListener());
actualContext.create(waitFor);
@ -182,15 +187,15 @@ public class AwtPanelsContext implements JmeContext {
public void destroy(boolean waitFor) {
if (actualContext == null)
throw new IllegalStateException("Not created");
// destroy parent context
actualContext.destroy(waitFor);
}
public void setTitle(String title) {
// not relevant, ignore
}
public void setAutoFlushFrames(boolean enabled) {
// not relevant, ignore
}
@ -198,5 +203,5 @@ public class AwtPanelsContext implements JmeContext {
public void restart() {
// only relevant if changing pixel format.
}
}

@ -35,6 +35,7 @@ package com.jme3.system.lwjgl;
import com.jme3.input.JoyInput;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.SensorInput;
import com.jme3.input.TouchInput;
import com.jme3.input.lwjgl.JInputJoyInput;
import com.jme3.input.lwjgl.LwjglKeyInput;
@ -54,7 +55,7 @@ import org.lwjgl.opengl.Util;
public abstract class LwjglAbstractDisplay extends LwjglContext implements Runnable {
private static final Logger logger = Logger.getLogger(LwjglAbstractDisplay.class.getName());
protected AtomicBoolean needClose = new AtomicBoolean(false);
protected boolean wasActive = false;
protected int frameRate = 0;
@ -98,7 +99,7 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
public void uncaughtException(Thread thread, Throwable thrown) {
listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
if (needClose.get()){
// listener.handleError() has requested the
// listener.handleError() has requested the
// context to close. Satisfy request.
deinitInThread();
}
@ -126,7 +127,7 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
listener.handleError("Failed to create display", ex);
return false; // if we failed to create display, do not continue
}
listener.initialize();
return true;
}
@ -149,7 +150,7 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
throw new IllegalStateException();
listener.update();
// All this does is call swap buffers
// If the canvas is not active, there's no need to waste time
// doing that ..
@ -253,11 +254,15 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
}
return keyInput;
}
public TouchInput getTouchInput() {
return null;
}
public SensorInput getSensorInput() {
return null;
}
public void setAutoFlushFrames(boolean enabled){
this.autoFlush = enabled;
}

@ -35,6 +35,7 @@ package com.jme3.system.lwjgl;
import com.jme3.input.JoyInput;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.SensorInput;
import com.jme3.input.TouchInput;
import com.jme3.input.dummy.DummyKeyInput;
import com.jme3.input.dummy.DummyMouseInput;
@ -53,7 +54,7 @@ public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
private int width;
private int height;
private PixelFormat pixelFormat;
protected void initInThread(){
if ((Pbuffer.getCapabilities() & Pbuffer.PBUFFER_SUPPORTED) == 0){
logger.severe("Offscreen surfaces are not supported.");
@ -69,7 +70,7 @@ public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
settings.getDepthBits(),
settings.getStencilBits(),
settings.getSamples());
width = settings.getWidth();
height = settings.getHeight();
try{
@ -122,9 +123,9 @@ public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
listener.update();
assert checkGLError();
renderer.onFrame();
int frameRate = settings.getFrameRate();
if (frameRate >= 1){
Display.sync(frameRate);
@ -133,7 +134,7 @@ public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
protected void deinitInThread(){
renderable.set(false);
listener.destroy();
renderer.cleanup();
pbuffer.destroy();
@ -187,11 +188,15 @@ public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
public JoyInput getJoyInput() {
return null;
}
public TouchInput getTouchInput() {
return null;
}
public SensorInput getSensorInput() {
return null;
}
public void setTitle(String title) {
}

@ -216,6 +216,9 @@ public class InputSystemJme implements InputSystem, RawInputListener {
public void onJoyButtonEvent(JoyButtonEvent evt) {
}
public void onMotionSensorEvent(MotionSensorEvent evt) {
}
public void onKeyEvent(KeyInputEvent evt) {
inputQueue.add(evt);
}

@ -47,7 +47,7 @@ public class TestBitmapFont extends SimpleApplication {
private String txtB =
"ABCDEFGHIKLMNOPQRSTUVWXYZ1234567 890`~!@#$%^&*()-=_+[]\\;',./{}|:<>?";
private BitmapText txt;
private BitmapText txt2;
private BitmapText txt3;
@ -62,7 +62,7 @@ public class TestBitmapFont extends SimpleApplication {
inputManager.addMapping("WordWrap", new KeyTrigger(KeyInput.KEY_TAB));
inputManager.addListener(keyListener, "WordWrap");
inputManager.addRawInputListener(textListener);
BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt");
txt = new BitmapText(fnt, false);
txt.setBox(new Rectangle(0, 0, settings.getWidth(), settings.getHeight()));
@ -76,33 +76,33 @@ public class TestBitmapFont extends SimpleApplication {
txt2.setText("Text without restriction. \nText without restriction. Text without restriction. Text without restriction");
txt2.setLocalTranslation(0, txt2.getHeight(), 0);
guiNode.attachChild(txt2);
txt3 = new BitmapText(fnt, false);
txt3.setBox(new Rectangle(0, 0, settings.getWidth(), 0));
txt3.setText("Press Tab to toggle word-wrap. type text and enter to input text");
txt3.setLocalTranslation(0, settings.getHeight()/2, 0);
guiNode.attachChild(txt3);
}
private ActionListener keyListener = new ActionListener() {
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if (name.equals("WordWrap") && !isPressed) {
txt.setLineWrapMode( txt.getLineWrapMode() == LineWrapMode.Word ?
LineWrapMode.NoWrap : LineWrapMode.Word );
}
}
}
};
private RawInputListener textListener = new RawInputListener() {
private StringBuilder str = new StringBuilder();
@Override
public void onMouseMotionEvent(MouseMotionEvent evt) { }
public void onMouseMotionEvent(MouseMotionEvent evt) { }
@Override
public void onMouseButtonEvent(MouseButtonEvent evt) { }
public void onMouseButtonEvent(MouseButtonEvent evt) { }
@Override
public void onKeyEvent(KeyInputEvent evt) {
if (evt.isReleased())
@ -114,21 +114,24 @@ public class TestBitmapFont extends SimpleApplication {
str.append(evt.getKeyChar());
}
}
@Override
public void onJoyButtonEvent(JoyButtonEvent evt) { }
@Override
public void onJoyAxisEvent(JoyAxisEvent evt) { }
@Override
public void endInput() { }
@Override
public void beginInput() { }
@Override
public void onTouchEvent(TouchEvent evt) { }
@Override
public void onMotionSensorEvent(MotionSensorEvent evt) { }
};
}

@ -73,7 +73,9 @@ public class TestSoftwareMouse extends SimpleApplication {
}
public void onKeyEvent(KeyInputEvent evt) {
}
public void onTouchEvent(TouchEvent evt) {
public void onTouchEvent(TouchEvent evt) {
}
public void onMotionSensorEvent(MotionSensorEvent evt) {
}
};
@ -93,7 +95,7 @@ public class TestSoftwareMouse extends SimpleApplication {
// inputManager.setCursorVisible(false);
Texture tex = assetManager.loadTexture("Interface/Logo/Cursor.png");
cursor = new Picture("cursor");
cursor.setTexture(assetManager, (Texture2D) tex, true);
cursor.setWidth(64);

Loading…
Cancel
Save