/
TestAndroidSensors.java
314 lines (267 loc) · 13.5 KB
/
TestAndroidSensors.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
package jme3test.android;
import com.jme3.app.SimpleApplication;
import com.jme3.input.Joystick;
import com.jme3.input.JoystickAxis;
import com.jme3.input.MouseInput;
import com.jme3.input.SensorJoystickAxis;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Line;
import com.jme3.texture.Texture;
import com.jme3.util.IntMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Example Test Case to test using Android sensors as Joystick axes. Make sure to enable Joystick Events from
* the test chooser menus. Rotating the device will cause the block to rotate. Tapping the screen will cause the
* sensors to be calibrated (reset to zero) at the current orientation. Continuously tapping the screen causes
* the "rumble" to intensify until it reaches the maximum amount and then it shuts off.
*
* @author iwgeric
*/
public class TestAndroidSensors extends SimpleApplication implements ActionListener, AnalogListener {
private static final Logger logger = Logger.getLogger(TestAndroidSensors.class.getName());
private Geometry geomZero = null;
// Map of joysticks saved with the joyId as the key
private IntMap<Joystick> joystickMap = new IntMap<>();
// flag to allow for the joystick axis to be calibrated on startup
private boolean initialCalibrationComplete = false;
// mappings used for onAnalog
private final String ORIENTATION_X_PLUS = "Orientation_X_Plus";
private final String ORIENTATION_X_MINUS = "Orientation_X_Minus";
private final String ORIENTATION_Y_PLUS = "Orientation_Y_Plus";
private final String ORIENTATION_Y_MINUS = "Orientation_Y_Minus";
private final String ORIENTATION_Z_PLUS = "Orientation_Z_Plus";
private final String ORIENTATION_Z_MINUS = "Orientation_Z_Minus";
// variables to save the current rotation
// Used when controlling the geometry with device orientation
private float[] anglesCurrent = new float[]{0f, 0f, 0f};
private Quaternion rotationQuat = new Quaternion();
// switch to apply an absolute rotation (geometry.setLocalRotation) or
// an incremental constant rotation (geometry.rotate)
// Used when controlling the geometry with device orientation
private boolean useAbsolute = false;
// rotation speed to use when apply constant incremental rotation
// Used when controlling the geometry with device orientation
private float rotationSpeedX = 1f;
private float rotationSpeedY = 1f;
// current intensity of the rumble
float rumbleAmount = 0f;
// toggle to enable rumble
boolean enableRumble = true;
// toggle to enable device orientation in FlyByCamera
boolean enableFlyByCameraRotation = false;
// toggle to enable controlling geometry rotation
boolean enableGeometryRotation = true;
// Make sure to set joystickEventsEnabled = true in MainActivity for Android
private float toDegrees(float rad) {
return rad * FastMath.RAD_TO_DEG;
}
@Override
public void simpleInitApp() {
// useAbsolute = true;
// enableRumble = true;
if (enableFlyByCameraRotation) {
flyCam.setEnabled(true);
} else {
flyCam.setEnabled(false);
}
Mesh lineX = new Line(Vector3f.ZERO, Vector3f.ZERO.add(Vector3f.UNIT_X.mult(3)));
Mesh lineY = new Line(Vector3f.ZERO, Vector3f.ZERO.add(Vector3f.UNIT_Y.mult(3)));
Mesh lineZ = new Line(Vector3f.ZERO, Vector3f.ZERO.add(Vector3f.UNIT_Z.mult(3)));
Geometry geoX = new Geometry("X", lineX);
Material matX = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
matX.setColor("Color", ColorRGBA.Red);
matX.getAdditionalRenderState().setLineWidth(30);
geoX.setMaterial(matX);
rootNode.attachChild(geoX);
Geometry geoY = new Geometry("Y", lineY);
Material matY = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
matY.setColor("Color", ColorRGBA.Green);
matY.getAdditionalRenderState().setLineWidth(30);
geoY.setMaterial(matY);
rootNode.attachChild(geoY);
Geometry geoZ = new Geometry("Z", lineZ);
Material matZ = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
matZ.setColor("Color", ColorRGBA.Blue);
matZ.getAdditionalRenderState().setLineWidth(30);
geoZ.setMaterial(matZ);
rootNode.attachChild(geoZ);
Box b = new Box(1, 1, 1);
geomZero = new Geometry("Box", b);
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.Yellow);
Texture tex_ml = assetManager.loadTexture("Interface/Logo/Monkey.jpg");
mat.setTexture("ColorMap", tex_ml);
geomZero.setMaterial(mat);
geomZero.setLocalTranslation(Vector3f.ZERO);
geomZero.setLocalRotation(Quaternion.IDENTITY);
rootNode.attachChild(geomZero);
// Touch (aka MouseInput.BUTTON_LEFT) is used to record the starting
// orientation when using absolute rotations
inputManager.addMapping("MouseClick", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addListener(this, "MouseClick");
Joystick[] joysticks = inputManager.getJoysticks();
if (joysticks == null || joysticks.length < 1) {
logger.log(Level.INFO, "Cannot find any joysticks!");
} else {
// Joysticks return a value of 0 to 1 based on how far the stick is
// push on the axis. This value is then scaled based on how long
// during the frame the joystick axis has been in that position.
// If the joystick is push all the way for the whole frame,
// then the value in onAnalog is equal to tpf.
// If the joystick is push 1/2 way for the entire frame, then the
// onAnalog value is 1/2 tpf.
// Similarly, if the joystick is pushed to the maximum during a frame
// the value in onAnalog will also be scaled.
// For Android sensors, rotating the device 90deg is the same as
// pushing an actual joystick axis to the maximum.
logger.log(Level.INFO, "Number of joysticks: {0}", joysticks.length);
JoystickAxis axis;
for (Joystick joystick : joysticks) {
// Get and display all axes in joystick.
List<JoystickAxis> axes = joystick.getAxes();
for (JoystickAxis joystickAxis : axes) {
logger.log(Level.INFO, "{0} axis scan Name: {1}, LogicalId: {2}, AxisId: {3}",
new Object[]{joystick.getName(), joystickAxis.getName(), joystickAxis.getLogicalId(), joystickAxis.getAxisId()});
}
// Get specific axis based on LogicalId of the JoystickAxis
// If found, map axis
axis = joystick.getAxis(SensorJoystickAxis.ORIENTATION_X);
if (axis != null) {
axis.assignAxis(ORIENTATION_X_PLUS, ORIENTATION_X_MINUS);
inputManager.addListener(this, ORIENTATION_X_PLUS, ORIENTATION_X_MINUS);
logger.log(Level.INFO, "Found {0} Joystick, assigning mapping for X axis: {1}, with max value: {2}",
new Object[]{joystick.toString(), axis.toString(), ((SensorJoystickAxis) axis).getMaxRawValue()});
}
axis = joystick.getAxis(SensorJoystickAxis.ORIENTATION_Y);
if (axis != null) {
axis.assignAxis(ORIENTATION_Y_PLUS, ORIENTATION_Y_MINUS);
inputManager.addListener(this, ORIENTATION_Y_PLUS, ORIENTATION_Y_MINUS);
logger.log(Level.INFO, "Found {0} Joystick, assigning mapping for Y axis: {1}, with max value: {2}",
new Object[]{joystick.toString(), axis.toString(), ((SensorJoystickAxis) axis).getMaxRawValue()});
}
axis = joystick.getAxis(SensorJoystickAxis.ORIENTATION_Z);
if (axis != null) {
axis.assignAxis(ORIENTATION_Z_PLUS, ORIENTATION_Z_MINUS);
inputManager.addListener(this, ORIENTATION_Z_PLUS, ORIENTATION_Z_MINUS);
logger.log(Level.INFO, "Found {0} Joystick, assigning mapping for Z axis: {1}, with max value: {2}",
new Object[]{joystick.toString(), axis.toString(), ((SensorJoystickAxis) axis).getMaxRawValue()});
}
joystickMap.put(joystick.getJoyId(), joystick);
}
}
}
@Override
public void simpleUpdate(float tpf) {
if (!initialCalibrationComplete) {
// Calibrate the axis (set new zero position) if the axis
// is a sensor joystick axis
for (IntMap.Entry<Joystick> entry : joystickMap) {
for (JoystickAxis axis : entry.getValue().getAxes()) {
if (axis instanceof SensorJoystickAxis) {
logger.log(Level.INFO, "Calibrating Axis: {0}", axis.toString());
((SensorJoystickAxis) axis).calibrateCenter();
}
}
}
initialCalibrationComplete = true;
}
if (enableGeometryRotation) {
rotationQuat.fromAngles(anglesCurrent);
rotationQuat.normalizeLocal();
if (useAbsolute) {
geomZero.setLocalRotation(rotationQuat);
} else {
geomZero.rotate(rotationQuat);
}
anglesCurrent[0] = anglesCurrent[1] = anglesCurrent[2] = 0f;
}
}
@Override
public void onAction(String string, boolean pressed, float tpf) {
if (string.equalsIgnoreCase("MouseClick") && pressed) {
// Calibrate the axis (set new zero position) if the axis
// is a sensor joystick axis
for (IntMap.Entry<Joystick> entry : joystickMap) {
for (JoystickAxis axis : entry.getValue().getAxes()) {
if (axis instanceof SensorJoystickAxis) {
logger.log(Level.INFO, "Calibrating Axis: {0}", axis.toString());
((SensorJoystickAxis) axis).calibrateCenter();
}
}
}
if (enableRumble) {
// manipulate joystick rumble
for (IntMap.Entry<Joystick> entry : joystickMap) {
rumbleAmount += 0.1f;
if (rumbleAmount > 1f + FastMath.ZERO_TOLERANCE) {
rumbleAmount = 0f;
}
logger.log(Level.INFO, "rumbling with amount: {0}", rumbleAmount);
entry.getValue().rumble(rumbleAmount);
}
}
}
}
@Override
public void onAnalog(String string, float value, float tpf) {
logger.log(Level.INFO, "onAnalog for {0}, value: {1}, tpf: {2}",
new Object[]{string, value, tpf});
float scaledValue = value;
if (string.equalsIgnoreCase(ORIENTATION_X_PLUS)) {
if (useAbsolute) {
// set rotation amount
// divide by tpf to get back to actual axis value (0 to 1)
// multiply by 90deg so that 90deg = full axis (value = tpf)
anglesCurrent[0] = (scaledValue / tpf * FastMath.HALF_PI);
} else {
// apply an incremental rotation amount based on rotationSpeed
anglesCurrent[0] += scaledValue * rotationSpeedX;
}
}
if (string.equalsIgnoreCase(ORIENTATION_X_MINUS)) {
if (useAbsolute) {
// set rotation amount
// divide by tpf to get back to actual axis value (0 to 1)
// multiply by 90deg so that 90deg = full axis (value = tpf)
anglesCurrent[0] = (-scaledValue / tpf * FastMath.HALF_PI);
} else {
// apply an incremental rotation amount based on rotationSpeed
anglesCurrent[0] -= scaledValue * rotationSpeedX;
}
}
if (string.equalsIgnoreCase(ORIENTATION_Y_PLUS)) {
if (useAbsolute) {
// set rotation amount
// divide by tpf to get back to actual axis value (0 to 1)
// multiply by 90deg so that 90deg = full axis (value = tpf)
anglesCurrent[1] = (scaledValue / tpf * FastMath.HALF_PI);
} else {
// apply an incremental rotation amount based on rotationSpeed
anglesCurrent[1] += scaledValue * rotationSpeedY;
}
}
if (string.equalsIgnoreCase(ORIENTATION_Y_MINUS)) {
if (useAbsolute) {
// set rotation amount
// divide by tpf to get back to actual axis value (0 to 1)
// multiply by 90deg so that 90deg = full axis (value = tpf)
anglesCurrent[1] = (-scaledValue / tpf * FastMath.HALF_PI);
} else {
// apply an incremental rotation amount based on rotationSpeed
anglesCurrent[1] -= scaledValue * rotationSpeedY;
}
}
}
}