//----------------------------------------------------------------------- // // // Copyright 2018 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. // // //----------------------------------------------------------------------- // Modifications copyright © 2020 Unity Technologies ApS #if AR_FOUNDATION_PRESENT || PACKAGE_DOCS_GENERATION using System; using System.Collections.Generic; using JetBrains.Annotations; using Unity.XR.CoreUtils; using UnityEngine.XR.ARFoundation; using UnityEngine.XR.ARSubsystems; using UnityEngine.XR.Interaction.Toolkit.Utilities; namespace UnityEngine.XR.Interaction.Toolkit.AR { /// /// Provides helper functions for common functionality to transform objects in AR. /// [Obsolete("GestureTransformationUtility has been replaced in function by the ARTransformer. Please use the ARTransformer instead.")] public static class GestureTransformationUtility { /// /// Represents the alignment of a plane where translation is allowed. /// /// /// public enum GestureTranslationMode { /// /// Allow translation when the plane is horizontal. /// Horizontal, /// /// Allow translation when the plane is vertical. /// Vertical, /// /// Allow translation on any plane. /// Any, } /// /// Max amount (inches) to offset the screen touch in . /// The actual amount depends on the angle of the camera relative. /// The further downward the camera is angled, the more the screen touch is offset. /// const float k_MaxScreenTouchOffset = 0.4f; /// /// In , when the camera is closer than this value to the object, /// reduce how much the object hovers. /// const float k_HoverDistanceThreshold = 1f; static XROrigin s_XROrigin; #pragma warning disable CS0618 // ARSessionOrigin is deprecated in 5.0, but kept to support older AR Foundation versions static ARSessionOrigin s_ARSessionOrigin; #pragma warning restore CS0618 static ARRaycastManager s_ARRaycastManager; static ARPlaneManager s_ARPlaneManager; static readonly List s_Hits = new List(); static bool TryGetTrackableManager([CanBeNull] XROrigin sessionOrigin, out ARRaycastManager raycastManager) => TryGetTrackableManager(sessionOrigin, ref s_ARRaycastManager, out raycastManager); static bool TryGetTrackableManager([CanBeNull] XROrigin sessionOrigin, out ARPlaneManager planeManager) => TryGetTrackableManager(sessionOrigin, ref s_ARPlaneManager, out planeManager); static bool TryGetTrackableManager([CanBeNull] XROrigin sessionOrigin, ref T cachedManager, out T manager) { // This method is used to get the Trackable Manager component on the XROrigin GameObject // by caching the manager component from the most recently used XROrigin. // There is typically only one XROrigin, so this serves as a simple cache to avoid // doing a GetComponent call to get the manager component each time. if (sessionOrigin == null) { if (s_XROrigin == null) { cachedManager = default; if (!ComponentLocatorUtility.TryFindComponent(out s_XROrigin)) { Debug.LogWarning($"Could not find {nameof(XROrigin)} in scene."); manager = default; return false; } } } else if (sessionOrigin != s_XROrigin) { s_XROrigin = sessionOrigin; cachedManager = default; } // The cached Trackable Managers are associated with an XROrigin. // Update and use cached version var found = cachedManager != null || s_XROrigin.TryGetComponent(out cachedManager); manager = cachedManager; return found; } static bool TryGetSessionOrigin(out XROrigin sessionOrigin) { if (s_XROrigin == null) { if (!ComponentLocatorUtility.TryFindComponent(out s_XROrigin)) { Debug.LogWarning($"Could not find {nameof(XROrigin)} in scene."); sessionOrigin = s_XROrigin; return false; } } sessionOrigin = s_XROrigin; return true; } #region Deprecated ARSessionOrigin overloads #pragma warning disable CS0618 // ARSessionOrigin is deprecated in 5.0, but kept to support older AR Foundation versions static bool TryGetTrackableManager([CanBeNull] ARSessionOrigin sessionOrigin, out ARRaycastManager raycastManager) => TryGetTrackableManager(sessionOrigin, ref s_ARRaycastManager, out raycastManager); static bool TryGetTrackableManager([CanBeNull] ARSessionOrigin sessionOrigin, out ARPlaneManager planeManager) => TryGetTrackableManager(sessionOrigin, ref s_ARPlaneManager, out planeManager); static bool TryGetTrackableManager([CanBeNull] ARSessionOrigin sessionOrigin, ref T cachedManager, out T manager) { // This method is used to get the Trackable Manager component on the ARSessionOrigin GameObject // by caching the manager component from the most recently used ARSessionOrigin. // There is typically only one ARSessionOrigin, so this serves as a simple cache to avoid // doing a GetComponent call to get the manager component each time. if (sessionOrigin == null) { if (s_ARSessionOrigin == null) { cachedManager = default; if (!ComponentLocatorUtility.TryFindComponent(out s_ARSessionOrigin)) { Debug.LogWarning($"Could not find {nameof(ARSessionOrigin)} in scene."); manager = default; return false; } } } else if (sessionOrigin != s_ARSessionOrigin) { s_ARSessionOrigin = sessionOrigin; cachedManager = default; } // The cached Trackable Managers are associated with an ARSessionOrigin. // Update and use cached version var found = cachedManager != null || s_ARSessionOrigin.TryGetComponent(out cachedManager); manager = cachedManager; return found; } static bool TryGetSessionOrigin(out ARSessionOrigin sessionOrigin) { if (s_ARSessionOrigin == null) { if (!ComponentLocatorUtility.TryFindComponent(out s_ARSessionOrigin)) { Debug.LogWarning($"Could not find {nameof(ARSessionOrigin)} in scene."); sessionOrigin = s_ARSessionOrigin; return false; } } sessionOrigin = s_ARSessionOrigin; return true; } #pragma warning restore CS0618 #endregion /// /// Cast a ray from a point in screen space against trackables, i.e., detected features such as planes. /// Can optionally fallback to hit test against Colliders in the loaded Scenes when no trackables were hit. /// /// /// Unity uses the 3D physics scene of the camera when performing the fallback ray cast. /// /// The point, in device screen pixels, from which to cast. /// Contents are replaced with the ray cast results, if successful. /// The used for ray casting. /// (Optional) The types of trackables to cast against. /// (Optional) The that Unity uses during an additional ray cast when no trackables are hit. /// Defaults to Nothing which skips the fallback ray cast. /// Returns if the ray cast hit a trackable in the or if the fallback ray cast hit. /// Otherwise, returns . public static bool Raycast( Vector2 screenPoint, List hitResults, XROrigin sessionOrigin, #if AR_FOUNDATION_4_2_OR_NEWER TrackableType trackableTypes = TrackableType.AllTypes, #else TrackableType trackableTypes = TrackableType.All, #endif int fallbackLayerMask = 0) { if ((sessionOrigin != null || TryGetSessionOrigin(out sessionOrigin)) && TryGetTrackableManager(sessionOrigin, out ARRaycastManager raycastManager) && raycastManager.Raycast(screenPoint, hitResults, trackableTypes)) { return true; } // No hits on trackables, try debug planes hitResults.Clear(); const TrackableType hitType = TrackableType.PlaneWithinPolygon; if (fallbackLayerMask == 0 || (trackableTypes & hitType) == 0) return false; var camera = sessionOrigin != null ? sessionOrigin.Camera : Camera.main; if (camera == null) return false; var ray = camera.ScreenPointToRay(screenPoint); if (camera.gameObject.scene.GetPhysicsScene().Raycast(ray.origin, ray.direction, out var hit, Mathf.Infinity, fallbackLayerMask)) { var transform = sessionOrigin != null ? sessionOrigin.transform : hit.collider.transform; hitResults.Add(new ARRaycastHit( new XRRaycastHit( TrackableId.invalidId, new Pose(hit.point, Quaternion.LookRotation(Vector3.forward, hit.normal)), hit.distance, hitType), hit.distance, #if AR_FOUNDATION_4_1_OR_NEWER transform, null)); #else transform)); #endif return true; } return false; } /// /// Calculates the best position to place an object in AR based on screen position. /// Could be used for tapping a location on the screen, dragging an object, or using a fixed /// cursor in the center of the screen for placing and moving objects. /// /// Returns the best placement position. /// Position of the parent anchor, i.e., where the /// object is before translation starts. /// Location on the screen in pixels to place the object at. /// The starting height of the plane to place the object on. /// How much should the object hover above the groundingPlane /// before it has been placed. /// The maximum distance allowed to translate the object. /// The translation mode, indicating the plane types allowed. /// /// The used for ray casting. /// (Optional) The types of trackables to cast against. /// (Optional) The that Unity uses during /// an additional ray cast when no trackables are hit. Defaults to Nothing which skips the fallback ray cast. /// /// Unity places objects along the x/z of the grounding plane. When placed on an AR plane /// below the grounding plane, the object will drop straight down onto it in world space. /// This prevents the object from being pushed deeper into the scene when moving from a /// higher plane to a lower plane. When moving from a lower plane to a higher plane, this /// function returns a new groundingPlane to replace the old one. /// public static Placement GetBestPlacementPosition( Vector3 currentAnchorPosition, Vector2 screenPosition, float groundingPlaneHeight, float hoverOffset, float maxTranslationDistance, GestureTranslationMode gestureTranslationMode, XROrigin sessionOrigin, TrackableType trackableTypes = TrackableType.PlaneWithinPolygon, int fallbackLayerMask = 0) { var result = new Placement(); if (sessionOrigin == null) TryGetSessionOrigin(out sessionOrigin); var camera = sessionOrigin != null ? sessionOrigin.Camera : Camera.main; if (camera == null) return result; var cameraTransform = camera.transform; result.updatedGroundingPlaneHeight = groundingPlaneHeight; // Get the angle between the camera and the object's down direction. var angle = 90f - Vector3.Angle(cameraTransform.forward, Vector3.down); var touchOffsetRatio = Mathf.Clamp01(angle / 90f); var screenTouchOffset = touchOffsetRatio * k_MaxScreenTouchOffset; screenPosition.y += GestureTouchesUtility.InchesToPixels(screenTouchOffset); var hoverRatio = Mathf.Clamp01(angle / 45f); hoverOffset *= hoverRatio; var distance = (cameraTransform.position - currentAnchorPosition).magnitude; var distanceHoverRatio = Mathf.Clamp01(distance / k_HoverDistanceThreshold); hoverOffset *= distanceHoverRatio; // The best estimate of the point in the plane where the object will be placed: Vector3 groundingPoint; // Get the ray to cast into the scene from the perspective of the camera. if (Raycast(screenPosition, s_Hits, sessionOrigin, trackableTypes, fallbackLayerMask)) { if (!TryGetTrackableManager(sessionOrigin, out ARPlaneManager planeManager)) return result; var firstHit = s_Hits[0]; var plane = planeManager.GetPlane(firstHit.trackableId); if (plane == null || IsPlaneTypeAllowed(gestureTranslationMode, plane.alignment)) { // Avoid detecting the back of existing planes. if (Vector3.Dot(cameraTransform.position - firstHit.pose.position, firstHit.pose.rotation * Vector3.up) < 0f) return result; // Don't allow hovering for vertical or horizontal downward facing planes. if (plane == null || plane.alignment == PlaneAlignment.Vertical || plane.alignment == PlaneAlignment.HorizontalDown || plane.alignment == PlaneAlignment.HorizontalUp) { groundingPoint = LimitTranslation( firstHit.pose.position, currentAnchorPosition, maxTranslationDistance); if (plane != null) { result.placementPlane = plane; result.hasPlane = true; } result.hasPlacementPosition = true; result.placementPosition = groundingPoint; result.hasHoveringPosition = true; result.hoveringPosition = groundingPoint; result.updatedGroundingPlaneHeight = groundingPoint.y; result.placementRotation = firstHit.pose.rotation; return result; } } else { // Plane type not allowed. return result; } } // Return early if the camera is pointing upwards. if (angle < 0f) { return result; } // If the grounding point is lower than the current grounding plane height, or if the // ray cast did not return a hit, then we extend the grounding plane to infinity, and do // a new ray cast into the scene from the perspective of the camera. var cameraRay = camera.ScreenPointToRay(screenPosition); var groundingPlane = new Plane(Vector3.up, new Vector3(0f, groundingPlaneHeight, 0f)); // Find the hovering position by casting from the camera onto the grounding plane // and offsetting the result by the hover offset. if (groundingPlane.Raycast(cameraRay, out var enter)) { groundingPoint = LimitTranslation( cameraRay.GetPoint(enter), currentAnchorPosition, maxTranslationDistance); result.hasHoveringPosition = true; result.hoveringPosition = groundingPoint + (Vector3.up * hoverOffset); } else { // If we can't successfully cast onto the groundingPlane, just return early. return result; } return result; } /// /// Limits the translation to the maximum distance allowed. /// /// Returns the new target position, limited so that the object does not translate more /// than the maximum allowed distance. /// Desired position. /// Current position. /// Max translation distance. static Vector3 LimitTranslation(Vector3 desiredPosition, Vector3 currentPosition, float maxTranslationDistance) { if ((desiredPosition - currentPosition).sqrMagnitude > Mathf.Pow(maxTranslationDistance, 2f)) { return currentPosition + ( (desiredPosition - currentPosition).normalized * maxTranslationDistance); } return desiredPosition; } static bool IsPlaneTypeAllowed(GestureTranslationMode gestureTranslationMode, PlaneAlignment planeAlignment) { if (gestureTranslationMode == GestureTranslationMode.Any) { return true; } if (gestureTranslationMode == GestureTranslationMode.Horizontal && (planeAlignment == PlaneAlignment.HorizontalDown || planeAlignment == PlaneAlignment.HorizontalUp)) { return true; } if (gestureTranslationMode == GestureTranslationMode.Vertical && planeAlignment == PlaneAlignment.Vertical) { return true; } return false; } /// /// Result of the function /// that indicates if a placement position /// was found and information about the placement position. /// public partial struct Placement { /// /// if this Placement has a valid value, otherwise . /// /// public bool hasHoveringPosition { get; set; } /// /// The position that the object should be displayed at before the placement has been /// confirmed. /// /// public Vector3 hoveringPosition { get; set; } /// /// if this Placement has a valid value, otherwise . /// /// public bool hasPlacementPosition { get; set; } /// /// The resulting position that the object should be placed at. /// /// public Vector3 placementPosition { get; set; } /// /// The resulting rotation that the object should have. /// public Quaternion placementRotation { get; set; } /// /// if this Placement has a , otherwise . /// /// public bool hasPlane { get; set; } /// /// The ARPlane /// that the object is being placed on. /// /// public ARPlane placementPlane { get; set; } /// /// The resulting starting height of the plane that the object is being placed along. /// public float updatedGroundingPlaneHeight { get; set; } } /// /// Cast a ray from a point in screen space against trackables, i.e., detected features such as planes. /// Can optionally fallback to hit test against Colliders in the loaded Scenes when no trackables were hit. /// /// The point, in device screen pixels, from which to cast. /// Contents are replaced with the ray cast results, if successful. /// The used for ray casting. /// (Optional) The types of trackables to cast against. /// (Optional) The that Unity uses during an additional ray cast when no trackables are hit. /// Defaults to Nothing which skips the fallback ray cast. /// Returns if the ray cast hit a trackable in the or if the fallback ray cast hit. /// Otherwise, returns . [Obsolete("Raycast with the ARSessionOrigin parameter has been deprecated. Use Raycast with the XROrigin parameter instead.")] public static bool Raycast( Vector2 screenPoint, List hitResults, ARSessionOrigin sessionOrigin, TrackableType trackableTypes = TrackableType.All, int fallbackLayerMask = 0) { if ((sessionOrigin != null || TryGetSessionOrigin(out sessionOrigin)) && TryGetTrackableManager(sessionOrigin, out ARRaycastManager raycastManager) && raycastManager.Raycast(screenPoint, hitResults, trackableTypes)) { return true; } // No hits on trackables, try debug planes hitResults.Clear(); const TrackableType hitType = TrackableType.PlaneWithinPolygon; if (fallbackLayerMask == 0 || (trackableTypes & hitType) == 0) return false; var camera = sessionOrigin != null ? sessionOrigin.camera : Camera.main; if (camera == null) return false; var ray = camera.ScreenPointToRay(screenPoint); if (Physics.Raycast(ray, out var hit, Mathf.Infinity, fallbackLayerMask)) { hitResults.Add(new ARRaycastHit( new XRRaycastHit( TrackableId.invalidId, new Pose(hit.point, Quaternion.LookRotation(Vector3.forward, hit.normal)), hit.distance, hitType), hit.distance, sessionOrigin != null ? sessionOrigin.transform : hit.collider.transform)); return true; } return false; } /// /// Calculates the best position to place an object in AR based on screen position. /// Could be used for tapping a location on the screen, dragging an object, or using a fixed /// cursor in the center of the screen for placing and moving objects. /// /// Returns the best placement position. /// Position of the parent anchor, i.e., where the /// object is before translation starts. /// Location on the screen in pixels to place the object at. /// The starting height of the plane to place the object on. /// How much should the object hover above the groundingPlane /// before it has been placed. /// The maximum distance allowed to translate the object. /// The translation mode, indicating the plane types allowed. /// /// The used for ray casting. /// (Optional) The types of trackables to cast against. /// (Optional) The that Unity uses during /// an additional ray cast when no trackables are hit. Defaults to Nothing which skips the fallback ray cast. /// /// Unity places objects along the x/z of the grounding plane. When placed on an AR plane /// below the grounding plane, the object will drop straight down onto it in world space. /// This prevents the object from being pushed deeper into the scene when moving from a /// higher plane to a lower plane. When moving from a lower plane to a higher plane, this /// function returns a new groundingPlane to replace the old one. /// [Obsolete("GetBestPlacementPosition with the ARSessionOrigin parameter has been deprecated. Use GetBestPlacementPosition with the XROrigin parameter instead.")] public static Placement GetBestPlacementPosition( Vector3 currentAnchorPosition, Vector2 screenPosition, float groundingPlaneHeight, float hoverOffset, float maxTranslationDistance, GestureTranslationMode gestureTranslationMode, ARSessionOrigin sessionOrigin, TrackableType trackableTypes = TrackableType.PlaneWithinPolygon, int fallbackLayerMask = 0) { var result = new Placement(); if (sessionOrigin == null) TryGetSessionOrigin(out sessionOrigin); var camera = sessionOrigin != null ? sessionOrigin.camera : Camera.main; if (camera == null) return result; var cameraTransform = camera.transform; result.updatedGroundingPlaneHeight = groundingPlaneHeight; // Get the angle between the camera and the object's down direction. var angle = 90f - Vector3.Angle(cameraTransform.forward, Vector3.down); var touchOffsetRatio = Mathf.Clamp01(angle / 90f); var screenTouchOffset = touchOffsetRatio * k_MaxScreenTouchOffset; screenPosition.y += GestureTouchesUtility.InchesToPixels(screenTouchOffset); var hoverRatio = Mathf.Clamp01(angle / 45f); hoverOffset *= hoverRatio; var distance = (cameraTransform.position - currentAnchorPosition).magnitude; var distanceHoverRatio = Mathf.Clamp01(distance / k_HoverDistanceThreshold); hoverOffset *= distanceHoverRatio; // The best estimate of the point in the plane where the object will be placed: Vector3 groundingPoint; // Get the ray to cast into the scene from the perspective of the camera. if (Raycast(screenPosition, s_Hits, sessionOrigin, trackableTypes, fallbackLayerMask)) { if (!TryGetTrackableManager(sessionOrigin, out ARPlaneManager planeManager)) return result; var firstHit = s_Hits[0]; var plane = planeManager.GetPlane(firstHit.trackableId); if (plane == null || IsPlaneTypeAllowed(gestureTranslationMode, plane.alignment)) { // Avoid detecting the back of existing planes. if (Vector3.Dot(cameraTransform.position - firstHit.pose.position, firstHit.pose.rotation * Vector3.up) < 0f) return result; // Don't allow hovering for vertical or horizontal downward facing planes. if (plane == null || plane.alignment == PlaneAlignment.Vertical || plane.alignment == PlaneAlignment.HorizontalDown || plane.alignment == PlaneAlignment.HorizontalUp) { groundingPoint = LimitTranslation( firstHit.pose.position, currentAnchorPosition, maxTranslationDistance); if (plane != null) { result.placementPlane = plane; result.hasPlane = true; } result.hasPlacementPosition = true; result.placementPosition = groundingPoint; result.hasHoveringPosition = true; result.hoveringPosition = groundingPoint; result.updatedGroundingPlaneHeight = groundingPoint.y; result.placementRotation = firstHit.pose.rotation; return result; } } else { // Plane type not allowed. return result; } } // Return early if the camera is pointing upwards. if (angle < 0f) { return result; } // If the grounding point is lower than the current grounding plane height, or if the // ray cast did not return a hit, then we extend the grounding plane to infinity, and do // a new ray cast into the scene from the perspective of the camera. var cameraRay = camera.ScreenPointToRay(screenPosition); var groundingPlane = new Plane(Vector3.up, new Vector3(0f, groundingPlaneHeight, 0f)); // Find the hovering position by casting from the camera onto the grounding plane // and offsetting the result by the hover offset. if (groundingPlane.Raycast(cameraRay, out var enter)) { groundingPoint = LimitTranslation( cameraRay.GetPoint(enter), currentAnchorPosition, maxTranslationDistance); result.hasHoveringPosition = true; result.hoveringPosition = groundingPoint + (Vector3.up * hoverOffset); } else { // If we can't successfully cast onto the groundingPlane, just return early. return result; } return result; } /// /// Cast a ray from a point in screen space against trackables, i.e., detected features such as planes. /// /// The point, in device screen pixels, from which to cast. /// Contents are replaced with the ray cast results, if successful. /// (Optional) The types of trackables to cast against. /// Returns if the ray cast hit a trackable in the . /// Otherwise, returns . [Obsolete("Raycast has been deprecated. Use Raycast with updated signature instead.")] public static bool Raycast(Vector2 screenPoint, List hitResults, TrackableType trackableTypes = TrackableType.All) { // For backwards compatibility, use the TestPlanes layer value from the AR examples project. const int fallbackLayerMask = 1 << 9; return Raycast(screenPoint, hitResults, (ARSessionOrigin)null, trackableTypes, fallbackLayerMask); } /// /// Calculates the best position to place an object in AR based on screen position. /// Could be used for tapping a location on the screen, dragging an object, or using a fixed /// cursor in the center of the screen for placing and moving objects. /// /// Returns the best placement position. /// Position of the parent anchor, i.e., where the /// object is before translation starts. /// Location on the screen in pixels to place the object at. /// The starting height of the plane to place the object on. /// How much should the object hover above the groundingPlane /// before it has been placed. /// The maximum distance allowed to translate the object. /// The translation mode, indicating the plane types allowed. /// /// /// Unity places objects along the x/z of the grounding plane. When placed on an AR plane /// below the grounding plane, the object will drop straight down onto it in world space. /// This prevents the object from being pushed deeper into the scene when moving from a /// higher plane to a lower plane. When moving from a lower plane to a higher plane, this /// function returns a new groundingPlane to replace the old one. /// [Obsolete("GetBestPlacementPosition has been deprecated. Use GetBestPlacementPosition with updated signature instead.")] public static Placement GetBestPlacementPosition( Vector3 currentAnchorPosition, Vector2 screenPos, float groundingPlaneHeight, float hoverOffset, float maxTranslationDistance, GestureTranslationMode gestureTranslationMode) { // For backwards compatibility, use the TestPlanes layer value from the AR examples project. const int fallbackLayerMask = 1 << 9; return GetBestPlacementPosition(currentAnchorPosition, screenPos, groundingPlaneHeight, hoverOffset, maxTranslationDistance, gestureTranslationMode, (ARSessionOrigin)null, fallbackLayerMask: fallbackLayerMask); } public partial struct Placement { #pragma warning disable IDE1006 // Naming Styles /// [Obsolete("HasHoveringPosition has been deprecated. Use hasHoveringPosition instead. (UnityUpgradable) -> hasHoveringPosition")] public bool HasHoveringPosition { get => hasHoveringPosition; set => hasHoveringPosition = value; } /// [Obsolete("HoveringPosition has been deprecated. Use hoveringPosition instead. (UnityUpgradable) -> hoveringPosition")] public Vector3 HoveringPosition { get => hoveringPosition; set => hoveringPosition = value; } /// [Obsolete("HasPlacementPosition has been deprecated. Use hasPlacementPosition instead. (UnityUpgradable) -> hasPlacementPosition")] public bool HasPlacementPosition { get => hasPlacementPosition; set => hasPlacementPosition = value; } /// [Obsolete("PlacementPosition has been deprecated. Use placementPosition instead. (UnityUpgradable) -> placementPosition")] public Vector3 PlacementPosition { get => placementPosition; set => placementPosition = value; } /// [Obsolete("PlacementRotation has been deprecated. Use placementRotation instead. (UnityUpgradable) -> placementRotation")] public Quaternion PlacementRotation { get => placementRotation; set => placementRotation = value; } /// [Obsolete("HasPlane has been deprecated. Use hasPlane instead. (UnityUpgradable) -> hasPlane")] public bool HasPlane { get => hasPlane; set => hasPlane = value; } /// [Obsolete("PlacementPlane has been deprecated. Use placementPlane instead. (UnityUpgradable) -> placementPlane")] public ARPlane PlacementPlane { get => placementPlane; set => placementPlane = value; } /// [Obsolete("UpdatedGroundingPlaneHeight has been deprecated. Use updatedGroundingPlaneHeight instead. (UnityUpgradable) -> updatedGroundingPlaneHeight")] public float UpdatedGroundingPlaneHeight { get => updatedGroundingPlaneHeight; set => updatedGroundingPlaneHeight = value; } #pragma warning restore IDE1006 // Naming Styles } } } #endif