public void UpdateTranslation(VirtualObject virtualObject, CGPoint midpoint)
        {
            if (!TranslationThresholdPassed)
            {
                var initialLocationToCurrentLocation = midpoint.Subtract(InitialMidpoint);
                var distanceFromStartLocation        = initialLocationToCurrentLocation.Length();

                // Check if the translate gesture has crossed the threshold.
                // If the user is already rotating and or scaling we use a bigger threshold.

                var threshold = TranslationThreshold;
                if (RotationThresholdPassed || TranslationThresholdPassed)
                {
                    threshold = TranslationThresholdHarder;
                }

                if (distanceFromStartLocation >= threshold)
                {
                    TranslationThresholdPassed = true;

                    var currentObjectLocation = CGPointExtensions.FromVector(SceneView.ProjectPoint(virtualObject.Position));
                    DragOffset = midpoint.Subtract(currentObjectLocation);
                }
            }

            if (TranslationThresholdPassed)
            {
                var offsetPos = midpoint.Subtract(DragOffset);
                manager.Translate(virtualObject, SceneView, offsetPos, false, true);
                LastUsedObject = virtualObject;
            }
        }
        public void UpdateVirtualObjectPosition(VirtualObject virtualObject, SCNVector3 position, bool filterPosition, NMatrix4 cameraTransform)
        {
            var cameraWorldPos   = cameraTransform.Translation();
            var cameraToPosition = position.Subtract(cameraWorldPos);

            // Limit the distance of the object from the camera to a maximum of 10m
            if (cameraToPosition.LengthFast > 10)
            {
                cameraToPosition = cameraToPosition.Normalized() * 10;
            }

            // Compute the average distance of the object from the camera over the last ten
            // updates. If filterPosition is true, compute a new position for the object
            // with this average. Notice that the distance is applied to the vector from
            // the camera to the content, so it only affects the percieved distance of the
            // object - the averaging does _not_ make the content "lag".
            var hitTestResultDistance = cameraToPosition.LengthFast;

            virtualObject.RecentVirtualObjectDistances.Add(hitTestResultDistance);
            virtualObject.RecentVirtualObjectDistances.KeepLast(10);

            if (filterPosition)
            {
                var averageDistance     = virtualObject.RecentVirtualObjectDistances.Average();
                var averagedDistancePos = cameraWorldPos + cameraToPosition.Normalized() * averageDistance;
                virtualObject.Position = averagedDistancePos;
            }
            else
            {
                virtualObject.Position = cameraWorldPos + cameraToPosition;
            }
        }
        public void DidLoad(VirtualObjectManager manager, VirtualObject virtualObject)
        {
            IsLoadingObject = false;

            //Remove progress indicator
            Spinner.RemoveFromSuperview();
            AddObjectButton.SetImage(UIImage.FromBundle("add"), UIControlState.Normal);
            AddObjectButton.SetImage(UIImage.FromBundle("addPressed"), UIControlState.Highlighted);
        }
Beispiel #4
0
        public SingleFingerGesture(NSSet touches, ARSCNView scnView, VirtualObject lastUsedObject, VirtualObjectManager vom)
            : base(touches, scnView, lastUsedObject, vom)
        {
            var firstTouch = (UITouch)touches.First();

            initialTouchPosition = firstTouch.LocationInView(scnView);
            latestTouchPosition  = initialTouchPosition;

            firstTouchedObject = this.VirtualObjectAt(initialTouchPosition);
        }
 public void WillLoad(VirtualObjectManager manager, VirtualObject virtualObject)
 {
     // Show progress indicator
     Spinner        = new UIActivityIndicatorView();
     Spinner.Center = AddObjectButton.Center;
     Spinner.Bounds = new CGRect(0, 0, AddObjectButton.Bounds.Width - 5, AddObjectButton.Bounds.Height - 5);
     AddObjectButton.SetImage(UIImage.FromBundle("buttonring"), UIControlState.Normal);
     SceneView.AddSubview(Spinner);
     Spinner.StartAnimating();
     IsLoadingObject = true;
 }
