using System.Numerics;
/****************************************************************************
* Copyright 2019 Nreal Techonology Limited. All rights reserved.
*
* This file is part of NRSDK.
*
* https://www.nreal.ai/
*
*****************************************************************************/
namespace NRKernal
{
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// A controller tracker.
public class ControllerTracker : MonoBehaviour
{
/// The default hand enum.
public ControllerHandEnum defaultHandEnum;
/// The raycaster.
public NRPointerRaycaster raycaster;
/// The model anchor.
public Transform modelAnchor;
/// True if is enabled, false if not.
private bool m_IsEnabled;
/// True if is 6dof, false if not.
private bool m_Is6dof;
/// The default local offset.
private Vector3 m_DefaultLocalOffset;
/// Cache world matrix.
private Matrix4x4 m_CachedWorldMatrix = Matrix4x4.identity;
/// Gets the camera center.
/// The camera center.
private Transform CameraCenter
{
get
{
return NRInput.CameraCenter;
}
}
/// Awakes this object.
private void Awake()
{
m_DefaultLocalOffset = transform.localPosition;
raycaster.RelatedHand = defaultHandEnum;
}
/// Executes the 'enable' action.
private void OnEnable()
{
NRInput.OnControllerRecentering += OnRecentering;
NRInput.OnControllerStatesUpdated += OnControllerStatesUpdated;
NRHMDPoseTracker.OnWorldPoseReset += OnWorldPoseReset;
}
/// Executes the 'disable' action.
private void OnDisable()
{
NRInput.OnControllerRecentering -= OnRecentering;
NRInput.OnControllerStatesUpdated -= OnControllerStatesUpdated;
NRHMDPoseTracker.OnWorldPoseReset -= OnWorldPoseReset;
}
private void Start()
{
m_Is6dof = NRInput.GetControllerAvailableFeature(ControllerAvailableFeature.CONTROLLER_AVAILABLE_FEATURE_POSITION)
&& NRInput.GetControllerAvailableFeature(ControllerAvailableFeature.CONTROLLER_AVAILABLE_FEATURE_ROTATION);
}
/// Executes the 'controller states updated' action.
private void OnControllerStatesUpdated()
{
UpdateTracker();
}
/// Updates the tracker.
private void UpdateTracker()
{
if (CameraCenter == null)
return;
m_IsEnabled = NRInput.CheckControllerAvailable(defaultHandEnum) && !NRInput.Hands.IsRunning;
raycaster.gameObject.SetActive(m_IsEnabled && NRInput.RaycastersActive && NRInput.RaycastMode == RaycastModeEnum.Laser);
modelAnchor.gameObject.SetActive(m_IsEnabled);
if (m_IsEnabled)
{
TrackPose();
}
}
/// Track pose.
private void TrackPose()
{
Pose poseInAPIWorld = new Pose(NRInput.GetPosition(defaultHandEnum), NRInput.GetRotation(defaultHandEnum));
Pose pose = ApplyWorldMatrix(poseInAPIWorld);
transform.position = m_Is6dof ? pose.position : CameraCenter.TransformPoint(m_DefaultLocalOffset);
transform.rotation = pose.rotation;
}
/// Apply world transform.
private Pose ApplyWorldMatrix(Pose pose)
{
var objectMatrix = ConversionUtility.GetTMatrix(pose.position, pose.rotation);
var object_in_world = m_CachedWorldMatrix * objectMatrix;
return new Pose(ConversionUtility.GetPositionFromTMatrix(object_in_world),
ConversionUtility.GetRotationFromTMatrix(object_in_world));
}
///
/// Recenter the φ coordinate of laser to make sure the laser is pointing to forward of camera. But the θ coordinate of the laser keeps in sync with controller device.
///
private void OnRecentering()
{
Plane horizontal_plane = new Plane(Vector3.up, Vector3.zero);
Vector3 horizontalFoward = horizontal_plane.ClosestPointOnPlane(CameraCenter.forward).normalized;
var horizontalRotEuler = Quaternion.LookRotation(horizontalFoward, Vector3.up).eulerAngles;
// var worldMatrix = NRSessionManager.Instance.NRHMDPoseTracker.GetWorldOffsetMatrixFromNative();
// var worldRot = ConversionUtility.GetRotationFromTMatrix(worldMatrix);
// Quaternion correctRot = worldRot * Quaternion.Euler(0, horizontalRotEuler.y, 0);
var verticalDegree = NRSessionManager.Instance.NRHMDPoseTracker.GetCachedWorldPitch();
// Use the yaw of camera and the pitch of the world offset from native.
Quaternion correctRot = Quaternion.Euler(verticalDegree, 0, 0) * Quaternion.Euler(0, horizontalRotEuler.y, 0);
// For 6dof controller, the position should be cached as pose of controller device is reset.
Vector3 position = m_Is6dof ? transform.position : Vector3.zero;
m_CachedWorldMatrix = ConversionUtility.GetTMatrix(position, correctRot);
NRDebugger.Info("[ControllerTracker] OnRecentering : forward={0}, horRot={1}, vertRot={2}, correctRot={3}",
CameraCenter.forward.ToString("F4"), horizontalRotEuler.ToString("F4"), verticalDegree.ToString("F4"), correctRot.eulerAngles.ToString("F4"));
}
private void OnWorldPoseReset()
{
NRInput.RecenterController();
}
}
}