// Copyright 2015 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #if UNITY_ANDROID using System.Runtime.InteropServices; using UnityEngine; // Android device using the Google Cardboard SDK for Android. public class CardboardAndroidDevice : AndroidBaseVRDevice { [DllImport("RenderingPlugin")] private static extern void InitFromUnity(int textureID); // Event IDs supported by our native render plugin. private const int kPerformDistortionCorrection = 1; private const int kDrawCardboardUILayer = 2; private const string cardboardClass = "com.google.vrtoolkit.cardboard.plugins.unity.UnityCardboardActivity"; private Matrix4x4 headView = new Matrix4x4(); private Matrix4x4 leftEyeView = new Matrix4x4(); private Matrix4x4 rightEyeView = new Matrix4x4(); private float[] frameInfo = null; public override void Init() { ConnectToActivity(); } protected override void ConnectToActivity() { try { using (AndroidJavaClass player = new AndroidJavaClass(cardboardClass)) { cardboardActivity = player.CallStatic("getActivity"); } } catch (AndroidJavaException e) { cardboardActivity = null; Debug.LogError("Cannot access UnityCardboardActivity. " + "Verify that the jar is in Assets/Plugins/Android. " + e); } } public override void SetDistortionCorrectionEnabled(bool enabled) { CallActivityMethod("setDistortionCorrectionEnabled", enabled); } public override void SetVRModeEnabled(bool enabled) { CallActivityMethod("setVRModeEnabled", enabled); } public override void SetAlignmentMarkerEnabled(bool enabled) { CallActivityMethod("setAlignmentMarkerEnabled", enabled); } public override void SetSettingsButtonEnabled(bool enabled) { CallActivityMethod("setSettingsButtonEnabled", enabled); } public override void SetNeckModelScale(float scale) { CallActivityMethod("setNeckModelFactor", scale); } public override void SetAutoDriftCorrectionEnabled(bool enabled) { CallActivityMethod("setGyroBiasEstimationEnabled", enabled); } public override void SetStereoScreen(RenderTexture stereoScreen) { if (cardboardActivity != null) { InitFromUnity(stereoScreen != null ? stereoScreen.GetNativeTextureID() : 0); } } public override void UpdateState() { // Pass nominal clip distances - will correct later for each camera. if (!CallActivityMethod(ref frameInfo, "getFrameParams", 1.0f /* near */, 1000.0f /* far */)) { return; } // Extract the matrices (currently that's all we get back). int j = 0; for (int i = 0; i < 16; ++i, ++j) { headView[i] = frameInfo[j]; } for (int i = 0; i < 16; ++i, ++j) { leftEyeView[i] = frameInfo[j]; } for (int i = 0; i < 16; ++i, ++j) { leftEyeDistortedProjection[i] = frameInfo[j]; } for (int i = 0; i < 16; ++i, ++j) { rightEyeView[i] = frameInfo[j]; } for (int i = 0; i < 16; ++i, ++j) { rightEyeDistortedProjection[i] = frameInfo[j]; } for (int i = 0; i < 16; ++i, ++j) { leftEyeUndistortedProjection[i] = frameInfo[j]; } for (int i = 0; i < 16; ++i, ++j) { rightEyeUndistortedProjection[i] = frameInfo[j]; } leftEyeUndistortedViewport = new Rect(frameInfo[j], frameInfo[j+1], frameInfo[j+2], frameInfo[j+3]); leftEyeDistortedViewport = leftEyeUndistortedViewport; j += 4; rightEyeUndistortedViewport = new Rect(frameInfo[j], frameInfo[j+1], frameInfo[j+2], frameInfo[j+3]); rightEyeDistortedViewport = rightEyeUndistortedViewport; // Convert views to left-handed coordinates because Unity uses them // for Transforms, which is what we will update from the views. // Also invert because the incoming matrices go from camera space to // cardboard space, and we want the opposite. // Lastly, cancel out the head rotation from the eye views, // because we are applying that on a parent object. leftEyePose.SetRightHanded(headView * leftEyeView.inverse); rightEyePose.SetRightHanded(headView * rightEyeView.inverse); headPose.SetRightHanded(headView.inverse); } override public void UpdateScreenData() { CardboardProfile.Device device = new CardboardProfile.Device(); CardboardProfile.Screen screen = new CardboardProfile.Screen(); float[] lensData = null; if (CallActivityMethod(ref lensData, "getLensParameters")) { device.lenses.separation = lensData[0]; device.lenses.offset = lensData[1]; device.lenses.screenDistance = lensData[2]; device.lenses.alignment = (int)lensData[3]; } float[] screenSize = null; if (CallActivityMethod(ref screenSize, "getScreenSizeMeters")) { screen.width = screenSize[0]; screen.height = screenSize[1]; screen.border = screenSize[2]; } float[] distCoeff = null; if (CallActivityMethod(ref distCoeff, "getDistortionCoefficients")) { device.distortion.k1 = distCoeff[0]; device.distortion.k2 = distCoeff[1]; } float[] invDistCoeff = null; if (CallActivityMethod(ref invDistCoeff, "getInverseDistortionCoefficients")) { device.inverse.k1 = invDistCoeff[0]; device.inverse.k2 = invDistCoeff[1]; } float[] maxFov = null; if (CallActivityMethod(ref maxFov, "getLeftEyeMaximumFOV")) { device.maxFOV.outer = maxFov[0]; device.maxFOV.upper = maxFov[1]; device.maxFOV.inner = maxFov[2]; device.maxFOV.lower = maxFov[3]; } Profile.screen = screen; Profile.device = device; } public override void Recenter() { CallActivityMethod("resetHeadTracker"); } public override void SetTouchCoordinates(int x, int y) { CallActivityMethod("setTouchCoordinates", x, y); } public override void PostRender(bool vrMode) { if (vrMode) { if (Cardboard.SDK.DistortionCorrection && Cardboard.SDK.NativeDistortionCorrectionSupported) { GL.IssuePluginEvent(kPerformDistortionCorrection); } if (Cardboard.SDK.EnableAlignmentMarker || Cardboard.SDK.EnableSettingsButton) { GL.IssuePluginEvent(kDrawCardboardUILayer); } GL.InvalidateState(); } } } #endif