1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.compass; 18 19 import java.nio.ByteBuffer; 20 import java.nio.ByteOrder; 21 import java.nio.FloatBuffer; 22 23 import javax.microedition.khronos.egl.EGL10; 24 import javax.microedition.khronos.egl.EGLConfig; 25 import javax.microedition.khronos.opengles.GL10; 26 import static javax.microedition.khronos.opengles.GL10.*; 27 28 import android.app.Activity; 29 import android.content.Context; 30 import android.hardware.Sensor; 31 import android.hardware.SensorEvent; 32 import android.hardware.SensorEventListener; 33 import android.hardware.SensorManager; 34 import android.opengl.GLSurfaceView; 35 import android.opengl.GLSurfaceView.Renderer; 36 import android.os.Bundle; 37 import android.util.Log; 38 39 /** 40 * This class provides a basic demonstration of how to use the 41 * {@link android.hardware.SensorManager SensorManager} API to draw 42 * a 3D compass. 43 */ 44 public class CompassActivity extends Activity implements Renderer, SensorEventListener { 45 private GLSurfaceView mGLSurfaceView; 46 private SensorManager mSensorManager; 47 private float[] mGData = new float[3]; 48 private float[] mMData = new float[3]; 49 private float[] mR = new float[16]; 50 private float[] mI = new float[16]; 51 private FloatBuffer mVertexBuffer; 52 private FloatBuffer mColorBuffer; 53 private ByteBuffer mIndexBuffer; 54 private float[] mOrientation = new float[3]; 55 private int mCount; 56 57 public CompassActivity() { 58 } 59 60 /** Called with the activity is first created. */ 61 @Override 62 public void onCreate(Bundle savedInstanceState) { 63 super.onCreate(savedInstanceState); 64 65 mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE); 66 mGLSurfaceView = new GLSurfaceView(this); 67 mGLSurfaceView.setRenderer(this); 68 setContentView(mGLSurfaceView); 69 } 70 71 @Override 72 protected void onResume() { 73 // Ideally a game should implement onResume() and onPause() 74 // to take appropriate action when the activity looses focus 75 super.onResume(); 76 mGLSurfaceView.onResume(); 77 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 78 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); 79 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_GAME); 80 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_GAME); 81 } 82 83 @Override 84 protected void onPause() { 85 // Ideally a game should implement onResume() and onPause() 86 // to take appropriate action when the activity looses focus 87 super.onPause(); 88 mGLSurfaceView.onPause(); 89 mSensorManager.unregisterListener(this); 90 } 91 92 public void onDrawFrame(GL10 gl) { 93 /* 94 * Usually, the first thing one might want to do is to clear 95 * the screen. The most efficient way of doing this is to use 96 * glClear(). 97 */ 98 99 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 100 101 /* 102 * Now we're ready to draw some 3D objects 103 */ 104 105 gl.glMatrixMode(GL10.GL_MODELVIEW); 106 gl.glLoadIdentity(); 107 gl.glTranslatef(0, 0, -2); 108 109 /* 110 * All the magic happens here. The rotation matrix mR reported by 111 * SensorManager.getRotationMatrix() is a 4x4 row-major matrix. 112 * We need to use its inverse for rendering. The inverse is 113 * simply calculated by taking the matrix' transpose. However, since 114 * glMultMatrixf() expects a column-major matrix, we can use mR 115 * directly! 116 */ 117 gl.glMultMatrixf(mR, 0); 118 // some test code which will be used/cleaned up before we ship this. 119 //gl.glMultMatrixf(mI, 0); 120 121 gl.glVertexPointer(3, GL_FLOAT, 0, mVertexBuffer); 122 gl.glColorPointer(4, GL_FLOAT, 0, mColorBuffer); 123 gl.glDrawElements(GL_LINES, 6, GL_UNSIGNED_BYTE, mIndexBuffer); 124 } 125 126 public void onSurfaceChanged(GL10 gl, int width, int height) { 127 gl.glViewport(0, 0, width, height); 128 129 /* 130 * Set our projection matrix. This doesn't have to be done 131 * each time we draw, but usually a new projection needs to 132 * be set when the viewport is resized. 133 */ 134 135 float ratio = (float) width / height; 136 gl.glMatrixMode(GL10.GL_PROJECTION); 137 gl.glLoadIdentity(); 138 gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); 139 } 140 141 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 142 /* 143 * By default, OpenGL enables features that improve quality 144 * but reduce performance. One might want to tweak that 145 * especially on software renderer. 146 */ 147 gl.glDisable(GL10.GL_DITHER); 148 149 /* 150 * Some one-time OpenGL initialization can be made here 151 * probably based on features of this particular context 152 */ 153 gl.glClearColor(1,1,1,1); 154 gl.glEnable(GL10.GL_CULL_FACE); 155 gl.glShadeModel(GL10.GL_SMOOTH); 156 gl.glEnable(GL10.GL_DEPTH_TEST); 157 158 /* 159 * create / load the our 3D models here 160 */ 161 162 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 163 gl.glEnableClientState(GL10.GL_COLOR_ARRAY); 164 165 float vertices[] = { 166 0,0,0, 167 1,0,0, 168 0,1,0, 169 0,0,1 170 }; 171 float colors[] = { 172 0,0,0,0, 173 1,0,0,1, 174 0,1,0,1, 175 0,0,1,1 176 }; 177 byte indices[] = { 0, 1, 0, 2, 0, 3 }; 178 179 // Buffers to be passed to gl*Pointer() functions 180 // must be direct, i.e., they must be placed on the 181 // native heap where the garbage collector cannot 182 // move them. 183 // 184 // Buffers with multi-byte datatypes (e.g., short, int, float) 185 // must have their byte order set to native order 186 187 ByteBuffer vbb; 188 vbb = ByteBuffer.allocateDirect(vertices.length*4); 189 vbb.order(ByteOrder.nativeOrder()); 190 mVertexBuffer = vbb.asFloatBuffer(); 191 mVertexBuffer.put(vertices); 192 mVertexBuffer.position(0); 193 194 vbb = ByteBuffer.allocateDirect(colors.length*4); 195 vbb.order(ByteOrder.nativeOrder()); 196 mColorBuffer = vbb.asFloatBuffer(); 197 mColorBuffer.put(colors); 198 mColorBuffer.position(0); 199 200 mIndexBuffer = ByteBuffer.allocateDirect(indices.length); 201 mIndexBuffer.put(indices); 202 mIndexBuffer.position(0); 203 } 204 205 public void onAccuracyChanged(Sensor sensor, int accuracy) { 206 } 207 208 public void onSensorChanged(SensorEvent event) { 209 int type = event.sensor.getType(); 210 float[] data; 211 if (type == Sensor.TYPE_ACCELEROMETER) { 212 data = mGData; 213 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) { 214 data = mMData; 215 } else { 216 // we should not be here. 217 return; 218 } 219 for (int i=0 ; i<3 ; i++) 220 data[i] = event.values[i]; 221 222 SensorManager.getRotationMatrix(mR, mI, mGData, mMData); 223 // some test code which will be used/cleaned up before we ship this. 224 // SensorManager.remapCoordinateSystem(mR, 225 // SensorManager.AXIS_X, SensorManager.AXIS_Z, mR); 226 // SensorManager.remapCoordinateSystem(mR, 227 // SensorManager.AXIS_Y, SensorManager.AXIS_MINUS_X, mR); 228 SensorManager.getOrientation(mR, mOrientation); 229 float incl = SensorManager.getInclination(mI); 230 231 if (mCount++ > 50) { 232 final float rad2deg = (float)(180.0f/Math.PI); 233 mCount = 0; 234 Log.d("Compass", "yaw: " + (int)(mOrientation[0]*rad2deg) + 235 " pitch: " + (int)(mOrientation[1]*rad2deg) + 236 " roll: " + (int)(mOrientation[2]*rad2deg) + 237 " incl: " + (int)(incl*rad2deg) 238 ); 239 } 240 } 241 } 242