Newer
Older
HoloAnatomy / Assets / HoloToolkit-Examples / SpatialUnderstanding / Scripts / LevelSolver.cs
SURFACEBOOK2\jackwynne on 25 May 2018 22 KB v1
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using UnityEngine;
using System.Collections;
using HoloToolkit.Unity;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System;

namespace HoloToolkit.Examples.SpatialUnderstandingFeatureOverview
{
    public class LevelSolver : LineDrawer
    {
        // Singleton
        public static LevelSolver Instance;

        // Enums
        public enum QueryStates
        {
            None,
            Processing,
            Finished
        }

        // Structs
        private struct QueryStatus
        {
            public void Reset()
            {
                State = QueryStates.None;
                Name = "";
                CountFail = 0;
                CountSuccess = 0;
                QueryResult = new List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult>();
            }

            public QueryStates State;
            public string Name;
            public int CountFail;
            public int CountSuccess;
            public List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult> QueryResult;
        }

        private struct PlacementQuery
        {
            public PlacementQuery(
                SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition placementDefinition,
                List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule> placementRules = null,
                List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementConstraint> placementConstraints = null)
            {
                PlacementDefinition = placementDefinition;
                PlacementRules = placementRules;
                PlacementConstraints = placementConstraints;
            }

            public SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition PlacementDefinition;
            public List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule> PlacementRules;
            public List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementConstraint> PlacementConstraints;
        }

        private class PlacementResult
        {
            public PlacementResult(float timeDelay, SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult result)
            {
                Box = new AnimatedBox(timeDelay, result.Position, Quaternion.LookRotation(result.Forward, result.Up), Color.blue, result.HalfDims);
                Result = result;
            }

            public LineDrawer.AnimatedBox Box;
            public SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult Result;
        }

        // Properties
        public bool IsSolverInitialized { get; private set; }

        // Privates
        private List<PlacementResult> placementResults = new List<PlacementResult>();
        private QueryStatus queryStatus = new QueryStatus();

        // Functions
        private void Awake()
        {
            Instance = this;
        }

        public void ClearGeometry(bool clearAll = true)
        {
            placementResults.Clear();
            if (SpatialUnderstanding.Instance.AllowSpatialUnderstanding)
            {
                SpatialUnderstandingDllObjectPlacement.Solver_RemoveAllObjects();
            }
            AppState.Instance.ObjectPlacementDescription = "";

            if (clearAll && (SpaceVisualizer.Instance != null))
            {
                SpaceVisualizer.Instance.ClearGeometry(false);
            }
        }

        private bool Draw_PlacementResults()
        {
            bool needsUpdate = false;

            for (int i = 0; i < placementResults.Count; ++i)
            {
                needsUpdate |= Draw_AnimatedBox(placementResults[i].Box);
            }

            return needsUpdate;
        }

        private bool PlaceObjectAsync(
            string placementName,
            SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition placementDefinition,
            List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule> placementRules = null,
            List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementConstraint> placementConstraints = null,
            bool clearObjectsFirst = true)
        {
            return PlaceObjectAsync(
                placementName,
                new List<PlacementQuery>() { new PlacementQuery(placementDefinition, placementRules, placementConstraints) },
                clearObjectsFirst);
        }

        private bool PlaceObjectAsync(
            string placementName,
            List<PlacementQuery> placementList,
            bool clearObjectsFirst = true)
        {
            // If we already mid-query, reject the request
            if (queryStatus.State != QueryStates.None)
            {
                return false;
            }

            // Clear geo
            if (clearObjectsFirst)
            {
                ClearGeometry();
            }

            // Mark it
            queryStatus.Reset();
            queryStatus.State = QueryStates.Processing;
            queryStatus.Name = placementName;

            // Tell user we are processing
            AppState.Instance.ObjectPlacementDescription = placementName + " (processing)";

            // Kick off a thread to do process the queries
#if UNITY_EDITOR || !UNITY_WSA
            new System.Threading.Thread
#else
            System.Threading.Tasks.Task.Run
#endif
            (() =>
                {
                    // Go through the queries in the list
                    for (int i = 0; i < placementList.Count; ++i)
                    {
                        // Do the query
                        bool success = PlaceObject(
                            placementName,
                            placementList[i].PlacementDefinition,
                            placementList[i].PlacementRules,
                            placementList[i].PlacementConstraints, 
                            clearObjectsFirst, 
                            true);

                        // Mark the result
                        queryStatus.CountSuccess = success ? (queryStatus.CountSuccess + 1) : queryStatus.CountSuccess;
                        queryStatus.CountFail = !success ? (queryStatus.CountFail + 1) : queryStatus.CountFail;
                    }

                    // Done
                    queryStatus.State = QueryStates.Finished;
                }
            )
#if UNITY_EDITOR || !UNITY_WSA
            .Start()
#endif
            ;

            return true;
        }