Beispiel #6
0
        public Gesture(NSSet currentTouches, ARSCNView sceneView, VirtualObject lastUsedObject, VirtualObjectManager manager)
        {
            this.currentTouches = currentTouches;
            this.SceneView      = sceneView;
            this.LastUsedObject = lastUsedObject;
            this.manager        = manager;

            // Refresh the current gesture at 60 Hz - This ensures smooth updates even when no
            // new touch events are incoming (but the camera might have moved).
            this.refreshTimer = new System.Threading.Timer(CheckedUpdateGesture, null, 0, 17);
        }
 private void SetPosition(VirtualObject virtualObject, SCNVector3 position, bool instantly, bool filterPosition, NMatrix4 cameraTransform)
 {
     if (instantly)
     {
         SetNewVirtualObjectPosition(virtualObject, position, cameraTransform);
     }
     else
     {
         UpdateVirtualObjectPosition(virtualObject, position, filterPosition, cameraTransform);
     }
 }
        public TwoFingerGesture(NSSet touches, ARSCNView view, VirtualObject parentObject, VirtualObjectManager manager) : base(touches, view, parentObject, manager)
        {
            var tArray = touches.ToArray <UITouch>();

            FirstTouch  = tArray[0];
            SecondTouch = tArray[1];

            var firstTouchPoint  = FirstTouch.LocationInView(SceneView);
            var secondTouchPoint = SecondTouch.LocationInView(SceneView);

            InitialMidpoint = firstTouchPoint.Add(secondTouchPoint).Divide(2f);

            // Compute the two other corners of the rectangle defined by the two fingers
            // and compute the points in between.
            var thirdCorner  = new CGPoint(firstTouchPoint.X, secondTouchPoint.Y);
            var fourthCorner = new CGPoint(secondTouchPoint.X, firstTouchPoint.Y);

            //  Compute points in between.
            var midpoints = new[]
            {
                thirdCorner.Add(firstTouchPoint).Divide(2f),
                thirdCorner.Add(secondTouchPoint).Divide(2f),
                fourthCorner.Add(firstTouchPoint).Divide(2f),
                fourthCorner.Add(secondTouchPoint).Divide(2f),
                InitialMidpoint.Add(firstTouchPoint).Divide(2f),
                InitialMidpoint.Add(secondTouchPoint).Divide(2f),
                InitialMidpoint.Add(thirdCorner).Divide(2f),
                InitialMidpoint.Add(fourthCorner).Divide(2f)
            };

            // Check if any of the two fingers or their midpoint is touching the object.
            // Based on that, translation, rotation and scale will be enabled or disabled.
            var allPoints = new List <CGPoint>(new[] { firstTouchPoint, secondTouchPoint, thirdCorner, fourthCorner, InitialMidpoint });

            allPoints.AddRange(midpoints);
            FirstTouchedObject = allPoints.Select(pt => this.VirtualObjectAt(pt)).Where(vo => vo != null).FirstOrDefault();

            if (FirstTouchedObject != null)
            {
                ObjectBaseScale  = FirstTouchedObject.Scale.X;
                AllowTranslation = true;
                AllowRotation    = true;
                InitialDistanceBetweenFingers = (firstTouchPoint.Subtract(secondTouchPoint)).Length();
                InitialFingerAngle            = Math.Atan2(InitialMidpoint.X, InitialMidpoint.Y);
                InitialObjectAngle            = FirstTouchedObject.EulerAngles.Y;
            }
            else
            {
                AllowTranslation = false;
                AllowRotation    = false;
            }
        }
 private void UnloadVirtualObject(VirtualObject vo)
 {
     vo.Unload();
     vo.RemoveFromParentNode();
     if (lastUsedObject == vo)
     {
         lastUsedObject = null;
         if (VirtualObjects.Count() > 1)
         {
             lastUsedObject = VirtualObjects[0];
         }
     }
 }
        private void SetNewVirtualObjectPosition(VirtualObject virtualObject, SCNVector3 position, NMatrix4 cameraTransform)
        {
            var cameraWorldPos   = cameraTransform.Translation();
            var cameraToPosition = position.Subtract(cameraWorldPos);

            // Limit the distance of the object from the camera to a maximum of 10m
            if (cameraToPosition.LengthFast > 10)
            {
                cameraToPosition = cameraToPosition.Normalized() * 10;
            }

            virtualObject.Position = cameraWorldPos + cameraToPosition;
            virtualObject.RecentVirtualObjectDistances.Clear();
        }
