Java tutorial
/* vim: set ts=2 sw=2: */ /* * Copyright 2014 Google Inc. All Rights Reserved. * * 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.google.vrtoolkit.cardboard.samples.treasurehunt; import com.google.vrtoolkit.cardboard.CardboardActivity; import com.google.vrtoolkit.cardboard.CardboardView; import com.google.vrtoolkit.cardboard.Eye; import com.google.vrtoolkit.cardboard.HeadTransform; import com.google.vrtoolkit.cardboard.Viewport; import com.google.vrtoolkit.cardboard.audio.CardboardAudioEngine; import android.content.Context; import android.opengl.GLES20; import android.opengl.Matrix; import android.os.Bundle; import android.os.Vibrator; import android.util.Log; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.egl.EGLConfig; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; import java.net.URI; import java.net.URISyntaxException; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.net.Socket; import java.io.OutputStream; /** * A Cardboard sample application. */ public class MainActivity extends CardboardActivity implements CardboardView.StereoRenderer { private static final String TAG = "MainActivity"; private static final float Z_NEAR = 0.1f; private static final float Z_FAR = 100.0f; private static final float CAMERA_Z = 0.01f; private static final float TIME_DELTA = 0.3f; private static final float YAW_LIMIT = 0.12f; private static final float PITCH_LIMIT = 0.12f; private static final int COORDS_PER_VERTEX = 3; // We keep the light always position just above the user. private static final float[] LIGHT_POS_IN_WORLD_SPACE = new float[] { 0.0f, 2.0f, 0.0f, 1.0f }; private static final float MIN_MODEL_DISTANCE = 3.0f; private static final float MAX_MODEL_DISTANCE = 7.0f; private static final String SOUND_FILE = "cube_sound.wav"; private final float[] lightPosInEyeSpace = new float[4]; private FloatBuffer floorVertices; private FloatBuffer floorColors; private FloatBuffer floorNormals; private FloatBuffer cubeVertices; private FloatBuffer cubeColors; private FloatBuffer cubeFoundColors; private FloatBuffer cubeNormals; private FloatBuffer miniCubeVertices; private FloatBuffer miniCubeColors; private FloatBuffer miniCubeFoundColors; private FloatBuffer miniCubeNormals; private int cubeProgram; private int miniCubeProgram; private int floorProgram; private int cubePositionParam; private int cubeNormalParam; private int cubeColorParam; private int cubeModelParam; private int cubeModelViewParam; private int cubeModelViewProjectionParam; private int cubeLightPosParam; private int miniCubePositionParam; private int miniCubeNormalParam; private int miniCubeColorParam; private int miniCubeModelParam; private int miniCubeModelViewParam; private int miniCubeModelViewProjectionParam; private int miniCubeLightPosParam; private int floorPositionParam; private int floorNormalParam; private int floorColorParam; private int floorModelParam; private int floorModelViewParam; private int floorModelViewProjectionParam; private int floorLightPosParam; private float[] modelCube; private float[] modelMiniCube; private float[] camera; private float[] view; private float[] headView; private float[] modelViewProjection; private float[] modelView; private float[] modelFloor; private float[] modelPosition; private float[] headRotation; private int score = 0; private float objectDistance = MAX_MODEL_DISTANCE / 2.0f; private float floorDepth = 20f; private Vibrator vibrator; private CardboardOverlayView overlayView; private CardboardAudioEngine cardboardAudioEngine; private volatile int soundId = CardboardAudioEngine.INVALID_ID; //Websocket things private WebSocketClient mWebSocketClient; private float[] handPos; private float WALL_DIST = 4.25f; /** * Converts a raw text file, saved as a resource, into an OpenGL ES shader. * * @param type The type of shader we will be creating. * @param resId The resource ID of the raw text file about to be turned into a shader. * @return The shader object handler. */ private int loadGLShader(int type, int resId) { String code = readRawTextFile(resId); int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, code); GLES20.glCompileShader(shader); // Get the compilation status. final int[] compileStatus = new int[1]; GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0); // If the compilation failed, delete the shader. if (compileStatus[0] == 0) { Log.e(TAG, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); shader = 0; } if (shader == 0) { throw new RuntimeException("Error creating shader."); } return shader; } /** * Checks if we've had an error inside of OpenGL ES, and if so what that error is. * * @param label Label to report in case of error. */ private static void checkGLError(String label) { int error; while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { Log.e(TAG, label + ": glError " + error); throw new RuntimeException(label + ": glError " + error); } } /** * Sets the view to our CardboardView and initializes the transformation matrices we will use * to render our scene. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.common_ui); CardboardView cardboardView = (CardboardView) findViewById(R.id.cardboard_view); cardboardView.setRestoreGLStateEnabled(false); cardboardView.setRenderer(this); setCardboardView(cardboardView); modelCube = new float[16]; modelMiniCube = new float[16]; camera = new float[16]; view = new float[16]; modelViewProjection = new float[16]; modelView = new float[16]; modelFloor = new float[16]; // Model first appears directly in front of user. //modelPosition = new float[] {0.0f, 0.0f, -MAX_MODEL_DISTANCE / 0.5f}; modelPosition = new float[] { 0.0f, 0.0f, -WALL_DIST }; headRotation = new float[4]; headView = new float[16]; vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); overlayView = (CardboardOverlayView) findViewById(R.id.overlay); overlayView.show3DToast("Pull the magnet when you find an object."); // Initialize 3D audio engine. cardboardAudioEngine = new CardboardAudioEngine(getAssets(), CardboardAudioEngine.RenderingQuality.HIGH); connectWebSocket(); handPos = new float[3]; } @Override public void onPause() { cardboardAudioEngine.pause(); super.onPause(); } @Override public void onResume() { super.onResume(); cardboardAudioEngine.resume(); } @Override public void onRendererShutdown() { Log.i(TAG, "onRendererShutdown"); } @Override public void onSurfaceChanged(int width, int height) { Log.i(TAG, "onSurfaceChanged"); } /** * Creates the buffers we use to store information about the 3D world. * * <p>OpenGL doesn't use Java arrays, but rather needs data in a format it can understand. * Hence we use ByteBuffers. * * @param config The EGL configuration used when creating the surface. */ @Override public void onSurfaceCreated(EGLConfig config) { Log.i(TAG, "onSurfaceCreated"); GLES20.glClearColor(0.1f, 0.1f, 0.1f, 0.5f); // Dark background so text shows up well. ByteBuffer bbVertices = ByteBuffer.allocateDirect(WorldLayoutData.CUBE_COORDS.length * 4); bbVertices.order(ByteOrder.nativeOrder()); cubeVertices = bbVertices.asFloatBuffer(); cubeVertices.put(WorldLayoutData.CUBE_COORDS); cubeVertices.position(0); ByteBuffer bbColors = ByteBuffer.allocateDirect(WorldLayoutData.CUBE_COLORS.length * 4); bbColors.order(ByteOrder.nativeOrder()); cubeColors = bbColors.asFloatBuffer(); cubeColors.put(WorldLayoutData.CUBE_COLORS); cubeColors.position(0); ByteBuffer bbFoundColors = ByteBuffer.allocateDirect(WorldLayoutData.CUBE_FOUND_COLORS.length * 4); bbFoundColors.order(ByteOrder.nativeOrder()); cubeFoundColors = bbFoundColors.asFloatBuffer(); cubeFoundColors.put(WorldLayoutData.CUBE_FOUND_COLORS); cubeFoundColors.position(0); ByteBuffer bbNormals = ByteBuffer.allocateDirect(WorldLayoutData.CUBE_NORMALS.length * 4); bbNormals.order(ByteOrder.nativeOrder()); cubeNormals = bbNormals.asFloatBuffer(); cubeNormals.put(WorldLayoutData.CUBE_NORMALS); cubeNormals.position(0); ByteBuffer mcbbVertices = ByteBuffer.allocateDirect(WorldLayoutData.MINI_CUBE_COORDS.length * 4); mcbbVertices.order(ByteOrder.nativeOrder()); miniCubeVertices = mcbbVertices.asFloatBuffer(); miniCubeVertices.put(WorldLayoutData.MINI_CUBE_COORDS); miniCubeVertices.position(0); ByteBuffer mcbbColors = ByteBuffer.allocateDirect(WorldLayoutData.MINI_CUBE_COLORS.length * 4); mcbbColors.order(ByteOrder.nativeOrder()); miniCubeColors = mcbbColors.asFloatBuffer(); miniCubeColors.put(WorldLayoutData.MINI_CUBE_COLORS); miniCubeColors.position(0); ByteBuffer mcbbNormals = ByteBuffer.allocateDirect(WorldLayoutData.CUBE_NORMALS.length * 4); mcbbNormals.order(ByteOrder.nativeOrder()); miniCubeNormals = mcbbNormals.asFloatBuffer(); miniCubeNormals.put(WorldLayoutData.CUBE_NORMALS); miniCubeNormals.position(0); // make a floor ByteBuffer bbFloorVertices = ByteBuffer.allocateDirect(WorldLayoutData.FLOOR_COORDS.length * 4); bbFloorVertices.order(ByteOrder.nativeOrder()); floorVertices = bbFloorVertices.asFloatBuffer(); floorVertices.put(WorldLayoutData.FLOOR_COORDS); floorVertices.position(0); ByteBuffer bbFloorNormals = ByteBuffer.allocateDirect(WorldLayoutData.FLOOR_NORMALS.length * 4); bbFloorNormals.order(ByteOrder.nativeOrder()); floorNormals = bbFloorNormals.asFloatBuffer(); floorNormals.put(WorldLayoutData.FLOOR_NORMALS); floorNormals.position(0); ByteBuffer bbFloorColors = ByteBuffer.allocateDirect(WorldLayoutData.FLOOR_COLORS.length * 4); bbFloorColors.order(ByteOrder.nativeOrder()); floorColors = bbFloorColors.asFloatBuffer(); floorColors.put(WorldLayoutData.FLOOR_COLORS); floorColors.position(0); int vertexShader = loadGLShader(GLES20.GL_VERTEX_SHADER, R.raw.light_vertex); int gridShader = loadGLShader(GLES20.GL_FRAGMENT_SHADER, R.raw.grid_fragment); int passthroughShader = loadGLShader(GLES20.GL_FRAGMENT_SHADER, R.raw.passthrough_fragment); cubeProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(cubeProgram, vertexShader); GLES20.glAttachShader(cubeProgram, passthroughShader); GLES20.glLinkProgram(cubeProgram); GLES20.glUseProgram(cubeProgram); checkGLError("Cube program"); cubePositionParam = GLES20.glGetAttribLocation(cubeProgram, "a_Position"); cubeNormalParam = GLES20.glGetAttribLocation(cubeProgram, "a_Normal"); cubeColorParam = GLES20.glGetAttribLocation(cubeProgram, "a_Color"); cubeModelParam = GLES20.glGetUniformLocation(cubeProgram, "u_Model"); cubeModelViewParam = GLES20.glGetUniformLocation(cubeProgram, "u_MVMatrix"); cubeModelViewProjectionParam = GLES20.glGetUniformLocation(cubeProgram, "u_MVP"); cubeLightPosParam = GLES20.glGetUniformLocation(cubeProgram, "u_LightPos"); GLES20.glEnableVertexAttribArray(cubePositionParam); GLES20.glEnableVertexAttribArray(cubeNormalParam); GLES20.glEnableVertexAttribArray(cubeColorParam); checkGLError("Cube program params"); //Minicube miniCubeProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(miniCubeProgram, vertexShader); GLES20.glAttachShader(miniCubeProgram, passthroughShader); GLES20.glLinkProgram(miniCubeProgram); GLES20.glUseProgram(miniCubeProgram); checkGLError("Cube program"); miniCubePositionParam = GLES20.glGetAttribLocation(miniCubeProgram, "a_Position"); miniCubeNormalParam = GLES20.glGetAttribLocation(miniCubeProgram, "a_Normal"); miniCubeColorParam = GLES20.glGetAttribLocation(miniCubeProgram, "a_Color"); miniCubeModelParam = GLES20.glGetUniformLocation(miniCubeProgram, "u_Model"); miniCubeModelViewParam = GLES20.glGetUniformLocation(miniCubeProgram, "u_MVMatrix"); miniCubeModelViewProjectionParam = GLES20.glGetUniformLocation(miniCubeProgram, "u_MVP"); miniCubeLightPosParam = GLES20.glGetUniformLocation(miniCubeProgram, "u_LightPos"); GLES20.glEnableVertexAttribArray(miniCubePositionParam); GLES20.glEnableVertexAttribArray(miniCubeNormalParam); GLES20.glEnableVertexAttribArray(miniCubeColorParam); checkGLError("Cube program params"); floorProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(floorProgram, vertexShader); GLES20.glAttachShader(floorProgram, gridShader); GLES20.glLinkProgram(floorProgram); GLES20.glUseProgram(floorProgram); checkGLError("Floor program"); floorModelParam = GLES20.glGetUniformLocation(floorProgram, "u_Model"); floorModelViewParam = GLES20.glGetUniformLocation(floorProgram, "u_MVMatrix"); floorModelViewProjectionParam = GLES20.glGetUniformLocation(floorProgram, "u_MVP"); floorLightPosParam = GLES20.glGetUniformLocation(floorProgram, "u_LightPos"); floorPositionParam = GLES20.glGetAttribLocation(floorProgram, "a_Position"); floorNormalParam = GLES20.glGetAttribLocation(floorProgram, "a_Normal"); floorColorParam = GLES20.glGetAttribLocation(floorProgram, "a_Color"); GLES20.glEnableVertexAttribArray(floorPositionParam); GLES20.glEnableVertexAttribArray(floorNormalParam); GLES20.glEnableVertexAttribArray(floorColorParam); checkGLError("Floor program params"); Matrix.setIdentityM(modelFloor, 0); Matrix.translateM(modelFloor, 0, 0, -floorDepth, 0); // Floor appears below user. // Avoid any delays during start-up due to decoding of sound files. new Thread(new Runnable() { public void run() { // Start spatial audio playback of SOUND_FILE at the model postion. The returned //soundId handle is stored and allows for repositioning the sound object whenever // the cube position changes. cardboardAudioEngine.preloadSoundFile(SOUND_FILE); soundId = cardboardAudioEngine.createSoundObject(SOUND_FILE); cardboardAudioEngine.setSoundObjectPosition(soundId, modelPosition[0], modelPosition[1], modelPosition[2]); cardboardAudioEngine.playSound(soundId, true /* looped playback */); } }).start(); updateModelPosition(); checkGLError("onSurfaceCreated"); } /** * Updates the cube model position. */ private void updateModelPosition() { Matrix.setIdentityM(modelCube, 0); Matrix.translateM(modelCube, 0, modelPosition[0], modelPosition[1], modelPosition[2]); // Update the sound location to match it with the new cube position. if (soundId != CardboardAudioEngine.INVALID_ID) { cardboardAudioEngine.setSoundObjectPosition(soundId, modelPosition[0], modelPosition[1], modelPosition[2]); } checkGLError("updateCubePosition"); } private float[] quaternionToMatrix(float[] _data, float x, float y, float z, float w) { float x2, y2, z2, xx, xy, xz, yy, yz, zz, wx, wy, wz; //if(null == _data) //_data = new float[16]; // calculate coefficients x2 = x + x; y2 = y + y; z2 = z + z; xx = x * x2; xy = x * y2; xz = x * z2; yy = y * y2; yz = y * z2; zz = z * z2; wx = w * x2; wy = w * y2; wz = w * z2; _data[0] = 1.0f - (yy + zz); _data[1] = xy - wz; _data[2] = xz + wy; _data[3] = 0.0f; _data[4] = xy + wz; _data[5] = 1.0f - (xx + zz); _data[6] = yz - wx; _data[7] = 0.0f; _data[8] = xz - wy; _data[9] = yz + wx; _data[10] = 1.0f - (xx + yy); _data[11] = 0.0f; _data[12] = 0.0f; _data[13] = 0.0f; _data[14] = 0.0f; _data[15] = 1.0f; return _data; } //Takes quaternion private void updateMiniCubePosition(float x, float y, float z, float w) { //Matrix.setIdentityM(modelMiniCube, 0); //We normalize the distance as well //Matrix.translateM(modelMiniCube, 0, handPos[0]/(float)50.0, handPos[1]/(float)50.0, handPos[2]/(float)50.0); //This accounts for the reversing //Matrix.translateM(modelMiniCube, 0, -handPos[0]/(float)50.0, -handPos[2]/(float)50.0, -handPos[1]/(float)50.0); float[] temp_modelMiniCube = new float[16]; float[] temp_mRotate = new float[16]; Matrix.setIdentityM(temp_modelMiniCube, 0); Matrix.translateM(temp_modelMiniCube, 0, -handPos[0] / (float) 50.0, -handPos[2] / (float) 50.0, -handPos[1] / (float) 50.0); //Matrix.setIdentityM(temp_mRotate, 0); //Matrix.rotateM(temp_mRotate, 0, 45, 1, 0, 0); //This rotates the cube quaternionToMatrix(temp_mRotate, -x, -y, -z, w); Matrix.multiplyMM(modelMiniCube, 0, temp_mRotate, 0, temp_modelMiniCube, 0); Log.i("Armo", String.format("%f", -handPos[1] / (float) 50)); if (-handPos[1] / (float) 50 < -WALL_DIST + 1) { //offset because this is cube center, we want the wall position sendArmoRequest(true); Log.i("Armo", "Sending_lock"); } else { sendArmoRequest(false); Log.i("Armo", "Sending_unlock"); } } private void updateMiniCubePosition(float pitch, float yaw, float roll) { float[] temp_modelMiniCube = new float[16]; float[] temp_mRotate = new float[16]; Matrix.setIdentityM(temp_modelMiniCube, 0); Matrix.translateM(temp_modelMiniCube, 0, -handPos[0] / (float) 50.0, -handPos[2] / (float) 50.0, -handPos[1] / (float) 50.0); Matrix.setIdentityM(temp_mRotate, 0); Matrix.rotateM(temp_mRotate, 0, 45, 1, 0, 0); //This rotates the cube //quaternionToMatrix(temp_mRotate, x, y, z, w); Matrix.multiplyMM(modelMiniCube, 0, temp_mRotate, 0, temp_modelMiniCube, 0); } /** * Converts a raw text file into a string. * * @param resId The resource ID of the raw text file about to be turned into a shader. * @return The context of the text file, or null in case of error. */ private String readRawTextFile(int resId) { InputStream inputStream = getResources().openRawResource(resId); try { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); } reader.close(); return sb.toString(); } catch (IOException e) { e.printStackTrace(); } return null; } /** * Prepares OpenGL ES before we draw a frame. * * @param headTransform The head transformation in the new frame. */ @Override public void onNewFrame(HeadTransform headTransform) { // Build the Model part of the ModelView matrix. //Matrix.rotateM(modelCube, 0, TIME_DELTA, 0.5f, 0.5f, 1.0f); // Build the camera matrix and apply it to the ModelView. Matrix.setLookAtM(camera, 0, 0.0f, 0.0f, CAMERA_Z, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); headTransform.getHeadView(headView, 0); // Update the 3d audio engine with the most recent head rotation. headTransform.getQuaternion(headRotation, 0); cardboardAudioEngine.setHeadRotation(headRotation[0], headRotation[1], headRotation[2], headRotation[3]); checkGLError("onReadyToDraw"); //float[] headAngles = new float[3]; //headTransform.getEulerAngles(headAngles, 0) //float[] quat = new float[4]; //headTransform.getQuaternion(quat, 0); updateMiniCubePosition(headRotation[0], headRotation[1], headRotation[2], headRotation[3]); } /** * Draws a frame for an eye. * * @param eye The eye to render. Includes all required transformations. */ @Override public void onDrawEye(Eye eye) { GLES20.glEnable(GLES20.GL_DEPTH_TEST); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); checkGLError("colorParam"); // Apply the eye transformation to the camera. Matrix.multiplyMM(view, 0, eye.getEyeView(), 0, camera, 0); // Set the position of the light Matrix.multiplyMV(lightPosInEyeSpace, 0, view, 0, LIGHT_POS_IN_WORLD_SPACE, 0); // Build the ModelView and ModelViewProjection matrices // for calculating cube position and light. float[] perspective = eye.getPerspective(Z_NEAR, Z_FAR); Matrix.multiplyMM(modelView, 0, view, 0, modelCube, 0); Matrix.multiplyMM(modelViewProjection, 0, perspective, 0, modelView, 0); drawCube(); float[] mRotationCube = new float[16]; //Not sure if we can just reuse modelView like this Matrix.multiplyMM(modelView, 0, view, 0, modelMiniCube, 0); //Matrix.multiplyMM(mRotationCube, 0, view, 0, modelView, 0); //Not working //float[] ident = new Matrix(eye.getPerspective(Z_NEAR, Z_FAR)); //Matrix.setIdentityM(ident, 0); //Cheating so that we have relative hand movement //end not working Matrix.multiplyMM(modelViewProjection, 0, perspective, 0, modelView, 0); //Matrix.multiplyMM(modelViewProjection, 0, perspective, 0, mRotationCube, 0); //doesn't work :( //Matrix.multiplyMM(modelViewProjection, 0, perspective, 0, modelMiniCube, 0); drawMiniCube(); // Set modelView for the floor, so we draw floor in the correct location Matrix.multiplyMM(modelView, 0, view, 0, modelFloor, 0); Matrix.multiplyMM(modelViewProjection, 0, perspective, 0, modelView, 0); drawFloor(); } @Override public void onFinishFrame(Viewport viewport) { } /** * Draw the cube. * * <p>We've set all of our transformation matrices. Now we simply pass them into the shader. */ public void drawCube() { GLES20.glUseProgram(cubeProgram); GLES20.glUniform3fv(cubeLightPosParam, 1, lightPosInEyeSpace, 0); // Set the Model in the shader, used to calculate lighting GLES20.glUniformMatrix4fv(cubeModelParam, 1, false, modelCube, 0); // Set the ModelView in the shader, used to calculate lighting GLES20.glUniformMatrix4fv(cubeModelViewParam, 1, false, modelView, 0); // Set the position of the cube GLES20.glVertexAttribPointer(cubePositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, cubeVertices); // Set the ModelViewProjection matrix in the shader. GLES20.glUniformMatrix4fv(cubeModelViewProjectionParam, 1, false, modelViewProjection, 0); // Set the normal positions of the cube, again for shading GLES20.glVertexAttribPointer(cubeNormalParam, 3, GLES20.GL_FLOAT, false, 0, cubeNormals); GLES20.glVertexAttribPointer(cubeColorParam, 4, GLES20.GL_FLOAT, false, 0, isLookingAtObject() ? cubeFoundColors : cubeColors); GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 36); checkGLError("Drawing cube"); } public void drawMiniCube() { GLES20.glUseProgram(miniCubeProgram); GLES20.glUniform3fv(miniCubeLightPosParam, 1, lightPosInEyeSpace, 0); // Set the Model in the shader, used to calculate lighting GLES20.glUniformMatrix4fv(miniCubeModelParam, 1, false, modelMiniCube, 0); // Set the ModelView in the shader, used to calculate lighting GLES20.glUniformMatrix4fv(miniCubeModelViewParam, 1, false, modelView, 0); // Set the position of the miniCube GLES20.glVertexAttribPointer(miniCubePositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, miniCubeVertices); // Set the ModelViewProjection matrix in the shader. GLES20.glUniformMatrix4fv(miniCubeModelViewProjectionParam, 1, false, modelViewProjection, 0); // Set the normal positions of the miniCube, again for shading GLES20.glVertexAttribPointer(miniCubeNormalParam, 3, GLES20.GL_FLOAT, false, 0, miniCubeNormals); GLES20.glVertexAttribPointer(miniCubeColorParam, 4, GLES20.GL_FLOAT, false, 0, miniCubeColors); GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 36); checkGLError("Drawing miniCube"); } /** * Draw the floor. * * <p>This feeds in data for the floor into the shader. Note that this doesn't feed in data about * position of the light, so if we rewrite our code to draw the floor first, the lighting might * look strange. */ public void drawFloor() { GLES20.glUseProgram(floorProgram); // Set ModelView, MVP, position, normals, and color. GLES20.glUniform3fv(floorLightPosParam, 1, lightPosInEyeSpace, 0); GLES20.glUniformMatrix4fv(floorModelParam, 1, false, modelFloor, 0); GLES20.glUniformMatrix4fv(floorModelViewParam, 1, false, modelView, 0); GLES20.glUniformMatrix4fv(floorModelViewProjectionParam, 1, false, modelViewProjection, 0); GLES20.glVertexAttribPointer(floorPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, floorVertices); GLES20.glVertexAttribPointer(floorNormalParam, 3, GLES20.GL_FLOAT, false, 0, floorNormals); GLES20.glVertexAttribPointer(floorColorParam, 4, GLES20.GL_FLOAT, false, 0, floorColors); GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6); checkGLError("drawing floor"); } /** * Called when the Cardboard trigger is pulled. */ @Override public void onCardboardTrigger() { Log.i(TAG, "onCardboardTrigger"); if (isLookingAtObject()) { score++; overlayView.show3DToast("Found it! Look around for another one.\nScore = " + score); //hideObject(); } else { overlayView.show3DToast("Look around to find the object!"); } // Always give user feedback. vibrator.vibrate(50); } /** * Find a new random position for the object. * * <p>We'll rotate it around the Y-axis so it's out of sight, and then up or down by a little bit. */ private void hideObject() { float[] rotationMatrix = new float[16]; float[] posVec = new float[4]; // First rotate in XZ plane, between 90 and 270 deg away, and scale so that we vary // the object's distance from the user. float angleXZ = (float) Math.random() * 180 + 90; Matrix.setRotateM(rotationMatrix, 0, angleXZ, 0f, 1f, 0f); float oldObjectDistance = objectDistance; objectDistance = (float) Math.random() * (MAX_MODEL_DISTANCE - MIN_MODEL_DISTANCE) + MIN_MODEL_DISTANCE; float objectScalingFactor = objectDistance / oldObjectDistance; Matrix.scaleM(rotationMatrix, 0, objectScalingFactor, objectScalingFactor, objectScalingFactor); Matrix.multiplyMV(posVec, 0, rotationMatrix, 0, modelCube, 12); float angleY = (float) Math.random() * 80 - 40; // Angle in Y plane, between -40 and 40. angleY = (float) Math.toRadians(angleY); float newY = (float) Math.tan(angleY) * objectDistance; modelPosition[0] = posVec[0]; modelPosition[1] = newY; modelPosition[2] = posVec[2]; updateModelPosition(); } /** * Check if user is looking at object by calculating where the object is in eye-space. * * @return true if the user is looking at the object. */ private boolean isLookingAtObject() { float[] initVec = { 0, 0, 0, 1.0f }; float[] objPositionVec = new float[4]; // Convert object space to camera space. Use the headView from onNewFrame. Matrix.multiplyMM(modelView, 0, headView, 0, modelCube, 0); Matrix.multiplyMV(objPositionVec, 0, modelView, 0, initVec, 0); float pitch = (float) Math.atan2(objPositionVec[1], -objPositionVec[2]); float yaw = (float) Math.atan2(objPositionVec[0], -objPositionVec[2]); return Math.abs(pitch) < PITCH_LIMIT && Math.abs(yaw) < YAW_LIMIT; } private void connectWebSocket() { URI uri; try { //uri = new URI("ws://192.168.0.105:6437/"); uri = new URI("ws://172.20.10.4:6437/"); } catch (URISyntaxException e) { e.printStackTrace(); return; } mWebSocketClient = new WebSocketClient(uri) { @Override public void onOpen(ServerHandshake serverHandshake) { Log.i("Websocket", "Opened"); } @Override public void onMessage(String s) { final String message = s; //Log.i("Websocket_message", s); try { JSONObject obj = new JSONObject(s); JSONArray pos = obj.getJSONArray("hands").getJSONObject(0).getJSONArray("palmPosition"); handPos[0] = (float) pos.getDouble(0); handPos[1] = (float) pos.getDouble(1); handPos[2] = (float) pos.getDouble(2); Log.i("Websocket_json", String.format("(%f, %f, %f)", handPos[0], handPos[1], handPos[2])); } catch (JSONException e) { Log.i("Websocket_json", "Not the right json obj"); } } @Override public void onClose(int i, String s, boolean b) { Log.i("Websocket", "Closed " + s); } @Override public void onError(Exception e) { Log.i("Websocket", "Error " + e.getMessage()); } }; mWebSocketClient.connect(); } //private String ARMO_HOST = "192.168.0.107"; private String ARMO_HOST = "172.20.10.5"; private boolean armo_locked = false; private void sendArmoRequest(boolean _lock) { final boolean lock = _lock; if (lock == false) { if (armo_locked == false) { return; } } if (lock) { if (armo_locked == true) { return; } } new Thread(new Runnable() { public void run() { try { String host = ARMO_HOST; Socket socket = new Socket(host, 5000); String request = "GET /position/180 HTTP/1.0\r\n\r\n"; if (lock == false) { if (armo_locked == false) { return; } armo_locked = false; } if (lock) { if (armo_locked == true) { return; } request = "GET /position/85 HTTP/1.0\r\n\r\n"; armo_locked = true; } OutputStream os = socket.getOutputStream(); os.write(request.getBytes()); os.flush(); InputStream is = socket.getInputStream(); int ch; while ((ch = is.read()) != -1) System.out.print((char) ch); socket.close(); } catch (IOException e) { Log.e("Lock_request", "Something went wrong: " + e.getMessage()); } } }).start(); } }