FittsLaw/Assets/GoogleVR/Scripts/GvrEditorEmulator.cs
2018-10-08 23:54:11 -04:00

171 lines
6.2 KiB
C#

// 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 UnityEngine;
using System;
using System.Collections.Generic;
using Gvr.Internal;
/// Provides mouse-controlled head tracking emulation in the Unity editor.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrEditorEmulator")]
public class GvrEditorEmulator : MonoBehaviour {
// GvrEditorEmulator should only be compiled in the Editor.
//
// Otherwise, it will override the camera pose every frame on device which causes the
// following behaviour:
//
// The rendered camera pose will still be correct because the VR.InputTracking pose
// gets applied after LateUpdate has occured. However, any functionality that
// queries the camera pose during Update or LateUpdate after GvrEditorEmulator has been
// updated will get the wrong value applied by GvrEditorEmulator intsead.
#if UNITY_EDITOR
private static GvrEditorEmulator instance;
private static bool instance_searched_for = false;
public static GvrEditorEmulator Instance {
get {
if (instance == null && !instance_searched_for) {
instance = FindObjectOfType<GvrEditorEmulator>();
instance_searched_for = true;
}
return instance;
}
}
// Allocate an initial capacity; this will be resized if needed.
private static Camera[] AllCameras = new Camera[32];
private const string AXIS_MOUSE_X = "Mouse X";
private const string AXIS_MOUSE_Y = "Mouse Y";
// Simulated neck model. Vector from the neck pivot point to the point between the eyes.
private static readonly Vector3 NECK_OFFSET = new Vector3(0, 0.075f, 0.08f);
// Use mouse to emulate head in the editor.
// These variables must be static so that head pose is maintained between scene changes,
// as it is on device.
private float mouseX = 0;
private float mouseY = 0;
private float mouseZ = 0;
public Vector3 HeadPosition { get; private set; }
public Quaternion HeadRotation { get; private set; }
public void Recenter() {
mouseX = mouseZ = 0; // Do not reset pitch, which is how it works on the phone.
UpdateHeadPositionAndRotation();
ApplyHeadOrientationToVRCameras();
}
public void UpdateEditorEmulation() {
if (GvrControllerInput.Recentered) {
Recenter();
}
bool rolled = false;
if (CanChangeYawPitch()) {
GvrCursorHelper.HeadEmulationActive = true;
mouseX += Input.GetAxis(AXIS_MOUSE_X) * 5;
if (mouseX <= -180) {
mouseX += 360;
} else if (mouseX > 180) {
mouseX -= 360;
}
mouseY -= Input.GetAxis(AXIS_MOUSE_Y) * 2.4f;
mouseY = Mathf.Clamp(mouseY, -85, 85);
} else if (CanChangeRoll()) {
GvrCursorHelper.HeadEmulationActive = true;
rolled = true;
mouseZ += Input.GetAxis(AXIS_MOUSE_X) * 5;
mouseZ = Mathf.Clamp(mouseZ, -85, 85);
} else {
GvrCursorHelper.HeadEmulationActive = false;
}
if (!rolled) {
// People don't usually leave their heads tilted to one side for long.
mouseZ = Mathf.Lerp(mouseZ, 0, Time.deltaTime / (Time.deltaTime + 0.1f));
}
UpdateHeadPositionAndRotation();
ApplyHeadOrientationToVRCameras();
}
void Awake() {
if (Instance == null) {
instance = this;
} else if (Instance != this) {
Debug.LogError("More than one active GvrEditorEmulator instance was found in your scene. "
+ "Ensure that there is only one active GvrEditorEmulator.");
this.enabled = false;
return;
}
}
void Update() {
// GvrControllerInput automatically updates GvrEditorEmulator.
// This guarantees that GvrEditorEmulator is updated before anything else responds to
// controller input, which ensures that re-centering works correctly in the editor.
// If GvrControllerInput is not available, then fallback to using Update().
if (GvrControllerInput.ApiStatus != GvrControllerApiStatus.Error) {
return;
}
UpdateEditorEmulation();
}
private bool CanChangeYawPitch() {
// If the MouseControllerProvider is currently active, then don't move the camera.
if (MouseControllerProvider.IsActivateButtonPressed) {
return false;
}
return Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt);
}
private bool CanChangeRoll() {
// If the MouseControllerProvider is currently active, then don't move the camera.
if (MouseControllerProvider.IsActivateButtonPressed) {
return false;
}
return Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
}
private void UpdateHeadPositionAndRotation() {
HeadRotation = Quaternion.Euler(mouseY, mouseX, mouseZ);
HeadPosition = HeadRotation * NECK_OFFSET - NECK_OFFSET.y * Vector3.up;
}
private void ApplyHeadOrientationToVRCameras() {
// Get all Cameras in the scene using persistent data structures.
if (Camera.allCamerasCount > AllCameras.Length) {
int newAllCamerasSize = Camera.allCamerasCount;
while (Camera.allCamerasCount > newAllCamerasSize) {
newAllCamerasSize *= 2;
}
AllCameras = new Camera[newAllCamerasSize];
}
// The GetAllCameras method doesn't allocate memory (Camera.allCameras does).
Camera.GetAllCameras(AllCameras);
// Update all VR cameras using Head position and rotation information.
for (int i=0; i < Camera.allCamerasCount; ++i) {
Camera cam = AllCameras[i];
// Check if the Camera is a valid VR Camera, and if so update it to track head motion.
if (cam && cam.enabled && cam.stereoTargetEye != StereoTargetEyeMask.None) {
cam.transform.localPosition = HeadPosition * cam.transform.lossyScale.y;
cam.transform.localRotation = HeadRotation;
}
}
}
#endif // UNITY_EDITOR
}