jimmy tremblay-Bernier c1bf5a4ca1 initial commit
2022-03-12 22:04:30 -04:00

717 lines
25 KiB
C#

using UnityEngine;
using System;
using System.IO;
using System.Threading;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine.XR;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
public delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
public class OmniMovementComponent : MonoBehaviour {
[Header("-----Movement Options-----")]
[Tooltip("Affects overall speed forward, back, left, right.")]
public float maxSpeed = 10;
[Range(0.0f, 1.0f)]
[Tooltip("Multiplier to reduce strafing speed (Strafing at full speed can feel too fast).")]
public float strafeSpeedMultiplier = 1.0f;
[Range(0.0f, 1.0f)]
[Tooltip("Multiplier to reduce backwards speed (Moving backwards at full speed can feel too fast).")]
public float backwardsSpeedMultiplier = 1.0f;
[Tooltip("Multiplier for gravity for character controller.")]
public float gravityMultiplier = 1;
[HideInInspector]
[Range(0.0f, 1.0f)]
[Tooltip("Fully coupled to camera = 100%, Fully decoupled (follows torso/ring angle = 0%.")]
public float couplingPercentage = 1.0f;
[Tooltip("Set to True if you want to use a joystick or WASD for testing instead of the Omni and no HMD. Please uncheck when you do a full build for release.")]
public bool developerMode = false;
[Header("Output and Calculated Variables. Can be used to debug or poll during game.")]
[HideInInspector]
public Vector2 hidInput; // holds x and y values for movement from the controller
[HideInInspector]
public float currentOmniYaw;
[HideInInspector]
public int currentStepCount;
[HideInInspector]
public float omniOffset = 0f;
[HideInInspector]
public float angleBetweenOmniAndCamera;
//Has the Omni been found
[HideInInspector]
public bool omniFound = false;
[Header("-----GameObject References-----")]
public Transform cameraReference;
public Transform dummyObject;
protected Vector3 dummyForward;
protected CharacterController characterController;
protected bool hasCalibrated = false;
protected int startingStepCount;
protected bool hasSetCouplingPercentage = false;
protected OmniManager omniManager;
private const int SOP = (int)OmniCommon.packet_start_end.OMNI_SOP;
private const int EOP = (int)OmniCommon.packet_start_end.OMNI_EOP;
/* Disable unused variable warnings */
#pragma warning disable 0414
protected static OmniCommon.Messages.OmniRawData rawData;
protected static OmniCommon.Messages.OmniMotionData motionData;
#pragma warning restore 0414
protected byte rawX = 0;
protected byte rawY = 0;
protected float omniX = 0;
protected float omniY = 0;
protected bool hasFullyInitialized = false;
//private int debugCounterForDataMessages = 0;
private bool debugNotReceivingData = false;
private bool hasReceivedOmniData = false;
private float joystickDeadzone = 0.05f;
private Vector3 forwardMovement;
private Vector3 strafeMovement;
private enum SecureAuthenticationProtocolState { SAPEnabled, SAPDisabled, SAPError };
private SecureAuthenticationProtocolState mySAPStatus = SecureAuthenticationProtocolState.SAPError;
private int SAPErrorChecks = 0;
#region reconnecting logic
IntPtr hMainWindow;
IntPtr oldWndProcPtr;
IntPtr newWndProcPtr;
WndProcDelegate newWndProc;
bool isrunning = false;
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern System.IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll")]
static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
IntPtr wndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
if (msg == 0x0219)
{
//Debug.LogError("Event Triggered; " + wParam.ToInt32());// + "; " + lParam.ToInt32());
switch (wParam.ToInt32())
{
case 0x8000: // DBT_DEVICEARRIVAL
//case 0x8004: // DBT_DEVICEREMOVECOMPLETE
//case 0x0007: // DBT_DEVNODES_CHANGED:
AttemptToReconnectTheOmni();
break;
}
}
return CallWindowProc(oldWndProcPtr, hWnd, msg, wParam, lParam);
}
#endregion
void Start()
{
Debug.Log(System.DateTime.Now.ToLongTimeString() + ": OmniMovementComponent- Omni/SecureOmni SDK Version 2.3");
if (!Application.isEditor)
{
developerMode = false;
}
OmniInitialize();
Start_TryingtoReconnect();
}
void Start_TryingtoReconnect()
{
if (isrunning) return;
hMainWindow = GetForegroundWindow();
newWndProc = new WndProcDelegate(wndProc);
newWndProcPtr = Marshal.GetFunctionPointerForDelegate(newWndProc);
oldWndProcPtr = SetWindowLongPtr(hMainWindow, -4, newWndProcPtr);
isrunning = true;
}
private IEnumerator ConfigureOmniConnect()
{
yield return new WaitForSeconds(3f);
StartCoroutine(SetGameMode());
SAPErrorChecks = 0;
mySAPStatus = SecureAuthenticationProtocolState.SAPError;
StartCoroutine(VerifySAP());
yield return new WaitForSeconds(10f);
string systemReadyCheckUrl = "http://localhost:8085/systemReady";
string systemReadyWebResponse = "-1";
yield return StartCoroutine(OmniConnectWebRequest.CoroutineGetRequest(systemReadyCheckUrl, value => systemReadyWebResponse = value));
if (systemReadyWebResponse.Contains("OK;Secure;MotionDisabled"))
{
Debug.LogError("Authentication Protocol Failed. Your Omni is not registered and therefore cannot be enabled. Please contact your Omni provider to register your Omni.");
}
}
private IEnumerator VerifySAP()
{
SecureAuthenticationProtocolState SAPGetResponse = SecureAuthenticationProtocolState.SAPError;
yield return StartCoroutine(GetSecureAuthentication(value => SAPGetResponse = value));
mySAPStatus = SAPGetResponse;
if (mySAPStatus == SecureAuthenticationProtocolState.SAPEnabled)
{
Debug.Log("SAP Enabled. Initializing SDK.");
/* For Initialization to work, you must create a developer account, and register your game with Virtuix by
* emailing your Omni provider. Please provide the serial number for any Omnis that you wish to use for development.*/
OVSDK.Init(1000, "01ec17dac7140c0fbe936ee128310000", "omni=1");
hasSetCouplingPercentage = false;
}
else if (mySAPStatus == SecureAuthenticationProtocolState.SAPDisabled)
{
Debug.Log("SAP Disabled.");
hasSetCouplingPercentage = false;
}
else
{
if (SAPErrorChecks < 3)
{
SAPErrorChecks++;
yield return new WaitForSeconds(2f);
StartCoroutine(VerifySAP());
}
else
{
Debug.LogError("There is an error with your configuration in Omni Connect. Ensure that Omni Connect is installed and running on your system. " +
"Verify that it is updated to the latest version and that the PODs are connected and on.");
}
}
}
private IEnumerator SetGameMode()
{
//Check the Game Mode to see if it is already set to "Gamepad"
string coroutineUrl = "http://localhost:8085/gamemode";
string GameModeGetResponse = "-1";
yield return StartCoroutine(OmniConnectWebRequest.CoroutineGetRequest(coroutineUrl, value => GameModeGetResponse = value));
//Set Game mode to "Gamepad" if it is not already.
if (!GameModeGetResponse.Contains("Gamepad") && GameModeGetResponse != "Error")
{
OmniConnectMode omniConnectMode = new OmniConnectMode
{
Data = "Gamepad"
};
string json = JsonUtility.ToJson(omniConnectMode);
byte[] pData = Encoding.ASCII.GetBytes(json.ToCharArray());
string webPostResponse = "-1";
yield return StartCoroutine(OmniConnectWebRequest.CoroutinePostRequest(coroutineUrl, pData, value => webPostResponse = value));
}
}
private IEnumerator GetSecureAuthentication(Action<SecureAuthenticationProtocolState> result)
{
string versionCheckUrl = "http://localhost:8085/version";
string versionWebResponse = "-1";
yield return StartCoroutine(OmniConnectWebRequest.CoroutineGetRequest(versionCheckUrl, value => versionWebResponse = value));
string v1 = versionWebResponse[23].ToString() + versionWebResponse[24].ToString() + versionWebResponse[25].ToString() + versionWebResponse[26].ToString() + versionWebResponse[27].ToString() + versionWebResponse[28].ToString() + versionWebResponse[29].ToString();
string v2 = "1.3.4.0";
var version1 = new Version(v1);
var version2 = new Version(v2);
var versionComparisonResult = version1.CompareTo(version2);
//If 1.3.4.0 or newer, use the new healthcheck endpoint.
if (versionComparisonResult >= 0)
{
string healthCheckUrl = "http://localhost:8085/healthcheck";
string SACWebResponse = "-1";
yield return StartCoroutine(OmniConnectWebRequest.CoroutineGetRequest(healthCheckUrl, value => SACWebResponse = value));
if (SACWebResponse != "Error")
{
switch (SACWebResponse[10])
{
case 'B':
Debug.Log("Secure Authentication Protocol Enabled");
result(SecureAuthenticationProtocolState.SAPEnabled);
break;
case 'U':
Debug.Log("Secure Authentication Protocol Disabled");
result(SecureAuthenticationProtocolState.SAPDisabled);
break;
case '-':
Debug.Log("Omni Not Connected: Please Connect Omni");
result(SecureAuthenticationProtocolState.SAPError);
break;
default:
Debug.Log("Secure Authentication Protocol check failed. Returned Default.");
result(SecureAuthenticationProtocolState.SAPError);
break;
}
}
}
//If older than 1.3.4.0, use the old methods.
if (versionComparisonResult < 0)
{
string systemReadyCheckUrl = "http://localhost:8085/systemReady";
string systemReadyWebResponse = "-1";
yield return StartCoroutine(OmniConnectWebRequest.CoroutineGetRequest(systemReadyCheckUrl, value => systemReadyWebResponse = value));
if (systemReadyWebResponse.Contains("OK;Unsecure"))
{
result(SecureAuthenticationProtocolState.SAPDisabled);
}
else if (systemReadyWebResponse.Contains("OK;Secure"))
{
result(SecureAuthenticationProtocolState.SAPEnabled);
}
else
{
result(SecureAuthenticationProtocolState.SAPError);
}
}
}
public Vector3 GetForwardMovement()
{
return forwardMovement;
}
public Vector3 GetStrafeMovement()
{
return strafeMovement;
}
//angle difference between camera and omni angle, used for decoupled effect
protected float ComputeAngleBetweenControllerAndCamera()
{
float cameraYaw = cameraReference.rotation.eulerAngles.y;
float adjustedOmniYaw = currentOmniYaw - omniOffset + transform.rotation.eulerAngles.y;
float angleBetweenControllerAndCamera = 0f;
angleBetweenControllerAndCamera = Mathf.Abs(cameraYaw - adjustedOmniYaw) % 360;
angleBetweenControllerAndCamera = angleBetweenControllerAndCamera > 180 ? 360 - angleBetweenControllerAndCamera : angleBetweenControllerAndCamera;
//calculate sign
float angleForSignCalculation = (cameraYaw - adjustedOmniYaw) % 360;
float sign = (angleForSignCalculation >= 0 && angleForSignCalculation <= 180) ||
(angleForSignCalculation <= -180 && angleForSignCalculation >= -360) ? 1 : -1;
angleBetweenControllerAndCamera *= sign;
return angleBetweenControllerAndCamera;
}
//called on exit
void OnApplicationQuit()
{
StopOmni();
}
//called on scene change
void OnDisable()
{
StopOmni();
// Disable the reconnect attempting
Debug.Log(System.DateTime.Now.ToLongTimeString() + ": OmniMovementComponent(OnDisable) - Uninstall Hook");
if (!isrunning) return;
SetWindowLongPtr(hMainWindow, -4, oldWndProcPtr);
hMainWindow = IntPtr.Zero;
oldWndProcPtr = IntPtr.Zero;
newWndProcPtr = IntPtr.Zero;
newWndProc = null;
isrunning = false;
}
void StopOmni()
{
if (omniManager != null)
{
omniManager.Cleanup();
omniManager = null;
}
}
/// <summary>
/// Finds the Omni so we can start reading data.
/// </summary>
public virtual void OmniInitialize()
{
if (developerMode)
{
if (cameraReference == null)
{
Debug.LogError(System.DateTime.Now.ToLongTimeString() + ": OmniMovementComponent(OmniInitialize) - Attempted to Initialize the Omni - developer mode is true and Camera Reference not set in prefab.");
return;
}
if (!isPresent())
{
cameraReference.gameObject.AddComponent<SmoothMouseLook>();
Vector3 adjustedCameraPosition = cameraReference.localPosition;
adjustedCameraPosition.y = 1.5f;
cameraReference.localPosition = adjustedCameraPosition;
}
return;
}
// Create a new OmniManager and see if we can find the Omni
omniManager = new OmniManager();
if (omniManager.FindOmni())
{
Debug.Log(System.DateTime.Now.ToLongTimeString() + ": OmniMovementComponent(OmniInitialize) - Successfully found the Omni.");
omniFound = true;
}
else
{
Debug.LogError(System.DateTime.Now.ToLongTimeString() + ": OmniMovementComponent(OmniInitialize) - Attempted to Initialize the Omni, but Omni not found.");
omniFound = false;
return;
}
}
void AttemptToReconnectTheOmni()
{
if (omniManager == null)
omniManager = new OmniManager();
if (!omniManager.omniDisconnected)
return;
if (omniManager.FindOmni())
{
Debug.Log(System.DateTime.Now.ToLongTimeString() + ": OmniMovementComponent(AttemptToReconnectTheOmni) - Successfully found the Omni for reconnect.");
omniFound = true;
}
else
{
Debug.LogError(System.DateTime.Now.ToLongTimeString() + ": OmniMovementComponent(AttemptToReconnectTheOmni) - Attempted to Reconnect the Omni, but Omni not found.");
omniFound = false;
return;
}
}
/// <summary>
/// Pulls packets from the Omni and decodes them to populate the class variables and motiondata.
/// </summary>
void ReadOmniData()
{
byte[] packet = null;
if (omniManager.ReadData(ref packet) > 0)
{
OmniCommon.Messages.OmniBaseMessage obm = OmniCommon.OmniPacketBuilder.decodePacket(packet);
if (debugNotReceivingData == true || hasReceivedOmniData == false)
{
Debug.Log(System.DateTime.Now.ToLongTimeString() + ": OmniMovementComponent(ReadOmniData) - During this frame, the Omni started reading data from the Omni data packet.");
debugNotReceivingData = false;
StartCoroutine(ConfigureOmniConnect());
}
hasReceivedOmniData = true;
if (obm != null)
{
switch (obm.MsgType)
{
case OmniCommon.Messages.MessageType.OmniMotionAndRawDataMessage:
OmniCommon.Messages.OmniMotionAndRawDataMessage OmniMotionAndRawData = new OmniCommon.Messages.OmniMotionAndRawDataMessage(obm);
motionData = OmniMotionAndRawData.GetMotionData();
break;
default:
break;
}
}
}
else
{
motionData = null;
if (debugNotReceivingData == false)
{
Debug.LogError(System.DateTime.Now.ToLongTimeString() + ": OmniMovementComponent(ReadOmniData) - During this frame, the Omni stopped receiving valid data from the Omni data packet.");
debugNotReceivingData = true;
}
}
if (motionData != null)
{
/*
debugCounterForDataMessages++;
if (debugCounterForDataMessages > 30)
{
Debug.Log(System.DateTime.Now.ToLongTimeString() +
": Timestamp = " + motionData.Timestamp +
"; Step count = " + motionData.StepCount +
"; Ring Angle = " + motionData.RingAngle +
"; Ring Delta = " + motionData.RingDelta +
"; joy x = " + motionData.GamePad_X +
"; joy y = " + motionData.GamePad_Y);
debugCounterForDataMessages = 0;
}*/
currentOmniYaw = motionData.RingAngle;
rawX = motionData.GamePad_X;
rawY = motionData.GamePad_Y;
omniX = motionData.GamePad_X;
omniY = motionData.GamePad_Y;
if (omniX > 0)
{
omniX = (omniX / 255.0f) * 2f - 1f;
}
else
omniX = -1f;
if (omniY > 0)
{
omniY = (omniY / 255.0f) * 2f - 1f;
}
else
omniY = -1;
//clamp '0'
if (rawX == 127)
{
omniX = 0f;
}
if (rawY == 127)
{
omniY = 0f;
}
//@TODO - may need to change this for fully coupled mode...
omniY *= -1f;
hidInput = new Vector2(omniX, omniY);
if (hasFullyInitialized) UpdateStepCountFromMotionData();
}
else
{
hidInput = Vector2.zero;
}
}
/// <summary>
/// Checks joystick or WASD for input if in developer mode
/// </summary>
void CheckInputForMovementTesting()
{
//Get values from joystick or WASD.
float inputX = Input.GetAxis("Horizontal");
float inputY = Input.GetAxis("Vertical");
//Clamp to deadzone
if (inputY < joystickDeadzone && inputY > 0 || inputY > -joystickDeadzone && inputY < 0)
{
inputY = 0;
}
if (inputX < joystickDeadzone && inputX > 0 || inputX > -joystickDeadzone && inputX < 0)
{
inputX = 0;
}
hidInput.y = inputY;
hidInput.x = inputX;
}
/// <summary>
/// Updates step count from motion data after getting the initial step count in the align.
/// </summary>
protected void UpdateStepCountFromMotionData()
{
if (motionData == null) return;
currentStepCount = (int)motionData.StepCount - startingStepCount;
}
/// <summary>
/// Resets step counter to 0. Call at anytime you want to reset the step count.
/// </summary>
public void ResetStepCount()
{
if (motionData == null) return;
startingStepCount = (int)motionData.StepCount;
currentStepCount = 0;
}
/// <summary>
/// Calibration Function. Sets up proper alignment between Omni and HMD based on Omni input data and HMD orientation.
/// </summary>
public virtual void CalibrateOmni()
{
if (!hasCalibrated)
{
//set offset to be current ring angle
if (motionData != null)
{
if ((cameraReference.transform.position.x != 0) && (cameraReference.transform.position.y != 0) && (cameraReference.transform.position.z != 0) &&
(cameraReference.rotation.eulerAngles.x != 0) && (cameraReference.rotation.eulerAngles.y != 0) && (cameraReference.rotation.eulerAngles.z != 0))
{
if (mySAPStatus == SecureAuthenticationProtocolState.SAPEnabled)
{
//omniOffset = OVSDK.GetOmniYawOffset();
omniOffset = OmniMovementCalibration.GetCalibrationValue();
}
else
{
omniOffset = OmniMovementCalibration.GetCalibrationValue();
}
hasCalibrated = true;
Debug.Log(System.DateTime.Now.ToLongTimeString() + ": OmniMovementComponent(CalibrateOmni) - Successfully calibrated Omni.");
}
if (!hasFullyInitialized)
{
//grab initial step count here
ResetStepCount();
hasFullyInitialized = true;
}
}
}
}
public void GetOmniInputForCharacterMovement()
{
if (cameraReference == null)
{
Debug.LogError("OmniMovementComponent(GetOmniInputForCharacterMovement) - Camera Reference not set in prefab.");
return;
}
forwardMovement = new Vector3(0.0f, 0.0f, 0.0f);
strafeMovement = forwardMovement;
//calculate angle between camera and omni
angleBetweenOmniAndCamera = ComputeAngleBetweenControllerAndCamera();
float forwardRotation = 0f;
//keep within bounds (0, 360)
if (currentOmniYaw > 360f) currentOmniYaw -= 360f;
if (currentOmniYaw < 0f) currentOmniYaw += 360f;
//Get the coupling percentage from Omniverse
if (hasSetCouplingPercentage == false)
{
if (mySAPStatus == SecureAuthenticationProtocolState.SAPEnabled)
{
//couplingPercentage = developerMode ? 1.0f : OVSDK.GetOmniCoupleRate();
couplingPercentage = developerMode ? 1.0f : OmniMovementCalibration.GetCouplingPercentage();
hasSetCouplingPercentage = true;
}
else
{
couplingPercentage = developerMode ? 1.0f : OmniMovementCalibration.GetCouplingPercentage();
hasSetCouplingPercentage = true;
}
Debug.Log(System.DateTime.Now.ToLongTimeString() + ": OmniMovementComponent(GetOmniInputForCharacterMovement) - Coupling Percentage = " + couplingPercentage);
}
//calculate forward rotation
forwardRotation = (currentOmniYaw - omniOffset + transform.rotation.eulerAngles.y) + (angleBetweenOmniAndCamera * couplingPercentage);
if(developerMode)
forwardRotation -= 90;
//display forward rotation defined by our coupling percentage
dummyObject.rotation = Quaternion.Euler(0, forwardRotation, 0);
dummyForward = dummyObject.forward;
//calculate forward movement
Vector3 movementInputVector = new Vector3(hidInput.y * dummyForward.x, 0, hidInput.y * dummyForward.z);
//apply multiplier to reduce backwards movement speed by a given percentage
if (hidInput.y < 0) { movementInputVector *= backwardsSpeedMultiplier; }
//display rounded values
if (dummyForward.x != 0)
dummyForward.x = Mathf.Round(dummyForward.x * 100) / 100;
if (dummyForward.z != 0)
dummyForward.z = Mathf.Round(dummyForward.z * 100) / 100;
//apply gravity
movementInputVector += Physics.gravity * gravityMultiplier;
//perform forward movement
forwardMovement = (movementInputVector * maxSpeed * Time.deltaTime);
//check if there is strafe input
if (hidInput.x != 0)
{
//calculate strafe movement
movementInputVector = new Vector3(hidInput.x * dummyObject.right.x, 0, hidInput.x * dummyObject.right.z);
//apply modifier to reduce strafe movement by a given percentage
movementInputVector *= strafeSpeedMultiplier;
//apply gravity
movementInputVector += Physics.gravity * gravityMultiplier;
//perform strafe movement
strafeMovement = (movementInputVector * maxSpeed * Time.deltaTime);
}
}
void Update()
{
if (!developerMode)
{
ReadOmniData();
CalibrateOmni();
}
}
public virtual void DeveloperModeUpdate()
{
CheckInputForMovementTesting();
GetOmniInputForCharacterMovement();
}
bool isPresent()
{
var xrDisplay = new List<XRDisplaySubsystem>();
SubsystemManager.GetInstances<XRDisplaySubsystem>(xrDisplay);
foreach (var xrD in xrDisplay)
{
if (xrD.running)
{
return true;
}
}
return false;
}
}