Looking at gaze

Share on Twitter Share on Facebook Share on LinkedIn

Looking at gaze

Description

So, while we can add things to our scene and even interact with them by touching them on our screen as seen in the Gestures lesson.

You may also want to determine which node your camera is currently looking at. Have a look at the code sample below.

You can see in the video and in the sample that i've added a crosshair to represent the center of the screen and camera gaze. You could always remove or change the crosshair if you wanted


Video


Code

using ARKit;
using SceneKit;
using UIKit;
using System.Linq;
using Foundation;
using CoreGraphics;
using CoreFoundation;
using System;
using System.Collections.Generic;

namespace XamarinArkitSample
{
    public partial class GazeViewController : UIViewController, IARSCNViewDelegate
    {
        private readonly ARSCNView sceneView;
        PlaneNode node;
        List<PlaneNode> nodes = new List<PlaneNode>();

        public GazeViewController()
        {
            this.sceneView = new ARSCNView
            {
                AutoenablesDefaultLighting = true,
                Delegate = this
            };

            this.View.AddSubview(this.sceneView);
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            this.sceneView.Frame = this.View.Frame;
        }

        public override void ViewDidAppear(bool animated)
        {
            base.ViewDidAppear(animated);

            this.sceneView.Session.Run(new ARWorldTrackingConfiguration
            {
                AutoFocusEnabled = true,
                LightEstimationEnabled = true,
                WorldAlignment = ARWorldAlignment.Gravity,
            }, ARSessionRunOptions.ResetTracking | ARSessionRunOptions.RemoveExistingAnchors); ;

            // Add 9 squares to the screen
            nodes.Add(GetPlaneNode(new SCNVector3(-0.2f, 0, 0)));
            nodes.Add(GetPlaneNode(new SCNVector3(-0.2f, -0.2f, 0)));
            nodes.Add(GetPlaneNode(new SCNVector3(-0.2f, 0.2f, 0)));
            nodes.Add(GetPlaneNode(new SCNVector3(0, 0, 0)));
            nodes.Add(GetPlaneNode(new SCNVector3(0, -0.2f, 0)));
            nodes.Add(GetPlaneNode(new SCNVector3(0, 0.2f, 0)));
            nodes.Add(GetPlaneNode(new SCNVector3(0.2f, 0, 0)));
            nodes.Add(GetPlaneNode(new SCNVector3(0.2f, -0.2f, 0)));
            nodes.Add(GetPlaneNode(new SCNVector3(0.2f, 0.2f, 0)));

            foreach(SCNNode node in nodes)
            {
                this.sceneView.Scene.RootNode.AddChildNode(node);
            }

            // Add crosshair to middle of screen to represent direction of gaze
            UILabel label = new UILabel();
            label.Text = "+";
            label.Center = new CGPoint(sceneView.Bounds.GetMidX(), sceneView.Bounds.GetMidY());
            label.TextAlignment = UITextAlignment.Center;
            label.Bounds = new CGRect(100, 100, 100, 100);

            this.sceneView.AddSubview(label);
        }

        private PlaneNode GetPlaneNode(SCNVector3 position)
        {
            var node = new PlaneNode(position);
            node.Opacity = 0.8f;
            
            var material = new SCNMaterial();
            material.Diffuse.Contents = UIColor.White;

            var geometry = SCNPlane.Create(0.18f, 0.18f);
            geometry.CornerRadius = 0.01f;
            geometry.FirstMaterial = material;

            node.Geometry = geometry;

            return node;
        }

        private void PerformHitTest()
        {
            var screenCenter = new CGPoint(sceneView.Bounds.GetMidX(), sceneView.Bounds.GetMidY());
            var hitTestOptions = new SCNHitTestOptions();
            var hits = this.sceneView.HitTest(screenCenter, hitTestOptions);
            var hit = hits.FirstOrDefault();

            // Set all planes to white
            if (hit == null)
            {
                if (node == null)
                    return;

                var materialWhite = new SCNMaterial();
                materialWhite.Diffuse.Contents = UIColor.White;

                foreach (PlaneNode node in nodes)
                {
                    node.Geometry.FirstMaterial = materialWhite;

                    if (node.IsActive)
                    {
                        node.MoveBack();
                        node.IsActive = false;
                    }
                }

                return;
            }

            // Set to red
            node = hit.Node as PlaneNode;

            if (node == null)
                return;

            var material = new SCNMaterial();
            material.Diffuse.Contents = UIColor.Red;
            node.Geometry.FirstMaterial = material;

            if(!node.IsActive)
            {
                node.MoveForward();
                node.IsActive = true;
            }

            return;
        }

        [Export("renderer:updateAtTime:")]
        public void RendererUpdateAtTime(SCNSceneRenderer renderer, double updateAtTime)
        {
            DispatchQueue.MainQueue.DispatchAsync(() =>
            {
                PerformHitTest();
            });
        }

        public override void ViewDidDisappear(bool animated)
        {
            base.ViewDidDisappear(animated);

            foreach (var node in this.sceneView.Scene.RootNode.ChildNodes)
            {
                node.RemoveFromParentNode();
            }

            this.sceneView.Session.Pause();
        }

        public class PlaneNode : SCNNode
        {
            SCNAction MoveForwardAction;
            SCNAction MoveBackAction;

            public bool IsActive { get; set; }
            public SCNVector3 OriginalPosition { get; set; }
            public SCNVector3 ForwardPosition { get; set; }

            public PlaneNode(SCNVector3 position)
            {
                Position = position;
                OriginalPosition = position;
                ForwardPosition = new SCNVector3(position.X, position.Y, position.Z + 0.075f);
                MoveForwardAction = SCNAction.MoveTo(ForwardPosition, 0.2);
                MoveBackAction = SCNAction.MoveTo(OriginalPosition, 0.2);
            }

            public void MoveForward()
            {
                this.RunAction(MoveForwardAction);
            }

            public void MoveBack()
            {
                this.RunAction(MoveBackAction);
            }
        }
    }
}

Next Step : SkiaSharp 2D Graphics

After you have mastered this you should try SkiaSharp 2D Graphics