        private bool PlaceObject(
            string placementName,
            SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition placementDefinition,
            List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule> placementRules = null,
            List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementConstraint> placementConstraints = null,
            bool clearObjectsFirst = true,
            bool isASync = false)
        {
            // Clear objects (if requested)
            if (!isASync && clearObjectsFirst)
            {
                ClearGeometry();
            }
            if (!SpatialUnderstanding.Instance.AllowSpatialUnderstanding)
            {
                return false;
            }

            // New query
            if (SpatialUnderstandingDllObjectPlacement.Solver_PlaceObject(
                    placementName,
                    SpatialUnderstanding.Instance.UnderstandingDLL.PinObject(placementDefinition),
                    (placementRules != null) ? placementRules.Count : 0,
                    ((placementRules != null) && (placementRules.Count > 0)) ? SpatialUnderstanding.Instance.UnderstandingDLL.PinObject(placementRules.ToArray()) : IntPtr.Zero,
                    (placementConstraints != null) ? placementConstraints.Count : 0,
                    ((placementConstraints != null) && (placementConstraints.Count > 0)) ? SpatialUnderstanding.Instance.UnderstandingDLL.PinObject(placementConstraints.ToArray()) : IntPtr.Zero,
                    SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticObjectPlacementResultPtr()) > 0)
            {
                SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult placementResult = SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticObjectPlacementResult();
                if (!isASync)
                {
                    // If not running async, we can just add the results to the draw list right now
                    AppState.Instance.ObjectPlacementDescription = placementName + " (1)";
                    float timeDelay = (float)placementResults.Count * AnimatedBox.DelayPerItem;
                    placementResults.Add(new PlacementResult(timeDelay, placementResult.Clone() as SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult));
                }
                else
                {
                    queryStatus.QueryResult.Add(placementResult.Clone() as SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult);
                }
                return true;
            }
            if (!isASync)
            {
                AppState.Instance.ObjectPlacementDescription = placementName + " (0)";
            }
            return false;
        }

        private void ProcessPlacementResults()
        {
            // Check it
            if (queryStatus.State != QueryStates.Finished)
            {
                return;
            }
            if (!SpatialUnderstanding.Instance.AllowSpatialUnderstanding)
            {
                return;
            }

            // Clear results
            ClearGeometry();

            // We will reject any above or below the ceiling/floor
            SpatialUnderstandingDll.Imports.QueryPlayspaceAlignment(SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceAlignmentPtr());
            SpatialUnderstandingDll.Imports.PlayspaceAlignment alignment = SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceAlignment();

            // Copy over the results
            for (int i = 0; i < queryStatus.QueryResult.Count; ++i)
            {
                if ((queryStatus.QueryResult[i].Position.y < alignment.CeilingYValue) &&
                    (queryStatus.QueryResult[i].Position.y > alignment.FloorYValue))
                {
                    float timeDelay = (float)placementResults.Count * AnimatedBox.DelayPerItem;
                    placementResults.Add(new PlacementResult(timeDelay, queryStatus.QueryResult[i].Clone() as SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult));
                }
            }

            // Text
            AppState.Instance.ObjectPlacementDescription = queryStatus.Name + " (" + placementResults.Count + "/" + (queryStatus.CountSuccess + queryStatus.CountFail) + ")";

            // Mark done
            queryStatus.Reset();
        }

        public void Query_OnFloor()
        {
            List<PlacementQuery> placementQuery = new List<PlacementQuery>();
            for (int i = 0; i < 4; ++i)
            {
                float halfDimSize = UnityEngine.Random.Range(0.15f, 0.35f);
                placementQuery.Add(
                    new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnFloor(new Vector3(halfDimSize, halfDimSize, halfDimSize * 2.0f)),
                                        new List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule>() {
                                            SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule.Create_AwayFromOtherObjects(halfDimSize * 3.0f),
                                        }));
            }
            PlaceObjectAsync("OnFloor", placementQuery);
        }

