Added VR libraries
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
/// Factory that provides a concrete implementation of IControllerProvider for the
|
||||
/// current platform.
|
||||
static class ControllerProviderFactory {
|
||||
/// Provides a concrete implementation of IControllerProvider appropriate for the current
|
||||
/// platform. This method never returns null. In the worst case, it might return a dummy
|
||||
/// provider if the platform is not supported. For demo purposes the emulator controller
|
||||
/// is returned in the editor and in Standalone buids, for use inside the desktop player.
|
||||
static internal IControllerProvider CreateControllerProvider(GvrControllerInput owner) {
|
||||
// Use emualtor in editor, and in Standalone builds (for demo purposes).
|
||||
#if UNITY_EDITOR
|
||||
// Use the Editor controller provider that supports the controller emulator and the mouse.
|
||||
return new EditorControllerProvider(owner.emulatorConnectionMode);
|
||||
#elif UNITY_ANDROID
|
||||
// Use the GVR C API.
|
||||
return new AndroidNativeControllerProvider();
|
||||
#else
|
||||
// Platform not supported.
|
||||
Debug.LogWarning("No controller support on this platform.");
|
||||
return new DummyControllerProvider();
|
||||
#endif // UNITY_EDITOR || UNITY_ANDROID
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f9210011b71142d5966eec2db6cc696
|
||||
timeCreated: 1462043669
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8adfc793bee33b24b861e195c2b5b483
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,459 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// This provider is only available on an Android device.
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
/// Controller Provider that uses the native GVR C API to communicate with controllers
|
||||
/// via Google VR Services on Android.
|
||||
class AndroidNativeControllerProvider : IControllerProvider {
|
||||
// Note: keep structs and function signatures in sync with the C header file (gvr_controller.h).
|
||||
// GVR controller option flags.
|
||||
private const int GVR_CONTROLLER_ENABLE_ORIENTATION = 1 << 0;
|
||||
private const int GVR_CONTROLLER_ENABLE_TOUCH = 1 << 1;
|
||||
private const int GVR_CONTROLLER_ENABLE_GYRO = 1 << 2;
|
||||
private const int GVR_CONTROLLER_ENABLE_ACCEL = 1 << 3;
|
||||
private const int GVR_CONTROLLER_ENABLE_GESTURES = 1 << 4;
|
||||
private const int GVR_CONTROLLER_ENABLE_POSE_PREDICTION = 1 << 5;
|
||||
private const int GVR_CONTROLLER_ENABLE_POSITION = 1 << 6;
|
||||
private const int GVR_CONTROLLER_ENABLE_BATTERY = 1 << 7;
|
||||
private const int GVR_CONTROLLER_ENABLE_ARM_MODEL = 1 << 8;
|
||||
|
||||
// enum gvr_controller_button:
|
||||
private const int GVR_CONTROLLER_BUTTON_NONE = 0;
|
||||
private const int GVR_CONTROLLER_BUTTON_CLICK = 1;
|
||||
private const int GVR_CONTROLLER_BUTTON_HOME = 2;
|
||||
private const int GVR_CONTROLLER_BUTTON_APP = 3;
|
||||
private const int GVR_CONTROLLER_BUTTON_VOLUME_UP = 4;
|
||||
private const int GVR_CONTROLLER_BUTTON_VOLUME_DOWN = 5;
|
||||
private const int GVR_CONTROLLER_BUTTON_RESERVED0 = 6;
|
||||
private const int GVR_CONTROLLER_BUTTON_RESERVED1 = 7;
|
||||
private const int GVR_CONTROLLER_BUTTON_RESERVED2 = 8;
|
||||
private const int GVR_CONTROLLER_BUTTON_COUNT = 9;
|
||||
|
||||
// enum gvr_controller_connection_state:
|
||||
private const int GVR_CONTROLLER_DISCONNECTED = 0;
|
||||
private const int GVR_CONTROLLER_SCANNING = 1;
|
||||
private const int GVR_CONTROLLER_CONNECTING = 2;
|
||||
private const int GVR_CONTROLLER_CONNECTED = 3;
|
||||
|
||||
// enum gvr_controller_api_status
|
||||
private const int GVR_CONTROLLER_API_OK = 0;
|
||||
private const int GVR_CONTROLLER_API_UNSUPPORTED = 1;
|
||||
private const int GVR_CONTROLLER_API_NOT_AUTHORIZED = 2;
|
||||
private const int GVR_CONTROLLER_API_UNAVAILABLE = 3;
|
||||
private const int GVR_CONTROLLER_API_SERVICE_OBSOLETE = 4;
|
||||
private const int GVR_CONTROLLER_API_CLIENT_OBSOLETE = 5;
|
||||
private const int GVR_CONTROLLER_API_MALFUNCTION = 6;
|
||||
|
||||
// The serialization of button-state used to determine which buttons are being pressed.
|
||||
private readonly GvrControllerButton[] GVR_UNITY_BUTTONS = new GvrControllerButton[] {
|
||||
GvrControllerButton.App,
|
||||
GvrControllerButton.System,
|
||||
GvrControllerButton.TouchPadButton,
|
||||
GvrControllerButton.Reserved0,
|
||||
GvrControllerButton.Reserved1,
|
||||
GvrControllerButton.Reserved2
|
||||
};
|
||||
private readonly int[] GVR_BUTTONS = new int[] {
|
||||
GVR_CONTROLLER_BUTTON_APP,
|
||||
GVR_CONTROLLER_BUTTON_HOME,
|
||||
GVR_CONTROLLER_BUTTON_CLICK,
|
||||
GVR_CONTROLLER_BUTTON_RESERVED0,
|
||||
GVR_CONTROLLER_BUTTON_RESERVED1,
|
||||
GVR_CONTROLLER_BUTTON_RESERVED2
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct gvr_quat {
|
||||
internal float x;
|
||||
internal float y;
|
||||
internal float z;
|
||||
internal float w;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct gvr_vec3 {
|
||||
internal float x;
|
||||
internal float y;
|
||||
internal float z;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct gvr_vec2 {
|
||||
internal float x;
|
||||
internal float y;
|
||||
}
|
||||
|
||||
private const string dllName = GvrActivityHelper.GVR_DLL_NAME;
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern int gvr_controller_get_default_options();
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern IntPtr gvr_controller_create_and_init_android(
|
||||
IntPtr jniEnv, IntPtr androidContext, IntPtr classLoader,
|
||||
int options, IntPtr context);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern void gvr_controller_destroy(ref IntPtr api);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern void gvr_controller_pause(IntPtr api);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern void gvr_controller_resume(IntPtr api);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern IntPtr gvr_controller_state_create();
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern void gvr_controller_state_destroy(ref IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern void gvr_controller_state_update(IntPtr api, int flags, IntPtr out_state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern int gvr_controller_state_get_api_status(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern int gvr_controller_state_get_connection_state(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern gvr_quat gvr_controller_state_get_orientation(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern gvr_vec3 gvr_controller_state_get_position(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern gvr_vec3 gvr_controller_state_get_gyro(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern gvr_vec3 gvr_controller_state_get_accel(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_is_touching(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern gvr_vec2 gvr_controller_state_get_touch_pos(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_touch_down(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_touch_up(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_recentered(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_button_state(IntPtr state, int button);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_button_down(IntPtr state, int button);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_button_up(IntPtr state, int button);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern long gvr_controller_state_get_last_orientation_timestamp(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern long gvr_controller_state_get_last_gyro_timestamp(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern long gvr_controller_state_get_last_accel_timestamp(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern long gvr_controller_state_get_last_touch_timestamp(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern long gvr_controller_state_get_last_button_timestamp(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_battery_charging(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern int gvr_controller_state_get_battery_level(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern long gvr_controller_state_get_last_battery_timestamp(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern int gvr_controller_get_count(IntPtr api);
|
||||
|
||||
private const string VRCORE_UTILS_CLASS = "com.google.vr.vrcore.base.api.VrCoreUtils";
|
||||
|
||||
private IntPtr api;
|
||||
private bool hasBatteryMethods = false;
|
||||
|
||||
private AndroidJavaObject androidContext;
|
||||
private AndroidJavaObject classLoader;
|
||||
|
||||
private bool error = false;
|
||||
private string errorDetails = string.Empty;
|
||||
|
||||
private IntPtr statePtr;
|
||||
|
||||
private MutablePose3D pose3d = new MutablePose3D();
|
||||
|
||||
private GvrControllerButton[] lastButtonsState = new GvrControllerButton[2];
|
||||
|
||||
public bool SupportsBatteryStatus {
|
||||
get { return hasBatteryMethods; }
|
||||
}
|
||||
|
||||
public int MaxControllerCount {
|
||||
get {
|
||||
if (api == IntPtr.Zero) {
|
||||
return 0;
|
||||
}
|
||||
return gvr_controller_get_count(api);
|
||||
}
|
||||
}
|
||||
|
||||
internal AndroidNativeControllerProvider() {
|
||||
// Debug.Log("Initializing Daydream controller API.");
|
||||
|
||||
int options = gvr_controller_get_default_options();
|
||||
options |= GVR_CONTROLLER_ENABLE_ACCEL;
|
||||
options |= GVR_CONTROLLER_ENABLE_GYRO;
|
||||
options |= GVR_CONTROLLER_ENABLE_POSITION;
|
||||
|
||||
statePtr = gvr_controller_state_create();
|
||||
// Get a hold of the activity, context and class loader.
|
||||
AndroidJavaObject activity = GvrActivityHelper.GetActivity();
|
||||
if (activity == null) {
|
||||
error = true;
|
||||
errorDetails = "Failed to get Activity from Unity Player.";
|
||||
return;
|
||||
}
|
||||
androidContext = GvrActivityHelper.GetApplicationContext(activity);
|
||||
if (androidContext == null) {
|
||||
error = true;
|
||||
errorDetails = "Failed to get Android application context from Activity.";
|
||||
return;
|
||||
}
|
||||
classLoader = GetClassLoaderFromActivity(activity);
|
||||
if (classLoader == null) {
|
||||
error = true;
|
||||
errorDetails = "Failed to get class loader from Activity.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Use IntPtr instead of GetRawObject() so that Unity can shut down gracefully on
|
||||
// Application.Quit(). Note that GetRawObject() is not pinned by the receiver so it's not
|
||||
// cleaned up appropriately on shutdown, which is a known bug in Unity.
|
||||
IntPtr androidContextPtr = AndroidJNI.NewLocalRef(androidContext.GetRawObject());
|
||||
IntPtr classLoaderPtr = AndroidJNI.NewLocalRef(classLoader.GetRawObject());
|
||||
Debug.Log ("Creating and initializing GVR API controller object.");
|
||||
api = gvr_controller_create_and_init_android (IntPtr.Zero, androidContextPtr, classLoaderPtr,
|
||||
options, IntPtr.Zero);
|
||||
AndroidJNI.DeleteLocalRef(androidContextPtr);
|
||||
AndroidJNI.DeleteLocalRef(classLoaderPtr);
|
||||
if (IntPtr.Zero == api) {
|
||||
Debug.LogError("Error creating/initializing Daydream controller API.");
|
||||
error = true;
|
||||
errorDetails = "Failed to initialize Daydream controller API.";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
gvr_controller_state_get_battery_charging(statePtr);
|
||||
gvr_controller_state_get_battery_level(statePtr);
|
||||
hasBatteryMethods = true;
|
||||
} catch (EntryPointNotFoundException) {
|
||||
// Older VrCore version. Does not support battery indicator.
|
||||
// Note that controller API is not dynamically loaded as of June 2017 (b/35662043),
|
||||
// so we'll need to support this case indefinitely...
|
||||
}
|
||||
|
||||
// Debug.Log("GVR API successfully initialized. Now resuming it.");
|
||||
gvr_controller_resume(api);
|
||||
// Debug.Log("GVR API resumed.");
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing) {
|
||||
if (disposing) {
|
||||
// Debug.Log("Destroying GVR API structures.");
|
||||
gvr_controller_state_destroy(ref statePtr);
|
||||
gvr_controller_destroy(ref api);
|
||||
if (statePtr != IntPtr.Zero) {
|
||||
Debug.LogError("gvr_controller_state not zeroed after destroy");
|
||||
}
|
||||
if (api != IntPtr.Zero) {
|
||||
Debug.LogError("gvr_controller_api not zeroed after destroy");
|
||||
}
|
||||
// Debug.Log("AndroidNativeControllerProvider destroyed.");
|
||||
}
|
||||
}
|
||||
|
||||
public void ReadState(ControllerState outState, int controller_id) {
|
||||
if (error) {
|
||||
outState.connectionState = GvrConnectionState.Error;
|
||||
outState.apiStatus = GvrControllerApiStatus.Error;
|
||||
outState.errorDetails = errorDetails;
|
||||
return;
|
||||
}
|
||||
if (api == IntPtr.Zero || statePtr == IntPtr.Zero) {
|
||||
Debug.LogError("AndroidNativeControllerProvider used after dispose.");
|
||||
return;
|
||||
}
|
||||
gvr_controller_state_update(api, controller_id, statePtr);
|
||||
|
||||
outState.connectionState = ConvertConnectionState(
|
||||
gvr_controller_state_get_connection_state(statePtr));
|
||||
outState.apiStatus = ConvertControllerApiStatus(
|
||||
gvr_controller_state_get_api_status(statePtr));
|
||||
|
||||
gvr_quat rawOri = gvr_controller_state_get_orientation(statePtr);
|
||||
gvr_vec3 rawAccel = gvr_controller_state_get_accel(statePtr);
|
||||
gvr_vec3 rawGyro = gvr_controller_state_get_gyro(statePtr);
|
||||
gvr_vec3 rawPos = gvr_controller_state_get_position(statePtr);
|
||||
|
||||
// Convert GVR API orientation (right-handed) into Unity axis system (left-handed).
|
||||
pose3d.Set(new Vector3(rawPos.x,rawPos.y,rawPos.z), new Quaternion(rawOri.x, rawOri.y, rawOri.z, rawOri.w));
|
||||
pose3d.SetRightHanded(pose3d.Matrix);
|
||||
outState.orientation = pose3d.Orientation;
|
||||
outState.position = pose3d.Position;
|
||||
|
||||
// For accelerometer, we have to flip Z because the GVR API has Z pointing backwards
|
||||
// and Unity has Z pointing forward.
|
||||
outState.accel = new Vector3(rawAccel.x, rawAccel.y, -rawAccel.z);
|
||||
|
||||
// Gyro in GVR represents a right-handed angular velocity about each axis (positive means
|
||||
// clockwise when sighting along axis). Since Unity uses a left-handed system, we flip the
|
||||
// signs to adjust the sign of the rotational velocity (so that positive means
|
||||
// counter-clockwise). In addition, since in Unity the Z axis points forward while GVR
|
||||
// has Z pointing backwards, we flip the Z axis sign again. So the result is that
|
||||
// we should use -X, -Y, +Z:
|
||||
outState.gyro = new Vector3(-rawGyro.x, -rawGyro.y, rawGyro.z);
|
||||
|
||||
gvr_vec2 touchPos = gvr_controller_state_get_touch_pos(statePtr);
|
||||
outState.touchPos = new Vector2(touchPos.x, touchPos.y);
|
||||
|
||||
outState.buttonsState = 0;
|
||||
for (int i=0; i<GVR_BUTTONS.Length; i++) {
|
||||
if (0 != gvr_controller_state_get_button_state(statePtr, GVR_BUTTONS[i])) {
|
||||
outState.buttonsState |= GVR_UNITY_BUTTONS[i];
|
||||
}
|
||||
}
|
||||
if (0 != gvr_controller_state_is_touching(statePtr)) {
|
||||
outState.buttonsState |= GvrControllerButton.TouchPadTouch;
|
||||
}
|
||||
|
||||
outState.SetButtonsUpDownFromPrevious(lastButtonsState[controller_id]);
|
||||
lastButtonsState[controller_id] = outState.buttonsState;
|
||||
|
||||
outState.recentered = 0 != gvr_controller_state_get_recentered(statePtr);
|
||||
outState.gvrPtr = statePtr;
|
||||
|
||||
if (hasBatteryMethods) {
|
||||
outState.isCharging = 0 != gvr_controller_state_get_battery_charging(statePtr);
|
||||
outState.batteryLevel = (GvrControllerBatteryLevel)gvr_controller_state_get_battery_level(statePtr);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPause() {
|
||||
if (IntPtr.Zero != api) {
|
||||
gvr_controller_pause(api);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnResume() {
|
||||
if (IntPtr.Zero != api) {
|
||||
gvr_controller_resume(api);
|
||||
}
|
||||
}
|
||||
|
||||
private GvrConnectionState ConvertConnectionState(int connectionState) {
|
||||
switch (connectionState) {
|
||||
case GVR_CONTROLLER_CONNECTED:
|
||||
return GvrConnectionState.Connected;
|
||||
case GVR_CONTROLLER_CONNECTING:
|
||||
return GvrConnectionState.Connecting;
|
||||
case GVR_CONTROLLER_SCANNING:
|
||||
return GvrConnectionState.Scanning;
|
||||
default:
|
||||
return GvrConnectionState.Disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
private GvrControllerApiStatus ConvertControllerApiStatus(int gvrControllerApiStatus) {
|
||||
switch (gvrControllerApiStatus) {
|
||||
case GVR_CONTROLLER_API_OK:
|
||||
return GvrControllerApiStatus.Ok;
|
||||
case GVR_CONTROLLER_API_UNSUPPORTED:
|
||||
return GvrControllerApiStatus.Unsupported;
|
||||
case GVR_CONTROLLER_API_NOT_AUTHORIZED:
|
||||
return GvrControllerApiStatus.NotAuthorized;
|
||||
case GVR_CONTROLLER_API_SERVICE_OBSOLETE:
|
||||
return GvrControllerApiStatus.ApiServiceObsolete;
|
||||
case GVR_CONTROLLER_API_CLIENT_OBSOLETE:
|
||||
return GvrControllerApiStatus.ApiClientObsolete;
|
||||
case GVR_CONTROLLER_API_MALFUNCTION:
|
||||
return GvrControllerApiStatus.ApiMalfunction;
|
||||
case GVR_CONTROLLER_API_UNAVAILABLE:
|
||||
default: // Fall through.
|
||||
return GvrControllerApiStatus.Unavailable;
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateInputEvents(bool currentState, ref bool previousState, ref bool up, ref bool down) {
|
||||
|
||||
down = !previousState && currentState;
|
||||
up = previousState && !currentState;
|
||||
|
||||
previousState = currentState;
|
||||
}
|
||||
|
||||
private static AndroidJavaObject GetClassLoaderFromActivity(AndroidJavaObject activity) {
|
||||
AndroidJavaObject result = activity.Call<AndroidJavaObject>("getClassLoader");
|
||||
if (result == null) {
|
||||
Debug.LogErrorFormat("Failed to get class loader from Activity.");
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int GetVrCoreClientApiVersion(AndroidJavaObject activity) {
|
||||
try {
|
||||
AndroidJavaClass utilsClass = new AndroidJavaClass(VRCORE_UTILS_CLASS);
|
||||
int apiVersion = utilsClass.CallStatic<int>("getVrCoreClientApiVersion", activity);
|
||||
// Debug.LogFormat("VrCore client API version: " + apiVersion);
|
||||
return apiVersion;
|
||||
} catch (Exception exc) {
|
||||
// Even though a catch-all block is normally frowned upon, in this case we really
|
||||
// need it because this method has to be robust to unpredictable circumstances:
|
||||
// VrCore might not exist in the device, the Java layer might be broken, etc, etc.
|
||||
// None of those should abort the app.
|
||||
Debug.LogError("Error obtaining VrCore client API version: " + exc);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
#endif // UNITY_ANDROID && !UNITY_EDITOR
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fda152dc25154b4a9cccb75fd77f018
|
||||
timeCreated: 1462060442
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
using Gvr;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
/// Dummy controller provider.
|
||||
/// Used in platforms that do not support controllers.
|
||||
class DummyControllerProvider : IControllerProvider {
|
||||
private ControllerState dummyState = new ControllerState();
|
||||
public bool SupportsBatteryStatus {
|
||||
get { return false; }
|
||||
}
|
||||
public int MaxControllerCount {
|
||||
get { return 1; }
|
||||
}
|
||||
internal DummyControllerProvider() {}
|
||||
public void Dispose() {}
|
||||
public void ReadState(ControllerState outState,int controller_id) {
|
||||
outState.CopyFrom(dummyState);
|
||||
}
|
||||
public void OnPause() {}
|
||||
public void OnResume() {}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f791be37caef48c79f72011276ab16a
|
||||
timeCreated: 1462043669
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// This provider is only available in the editor.
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using Gvr;
|
||||
|
||||
namespace Gvr.Internal {
|
||||
/// Controller provider used when playing in the Unity Editor.
|
||||
/// Supports the Controller Emulator and Mouse input to mock the controller.
|
||||
class EditorControllerProvider : IControllerProvider {
|
||||
private EmulatorControllerProvider emulatorControllerProvider;
|
||||
private MouseControllerProvider mouseControllerProvider;
|
||||
#if UNITY_HAS_GOOGLEVR
|
||||
/// Helper class to get Instant Preview controller events if connected.
|
||||
private InstantPreviewControllerProvider instantPreviewControllerProvider =
|
||||
new InstantPreviewControllerProvider();
|
||||
#endif // UNITY_HAS_GOOGLEVR
|
||||
|
||||
ControllerState emulatorState = new ControllerState();
|
||||
ControllerState mouseState = new ControllerState();
|
||||
|
||||
public bool SupportsBatteryStatus {
|
||||
get { return emulatorControllerProvider.SupportsBatteryStatus; }
|
||||
}
|
||||
public int MaxControllerCount {
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
internal EditorControllerProvider(GvrControllerInput.EmulatorConnectionMode connectionMode) {
|
||||
emulatorControllerProvider = new EmulatorControllerProvider(connectionMode);
|
||||
mouseControllerProvider = new MouseControllerProvider();
|
||||
}
|
||||
|
||||
public void Dispose() {}
|
||||
|
||||
public void ReadState(ControllerState outState, int controller_id) {
|
||||
if (controller_id != 0) {
|
||||
return;
|
||||
}
|
||||
#if UNITY_HAS_GOOGLEVR
|
||||
if (InstantPreview.Instance != null
|
||||
&& InstantPreview.Instance.IsCurrentlyConnected
|
||||
&& !EmulatorManager.Instance.Connected) {
|
||||
// Uses Instant Preview to get controller state if connected.
|
||||
instantPreviewControllerProvider.ReadState(outState);
|
||||
return;
|
||||
}
|
||||
#endif // UNITY_HAS_GOOGLEVR
|
||||
|
||||
// If Instant Preview is not connected, tries to use the emulator or
|
||||
// mouse.
|
||||
emulatorControllerProvider.ReadState(emulatorState, controller_id);
|
||||
mouseControllerProvider.ReadState(mouseState, controller_id);
|
||||
|
||||
// Defaults to mouse state if the emulator isn't available.
|
||||
if (emulatorState.connectionState != GvrConnectionState.Connected
|
||||
&& mouseState.connectionState == GvrConnectionState.Connected) {
|
||||
outState.CopyFrom(mouseState);
|
||||
} else {
|
||||
outState.CopyFrom(emulatorState);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPause() {
|
||||
emulatorControllerProvider.OnPause();
|
||||
mouseControllerProvider.OnPause();
|
||||
}
|
||||
|
||||
public void OnResume() {
|
||||
emulatorControllerProvider.OnResume();
|
||||
mouseControllerProvider.OnResume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UNITY_EDITOR
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0a32f2191dec4e1b8e05acab6002be6
|
||||
timeCreated: 1496354837
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,191 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// This class is only used in the Editor, so make sure to only compile it on that platform.
|
||||
// Additionally, it depends on EmulatorManager which is only compiled in the editor.
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
/// Controller provider that connects to the controller emulator to obtain controller events.
|
||||
class EmulatorControllerProvider : IControllerProvider {
|
||||
private ControllerState state = new ControllerState();
|
||||
|
||||
/// Yaw correction due to recentering.
|
||||
private Quaternion yawCorrection = Quaternion.identity;
|
||||
|
||||
/// True if we performed the initial recenter.
|
||||
private bool initialRecenterDone = false;
|
||||
|
||||
/// The last (uncorrected) orientation received from the emulator.
|
||||
private Quaternion lastRawOrientation = Quaternion.identity;
|
||||
private GvrControllerButton lastButtonsState;
|
||||
|
||||
public bool SupportsBatteryStatus {
|
||||
get { return true; }
|
||||
}
|
||||
public int MaxControllerCount {
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
/// Creates a new EmulatorControllerProvider with the specified settings.
|
||||
internal EmulatorControllerProvider(GvrControllerInput.EmulatorConnectionMode connectionMode) {
|
||||
if (connectionMode == GvrControllerInput.EmulatorConnectionMode.USB) {
|
||||
EmulatorConfig.Instance.PHONE_EVENT_MODE = EmulatorConfig.Mode.USB;
|
||||
} else if (connectionMode == GvrControllerInput.EmulatorConnectionMode.WIFI) {
|
||||
EmulatorConfig.Instance.PHONE_EVENT_MODE = EmulatorConfig.Mode.WIFI;
|
||||
} else {
|
||||
EmulatorConfig.Instance.PHONE_EVENT_MODE = EmulatorConfig.Mode.OFF;
|
||||
}
|
||||
|
||||
EmulatorManager.Instance.touchEventListeners += HandleTouchEvent;
|
||||
EmulatorManager.Instance.orientationEventListeners += HandleOrientationEvent;
|
||||
EmulatorManager.Instance.buttonEventListeners += HandleButtonEvent;
|
||||
EmulatorManager.Instance.gyroEventListeners += HandleGyroEvent;
|
||||
EmulatorManager.Instance.accelEventListeners += HandleAccelEvent;
|
||||
}
|
||||
|
||||
public void Dispose() {}
|
||||
|
||||
public void ReadState(ControllerState outState, int controller_id) {
|
||||
if (controller_id != 0) {
|
||||
return;
|
||||
}
|
||||
lock (state) {
|
||||
state.connectionState = GvrConnectionState.Connected;
|
||||
if (!EmulatorManager.Instance.Connected) {
|
||||
state.connectionState = EmulatorManager.Instance.Connecting ?
|
||||
GvrConnectionState.Connecting : GvrConnectionState.Disconnected;
|
||||
}
|
||||
state.apiStatus = EmulatorManager.Instance.Connected ? GvrControllerApiStatus.Ok :
|
||||
GvrControllerApiStatus.Unavailable;
|
||||
|
||||
// During emulation, just assume the controller is fully charged
|
||||
state.isCharging = false;
|
||||
state.batteryLevel = GvrControllerBatteryLevel.Full;
|
||||
|
||||
state.SetButtonsUpDownFromPrevious(lastButtonsState);
|
||||
lastButtonsState = state.buttonsState;
|
||||
|
||||
outState.CopyFrom(state);
|
||||
}
|
||||
state.ClearTransientState();
|
||||
}
|
||||
|
||||
public void OnPause() {}
|
||||
public void OnResume() {}
|
||||
|
||||
private void HandleTouchEvent(EmulatorTouchEvent touchEvent) {
|
||||
if (touchEvent.pointers.Count < 1) return;
|
||||
EmulatorTouchEvent.Pointer pointer = touchEvent.pointers[0];
|
||||
|
||||
lock (state) {
|
||||
state.touchPos = new Vector2(pointer.normalizedX, pointer.normalizedY);
|
||||
switch (touchEvent.getActionMasked()) {
|
||||
case EmulatorTouchEvent.Action.kActionDown:
|
||||
state.buttonsState |= GvrControllerButton.TouchPadTouch;
|
||||
break;
|
||||
case EmulatorTouchEvent.Action.kActionMove:
|
||||
state.buttonsState |= GvrControllerButton.TouchPadTouch;
|
||||
break;
|
||||
case EmulatorTouchEvent.Action.kActionUp:
|
||||
state.buttonsState &= ~GvrControllerButton.TouchPadTouch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleOrientationEvent(EmulatorOrientationEvent orientationEvent) {
|
||||
lastRawOrientation = ConvertEmulatorQuaternion(orientationEvent.orientation);
|
||||
if (!initialRecenterDone) {
|
||||
Recenter();
|
||||
initialRecenterDone = true;
|
||||
}
|
||||
lock (state) {
|
||||
state.orientation = yawCorrection * lastRawOrientation;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleButtonEvent(EmulatorButtonEvent buttonEvent) {
|
||||
GvrControllerButton buttonMask = 0;
|
||||
switch (buttonEvent.code) {
|
||||
case EmulatorButtonEvent.ButtonCode.kApp:
|
||||
buttonMask = GvrControllerButton.App;
|
||||
break;
|
||||
case EmulatorButtonEvent.ButtonCode.kHome:
|
||||
buttonMask = GvrControllerButton.System;
|
||||
break;
|
||||
case EmulatorButtonEvent.ButtonCode.kClick:
|
||||
buttonMask = GvrControllerButton.TouchPadButton;
|
||||
break;
|
||||
}
|
||||
if (buttonMask != 0) {
|
||||
lock (state) {
|
||||
state.buttonsState &= ~buttonMask;
|
||||
if (buttonEvent.down) {
|
||||
state.buttonsState |= buttonMask;
|
||||
}
|
||||
}
|
||||
if (buttonMask == GvrControllerButton.System) {
|
||||
if (!buttonEvent.down) {
|
||||
// Finished the recentering gesture. Recenter controller.
|
||||
Recenter();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleGyroEvent(EmulatorGyroEvent gyroEvent) {
|
||||
lock (state) {
|
||||
state.gyro = ConvertEmulatorGyro(gyroEvent.value);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleAccelEvent(EmulatorAccelEvent accelEvent) {
|
||||
lock (state) {
|
||||
state.accel = ConvertEmulatorAccel(accelEvent.value);
|
||||
}
|
||||
}
|
||||
|
||||
private static Quaternion ConvertEmulatorQuaternion(Quaternion emulatorQuat) {
|
||||
// Convert from the emulator's coordinate space to Unity's standard coordinate space.
|
||||
return new Quaternion(emulatorQuat.x, -emulatorQuat.z, emulatorQuat.y, emulatorQuat.w);
|
||||
}
|
||||
|
||||
private static Vector3 ConvertEmulatorGyro(Vector3 emulatorGyro) {
|
||||
// Convert from the emulator's coordinate space to Unity's standard coordinate space.
|
||||
return new Vector3(-emulatorGyro.x, -emulatorGyro.z, -emulatorGyro.y);
|
||||
}
|
||||
|
||||
private static Vector3 ConvertEmulatorAccel(Vector3 emulatorAccel) {
|
||||
// Convert from the emulator's coordinate space to Unity's standard coordinate space.
|
||||
return new Vector3(emulatorAccel.x, emulatorAccel.z, emulatorAccel.y);
|
||||
}
|
||||
|
||||
private void Recenter() {
|
||||
lock (state) {
|
||||
// We want the current orientation to be "forward" so, we set the yaw correction
|
||||
// to undo the current rotation's yaw.
|
||||
yawCorrection = Quaternion.AngleAxis(-lastRawOrientation.eulerAngles.y, Vector3.up);
|
||||
state.orientation = Quaternion.identity;
|
||||
state.recentered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
#endif // UNITY_EDITOR
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ca644865f5f4479fb50471605078cf0
|
||||
timeCreated: 1462051657
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,198 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
using Gvr;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Gvr.Internal {
|
||||
/// Mocks controller input by using the mouse.
|
||||
/// The controller is connected when holding left shift.
|
||||
/// Move the mouse to control gyroscope and orientation.
|
||||
/// The left mouse button is used for the clickButton.
|
||||
/// The right mouse button is used for the appButton.
|
||||
/// The middle mouse button is used for the homeButton.
|
||||
class MouseControllerProvider : IControllerProvider {
|
||||
private const string AXIS_MOUSE_X = "Mouse X";
|
||||
private const string AXIS_MOUSE_Y = "Mouse Y";
|
||||
|
||||
private ControllerState state = new ControllerState();
|
||||
|
||||
private Vector2 mouseDelta = new Vector2();
|
||||
|
||||
/// Need to store the state of the buttons from the previous frame.
|
||||
/// This is because Input.GetMouseButtonDown and Input.GetMouseButtonUp
|
||||
/// don't work when called after WaitForEndOfFrame, which is when ReadState is called.
|
||||
private bool wasTouching;
|
||||
private GvrControllerButton lastButtonsState;
|
||||
|
||||
private const float ROTATE_SENSITIVITY = 4.5f;
|
||||
private const float TOUCH_SENSITIVITY = .12f;
|
||||
private static readonly Vector3 INVERT_Y = new Vector3(1, -1, 1);
|
||||
|
||||
public static bool IsMouseAvailable {
|
||||
get {
|
||||
return Input.mousePresent && IsActivateButtonPressed;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsActivateButtonPressed {
|
||||
get {
|
||||
return Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsClickButtonPressed {
|
||||
get {
|
||||
return Input.GetMouseButton(0);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsAppButtonPressed {
|
||||
get {
|
||||
return Input.GetMouseButton(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsHomeButtonPressed {
|
||||
get {
|
||||
return Input.GetMouseButton(2);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsTouching {
|
||||
get {
|
||||
return Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
|
||||
}
|
||||
}
|
||||
|
||||
public bool SupportsBatteryStatus {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public int MaxControllerCount {
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
internal MouseControllerProvider() {}
|
||||
|
||||
public void Dispose() {}
|
||||
|
||||
public void ReadState(ControllerState outState, int controller_id) {
|
||||
if (controller_id != 0) {
|
||||
return;
|
||||
}
|
||||
lock (state) {
|
||||
UpdateState();
|
||||
|
||||
outState.CopyFrom(state);
|
||||
}
|
||||
state.ClearTransientState();
|
||||
}
|
||||
|
||||
public void OnPause() {}
|
||||
public void OnResume() {}
|
||||
|
||||
private void UpdateState() {
|
||||
GvrCursorHelper.ControllerEmulationActive = IsMouseAvailable;
|
||||
|
||||
if (!IsMouseAvailable) {
|
||||
ClearState();
|
||||
return;
|
||||
}
|
||||
|
||||
state.connectionState = GvrConnectionState.Connected;
|
||||
state.apiStatus = GvrControllerApiStatus.Ok;
|
||||
state.isCharging = false;
|
||||
state.batteryLevel = GvrControllerBatteryLevel.Full;
|
||||
|
||||
UpdateButtonStates();
|
||||
|
||||
mouseDelta.Set(
|
||||
Input.GetAxis(AXIS_MOUSE_X),
|
||||
Input.GetAxis(AXIS_MOUSE_Y)
|
||||
);
|
||||
|
||||
if (0 != (state.buttonsState & GvrControllerButton.TouchPadTouch)) {
|
||||
UpdateTouchPos();
|
||||
} else {
|
||||
UpdateOrientation();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTouchPos() {
|
||||
Vector3 currentMousePosition = Input.mousePosition;
|
||||
Vector2 touchDelta = mouseDelta * TOUCH_SENSITIVITY;
|
||||
touchDelta.y *= -1.0f;
|
||||
|
||||
state.touchPos += touchDelta;
|
||||
state.touchPos.x = Mathf.Clamp01(state.touchPos.x);
|
||||
state.touchPos.y = Mathf.Clamp01(state.touchPos.y);
|
||||
}
|
||||
|
||||
private void UpdateOrientation() {
|
||||
Vector3 deltaDegrees = Vector3.Scale(mouseDelta, INVERT_Y) * ROTATE_SENSITIVITY;
|
||||
|
||||
state.gyro = deltaDegrees * (Mathf.Deg2Rad / Time.deltaTime);
|
||||
|
||||
Quaternion yaw = Quaternion.AngleAxis(deltaDegrees.x, Vector3.up);
|
||||
Quaternion pitch = Quaternion.AngleAxis(deltaDegrees.y, Vector3.right);
|
||||
state.orientation = state.orientation * yaw * pitch;
|
||||
}
|
||||
|
||||
private void UpdateButtonStates() {
|
||||
state.buttonsState = 0;
|
||||
if (IsClickButtonPressed) {
|
||||
state.buttonsState |= GvrControllerButton.TouchPadButton;
|
||||
}
|
||||
if (IsAppButtonPressed) {
|
||||
state.buttonsState |= GvrControllerButton.App;
|
||||
}
|
||||
if (IsHomeButtonPressed) {
|
||||
state.buttonsState |= GvrControllerButton.System;
|
||||
}
|
||||
if (IsTouching) {
|
||||
state.buttonsState |= GvrControllerButton.TouchPadTouch;
|
||||
}
|
||||
|
||||
state.SetButtonsUpDownFromPrevious(lastButtonsState);
|
||||
lastButtonsState = state.buttonsState;
|
||||
|
||||
if (0 != (state.buttonsUp & GvrControllerButton.TouchPadTouch)) {
|
||||
ClearTouchPos();
|
||||
}
|
||||
|
||||
if (0 != (state.buttonsUp & GvrControllerButton.System)) {
|
||||
Recenter();
|
||||
}
|
||||
}
|
||||
|
||||
private void Recenter() {
|
||||
Quaternion yawCorrection = Quaternion.AngleAxis(-state.orientation.eulerAngles.y, Vector3.up);
|
||||
state.orientation = state.orientation * yawCorrection;
|
||||
state.recentered = true;
|
||||
}
|
||||
|
||||
private void ClearTouchPos() {
|
||||
state.touchPos = new Vector2(0.5f, 0.5f);
|
||||
}
|
||||
|
||||
private void ClearState() {
|
||||
state.connectionState = GvrConnectionState.Disconnected;
|
||||
state.buttonsState = 0;
|
||||
state.buttonsDown = 0;
|
||||
state.buttonsUp = 0;
|
||||
ClearTouchPos();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8b9c789363a44b1e87727b1c4d085f6
|
||||
timeCreated: 1496352344
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
using Gvr;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
/// Internal representation of the controller's current state.
|
||||
/// This representation is used by controller providers to represent the controller's state.
|
||||
///
|
||||
/// The fields in this class have identical meanings to their correspondents in the GVR C API,
|
||||
/// so they are not redundantly documented here.
|
||||
class ControllerState {
|
||||
internal GvrConnectionState connectionState = GvrConnectionState.Disconnected;
|
||||
internal GvrControllerApiStatus apiStatus = GvrControllerApiStatus.Unavailable;
|
||||
internal Quaternion orientation = Quaternion.identity;
|
||||
internal Vector3 position = Vector3.zero;
|
||||
internal Vector3 gyro = Vector3.zero;
|
||||
internal Vector3 accel = Vector3.zero;
|
||||
internal Vector2 touchPos = Vector2.zero;
|
||||
internal bool recentered = false;
|
||||
|
||||
internal GvrControllerButton buttonsState;
|
||||
internal GvrControllerButton buttonsDown;
|
||||
internal GvrControllerButton buttonsUp;
|
||||
|
||||
internal string errorDetails = "";
|
||||
internal IntPtr gvrPtr = IntPtr.Zero;
|
||||
|
||||
internal bool isCharging = false;
|
||||
internal GvrControllerBatteryLevel batteryLevel = GvrControllerBatteryLevel.Unknown;
|
||||
|
||||
public void CopyFrom(ControllerState other) {
|
||||
connectionState = other.connectionState;
|
||||
apiStatus = other.apiStatus;
|
||||
orientation = other.orientation;
|
||||
position = other.position;
|
||||
gyro = other.gyro;
|
||||
accel = other.accel;
|
||||
touchPos = other.touchPos;
|
||||
recentered = other.recentered;
|
||||
buttonsState = other.buttonsState;
|
||||
buttonsDown = other.buttonsDown;
|
||||
buttonsUp = other.buttonsUp;
|
||||
errorDetails = other.errorDetails;
|
||||
gvrPtr = other.gvrPtr;
|
||||
isCharging = other.isCharging;
|
||||
batteryLevel = other.batteryLevel;
|
||||
}
|
||||
|
||||
/// Resets the transient state (the state variables that represent events, and which are true
|
||||
/// for only one frame).
|
||||
public void ClearTransientState() {
|
||||
recentered = false;
|
||||
buttonsState = 0;
|
||||
buttonsDown = 0;
|
||||
buttonsUp = 0;
|
||||
}
|
||||
|
||||
public void SetButtonsUpDownFromPrevious(GvrControllerButton prevButtonsState) {
|
||||
buttonsDown = ~prevButtonsState & buttonsState;
|
||||
buttonsUp = prevButtonsState & ~buttonsState;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9159532a8a3d946aa9df74e771243e5b
|
||||
timeCreated: 1462043669
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
using Gvr;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
class ControllerUtils {
|
||||
|
||||
/// Convenience array of all hands.
|
||||
public static GvrControllerHand[] AllHands = {
|
||||
GvrControllerHand.Right,
|
||||
GvrControllerHand.Left,
|
||||
};
|
||||
|
||||
/// Returns true while the user holds down any of buttons specified in `buttons` on
|
||||
/// any controller.
|
||||
public static bool AnyButton(GvrControllerButton buttons) {
|
||||
bool ret = false;
|
||||
foreach (var hand in AllHands) {
|
||||
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
|
||||
ret |= device.GetButton(buttons);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns true in the frame the user starts pressing down any of buttons specified
|
||||
/// in `buttons` on any controller.
|
||||
public static bool AnyButtonDown(GvrControllerButton buttons) {
|
||||
bool ret = false;
|
||||
foreach (var hand in AllHands) {
|
||||
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
|
||||
ret |= device.GetButtonDown(buttons);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns true the frame after the user stops pressing down any of buttons specified
|
||||
/// in `buttons` on any controller.
|
||||
public static bool AnyButtonUp(GvrControllerButton buttons) {
|
||||
bool ret = false;
|
||||
foreach (var hand in AllHands) {
|
||||
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
|
||||
ret |= device.GetButtonUp(buttons);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8358cf789a848412fbb10ba05c4f3fce
|
||||
timeCreated: 1524508137
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c82eb0d7ee68d344bd339cec427a446
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,258 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// This class is only used in the Editor, so make sure to only compile it on that platform.
|
||||
// Additionally, If this class is compiled on Android then Unity will insert the INTERNET permission
|
||||
// into the manifest because of the reference to the type TCPClient. Excluding this class in the android
|
||||
// build ensures that it is only included if the developer using the SDK actually uses INTERNET related services.
|
||||
// This MonoBehaviour is only ever instantiated dynamically, so it is fine that it is only compiled in the Editor,
|
||||
// Otherwise it would cause serialization issues.
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
using proto;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
|
||||
public enum EmulatorClientSocketConnectionState {
|
||||
Disconnected = 0,
|
||||
Connecting = 1,
|
||||
Connected = 2,
|
||||
};
|
||||
|
||||
class EmulatorClientSocket : MonoBehaviour {
|
||||
private static readonly int kPhoneEventPort = 7003;
|
||||
private const int kSocketReadTimeoutMillis = 5000;
|
||||
|
||||
// Minimum interval, in seconds, between attempts to reconnect the socket.
|
||||
private const float kMinReconnectInterval = 1f;
|
||||
|
||||
private TcpClient phoneMirroringSocket;
|
||||
|
||||
private Thread phoneEventThread;
|
||||
|
||||
private volatile bool shouldStop = false;
|
||||
|
||||
// Flag used to limit connection state logging to initial failure and successful reconnects.
|
||||
private volatile bool lastConnectionAttemptWasSuccessful = true;
|
||||
|
||||
private EmulatorManager phoneRemote;
|
||||
public EmulatorClientSocketConnectionState connected { get; private set; }
|
||||
|
||||
public void Init(EmulatorManager remote) {
|
||||
phoneRemote = remote;
|
||||
|
||||
if (EmulatorConfig.Instance.PHONE_EVENT_MODE != EmulatorConfig.Mode.OFF) {
|
||||
phoneEventThread = new Thread(phoneEventSocketLoop);
|
||||
phoneEventThread.IsBackground = true;
|
||||
phoneEventThread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void phoneEventSocketLoop() {
|
||||
while (!shouldStop) {
|
||||
long lastConnectionAttemptTime = DateTime.Now.Ticks;
|
||||
try {
|
||||
phoneConnect();
|
||||
} catch(Exception e) {
|
||||
if (lastConnectionAttemptWasSuccessful) {
|
||||
Debug.LogWarningFormat("{0}\n{1}", e.Message, e.StackTrace);
|
||||
// Suppress additional failures until we have successfully reconnected.
|
||||
lastConnectionAttemptWasSuccessful = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait a while in order to enforce the minimum time between connection attempts.
|
||||
TimeSpan elapsed = new TimeSpan(DateTime.Now.Ticks - lastConnectionAttemptTime);
|
||||
float toWait = kMinReconnectInterval - (float) elapsed.TotalSeconds;
|
||||
if (toWait > 0) {
|
||||
Thread.Sleep((int) (toWait * 1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void phoneConnect() {
|
||||
string addr = EmulatorConfig.Instance.PHONE_EVENT_MODE == EmulatorConfig.Mode.USB
|
||||
? EmulatorConfig.USB_SERVER_IP : EmulatorConfig.WIFI_SERVER_IP;
|
||||
|
||||
try {
|
||||
if (EmulatorConfig.Instance.PHONE_EVENT_MODE == EmulatorConfig.Mode.USB) {
|
||||
setupPortForwarding(kPhoneEventPort);
|
||||
}
|
||||
TcpClient tcpClient = new TcpClient(addr, kPhoneEventPort);
|
||||
connected = EmulatorClientSocketConnectionState.Connecting;
|
||||
ProcessConnection(tcpClient);
|
||||
tcpClient.Close();
|
||||
} finally {
|
||||
connected = EmulatorClientSocketConnectionState.Disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
private void setupPortForwarding(int port) {
|
||||
#if !UNITY_WEBPLAYER
|
||||
string adbCommand = string.Format("adb forward tcp:{0} tcp:{0}", port);
|
||||
System.Diagnostics.Process myProcess = new System.Diagnostics.Process();
|
||||
string processFilename;
|
||||
string processArguments;
|
||||
int kExitCodeCommandNotFound;
|
||||
|
||||
if (Application.platform == RuntimePlatform.WindowsEditor ||
|
||||
Application.platform == RuntimePlatform.WindowsPlayer) {
|
||||
processFilename = "CMD.exe";
|
||||
processArguments = @"/k " + adbCommand + " & exit";
|
||||
// See "Common Error Lookup Tool" (https://www.microsoft.com/en-us/download/details.aspx?id=985)
|
||||
// MSG_DIR_BAD_COMMAND_OR_FILE (cmdmsg.h)
|
||||
kExitCodeCommandNotFound = 9009; // 0x2331
|
||||
} else { // Unix
|
||||
processFilename = "bash";
|
||||
processArguments = string.Format("-l -c \"{0}\"", adbCommand);
|
||||
// "command not found" (see http://tldp.org/LDP/abs/html/exitcodes.html)
|
||||
kExitCodeCommandNotFound = 127;
|
||||
}
|
||||
|
||||
System.Diagnostics.ProcessStartInfo myProcessStartInfo =
|
||||
new System.Diagnostics.ProcessStartInfo(processFilename, processArguments);
|
||||
myProcessStartInfo.UseShellExecute = false;
|
||||
myProcessStartInfo.RedirectStandardError = true;
|
||||
myProcessStartInfo.CreateNoWindow = true;
|
||||
myProcess.StartInfo = myProcessStartInfo;
|
||||
myProcess.Start();
|
||||
myProcess.WaitForExit();
|
||||
// Also wait for HasExited here, to avoid ExitCode access below occasionally throwing InvalidOperationException
|
||||
while (!myProcess.HasExited) {
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
int exitCode = myProcess.ExitCode;
|
||||
string standardError = myProcess.StandardError.ReadToEnd();
|
||||
myProcess.Close();
|
||||
|
||||
if (exitCode == 0) {
|
||||
// Port forwarding setup successfully.
|
||||
return;
|
||||
}
|
||||
|
||||
if (exitCode == kExitCodeCommandNotFound) {
|
||||
// Caught by phoneEventSocketLoop.
|
||||
throw new Exception(
|
||||
"Android Debug Bridge (`adb`) command not found." +
|
||||
"\nVerify that the Android SDK is installed and that the directory containing" +
|
||||
" `adb` is included in your PATH environment variable.");
|
||||
}
|
||||
// Caught by phoneEventSocketLoop.
|
||||
throw new Exception(
|
||||
string.Format(
|
||||
"Failed to setup port forwarding." +
|
||||
" Exit code {0} returned by process: {1} {2}\n{3}",
|
||||
exitCode, processFilename, processArguments, standardError));
|
||||
#endif // !UNITY_WEBPLAYER
|
||||
}
|
||||
|
||||
private void ProcessConnection(TcpClient tcpClient) {
|
||||
byte[] buffer = new byte[4];
|
||||
NetworkStream stream = tcpClient.GetStream();
|
||||
stream.ReadTimeout = kSocketReadTimeoutMillis;
|
||||
tcpClient.ReceiveTimeout = kSocketReadTimeoutMillis;
|
||||
while (!shouldStop) {
|
||||
int bytesRead = blockingRead(stream, buffer, 0, 4);
|
||||
if (bytesRead < 4) {
|
||||
// Caught by phoneEventSocketLoop.
|
||||
throw new Exception(
|
||||
"Failed to read from controller emulator app event socket." +
|
||||
"\nVerify that the controller emulator app is running.");
|
||||
}
|
||||
int msgLen = unpack32bits(correctEndianness(buffer), 0);
|
||||
|
||||
byte[] dataBuffer = new byte[msgLen];
|
||||
bytesRead = blockingRead(stream, dataBuffer, 0, msgLen);
|
||||
if (bytesRead < msgLen) {
|
||||
// Caught by phoneEventSocketLoop.
|
||||
throw new Exception(
|
||||
"Failed to read from controller emulator app event socket." +
|
||||
"\nVerify that the controller emulator app is running.");
|
||||
}
|
||||
|
||||
PhoneEvent proto =
|
||||
PhoneEvent.CreateBuilder().MergeFrom(dataBuffer).Build();
|
||||
phoneRemote.OnPhoneEvent(proto);
|
||||
|
||||
connected = EmulatorClientSocketConnectionState.Connected;
|
||||
|
||||
if (!lastConnectionAttemptWasSuccessful) {
|
||||
Debug.Log("Successfully connected to controller emulator app.");
|
||||
// Log first failure after after successful read from event socket.
|
||||
lastConnectionAttemptWasSuccessful = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int blockingRead(NetworkStream stream, byte[] buffer, int index,
|
||||
int count) {
|
||||
int bytesRead = 0;
|
||||
while (!shouldStop && bytesRead < count) {
|
||||
try {
|
||||
int n = stream.Read(buffer, index + bytesRead, count - bytesRead);
|
||||
if (n <= 0) {
|
||||
// Failed to read.
|
||||
return -1;
|
||||
}
|
||||
bytesRead += n;
|
||||
} catch (IOException) {
|
||||
// Read failed or timed out.
|
||||
return -1;
|
||||
} catch (ObjectDisposedException) {
|
||||
// Socket closed.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
void OnDestroy() {
|
||||
shouldStop = true;
|
||||
|
||||
if (phoneMirroringSocket != null) {
|
||||
phoneMirroringSocket.Close ();
|
||||
phoneMirroringSocket = null;
|
||||
}
|
||||
|
||||
if (phoneEventThread != null) {
|
||||
phoneEventThread.Join();
|
||||
}
|
||||
}
|
||||
|
||||
private int unpack32bits(byte[] array, int offset) {
|
||||
int num = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
num += array [offset + i] << (i * 8);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
static private byte[] correctEndianness(byte[] array) {
|
||||
if (BitConverter.IsLittleEndian)
|
||||
Array.Reverse(array);
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
#endif // UNITY_EDITOR
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bf1f92fb4ae24291b71e77c1ccac323
|
||||
timeCreated: 1462051657
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
class EmulatorConfig : MonoBehaviour {
|
||||
public static EmulatorConfig Instance {
|
||||
get {
|
||||
if (instance == null) {
|
||||
EmulatorConfig[] configs = (EmulatorConfig[]) FindObjectsOfType(typeof(EmulatorConfig));
|
||||
if (configs.Length == 1) {
|
||||
instance = configs[0];
|
||||
} else if (configs.Length > 1) {
|
||||
Debug.LogError(
|
||||
"Multiple PhoneRemote/Config objects in scene. Ignoring all.");
|
||||
}
|
||||
}
|
||||
if (instance == null) {
|
||||
var gameObject = new GameObject("PhoneRemoteConfig");
|
||||
instance = gameObject.AddComponent<EmulatorConfig>();
|
||||
DontDestroyOnLoad(instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
private static EmulatorConfig instance = null;
|
||||
|
||||
public enum Mode {
|
||||
OFF,
|
||||
USB,
|
||||
WIFI,
|
||||
}
|
||||
|
||||
// Set this value to match how the PC is connected to the phone that is
|
||||
// streaming gyro, accel, and touch events. Set to OFF if using Wifi instead.
|
||||
public Mode PHONE_EVENT_MODE = Mode.USB;
|
||||
|
||||
/*----- Internal Parameters (should not require any changes). -----*/
|
||||
|
||||
// IP address of the phone, when connected to the PC via USB.
|
||||
public static readonly string USB_SERVER_IP = "127.0.0.1";
|
||||
|
||||
// IP address of the phone, when connected to the PC via WiFi.
|
||||
public static readonly string WIFI_SERVER_IP = "192.168.43.1";
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a229fefd8ee7448b0b700f6000ebdec3
|
||||
timeCreated: 1462051657
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,189 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using proto;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
struct EmulatorGyroEvent {
|
||||
public readonly long timestamp;
|
||||
public readonly Vector3 value;
|
||||
|
||||
public EmulatorGyroEvent(PhoneEvent.Types.GyroscopeEvent proto) {
|
||||
timestamp = proto.Timestamp;
|
||||
value = new Vector3(proto.X, proto.Y, proto.Z);
|
||||
}
|
||||
}
|
||||
|
||||
struct EmulatorAccelEvent {
|
||||
public readonly long timestamp;
|
||||
public readonly Vector3 value;
|
||||
|
||||
public EmulatorAccelEvent(PhoneEvent.Types.AccelerometerEvent proto) {
|
||||
timestamp = proto.Timestamp;
|
||||
value = new Vector3(proto.X, proto.Y, proto.Z);
|
||||
}
|
||||
}
|
||||
|
||||
struct EmulatorTouchEvent {
|
||||
// Action constants. These should match the constants in the Android
|
||||
// MotionEvent:
|
||||
// http://developer.android.com/reference/android/view/MotionEvent.html#ACTION_CANCEL
|
||||
public enum Action {
|
||||
kActionDown = 0,
|
||||
kActionUp = 1,
|
||||
kActionMove = 2,
|
||||
kActionCancel = 3,
|
||||
kActionPointerDown = 5,
|
||||
kActionPointerUp = 6,
|
||||
kActionHoverMove = 7,
|
||||
kActionHoverEnter = 9,
|
||||
kActionHoverExit = 10
|
||||
};
|
||||
|
||||
// Use getActionMasked() and getActionPointer() instead.
|
||||
private readonly int action;
|
||||
public readonly int relativeTimestamp;
|
||||
public readonly List<Pointer> pointers;
|
||||
|
||||
public struct Pointer {
|
||||
public readonly int fingerId;
|
||||
public readonly float normalizedX;
|
||||
public readonly float normalizedY;
|
||||
|
||||
public Pointer(int fingerId, float normalizedX, float normalizedY) {
|
||||
this.fingerId = fingerId;
|
||||
this.normalizedX = normalizedX;
|
||||
this.normalizedY = normalizedY;
|
||||
}
|
||||
|
||||
public override string ToString () {
|
||||
return string.Format ("({0}, {1}, {2})", fingerId, normalizedX,
|
||||
normalizedY);
|
||||
}
|
||||
}
|
||||
|
||||
public EmulatorTouchEvent(PhoneEvent.Types.MotionEvent proto, long lastDownTimeMs) {
|
||||
action = proto.Action;
|
||||
relativeTimestamp =
|
||||
(Action)(proto.Action & ACTION_MASK) == Action.kActionDown
|
||||
? 0 : (int) (proto.Timestamp - lastDownTimeMs);
|
||||
pointers = new List<Pointer>();
|
||||
foreach (PhoneEvent.Types.MotionEvent.Types.Pointer pointer in
|
||||
proto.PointersList) {
|
||||
pointers.Add(
|
||||
new Pointer(pointer.Id, pointer.NormalizedX, pointer.NormalizedY));
|
||||
}
|
||||
}
|
||||
|
||||
public EmulatorTouchEvent(Action action, int pointerId, int relativeTimestamp,
|
||||
List<Pointer> pointers) {
|
||||
int fingerIndex = 0;
|
||||
if (action == Action.kActionPointerDown
|
||||
|| action == Action.kActionPointerUp) {
|
||||
fingerIndex = findPointerIndex(pointerId, pointers);
|
||||
if (fingerIndex == -1) {
|
||||
Debug.LogWarning("Could not find specific fingerId " + pointerId
|
||||
+ " in the supplied list of pointers.");
|
||||
fingerIndex = 0;
|
||||
}
|
||||
}
|
||||
this.action = getActionUnmasked(action, fingerIndex);
|
||||
this.relativeTimestamp = relativeTimestamp;
|
||||
this.pointers = pointers;
|
||||
}
|
||||
|
||||
// See Android's getActionMasked() and getActionIndex().
|
||||
private static readonly int ACTION_POINTER_INDEX_SHIFT = 8;
|
||||
private static readonly int ACTION_POINTER_INDEX_MASK = 0xff00;
|
||||
private static readonly int ACTION_MASK = 0xff;
|
||||
|
||||
public Action getActionMasked() {
|
||||
return (Action)(action & ACTION_MASK);
|
||||
}
|
||||
|
||||
public Pointer getActionPointer() {
|
||||
int index =
|
||||
(action & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
|
||||
return pointers[index];
|
||||
}
|
||||
|
||||
|
||||
private static int getActionUnmasked(Action action, int fingerIndex) {
|
||||
return ((int)action) | (fingerIndex << ACTION_POINTER_INDEX_SHIFT);
|
||||
}
|
||||
|
||||
private static int findPointerIndex(int fingerId, List<Pointer> pointers) {
|
||||
// Encode the fingerId info into the action, as Android does. See Android's
|
||||
// getActionMasked() and getActionIndex().
|
||||
int fingerIndex = -1;
|
||||
for (int i = 0; i < pointers.Count; i++) {
|
||||
if (fingerId == pointers[i].fingerId) {
|
||||
fingerIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fingerIndex;
|
||||
}
|
||||
|
||||
public override string ToString () {
|
||||
System.Text.StringBuilder builder = new System.Text.StringBuilder ();
|
||||
builder.AppendFormat("t = {0}; A = {1}; P = {2}; N = {3}; [",
|
||||
relativeTimestamp, getActionMasked (), getActionPointer ().fingerId,
|
||||
pointers.Count);
|
||||
for (int i = 0; i < pointers.Count; i++) {
|
||||
builder.Append(pointers[i]).Append (", ");
|
||||
}
|
||||
builder.Append ("]");
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
struct EmulatorOrientationEvent {
|
||||
public readonly long timestamp;
|
||||
public readonly Quaternion orientation;
|
||||
|
||||
public EmulatorOrientationEvent(PhoneEvent.Types.OrientationEvent proto) {
|
||||
timestamp = proto.Timestamp;
|
||||
// Convert from right-handed coordinates to left-handed.
|
||||
orientation = new Quaternion(proto.X, proto.Y, -proto.Z, proto.W);
|
||||
}
|
||||
}
|
||||
|
||||
struct EmulatorButtonEvent {
|
||||
// Codes as reported by the IC app (reuses Android KeyEvent codes).
|
||||
public enum ButtonCode {
|
||||
kNone = 0,
|
||||
kHome = 3, // android.view.KeyEvent.KEYCODE_HOME
|
||||
kVolumeUp = 25, // android.view.KeyEvent.KEYCODE_VOLUME_UP
|
||||
kVolumeDown = 24, // android.view.KeyEvent.KEYCODE_VOLUME_DOWN
|
||||
kClick = 66, // android.view.KeyEvent.KEYCODE_ENTER
|
||||
kApp = 82, // android.view.KeyEvent.KEYCODE_MENU
|
||||
}
|
||||
|
||||
public readonly ButtonCode code;
|
||||
public readonly bool down;
|
||||
public EmulatorButtonEvent(PhoneEvent.Types.KeyEvent proto) {
|
||||
code = (ButtonCode) proto.Code;
|
||||
down = proto.Action == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76d2b695633884daf905c07095c8a01c
|
||||
timeCreated: 1462051657
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,250 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// This class is only used in the Editor, so make sure to only compile it on that platform.
|
||||
// Additionally, it depends on EmulatorClientSocket which is only compiled in the editor.
|
||||
// This MonoBehaviour is only ever instantiated dynamically, so it is fine that it is only compiled in the Editor,
|
||||
// Otherwise it would cause serialization issues.
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
using proto;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
class EmulatorManager : MonoBehaviour {
|
||||
|
||||
private IEnumerator emulatorUpdate;
|
||||
private WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();
|
||||
|
||||
public static EmulatorManager Instance {
|
||||
get {
|
||||
if (instance == null) {
|
||||
var gameObject = new GameObject("PhoneRemote");
|
||||
instance = gameObject.AddComponent<EmulatorManager>();
|
||||
// This object should survive all scene transitions.
|
||||
GameObject.DontDestroyOnLoad(instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
private static EmulatorManager instance = null;
|
||||
|
||||
public delegate void OnGyroEvent(EmulatorGyroEvent gyroEvent);
|
||||
public event OnGyroEvent gyroEventListeners {
|
||||
add {
|
||||
if (value != null) {
|
||||
value(currentGyroEvent);
|
||||
}
|
||||
gyroEventListenersInternal += value;
|
||||
}
|
||||
|
||||
remove {
|
||||
gyroEventListenersInternal -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void OnAccelEvent(EmulatorAccelEvent accelEvent);
|
||||
public event OnAccelEvent accelEventListeners {
|
||||
add {
|
||||
if (value != null) {
|
||||
value(currentAccelEvent);
|
||||
}
|
||||
accelEventListenersInternal += value;
|
||||
}
|
||||
|
||||
remove {
|
||||
accelEventListenersInternal -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void OnTouchEvent(EmulatorTouchEvent touchEvent);
|
||||
public event OnTouchEvent touchEventListeners {
|
||||
add {
|
||||
if (value != null
|
||||
&& currentTouchEvent.pointers != null /* null only during init */) {
|
||||
value(currentTouchEvent);
|
||||
}
|
||||
touchEventListenersInternal += value;
|
||||
}
|
||||
|
||||
remove {
|
||||
touchEventListenersInternal -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void OnOrientationEvent(EmulatorOrientationEvent orientationEvent);
|
||||
public event OnOrientationEvent orientationEventListeners {
|
||||
add {
|
||||
if (value != null) {
|
||||
value(currentOrientationEvent);
|
||||
}
|
||||
orientationEventListenersInternal += value;
|
||||
}
|
||||
|
||||
remove {
|
||||
orientationEventListenersInternal -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void OnButtonEvent(EmulatorButtonEvent buttonEvent);
|
||||
public event OnButtonEvent buttonEventListeners {
|
||||
add {
|
||||
if (value != null) {
|
||||
value(currentButtonEvent);
|
||||
}
|
||||
buttonEventListenersInternal += value;
|
||||
}
|
||||
|
||||
remove {
|
||||
buttonEventListenersInternal -= value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void onGyroEvent(EmulatorGyroEvent e) {
|
||||
currentGyroEvent = e;
|
||||
if (gyroEventListenersInternal != null) {
|
||||
gyroEventListenersInternal(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onAccelEvent(EmulatorAccelEvent e) {
|
||||
currentAccelEvent = e;
|
||||
if (accelEventListenersInternal != null) {
|
||||
accelEventListenersInternal(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onTouchEvent(EmulatorTouchEvent e) {
|
||||
currentTouchEvent = e;
|
||||
if (touchEventListenersInternal != null) {
|
||||
touchEventListenersInternal(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onOrientationEvent(EmulatorOrientationEvent e) {
|
||||
currentOrientationEvent = e;
|
||||
if (orientationEventListenersInternal != null) {
|
||||
orientationEventListenersInternal(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onButtonEvent(EmulatorButtonEvent e) {
|
||||
currentButtonEvent = e;
|
||||
if (buttonEventListenersInternal != null) {
|
||||
buttonEventListenersInternal(e);
|
||||
}
|
||||
}
|
||||
|
||||
EmulatorGyroEvent currentGyroEvent;
|
||||
EmulatorAccelEvent currentAccelEvent;
|
||||
EmulatorTouchEvent currentTouchEvent;
|
||||
EmulatorOrientationEvent currentOrientationEvent;
|
||||
EmulatorButtonEvent currentButtonEvent;
|
||||
|
||||
private event OnGyroEvent gyroEventListenersInternal;
|
||||
private event OnAccelEvent accelEventListenersInternal;
|
||||
private event OnTouchEvent touchEventListenersInternal;
|
||||
private event OnOrientationEvent orientationEventListenersInternal;
|
||||
private event OnButtonEvent buttonEventListenersInternal;
|
||||
|
||||
private Queue pendingEvents = Queue.Synchronized(new Queue());
|
||||
private EmulatorClientSocket socket;
|
||||
private long lastDownTimeMs;
|
||||
|
||||
public bool Connected {
|
||||
get {
|
||||
return socket != null && socket.connected == EmulatorClientSocketConnectionState.Connected;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Connecting {
|
||||
get {
|
||||
return socket != null && socket.connected == EmulatorClientSocketConnectionState.Connecting;
|
||||
}
|
||||
}
|
||||
|
||||
public void Awake() {
|
||||
if (instance == null) {
|
||||
instance = this;
|
||||
}
|
||||
if (instance != this) {
|
||||
Debug.LogWarning("PhoneRemote must be a singleton.");
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void Start() {
|
||||
socket = gameObject.AddComponent<EmulatorClientSocket>();
|
||||
socket.Init(this);
|
||||
emulatorUpdate = EndOfFrame();
|
||||
StartCoroutine(emulatorUpdate);
|
||||
}
|
||||
|
||||
IEnumerator EndOfFrame() {
|
||||
while (true) {
|
||||
yield return waitForEndOfFrame;
|
||||
lock (pendingEvents.SyncRoot) {
|
||||
while (pendingEvents.Count > 0) {
|
||||
PhoneEvent phoneEvent = (PhoneEvent) pendingEvents.Dequeue();
|
||||
ProcessEventAtEndOfFrame(phoneEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPhoneEvent(PhoneEvent e) {
|
||||
pendingEvents.Enqueue(e);
|
||||
}
|
||||
|
||||
private void ProcessEventAtEndOfFrame(PhoneEvent e) {
|
||||
switch (e.Type) {
|
||||
case PhoneEvent.Types.Type.MOTION:
|
||||
EmulatorTouchEvent touchEvent = new EmulatorTouchEvent(e.MotionEvent, lastDownTimeMs);
|
||||
onTouchEvent(touchEvent);
|
||||
if (touchEvent.getActionMasked() == EmulatorTouchEvent.Action.kActionDown) {
|
||||
lastDownTimeMs = e.MotionEvent.Timestamp;
|
||||
}
|
||||
break;
|
||||
case PhoneEvent.Types.Type.GYROSCOPE:
|
||||
EmulatorGyroEvent gyroEvent = new EmulatorGyroEvent(e.GyroscopeEvent);
|
||||
onGyroEvent(gyroEvent);
|
||||
break;
|
||||
case PhoneEvent.Types.Type.ACCELEROMETER:
|
||||
EmulatorAccelEvent accelEvent = new EmulatorAccelEvent(e.AccelerometerEvent);
|
||||
onAccelEvent(accelEvent);
|
||||
break;
|
||||
case PhoneEvent.Types.Type.ORIENTATION:
|
||||
EmulatorOrientationEvent orientationEvent =
|
||||
new EmulatorOrientationEvent(e.OrientationEvent);
|
||||
onOrientationEvent(orientationEvent);
|
||||
break;
|
||||
case PhoneEvent.Types.Type.KEY:
|
||||
EmulatorButtonEvent buttonEvent = new EmulatorButtonEvent(e.KeyEvent);
|
||||
onButtonEvent(buttonEvent);
|
||||
break;
|
||||
default:
|
||||
Debug.Log("Unsupported PhoneEvent type: " + e.Type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
#endif // UNITY_EDITOR
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c42ca6bb02b364893b127c681c158442
|
||||
timeCreated: 1462051658
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3506
Assets/GoogleVR/Scripts/Controller/Internal/Emulator/PhoneEvent.cs
Normal file
3506
Assets/GoogleVR/Scripts/Controller/Internal/Emulator/PhoneEvent.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a6b456eb0cd540a489e0f82c377b187
|
||||
timeCreated: 1462046540
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
using System;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
/// Internal interface that abstracts an implementation of a controller.
|
||||
///
|
||||
/// Each platform has a different concrete implementation of a Controller Provider.
|
||||
/// For example, if running on the Unity Editor, we use an implementation that
|
||||
/// communicates with the controller emulator via USB or WiFi. If running on a real
|
||||
/// Android device, we use an implementation that uses the underlying Daydream controller API.
|
||||
interface IControllerProvider : IDisposable {
|
||||
/// True if controller has battery status support.
|
||||
bool SupportsBatteryStatus { get; }
|
||||
|
||||
/// Reads the number of controllers the system is configured to use. This does not
|
||||
/// indicate the number of currently connected controllers.
|
||||
int MaxControllerCount { get; }
|
||||
|
||||
/// Notifies the controller provider that the application has paused.
|
||||
void OnPause();
|
||||
|
||||
/// Notifies the controller provider that the application has resumed.
|
||||
void OnResume();
|
||||
|
||||
/// Reads the controller's current state and stores it in outState.
|
||||
void ReadState(ControllerState outState, int controller_id);
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a546592901a23411d99a5fef0ada01e7
|
||||
timeCreated: 1462043673
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user