Beispiel #11
0
        // Hit tests against the `sceneView` to find an object at the provided point.
        protected VirtualObject VirtualObjectAt(CGPoint loc)
        {
            var hitTestOptions = new SCNHitTestOptions {
                BoundingBoxOnly = true
            };
            var hitTestResults = SceneView.HitTest(loc, hitTestOptions);

            if (hitTestResults.Count() == 0)
            {
                return(null);
            }
            var firstNodeHit = hitTestResults.First((result) => VirtualObject.IsNodePartOfVirtualObject(result.Node));
            var voNode       = VirtualObject.ForChildNode(firstNodeHit.Node);

            return(voNode);
        }
        public void LoadVirtualObject(VirtualObject vo, SCNVector3 position, NMatrix4 cameraTransform)
        {
            VirtualObjects.Add(vo);
            if (Delegate != null)
            {
                Delegate.WillLoad(this, vo);
            }
            vo.Load();

            // Immediately place the object in 3D space
            SetNewVirtualObjectPosition(vo, position, cameraTransform);
            lastUsedObject = vo;
            if (Delegate != null)
            {
                Delegate.DidLoad(this, vo);
            }
        }
        public void UpdateRotation(VirtualObject virtualObject, CGPoint span)
        {
            var midPointToFirstTouch = span.Divide(2f);
            var currentAngle         = Math.Atan2(midPointToFirstTouch.X, midPointToFirstTouch.Y);

            var currentAngleToInitialFingerAngle = InitialFingerAngle - currentAngle;

            if (!RotationThresholdPassed)
            {
                var threshold = RotationThreshold;

                if (TranslationThresholdPassed || ScaleThresholdPassed)
                {
                    threshold = RotationThresholdHarder;
                }

                if ((float)Math.Abs(currentAngleToInitialFingerAngle) > threshold)
                {
                    RotationThresholdPassed = true;

                    // Change the initial object angle to prevent a sudden jump after crossing the threshold.
                    if (currentAngleToInitialFingerAngle > 0f)
                    {
                        InitialObjectAngle += threshold;
                    }
                    else
                    {
                        InitialObjectAngle -= threshold;
                    }
                }
            }

            if (RotationThresholdPassed)
            {
                // Note:
                // For looking down on the object (99% of all use cases), we need to subtract the angle.
                // To make rotation also work correctly when looking from below the object one would have to
                // flip the sign of the angle depending on whether the object is above or below the camera...
                var x = virtualObject.EulerAngles.X;
                var y = InitialObjectAngle - currentAngleToInitialFingerAngle;
                var z = virtualObject.EulerAngles.Z;
                virtualObject.EulerAngles = new SCNVector3(x, (float)y, z);
                LastUsedObject            = virtualObject;
            }
        }
        public void DidSelectObjectAt(int index)
        {
            if (Session == null || ViewController.CurrentFrame == null)
            {
                return;
            }
            var cameraTransform = ViewController.CurrentFrame.Camera.Transform;

            var definition = VirtualObjectManager.AvailableObjects[index];
            var vo         = new VirtualObject(definition);
            var position   = FocusSquare != null ? FocusSquare.LastPosition : new SCNVector3(0, 0, -1.0f);

            virtualObjectManager.LoadVirtualObject(vo, position, cameraTransform);
            if (vo.ParentNode == null)
            {
                SceneView.Scene.RootNode.AddChildNode(vo);
            }
        }
 public void ReactToTouchesBegan(NSSet touches, UIEvent evt, ARSCNView scnView)
 {
     if (!VirtualObjects.Any())
     {
         return;
     }
     if (currentGesture == null)
     {
         currentGesture = Gesture.StartGestureFromTouches(touches, scnView, lastUsedObject, this);
     }
     else
     {
         currentGesture = currentGesture.UpdateGestureFromTouches(touches, TouchEventType.TouchBegan);
     }
     if (currentGesture != null && currentGesture.LastUsedObject != null)
     {
         lastUsedObject = currentGesture.LastUsedObject;
     }
 }
        private void MoveIfNecessary(NSSet touches, UIEvent evt, TouchEventType evtType)
        {
            if (VirtualObjects.Count() == 0)
            {
                return;
            }
            currentGesture = currentGesture?.UpdateGestureFromTouches(touches, evtType);
            var newObj = currentGesture?.LastUsedObject;

            if (newObj != null)
            {
                lastUsedObject = newObj;
            }

            var gesture    = currentGesture;
            var touchedObj = gesture?.LastUsedObject;

            if (gesture != null && touchedObj != null)
            {
                Delegate?.TransformDidChangeFor(this, touchedObj);
            }
        }
 public void Translate(VirtualObject vObject, ARSCNView sceneView, CGPoint screenPos, bool instantly, bool infinitePlane)
 {
     DispatchQueue.MainQueue.DispatchAsync(() =>
     {
         var result      = WorldPositionFromScreenPosition(screenPos, sceneView, vObject.Position, infinitePlane);
         var newPosition = result.Item1;
         if (newPosition == null)
         {
             if (this.Delegate != null)
             {
                 this.Delegate.CouldNotPlace(this, vObject);
                 return;
             }
         }
         var currentFrame = ViewController.CurrentFrame;
         if (currentFrame == null || currentFrame.Camera == null)
         {
             return;
         }
         var cameraTransform = currentFrame.Camera.Transform;
         queue.DispatchAsync(() => SetPosition(vObject, newPosition.Value, instantly, result.Item3, cameraTransform));
     });
 }
 public void CouldNotPlace(VirtualObjectManager manager, VirtualObject virtualObject)
 {
     UserFeedback.ShowMessage("Cannot place object\nTry moving left or right.");
 }
Beispiel #19
0
        // Static factory method
        public static Gesture StartGestureFromTouches(NSSet currentTouches, ARSCNView sceneView, VirtualObject lastUsedObject, VirtualObjectManager manager)
        {
            switch (currentTouches.Count)
            {
            case 1: return(new SingleFingerGesture(currentTouches, sceneView, lastUsedObject, manager));

            case 2: return(new TwoFingerGesture(currentTouches, sceneView, lastUsedObject, manager));

            default:
                return(null);
            }
        }