        public void Query_OnWall()
        {
            List<PlacementQuery> placementQuery = new List<PlacementQuery>();
            for (int i = 0; i < 6; ++i)
            {
                float halfDimSize = UnityEngine.Random.Range(0.3f, 0.6f);
                placementQuery.Add(
                    new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnWall(new Vector3(halfDimSize, halfDimSize * 0.5f, 0.05f), 0.5f, 3.0f),
                                        new List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule>() {
                                            SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule.Create_AwayFromOtherObjects(halfDimSize * 4.0f),
                                        }));
            }
            PlaceObjectAsync("OnWall", placementQuery);
        }

        public void Query_OnCeiling()
        {
            List<PlacementQuery> placementQuery = new List<PlacementQuery>();
            for (int i = 0; i < 2; ++i)
            {
                float halfDimSize = UnityEngine.Random.Range(0.3f, 0.4f);
                placementQuery.Add(
                    new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnCeiling(new Vector3(halfDimSize, halfDimSize, halfDimSize)),
                                        new List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule>() {
                                            SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule.Create_AwayFromOtherObjects(halfDimSize * 3.0f),
                                        }));
            }
            PlaceObjectAsync("OnCeiling", placementQuery);
        }

        public void Query_OnEdge()
        {
            List<PlacementQuery> placementQuery = new List<PlacementQuery>();
            for (int i = 0; i < 8; ++i)
            {
                float halfDimSize = UnityEngine.Random.Range(0.05f, 0.1f);
                placementQuery.Add(
                    new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnEdge(new Vector3(halfDimSize, halfDimSize, halfDimSize),
                                                                                                                      new Vector3(halfDimSize, halfDimSize, halfDimSize)),
                                        new List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule>() {
                                            SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule.Create_AwayFromOtherObjects(halfDimSize * 3.0f),
                                        }));
            }
            PlaceObjectAsync("OnEdge", placementQuery);
        }

        public void Query_OnFloorAndCeiling()
        {
            SpatialUnderstandingDll.Imports.QueryPlayspaceAlignment(SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceAlignmentPtr());
            SpatialUnderstandingDll.Imports.PlayspaceAlignment alignment = SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceAlignment();
            List<PlacementQuery> placementQuery = new List<PlacementQuery>();
            for (int i = 0; i < 4; ++i)
            {
                float halfDimSize = UnityEngine.Random.Range(0.1f, 0.2f);
                placementQuery.Add(
                    new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnFloorAndCeiling(new Vector3(halfDimSize, (alignment.CeilingYValue - alignment.FloorYValue) * 0.5f, halfDimSize),
                                                                                                                             new Vector3(halfDimSize, (alignment.CeilingYValue - alignment.FloorYValue) * 0.5f, halfDimSize)),
                                        new List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule>() {
                                            SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule.Create_AwayFromOtherObjects(halfDimSize * 3.0f),
                                        }));
            }
            PlaceObjectAsync("OnFloorAndCeiling", placementQuery);
        }

        public void Query_RandomInAir_AwayFromMe()
        {
            List<PlacementQuery> placementQuery = new List<PlacementQuery>();
            for (int i = 0; i < 8; ++i)
            {
                float halfDimSize = UnityEngine.Random.Range(0.1f, 0.2f);
                placementQuery.Add(
                    new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_RandomInAir(new Vector3(halfDimSize, halfDimSize, halfDimSize)),
                                        new List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule>() {
                                            SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule.Create_AwayFromOtherObjects(halfDimSize * 3.0f),
                                            SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule.Create_AwayFromPosition(CameraCache.Main.transform.position, 2.5f),
                                        }));
            }
            PlaceObjectAsync("RandomInAir - AwayFromMe", placementQuery);
        }

        public void Query_OnEdge_NearCenter()
        {
            List<PlacementQuery> placementQuery = new List<PlacementQuery>();
            for (int i = 0; i < 4; ++i)
            {
                float halfDimSize = UnityEngine.Random.Range(0.05f, 0.1f);
                placementQuery.Add(
                    new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnEdge(new Vector3(halfDimSize, halfDimSize, halfDimSize), new Vector3(halfDimSize, halfDimSize, halfDimSize)),
                                       new List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule>() {
                                            SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule.Create_AwayFromOtherObjects(halfDimSize * 2.0f),
                                       },
                                       new List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementConstraint>() {
                                           SpatialUnderstandingDllObjectPlacement.ObjectPlacementConstraint.Create_NearCenter(),
                                       }));
            }
            PlaceObjectAsync("OnEdge - NearCenter", placementQuery);

        }

        public void Query_OnFloor_AwayFromMe()
        {
            List<PlacementQuery> placementQuery = new List<PlacementQuery>();
            for (int i = 0; i < 4; ++i)
            {
                float halfDimSize = UnityEngine.Random.Range(0.05f, 0.15f);
                placementQuery.Add(
                    new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnFloor(new Vector3(halfDimSize, halfDimSize, halfDimSize)),
                                       new List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule>() {
                                           SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule.Create_AwayFromPosition(CameraCache.Main.transform.position, 2.0f),
                                           SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule.Create_AwayFromOtherObjects(halfDimSize * 3.0f),
                                       }));
            }
            PlaceObjectAsync("OnFloor - AwayFromMe", placementQuery);
        }

        public void Query_OnFloor_NearMe()
        {
            List<PlacementQuery> placementQuery = new List<PlacementQuery>();
            for (int i = 0; i < 4; ++i)
            {
                float halfDimSize = UnityEngine.Random.Range(0.05f, 0.2f);
                placementQuery.Add(
                    new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnFloor(new Vector3(halfDimSize, halfDimSize, halfDimSize)),
                                       new List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule>() {
                                           SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule.Create_AwayFromOtherObjects(halfDimSize * 3.0f),
                                       },
                                       new List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementConstraint>() {
                                           SpatialUnderstandingDllObjectPlacement.ObjectPlacementConstraint.Create_NearPoint(CameraCache.Main.transform.position, 0.5f, 2.0f)
                                       }));
            }
            PlaceObjectAsync("OnFloor - NearMe", placementQuery);
        }

        private void Update_Queries()
        {
            if (Input.GetKeyDown(KeyCode.R))
            {
                Query_OnFloor();
            }
            if (Input.GetKeyDown(KeyCode.T))
            {
                Query_OnWall();
            }
            if (Input.GetKeyDown(KeyCode.Y))
            {
                Query_OnCeiling();
            }
            if (Input.GetKeyDown(KeyCode.U))
            {
                Query_OnEdge();
            }
            if (Input.GetKeyDown(KeyCode.I))
            {
                Query_OnFloorAndCeiling();
            }
            if (Input.GetKeyDown(KeyCode.O))
            {
                Query_RandomInAir_AwayFromMe();
            }
            if (Input.GetKeyDown(KeyCode.P))
            {
                Query_OnEdge_NearCenter();
            }
            if (Input.GetKeyDown(KeyCode.LeftBracket))
            {
                Query_OnFloor_AwayFromMe();
            }
            if (Input.GetKeyDown(KeyCode.RightBracket))
            {
                Query_OnFloor_NearMe();
            }
        }

        public bool InitializeSolver()
        {
            if (IsSolverInitialized ||
                !SpatialUnderstanding.Instance.AllowSpatialUnderstanding)
            {
                return IsSolverInitialized;
            }

            if (SpatialUnderstandingDllObjectPlacement.Solver_Init() == 1)
            {
                IsSolverInitialized = true;
            }
            return IsSolverInitialized;
        }

        private void Update()
        {
            // Can't do any of this till we're done with the scanning phase
            if (SpatialUnderstanding.Instance.ScanState != SpatialUnderstanding.ScanStates.Done)
            {
                return;
            }

            // Make sure the solver has been initialized
            if (!IsSolverInitialized &&
                SpatialUnderstanding.Instance.AllowSpatialUnderstanding)
            {
                InitializeSolver();
            }

            // Constraint queries
            if (SpatialUnderstanding.Instance.ScanState == SpatialUnderstanding.ScanStates.Done)
            {
                Update_Queries();
            }

            // Handle async query results
            ProcessPlacementResults();

            // Lines: Begin
            LineDraw_Begin();

            // Drawers
            bool needsUpdate = false;
            needsUpdate |= Draw_PlacementResults();

            // Lines: Finish up
            LineDraw_End(needsUpdate);
        }
    }
}