TestAndroidSensors.java 13 KB

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