Manages a single instance of the Tango 3D Reconstruction library, updating a single 3D model based on depth and color information.
Inheritance: IDisposable, ITangoLifecycle, ITangoDepthMultithreaded, ITangoVideoOverlayMultithreaded
 /// <summary>
 /// Set the grid index as having been observed from the given direction.
 /// </summary>
 /// <param name="gridIndex">Grid index to observe.</param>
 private void _ViewGridIndex(Tango3DReconstruction.GridIndex gridIndex)
 {
     // This update may occur somewhat later than the actual time of the camera pose observation.
     Vector3 dir = Camera.main.transform.forward;
     dir = new Vector3(dir.x, 0.0f, dir.z).normalized;
     Vector3[] directions = new Vector3[]
     {
         new Vector3(0, 0, 1),
         new Vector3(0, 0, -1),
         new Vector3(1, 0, 0),
         new Vector3(-1, 0, 0)
     };
     
     for (int i = 0; i < 4; i++)
     {
         // If the camera is facing one of 4 directions (every 90 degrees) within a 45 degree spread,
         // set that direction as seen.
         float dot = Vector3.Dot(dir, directions[i]);
         if (dot > m_minDirectionCheck)
         {
             // Bitwise OR new and old directions to show that the mesh has been observed from the new direction.
             byte direction = (byte)(1 << i);
             m_meshes[gridIndex].m_directions = (byte)(m_meshes[gridIndex].m_directions | direction);
             break;
         }
     }
 }
    /// <summary>
    /// Update the bounding box.
    /// </summary>
    /// <param name="gridIndex">Grid index to include in bounds.</param>
    private void _UpdateBounds(Tango3DReconstruction.GridIndex gridIndex)
    {
        float gridIndexSize = m_tangoApplication.m_3drResolutionMeters * 16;
        Vector3 pointToCompare = gridIndexSize * new Vector3(gridIndex.x, gridIndex.y, gridIndex.z);

        Vector3 min = m_bounds.min;
        Vector3 max = m_bounds.max;

        if (m_bounds.min.x > pointToCompare.x)
        {
            min.x = pointToCompare.x;
        }

        if (m_bounds.min.y > pointToCompare.y)
        {
            min.y = pointToCompare.y;
        }

        if (m_bounds.min.z > pointToCompare.z)
        {
            min.z = pointToCompare.z;
        }

        if (m_bounds.max.x < pointToCompare.x)
        {
            max.x = pointToCompare.x;
        }

        if (m_bounds.max.y < pointToCompare.y)
        {
            max.y = pointToCompare.y;
        }

        if (m_bounds.max.z < pointToCompare.z)
        {
            max.z = pointToCompare.z;
        }

        m_bounds.SetMinMax(min, max);
    }
    /// <summary>
    /// Extract and update (or create, if it doesn't exist) the mesh at the given grid index.
    /// </summary>
    /// <param name="gridIndex">Grid index.</param>
    /// <param name="needsResize">List to which indices needing a future resize will be added.</param>
    private void _UpdateMeshAtGridIndex(Tango3DReconstruction.GridIndex gridIndex, List<Tango3DReconstruction.GridIndex> needsResize)
    {
        TangoSingleDynamicMesh dynamicMesh;
        if (!m_meshes.TryGetValue(gridIndex, out dynamicMesh))
        {
            // build a dynamic mesh as a child of this game object.
            GameObject newObj = new GameObject();
            newObj.transform.parent = transform;
            newObj.name = string.Format("{0},{1},{2}", gridIndex.x, gridIndex.y, gridIndex.z);
            newObj.layer = gameObject.layer;
            dynamicMesh = newObj.AddComponent<TangoSingleDynamicMesh>();
            dynamicMesh.m_vertices = new Vector3[INITIAL_VERTEX_COUNT];
            if (m_tangoApplication.m_3drGenerateTexCoord)
            {
                dynamicMesh.m_uv = new Vector2[INITIAL_VERTEX_COUNT];
            }
            
            if (m_tangoApplication.m_3drGenerateColor)
            {
                dynamicMesh.m_colors = new Color32[INITIAL_VERTEX_COUNT];
            }
            
            dynamicMesh.m_triangles = new int[INITIAL_INDEX_COUNT];
            
            // Update debug info too.
            m_debugTotalVertices = dynamicMesh.m_vertices.Length;
            m_debugTotalTriangles = dynamicMesh.m_triangles.Length;
            
            // Add the other necessary objects
            MeshFilter meshFilter = newObj.AddComponent<MeshFilter>();
            dynamicMesh.m_mesh = meshFilter.mesh;
            
            if (m_meshRenderer != null)
            {
                MeshRenderer meshRenderer = newObj.AddComponent<MeshRenderer>();
                #if UNITY_5
                meshRenderer.shadowCastingMode = m_meshRenderer.shadowCastingMode;
                meshRenderer.receiveShadows = m_meshRenderer.receiveShadows;
                meshRenderer.sharedMaterials = m_meshRenderer.sharedMaterials;
                meshRenderer.useLightProbes = m_meshRenderer.useLightProbes;
                meshRenderer.reflectionProbeUsage = m_meshRenderer.reflectionProbeUsage;
                meshRenderer.probeAnchor = m_meshRenderer.probeAnchor;
                #elif UNITY_4_6
                meshRenderer.castShadows = m_meshRenderer.castShadows;
                meshRenderer.receiveShadows = m_meshRenderer.receiveShadows;
                meshRenderer.sharedMaterials = m_meshRenderer.sharedMaterials;
                meshRenderer.useLightProbes = m_meshRenderer.useLightProbes;
                meshRenderer.lightProbeAnchor = m_meshRenderer.lightProbeAnchor;
                #endif
            }
            
            if (m_meshCollider != null)
            {
                MeshCollider meshCollider = newObj.AddComponent<MeshCollider>();
                meshCollider.convex = m_meshCollider.convex;
                meshCollider.isTrigger = m_meshCollider.isTrigger;
                meshCollider.sharedMaterial = m_meshCollider.sharedMaterial;
                meshCollider.sharedMesh = dynamicMesh.m_mesh;
                dynamicMesh.m_meshCollider = meshCollider;
            }
            
            m_meshes.Add(gridIndex, dynamicMesh);
            _UpdateBounds(gridIndex);
        }

        // Skip updating this grid index if it is considered completed.
        if (m_enableSelectiveMeshing) 
        {
            if (dynamicMesh.m_completed)
            {
                return;
            }

            _ObserveGridIndex(gridIndex, dynamicMesh);
        }
        
        // Last frame the mesh needed more space.  Give it more room now.
        if (dynamicMesh.m_needsToGrow)
        {
            int newVertexSize = (int)(dynamicMesh.m_vertices.Length * GROWTH_FACTOR);
            int newTriangleSize = (int)(dynamicMesh.m_triangles.Length * GROWTH_FACTOR);
            newTriangleSize -= newTriangleSize % 3;
            
            // Remove the old size, add the new size.
            m_debugTotalVertices += newVertexSize - dynamicMesh.m_vertices.Length;
            m_debugTotalTriangles += newTriangleSize - dynamicMesh.m_triangles.Length;
            
            dynamicMesh.m_vertices = new Vector3[newVertexSize];
            if (m_tangoApplication.m_3drGenerateTexCoord)
            {
                dynamicMesh.m_uv = new Vector2[newVertexSize];
            }
            
            if (m_tangoApplication.m_3drGenerateColor)
            {
                dynamicMesh.m_colors = new Color32[newVertexSize];
            }
            
            dynamicMesh.m_triangles = new int[newTriangleSize];
            dynamicMesh.m_needsToGrow = false;
        }
        
        int numVertices;
        int numTriangles;
        Tango3DReconstruction.Status status = m_tangoApplication.Tango3DRExtractMeshSegment(
            gridIndex, dynamicMesh.m_vertices, null, dynamicMesh.m_colors, dynamicMesh.m_triangles,
            out numVertices, out numTriangles);
        if (status != Tango3DReconstruction.Status.INSUFFICIENT_SPACE
            && status != Tango3DReconstruction.Status.SUCCESS)
        {
            Debug.Log("Tango3DR extraction failed, status code = " + status + Environment.StackTrace);
            return;
        }
        else if (status == Tango3DReconstruction.Status.INSUFFICIENT_SPACE)
        {
            // We already spent the time extracting this mesh, let's not spend any more time this frame
            // to extract the mesh.
            Debug.Log(string.Format(
                "TangoDynamicMesh.Update() extraction ran out of space with room for {0} vertexes, {1} indexes.",
                dynamicMesh.m_vertices.Length, dynamicMesh.m_triangles.Length));
            dynamicMesh.m_needsToGrow = true;
            needsResize.Add(gridIndex);
        }
        
        // Make any leftover triangles degenerate.
        for (int triangleIt = numTriangles * 3; triangleIt < dynamicMesh.m_triangles.Length; ++triangleIt)
        {
            dynamicMesh.m_triangles[triangleIt] = 0;
        }
        
        if (dynamicMesh.m_uv != null)
        {
            // Add texture coordinates.
            for (int vertexIt = 0; vertexIt < numVertices; ++vertexIt)
            {
                Vector3 vertex = dynamicMesh.m_vertices[vertexIt];
                dynamicMesh.m_uv[vertexIt].x = vertex.x * UV_PER_METERS;
                dynamicMesh.m_uv[vertexIt].y = (vertex.z + vertex.y) * UV_PER_METERS;
            }
        }

        dynamicMesh.m_mesh.Clear();
        dynamicMesh.m_mesh.vertices = dynamicMesh.m_vertices;
        dynamicMesh.m_mesh.uv = dynamicMesh.m_uv;
        dynamicMesh.m_mesh.colors32 = dynamicMesh.m_colors;
        dynamicMesh.m_mesh.triangles = dynamicMesh.m_triangles;
        if (m_tangoApplication.m_3drGenerateNormal)
        {
            dynamicMesh.m_mesh.RecalculateNormals();
        }
        
        if (dynamicMesh.m_meshCollider != null)
        {
            // Force the mesh collider to update too.
            dynamicMesh.m_meshCollider.sharedMesh = null;
            dynamicMesh.m_meshCollider.sharedMesh = dynamicMesh.m_mesh;
        }
    }
    /// <summary>
    /// When the grid index has been updated, also determine whether it should be considered completed
    /// based on its neighboring grid indices, number of observations, and mesh completeness.
    /// 
    /// When checking a grid index for completeness, the observation count of neighboring grid indices is checked.
    /// If all grid indices contained in one of the configurations have a sufficient number of observations,
    /// the grid index is considered complete.
    /// </summary>
    /// <param name="gridIndex">Grid index to observe.</param>
    /// <param name="singleMesh">TangoSingleDynamicMesh to update and observe.</param>
    private void _ObserveGridIndex(Tango3DReconstruction.GridIndex gridIndex, TangoSingleDynamicMesh singleMesh)
    {
        // Increment the observations made for this grid index.
        singleMesh.m_observations++;

        // Add observation based on the direction of the observation.
        _ViewGridIndex(gridIndex);

        // Exit if the grid index has not been observed from all 8 directions.
        if (singleMesh.m_directions != DIRECTIONS_COMPLETE)
        {
            return;
        }

        // Run through each grid index configuration and check if the grid index is complete.
        for (int i = 0; i < m_gridIndexConfigs.Length; i++)
        {
            Vector3[] config = m_gridIndexConfigs[i];
            bool neighborsObserved = true;
            foreach (Vector3 nPosition in config)
            {
                Tango3DReconstruction.GridIndex neighbor = new Tango3DReconstruction.GridIndex();
                neighbor.x = (Int32)(nPosition.x + gridIndex.x);
                neighbor.y = (Int32)(nPosition.y + gridIndex.y);
                neighbor.z = (Int32)(nPosition.z + gridIndex.z);
                TangoSingleDynamicMesh nSingleMesh;
                if (m_meshes.TryGetValue(neighbor, out nSingleMesh))
                {
                    if (nSingleMesh.m_observations < NUM_OBSERVATIONS_TO_COMPLETE)
                    {
                        neighborsObserved = false;
                        break;
                    }
                }
            }

            // Complete using this configurations of the neighbors with sufficient observations.
            if (neighborsObserved)
            {
                // Add the grid index to the completed list, so it will be skipped during next mesh update.
                singleMesh.m_completed = true;
                return;
            }
        }
    }
    /// <summary>
    /// Gets each single dynamic mesh and fills out arrays with properties. Each mesh corresponds to the same index in each array.
    /// </summary>
    /// <param name="gridIndices">Filled out with grid index of each mesh.</param>
    /// <param name="completed">Filled out with completion state of each mesh.</param>
    /// <param name="completionScale">Filled out with amount that each mesh has been completed.</param>
    /// <param name="directions">Filled out with a byte representation of the observed directions of each mesh.</param>
    public void GetSingleMeshProperties(out Tango3DReconstruction.GridIndex[] gridIndices, out bool[] completed, out float[] completionScale, out byte[] directions)
    {
        int numIndices = m_meshes.Count;
        gridIndices = new Tango3DReconstruction.GridIndex[numIndices];
        completed = new bool[numIndices];
        completionScale = new float[numIndices];
        directions = new byte[numIndices];

        // Assign mesh properties to each index of the arrays.
        m_meshes.Keys.CopyTo(gridIndices, 0);
        for (int i = 0; i < numIndices; i++)
        {
            TangoSingleDynamicMesh mesh = m_meshes[gridIndices[i]];
            completed[i] = mesh.m_completed;
            completionScale[i] = 1.0f * mesh.m_observations / NUM_OBSERVATIONS_TO_COMPLETE;
            directions[i] = mesh.m_directions;
        }
    }
        /// <summary>
        /// Awake this instance.
        /// </summary>
        private void Awake()
        {
            AndroidHelper.RegisterPauseEvent(_androidOnPause);
            AndroidHelper.RegisterResumeEvent(_androidOnResume);
            AndroidHelper.RegisterOnActivityResultEvent(_androidOnActivityResult);
            AndroidHelper.RegisterOnScreenOrientationChangedEvent(_androidOnScreenOrientationChanged);

            // Setup listeners.
            m_tangoEventListener = new TangoEventListener();
            m_areaDescriptionEventListener = new AreaDescriptionEventListener();

            if (m_enableCloudADF)
            {
                m_tangoCloudEventListener = new TangoCloudEventListener();
            }

            if (m_enableMotionTracking)
            {
                m_poseListener = new PoseListener();
            }

            if (m_enableDepth)
            {
                m_depthListener = new DepthListener();
            }

            if (m_enableVideoOverlay)
            {
                int yTextureWidth = 0;
                int yTextureHeight = 0;
                int uvTextureWidth = 0;
                int uvTextureHeight = 0;

                m_yuvTexture = new YUVTexture(yTextureWidth, yTextureHeight, uvTextureWidth, uvTextureHeight, TextureFormat.RGBA32, false);
                m_videoOverlayListener = new VideoOverlayListener();
            }

            if (m_enable3DReconstruction)
            {
                m_tango3DReconstruction = new Tango3DReconstruction(m_3drResolutionMeters, m_3drGenerateColor, m_3drSpaceClearing);
                m_tango3DReconstruction.m_useAreaDescriptionPose = m_3drUseAreaDescriptionPose;
                m_tango3DReconstruction.m_sendColorToUpdate = m_3drGenerateColor;
            }

            // Setup configs.
            m_tangoConfig = new TangoConfig(TangoEnums.TangoConfigType.TANGO_CONFIG_DEFAULT);
            m_tangoRuntimeConfig = new TangoConfig(TangoEnums.TangoConfigType.TANGO_CONFIG_RUNTIME);

            TangoSupport.UpdateCurrentRotationIndex();
        }
        /// <summary>
        /// Extract a single grid cell's mesh.
        /// </summary>
        /// <returns>Status of the extraction.</returns>
        /// <param name="gridIndex">Grid index to extract.</param>
        /// <param name="vertices">Filled out with extracted vertices.</param>
        /// <param name="normals">Filled out with extracted normals.</param>
        /// <param name="colors">Filled out with extracted colors.</param>
        /// <param name="triangles">Filled out with extracted triangle indices.</param>
        /// <param name="numVertices">Filled out with the number of valid vertices.</param>
        /// <param name="numTriangles">Filled out with the number of valid triangles.</param>
        public Tango3DReconstruction.Status Tango3DRExtractMeshSegment(
            Tango3DReconstruction.GridIndex gridIndex, Vector3[] vertices, Vector3[] normals, Color32[] colors, 
            int[] triangles, out int numVertices, out int numTriangles)
        {
            if (m_tango3DReconstruction != null)
            {
                return m_tango3DReconstruction.ExtractMeshSegment(gridIndex, vertices, normals, colors, triangles,
                                                                  out numVertices, out numTriangles);
            }

            numVertices = 0;
            numTriangles = 0;
            return Tango3DReconstruction.Status.INVALID;
        }
        /// <summary>
        /// Extract an array of <c>SignedDistanceVoxel</c> objects.
        /// </summary>
        /// <returns>
        /// Returns Status.SUCCESS if the voxels are fully extracted and stared in the array.  In this case, <c>numVoxels</c>
        /// will say how many voxels are used, the rest of the array is untouched.
        /// 
        /// Returns Status.INVALID if the array length does not exactly equal the number of voxels in a single grid
        /// index.  By default, the number of voxels in a grid index is 16*16*16.
        /// 
        /// Returns Status.INVALID if some other error occurs.
        /// </returns>
        /// <param name="gridIndex">Grid index to extract.</param>
        /// <param name="voxels">
        /// On successful extraction this will get filled out with the signed distance voxels.
        /// </param>
        /// <param name="numVoxels">Number of voxels filled out.</param>
        public Tango3DReconstruction.Status Tango3DRExtractSignedDistanceVoxel(
            Tango3DReconstruction.GridIndex gridIndex, Tango3DReconstruction.SignedDistanceVoxel[] voxels,
            out int numVoxels)
        {
            if (m_tango3DReconstruction != null)
            {
                return m_tango3DReconstruction.ExtractSignedDistanceVoxel(gridIndex, voxels, out numVoxels);
            }

            numVoxels = 0;
            return Tango3DReconstruction.Status.INVALID;
        }
        /// <summary>
        /// Awake this instance.
        /// </summary>
        private void Awake()
        {
            if (!AndroidHelper.LoadTangoLibrary())
            {
                Debug.Log("Unable to load Tango library.  Things may not work.");
                return;
            }

            AndroidHelper.RegisterPauseEvent(_androidOnPause);
            AndroidHelper.RegisterResumeEvent(_androidOnResume);
            AndroidHelper.RegisterOnActivityResultEvent(_androidOnActivityResult);
            AndroidHelper.RegisterOnDisplayChangedEvent(_androidOnDisplayChanged);
            AndroidHelper.RegisterOnTangoServiceConnected(_androidOnTangoServiceConnected);
            AndroidHelper.RegisterOnTangoServiceDisconnected(_androidOnTangoServiceDisconnected);

            if (m_enableDepth)
            {
                DepthListener.SetPointCloudLimit(m_initialPointCloudMaxPoints);
            }

            if (m_enableVideoOverlay)
            {
                int yTextureWidth = 0;
                int yTextureHeight = 0;
                int uvTextureWidth = 0;
                int uvTextureHeight = 0;

                m_yuvTexture = new YUVTexture(yTextureWidth, yTextureHeight, uvTextureWidth, uvTextureHeight, TextureFormat.RGBA32, false);
            }

            if (m_enable3DReconstruction)
            {
                m_tango3DReconstruction = new Tango3DReconstruction(
                    resolution: m_3drResolutionMeters,
                    generateColor: m_3drGenerateColor,
                    spaceClearing: m_3drSpaceClearing,
                    minNumVertices: m_3drMinNumVertices,
                    updateMethod: m_3drUpdateMethod);
                m_tango3DReconstruction.m_useAreaDescriptionPose = m_3drUseAreaDescriptionPose;
                m_tango3DReconstruction.m_sendColorToUpdate = m_3drGenerateColor;
            }

            TangoSupport.UpdatePoseMatrixFromDeviceRotation(AndroidHelper.GetDisplayRotation(),
                                                            AndroidHelper.GetColorCameraRotation());

            if (m_adjustScreenResolution)
            {
                _ChangeResolutionForPerformance();
            }

            // Importing and exporting Area Descriptions can be done before you connect. We must
            // propogate those events if they happen.
            AreaDescriptionEventListener.SetCallback();

#if UNITY_EDITOR
            if (m_doSlowEmulation && (m_enableDepth || m_enableVideoOverlay))
            {
                if (m_emulationEnvironment == null)
                {
                    Debug.LogError("No Mesh for Emulation assigned on the Tango Application (commonly in the Tango Manager prefab)."
                                   + " Expect blank camera and/or depth frames.");
                }

                EmulatedEnvironmentRenderHelper.InitForEnvironment(m_emulationEnvironment, m_emulationEnvironmentTexture, m_emulationVideoOverlaySimpleLighting);
            }
            else
            {
                EmulatedEnvironmentRenderHelper.Clear();
            }
#endif
        }