| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.cts.verifier.sensors; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.nio.FloatBuffer; |
| import java.nio.ShortBuffer; |
| |
| import javax.microedition.khronos.egl.EGLConfig; |
| import javax.microedition.khronos.opengles.GL10; |
| |
| import android.content.Context; |
| import android.graphics.Bitmap; |
| import android.graphics.BitmapFactory; |
| import android.hardware.Sensor; |
| import android.hardware.SensorEvent; |
| import android.hardware.SensorEventListener; |
| import android.opengl.GLSurfaceView; |
| import android.opengl.GLU; |
| import android.opengl.GLUtils; |
| |
| import com.android.cts.verifier.R; |
| |
| public class AccelerometerTestRenderer implements GLSurfaceView.Renderer, SensorEventListener { |
| |
| /** |
| * A representation of a 3D triangular wedge or arrowhead shape, suitable |
| * for pointing a direction. |
| */ |
| private static class Wedge { |
| private final static int VERTS = 6; |
| |
| /** |
| * Storage for the vertices. |
| */ |
| private FloatBuffer mFVertexBuffer; |
| |
| /** |
| * Storage for the drawing sequence of the vertices. This contains |
| * integer indices into the mFVertextBuffer structure. |
| */ |
| private ShortBuffer mIndexBuffer; |
| |
| /** |
| * Storage for the texture used on the surface of the wedge. |
| */ |
| private FloatBuffer mTexBuffer; |
| |
| public Wedge() { |
| // Buffers to be passed to gl*Pointer() functions |
| // must be direct & use native ordering |
| |
| ByteBuffer vbb = ByteBuffer.allocateDirect(VERTS * 6 * 4); |
| vbb.order(ByteOrder.nativeOrder()); |
| mFVertexBuffer = vbb.asFloatBuffer(); |
| |
| ByteBuffer tbb = ByteBuffer.allocateDirect(VERTS * 2 * 4); |
| tbb.order(ByteOrder.nativeOrder()); |
| mTexBuffer = tbb.asFloatBuffer(); |
| |
| ByteBuffer ibb = ByteBuffer.allocateDirect(VERTS * 8 * 2); |
| ibb.order(ByteOrder.nativeOrder()); |
| mIndexBuffer = ibb.asShortBuffer(); |
| |
| /** |
| * Coordinates of the vertices making up a simple wedge. Six total |
| * vertices, representing two isosceles triangles, side by side, |
| * centered on the origin separated by 0.25 units, with elongated |
| * ends pointing down the negative Z axis. |
| */ |
| float[] coords = { |
| // X, Y, Z |
| -0.125f, -0.25f, -0.25f, |
| -0.125f, 0.25f, -0.25f, |
| -0.125f, 0.0f, 0.559016994f, |
| 0.125f, -0.25f, -0.25f, |
| 0.125f, 0.25f, -0.25f, |
| 0.125f, 0.0f, 0.559016994f, |
| }; |
| |
| for (int i = 0; i < VERTS; i++) { |
| for (int j = 0; j < 3; j++) { |
| mFVertexBuffer.put(coords[i * 3 + j] * 2.0f); |
| } |
| } |
| |
| for (int i = 0; i < VERTS; i++) { |
| for (int j = 0; j < 2; j++) { |
| mTexBuffer.put(coords[i * 3 + j] * 2.0f + 0.5f); |
| } |
| } |
| |
| // left face |
| mIndexBuffer.put((short) 0); |
| mIndexBuffer.put((short) 1); |
| mIndexBuffer.put((short) 2); |
| |
| // right face |
| mIndexBuffer.put((short) 5); |
| mIndexBuffer.put((short) 4); |
| mIndexBuffer.put((short) 3); |
| |
| // top side, 2 triangles to make rect |
| mIndexBuffer.put((short) 2); |
| mIndexBuffer.put((short) 5); |
| mIndexBuffer.put((short) 3); |
| mIndexBuffer.put((short) 3); |
| mIndexBuffer.put((short) 0); |
| mIndexBuffer.put((short) 2); |
| |
| // bottom side, 2 triangles to make rect |
| mIndexBuffer.put((short) 5); |
| mIndexBuffer.put((short) 2); |
| mIndexBuffer.put((short) 1); |
| mIndexBuffer.put((short) 1); |
| mIndexBuffer.put((short) 4); |
| mIndexBuffer.put((short) 5); |
| |
| // base, 2 triangles to make rect |
| mIndexBuffer.put((short) 0); |
| mIndexBuffer.put((short) 3); |
| mIndexBuffer.put((short) 4); |
| mIndexBuffer.put((short) 4); |
| mIndexBuffer.put((short) 1); |
| mIndexBuffer.put((short) 0); |
| |
| mFVertexBuffer.position(0); |
| mTexBuffer.position(0); |
| mIndexBuffer.position(0); |
| } |
| |
| public void draw(GL10 gl) { |
| gl.glFrontFace(GL10.GL_CCW); |
| gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer); |
| gl.glEnable(GL10.GL_TEXTURE_2D); |
| gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTexBuffer); |
| gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 24, GL10.GL_UNSIGNED_SHORT, mIndexBuffer); |
| } |
| } |
| |
| /** |
| * A representation of the Z-axis in vector form. |
| */ |
| protected static final float[] Z_AXIS = new float[] { |
| 0, 0, 1 |
| }; |
| |
| /** |
| * Computes the cross product of two vectors, storing the resulting |
| * pseudovector in out. All arrays must be length 3 or more, and out is |
| * overwritten. |
| * |
| * @param left the left operand of the cross product |
| * @param right the right operand of the cross product |
| * @param out the array into which to store the cross-product pseudovector's |
| * data |
| */ |
| public static void crossProduct(float[] left, float[] right, float[] out) { |
| out[0] = left[1] * right[2] - left[2] * right[1]; |
| out[1] = left[2] * right[0] - left[0] * right[2]; |
| out[2] = left[0] * right[1] - left[1] * right[0]; |
| } |
| |
| /** |
| * Computes the dot product of two vectors. |
| * |
| * @param left the first dot product operand |
| * @param right the second dot product operand |
| * @return the dot product of left and right |
| */ |
| public static float dotProduct(float[] left, float[] right) { |
| return left[0] * right[0] + left[1] * right[1] + left[2] * right[2]; |
| } |
| |
| /** |
| * Normalizes the input vector into a unit vector. |
| * |
| * @param vector the vector to normalize. Contents are overwritten. |
| */ |
| public static void normalize(float[] vector) { |
| double mag = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] |
| * vector[2]); |
| vector[0] /= mag; |
| vector[1] /= mag; |
| vector[2] /= mag; |
| } |
| |
| /** |
| * The angle around mCrossProd to rotate to align Z-axis with gravity. |
| */ |
| protected float mAngle; |
| |
| private Context mContext; |
| |
| /** |
| * The (pseudo)vector around which to rotate to align Z-axis with gravity. |
| */ |
| protected float[] mCrossProd = new float[3]; |
| |
| private int mTextureID; |
| |
| private Wedge mWedge; |
| |
| /** |
| * It's a constructor. Can you dig it? |
| * |
| * @param context the Android Context that owns this renderer |
| */ |
| public AccelerometerTestRenderer(Context context) { |
| mContext = context; |
| mWedge = new Wedge(); |
| } |
| |
| public void onAccuracyChanged(Sensor arg0, int arg1) { |
| // no-op |
| } |
| |
| /** |
| * Actually draws the wedge. |
| */ |
| public void onDrawFrame(GL10 gl) { |
| // set up the texture for drawing |
| gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE); |
| gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); |
| gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); |
| gl.glActiveTexture(GL10.GL_TEXTURE0); |
| gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID); |
| gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT); |
| gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT); |
| |
| // clear the screen and draw |
| gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); |
| gl.glMatrixMode(GL10.GL_MODELVIEW); |
| gl.glLoadIdentity(); |
| gl.glRotatef(-mAngle * 180 / (float) Math.PI, mCrossProd[0], mCrossProd[1], mCrossProd[2]); |
| mWedge.draw(gl); |
| } |
| |
| public void onSensorChanged(SensorEvent event) { |
| if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { |
| /* |
| * For this test we want *only* accelerometer data, so we can't use |
| * the convenience methods on SensorManager; so compute manually. |
| */ |
| normalize(event.values); |
| |
| /* |
| * Because we need to invert gravity (because the accelerometer vector |
| * actually points up), that constitutes a 180-degree rotation around X, |
| * which means we need to invert Y. |
| */ |
| event.values[1] *= -1; |
| |
| crossProduct(event.values, Z_AXIS, mCrossProd); |
| mAngle = (float) Math.acos(dotProduct(event.values, Z_AXIS)); |
| } |
| } |
| |
| public void onSurfaceChanged(GL10 gl, int w, int h) { |
| gl.glViewport(0, 0, w, h); |
| float ratio = (float) w / h; |
| gl.glMatrixMode(GL10.GL_PROJECTION); |
| gl.glLoadIdentity(); |
| gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7); |
| GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); |
| } |
| |
| public void onSurfaceCreated(GL10 gl, EGLConfig config) { |
| // set up general OpenGL config |
| gl.glClearColor(0.6f, 0f, 0.4f, 1); // a nice purpley magenta |
| gl.glShadeModel(GL10.GL_SMOOTH); |
| gl.glEnable(GL10.GL_DEPTH_TEST); |
| gl.glEnable(GL10.GL_TEXTURE_2D); |
| |
| // create the texture we use on the wedge |
| int[] textures = new int[1]; |
| gl.glGenTextures(1, textures, 0); |
| mTextureID = textures[0]; |
| |
| gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID); |
| gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); |
| gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); |
| gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); |
| gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); |
| gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE); |
| |
| InputStream is = mContext.getResources().openRawResource(R.raw.sns_texture); |
| Bitmap bitmap; |
| try { |
| bitmap = BitmapFactory.decodeStream(is); |
| } finally { |
| try { |
| is.close(); |
| } catch (IOException e) { |
| // Ignore. |
| } |
| } |
| |
| GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); |
| bitmap.recycle(); |
| } |
| } |