From 23aed137435d5e1d87d197c7ae483e5c49afd569 Mon Sep 17 00:00:00 2001 From: shichengfeng Date: Tue, 1 Sep 2015 14:17:56 -0700 Subject: Android Chromoting: Create new namespace cardboard for all Cardboard desktop activity related classes. BUG=516871 Review URL: https://codereview.chromium.org/1328543002 Cr-Commit-Position: refs/heads/master@{#346735} --- .../chromoting/CardboardActivityDesktop.java | 229 ------------- .../chromoting/CardboardActivityEyePoint.java | 77 ----- .../chromoting/CardboardActivityMenuBar.java | 105 ------ .../chromoting/CardboardActivityMenuItem.java | 176 ---------- .../chromoting/CardboardActivitySkybox.java | 238 ------------- .../chromoting/CardboardActivityUtility.java | 43 --- .../chromoting/CardboardDesktopActivity.java | 193 ----------- .../chromoting/CardboardDesktopRenderer.java | 373 --------------------- .../java/src/org/chromium/chromoting/Desktop.java | 3 +- .../src/org/chromium/chromoting/ShaderHelper.java | 103 ------ .../src/org/chromium/chromoting/TextureHelper.java | 85 ----- .../chromoting/cardboard/CardboardRenderer.java | 373 +++++++++++++++++++++ .../chromoting/cardboard/CardboardUtil.java | 43 +++ .../org/chromium/chromoting/cardboard/Desktop.java | 229 +++++++++++++ .../chromoting/cardboard/DesktopActivity.java | 195 +++++++++++ .../chromium/chromoting/cardboard/EyePoint.java | 77 +++++ .../org/chromium/chromoting/cardboard/MenuBar.java | 107 ++++++ .../chromium/chromoting/cardboard/MenuItem.java | 176 ++++++++++ .../chromoting/cardboard/ShaderHelper.java | 103 ++++++ .../org/chromium/chromoting/cardboard/Skybox.java | 239 +++++++++++++ .../chromoting/cardboard/TextureHelper.java | 85 +++++ 21 files changed, 1629 insertions(+), 1623 deletions(-) delete mode 100644 remoting/android/java/src/org/chromium/chromoting/CardboardActivityDesktop.java delete mode 100644 remoting/android/java/src/org/chromium/chromoting/CardboardActivityEyePoint.java delete mode 100644 remoting/android/java/src/org/chromium/chromoting/CardboardActivityMenuBar.java delete mode 100644 remoting/android/java/src/org/chromium/chromoting/CardboardActivityMenuItem.java delete mode 100644 remoting/android/java/src/org/chromium/chromoting/CardboardActivitySkybox.java delete mode 100644 remoting/android/java/src/org/chromium/chromoting/CardboardActivityUtility.java delete mode 100644 remoting/android/java/src/org/chromium/chromoting/CardboardDesktopActivity.java delete mode 100644 remoting/android/java/src/org/chromium/chromoting/CardboardDesktopRenderer.java delete mode 100644 remoting/android/java/src/org/chromium/chromoting/ShaderHelper.java delete mode 100644 remoting/android/java/src/org/chromium/chromoting/TextureHelper.java create mode 100644 remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java create mode 100644 remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardUtil.java create mode 100644 remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java create mode 100644 remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java create mode 100644 remoting/android/java/src/org/chromium/chromoting/cardboard/EyePoint.java create mode 100644 remoting/android/java/src/org/chromium/chromoting/cardboard/MenuBar.java create mode 100644 remoting/android/java/src/org/chromium/chromoting/cardboard/MenuItem.java create mode 100644 remoting/android/java/src/org/chromium/chromoting/cardboard/ShaderHelper.java create mode 100644 remoting/android/java/src/org/chromium/chromoting/cardboard/Skybox.java create mode 100644 remoting/android/java/src/org/chromium/chromoting/cardboard/TextureHelper.java (limited to 'remoting/android/java/src/org/chromium') diff --git a/remoting/android/java/src/org/chromium/chromoting/CardboardActivityDesktop.java b/remoting/android/java/src/org/chromium/chromoting/CardboardActivityDesktop.java deleted file mode 100644 index f420488..0000000 --- a/remoting/android/java/src/org/chromium/chromoting/CardboardActivityDesktop.java +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chromoting; - -import static org.chromium.chromoting.CardboardActivityUtility.makeFloatBuffer; -import static org.chromium.chromoting.CardboardActivityUtility.makeRectangularTextureBuffer; - -import android.graphics.Bitmap; -import android.graphics.Point; -import android.opengl.GLES20; - -import org.chromium.chromoting.jni.JniInterface; - -import java.nio.FloatBuffer; - -/** - * Chromoting Cardboard activity desktop, which is used to display host desktop. - */ -public class CardboardActivityDesktop { - private static final String VERTEX_SHADER = - "uniform mat4 u_CombinedMatrix;" - + "attribute vec4 a_Position;" - + "attribute vec2 a_TexCoordinate;" - + "varying vec2 v_TexCoordinate;" - + "void main() {" - + " v_TexCoordinate = a_TexCoordinate;" - + " gl_Position = u_CombinedMatrix * a_Position;" - + "}"; - - private static final String FRAGMENT_SHADER = - "precision highp float;" - + "uniform sampler2D u_Texture;" - + "varying vec2 v_TexCoordinate;" - + "const float borderWidth = 0.002;" - + "void main() {" - + " if (v_TexCoordinate.x > (1.0 - borderWidth) || v_TexCoordinate.x < borderWidth" - + " || v_TexCoordinate.y > (1.0 - borderWidth)" - + " || v_TexCoordinate.y < borderWidth) {" - + " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);" - + " } else {" - + " gl_FragColor = texture2D(u_Texture, v_TexCoordinate);" - + " }" - + "}"; - - private static final FloatBuffer TEXTURE_COORDINATES = makeRectangularTextureBuffer( - 0.0f, 1.0f, 0.0f, 1.0f); - - private static final int POSITION_DATA_SIZE = 3; - private static final int TEXTURE_COORDINATE_DATA_SIZE = 2; - - // Fix the desktop height and adjust width accordingly. - private static final float HALF_HEIGHT = 1.0f; - - // Number of vertices passed to glDrawArrays(). - private static final int VERTICES_NUMBER = 6; - - private int mVertexShaderHandle; - private int mFragmentShaderHandle; - private int mProgramHandle; - private int mCombinedMatrixHandle; - private int mTextureUniformHandle; - private int mPositionHandle; - private int mTextureDataHandle; - private int mTextureCoordinateHandle; - private FloatBuffer mPosition; - private float mHalfWidth; - - // Lock to allow multithreaded access to mHalfWidth. - private final Object mHalfWidthLock = new Object(); - - private Bitmap mVideoFrame; - - // Lock to allow multithreaded access to mVideoFrame. - private final Object mVideoFrameLock = new Object(); - - // Flag to indicate whether to reload the desktop texture. - private boolean mReloadTexture; - - // Lock to allow multithreaded access to mReloadTexture. - private final Object mReloadTextureLock = new Object(); - - public CardboardActivityDesktop() { - mVertexShaderHandle = - ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); - mFragmentShaderHandle = - ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER); - mProgramHandle = ShaderHelper.createAndLinkProgram(mVertexShaderHandle, - mFragmentShaderHandle, new String[] {"a_Position", "a_TexCoordinate", - "u_CombinedMatrix", "u_Texture"}); - mCombinedMatrixHandle = - GLES20.glGetUniformLocation(mProgramHandle, "u_CombinedMatrix"); - mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture"); - mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position"); - mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate"); - mTextureDataHandle = TextureHelper.createTextureHandle(); - } - - /** - * Draw the desktop. Make sure {@link hasVideoFrame} returns true. - */ - public void draw(float[] combinedMatrix) { - GLES20.glUseProgram(mProgramHandle); - - // Pass in model view project matrix. - GLES20.glUniformMatrix4fv(mCombinedMatrixHandle, 1, false, combinedMatrix, 0); - - // Pass in texture coordinate. - GLES20.glVertexAttribPointer(mTextureCoordinateHandle, TEXTURE_COORDINATE_DATA_SIZE, - GLES20.GL_FLOAT, false, 0, TEXTURE_COORDINATES); - GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle); - - GLES20.glVertexAttribPointer(mPositionHandle, POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, - 0, mPosition); - GLES20.glEnableVertexAttribArray(mPositionHandle); - - // Link texture data with texture unit. - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle); - GLES20.glUniform1i(mTextureUniformHandle, 0); - - GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, VERTICES_NUMBER); - } - - /** - * Update the desktop frame data based on the mVideoFrame. Note here we fix the - * height of the desktop and vary width accordingly. - */ - private void updateVideoFrame(Bitmap videoFrame) { - float newHalfDesktopWidth; - synchronized (mVideoFrameLock) { - mVideoFrame = videoFrame; - TextureHelper.linkTexture(mTextureDataHandle, videoFrame); - newHalfDesktopWidth = videoFrame.getWidth() * HALF_HEIGHT / videoFrame.getHeight(); - } - - synchronized (mHalfWidthLock) { - if (Math.abs(mHalfWidth - newHalfDesktopWidth) > 0.0001) { - mHalfWidth = newHalfDesktopWidth; - mPosition = makeFloatBuffer(new float[] { - // Desktop model coordinates. - -mHalfWidth, HALF_HEIGHT, 0.0f, - -mHalfWidth, -HALF_HEIGHT, 0.0f, - mHalfWidth, HALF_HEIGHT, 0.0f, - -mHalfWidth, -HALF_HEIGHT, 0.0f, - mHalfWidth, -HALF_HEIGHT, 0.0f, - mHalfWidth, HALF_HEIGHT, 0.0f - }); - } - } - } - - /** - * Clean up opengl data. - */ - public void cleanup() { - GLES20.glDeleteShader(mVertexShaderHandle); - GLES20.glDeleteShader(mFragmentShaderHandle); - GLES20.glDeleteTextures(1, new int[] {mTextureDataHandle}, 0); - } - - /** - * Return true if video frame data are already loaded in. - */ - public boolean hasVideoFrame() { - synchronized (mVideoFrameLock) { - return mVideoFrame != null; - } - } - - public float getHalfHeight() { - return HALF_HEIGHT; - } - - public float getHalfWidth() { - synchronized (mHalfWidthLock) { - return mHalfWidth; - } - } - - /** - * Get desktop height and width in pixels. - */ - public Point getFrameSizePixels() { - synchronized (mVideoFrameLock) { - return new Point(mVideoFrame == null ? 0 : mVideoFrame.getHeight(), - mVideoFrame == null ? 0 : mVideoFrame.getWidth()); - } - } - - /** - * Link desktop texture if {@link reloadTexture} was previously called. - * Invoked from {@link com.google.vrtoolkit.cardboard.CardboardView.StereoRenderer.onNewFrame} - * so that both eyes will have the same texture. - */ - public void maybeLoadDesktopTexture() { - synchronized (mReloadTextureLock) { - if (!mReloadTexture) { - return; - } - } - - // TODO(shichengfeng): Record the time desktop drawing takes. - Bitmap bitmap = JniInterface.getVideoFrame(); - - if (bitmap == null) { - // This can happen if the client is connected, but a complete video frame has not yet - // been decoded. - return; - } - - updateVideoFrame(bitmap); - - synchronized (mReloadTextureLock) { - mReloadTexture = false; - } - } - - /** - * Inform this object that a new video frame should be rendered. - * Called from native display thread. - */ - public void reloadTexture() { - synchronized (mReloadTextureLock) { - mReloadTexture = true; - } - } -} \ No newline at end of file diff --git a/remoting/android/java/src/org/chromium/chromoting/CardboardActivityEyePoint.java b/remoting/android/java/src/org/chromium/chromoting/CardboardActivityEyePoint.java deleted file mode 100644 index 0312cad..0000000 --- a/remoting/android/java/src/org/chromium/chromoting/CardboardActivityEyePoint.java +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chromoting; - -import android.opengl.GLES20; - -/** - * Chromoting Cardboard activity eye point, which represents the location on the desktop - * where user is looking at. - */ -public class CardboardActivityEyePoint { - private static final String VERTEX_SHADER = - "uniform mat4 u_CombinedMatrix;" - + "attribute vec4 a_EyePosition;" - + "void main() {" - + " gl_Position = u_CombinedMatrix * a_EyePosition;" - + " gl_PointSize = 3.0;" - + "}"; - - private static final String FRAGMENT_SHADER = - "precision mediump float;" - + "void main() {" - + " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);" - + "}"; - - // Size of the vertexes to draw eye point. - private static final int VERTEXES_NUMBER = 1; - - private int mVertexShaderHandle; - private int mFragmentShaderHandle; - private int mProgramHandle; - private int mCombinedMatrixHandle; - private int mPositionHandle; - - public CardboardActivityEyePoint() { - // Set handlers for eye point drawing. - mVertexShaderHandle = - ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); - mFragmentShaderHandle = - ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER); - mProgramHandle = ShaderHelper.createAndLinkProgram(mVertexShaderHandle, - mFragmentShaderHandle, new String[] {"a_EyePosition", "u_CombinedMatrix"}); - mPositionHandle = - GLES20.glGetAttribLocation(mProgramHandle, "a_EyePosition"); - mCombinedMatrixHandle = - GLES20.glGetUniformLocation(mProgramHandle, "u_CombinedMatrix"); - } - - /** - * Draw the eye point based on given model view projection matrix. - */ - public void draw(float[] combinedMatrix) { - GLES20.glUseProgram(mProgramHandle); - - // Set the eye point in front of desktop. - GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT); - - GLES20.glUniformMatrix4fv(mCombinedMatrixHandle, 1, false, combinedMatrix, 0); - - GLES20.glVertexAttrib4f(mPositionHandle, 0.0f, 0.0f, 0.0f, 1.0f); - - // Since we are not using a buffer object, disable vertex arrays for this attribute. - GLES20.glDisableVertexAttribArray(mPositionHandle); - - GLES20.glDrawArrays(GLES20.GL_POINTS, 0, VERTEXES_NUMBER); - } - - /** - * Clean up opengl data. - */ - public void cleanup() { - GLES20.glDeleteShader(mVertexShaderHandle); - GLES20.glDeleteShader(mFragmentShaderHandle); - } -} \ No newline at end of file diff --git a/remoting/android/java/src/org/chromium/chromoting/CardboardActivityMenuBar.java b/remoting/android/java/src/org/chromium/chromoting/CardboardActivityMenuBar.java deleted file mode 100644 index 37b1436..0000000 --- a/remoting/android/java/src/org/chromium/chromoting/CardboardActivityMenuBar.java +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chromoting; - -import android.content.Context; -import android.graphics.PointF; -import android.graphics.RectF; -import android.opengl.Matrix; - -/** - * Cardboard activity menu bar that contains multiple menu items. - */ -public class CardboardActivityMenuBar { - public enum MenuItemType { - HOME(R.drawable.ic_home), - VOICE_INPUT(R.drawable.ic_voice_input), - MOVE_FORWARD(R.drawable.ic_move_forward), - MOVE_BACKWARD(R.drawable.ic_move_backward); - - private final int mResourceId; - - MenuItemType(int resourceId) { - mResourceId = resourceId; - } - - public int resourceId() { - return mResourceId; - } - } - - public static final float MENU_ITEM_SIZE = 0.2f; - - private final RectF mMenuBarRect; - - private final CardboardActivityMenuItem[] mItems; - - private float[] mModelMatrix; - private float[] mCombinedMatrix; - - public CardboardActivityMenuBar(Context context) { - MenuItemType[] menuItemTypes = MenuItemType.values(); - final int numItem = menuItemTypes.length; - mCombinedMatrix = new float[16]; - mModelMatrix = new float[16]; - - final float halfMenuWidth = numItem * MENU_ITEM_SIZE / 2; - final float halfMenuHeight = MENU_ITEM_SIZE / 2; - mMenuBarRect = new RectF(-halfMenuWidth, -halfMenuHeight, halfMenuWidth, halfMenuHeight); - - RectF currentRect = new RectF(-halfMenuWidth, -halfMenuHeight, - -halfMenuWidth + MENU_ITEM_SIZE, halfMenuHeight); - mItems = new CardboardActivityMenuItem[numItem]; - for (int i = 0; i < numItem; i++) { - mItems[i] = new CardboardActivityMenuItem(context, menuItemTypes[i], currentRect); - currentRect.offset(MENU_ITEM_SIZE, 0); - } - } - - /** - * Get menu item that user is looking at. - * Return the CardboardActivity menu item that contains the passed in coordinates or - * null if none of menu items contains the passed in coordinates. - */ - public CardboardActivityMenuItem getLookingItem(PointF lookingPosition) { - for (CardboardActivityMenuItem item : mItems) { - if (item.contains(lookingPosition)) { - return item; - } - } - - return null; - } - - public void draw(float[] viewMatrix, float[] projectionMatrix, PointF eyeMenuBarPosition, - float centerX, float centerY, float centerZ) { - for (CardboardActivityMenuItem item : mItems) { - Matrix.setIdentityM(mModelMatrix, 0); - Matrix.translateM(mModelMatrix, 0, item.getPosition().x + centerX, - item.getPosition().y + centerY, centerZ); - Matrix.multiplyMM(mCombinedMatrix, 0, viewMatrix, 0, mModelMatrix, 0); - Matrix.multiplyMM(mCombinedMatrix, 0, projectionMatrix, 0, mCombinedMatrix, 0); - item.draw(mCombinedMatrix, item.contains(eyeMenuBarPosition)); - } - } - - /** - * Return true if the passed in point is inside the menu bar. - * @param x x coordinate user is looking at. - * @param y y coordinate user is looking at. - */ - public boolean contains(PointF lookingPosition) { - return mMenuBarRect.contains(lookingPosition.x, lookingPosition.y); - } - - /** - * Clean up opengl data. - */ - public void cleanup() { - for (CardboardActivityMenuItem item : mItems) { - item.clean(); - } - } -} \ No newline at end of file diff --git a/remoting/android/java/src/org/chromium/chromoting/CardboardActivityMenuItem.java b/remoting/android/java/src/org/chromium/chromoting/CardboardActivityMenuItem.java deleted file mode 100644 index a12fa5d..0000000 --- a/remoting/android/java/src/org/chromium/chromoting/CardboardActivityMenuItem.java +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chromoting; - -import static org.chromium.chromoting.CardboardActivityUtility.makeFloatBuffer; -import static org.chromium.chromoting.CardboardActivityUtility.makeRectangularTextureBuffer; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.PointF; -import android.graphics.RectF; -import android.opengl.GLES20; - -import org.chromium.chromoting.CardboardActivityMenuBar.MenuItemType; - -import java.nio.FloatBuffer; - -/** - * Cardboard activity menu item representing a corresponding function. - */ -public class CardboardActivityMenuItem { - private static final String VERTEX_SHADER = - "uniform mat4 u_CombinedMatrix;" - + "attribute vec4 a_Position;" - + "attribute vec2 a_TexCoordinate;" - + "varying vec2 v_TexCoordinate;" - + "attribute float a_selected;" - + "varying float v_selected;" - + "void main() {" - + " v_selected = a_selected;" - + " v_TexCoordinate = a_TexCoordinate;" - + " gl_Position = u_CombinedMatrix * a_Position;" - + "}"; - - private static final String FRAGMENT_SHADER = - "precision mediump float;" - + "uniform sampler2D u_Texture;" - + "varying vec2 v_TexCoordinate;" - + "varying float v_selected;" - + "void main() {" - + " vec4 texture = texture2D(u_Texture, v_TexCoordinate);" - + " if (v_selected > 0.5) {" - + " gl_FragColor = vec4(2.0, texture.g, texture.b, texture.a);" - + " } else {" - + " gl_FragColor = texture;" - + " }" - + "}"; - - private static final FloatBuffer TEXTURE_COORDINATES = makeRectangularTextureBuffer( - 0.0f, 1.0f, 0.0f, 1.0f); - - private static final int POSITION_COORDINATE_DATA_SIZE = 3; - private static final int TEXTURE_COORDINATE_DATA_SIZE = 2; - - // Number of vertices passed to glDrawArrays(). - private static final int VERTICES_NUMBER = 6; - - private final FloatBuffer mPositionCoordinates; - - private int mVertexShaderHandle; - private int mFragmentShaderHandle; - private int mProgramHandle; - private int mCombinedMatrixHandle; - private int mTextureUniformHandle; - private int mPositionHandle; - private int mTextureDataHandle; - private int mTextureCoordinateHandle; - private int mItemSelectedHandle; - - private MenuItemType mType; - - private RectF mRect; - - public CardboardActivityMenuItem(Context context, MenuItemType type, RectF rect) { - mType = type; - mRect = new RectF(rect); - float halfHeight = mRect.height() / 2; - float halfWidth = mRect.width() / 2; - mPositionCoordinates = makeFloatBuffer(new float[] { - // Desktop model coordinates. - -halfWidth, halfHeight, 0.0f, - -halfWidth, -halfHeight, 0.0f, - halfWidth, halfHeight, 0.0f, - -halfWidth, -halfHeight, 0.0f, - halfWidth, -halfHeight, 0.0f, - halfWidth, halfHeight, 0.0f - }); - - mVertexShaderHandle = - ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); - mFragmentShaderHandle = - ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER); - mProgramHandle = ShaderHelper.createAndLinkProgram(mVertexShaderHandle, - mFragmentShaderHandle, new String[] {"a_Position", "a_TexCoordinate", - "a_selected", "u_CombinedMatrix", "u_Texture"}); - - mCombinedMatrixHandle = - GLES20.glGetUniformLocation(mProgramHandle, "u_CombinedMatrix"); - mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture"); - mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position"); - mItemSelectedHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_selected"); - mTextureDataHandle = TextureHelper.createTextureHandle(); - mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate"); - - Bitmap texture = BitmapFactory.decodeResource(context.getResources(), - type.resourceId()); - TextureHelper.linkTexture(mTextureDataHandle, texture); - texture.recycle(); - } - - /** - * Return the type of this menu item. - */ - public MenuItemType getType() { - return mType; - } - - /** - * Return the position of the center of this menu item. - */ - public PointF getPosition() { - return new PointF(mRect.centerX(), mRect.centerY()); - } - - /** - * Return true if the point is inside this menu item. - */ - public boolean contains(PointF point) { - return mRect.contains(point.x, point.y); - } - - /** - * Draw menu item according to the given model view projection matrix. - */ - public void draw(float[] combinedMatrix, boolean selected) { - GLES20.glUseProgram(mProgramHandle); - - // Pass in model view project matrix. - GLES20.glUniformMatrix4fv(mCombinedMatrixHandle, 1, false, combinedMatrix, 0); - - // Pass in whether the item is selected - GLES20.glVertexAttrib1f(mItemSelectedHandle, selected ? 1.0f : 0.0f); - - // Pass in model position. - GLES20.glVertexAttribPointer(mPositionHandle, POSITION_COORDINATE_DATA_SIZE, - GLES20.GL_FLOAT, false, 0, mPositionCoordinates); - GLES20.glEnableVertexAttribArray(mPositionHandle); - - // Pass in texture coordinate. - GLES20.glVertexAttribPointer(mTextureCoordinateHandle, TEXTURE_COORDINATE_DATA_SIZE, - GLES20.GL_FLOAT, false, 0, TEXTURE_COORDINATES); - GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle); - - // Enable the transparent background. - GLES20.glEnable(GLES20.GL_BLEND); - GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); - - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle); - GLES20.glUniform1i(mTextureUniformHandle, 0); - - GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, VERTICES_NUMBER); - } - - /* - * Clean menu item related opengl data. - */ - public void clean() { - GLES20.glDeleteShader(mVertexShaderHandle); - GLES20.glDeleteShader(mFragmentShaderHandle); - GLES20.glDeleteTextures(1, new int[]{mTextureDataHandle}, 0); - } -} diff --git a/remoting/android/java/src/org/chromium/chromoting/CardboardActivitySkybox.java b/remoting/android/java/src/org/chromium/chromoting/CardboardActivitySkybox.java deleted file mode 100644 index 9de022ac..0000000 --- a/remoting/android/java/src/org/chromium/chromoting/CardboardActivitySkybox.java +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chromoting; - -import static org.chromium.chromoting.CardboardActivityUtility.makeFloatBuffer; - -import android.app.Activity; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.opengl.GLES20; - -import org.chromium.base.Log; - -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; - -/** - * Cardboard Activity skybox, which is used to draw the activity environment. - */ -public class CardboardActivitySkybox { - private static final String TAG = "cr.CardboardSkybox"; - - private static final String VERTEX_SHADER = - "uniform mat4 u_CombinedMatrix;" - + "attribute vec3 a_Position;" - + "varying vec3 v_Position;" - + "void main() {" - + " v_Position = a_Position;" - // Make sure to convert from the right-handed coordinate system of the - // world to the left-handed coordinate system of the cube map, otherwise, - // our cube map will still work but everything will be flipped. - + " v_Position.z = -v_Position.z;" - + " gl_Position = u_CombinedMatrix * vec4(a_Position, 1.0);" - + " gl_Position = gl_Position.xyww;" - + "}"; - - private static final String FRAGMENT_SHADER = - "precision mediump float;" - + "uniform samplerCube u_TextureUnit;" - + "varying vec3 v_Position;" - + "void main() {" - + " gl_FragColor = textureCube(u_TextureUnit, v_Position);" - + "}"; - - private static final int POSITION_DATA_SIZE = 3; - private static final float HALF_SKYBOX_SIZE = 100.0f; - - // Number of vertices passed to glDrawArrays(). - private static final int VERTICES_NUMBER = 36; - - private static final FloatBuffer POSITION_COORDINATES = makeFloatBuffer(new float[] { - -HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, // (0) Top-left near - HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, // (1) Top-right near - -HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, // (2) Bottom-left near - HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, // (3) Bottom-right near - -HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, // (4) Top-left far - HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, // (5) Top-right far - -HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, // (6) Bottom-left far - HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE // (7) Bottom-right far - }); - - private static final ByteBuffer INDICES_BYTE_BUFFER = ByteBuffer.wrap(new byte[] { - // Front - 1, 3, 0, - 0, 3, 2, - - // Back - 4, 6, 5, - 5, 6, 7, - - // Left - 0, 2, 4, - 4, 2, 6, - - // Right - 5, 7, 1, - 1, 7, 3, - - // Top - 5, 1, 4, - 4, 1, 0, - - // Bottom - 6, 2, 7, - 7, 2, 3 - }); - - private static final String[] IMAGE_URIS = new String[] { - "https://dl.google.com/chrome-remote-desktop/android-assets/room_left.png", - "https://dl.google.com/chrome-remote-desktop/android-assets/room_right.png", - "https://dl.google.com/chrome-remote-desktop/android-assets/room_bottom.png", - "https://dl.google.com/chrome-remote-desktop/android-assets/room_top.png", - "https://dl.google.com/chrome-remote-desktop/android-assets/room_back.png", - "https://dl.google.com/chrome-remote-desktop/android-assets/room_front.png" - }; - - private static final String[] IMAGE_NAMES = new String[] { - "skybox_left", "skybox_right", "skybox_bottom", "skybox_top", "skybox_back", "skybox_front" - }; - - private int mVertexShaderHandle; - private int mFragmentShaderHandle; - private int mProgramHandle; - private int mCombinedMatrixHandle; - private int mPositionHandle; - private int mTextureDataHandle; - private int mTextureUnitHandle; - private Activity mActivity; - - // Flag to signal that the skybox images are fully decoded and should be loaded - // into the OpenGL textures. - private boolean mLoadTexture; - - // Lock to allow multithreaded access to mLoadTexture. - private final Object mLoadTextureLock = new Object(); - - ChromotingDownloadManager mDownloadManager; - - public CardboardActivitySkybox(Activity activity) { - mActivity = activity; - - GLES20.glEnable(GLES20.GL_TEXTURE_CUBE_MAP); - mVertexShaderHandle = - ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); - mFragmentShaderHandle = - ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER); - mProgramHandle = ShaderHelper.createAndLinkProgram(mVertexShaderHandle, - mFragmentShaderHandle, new String[] {"a_Position", "u_CombinedMatrix", - "u_TextureUnit"}); - mPositionHandle = - GLES20.glGetAttribLocation(mProgramHandle, "a_Position"); - mCombinedMatrixHandle = - GLES20.glGetUniformLocation(mProgramHandle, "u_CombinedMatrix"); - mTextureUnitHandle = - GLES20.glGetUniformLocation(mProgramHandle, "u_TextureUnit"); - mTextureDataHandle = TextureHelper.createTextureHandle(); - - // Download the skybox images. - mDownloadManager = new ChromotingDownloadManager(mActivity, IMAGE_NAMES, - IMAGE_URIS, new ChromotingDownloadManager.Callback() { - @Override - public void onBatchDownloadComplete() { - synchronized (mLoadTextureLock) { - mLoadTexture = true; - } - } - }); - mDownloadManager.download(); - } - - /** - * Set the textures for skybox and clean temporary decoded images at the end. - * Only call this method when we have complete skybox images. - */ - public void maybeLoadTextureAndCleanImages() { - synchronized (mLoadTextureLock) { - if (!mLoadTexture) { - return; - } - mLoadTexture = false; - } - - Bitmap[] images; - try { - images = decodeSkyboxImages(); - } catch (DecodeFileException e) { - Log.i(TAG, "Failed to decode image files."); - return; - } - TextureHelper.linkCubeMap(mTextureDataHandle, images); - - for (Bitmap image : images) { - image.recycle(); - } - } - - /** - * Draw the skybox. Make sure texture, position, and model view projection matrix - * are passed in before calling this method. - */ - public void draw(float[] combinedMatrix) { - GLES20.glUseProgram(mProgramHandle); - - GLES20.glUniformMatrix4fv(mCombinedMatrixHandle, 1, false, - combinedMatrix, 0); - - GLES20.glVertexAttribPointer(mPositionHandle, POSITION_DATA_SIZE, GLES20.GL_FLOAT, - false, 0, POSITION_COORDINATES); - GLES20.glEnableVertexAttribArray(mPositionHandle); - - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, mTextureDataHandle); - GLES20.glUniform1i(mTextureUnitHandle, 0); - - GLES20.glDrawElements(GLES20.GL_TRIANGLES, VERTICES_NUMBER, GLES20.GL_UNSIGNED_BYTE, - INDICES_BYTE_BUFFER); - } - - /** - * Clean up opengl data. - */ - public void cleanup() { - GLES20.glDeleteShader(mVertexShaderHandle); - GLES20.glDeleteShader(mFragmentShaderHandle); - GLES20.glDeleteTextures(1, new int[] {mTextureDataHandle}, 0); - - mActivity.runOnUiThread(new Runnable() { - public void run() { - mDownloadManager.close(); - } - }); - } - - /** - * Decode all skybox images to Bitmap files and return them. - * Only call this method when we have complete skybox images. - * @throws DecodeFileException if BitmapFactory fails to decode file. - */ - private Bitmap[] decodeSkyboxImages() throws DecodeFileException { - Bitmap[] result = new Bitmap[IMAGE_NAMES.length]; - String fileDirectory = mDownloadManager.getDownloadDirectory(); - for (int i = 0; i < IMAGE_NAMES.length; i++) { - result[i] = BitmapFactory.decodeFile(fileDirectory + "/" + IMAGE_NAMES[i]); - if (result[i] == null) { - throw new DecodeFileException(); - } - } - return result; - } - - /** - * Exception when BitmapFactory fails to decode file. - */ - private static class DecodeFileException extends Exception { - } -} \ No newline at end of file diff --git a/remoting/android/java/src/org/chromium/chromoting/CardboardActivityUtility.java b/remoting/android/java/src/org/chromium/chromoting/CardboardActivityUtility.java deleted file mode 100644 index 4363b28..0000000 --- a/remoting/android/java/src/org/chromium/chromoting/CardboardActivityUtility.java +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chromoting; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - -/** - * Utility class for Cardboard activity. - */ -public class CardboardActivityUtility { - private static final int BYTE_PER_FLOAT = 4; - - /** - * Create rectangular texture float buffer. - */ - public static FloatBuffer makeRectangularTextureBuffer(float left, float right, - float bottom, float top) { - float[] position = new float[] { - left, bottom, - left, top, - right, bottom, - left, top, - right, top, - right, bottom - }; - return makeFloatBuffer(position); - } - - /** - * Convert float array to a FloatBuffer for use in OpenGL calls. - */ - public static FloatBuffer makeFloatBuffer(float[] data) { - FloatBuffer result = ByteBuffer - .allocateDirect(data.length * BYTE_PER_FLOAT) - .order(ByteOrder.nativeOrder()).asFloatBuffer(); - result.put(data).position(0); - return result; - } -} \ No newline at end of file diff --git a/remoting/android/java/src/org/chromium/chromoting/CardboardDesktopActivity.java b/remoting/android/java/src/org/chromium/chromoting/CardboardDesktopActivity.java deleted file mode 100644 index a92cbed..0000000 --- a/remoting/android/java/src/org/chromium/chromoting/CardboardDesktopActivity.java +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chromoting; - -import android.content.Intent; -import android.graphics.PointF; -import android.os.Bundle; -import android.speech.RecognitionListener; -import android.speech.RecognizerIntent; -import android.speech.SpeechRecognizer; - -import com.google.vrtoolkit.cardboard.CardboardActivity; -import com.google.vrtoolkit.cardboard.CardboardView; - -import org.chromium.chromoting.jni.JniInterface; - -import java.util.ArrayList; - -/** - * Virtual desktop activity for Cardboard. - */ -public class CardboardDesktopActivity extends CardboardActivity { - // Flag to indicate whether the current activity is going to switch to normal - // desktop activity. - private boolean mSwitchToDesktopActivity; - - private CardboardDesktopRenderer mRenderer; - private SpeechRecognizer mSpeechRecognizer; - - // Flag to indicate whether the speech recognizer is listening or not. - private boolean mIsListening; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.cardboard_desktop); - mSwitchToDesktopActivity = false; - CardboardView cardboardView = (CardboardView) findViewById(R.id.cardboard_view); - mRenderer = new CardboardDesktopRenderer(this); - mIsListening = false; - - // Associate a CardboardView.StereoRenderer with cardboardView. - cardboardView.setRenderer(mRenderer); - - // Associate the cardboardView with this activity. - setCardboardView(cardboardView); - } - - @Override - public void onCardboardTrigger() { - if (mRenderer.isMenuBarVisible()) { - if (mRenderer.isLookingAtMenuBar()) { - switch (mRenderer.getMenuItem().getType()) { - case HOME: - mSwitchToDesktopActivity = true; - finish(); - break; - case VOICE_INPUT: - listenForVoiceInput(); - break; - case MOVE_FORWARD: - mRenderer.moveTowardsDesktop(); - break; - case MOVE_BACKWARD: - mRenderer.moveAwayFromDesktop(); - break; - } - } else { - mRenderer.setMenuBarVisible(false); - } - } else { - if (mRenderer.isLookingAtDesktop()) { - PointF coordinates = mRenderer.getMouseCoordinates(); - JniInterface.sendMouseEvent((int) coordinates.x, (int) coordinates.y, - TouchInputHandler.BUTTON_LEFT, true); - JniInterface.sendMouseEvent((int) coordinates.x, (int) coordinates.y, - TouchInputHandler.BUTTON_LEFT, false); - } else { - mRenderer.setMenuBarVisible(true); - } - } - } - - @Override - protected void onStart() { - super.onStart(); - JniInterface.enableVideoChannel(true); - } - - @Override - protected void onPause() { - super.onPause(); - if (!mSwitchToDesktopActivity) { - JniInterface.enableVideoChannel(false); - } - if (mSpeechRecognizer != null) { - mSpeechRecognizer.stopListening(); - } - } - - @Override - protected void onResume() { - super.onResume(); - JniInterface.enableVideoChannel(true); - } - - @Override - protected void onStop() { - super.onStop(); - if (mSwitchToDesktopActivity) { - mSwitchToDesktopActivity = false; - } else { - JniInterface.enableVideoChannel(false); - } - if (mSpeechRecognizer != null) { - mSpeechRecognizer.stopListening(); - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (mSpeechRecognizer != null) { - mSpeechRecognizer.cancel(); - mSpeechRecognizer.destroy(); - } - } - - private void listenForVoiceInput() { - if (mIsListening) { - return; - } - - if (mSpeechRecognizer == null) { - if (SpeechRecognizer.isRecognitionAvailable(this)) { - mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(this); - mSpeechRecognizer.setRecognitionListener(new VoiceInputRecognitionListener()); - } else { - return; - } - } - - mIsListening = true; - - Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); - - // LANGUAGE_MODEL_FREE_FORM is used to improve dictation accuracy - // for the voice keyboard. - intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, - RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); - - mSpeechRecognizer.startListening(intent); - } - - private class VoiceInputRecognitionListener implements RecognitionListener { - public void onReadyForSpeech(Bundle params) { - } - - public void onBeginningOfSpeech() { - } - - public void onRmsChanged(float rmsdB){ - } - - public void onBufferReceived(byte[] buffer) { - } - - public void onEndOfSpeech() { - mIsListening = false; - } - - public void onError(int error) { - mIsListening = false; - } - - public void onResults(Bundle results) { - // TODO(shichengfeng): If necessary, provide a list of choices for user to pick. - ArrayList data = - results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); - if (!data.isEmpty()) { - JniInterface.sendTextEvent(data.get(0)); - } - } - - public void onPartialResults(Bundle partialResults) { - } - - public void onEvent(int eventType, Bundle params) { - } - } -} diff --git a/remoting/android/java/src/org/chromium/chromoting/CardboardDesktopRenderer.java b/remoting/android/java/src/org/chromium/chromoting/CardboardDesktopRenderer.java deleted file mode 100644 index 165fc29..0000000 --- a/remoting/android/java/src/org/chromium/chromoting/CardboardDesktopRenderer.java +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chromoting; - -import android.app.Activity; -import android.graphics.Point; -import android.graphics.PointF; -import android.opengl.GLES20; -import android.opengl.Matrix; - -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 org.chromium.chromoting.jni.JniInterface; - -import javax.microedition.khronos.egl.EGLConfig; - -/** - * Renderer for Cardboard view. - */ -public class CardboardDesktopRenderer implements CardboardView.StereoRenderer { - private static final String TAG = "cr.CardboardRenderer"; - - private static final int BYTE_PER_FLOAT = 4; - private static final int POSITION_DATA_SIZE = 3; - private static final int TEXTURE_COORDINATE_DATA_SIZE = 2; - private static final float Z_NEAR = 0.1f; - private static final float Z_FAR = 100.0f; - private static final float DESKTOP_POSITION_X = 0.0f; - private static final float DESKTOP_POSITION_Y = 0.0f; - private static final float DESKTOP_POSITION_Z = -2.0f; - private static final float MENU_BAR_POSITION_X = 0.0f; - private static final float MENU_BAR_POSITION_Y = 0.0f; - private static final float MENU_BAR_POSITION_Z = -1.5f; - private static final float HALF_SKYBOX_SIZE = 100.0f; - private static final float VIEW_POSITION_MIN = -1.0f; - private static final float VIEW_POSITION_MAX = 3.0f; - - // Allows user to click even when looking outside the desktop - // but within edge margin. - private static final float EDGE_MARGIN = 0.1f; - - // Distance to move camera each time. - private static final float CAMERA_MOTION_STEP = 0.5f; - - private final Activity mActivity; - - private float mCameraPosition; - - // Lock to allow multithreaded access to mCameraPosition. - private final Object mCameraPositionLock = new Object(); - - private float[] mCameraMatrix; - private float[] mViewMatrix; - private float[] mProjectionMatrix; - - // Make matrix member variable to avoid unnecessary initialization. - private float[] mDesktopModelMatrix; - private float[] mDesktopCombinedMatrix; - private float[] mEyePointModelMatrix; - private float[] mEyePointCombinedMatrix; - private float[] mSkyboxCombinedMatrix; - - // Direction that user is looking towards. - private float[] mForwardVector; - - // Eye position at the desktop distance. - private PointF mEyeDesktopPosition; - - // Eye position at the menu bar distance; - private PointF mEyeMenuBarPosition; - - private CardboardActivityDesktop mDesktop; - private CardboardActivityEyePoint mEyePoint; - private CardboardActivitySkybox mSkybox; - private CardboardActivityMenuBar mMenuBar; - - // Lock for eye position related operations. - // This protects access to mEyeDesktopPosition. - private final Object mEyeDesktopPositionLock = new Object(); - - // Flag to indicate whether to show menu bar. - private boolean mMenuBarVisible; - - public CardboardDesktopRenderer(Activity activity) { - mActivity = activity; - mCameraPosition = 0.0f; - - mCameraMatrix = new float[16]; - mViewMatrix = new float[16]; - mProjectionMatrix = new float[16]; - mDesktopModelMatrix = new float[16]; - mDesktopCombinedMatrix = new float[16]; - mEyePointModelMatrix = new float[16]; - mEyePointCombinedMatrix = new float[16]; - mSkyboxCombinedMatrix = new float[16]; - - mForwardVector = new float[3]; - } - - // This can be called on any thread. - public void attachRedrawCallback() { - JniInterface.provideRedrawCallback(new Runnable() { - @Override - public void run() { - mDesktop.reloadTexture(); - } - }); - } - - @Override - public void onSurfaceCreated(EGLConfig config) { - // Set the background clear color to black. - GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - - // Use culling to remove back faces. - GLES20.glEnable(GLES20.GL_CULL_FACE); - - // Enable depth testing. - GLES20.glEnable(GLES20.GL_DEPTH_TEST); - - mDesktop = new CardboardActivityDesktop(); - mEyePoint = new CardboardActivityEyePoint(); - mSkybox = new CardboardActivitySkybox(mActivity); - mMenuBar = new CardboardActivityMenuBar(mActivity); - - attachRedrawCallback(); - } - - @Override - public void onSurfaceChanged(int width, int height) { - } - - @Override - public void onNewFrame(HeadTransform headTransform) { - // Position the eye at the origin. - float eyeX = 0.0f; - float eyeY = 0.0f; - float eyeZ; - synchronized (mCameraPositionLock) { - eyeZ = mCameraPosition; - } - - // We are looking toward the negative Z direction. - float lookX = DESKTOP_POSITION_X; - float lookY = DESKTOP_POSITION_Y; - float lookZ = DESKTOP_POSITION_Z; - - // Set our up vector. This is where our head would be pointing were we holding the camera. - float upX = 0.0f; - float upY = 1.0f; - float upZ = 0.0f; - - Matrix.setLookAtM(mCameraMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ); - - headTransform.getForwardVector(mForwardVector, 0); - mEyeDesktopPosition = getLookingPosition(DESKTOP_POSITION_Z); - mEyeMenuBarPosition = getLookingPosition(MENU_BAR_POSITION_Z); - mDesktop.maybeLoadDesktopTexture(); - mSkybox.maybeLoadTextureAndCleanImages(); - } - - @Override - public void onDrawEye(Eye eye) { - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); - - // Apply the eye transformation to the camera. - Matrix.multiplyMM(mViewMatrix, 0, eye.getEyeView(), 0, mCameraMatrix, 0); - - mProjectionMatrix = eye.getPerspective(Z_NEAR, Z_FAR); - - drawSkybox(); - drawDesktop(); - drawMenuBar(); - drawEyePoint(); - } - - @Override - public void onRendererShutdown() { - mDesktop.cleanup(); - mEyePoint.cleanup(); - mSkybox.cleanup(); - mMenuBar.cleanup(); - } - - @Override - public void onFinishFrame(Viewport viewport) { - } - - private void drawDesktop() { - if (!mDesktop.hasVideoFrame()) { - // This can happen if the client is connected, but a complete - // video frame has not yet been decoded. - return; - } - - Matrix.setIdentityM(mDesktopModelMatrix, 0); - Matrix.translateM(mDesktopModelMatrix, 0, DESKTOP_POSITION_X, - DESKTOP_POSITION_Y, DESKTOP_POSITION_Z); - - // Pass in Model View Matrix and Model View Project Matrix. - Matrix.multiplyMM(mDesktopCombinedMatrix, 0, mViewMatrix, 0, mDesktopModelMatrix, 0); - Matrix.multiplyMM(mDesktopCombinedMatrix, 0, mProjectionMatrix, - 0, mDesktopCombinedMatrix, 0); - - mDesktop.draw(mDesktopCombinedMatrix); - } - - private void drawEyePoint() { - if (!isLookingAtDesktop() || (isMenuBarVisible() && isLookingAtMenuBar())) { - return; - } - - float eyePointX = clamp(mEyeDesktopPosition.x, -mDesktop.getHalfWidth(), - mDesktop.getHalfWidth()); - float eyePointY = clamp(mEyeDesktopPosition.y, -mDesktop.getHalfHeight(), - mDesktop.getHalfHeight()); - Matrix.setIdentityM(mEyePointModelMatrix, 0); - Matrix.translateM(mEyePointModelMatrix, 0, eyePointX, eyePointY, - DESKTOP_POSITION_Z); - Matrix.multiplyMM(mEyePointCombinedMatrix, 0, mViewMatrix, 0, mEyePointModelMatrix, 0); - Matrix.multiplyMM(mEyePointCombinedMatrix, 0, mProjectionMatrix, - 0, mEyePointCombinedMatrix, 0); - - mEyePoint.draw(mEyePointCombinedMatrix); - } - - private void drawSkybox() { - // Since we will always put the skybox center in the origin, so skybox - // model matrix will always be identity matrix which we could ignore. - Matrix.multiplyMM(mSkyboxCombinedMatrix, 0, mProjectionMatrix, - 0, mViewMatrix, 0); - mSkybox.draw(mSkyboxCombinedMatrix); - } - - private void drawMenuBar() { - if (!mMenuBarVisible) { - return; - } - - mMenuBar.draw(mViewMatrix, mProjectionMatrix, mEyeMenuBarPosition, MENU_BAR_POSITION_X, - MENU_BAR_POSITION_Y, MENU_BAR_POSITION_Z); - } - - /** - * Return menu item that is currently looking at or null if not looking at menu bar. - */ - public CardboardActivityMenuItem getMenuItem() { - // Transform world view to model view. - return mMenuBar.getLookingItem(new PointF(mEyeMenuBarPosition.x - MENU_BAR_POSITION_X, - mEyeMenuBarPosition.y - MENU_BAR_POSITION_Y)); - } - - /** - * Returns coordinates in units of pixels in the desktop bitmap. - * This can be called on any thread. - */ - public PointF getMouseCoordinates() { - PointF result = new PointF(); - Point shapePixels = mDesktop.getFrameSizePixels(); - int heightPixels = shapePixels.x; - int widthPixels = shapePixels.y; - - synchronized (mEyeDesktopPositionLock) { - // Due to the coordinate direction, we only have to inverse x. - result.x = (mEyeDesktopPosition.x + mDesktop.getHalfWidth()) - / (2 * mDesktop.getHalfWidth()) * widthPixels; - result.y = (-mEyeDesktopPosition.y + mDesktop.getHalfHeight()) - / (2 * mDesktop.getHalfHeight()) * heightPixels; - result.x = clamp(result.x, 0, widthPixels); - result.y = clamp(result.y, 0, heightPixels); - } - return result; - } - - /** - * Returns the passed in value if it resides within the specified range (inclusive). If not, - * it will return the closest boundary from the range. The ordering of the boundary values - * does not matter. - * - * @param value The value to be compared against the range. - * @param a First boundary range value. - * @param b Second boundary range value. - * @return The passed in value if it is within the range, otherwise the closest boundary value. - */ - private static float clamp(float value, float a, float b) { - float min = (a > b) ? b : a; - float max = (a > b) ? a : b; - if (value < min) { - value = min; - } else if (value > max) { - value = max; - } - return value; - } - - /** - * Move the camera towards desktop. - * This method can be called on any thread. - */ - public void moveTowardsDesktop() { - synchronized (mCameraPositionLock) { - float newPosition = mCameraPosition - CAMERA_MOTION_STEP; - if (newPosition >= VIEW_POSITION_MIN) { - mCameraPosition = newPosition; - } - } - } - - /** - * Move the camera away from desktop. - * This method can be called on any thread. - */ - public void moveAwayFromDesktop() { - synchronized (mCameraPositionLock) { - float newPosition = mCameraPosition + CAMERA_MOTION_STEP; - if (newPosition <= VIEW_POSITION_MAX) { - mCameraPosition = newPosition; - } - } - } - - /** - * Return true if user is looking at the desktop. - * This method can be called on any thread. - */ - public boolean isLookingAtDesktop() { - synchronized (mEyeDesktopPositionLock) { - // TODO(shichengfeng): Move logic to CardboardActivityDesktop. - return Math.abs(mEyeDesktopPosition.x) <= (mDesktop.getHalfWidth() + EDGE_MARGIN) - && Math.abs(mEyeDesktopPosition.y) <= (mDesktop.getHalfHeight() + EDGE_MARGIN); - } - } - - /** - * Return true if user is looking at the menu bar. - */ - public boolean isLookingAtMenuBar() { - return mMenuBar.contains(new PointF(mEyeMenuBarPosition.x - MENU_BAR_POSITION_X, - mEyeMenuBarPosition.y - MENU_BAR_POSITION_Y)); - } - - /** - * Get eye position at the given distance. - */ - private PointF getLookingPosition(float distance) { - if (Math.abs(mForwardVector[2]) < 0.00001f) { - return new PointF(-Math.signum(mForwardVector[0]) * Float.MAX_VALUE, - -Math.signum(mForwardVector[1]) * Float.MAX_VALUE); - } else { - return new PointF(-mForwardVector[0] * distance / mForwardVector[2], - -mForwardVector[1] * distance / mForwardVector[2]); - } - } - - /** - * Set the visibility of the menu bar. - */ - public void setMenuBarVisible(boolean visible) { - mMenuBarVisible = visible; - } - - /** - * Return true if menu bar is visible. - */ - public boolean isMenuBarVisible() { - return mMenuBarVisible; - } -} \ No newline at end of file diff --git a/remoting/android/java/src/org/chromium/chromoting/Desktop.java b/remoting/android/java/src/org/chromium/chromoting/Desktop.java index 68219cb..fcc6aee 100644 --- a/remoting/android/java/src/org/chromium/chromoting/Desktop.java +++ b/remoting/android/java/src/org/chromium/chromoting/Desktop.java @@ -19,6 +19,7 @@ import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethodManager; +import org.chromium.chromoting.cardboard.DesktopActivity; import org.chromium.chromoting.jni.JniInterface; import java.util.Set; @@ -204,7 +205,7 @@ public class Desktop extends ActionBarActivity implements View.OnSystemUiVisibil if (id == R.id.actionbar_cardboard) { mSwitchToCardboardDesktopActivity = true; - Intent intent = new Intent(this, CardboardDesktopActivity.class); + Intent intent = new Intent(this, DesktopActivity.class); startActivityForResult(intent, Chromoting.CARDBOARD_DESKTOP_ACTIVITY); return true; } diff --git a/remoting/android/java/src/org/chromium/chromoting/ShaderHelper.java b/remoting/android/java/src/org/chromium/chromoting/ShaderHelper.java deleted file mode 100644 index 9c05aac..0000000 --- a/remoting/android/java/src/org/chromium/chromoting/ShaderHelper.java +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chromoting; - -import android.opengl.GLES20; - -import org.chromium.base.Log; - -/** - * Helper class for working with OpenGL shaders and programs. - */ -public class ShaderHelper { - private static final String TAG = "cr.ShaderHelper"; - - /** - * Compile a shader. - * - * @param shaderType The shader type. - * @param shaderSource The shader source code. - * @return An OpenGL handle to the shader. - */ - public static int compileShader(int shaderType, String shaderSource) { - int shaderHandle = GLES20.glCreateShader(shaderType); - - if (shaderHandle != 0) { - // Pass in the shader source. - GLES20.glShaderSource(shaderHandle, shaderSource); - - // Compile the shader. - GLES20.glCompileShader(shaderHandle); - - // Get the compilation status. - int[] compileStatus = new int[1]; - GLES20.glGetShaderiv(shaderHandle, 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(shaderHandle)); - GLES20.glDeleteShader(shaderHandle); - shaderHandle = 0; - } - } - - if (shaderHandle == 0) { - // TODO(shichengfeng): Handle Exception gracefully. - throw new RuntimeException("Error creating shader."); - } - - return shaderHandle; - } - - /** - * Compile and link a program. - * - * @param vertexShaderHandle An OpenGL handle to an already-compiled vertex shader. - * @param fragmentShaderHandle An OpenGL handle to an already-compiled fragment shader. - * @param attributes Attributes that need to be bound to the program. - * @return An OpenGL handle to the program. - */ - public static int createAndLinkProgram(int vertexShaderHandle, - int fragmentShaderHandle, String[] attributes) { - int programHandle = GLES20.glCreateProgram(); - - if (programHandle != 0) { - // Bind the vertex shader to the program. - GLES20.glAttachShader(programHandle, vertexShaderHandle); - - // Bind the fragment shader to the program. - GLES20.glAttachShader(programHandle, fragmentShaderHandle); - - // Bind attributes - if (attributes != null) { - int size = attributes.length; - for (int i = 0; i < size; i++) { - GLES20.glBindAttribLocation(programHandle, i, attributes[i]); - } - } - - // Link the two shaders together into a program. - GLES20.glLinkProgram(programHandle); - - // Get the link status. - int[] linkStatus = new int[1]; - GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0); - - // If the link failed, delete the program. - if (linkStatus[0] == 0) { - Log.e(TAG, "Error compiling program: " + GLES20.glGetProgramInfoLog(programHandle)); - GLES20.glDeleteProgram(programHandle); - programHandle = 0; - } - } - - if (programHandle == 0) { - // TODO(shichengfeng): Handle Exception gracefully. - throw new RuntimeException("Error creating program."); - } - - return programHandle; - } -} diff --git a/remoting/android/java/src/org/chromium/chromoting/TextureHelper.java b/remoting/android/java/src/org/chromium/chromoting/TextureHelper.java deleted file mode 100644 index fb710eb..0000000 --- a/remoting/android/java/src/org/chromium/chromoting/TextureHelper.java +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chromoting; - -import static android.opengl.GLES20.GL_LINEAR; -import static android.opengl.GLES20.GL_NEAREST; -import static android.opengl.GLES20.GL_TEXTURE_2D; -import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP; -import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_X; -import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; -import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; -import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X; -import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_Y; -import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_Z; -import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER; -import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER; -import static android.opengl.GLES20.glBindTexture; -import static android.opengl.GLES20.glDeleteTextures; -import static android.opengl.GLES20.glGenTextures; -import static android.opengl.GLES20.glTexParameteri; -import static android.opengl.GLUtils.texImage2D; - -import android.graphics.Bitmap; - -/** - * Helper class for working with OpenGL textures. - */ -public class TextureHelper { - /** - * Create new texture handle. - * @return New texture handle. - */ - public static int createTextureHandle() { - int[] textureDataHandle = new int[1]; - glGenTextures(1, textureDataHandle, 0); - if (textureDataHandle[0] != 0) { - return textureDataHandle[0]; - } else { - throw new RuntimeException("Error generating texture handle."); - } - } - - /** - * Link desktop texture with a handle. - * @param textureDataHandle the handle to attach texture to - */ - public static void linkTexture(int textureDataHandle, Bitmap bitmap) { - // Delete previously attached texture. - glDeleteTextures(1, new int[]{textureDataHandle}, 0); - - // Bind to the texture in OpenGL. - glBindTexture(GL_TEXTURE_2D, textureDataHandle); - - glTexParameteri(GL_TEXTURE_2D, - GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, - GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - // Load the bitmap into the bound texture. - texImage2D(GL_TEXTURE_2D, 0, bitmap, 0); - } - - /** - * Link the cubemap images with a given texture handle. - */ - public static void linkCubeMap(int textureDataHandle, Bitmap[] cubeBitmaps) { - glBindTexture(GL_TEXTURE_CUBE_MAP, textureDataHandle); - - // Linear filtering for minification and magnification. - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // Link left, right, bottom, top, back and front image in order. - texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, cubeBitmaps[0], 0); - texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, cubeBitmaps[1], 0); - - texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, cubeBitmaps[2], 0); - texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, cubeBitmaps[3], 0); - - texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, cubeBitmaps[4], 0); - texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, cubeBitmaps[5], 0); - } -} \ No newline at end of file diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java new file mode 100644 index 0000000..5b3603b --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java @@ -0,0 +1,373 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromoting.cardboard; + +import android.app.Activity; +import android.graphics.Point; +import android.graphics.PointF; +import android.opengl.GLES20; +import android.opengl.Matrix; + +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 org.chromium.chromoting.jni.JniInterface; + +import javax.microedition.khronos.egl.EGLConfig; + +/** + * Renderer for Cardboard view. + */ +public class CardboardRenderer implements CardboardView.StereoRenderer { + private static final String TAG = "cr.CardboardRenderer"; + + private static final int BYTE_PER_FLOAT = 4; + private static final int POSITION_DATA_SIZE = 3; + private static final int TEXTURE_COORDINATE_DATA_SIZE = 2; + private static final float Z_NEAR = 0.1f; + private static final float Z_FAR = 100.0f; + private static final float DESKTOP_POSITION_X = 0.0f; + private static final float DESKTOP_POSITION_Y = 0.0f; + private static final float DESKTOP_POSITION_Z = -2.0f; + private static final float MENU_BAR_POSITION_X = 0.0f; + private static final float MENU_BAR_POSITION_Y = 0.0f; + private static final float MENU_BAR_POSITION_Z = -1.5f; + private static final float HALF_SKYBOX_SIZE = 100.0f; + private static final float VIEW_POSITION_MIN = -1.0f; + private static final float VIEW_POSITION_MAX = 3.0f; + + // Allows user to click even when looking outside the desktop + // but within edge margin. + private static final float EDGE_MARGIN = 0.1f; + + // Distance to move camera each time. + private static final float CAMERA_MOTION_STEP = 0.5f; + + private final Activity mActivity; + + private float mCameraPosition; + + // Lock to allow multithreaded access to mCameraPosition. + private final Object mCameraPositionLock = new Object(); + + private float[] mCameraMatrix; + private float[] mViewMatrix; + private float[] mProjectionMatrix; + + // Make matrix member variable to avoid unnecessary initialization. + private float[] mDesktopModelMatrix; + private float[] mDesktopCombinedMatrix; + private float[] mEyePointModelMatrix; + private float[] mEyePointCombinedMatrix; + private float[] mSkyboxCombinedMatrix; + + // Direction that user is looking towards. + private float[] mForwardVector; + + // Eye position at the desktop distance. + private PointF mEyeDesktopPosition; + + // Eye position at the menu bar distance; + private PointF mEyeMenuBarPosition; + + private Desktop mDesktop; + private EyePoint mEyePoint; + private Skybox mSkybox; + private MenuBar mMenuBar; + + // Lock for eye position related operations. + // This protects access to mEyeDesktopPosition. + private final Object mEyeDesktopPositionLock = new Object(); + + // Flag to indicate whether to show menu bar. + private boolean mMenuBarVisible; + + public CardboardRenderer(Activity activity) { + mActivity = activity; + mCameraPosition = 0.0f; + + mCameraMatrix = new float[16]; + mViewMatrix = new float[16]; + mProjectionMatrix = new float[16]; + mDesktopModelMatrix = new float[16]; + mDesktopCombinedMatrix = new float[16]; + mEyePointModelMatrix = new float[16]; + mEyePointCombinedMatrix = new float[16]; + mSkyboxCombinedMatrix = new float[16]; + + mForwardVector = new float[3]; + } + + // This can be called on any thread. + public void attachRedrawCallback() { + JniInterface.provideRedrawCallback(new Runnable() { + @Override + public void run() { + mDesktop.reloadTexture(); + } + }); + } + + @Override + public void onSurfaceCreated(EGLConfig config) { + // Set the background clear color to black. + GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + // Use culling to remove back faces. + GLES20.glEnable(GLES20.GL_CULL_FACE); + + // Enable depth testing. + GLES20.glEnable(GLES20.GL_DEPTH_TEST); + + mDesktop = new Desktop(); + mEyePoint = new EyePoint(); + mSkybox = new Skybox(mActivity); + mMenuBar = new MenuBar(mActivity); + + attachRedrawCallback(); + } + + @Override + public void onSurfaceChanged(int width, int height) { + } + + @Override + public void onNewFrame(HeadTransform headTransform) { + // Position the eye at the origin. + float eyeX = 0.0f; + float eyeY = 0.0f; + float eyeZ; + synchronized (mCameraPositionLock) { + eyeZ = mCameraPosition; + } + + // We are looking toward the negative Z direction. + float lookX = DESKTOP_POSITION_X; + float lookY = DESKTOP_POSITION_Y; + float lookZ = DESKTOP_POSITION_Z; + + // Set our up vector. This is where our head would be pointing were we holding the camera. + float upX = 0.0f; + float upY = 1.0f; + float upZ = 0.0f; + + Matrix.setLookAtM(mCameraMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ); + + headTransform.getForwardVector(mForwardVector, 0); + mEyeDesktopPosition = getLookingPosition(DESKTOP_POSITION_Z); + mEyeMenuBarPosition = getLookingPosition(MENU_BAR_POSITION_Z); + mDesktop.maybeLoadDesktopTexture(); + mSkybox.maybeLoadTextureAndCleanImages(); + } + + @Override + public void onDrawEye(Eye eye) { + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); + + // Apply the eye transformation to the camera. + Matrix.multiplyMM(mViewMatrix, 0, eye.getEyeView(), 0, mCameraMatrix, 0); + + mProjectionMatrix = eye.getPerspective(Z_NEAR, Z_FAR); + + drawSkybox(); + drawDesktop(); + drawMenuBar(); + drawEyePoint(); + } + + @Override + public void onRendererShutdown() { + mDesktop.cleanup(); + mEyePoint.cleanup(); + mSkybox.cleanup(); + mMenuBar.cleanup(); + } + + @Override + public void onFinishFrame(Viewport viewport) { + } + + private void drawDesktop() { + if (!mDesktop.hasVideoFrame()) { + // This can happen if the client is connected, but a complete + // video frame has not yet been decoded. + return; + } + + Matrix.setIdentityM(mDesktopModelMatrix, 0); + Matrix.translateM(mDesktopModelMatrix, 0, DESKTOP_POSITION_X, + DESKTOP_POSITION_Y, DESKTOP_POSITION_Z); + + // Pass in Model View Matrix and Model View Project Matrix. + Matrix.multiplyMM(mDesktopCombinedMatrix, 0, mViewMatrix, 0, mDesktopModelMatrix, 0); + Matrix.multiplyMM(mDesktopCombinedMatrix, 0, mProjectionMatrix, + 0, mDesktopCombinedMatrix, 0); + + mDesktop.draw(mDesktopCombinedMatrix); + } + + private void drawEyePoint() { + if (!isLookingAtDesktop() || (isMenuBarVisible() && isLookingAtMenuBar())) { + return; + } + + float eyePointX = clamp(mEyeDesktopPosition.x, -mDesktop.getHalfWidth(), + mDesktop.getHalfWidth()); + float eyePointY = clamp(mEyeDesktopPosition.y, -mDesktop.getHalfHeight(), + mDesktop.getHalfHeight()); + Matrix.setIdentityM(mEyePointModelMatrix, 0); + Matrix.translateM(mEyePointModelMatrix, 0, eyePointX, eyePointY, + DESKTOP_POSITION_Z); + Matrix.multiplyMM(mEyePointCombinedMatrix, 0, mViewMatrix, 0, mEyePointModelMatrix, 0); + Matrix.multiplyMM(mEyePointCombinedMatrix, 0, mProjectionMatrix, + 0, mEyePointCombinedMatrix, 0); + + mEyePoint.draw(mEyePointCombinedMatrix); + } + + private void drawSkybox() { + // Since we will always put the skybox center in the origin, so skybox + // model matrix will always be identity matrix which we could ignore. + Matrix.multiplyMM(mSkyboxCombinedMatrix, 0, mProjectionMatrix, + 0, mViewMatrix, 0); + mSkybox.draw(mSkyboxCombinedMatrix); + } + + private void drawMenuBar() { + if (!mMenuBarVisible) { + return; + } + + mMenuBar.draw(mViewMatrix, mProjectionMatrix, mEyeMenuBarPosition, MENU_BAR_POSITION_X, + MENU_BAR_POSITION_Y, MENU_BAR_POSITION_Z); + } + + /** + * Return menu item that is currently looking at or null if not looking at menu bar. + */ + public MenuItem getMenuItem() { + // Transform world view to model view. + return mMenuBar.getLookingItem(new PointF(mEyeMenuBarPosition.x - MENU_BAR_POSITION_X, + mEyeMenuBarPosition.y - MENU_BAR_POSITION_Y)); + } + + /** + * Returns coordinates in units of pixels in the desktop bitmap. + * This can be called on any thread. + */ + public PointF getMouseCoordinates() { + PointF result = new PointF(); + Point shapePixels = mDesktop.getFrameSizePixels(); + int heightPixels = shapePixels.x; + int widthPixels = shapePixels.y; + + synchronized (mEyeDesktopPositionLock) { + // Due to the coordinate direction, we only have to inverse x. + result.x = (mEyeDesktopPosition.x + mDesktop.getHalfWidth()) + / (2 * mDesktop.getHalfWidth()) * widthPixels; + result.y = (-mEyeDesktopPosition.y + mDesktop.getHalfHeight()) + / (2 * mDesktop.getHalfHeight()) * heightPixels; + result.x = clamp(result.x, 0, widthPixels); + result.y = clamp(result.y, 0, heightPixels); + } + return result; + } + + /** + * Returns the passed in value if it resides within the specified range (inclusive). If not, + * it will return the closest boundary from the range. The ordering of the boundary values + * does not matter. + * + * @param value The value to be compared against the range. + * @param a First boundary range value. + * @param b Second boundary range value. + * @return The passed in value if it is within the range, otherwise the closest boundary value. + */ + private static float clamp(float value, float a, float b) { + float min = (a > b) ? b : a; + float max = (a > b) ? a : b; + if (value < min) { + value = min; + } else if (value > max) { + value = max; + } + return value; + } + + /** + * Move the camera towards desktop. + * This method can be called on any thread. + */ + public void moveTowardsDesktop() { + synchronized (mCameraPositionLock) { + float newPosition = mCameraPosition - CAMERA_MOTION_STEP; + if (newPosition >= VIEW_POSITION_MIN) { + mCameraPosition = newPosition; + } + } + } + + /** + * Move the camera away from desktop. + * This method can be called on any thread. + */ + public void moveAwayFromDesktop() { + synchronized (mCameraPositionLock) { + float newPosition = mCameraPosition + CAMERA_MOTION_STEP; + if (newPosition <= VIEW_POSITION_MAX) { + mCameraPosition = newPosition; + } + } + } + + /** + * Return true if user is looking at the desktop. + * This method can be called on any thread. + */ + public boolean isLookingAtDesktop() { + synchronized (mEyeDesktopPositionLock) { + // TODO(shichengfeng): Move logic to CardboardActivityDesktop. + return Math.abs(mEyeDesktopPosition.x) <= (mDesktop.getHalfWidth() + EDGE_MARGIN) + && Math.abs(mEyeDesktopPosition.y) <= (mDesktop.getHalfHeight() + EDGE_MARGIN); + } + } + + /** + * Return true if user is looking at the menu bar. + */ + public boolean isLookingAtMenuBar() { + return mMenuBar.contains(new PointF(mEyeMenuBarPosition.x - MENU_BAR_POSITION_X, + mEyeMenuBarPosition.y - MENU_BAR_POSITION_Y)); + } + + /** + * Get eye position at the given distance. + */ + private PointF getLookingPosition(float distance) { + if (Math.abs(mForwardVector[2]) < 0.00001f) { + return new PointF(-Math.signum(mForwardVector[0]) * Float.MAX_VALUE, + -Math.signum(mForwardVector[1]) * Float.MAX_VALUE); + } else { + return new PointF(-mForwardVector[0] * distance / mForwardVector[2], + -mForwardVector[1] * distance / mForwardVector[2]); + } + } + + /** + * Set the visibility of the menu bar. + */ + public void setMenuBarVisible(boolean visible) { + mMenuBarVisible = visible; + } + + /** + * Return true if menu bar is visible. + */ + public boolean isMenuBarVisible() { + return mMenuBarVisible; + } +} \ No newline at end of file diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardUtil.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardUtil.java new file mode 100644 index 0000000..1cc32a0 --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardUtil.java @@ -0,0 +1,43 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromoting.cardboard; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +/** + * Utility class for Cardboard activity. + */ +public class CardboardUtil { + private static final int BYTE_PER_FLOAT = 4; + + /** + * Create rectangular texture float buffer. + */ + public static FloatBuffer makeRectangularTextureBuffer(float left, float right, + float bottom, float top) { + float[] position = new float[] { + left, bottom, + left, top, + right, bottom, + left, top, + right, top, + right, bottom + }; + return makeFloatBuffer(position); + } + + /** + * Convert float array to a FloatBuffer for use in OpenGL calls. + */ + public static FloatBuffer makeFloatBuffer(float[] data) { + FloatBuffer result = ByteBuffer + .allocateDirect(data.length * BYTE_PER_FLOAT) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + result.put(data).position(0); + return result; + } +} \ No newline at end of file diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java new file mode 100644 index 0000000..8a3516c --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java @@ -0,0 +1,229 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromoting.cardboard; + +import static org.chromium.chromoting.cardboard.CardboardUtil.makeFloatBuffer; +import static org.chromium.chromoting.cardboard.CardboardUtil.makeRectangularTextureBuffer; + +import android.graphics.Bitmap; +import android.graphics.Point; +import android.opengl.GLES20; + +import org.chromium.chromoting.jni.JniInterface; + +import java.nio.FloatBuffer; + +/** + * Chromoting Cardboard activity desktop, which is used to display host desktop. + */ +public class Desktop { + private static final String VERTEX_SHADER = + "uniform mat4 u_CombinedMatrix;" + + "attribute vec4 a_Position;" + + "attribute vec2 a_TexCoordinate;" + + "varying vec2 v_TexCoordinate;" + + "void main() {" + + " v_TexCoordinate = a_TexCoordinate;" + + " gl_Position = u_CombinedMatrix * a_Position;" + + "}"; + + private static final String FRAGMENT_SHADER = + "precision highp float;" + + "uniform sampler2D u_Texture;" + + "varying vec2 v_TexCoordinate;" + + "const float borderWidth = 0.002;" + + "void main() {" + + " if (v_TexCoordinate.x > (1.0 - borderWidth) || v_TexCoordinate.x < borderWidth" + + " || v_TexCoordinate.y > (1.0 - borderWidth)" + + " || v_TexCoordinate.y < borderWidth) {" + + " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);" + + " } else {" + + " gl_FragColor = texture2D(u_Texture, v_TexCoordinate);" + + " }" + + "}"; + + private static final FloatBuffer TEXTURE_COORDINATES = makeRectangularTextureBuffer( + 0.0f, 1.0f, 0.0f, 1.0f); + + private static final int POSITION_DATA_SIZE = 3; + private static final int TEXTURE_COORDINATE_DATA_SIZE = 2; + + // Fix the desktop height and adjust width accordingly. + private static final float HALF_HEIGHT = 1.0f; + + // Number of vertices passed to glDrawArrays(). + private static final int VERTICES_NUMBER = 6; + + private int mVertexShaderHandle; + private int mFragmentShaderHandle; + private int mProgramHandle; + private int mCombinedMatrixHandle; + private int mTextureUniformHandle; + private int mPositionHandle; + private int mTextureDataHandle; + private int mTextureCoordinateHandle; + private FloatBuffer mPosition; + private float mHalfWidth; + + // Lock to allow multithreaded access to mHalfWidth. + private final Object mHalfWidthLock = new Object(); + + private Bitmap mVideoFrame; + + // Lock to allow multithreaded access to mVideoFrame. + private final Object mVideoFrameLock = new Object(); + + // Flag to indicate whether to reload the desktop texture. + private boolean mReloadTexture; + + // Lock to allow multithreaded access to mReloadTexture. + private final Object mReloadTextureLock = new Object(); + + public Desktop() { + mVertexShaderHandle = + ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); + mFragmentShaderHandle = + ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER); + mProgramHandle = ShaderHelper.createAndLinkProgram(mVertexShaderHandle, + mFragmentShaderHandle, new String[] {"a_Position", "a_TexCoordinate", + "u_CombinedMatrix", "u_Texture"}); + mCombinedMatrixHandle = + GLES20.glGetUniformLocation(mProgramHandle, "u_CombinedMatrix"); + mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture"); + mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position"); + mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate"); + mTextureDataHandle = TextureHelper.createTextureHandle(); + } + + /** + * Draw the desktop. Make sure {@link hasVideoFrame} returns true. + */ + public void draw(float[] combinedMatrix) { + GLES20.glUseProgram(mProgramHandle); + + // Pass in model view project matrix. + GLES20.glUniformMatrix4fv(mCombinedMatrixHandle, 1, false, combinedMatrix, 0); + + // Pass in texture coordinate. + GLES20.glVertexAttribPointer(mTextureCoordinateHandle, TEXTURE_COORDINATE_DATA_SIZE, + GLES20.GL_FLOAT, false, 0, TEXTURE_COORDINATES); + GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle); + + GLES20.glVertexAttribPointer(mPositionHandle, POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, + 0, mPosition); + GLES20.glEnableVertexAttribArray(mPositionHandle); + + // Link texture data with texture unit. + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle); + GLES20.glUniform1i(mTextureUniformHandle, 0); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, VERTICES_NUMBER); + } + + /** + * Update the desktop frame data based on the mVideoFrame. Note here we fix the + * height of the desktop and vary width accordingly. + */ + private void updateVideoFrame(Bitmap videoFrame) { + float newHalfDesktopWidth; + synchronized (mVideoFrameLock) { + mVideoFrame = videoFrame; + TextureHelper.linkTexture(mTextureDataHandle, videoFrame); + newHalfDesktopWidth = videoFrame.getWidth() * HALF_HEIGHT / videoFrame.getHeight(); + } + + synchronized (mHalfWidthLock) { + if (Math.abs(mHalfWidth - newHalfDesktopWidth) > 0.0001) { + mHalfWidth = newHalfDesktopWidth; + mPosition = makeFloatBuffer(new float[] { + // Desktop model coordinates. + -mHalfWidth, HALF_HEIGHT, 0.0f, + -mHalfWidth, -HALF_HEIGHT, 0.0f, + mHalfWidth, HALF_HEIGHT, 0.0f, + -mHalfWidth, -HALF_HEIGHT, 0.0f, + mHalfWidth, -HALF_HEIGHT, 0.0f, + mHalfWidth, HALF_HEIGHT, 0.0f + }); + } + } + } + + /** + * Clean up opengl data. + */ + public void cleanup() { + GLES20.glDeleteShader(mVertexShaderHandle); + GLES20.glDeleteShader(mFragmentShaderHandle); + GLES20.glDeleteTextures(1, new int[] {mTextureDataHandle}, 0); + } + + /** + * Return true if video frame data are already loaded in. + */ + public boolean hasVideoFrame() { + synchronized (mVideoFrameLock) { + return mVideoFrame != null; + } + } + + public float getHalfHeight() { + return HALF_HEIGHT; + } + + public float getHalfWidth() { + synchronized (mHalfWidthLock) { + return mHalfWidth; + } + } + + /** + * Get desktop height and width in pixels. + */ + public Point getFrameSizePixels() { + synchronized (mVideoFrameLock) { + return new Point(mVideoFrame == null ? 0 : mVideoFrame.getHeight(), + mVideoFrame == null ? 0 : mVideoFrame.getWidth()); + } + } + + /** + * Link desktop texture if {@link reloadTexture} was previously called. + * Invoked from {@link com.google.vrtoolkit.cardboard.CardboardView.StereoRenderer.onNewFrame} + * so that both eyes will have the same texture. + */ + public void maybeLoadDesktopTexture() { + synchronized (mReloadTextureLock) { + if (!mReloadTexture) { + return; + } + } + + // TODO(shichengfeng): Record the time desktop drawing takes. + Bitmap bitmap = JniInterface.getVideoFrame(); + + if (bitmap == null) { + // This can happen if the client is connected, but a complete video frame has not yet + // been decoded. + return; + } + + updateVideoFrame(bitmap); + + synchronized (mReloadTextureLock) { + mReloadTexture = false; + } + } + + /** + * Inform this object that a new video frame should be rendered. + * Called from native display thread. + */ + public void reloadTexture() { + synchronized (mReloadTextureLock) { + mReloadTexture = true; + } + } +} \ No newline at end of file diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java new file mode 100644 index 0000000..00c8b33 --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java @@ -0,0 +1,195 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromoting.cardboard; + +import android.content.Intent; +import android.graphics.PointF; +import android.os.Bundle; +import android.speech.RecognitionListener; +import android.speech.RecognizerIntent; +import android.speech.SpeechRecognizer; + +import com.google.vrtoolkit.cardboard.CardboardActivity; +import com.google.vrtoolkit.cardboard.CardboardView; + +import org.chromium.chromoting.R; +import org.chromium.chromoting.TouchInputHandler; +import org.chromium.chromoting.jni.JniInterface; + +import java.util.ArrayList; + +/** + * Virtual desktop activity for Cardboard. + */ +public class DesktopActivity extends CardboardActivity { + // Flag to indicate whether the current activity is going to switch to normal + // desktop activity. + private boolean mSwitchToDesktopActivity; + + private CardboardRenderer mRenderer; + private SpeechRecognizer mSpeechRecognizer; + + // Flag to indicate whether the speech recognizer is listening or not. + private boolean mIsListening; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.cardboard_desktop); + mSwitchToDesktopActivity = false; + CardboardView cardboardView = (CardboardView) findViewById(R.id.cardboard_view); + mRenderer = new CardboardRenderer(this); + mIsListening = false; + + // Associate a CardboardView.StereoRenderer with cardboardView. + cardboardView.setRenderer(mRenderer); + + // Associate the cardboardView with this activity. + setCardboardView(cardboardView); + } + + @Override + public void onCardboardTrigger() { + if (mRenderer.isMenuBarVisible()) { + if (mRenderer.isLookingAtMenuBar()) { + switch (mRenderer.getMenuItem().getType()) { + case HOME: + mSwitchToDesktopActivity = true; + finish(); + break; + case VOICE_INPUT: + listenForVoiceInput(); + break; + case MOVE_FORWARD: + mRenderer.moveTowardsDesktop(); + break; + case MOVE_BACKWARD: + mRenderer.moveAwayFromDesktop(); + break; + } + } else { + mRenderer.setMenuBarVisible(false); + } + } else { + if (mRenderer.isLookingAtDesktop()) { + PointF coordinates = mRenderer.getMouseCoordinates(); + JniInterface.sendMouseEvent((int) coordinates.x, (int) coordinates.y, + TouchInputHandler.BUTTON_LEFT, true); + JniInterface.sendMouseEvent((int) coordinates.x, (int) coordinates.y, + TouchInputHandler.BUTTON_LEFT, false); + } else { + mRenderer.setMenuBarVisible(true); + } + } + } + + @Override + protected void onStart() { + super.onStart(); + JniInterface.enableVideoChannel(true); + } + + @Override + protected void onPause() { + super.onPause(); + if (!mSwitchToDesktopActivity) { + JniInterface.enableVideoChannel(false); + } + if (mSpeechRecognizer != null) { + mSpeechRecognizer.stopListening(); + } + } + + @Override + protected void onResume() { + super.onResume(); + JniInterface.enableVideoChannel(true); + } + + @Override + protected void onStop() { + super.onStop(); + if (mSwitchToDesktopActivity) { + mSwitchToDesktopActivity = false; + } else { + JniInterface.enableVideoChannel(false); + } + if (mSpeechRecognizer != null) { + mSpeechRecognizer.stopListening(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mSpeechRecognizer != null) { + mSpeechRecognizer.cancel(); + mSpeechRecognizer.destroy(); + } + } + + private void listenForVoiceInput() { + if (mIsListening) { + return; + } + + if (mSpeechRecognizer == null) { + if (SpeechRecognizer.isRecognitionAvailable(this)) { + mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(this); + mSpeechRecognizer.setRecognitionListener(new VoiceInputRecognitionListener()); + } else { + return; + } + } + + mIsListening = true; + + Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); + + // LANGUAGE_MODEL_FREE_FORM is used to improve dictation accuracy + // for the voice keyboard. + intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, + RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); + + mSpeechRecognizer.startListening(intent); + } + + private class VoiceInputRecognitionListener implements RecognitionListener { + public void onReadyForSpeech(Bundle params) { + } + + public void onBeginningOfSpeech() { + } + + public void onRmsChanged(float rmsdB){ + } + + public void onBufferReceived(byte[] buffer) { + } + + public void onEndOfSpeech() { + mIsListening = false; + } + + public void onError(int error) { + mIsListening = false; + } + + public void onResults(Bundle results) { + // TODO(shichengfeng): If necessary, provide a list of choices for user to pick. + ArrayList data = + results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); + if (!data.isEmpty()) { + JniInterface.sendTextEvent(data.get(0)); + } + } + + public void onPartialResults(Bundle partialResults) { + } + + public void onEvent(int eventType, Bundle params) { + } + } +} diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/EyePoint.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/EyePoint.java new file mode 100644 index 0000000..c43feba --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/EyePoint.java @@ -0,0 +1,77 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromoting.cardboard; + +import android.opengl.GLES20; + +/** + * Chromoting Cardboard activity eye point, which represents the location on the desktop + * where user is looking at. + */ +public class EyePoint { + private static final String VERTEX_SHADER = + "uniform mat4 u_CombinedMatrix;" + + "attribute vec4 a_EyePosition;" + + "void main() {" + + " gl_Position = u_CombinedMatrix * a_EyePosition;" + + " gl_PointSize = 3.0;" + + "}"; + + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + "void main() {" + + " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);" + + "}"; + + // Size of the vertexes to draw eye point. + private static final int VERTEXES_NUMBER = 1; + + private int mVertexShaderHandle; + private int mFragmentShaderHandle; + private int mProgramHandle; + private int mCombinedMatrixHandle; + private int mPositionHandle; + + public EyePoint() { + // Set handlers for eye point drawing. + mVertexShaderHandle = + ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); + mFragmentShaderHandle = + ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER); + mProgramHandle = ShaderHelper.createAndLinkProgram(mVertexShaderHandle, + mFragmentShaderHandle, new String[] {"a_EyePosition", "u_CombinedMatrix"}); + mPositionHandle = + GLES20.glGetAttribLocation(mProgramHandle, "a_EyePosition"); + mCombinedMatrixHandle = + GLES20.glGetUniformLocation(mProgramHandle, "u_CombinedMatrix"); + } + + /** + * Draw the eye point based on given model view projection matrix. + */ + public void draw(float[] combinedMatrix) { + GLES20.glUseProgram(mProgramHandle); + + // Set the eye point in front of desktop. + GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT); + + GLES20.glUniformMatrix4fv(mCombinedMatrixHandle, 1, false, combinedMatrix, 0); + + GLES20.glVertexAttrib4f(mPositionHandle, 0.0f, 0.0f, 0.0f, 1.0f); + + // Since we are not using a buffer object, disable vertex arrays for this attribute. + GLES20.glDisableVertexAttribArray(mPositionHandle); + + GLES20.glDrawArrays(GLES20.GL_POINTS, 0, VERTEXES_NUMBER); + } + + /** + * Clean up opengl data. + */ + public void cleanup() { + GLES20.glDeleteShader(mVertexShaderHandle); + GLES20.glDeleteShader(mFragmentShaderHandle); + } +} \ No newline at end of file diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/MenuBar.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/MenuBar.java new file mode 100644 index 0000000..4fe2ee1 --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/MenuBar.java @@ -0,0 +1,107 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromoting.cardboard; + +import android.content.Context; +import android.graphics.PointF; +import android.graphics.RectF; +import android.opengl.Matrix; + +import org.chromium.chromoting.R; + +/** + * Cardboard activity menu bar that contains multiple menu items. + */ +public class MenuBar { + public enum MenuItemType { + HOME(R.drawable.ic_home), + VOICE_INPUT(R.drawable.ic_voice_input), + MOVE_FORWARD(R.drawable.ic_move_forward), + MOVE_BACKWARD(R.drawable.ic_move_backward); + + private final int mResourceId; + + MenuItemType(int resourceId) { + mResourceId = resourceId; + } + + public int resourceId() { + return mResourceId; + } + } + + public static final float MENU_ITEM_SIZE = 0.2f; + + private final RectF mMenuBarRect; + + private final MenuItem[] mItems; + + private float[] mModelMatrix; + private float[] mCombinedMatrix; + + public MenuBar(Context context) { + MenuItemType[] menuItemTypes = MenuItemType.values(); + final int numItem = menuItemTypes.length; + mCombinedMatrix = new float[16]; + mModelMatrix = new float[16]; + + final float halfMenuWidth = numItem * MENU_ITEM_SIZE / 2; + final float halfMenuHeight = MENU_ITEM_SIZE / 2; + mMenuBarRect = new RectF(-halfMenuWidth, -halfMenuHeight, halfMenuWidth, halfMenuHeight); + + RectF currentRect = new RectF(-halfMenuWidth, -halfMenuHeight, + -halfMenuWidth + MENU_ITEM_SIZE, halfMenuHeight); + mItems = new MenuItem[numItem]; + for (int i = 0; i < numItem; i++) { + mItems[i] = new MenuItem(context, menuItemTypes[i], currentRect); + currentRect.offset(MENU_ITEM_SIZE, 0); + } + } + + /** + * Get menu item that user is looking at. + * Return the CardboardActivity menu item that contains the passed in coordinates or + * null if none of menu items contains the passed in coordinates. + */ + public MenuItem getLookingItem(PointF lookingPosition) { + for (MenuItem item : mItems) { + if (item.contains(lookingPosition)) { + return item; + } + } + + return null; + } + + public void draw(float[] viewMatrix, float[] projectionMatrix, PointF eyeMenuBarPosition, + float centerX, float centerY, float centerZ) { + for (MenuItem item : mItems) { + Matrix.setIdentityM(mModelMatrix, 0); + Matrix.translateM(mModelMatrix, 0, item.getPosition().x + centerX, + item.getPosition().y + centerY, centerZ); + Matrix.multiplyMM(mCombinedMatrix, 0, viewMatrix, 0, mModelMatrix, 0); + Matrix.multiplyMM(mCombinedMatrix, 0, projectionMatrix, 0, mCombinedMatrix, 0); + item.draw(mCombinedMatrix, item.contains(eyeMenuBarPosition)); + } + } + + /** + * Return true if the passed in point is inside the menu bar. + * @param x x coordinate user is looking at. + * @param y y coordinate user is looking at. + */ + public boolean contains(PointF lookingPosition) { + return mMenuBarRect.contains(lookingPosition.x, lookingPosition.y); + } + + /** + * Clean up opengl data. + */ + public void cleanup() { + for (MenuItem item : mItems) { + item.clean(); + } + } +} \ No newline at end of file diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/MenuItem.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/MenuItem.java new file mode 100644 index 0000000..200f776 --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/MenuItem.java @@ -0,0 +1,176 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromoting.cardboard; + +import static org.chromium.chromoting.cardboard.CardboardUtil.makeFloatBuffer; +import static org.chromium.chromoting.cardboard.CardboardUtil.makeRectangularTextureBuffer; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.PointF; +import android.graphics.RectF; +import android.opengl.GLES20; + +import org.chromium.chromoting.cardboard.MenuBar.MenuItemType; + +import java.nio.FloatBuffer; + +/** + * Cardboard activity menu item representing a corresponding function. + */ +public class MenuItem { + private static final String VERTEX_SHADER = + "uniform mat4 u_CombinedMatrix;" + + "attribute vec4 a_Position;" + + "attribute vec2 a_TexCoordinate;" + + "varying vec2 v_TexCoordinate;" + + "attribute float a_selected;" + + "varying float v_selected;" + + "void main() {" + + " v_selected = a_selected;" + + " v_TexCoordinate = a_TexCoordinate;" + + " gl_Position = u_CombinedMatrix * a_Position;" + + "}"; + + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + "uniform sampler2D u_Texture;" + + "varying vec2 v_TexCoordinate;" + + "varying float v_selected;" + + "void main() {" + + " vec4 texture = texture2D(u_Texture, v_TexCoordinate);" + + " if (v_selected > 0.5) {" + + " gl_FragColor = vec4(2.0, texture.g, texture.b, texture.a);" + + " } else {" + + " gl_FragColor = texture;" + + " }" + + "}"; + + private static final FloatBuffer TEXTURE_COORDINATES = makeRectangularTextureBuffer( + 0.0f, 1.0f, 0.0f, 1.0f); + + private static final int POSITION_COORDINATE_DATA_SIZE = 3; + private static final int TEXTURE_COORDINATE_DATA_SIZE = 2; + + // Number of vertices passed to glDrawArrays(). + private static final int VERTICES_NUMBER = 6; + + private final FloatBuffer mPositionCoordinates; + + private int mVertexShaderHandle; + private int mFragmentShaderHandle; + private int mProgramHandle; + private int mCombinedMatrixHandle; + private int mTextureUniformHandle; + private int mPositionHandle; + private int mTextureDataHandle; + private int mTextureCoordinateHandle; + private int mItemSelectedHandle; + + private MenuItemType mType; + + private RectF mRect; + + public MenuItem(Context context, MenuItemType type, RectF rect) { + mType = type; + mRect = new RectF(rect); + float halfHeight = mRect.height() / 2; + float halfWidth = mRect.width() / 2; + mPositionCoordinates = makeFloatBuffer(new float[] { + // Desktop model coordinates. + -halfWidth, halfHeight, 0.0f, + -halfWidth, -halfHeight, 0.0f, + halfWidth, halfHeight, 0.0f, + -halfWidth, -halfHeight, 0.0f, + halfWidth, -halfHeight, 0.0f, + halfWidth, halfHeight, 0.0f + }); + + mVertexShaderHandle = + ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); + mFragmentShaderHandle = + ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER); + mProgramHandle = ShaderHelper.createAndLinkProgram(mVertexShaderHandle, + mFragmentShaderHandle, new String[] {"a_Position", "a_TexCoordinate", + "a_selected", "u_CombinedMatrix", "u_Texture"}); + + mCombinedMatrixHandle = + GLES20.glGetUniformLocation(mProgramHandle, "u_CombinedMatrix"); + mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture"); + mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position"); + mItemSelectedHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_selected"); + mTextureDataHandle = TextureHelper.createTextureHandle(); + mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate"); + + Bitmap texture = BitmapFactory.decodeResource(context.getResources(), + type.resourceId()); + TextureHelper.linkTexture(mTextureDataHandle, texture); + texture.recycle(); + } + + /** + * Return the type of this menu item. + */ + public MenuItemType getType() { + return mType; + } + + /** + * Return the position of the center of this menu item. + */ + public PointF getPosition() { + return new PointF(mRect.centerX(), mRect.centerY()); + } + + /** + * Return true if the point is inside this menu item. + */ + public boolean contains(PointF point) { + return mRect.contains(point.x, point.y); + } + + /** + * Draw menu item according to the given model view projection matrix. + */ + public void draw(float[] combinedMatrix, boolean selected) { + GLES20.glUseProgram(mProgramHandle); + + // Pass in model view project matrix. + GLES20.glUniformMatrix4fv(mCombinedMatrixHandle, 1, false, combinedMatrix, 0); + + // Pass in whether the item is selected + GLES20.glVertexAttrib1f(mItemSelectedHandle, selected ? 1.0f : 0.0f); + + // Pass in model position. + GLES20.glVertexAttribPointer(mPositionHandle, POSITION_COORDINATE_DATA_SIZE, + GLES20.GL_FLOAT, false, 0, mPositionCoordinates); + GLES20.glEnableVertexAttribArray(mPositionHandle); + + // Pass in texture coordinate. + GLES20.glVertexAttribPointer(mTextureCoordinateHandle, TEXTURE_COORDINATE_DATA_SIZE, + GLES20.GL_FLOAT, false, 0, TEXTURE_COORDINATES); + GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle); + + // Enable the transparent background. + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle); + GLES20.glUniform1i(mTextureUniformHandle, 0); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, VERTICES_NUMBER); + } + + /* + * Clean menu item related opengl data. + */ + public void clean() { + GLES20.glDeleteShader(mVertexShaderHandle); + GLES20.glDeleteShader(mFragmentShaderHandle); + GLES20.glDeleteTextures(1, new int[]{mTextureDataHandle}, 0); + } +} diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/ShaderHelper.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/ShaderHelper.java new file mode 100644 index 0000000..5bc25b5 --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/ShaderHelper.java @@ -0,0 +1,103 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromoting.cardboard; + +import android.opengl.GLES20; + +import org.chromium.base.Log; + +/** + * Helper class for working with OpenGL shaders and programs. + */ +public class ShaderHelper { + private static final String TAG = "cr.ShaderHelper"; + + /** + * Compile a shader. + * + * @param shaderType The shader type. + * @param shaderSource The shader source code. + * @return An OpenGL handle to the shader. + */ + public static int compileShader(int shaderType, String shaderSource) { + int shaderHandle = GLES20.glCreateShader(shaderType); + + if (shaderHandle != 0) { + // Pass in the shader source. + GLES20.glShaderSource(shaderHandle, shaderSource); + + // Compile the shader. + GLES20.glCompileShader(shaderHandle); + + // Get the compilation status. + int[] compileStatus = new int[1]; + GLES20.glGetShaderiv(shaderHandle, 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(shaderHandle)); + GLES20.glDeleteShader(shaderHandle); + shaderHandle = 0; + } + } + + if (shaderHandle == 0) { + // TODO(shichengfeng): Handle Exception gracefully. + throw new RuntimeException("Error creating shader."); + } + + return shaderHandle; + } + + /** + * Compile and link a program. + * + * @param vertexShaderHandle An OpenGL handle to an already-compiled vertex shader. + * @param fragmentShaderHandle An OpenGL handle to an already-compiled fragment shader. + * @param attributes Attributes that need to be bound to the program. + * @return An OpenGL handle to the program. + */ + public static int createAndLinkProgram(int vertexShaderHandle, + int fragmentShaderHandle, String[] attributes) { + int programHandle = GLES20.glCreateProgram(); + + if (programHandle != 0) { + // Bind the vertex shader to the program. + GLES20.glAttachShader(programHandle, vertexShaderHandle); + + // Bind the fragment shader to the program. + GLES20.glAttachShader(programHandle, fragmentShaderHandle); + + // Bind attributes + if (attributes != null) { + int size = attributes.length; + for (int i = 0; i < size; i++) { + GLES20.glBindAttribLocation(programHandle, i, attributes[i]); + } + } + + // Link the two shaders together into a program. + GLES20.glLinkProgram(programHandle); + + // Get the link status. + int[] linkStatus = new int[1]; + GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0); + + // If the link failed, delete the program. + if (linkStatus[0] == 0) { + Log.e(TAG, "Error compiling program: " + GLES20.glGetProgramInfoLog(programHandle)); + GLES20.glDeleteProgram(programHandle); + programHandle = 0; + } + } + + if (programHandle == 0) { + // TODO(shichengfeng): Handle Exception gracefully. + throw new RuntimeException("Error creating program."); + } + + return programHandle; + } +} diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/Skybox.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/Skybox.java new file mode 100644 index 0000000..b7d09d3 --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/Skybox.java @@ -0,0 +1,239 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromoting.cardboard; + +import static org.chromium.chromoting.cardboard.CardboardUtil.makeFloatBuffer; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.opengl.GLES20; + +import org.chromium.base.Log; +import org.chromium.chromoting.ChromotingDownloadManager; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +/** + * Cardboard Activity skybox, which is used to draw the activity environment. + */ +public class Skybox { + private static final String TAG = "cr.CardboardSkybox"; + + private static final String VERTEX_SHADER = + "uniform mat4 u_CombinedMatrix;" + + "attribute vec3 a_Position;" + + "varying vec3 v_Position;" + + "void main() {" + + " v_Position = a_Position;" + // Make sure to convert from the right-handed coordinate system of the + // world to the left-handed coordinate system of the cube map, otherwise, + // our cube map will still work but everything will be flipped. + + " v_Position.z = -v_Position.z;" + + " gl_Position = u_CombinedMatrix * vec4(a_Position, 1.0);" + + " gl_Position = gl_Position.xyww;" + + "}"; + + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + "uniform samplerCube u_TextureUnit;" + + "varying vec3 v_Position;" + + "void main() {" + + " gl_FragColor = textureCube(u_TextureUnit, v_Position);" + + "}"; + + private static final int POSITION_DATA_SIZE = 3; + private static final float HALF_SKYBOX_SIZE = 100.0f; + + // Number of vertices passed to glDrawArrays(). + private static final int VERTICES_NUMBER = 36; + + private static final FloatBuffer POSITION_COORDINATES = makeFloatBuffer(new float[] { + -HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, // (0) Top-left near + HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, // (1) Top-right near + -HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, // (2) Bottom-left near + HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, // (3) Bottom-right near + -HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, // (4) Top-left far + HALF_SKYBOX_SIZE, HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, // (5) Top-right far + -HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, // (6) Bottom-left far + HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE, -HALF_SKYBOX_SIZE // (7) Bottom-right far + }); + + private static final ByteBuffer INDICES_BYTE_BUFFER = ByteBuffer.wrap(new byte[] { + // Front + 1, 3, 0, + 0, 3, 2, + + // Back + 4, 6, 5, + 5, 6, 7, + + // Left + 0, 2, 4, + 4, 2, 6, + + // Right + 5, 7, 1, + 1, 7, 3, + + // Top + 5, 1, 4, + 4, 1, 0, + + // Bottom + 6, 2, 7, + 7, 2, 3 + }); + + private static final String[] IMAGE_URIS = new String[] { + "https://dl.google.com/chrome-remote-desktop/android-assets/room_left.png", + "https://dl.google.com/chrome-remote-desktop/android-assets/room_right.png", + "https://dl.google.com/chrome-remote-desktop/android-assets/room_bottom.png", + "https://dl.google.com/chrome-remote-desktop/android-assets/room_top.png", + "https://dl.google.com/chrome-remote-desktop/android-assets/room_back.png", + "https://dl.google.com/chrome-remote-desktop/android-assets/room_front.png" + }; + + private static final String[] IMAGE_NAMES = new String[] { + "skybox_left", "skybox_right", "skybox_bottom", "skybox_top", "skybox_back", "skybox_front" + }; + + private int mVertexShaderHandle; + private int mFragmentShaderHandle; + private int mProgramHandle; + private int mCombinedMatrixHandle; + private int mPositionHandle; + private int mTextureDataHandle; + private int mTextureUnitHandle; + private Activity mActivity; + + // Flag to signal that the skybox images are fully decoded and should be loaded + // into the OpenGL textures. + private boolean mLoadTexture; + + // Lock to allow multithreaded access to mLoadTexture. + private final Object mLoadTextureLock = new Object(); + + ChromotingDownloadManager mDownloadManager; + + public Skybox(Activity activity) { + mActivity = activity; + + GLES20.glEnable(GLES20.GL_TEXTURE_CUBE_MAP); + mVertexShaderHandle = + ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); + mFragmentShaderHandle = + ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER); + mProgramHandle = ShaderHelper.createAndLinkProgram(mVertexShaderHandle, + mFragmentShaderHandle, new String[] {"a_Position", "u_CombinedMatrix", + "u_TextureUnit"}); + mPositionHandle = + GLES20.glGetAttribLocation(mProgramHandle, "a_Position"); + mCombinedMatrixHandle = + GLES20.glGetUniformLocation(mProgramHandle, "u_CombinedMatrix"); + mTextureUnitHandle = + GLES20.glGetUniformLocation(mProgramHandle, "u_TextureUnit"); + mTextureDataHandle = TextureHelper.createTextureHandle(); + + // Download the skybox images. + mDownloadManager = new ChromotingDownloadManager(mActivity, IMAGE_NAMES, + IMAGE_URIS, new ChromotingDownloadManager.Callback() { + @Override + public void onBatchDownloadComplete() { + synchronized (mLoadTextureLock) { + mLoadTexture = true; + } + } + }); + mDownloadManager.download(); + } + + /** + * Set the textures for skybox and clean temporary decoded images at the end. + * Only call this method when we have complete skybox images. + */ + public void maybeLoadTextureAndCleanImages() { + synchronized (mLoadTextureLock) { + if (!mLoadTexture) { + return; + } + mLoadTexture = false; + } + + Bitmap[] images; + try { + images = decodeSkyboxImages(); + } catch (DecodeFileException e) { + Log.i(TAG, "Failed to decode image files."); + return; + } + TextureHelper.linkCubeMap(mTextureDataHandle, images); + + for (Bitmap image : images) { + image.recycle(); + } + } + + /** + * Draw the skybox. Make sure texture, position, and model view projection matrix + * are passed in before calling this method. + */ + public void draw(float[] combinedMatrix) { + GLES20.glUseProgram(mProgramHandle); + + GLES20.glUniformMatrix4fv(mCombinedMatrixHandle, 1, false, + combinedMatrix, 0); + + GLES20.glVertexAttribPointer(mPositionHandle, POSITION_DATA_SIZE, GLES20.GL_FLOAT, + false, 0, POSITION_COORDINATES); + GLES20.glEnableVertexAttribArray(mPositionHandle); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, mTextureDataHandle); + GLES20.glUniform1i(mTextureUnitHandle, 0); + + GLES20.glDrawElements(GLES20.GL_TRIANGLES, VERTICES_NUMBER, GLES20.GL_UNSIGNED_BYTE, + INDICES_BYTE_BUFFER); + } + + /** + * Clean up opengl data. + */ + public void cleanup() { + GLES20.glDeleteShader(mVertexShaderHandle); + GLES20.glDeleteShader(mFragmentShaderHandle); + GLES20.glDeleteTextures(1, new int[] {mTextureDataHandle}, 0); + + mActivity.runOnUiThread(new Runnable() { + public void run() { + mDownloadManager.close(); + } + }); + } + + /** + * Decode all skybox images to Bitmap files and return them. + * Only call this method when we have complete skybox images. + * @throws DecodeFileException if BitmapFactory fails to decode file. + */ + private Bitmap[] decodeSkyboxImages() throws DecodeFileException { + Bitmap[] result = new Bitmap[IMAGE_NAMES.length]; + String fileDirectory = mDownloadManager.getDownloadDirectory(); + for (int i = 0; i < IMAGE_NAMES.length; i++) { + result[i] = BitmapFactory.decodeFile(fileDirectory + "/" + IMAGE_NAMES[i]); + if (result[i] == null) { + throw new DecodeFileException(); + } + } + return result; + } + + /** + * Exception when BitmapFactory fails to decode file. + */ + private static class DecodeFileException extends Exception { + } +} \ No newline at end of file diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/TextureHelper.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/TextureHelper.java new file mode 100644 index 0000000..82b852a --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/TextureHelper.java @@ -0,0 +1,85 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chromoting.cardboard; + +import static android.opengl.GLES20.GL_LINEAR; +import static android.opengl.GLES20.GL_NEAREST; +import static android.opengl.GLES20.GL_TEXTURE_2D; +import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP; +import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_X; +import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; +import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; +import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X; +import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_Y; +import static android.opengl.GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_Z; +import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER; +import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER; +import static android.opengl.GLES20.glBindTexture; +import static android.opengl.GLES20.glDeleteTextures; +import static android.opengl.GLES20.glGenTextures; +import static android.opengl.GLES20.glTexParameteri; +import static android.opengl.GLUtils.texImage2D; + +import android.graphics.Bitmap; + +/** + * Helper class for working with OpenGL textures. + */ +public class TextureHelper { + /** + * Create new texture handle. + * @return New texture handle. + */ + public static int createTextureHandle() { + int[] textureDataHandle = new int[1]; + glGenTextures(1, textureDataHandle, 0); + if (textureDataHandle[0] != 0) { + return textureDataHandle[0]; + } else { + throw new RuntimeException("Error generating texture handle."); + } + } + + /** + * Link desktop texture with a handle. + * @param textureDataHandle the handle to attach texture to + */ + public static void linkTexture(int textureDataHandle, Bitmap bitmap) { + // Delete previously attached texture. + glDeleteTextures(1, new int[]{textureDataHandle}, 0); + + // Bind to the texture in OpenGL. + glBindTexture(GL_TEXTURE_2D, textureDataHandle); + + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // Load the bitmap into the bound texture. + texImage2D(GL_TEXTURE_2D, 0, bitmap, 0); + } + + /** + * Link the cubemap images with a given texture handle. + */ + public static void linkCubeMap(int textureDataHandle, Bitmap[] cubeBitmaps) { + glBindTexture(GL_TEXTURE_CUBE_MAP, textureDataHandle); + + // Linear filtering for minification and magnification. + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Link left, right, bottom, top, back and front image in order. + texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, cubeBitmaps[0], 0); + texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, cubeBitmaps[1], 0); + + texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, cubeBitmaps[2], 0); + texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, cubeBitmaps[3], 0); + + texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, cubeBitmaps[4], 0); + texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, cubeBitmaps[5], 0); + } +} \ No newline at end of file -- cgit v1.1