Search code examples
androidopengl-esandroid-sensorsrotational-matricessensormanager

Finding the right kind of Android sensors and using them


I've tried to search both on google and here the solution to my problem and I don't think it has been asked before (or may I be using the wrong words in my search? ^^')

Anyway, this is what I want to have: a OpenGL surface view (showing a cube for instance) that can rotate according to the orientation of the tablet. So far, nothing hard I guess and I have the code below that works perfectly well

public class RotationVectorDemo extends Activity {
private GLSurfaceView mGLSurfaceView;
private SensorManager mSensorManager;
private MyRenderer mRenderer;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Get an instance of the SensorManager
    mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
    // Create our Preview view and set it as the content of our
    // Activity
    mRenderer = new MyRenderer();
    mGLSurfaceView = new GLSurfaceView(this);
    mGLSurfaceView.setRenderer(mRenderer);
    setContentView(mGLSurfaceView);
}
@Override
protected void onResume() {
    // Ideally a game should implement onResume() and onPause()
    // to take appropriate action when the activity looses focus
    super.onResume();
    mRenderer.start();
    mGLSurfaceView.onResume();
}
@Override
protected void onPause() {
    // Ideally a game should implement onResume() and onPause()
    // to take appropriate action when the activity looses focus
    super.onPause();
    mRenderer.stop();
    mGLSurfaceView.onPause();
}
class MyRenderer implements GLSurfaceView.Renderer, SensorEventListener {
    private Cube mCube;
    private Sensor mRotationVectorSensor;
    private final float[] mRotationMatrix = new float[16];
    public MyRenderer() {
        // find the rotation-vector sensor
        mRotationVectorSensor = mSensorManager.getDefaultSensor(
                Sensor.TYPE_ROTATION_VECTOR);
        mCube = new Cube();
        // initialize the rotation matrix to identity
        mRotationMatrix[ 0] = 1;
        mRotationMatrix[ 4] = 1;
        mRotationMatrix[ 8] = 1;
        mRotationMatrix[12] = 1;
    }
    public void start() {
        // enable our sensor when the activity is resumed, ask for
        // 10 ms updates.
        mSensorManager.registerListener(this, mRotationVectorSensor, 10000);
    }
    public void stop() {
        // make sure to turn our sensor off when the activity is paused
        mSensorManager.unregisterListener(this);
    }
    public void onSensorChanged(SensorEvent event) {
        // we received a sensor event. it is a good practice to check
        // that we received the proper event
        if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
            // convert the rotation-vector to a 4x4 matrix. the matrix
            // is interpreted by Open GL as the inverse of the
            // rotation-vector, which is what we want.
            SensorManager.getRotationMatrixFromVector(
                    mRotationMatrix , event.values);
        }
    }
    public void onDrawFrame(GL10 gl) {
        // clear screen
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        // set-up modelview matrix
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();
        gl.glTranslatef(0, 0, -3.0f);
        gl.glMultMatrixf(mRotationMatrix, 0);
        // draw our object
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
        mCube.draw(gl);
    }
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        // set view-port
        gl.glViewport(0, 0, width, height);
        // set projection matrix
        float ratio = (float) width / height;
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
    }
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // dither is enabled by default, we don't need it
        gl.glDisable(GL10.GL_DITHER);
        // clear screen in white
        gl.glClearColor(1,1,1,1);
    }
    class Cube {
        // initialize our cube
        private FloatBuffer mVertexBuffer;
        private FloatBuffer mColorBuffer;
        private ByteBuffer  mIndexBuffer;
        public Cube() {
            final float vertices[] = {
                    -1, -1, -1,      1, -1, -1,
                    1,  1, -1,      -1,  1, -1,
                    -1, -1,  1,      1, -1,  1,
                    1,  1,  1,     -1,  1,  1,
            };
            final float colors[] = {
                    0,  0,  0,  1,  1,  0,  0,  1,
                    1,  1,  0,  1,  0,  1,  0,  1,
                    0,  0,  1,  1,  1,  0,  1,  1,
                    1,  1,  1,  1,  0,  1,  1,  1,
            };
            final byte indices[] = {
                    0, 4, 5,    0, 5, 1,
                    1, 5, 6,    1, 6, 2,
                    2, 6, 7,    2, 7, 3,
                    3, 7, 4,    3, 4, 0,
                    4, 7, 6,    4, 6, 5,
                    3, 0, 1,    3, 1, 2
            };
            ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
            vbb.order(ByteOrder.nativeOrder());
            mVertexBuffer = vbb.asFloatBuffer();
            mVertexBuffer.put(vertices);
            mVertexBuffer.position(0);
            ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
            cbb.order(ByteOrder.nativeOrder());
            mColorBuffer = cbb.asFloatBuffer();
            mColorBuffer.put(colors);
            mColorBuffer.position(0);
            mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
            mIndexBuffer.put(indices);
            mIndexBuffer.position(0);
        }
        public void draw(GL10 gl) {
            gl.glEnable(GL10.GL_CULL_FACE);
            gl.glFrontFace(GL10.GL_CW);
            gl.glShadeModel(GL10.GL_SMOOTH);
            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
            gl.glColorPointer(4, GL10.GL_FLOAT, 0, mColorBuffer);
            gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
        }
    }
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
}

}

However, when I am locking the screen, moving around and unlocking it afterwards the cube has moved too. Which is logical and I understand perfectly well why. Yet, I would like to know if it's possible to avoid that, like kinda resetting the sensors or something like that, and how I can do it.

I'm not sure I'm using the good kind of sensor at all of if I should change it, or if it's something that can be solved in the code or so. Bear with me as I'm just beginning to work with android sensors.

Basically, this problem is linked to an other that I have on a bigger application but I figured out it would be simpler to use this example to try and solve that. However, if you want to know what my problem is in my bigger application it's essentially the same except that to move the cube, people can either use their fingers (finger_mode) or the sensors (phone_mode). What I want is somehow to be able to rotate the cube with the fingers without paying attention to the sensors and when I go into sensor_mode that they do not change the orientation of the cube just because they are activated. I'm not sure it's clear, if it's not, lemme know.

I'm guessing, since i use touch to modify the rotation matrix that is used by OpenGL there might be some operations that can be done on rotation matrices to solve my problem. Or maybe it's just a sensor problem. Maybe both actually I have no clue so far but these are the different solutions I have been trying to use.

Thanks in advance for helping me figuring this out.

Best,


Solution

  • So the rotation vector is definitely the good kind of sensor to use. Both the accelerometer and the gyroscope won't be of any help for what I want to do.

    However I still have to figure out what to do with the rotation matrices that I have now.