// Copyright 2016 Nibiru. 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 NibiruAxis;
using UnityEngine;
/// This script provides head tracking support for a camera.
///
/// Attach this script to any game object that should match the user's head motion.
/// By default, it continuously updates the local transform to NxrViewer.HeadView.
/// A target object may be specified to provide an alternate reference frame for the motion.
///
/// This script will typically be attached directly to a _Camera_ object, or to its
/// parent if you need to offset the camera from the origin.
/// Alternatively it can be inserted as a child of the _Camera_ but parent of the
/// NxrEye camera. Do this if you already have steering logic driving the
/// mono Camera and wish to have the user's head motion be relative to that. Note
/// that in the latter setup, head tracking is visible only when VR Mode is enabled.
///
/// In some cases you may need two instances of NxrHead, referring to two
/// different targets (one of which may be the parent), in order to split where
/// the rotation is applied from where the positional offset is applied. Use the
/// #trackRotation and #trackPosition properties in this case.
namespace Nxr.Internal
{
[AddComponentMenu("NXR/NxrHead")]
public class NxrHead : MonoBehaviour
{
public Vector3 BasePosition { set; get; }
/// Determines whether to apply the user's head rotation to this gameobject's
/// orientation. True means to update the gameobject's orientation with the
/// user's head rotation, and false means don't modify the gameobject's orientation.
private bool trackRotation = true;
/// Determines whether to apply ther user's head offset to this gameobject's
/// position. True means to update the gameobject's position with the user's head offset,
/// and false means don't modify the gameobject's position.
private bool trackPosition = false;
///
/// Whether track position
///
///
public void SetTrackPosition(bool b)
{
Debug.Log("NxrHead.SetTrackPosition." + b);
trackPosition = b;
}
///
/// Whether track rotation
///
///
public void SetTrackRotation(bool b)
{
trackRotation = b;
}
public bool IsTrackRotation()
{
return trackRotation;
}
public bool IsTrackPosition()
{
return trackPosition;
}
public void Update3rdPartyPosition(Vector3 pos)
{
if (NxrViewer.Instance.UseThirdPartyPosition)
{
mTransform.position = pos;
}
}
void Awake()
{
NxrViewer.Create();
}
protected Transform mTransform;
public Transform GetTransform()
{
return mTransform;
}
void Start()
{
mTransform = this.transform;
}
// Normally, update head pose now.
void LateUpdate()
{
NxrViewer.Instance.UpdateHeadPose();
UpdateHead();
}
// 初始的Yaw欧拉角,有时进入时正方向有偏转,此时需要校正一下
private float initEulerYAngle = float.MaxValue;
private float totalTime = 0;
private bool triggerLerp = false;
// 初次进入,方向回正后,进行reset操作
private bool hasResetTracker = true;
private float moveSpeed = 2.0f;
// Compute new head pose.
private void UpdateHead()
{
if (NxrGlobal.hasInfinityARSDK)
{
trackRotation = false;
trackPosition = false;
}
if (NxrViewer.Instance.GetNibiruService() != null && NxrViewer.Instance.GetNibiruService().IsMarkerRecognizeRunning)
{
if (NxrGlobal.isMarkerVisible)
{
// Marker识别成功时,取消NxrHead的效果
trackRotation = false;
trackPosition = false;
return;
}
else
{
// Marker识别失败时,恢复头部转动效果
trackRotation = true;
trackPosition = false;
}
}
if (trackRotation)
{
float[] eulerRange = NxrViewer.Instance.GetHeadEulerAnglesRange();
Quaternion rot = NxrViewer.Instance.HeadPose.Orientation;
/*
if (rot.eulerAngles.y != 0 && initEulerYAngle == float.MaxValue)
{
initEulerYAngle = rot.eulerAngles.y;
if (float.IsNaN(initEulerYAngle))
{
Debug.Log("DATA IS ABNORMAL--------------------------->>>>>>>>>");
initEulerYAngle = float.MaxValue;
}
}
if (initEulerYAngle != float.MaxValue && NxrViewer.Instance.InitialRecenter && !triggerLerp
&& (Mathf.Abs(initEulerYAngle) <= 345 && Mathf.Abs(initEulerYAngle) >= 15))
{
// 初始位置有偏移,只要角度偏差很大时才进行校正操作
triggerLerp = true;
moveSpeed += (Mathf.Abs(initEulerYAngle) > 60 ? 0.3f : Mathf.Abs(initEulerYAngle) > 90 ? 0.5f : 0);
Debug.Log("triggerLerp.yaw=" + initEulerYAngle + ", sp=" + moveSpeed);
}
if (triggerLerp)
{
totalTime += Time.deltaTime * 2.10f;
if (totalTime > 1)
{
if (!hasResetTracker)
{
// 校正完毕,进行reset操作
rot.eulerAngles = new Vector3(rot.eulerAngles.x, rot.eulerAngles.y - initEulerYAngle, rot.eulerAngles.z);
hasResetTracker = true;
NxrViewer.Instance.ResetHeadTrackerFromAndroid();
}
}
else
{
rot.eulerAngles = new Vector3(rot.eulerAngles.x, Mathf.LerpAngle(initEulerYAngle, 0, totalTime), rot.eulerAngles.z);
}
}
*/
if(!hasResetTracker)
{
hasResetTracker = true;
NxrViewer.Instance.Recenter();
}
Vector3 eulerAngles = rot.eulerAngles;
if (eulerRange == null ||
(
// 水平有限制
(eulerRange != null && (eulerAngles[1] >= eulerRange[0] || eulerAngles[1] < eulerRange[1]) &&
// 垂直有限制
(eulerAngles[0] >= eulerRange[2] || eulerAngles[0] < eulerRange[3]))
)
)
{
mTransform.localRotation = rot;
}
}
#if UNITY_STANDALONE_WIN || ANDROID_REMOTE_NRR
Vector3 pos = NxrViewer.Instance.HeadPose.Position;
if (pos.x !=0 && pos.y !=0 && pos.z != 0)
{
mTransform.localPosition = BasePosition + pos;
}
#elif UNITY_ANDROID
if (trackPosition)
{
Vector3 pos = NxrViewer.Instance.HeadPose.Position;
mTransform.localPosition = BasePosition + pos;
if (NxrPlayerCtrl.Instance != null)
{
NxrPlayerCtrl.Instance.HeadPosition = mTransform.position;
}
}
#endif
}
public void ResetInitEulerYAngle()
{
initEulerYAngle = 0;
}
#if UNITY_EDITOR
private void Update()
{
Vector3 start = transform.position;
Vector3 vector = transform.TransformDirection(Vector3.forward);
UnityEngine.Debug.DrawRay(start, vector * 20, Color.red);
}
#endif
}
}