diff --git a/LICENSE b/LICENSE.md similarity index 100% rename from LICENSE rename to LICENSE.md diff --git a/LICENSE.md.meta b/LICENSE.md.meta new file mode 100644 index 0000000..cb1e8f5 --- /dev/null +++ b/LICENSE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 19cc458417b2b8741953fcb6487d7dfe +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DemoTarget.prefab b/Runtime/DemoTarget.prefab index 713b7d7..485bef7 100644 --- a/Runtime/DemoTarget.prefab +++ b/Runtime/DemoTarget.prefab @@ -293,6 +293,8 @@ MeshRenderer: m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -314,9 +316,11 @@ MeshRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_AdditionalVertexStreams: {fileID: 0} --- !u!65 &3225371635357050969 BoxCollider: @@ -352,6 +356,8 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: amountControlPoints: 3 + regiTargetVisualisation: {fileID: 4787713077208734620, guid: 9f46f9d192fa65c43829763b6b4fadcc, + type: 3} markers: - {fileID: 749095979477038632} - {fileID: 3836611436156247953} diff --git a/Runtime/RegistrationSphere.prefab b/Runtime/RegistrationSphere.prefab new file mode 100644 index 0000000..af78474 --- /dev/null +++ b/Runtime/RegistrationSphere.prefab @@ -0,0 +1,92 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &4787713077208734620 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1684654262528886088} + - component: {fileID: 4948061992107643806} + - component: {fileID: 2450485364167766399} + m_Layer: 0 + m_Name: RegistrationSphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1684654262528886088 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4787713077208734620} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.01, y: 0.01, z: 0.01} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4948061992107643806 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4787713077208734620} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &2450485364167766399 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4787713077208734620} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_MaskInteraction: 0 + m_AdditionalVertexStreams: {fileID: 0} diff --git a/Runtime/RegistrationSphere.prefab.meta b/Runtime/RegistrationSphere.prefab.meta new file mode 100644 index 0000000..e5a1e72 --- /dev/null +++ b/Runtime/RegistrationSphere.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9f46f9d192fa65c43829763b6b4fadcc +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/RegistartionUI.prefab b/Runtime/RegistrationUI.prefab similarity index 98% rename from Runtime/RegistartionUI.prefab rename to Runtime/RegistrationUI.prefab index c300f71..1640d8d 100644 --- a/Runtime/RegistartionUI.prefab +++ b/Runtime/RegistrationUI.prefab @@ -105,6 +105,7 @@ MonoBehaviour: m_VerticalAlignment: 256 m_textAlignment: 65535 m_characterSpacing: 0 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -274,6 +275,7 @@ MonoBehaviour: m_VerticalAlignment: 256 m_textAlignment: 65535 m_characterSpacing: 0 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -410,6 +412,7 @@ MonoBehaviour: m_VerticalAlignment: 256 m_textAlignment: 65535 m_characterSpacing: 0 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -623,6 +626,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 0 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -702,7 +706,7 @@ GameObject: - component: {fileID: 7148892776856201653} - component: {fileID: 6634393656915495675} m_Layer: 5 - m_Name: RegistartionUI + m_Name: RegistrationUI m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -776,7 +780,7 @@ MonoBehaviour: m_FallbackScreenDPI: 96 m_DefaultSpriteDPI: 96 m_DynamicPixelsPerUnit: 1 - m_PresetInfoIsWorld: 0 + m_PresetInfoIsWorld: 1 --- !u!114 &7148892776856201653 MonoBehaviour: m_ObjectHideFlags: 0 @@ -791,9 +795,13 @@ MonoBehaviour: m_EditorClassIdentifier: registration: {fileID: 0} controllerSelection: 0 - customObject: {fileID: 0} + onAlignmentAccepted: + m_PersistentCalls: + m_Calls: [] calibrateObject: 0 controllerInUse: {fileID: 0} + aligmentTarget: {fileID: 4787713077208734620, guid: 9f46f9d192fa65c43829763b6b4fadcc, + type: 3} --- !u!114 &6634393656915495675 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/Runtime/RegistartionUI.prefab.meta b/Runtime/RegistrationUI.prefab.meta similarity index 100% rename from Runtime/RegistartionUI.prefab.meta rename to Runtime/RegistrationUI.prefab.meta diff --git a/Runtime/Scripts/AnchorLoader.cs b/Runtime/Scripts/AnchorLoader.cs index 0a19b75..90ad823 100644 --- a/Runtime/Scripts/AnchorLoader.cs +++ b/Runtime/Scripts/AnchorLoader.cs @@ -37,7 +37,10 @@ public async void LoadAnchorsByUuid(RegiTarget target) RemoveExistingAnchor(target); var uuids = LoadAnchorUuidsFromPrefs(); - if (uuids.Length == 0) return; + if (uuids.Length == 0) + { + return; + } _spatialAnchorManager.Uuids.AddRange(uuids); @@ -63,35 +66,40 @@ private Guid[] LoadAnchorUuidsFromPrefs() if (!PlayerPrefs.HasKey(key)) PlayerPrefs.SetInt(key, 0); - int count = PlayerPrefs.GetInt(key); - if (count == 0) + int storedCount = PlayerPrefs.GetInt(key); + if (storedCount == 0) return Array.Empty(); - var uuids = new Guid[count]; - for (int i = 0; i < count; i++) + var uuids = new List(storedCount); + for (int i = 0; i < storedCount; i++) { var uuidStr = PlayerPrefs.GetString("uuid" + i, string.Empty); - if (Guid.TryParse(uuidStr, out var uuid)) + if (Guid.TryParse(uuidStr, out var uuid) && uuid != Guid.Empty) { - uuids[i] = uuid; + uuids.Add(uuid); } else { - Debug.LogWarning($"Invalid UUID at index {i}: {uuidStr}"); + Debug.LogWarning($"[AnchorLoader] Invalid UUID at key 'uuid{i}': '{uuidStr}'"); } } - return uuids; + return uuids.ToArray(); } private async Task> LoadUnboundAnchorsAsync(Guid[] uuids) { var unboundAnchors = new List(); - var result = await OVRSpatialAnchor.LoadUnboundAnchorsAsync(uuids, unboundAnchors); + var loadTask = OVRSpatialAnchor.LoadUnboundAnchorsAsync(uuids, unboundAnchors); + while (!loadTask.IsCompleted) + { + await Task.Yield(); + } + var result = loadTask.GetResult(); if (!result.Success) { - Debug.LogError($"Load anchors failed with status: {result.Status}"); + Debug.LogError($"[AnchorLoader] Load anchors failed. status={result.Status}"); return new List(); } @@ -111,7 +119,7 @@ private void OnLocalized(bool success, OVRSpatialAnchor.UnboundAnchor unboundAnc { if (!success) { - Debug.Log("NO SUCCESS"); + Debug.LogWarning("[AnchorLoader] Localization failed for one unbound anchor."); return; } unboundAnchor.TryGetPose(out Pose pose); @@ -122,4 +130,4 @@ private void OnLocalized(bool success, OVRSpatialAnchor.UnboundAnchor unboundAnc unboundAnchor.BindTo(LoadedObject.gameObject.GetComponent()); _spatialAnchorManager.LinkNewAnchor(spatialAnchor); } -} \ No newline at end of file +} diff --git a/Runtime/Scripts/AnchorLoaderManager.cs b/Runtime/Scripts/AnchorLoaderManager.cs index 8f9359b..e9198b1 100644 --- a/Runtime/Scripts/AnchorLoaderManager.cs +++ b/Runtime/Scripts/AnchorLoaderManager.cs @@ -15,6 +15,20 @@ /// public class AnchorLoaderManager : MonoBehaviour { + public readonly struct SaveAnchorOutcome + { + public bool Persisted { get; } + public Guid Uuid { get; } + public string Status { get; } + + public SaveAnchorOutcome(bool persisted, Guid uuid, string status) + { + Persisted = persisted; + Uuid = uuid; + Status = status; + } + } + public string numUuidsPlayerPref = "NumUuids"; public List anchors; public AnchorLoader AnchorLoader; @@ -41,7 +55,6 @@ public async Task DeleteAllAnchors() Uuids.Clear(); anchors.Clear(); - Debug.Log($"Anchors erased."); DeleteSavedUuids(); } @@ -52,8 +65,46 @@ public void LinkNewAnchor(OVRSpatialAnchor anchor) public void SaveAnchor(OVRSpatialAnchor anchor) { - anchor.SaveAnchorAsync(); - SaveUuid(anchor.Uuid); + _ = SaveAnchorAndPersistIfValidAsync(anchor); + } + + public async Task SaveAnchorAndPersistIfValidAsync(OVRSpatialAnchor anchor) + { + if (anchor == null) + { + Debug.LogWarning("[AnchorLoaderManager] SaveAnchorAndPersistIfValidAsync called with null anchor."); + return new SaveAnchorOutcome(false, Guid.Empty, "NullAnchor"); + } + + Guid initialUuid = anchor.Uuid; + + try + { + var saveTask = anchor.SaveAnchorAsync(); + while (!saveTask.IsCompleted) + { + await Task.Yield(); + } + + object saveResult = saveTask.GetResult(); + bool success = TryGetResultSuccess(saveResult, false); + string status = GetResultStatus(saveResult); + Guid finalUuid = anchor.Uuid; + + if (!success || finalUuid == Guid.Empty) + { + Debug.LogWarning($"[AnchorLoaderManager] Not persisting UUID. saveSuccess={success}, finalUuid={finalUuid}, status={status}"); + return new SaveAnchorOutcome(false, finalUuid, status); + } + + OverwriteSavedUuids(finalUuid); + return new SaveAnchorOutcome(true, finalUuid, status); + } + catch (Exception ex) + { + Debug.LogError($"[AnchorLoaderManager] SaveAnchorAsync threw for uuid={initialUuid}: {ex}"); + return new SaveAnchorOutcome(false, Guid.Empty, "Exception"); + } } private void SaveUuid(Guid uuid) @@ -64,7 +115,8 @@ private void SaveUuid(Guid uuid) } int playerNumUuids = PlayerPrefs.GetInt(numUuidsPlayerPref); - PlayerPrefs.SetString("uuid" + playerNumUuids, uuid.ToString()); + string key = "uuid" + playerNumUuids; + PlayerPrefs.SetString(key, uuid.ToString()); PlayerPrefs.SetInt(numUuidsPlayerPref, ++playerNumUuids); PlayerPrefs.Save(); } @@ -72,13 +124,18 @@ private void SaveUuid(Guid uuid) private void DeleteSavedUuids() { if (!PlayerPrefs.HasKey(numUuidsPlayerPref)) + { return; + } + int numUuids = PlayerPrefs.GetInt(numUuidsPlayerPref); for (int i = 0; i < numUuids; i++) { string key = $"uuid{i}"; if (PlayerPrefs.HasKey(key)) + { PlayerPrefs.DeleteKey(key); + } } PlayerPrefs.DeleteKey(numUuidsPlayerPref); @@ -97,4 +154,56 @@ private IEnumerator AnchorCreated(OVRSpatialAnchor instancedAnchor) tracker.uuidName = anchorGuid; anchors.Add(instancedAnchor); } -} \ No newline at end of file + + public List GetSavedUuids() + { + var result = new List(); + if (!PlayerPrefs.HasKey(numUuidsPlayerPref)) + return result; + + int numUuids = PlayerPrefs.GetInt(numUuidsPlayerPref); + for (int i = 0; i < numUuids; i++) + { + string uuidString = PlayerPrefs.GetString($"uuid{i}", string.Empty); + if (Guid.TryParse(uuidString, out Guid uuid) && uuid != Guid.Empty) + { + result.Add(uuid); + } + else if (!string.IsNullOrWhiteSpace(uuidString)) + { + Debug.LogWarning($"[AnchorLoaderManager] Invalid UUID in PlayerPrefs key 'uuid{i}': '{uuidString}'"); + } + } + + return result; + } + + private static bool TryGetResultSuccess(object result, bool defaultValue) + { + if (result == null) + return defaultValue; + + Type resultType = result.GetType(); + var successProperty = resultType.GetProperty("Success"); + if (successProperty == null) + return defaultValue; + + object successValue = successProperty.GetValue(result); + return successValue is bool success ? success : defaultValue; + } + + private static string GetResultStatus(object result) + { + if (result == null) + return "Unknown"; + + object status = result.GetType().GetProperty("Status")?.GetValue(result); + return status?.ToString() ?? "Unknown"; + } + + private void OverwriteSavedUuids(Guid uuid) + { + DeleteSavedUuids(); + SaveUuid(uuid); + } +} diff --git a/Runtime/Scripts/Registration.cs b/Runtime/Scripts/Registration.cs index d396d43..1446bcf 100644 --- a/Runtime/Scripts/Registration.cs +++ b/Runtime/Scripts/Registration.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using UnityEngine; /// @@ -10,7 +11,7 @@ /// /// David Mertens, TH Koeln. /// -/// +/// public class Registration : MonoBehaviour { public RegiTarget regiTarget; @@ -67,7 +68,6 @@ public void SetState(State nextState) ResetEverything(); OVRSpatialAnchor anchor = regiTarget.GetComponent(); if (anchor != null) Destroy(anchor); - regiTarget.SetVisible(false); break; case State.Inactive: @@ -83,8 +83,8 @@ public void SetState(State nextState) public void AddMarker(Vector3 position) { if (markers.Count >= regiTarget.amountControlPoints) return; - - GameObject go = Helper.CreateSmallSphere(); + + GameObject go = CreateMarkerVisual(); go.transform.position = position; go.AddComponent(); Helper.SetColor(go, Helper.GetColorForIndex(markers.Count)); @@ -115,15 +115,17 @@ public void ResetEverything() ResetTarget(); ResetMarker(); } - + /// /// Saves the current registration data asynchronously. /// - public async void SaveRegistration() + public void SaveRegistration() { - Debug.Log("Save ANCHOR"); - await _anchorLoaderManager.DeleteAllAnchors(); - regiTarget.gameObject.AddComponent(); + if (regiTarget.GetComponent() == null) + { + regiTarget.gameObject.AddComponent(); + } + StartCoroutine(SaveAnchorsDelayed()); } @@ -133,38 +135,92 @@ private void ResetTarget() regiTarget.transform.position = Vector3.zero; regiTarget.transform.rotation = Quaternion.identity; } - + private void ResetMarker() { markers.ForEach(Destroy); markers.Clear(); } + private GameObject CreateMarkerVisual() + { + if (regiTarget != null && regiTarget.RegiTargetVisualisation != null) + { + GameObject markerVisual = Instantiate(regiTarget.RegiTargetVisualisation, transform); + markerVisual.name = regiTarget.RegiTargetVisualisation.name; + return markerVisual; + } + + Debug.LogWarning($"[{nameof(Registration)}:{name}] RegiTargetVisualisation reference is missing. Created backup marker via Helper. Marker will not be visible in builds.", this); + return Helper.CreateSmallSphere(); + } + private void LinkPositionFromDevice() { - List uuids = AnchorStorage.LoadAllAnchorUuids(); - Debug.Log("LOAD ANCHORS: " + uuids.Count); _anchorLoaderManager.AnchorLoader.LoadAnchorsByUuid(regiTarget); SetState(State.Confirmation); } private IEnumerator SaveAnchorsDelayed() { - yield return new WaitForSeconds(0.01f); - _anchorLoaderManager.SaveAnchor(regiTarget.GetComponent()); + OVRSpatialAnchor anchor = regiTarget.GetComponent(); + if (anchor == null) + { + anchor = regiTarget.gameObject.AddComponent(); + } + + const float maxWaitSeconds = 10f; + float startTime = Time.realtimeSinceStartup; + while (anchor != null && (!anchor.Created || anchor.Uuid == Guid.Empty)) + { + if (Time.realtimeSinceStartup - startTime >= maxWaitSeconds) + { + Debug.LogWarning($"[Registration] SaveAnchorsDelayed aborted: anchor not ready within {maxWaitSeconds}s. created={anchor.Created}, uuid={anchor.Uuid}"); + yield break; + } + + yield return null; + anchor = regiTarget.GetComponent(); + } + + if (anchor == null) + { + Debug.LogWarning("[Registration] SaveAnchorsDelayed aborted: target has no OVRSpatialAnchor component after wait."); + yield break; + } + + Task saveTask = _anchorLoaderManager.SaveAnchorAndPersistIfValidAsync(anchor); + while (!saveTask.IsCompleted) + { + yield return null; + } + + if (saveTask.IsFaulted) + { + Debug.LogError($"[Registration] SaveAnchorsDelayed failed with exception: {saveTask.Exception}"); + yield break; + } + + AnchorLoaderManager.SaveAnchorOutcome saveOutcome = saveTask.Result; + if (!saveOutcome.Persisted) + { + Debug.LogWarning($"[Registration] SaveAnchorsDelayed did not persist UUID. status={saveOutcome.Status}, uuid={saveOutcome.Uuid}"); + } + + yield break; } private void Align(RegiTarget target) { if (markers == null || markers.Count == 0 || target == null) return; - + if (algorithmToUse == Algorithm.Kabsch) AlignMeshKabsch(markers.Select(marker => marker.transform.position).ToList(), target); - + if (algorithmToUse == Algorithm.ProjectionPlaneMapping) RegistrationPlaneProjection.AlignMesh(markers.Select(marker => marker.transform.position).ToList(), target); - + } private void AlignMeshKabsch(List selectedPositions, RegiTarget toTransform) @@ -191,4 +247,4 @@ private bool ReachedMaxMarkerAmount() { return markers.Count >= regiTarget.amountControlPoints; } -} \ No newline at end of file +} diff --git a/Runtime/Scripts/RegistrationTarget.cs b/Runtime/Scripts/RegistrationTarget.cs index a93ddd4..edd9b3d 100644 --- a/Runtime/Scripts/RegistrationTarget.cs +++ b/Runtime/Scripts/RegistrationTarget.cs @@ -7,9 +7,12 @@ public class RegiTarget : MonoBehaviour { [Range(3, 5)] public int amountControlPoints; + [SerializeField] private GameObject regiTargetVisualisation; [HideInInspector][SerializeField] public RegiMarker[] markers; - [HideInInspector]public Vector3[] relativeMarkerPositions; - [HideInInspector]public Guid uuidName; + [HideInInspector] public Vector3[] relativeMarkerPositions; + [HideInInspector] public Guid uuidName; + + public GameObject RegiTargetVisualisation { get => regiTargetVisualisation; } private void OnValidate() { @@ -80,4 +83,4 @@ private void InitMarkers() markers[i].color = Helper.GetColorForIndex(i); } } -} \ No newline at end of file +} diff --git a/Runtime/Scripts/RegistrationUI.cs b/Runtime/Scripts/RegistrationUI.cs index cae7f75..cd4d34a 100644 --- a/Runtime/Scripts/RegistrationUI.cs +++ b/Runtime/Scripts/RegistrationUI.cs @@ -3,7 +3,7 @@ using UnityEngine.UI; [RequireComponent(typeof(RegistrationVrController))] -public class SpatialPanel : MonoBehaviour +public class RegistrationUI : MonoBehaviour { public Image colorImage; @@ -97,4 +97,4 @@ public void SetColor(Color color) { colorImage.color = color; } -} \ No newline at end of file +} diff --git a/Runtime/Scripts/RegistrationVrController.cs b/Runtime/Scripts/RegistrationVrController.cs index 00cd4a7..c3caaf1 100644 --- a/Runtime/Scripts/RegistrationVrController.cs +++ b/Runtime/Scripts/RegistrationVrController.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -using System.Linq; using UnityEngine; +using UnityEngine.Events; /// /// Handles controller assignment, calibration, and registration workflow in VR registration scenarios. @@ -11,37 +11,62 @@ public class RegistrationVrController : MonoBehaviour { public Registration registration; - + [SerializeField] private Handedness controllerSelection; - [SerializeField] private GameObject customObject; - + [SerializeField] private UnityEvent onAlignmentAccepted; + [SerializeField] private bool calibrateObject; [HideInInspector] public GameObject controllerInUse; - - private Calibrator _calibrator; - private Vector3 _tipPosition; - private GameObject _demoObject; - private bool _isRecordingTipPosition; - private List _tipPositionsOverTime = new List(); - public readonly Vector3 PredefinedTipPosition = new Vector3(0.01211928f,-0.08250856f,-0.08393941f); + [SerializeField] private GameObject aligmentTarget; + private GameObject aligmentTargetInstance; + + protected Calibrator _calibrator; + protected Vector3 _tipPosition; + protected bool _isRecordingTipPosition; + protected readonly List _tipPositionsOverTime = new List(); + protected readonly Vector3 PredefinedTipPosition = new Vector3(0.01211928f, -0.08250856f, -0.08393941f); + + protected Handedness ControllerSelection => controllerSelection; + protected bool CalibrateObject => calibrateObject; + protected virtual float TipForwardOffset => 0.06f; + + public GameObject AligmentTarget + { + get + { + if (aligmentTargetInstance != null) + { + return aligmentTargetInstance; + } + + if (aligmentTarget != null) + { + aligmentTargetInstance = Instantiate(aligmentTarget, transform, false); + aligmentTargetInstance.name = aligmentTarget.name; + return aligmentTargetInstance; + } + + aligmentTargetInstance = Helper.CreateSmallSphere(); + aligmentTargetInstance.transform.SetParent(transform, false); + return aligmentTargetInstance; + } + } + public enum Handedness { RightHanded, LeftHanded } - private void Awake() + protected virtual void Awake() { _calibrator = gameObject.AddComponent(); SetupController(); - _demoObject = Helper.CreateSmallSphere(); - _demoObject.name = "Demo Object"; - _demoObject.transform.SetParent(transform); registration.StateChanged += OnStateChanged; } - - private void Start() + + protected virtual void Start() { if (calibrateObject) registration.SetState(Registration.State.Calibration); @@ -50,34 +75,39 @@ private void Start() _calibrator.SetRelativePostition(PredefinedTipPosition); } - private void OnStateChanged() + protected virtual void OnStateChanged() { switch (registration.currentState) { case Registration.State.Calibration: - _demoObject.SetActive(true); + AligmentTarget.SetActive(true); break; case Registration.State.MarkerSetup: - _demoObject.SetActive(true); + AligmentTarget.SetActive(true); break; case Registration.State.Confirmation: - _demoObject.SetActive(false); + AligmentTarget.SetActive(false); break; } } - private void OnEnable() + protected virtual void OnEnable() { SetupController(); } - private void SetupController() + protected virtual void OnDisable() + { + } + + protected virtual void SetupController() { controllerInUse = SearchForController(controllerSelection); - _calibrator.toCalibrate = controllerInUse; + if (_calibrator != null) + _calibrator.toCalibrate = controllerInUse; } - private void Update() + protected virtual void Update() { if (registration.currentState == Registration.State.Inactive) return; switch (registration.currentState) @@ -94,7 +124,7 @@ private void Update() } } - private void CalibrationActions() + protected virtual void CalibrationActions() { UpdateTipPosition(); UpdateDemoObject(); @@ -102,29 +132,30 @@ private void CalibrationActions() if (CommitButtonPressed()) registration.SetState(Registration.State.MarkerSetup); if (AnyTriggerDown()) { - _demoObject.SetActive(true); + AligmentTarget.SetActive(true); _calibrator.StartRecording(); } if (AnyTriggerUp()) _calibrator.StopRecording(); if (CancelButtonPressed()) _calibrator.SetRelativePostition(PredefinedTipPosition); - + } - private void MarkerStateActions() + protected virtual void MarkerStateActions() { UpdateTipPosition(); UpdateDemoObject(); - - if(_isRecordingTipPosition)_tipPositionsOverTime.Add(_tipPosition); - + + if (_isRecordingTipPosition) _tipPositionsOverTime.Add(_tipPosition); + LeftHandMarkerInteractions(); RightHandMarkerInteractions(); } - - private void ConfirmationStateActions() + + protected virtual void ConfirmationStateActions() { if (CommitButtonPressed()) { + onAlignmentAccepted?.Invoke(); registration.SaveRegistration(); registration.SetState(Registration.State.Inactive); } @@ -135,39 +166,40 @@ private void ConfirmationStateActions() } } - private void UpdateTipPosition() + protected virtual void UpdateTipPosition() { if (calibrateObject) _tipPosition = _calibrator.GetCalibratedCurrentPosition(); else if (controllerInUse != null) - _tipPosition = controllerInUse.transform.position + controllerInUse.transform.forward * 0.06f; + _tipPosition = controllerInUse.transform.position + controllerInUse.transform.forward * TipForwardOffset; else Debug.LogWarning("No Controller in Use!"); } - private void UpdateDemoObject() + protected virtual void UpdateDemoObject() { - if (_demoObject == null) return; + if (AligmentTarget == null) return; - _demoObject.transform.position = _tipPosition; - Helper.SetColor(_demoObject, Helper.GetColorForIndex(registration.markers.Count)); + AligmentTarget.transform.position = _tipPosition; + Helper.SetColor(AligmentTarget, Helper.GetColorForIndex(registration.markers.Count)); } - private void RightHandMarkerInteractions() + protected virtual void RightHandMarkerInteractions() { if (_isRecordingTipPosition && AnyTriggerUp()) EndRecordingTipPosition(); if (!_isRecordingTipPosition && AnyTriggerDown()) StartRecordingTipPosition(); if (CancelButtonPressed()) registration.ResetEverything(); } - private void StartRecordingTipPosition() + protected virtual void StartRecordingTipPosition() { _isRecordingTipPosition = true; _tipPositionsOverTime.Clear(); } - private void EndRecordingTipPosition() + + protected virtual void EndRecordingTipPosition() { - if(_tipPositionsOverTime == null || _tipPositionsOverTime.Count < 1)return; + if (_tipPositionsOverTime == null || _tipPositionsOverTime.Count < 1) return; _isRecordingTipPosition = false; Vector3 midPoint = Vector3.zero; _tipPositionsOverTime.ForEach(pos => midPoint += pos); @@ -175,7 +207,8 @@ private void EndRecordingTipPosition() _tipPositionsOverTime.Clear(); registration.AddMarker(midPoint); } - private GameObject SearchForController(Handedness handedness) + + protected virtual GameObject SearchForController(Handedness handedness) { string controllerName = handedness == Handedness.RightHanded ? "RightControllerAnchor" : "LeftControllerAnchor"; @@ -183,7 +216,7 @@ private GameObject SearchForController(Handedness handedness) return controllerToUse; } - private void LeftHandMarkerInteractions() + protected virtual void LeftHandMarkerInteractions() { if (OVRInput.GetDown(OVRInput.Button.One, OVRInput.Controller.LTouch)) { @@ -191,23 +224,23 @@ private void LeftHandMarkerInteractions() } } - private static bool CommitButtonPressed() + protected virtual bool CommitButtonPressed() { return OVRInput.GetDown(OVRInput.Button.One, OVRInput.Controller.RTouch); } - private static bool CancelButtonPressed() + protected virtual bool CancelButtonPressed() { return OVRInput.GetDown(OVRInput.Button.Two, OVRInput.Controller.RTouch); } - private static bool AnyTriggerDown() + protected virtual bool AnyTriggerDown() { return OVRInput.GetDown(OVRInput.Button.PrimaryHandTrigger, OVRInput.Controller.RTouch) || OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.RTouch); } - - private static bool AnyTriggerUp() + + protected virtual bool AnyTriggerUp() { return OVRInput.GetUp(OVRInput.Button.PrimaryHandTrigger, OVRInput.Controller.RTouch) || OVRInput.GetUp(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.RTouch);