/// <summary>
        /// Applies the quad region mask on the passed in game object.
        /// </summary>
        /// <param name="quad">Scene Understanding quad.</param>
        /// <param name="go">Game object associated with the quad.</param>
        /// <param name="color">Color to use for the valid regions.</param>
        public void ApplyQuadRegionMask(SceneUnderstanding.SceneQuad quad, GameObject go, Color?color)
        {
            if (quad == null || go == null)
            {
                Logger.LogWarning("SceneUnderstandingUtils.ApplyQuadRegionMask: One or more arguments are null.");
                return;
            }

            // If no color has been provided, paint it red.
            color = color == null ? Color.red : color.Value;

            // Resolution of the mask.
            ushort width  = 256;
            ushort height = 256;

            byte[] mask = new byte[width * height];
            quad.GetSurfaceMask(width, height, mask);

            MeshRenderer meshRenderer = go.GetComponent <MeshRenderer>();

            if (meshRenderer == null || meshRenderer.material == null || meshRenderer.material.HasProperty("_MainTex") == false)
            {
                Logger.LogWarning("SceneUnderstandingUtils.ApplyQuadRegionMask: Mesh renderer component is null or does not have a valid material.");
                return;
            }

            // Create a new texture.
            Texture2D texture = new Texture2D(width, height);

            texture.filterMode = FilterMode.Bilinear;
            texture.wrapMode   = TextureWrapMode.Clamp;

            // Transfer the invalidation mask onto the texture.
            Color[] pixels = texture.GetPixels();
            for (int i = 0; i < pixels.Length; ++i)
            {
                var value = mask[i];

                if (value == (byte)SceneUnderstanding.SceneRegionSurfaceKind.NotSurface)
                {
                    pixels[i] = Color.clear;
                }
                else
                {
                    pixels[i] = color.Value;
                }
            }

            texture.SetPixels(pixels);
            texture.Apply(true);

            // Set the texture on the material.
            meshRenderer.material.mainTexture = texture;
        }
        /// <summary>
        /// Generates and returns a Unity mesh for a Scene Understanding quad.
        /// </summary>
        /// <param name="quad">Scene Understanding quad.</param>
        /// <returns>Unity mesh.</returns>
        public Mesh GenerateUnityMeshForSceneObjectQuad(SceneUnderstanding.SceneQuad quad)
        {
            if (quad == null)
            {
                Logger.LogWarning("SceneUnderstandingUtils.GenerateUnityMeshForSceneObjectQuad: Quad is null.");
                return(null);
            }

            float widthInMeters  = quad.Extents.X;
            float heightInMeters = quad.Extents.Y;

            // Bounds of the quad.
            List <Vector3> vertices = new List <Vector3>()
            {
                new Vector3(-widthInMeters / 2, -heightInMeters / 2, 0),
                new Vector3(widthInMeters / 2, -heightInMeters / 2, 0),
                new Vector3(-widthInMeters / 2, heightInMeters / 2, 0),
                new Vector3(widthInMeters / 2, heightInMeters / 2, 0)
            };

            List <int> triangles = new List <int>()
            {
                1, 3, 0,
                3, 2, 0
            };

            List <Vector2> uvs = new List <Vector2>()
            {
                new Vector2(0, 0),
                new Vector2(1, 0),
                new Vector2(0, 1),
                new Vector2(1, 1)
            };

            Mesh unityMesh = new Mesh();

            unityMesh.SetVertices(vertices);
            unityMesh.SetIndices(triangles.ToArray(), MeshTopology.Triangles, 0);
            unityMesh.SetUVs(0, uvs);

            return(unityMesh);
        }
        /// <summary>
        /// Orients the root game object, such that the Scene Understanding floor lies on the Unity world's X-Z plane.
        /// </summary>
        /// <param name="sceneRoot">Root game object.</param>
        /// <param name="scene">Scene Understanding scene.</param>
        public void OrientSceneRootForPC(GameObject sceneRoot, SceneUnderstanding.Scene scene)
        {
            if (scene == null)
            {
                Logger.LogWarning("SceneUnderstandingUtils.OrientSceneRootForPC: Scene is null.");
                return;
            }

            IEnumerable <SceneUnderstanding.SceneObject> sceneObjects = scene.SceneObjects;

            float areaForlargestFloorSoFar = 0;

            SceneUnderstanding.SceneObject floorSceneObject = null;
            SceneUnderstanding.SceneQuad   floorQuad        = null;

            // Find the largest floor quad.
            foreach (SceneUnderstanding.SceneObject so in sceneObjects)
            {
                if (so.Kind == SceneUnderstanding.SceneObjectKind.Floor)
                {
                    IEnumerable <SceneUnderstanding.SceneQuad> quads = so.Quads;

                    if (quads != null)
                    {
                        foreach (SceneUnderstanding.SceneQuad quad in quads)
                        {
                            float quadArea = quad.Extents.X * quad.Extents.Y;

                            if (quadArea > areaForlargestFloorSoFar)
                            {
                                areaForlargestFloorSoFar = quadArea;
                                floorSceneObject         = so;
                                floorQuad = quad;
                            }
                        }
                    }
                }
            }

            if (floorQuad != null)
            {
                // Compute the floor quad's normal.
                float widthInMeters  = floorQuad.Extents.X;
                float heightInMeters = floorQuad.Extents.Y;

                System.Numerics.Vector3 point1 = new System.Numerics.Vector3(-widthInMeters / 2, -heightInMeters / 2, 0);
                System.Numerics.Vector3 point2 = new System.Numerics.Vector3(widthInMeters / 2, -heightInMeters / 2, 0);
                System.Numerics.Vector3 point3 = new System.Numerics.Vector3(-widthInMeters / 2, heightInMeters / 2, 0);

                System.Numerics.Matrix4x4 floorTransform = floorSceneObject.GetLocationAsMatrix();
                floorTransform = TransformUtils.ConvertRightHandedMatrix4x4ToLeftHanded(floorTransform);

                System.Numerics.Vector3 tPoint1 = System.Numerics.Vector3.Transform(point1, floorTransform);
                System.Numerics.Vector3 tPoint2 = System.Numerics.Vector3.Transform(point2, floorTransform);
                System.Numerics.Vector3 tPoint3 = System.Numerics.Vector3.Transform(point3, floorTransform);

                System.Numerics.Vector3 p21 = tPoint2 - tPoint1;
                System.Numerics.Vector3 p31 = tPoint3 - tPoint1;

                System.Numerics.Vector3 floorNormal = System.Numerics.Vector3.Cross(p31, p21);

                // Numerics to Unity conversion.
                Vector3 floorNormalUnity = new Vector3(floorNormal.X, floorNormal.Y, floorNormal.Z);

                // Get the rotation between the floor normal and Unity world's up vector.
                Quaternion rotation = Quaternion.FromToRotation(floorNormalUnity, Vector3.up);

                // Apply the rotation to the root, so that the floor is on the Camera's x-z plane.
                sceneRoot.transform.rotation = rotation;
            }
        }