static void GenerateVoxelGrids(GameObject prefabRoot, PlaneVoxelGenerationParams voxelGenerationParams, VoxelPlaneFindingParams planeFindingParams)
        {
            // Rather than modifying collider properties and risking corrupting the scene, for each object with a mesh
            // we create a game object with a collider that uses that mesh and add that object to a preview scene.
            var tempPreviewScene = EditorSceneManager.NewPreviewScene();
            var tempPhysicsScene = tempPreviewScene.GetPhysicsScene();

            // k_MeshFilters is cleared by GetComponentsInChildren
            prefabRoot.GetComponentsInChildren(k_MeshFilters);
            var meshesRoot = new GameObject("Meshes").transform;

            k_MeshColliders.Clear();
            foreach (var meshFilter in k_MeshFilters)
            {
                // Ignore synth planes
                if (meshFilter.GetComponent <SynthesizedPlane>())
                {
                    continue;
                }

                var meshCopyTrans    = new GameObject(string.Format("{0} (Mesh)", meshFilter.gameObject.name)).transform;
                var meshCopyCollider = meshCopyTrans.gameObject.AddComponent <MeshCollider>();
                k_MeshColliders.Add(meshCopyCollider);
                meshCopyCollider.sharedMesh = meshFilter.sharedMesh;
                meshCopyTrans.SetParent(meshesRoot);
                var meshFilterTrans = meshFilter.transform;
                meshCopyTrans.position   = meshFilterTrans.position;
                meshCopyTrans.rotation   = meshFilterTrans.rotation;
                meshCopyTrans.localScale = meshFilterTrans.lossyScale;
            }

            SceneManager.MoveGameObjectToScene(meshesRoot.gameObject, tempPreviewScene);

            var sceneBounds    = BoundsUtils.GetBounds(k_MeshColliders);
            var sceneBoundsMin = sceneBounds.min;
            var sceneBoundsMax = sceneBounds.max;

            // Make mesh colliders convex so that we can use CheckSphere to make sure our
            // raycasts don't originate within meshes.
            foreach (var meshCollider in k_MeshColliders)
            {
                meshCollider.convex = true;
            }

            // Seed the random number generator so that results are deterministic
            Random.InitState(voxelGenerationParams.raycastSeed);

            // Setup all ray origins before raycasting since we need mesh colliders to be convex for this step
            // but concave for the raycasting.
            var raycastCount = voxelGenerationParams.raycastCount;

            k_RayOrigins.Clear();
            k_RayOrigins.Capacity = raycastCount;
            for (var i = 0; i < raycastCount; i++)
            {
                var     rayGenerationAttempts = 0;
                Vector3 origin;
                while (true)
                {
                    origin = new Vector3(
                        Random.Range(sceneBoundsMin.x, sceneBoundsMax.x),
                        Random.Range(sceneBoundsMin.y, sceneBoundsMax.y),
                        Random.Range(sceneBoundsMin.z, sceneBoundsMax.z));

                    // If the randomly generated ray origin lies inside a mesh, try again
                    if (tempPhysicsScene.OverlapSphere(origin, k_CheckSphereRadius, k_Colliders,
                                                       Physics.DefaultRaycastLayers, QueryTriggerInteraction.Collide) == 0)
                    {
                        break;
                    }

                    rayGenerationAttempts++;
                    if (rayGenerationAttempts >= k_MaxRayGenerationRetries)
                    {
                        break;
                    }
                }

                k_RayOrigins.Add(origin);
            }

            foreach (var meshCollider in k_MeshColliders)
            {
                // Make mesh colliders concave for more useful raycast results
                meshCollider.convex = false;
            }

            k_UpGridPoints.Clear();
            k_DownGridPoints.Clear();
            k_ForwardGridPoints.Clear();
            k_BackGridPoints.Clear();
            k_RightGridPoints.Clear();
            k_LeftGridPoints.Clear();

            var outerPointsThreshold = voxelGenerationParams.outerPointsThreshold;
            var upGridBounds         = sceneBounds;
            var upGridMax            = upGridBounds.max;

            upGridMax.y     -= Mathf.Min(outerPointsThreshold, upGridBounds.size.y);
            upGridBounds.max = upGridMax;

            var downGridBounds = sceneBounds;
            var downGridMin    = downGridBounds.min;

            downGridMin.y     += Mathf.Min(outerPointsThreshold, downGridBounds.size.y);
            downGridBounds.min = downGridMin;

            var forwardGridBounds = sceneBounds;
            var forwardGridMax    = forwardGridBounds.max;

            forwardGridMax.z     -= Mathf.Min(outerPointsThreshold, forwardGridBounds.size.z);
            forwardGridBounds.max = forwardGridMax;

            var backGridBounds = sceneBounds;
            var backGridMin    = backGridBounds.min;

            backGridMin.z     += Mathf.Min(outerPointsThreshold, backGridBounds.size.z);
            backGridBounds.min = backGridMin;

            var rightGridBounds = sceneBounds;
            var rightGridMax    = rightGridBounds.max;

            rightGridMax.x     -= Mathf.Min(outerPointsThreshold, rightGridBounds.size.x);
            rightGridBounds.max = rightGridMax;

            var leftGridBounds = sceneBounds;
            var leftGridMin    = leftGridBounds.min;

            leftGridMin.x     += Mathf.Min(outerPointsThreshold, leftGridBounds.size.x);
            leftGridBounds.min = leftGridMin;

            // Start raycasting. We run raycasts synchronously so the user can't switch scenes or make scene changes while they are running.
            var maxHitDistance       = voxelGenerationParams.maxHitDistance;
            var normalToleranceAngle = voxelGenerationParams.normalToleranceAngle;
            var rayIndex             = 0;
            var up    = Vector3.up;
            var down  = Vector3.down;
            var back  = Vector3.back;
            var right = Vector3.right;
            var left  = Vector3.left;

            while (rayIndex < raycastCount)
            {
                if (EditorUtility.DisplayCancelableProgressBar(
                        "Generating Point Cloud",
                        string.Format("{0}/{1} raycasts completed", rayIndex, raycastCount),
                        (float)rayIndex / raycastCount))
                {
                    EditorUtility.ClearProgressBar();
                    GUIUtility.ExitGUI();
                    break;
                }

                for (var i = 0; i < k_RaycastProgressBatchSize && rayIndex < raycastCount; i++, rayIndex++)
                {
                    var        origin    = k_RayOrigins[rayIndex];
                    var        direction = RandomRayDirection();
                    RaycastHit raycastHit;
                    if (!tempPhysicsScene.Raycast(origin, direction, out raycastHit, maxHitDistance))
                    {
                        continue;
                    }

                    var point  = raycastHit.point;
                    var normal = raycastHit.normal;

                    if (Vector3.Angle(normal, up) <= normalToleranceAngle && upGridBounds.Contains(point))
                    {
                        k_UpGridPoints.Add(point);
                    }

                    if (Vector3.Angle(normal, down) <= normalToleranceAngle && downGridBounds.Contains(point))
                    {
                        k_DownGridPoints.Add(point);
                    }

                    if (Vector3.Angle(normal, k_Forward) <= normalToleranceAngle && forwardGridBounds.Contains(point))
                    {
                        k_ForwardGridPoints.Add(point);
                    }

                    if (Vector3.Angle(normal, back) <= normalToleranceAngle && backGridBounds.Contains(point))
                    {
                        k_BackGridPoints.Add(point);
                    }

                    if (Vector3.Angle(normal, right) <= normalToleranceAngle && rightGridBounds.Contains(point))
                    {
                        k_RightGridPoints.Add(point);
                    }

                    if (Vector3.Angle(normal, left) <= normalToleranceAngle && leftGridBounds.Contains(point))
                    {
                        k_LeftGridPoints.Add(point);
                    }
                }
            }

            EditorUtility.ClearProgressBar();
            EditorSceneManager.ClosePreviewScene(tempPreviewScene);

            // Reset the seed so other uses of Random are not deterministic
            Random.InitState((int)DateTime.Now.Ticks);

            // Generate voxel grids from point cloud
            var voxelSize = voxelGenerationParams.voxelSize;

            s_UpVoxelGrid      = new PlaneExtractionVoxelGrid(VoxelGridOrientation.Up, upGridBounds, voxelSize, planeFindingParams);
            s_DownVoxelGrid    = new PlaneExtractionVoxelGrid(VoxelGridOrientation.Down, downGridBounds, voxelSize, planeFindingParams);
            s_ForwardVoxelGrid = new PlaneExtractionVoxelGrid(VoxelGridOrientation.Forward, forwardGridBounds, voxelSize, planeFindingParams);
            s_BackVoxelGrid    = new PlaneExtractionVoxelGrid(VoxelGridOrientation.Back, backGridBounds, voxelSize, planeFindingParams);
            s_RightVoxelGrid   = new PlaneExtractionVoxelGrid(VoxelGridOrientation.Right, rightGridBounds, voxelSize, planeFindingParams);
            s_LeftVoxelGrid    = new PlaneExtractionVoxelGrid(VoxelGridOrientation.Left, leftGridBounds, voxelSize, planeFindingParams);
            s_UpVoxelGrid.AddPoints(k_UpGridPoints);
            s_DownVoxelGrid.AddPoints(k_DownGridPoints);
            s_ForwardVoxelGrid.AddPoints(k_ForwardGridPoints);
            s_BackVoxelGrid.AddPoints(k_BackGridPoints);
            s_RightVoxelGrid.AddPoints(k_RightGridPoints);
            s_LeftVoxelGrid.AddPoints(k_LeftGridPoints);
        }
    private void Update()
    {
        BeforeForwardOpaque.Clear();

        Renderer[] renderers = FindObjectsOfType <Renderer>();

        BeforeForwardOpaque.BeginSample("DepthOnly");
        BeforeForwardOpaque.SetViewMatrix(camera.worldToCameraMatrix);
        BeforeForwardOpaque.SetProjectionMatrix(camera.projectionMatrix);
        BeforeForwardOpaque.SetRenderTarget(_DepthTex);
        BeforeForwardOpaque.ClearRenderTarget(true, true, Color.black);
        for (int i = 0, imax = renderers.Length; i < imax; i++)
        {
            Renderer rend = renderers[i];
            if (rend.shadowCastingMode != ShadowCastingMode.Off)
            {
                //casterAABBs.Add(rend.bounds);
                if (BoundsUtils.IntersectFrustum(rend.bounds, rend.localToWorldMatrix, Camera.main.cullingMatrix))
                {
                    for (int m = 0, mmax = rend.sharedMaterials.Length; m < mmax; m++)
                    {
                        var mat  = rend.sharedMaterial; // "sharedMaterials" cases bugs;
                        int pass = mat.FindPass("DepthOnly");
                        if (pass < 0)
                        {
                            BeforeForwardOpaque.DrawRenderer(rend, DepthMaterial, m, 0);
                        }
                        else
                        {
                            BeforeForwardOpaque.DrawRenderer(rend, mat, m, pass);
                        }
                        mat.SetShaderPassEnabled("DepthOnly", false);
                    }
                }
            }
        }
        BeforeForwardOpaque.EndSample("DepthOnly");


        BeforeForwardOpaque.SetRenderTarget(BuiltinRenderTextureType.None);
        BeforeForwardOpaque.ClearRenderTarget(true, true, Color.white);

        Matrix4x4 lightMatrix = DirectionalLight.transform.worldToLocalMatrix;

        //if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLCore
        //    || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3)
        {
            Vector4 forward = lightMatrix.GetRow(2);
            lightMatrix.SetRow(2, -forward);
        }
        BeforeForwardOpaque.SetViewMatrix(lightMatrix);
        Matrix4x4 projMatrix = Matrix4x4.Ortho(-0.5f, 0.5f, -0.5f, 0.5f, 0.1f, 10);

        BeforeForwardOpaque.SetProjectionMatrix(projMatrix);
        BeforeForwardOpaque.SetViewport(new Rect(0, 0, dimension, dimension));

        /*if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal)
         * {
         *  Matrix4x4 mAdj = Matrix4x4.identity;
         *  mAdj.m22 = -0.5f;
         *  mAdj.m23 = 0.5f;
         *  projMatrix = mAdj * projMatrix;
         * }*/
        BeforeForwardOpaque.SetGlobalMatrix("_LightVP", projMatrix * lightMatrix);
        BeforeForwardOpaque.SetGlobalFloat("_HairAlpha", HairAlpha);

        BeforeForwardOpaque.BeginSample("ShadowMapMaterial");
        for (int i = 0, imax = renderers.Length; i < imax; i++)
        {
            Renderer rend = renderers[i];
            if (rend.shadowCastingMode != ShadowCastingMode.Off)
            {
                //casterAABBs.Add(rend.bounds);
                if (BoundsUtils.IntersectFrustum(rend.bounds, rend.localToWorldMatrix, Camera.main.cullingMatrix))
                {
                    for (int m = 0, mmax = rend.sharedMaterials.Length; m < mmax; m++)
                    {
                        var mat  = rend.sharedMaterial; // "sharedMaterials" cases bugs;
                        int pass = mat.FindPass("DeepShadowCaster");
                        if (pass < 0)
                        {
                            BeforeForwardOpaque.DrawRenderer(rend, ShadowMapMaterial, m, 0);
                        }
                        else
                        {
                            BeforeForwardOpaque.DrawRenderer(rend, mat, m, pass);
                        }
                        mat.SetShaderPassEnabled("DeepShadowCaster", false);
                    }
                }
            }
        }
        BeforeForwardOpaque.ClearRenderTarget(true, true, Color.black);
        BeforeForwardOpaque.EndSample("ShadowMapMaterial");

        BeforeForwardOpaque.SetRenderTarget(_ShadowTex);
        BeforeForwardOpaque.ClearRenderTarget(true, true, Color.white);
        BeforeForwardOpaque.SetComputeIntParam(ResolveCompute, "_ScreenWidth", Screen.width);
        BeforeForwardOpaque.SetComputeIntParam(ResolveCompute, "_ScreenHeight", Screen.height);
        BeforeForwardOpaque.SetComputeMatrixParam(ResolveCompute, "_CameraInvVP", camera.cullingMatrix.inverse);
        BeforeForwardOpaque.SetComputeMatrixParam(ResolveCompute, "_LightVP", projMatrix * lightMatrix);

        BeforeForwardOpaque.SetComputeIntParam(ResolveCompute, "_AsDefaultShadowmap", _AsDefaultShadowmap ? 1 : 0);

        BeforeForwardOpaque.DispatchCompute(ResolveCompute, KernelScreenSpaceDeepShadowmap, (7 + Screen.width) / 8, (7 + Screen.height) / 8, 1);


        BeforeForwardOpaque.SetRenderTarget(_BlurTex);
        BeforeForwardOpaque.ClearRenderTarget(true, true, Color.white);
        BeforeForwardOpaque.SetComputeIntParam(ResolveCompute, "_BlurStep", 1);
        BeforeForwardOpaque.SetComputeTextureParam(ResolveCompute, KernelGaussianBlurShadow, "_SourceShadowTexture", _ShadowTex);
        BeforeForwardOpaque.SetComputeTextureParam(ResolveCompute, KernelGaussianBlurShadow, "_BlurShadowTexture", _BlurTex);
        BeforeForwardOpaque.DispatchCompute(ResolveCompute, KernelGaussianBlurShadow, (7 + Screen.width) / 8, (7 + Screen.height) / 8, 1);

        //BeforeForwardOpaque.SetRenderTarget(_ShadowTex);
        //BeforeForwardOpaque.ClearRenderTarget(true, true, Color.white);
        //BeforeForwardOpaque.SetComputeIntParam(ResolveCompute, "_BlurStep", 2);
        //BeforeForwardOpaque.SetComputeTextureParam(ResolveCompute, KernelGaussianBlurShadow, "_SourceShadowTexture", _BlurTex);
        //BeforeForwardOpaque.SetComputeTextureParam(ResolveCompute, KernelGaussianBlurShadow, "_BlurShadowTexture", _ShadowTex);
        //BeforeForwardOpaque.DispatchCompute(ResolveCompute, KernelGaussianBlurShadow, (7 + Screen.width) / 8, (7 + Screen.height) / 8, 1);

        //BeforeForwardOpaque.SetRenderTarget(_BlurTex);
        //BeforeForwardOpaque.ClearRenderTarget(true, true, Color.white);
        //BeforeForwardOpaque.SetComputeIntParam(ResolveCompute, "_BlurStep", 4);
        //BeforeForwardOpaque.SetComputeTextureParam(ResolveCompute, KernelGaussianBlurShadow, "_SourceShadowTexture", _ShadowTex);
        //BeforeForwardOpaque.SetComputeTextureParam(ResolveCompute, KernelGaussianBlurShadow, "_BlurShadowTexture", _BlurTex);
        //BeforeForwardOpaque.DispatchCompute(ResolveCompute, KernelGaussianBlurShadow, (7 + Screen.width) / 8, (7 + Screen.height) / 8, 1);

        BeforeForwardOpaque.SetGlobalTexture("_BlurShadowTexture", _BlurTex);

        BeforeForwardOpaque.SetRenderTarget(BuiltinRenderTextureType.CameraTarget);

        BeforeForwardOpaque.SetViewMatrix(camera.worldToCameraMatrix);
        BeforeForwardOpaque.SetProjectionMatrix(camera.projectionMatrix);
        BeforeForwardOpaque.SetGlobalVector("CameraPos", camera.transform.position);
        BeforeForwardOpaque.SetGlobalVector("LightDir", DirectionalLight.transform.forward);

        BeforeForwardOpaque.SetGlobalColor("_HairColor", HairColor);


#if UNITY_EDITOR && DEBUG_DSM
        BeforeForwardOpaque.DispatchCompute(TestCompute, KernelResetTestResult, dimension / 8, dimension / 8, 1);
        BeforeForwardOpaque.SetComputeIntParam(TestCompute, "TestIndex", TestIndex);
        if (TestKernel == ETestKernel.KernelTestNumberBuffer)
        {
            BeforeForwardOpaque.DispatchCompute(TestCompute, KernelTestNumberBuffer, dimension / 8, dimension / 8, 1);
        }
        else if (TestKernel == ETestKernel.KernelTestDepthBuffer)
        {
            BeforeForwardOpaque.DispatchCompute(TestCompute, KernelTestDepthBuffer, dimension / 8, dimension / 8, 1);
        }
#endif
        AfterForwardOpaque.Clear();
        AfterForwardOpaque.DispatchCompute(ResetCompute, KernelResetDepthBuffer, dimension / 8, dimension / 8, 1);
        AfterForwardOpaque.DispatchCompute(ResetCompute, KernelResetNumberBuffer, dimension / 8, dimension / 8, 1);
    }
    private void Update()
    {
        BeforeForwardOpaque.Clear();

        BeforeForwardOpaque.SetRenderTarget(BuiltinRenderTextureType.None);
        BeforeForwardOpaque.ClearRenderTarget(true, true, Color.white);

        Matrix4x4 lightMatrix = DirectionalLight.transform.worldToLocalMatrix;

        //if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLCore
        //    || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3)
        {
            Vector4 forward = lightMatrix.GetRow(2);
            lightMatrix.SetRow(2, -forward);
        }
        BeforeForwardOpaque.SetViewMatrix(lightMatrix);
        Matrix4x4 projMatrix = Matrix4x4.Ortho(-1, 1, -1, 1, 0.1f, 10);

        BeforeForwardOpaque.SetProjectionMatrix(projMatrix);
        BeforeForwardOpaque.SetViewport(new Rect(0, 0, dimension, dimension));

        /*if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal)
         * {
         *  Matrix4x4 mAdj = Matrix4x4.identity;
         *  mAdj.m22 = -0.5f;
         *  mAdj.m23 = 0.5f;
         *  projMatrix = mAdj * projMatrix;
         * }*/
        BeforeForwardOpaque.SetGlobalMatrix("_LightVP", projMatrix * lightMatrix);
        BeforeForwardOpaque.SetGlobalFloat("_HairAlpha", HairAlpha);

        BeforeForwardOpaque.BeginSample("ShadowMapMaterial");
        Renderer[] renderers = FindObjectsOfType <Renderer>();
        for (int i = 0, imax = renderers.Length; i < imax; i++)
        {
            Renderer rend = renderers[i];
            if (rend.shadowCastingMode != ShadowCastingMode.Off)
            {
                //casterAABBs.Add(rend.bounds);
                if (BoundsUtils.IntersectFrustum(rend.bounds, rend.localToWorldMatrix, Camera.main.cullingMatrix))
                {
                    for (int m = 0, mmax = rend.sharedMaterials.Length; m < mmax; m++)
                    {
                        BeforeForwardOpaque.DrawRenderer(rend, ShadowMapMaterial, m, 0);
                    }
                }
            }
        }
        BeforeForwardOpaque.ClearRenderTarget(true, true, Color.black);
        BeforeForwardOpaque.EndSample("ShadowMapMaterial");


        BeforeForwardOpaque.DispatchCompute(SortCompute, KernelSortDepth, dimension / 8, dimension / 8, 1);

        BeforeForwardOpaque.SetRenderTarget(BuiltinRenderTextureType.CameraTarget);

        BeforeForwardOpaque.SetViewMatrix(camera.worldToCameraMatrix);
        BeforeForwardOpaque.SetProjectionMatrix(camera.projectionMatrix);
        BeforeForwardOpaque.SetGlobalVector("CameraPos", camera.transform.position);
        BeforeForwardOpaque.SetGlobalVector("LightDir", DirectionalLight.transform.forward);

        BeforeForwardOpaque.SetGlobalColor("_HairColor", HairColor);


#if UNITY_EDITOR
        BeforeForwardOpaque.DispatchCompute(TestCompute, KernelResetTestResult, dimension / 8, dimension / 8, 1);
        BeforeForwardOpaque.SetComputeIntParam(TestCompute, "TestIndex", TestIndex);
        if (TestKernel == ETestKernel.KernelTestNumberBuffer)
        {
            BeforeForwardOpaque.DispatchCompute(TestCompute, KernelTestNumberBuffer, dimension / 8, dimension / 8, 1);
        }
        else if (TestKernel == ETestKernel.KernelTestDepthBuffer)
        {
            BeforeForwardOpaque.DispatchCompute(TestCompute, KernelTestDepthBuffer, dimension / 8, dimension / 8, 1);
        }
        else if (TestKernel == ETestKernel.KernelTestRegressionBuffer)
        {
            BeforeForwardOpaque.DispatchCompute(TestCompute, KernelTestRegressionBuffer, dimension / 8, dimension / 8, 1);
        }
#endif
        AfterForwardOpaque.Clear();
        AfterForwardOpaque.DispatchCompute(ResetCompute, KernelResetDepthBuffer, dimension / 8, dimension / 8, 1);
        AfterForwardOpaque.DispatchCompute(ResetCompute, KernelResetRegressionBuffer, dimension / 8, dimension / 8, 1);
        AfterForwardOpaque.DispatchCompute(ResetCompute, KernelResetNumberBuffer, dimension / 8, dimension / 8, 1);
    }
        IEnumerator PlaceSceneObjectCoroutine(Transform obj, Vector3 targetScale)
        {
            var go = obj.gameObject;

            // Don't let us direct select while placing
            this.RemoveFromSpatialHash(go);

            var start    = Time.realtimeSinceStartup;
            var currTime = 0f;

            obj.parent = null;
            var localScale     = obj.localScale;
            var startScale     = localScale;
            var startPosition  = BoundsUtils.GetBounds(obj).center;
            var position       = obj.position;
            var pivotOffset    = position - startPosition;
            var startRotation  = obj.rotation;
            var targetRotation = startRotation.ConstrainYaw();

            //Get bounds at target scale and rotation (scaled and rotated from bounds center)
            var origScale = localScale;

            obj.rotation = targetRotation;
            var rotationDiff      = Quaternion.Inverse(startRotation) * targetRotation;
            var scaleDiff         = targetScale.magnitude / startScale.magnitude;
            var targetPivotOffset = rotationDiff * pivotOffset * scaleDiff;
            var bounds            = BoundsUtils.GetBounds(obj);

            localScale        = origScale;
            obj.localScale    = localScale;
            obj.localRotation = startRotation;
            obj.position      = startPosition + pivotOffset;

            // We want to position the object so that it fits within the camera perspective at its original scale
            var camera      = CameraUtils.GetMainCamera();
            var halfAngle   = camera.fieldOfView * 0.5f;
            var perspective = halfAngle + k_InstantiateFOVDifference;
            var camPosition = camera.transform.position;
            var forward     = startPosition - camPosition;

            var distance       = bounds.size.magnitude / Mathf.Tan(perspective * Mathf.Deg2Rad);
            var targetPosition = bounds.center;

            if (distance > forward.magnitude && obj.localScale != targetScale)
            {
                targetPosition = camPosition + forward.normalized * distance;
            }

            startPosition  += pivotOffset;
            targetPosition += targetPivotOffset;

            while (currTime < k_GrowDuration)
            {
                currTime = Time.realtimeSinceStartup - start;
                var t        = currTime / k_GrowDuration;
                var tSquared = t * t;
                obj.localScale = Vector3.Lerp(startScale, targetScale, tSquared);
                obj.position   = Vector3.Lerp(startPosition, targetPosition, tSquared);
                obj.rotation   = Quaternion.Lerp(startRotation, targetRotation, tSquared);
                yield return(null);
            }

            obj.localScale             = targetScale;
            Selection.activeGameObject = go;

            this.AddToSpatialHash(go);

#if UNITY_EDITOR
            UnityEditor.Undo.IncrementCurrentGroup();
#endif
        }
        IEnumerator PlaceSceneObjectsCoroutine(Transform[] transforms, Vector3[] targetPositionOffsets, Quaternion[] targetRotations, Vector3[] targetScales)
        {
            var start    = Time.realtimeSinceStartup;
            var currTime = 0f;

            var length         = transforms.Length;
            var startPositions = new Vector3[length];
            var startRotations = new Quaternion[length];
            var startScales    = new Vector3[length];
            var center         = BoundsUtils.GetBounds(transforms).center;
            var pivot          = Vector3.zero;

            //Get bounds at target scale and rotation (scaled and rotated from bounds center)
            for (var i = 0; i < length; i++)
            {
                var transform = transforms[i];
                this.RemoveFromSpatialHash(transform.gameObject);
                var position = transform.position;
                startPositions[i] = position;
                startRotations[i] = transform.rotation;
                startScales[i]    = transform.localScale;

                pivot += position;

                transform.position   = targetPositionOffsets[i];
                transform.rotation   = targetRotations[i];
                transform.localScale = targetScales[i];
            }

            pivot /= length;

            var bounds = BoundsUtils.GetBounds(transforms);

            for (var i = 0; i < length; i++)
            {
                var transform = transforms[i];
                transform.position   = startPositions[i];
                transform.rotation   = startRotations[i];
                transform.localScale = startScales[i];
            }

            // We want to position the object so that it fits within the camera perspective at its original scale
            var camera      = CameraUtils.GetMainCamera();
            var halfAngle   = camera.fieldOfView * 0.5f;
            var perspective = halfAngle + k_InstantiateFOVDifference;
            var camPosition = camera.transform.position;
            var forward     = center - camPosition;

            var distance       = bounds.size.magnitude / Mathf.Tan(perspective * Mathf.Deg2Rad);
            var targetPosition = pivot;

            if (distance > forward.magnitude)
            {
                targetPosition = camPosition + forward.normalized * distance;
            }

            for (var i = 0; i < length; i++)
            {
                targetPositionOffsets[i] += targetPosition;
            }

            while (currTime < k_GrowDuration)
            {
                currTime = Time.realtimeSinceStartup - start;
                var t        = currTime / k_GrowDuration;
                var tSquared = t * t;
                for (int i = 0; i < length; i++)
                {
                    var transform = transforms[i];
                    transform.localScale = Vector3.Lerp(startScales[i], targetScales[i], tSquared);
                    transform.position   = Vector3.Lerp(startPositions[i], targetPositionOffsets[i], tSquared);
                    transform.rotation   = Quaternion.Slerp(startRotations[i], targetRotations[i], tSquared);
                    yield return(null);
                }
            }

            var objects = new GameObject[length];

            for (int i = 0; i < length; i++)
            {
                var transform = transforms[i];
                objects[i]           = transform.gameObject;
                transform.localScale = targetScales[i];
                transform.rotation   = targetRotations[i];
                transform.position   = targetPositionOffsets[i];

                this.AddToSpatialHash(transform.gameObject);
            }

            Selection.objects = objects;

#if UNITY_EDITOR
            UnityEditor.Undo.IncrementCurrentGroup();
#endif
        }
        static void ImportEnvironment(string environmentName, string jsonText, string path,
                                      GameObject simulatedPlanePrefab, Material simulatedPlaneMaterial, Post postPrefab, MeshFilter meshPrefab,
                                      GameObject simulatedLightingPrefab, Action <UnityObject> callback)
        {
            var environment = SceneSerialization.FromJson <Environment>(jsonText);
            var height      = environment.height;
            var vertices    = environment.vertices;
            var center      = Vector3.zero;
            var posts       = CollectionPool <List <Post>, Post> .GetCollection();

            var  count    = vertices.Count;
            Post previous = null;

            foreach (var vertex in vertices)
            {
                var post = UnityObject.Instantiate(postPrefab);
                posts.Add(post);
                center += vertex;
                var postTransform = post.transform;
                postTransform.position = vertex;
                post.SetTopSphereHeight(height);

                if (previous != null)
                {
                    previous.Setup();
                    var previousPosition = previous.transform.position;
                    previous.UpdateWall(vertex, previousPosition - vertex, height);
                }

                previous = post;
            }

            if (count != 0)
            {
                center /= count;
            }

            var prefabRoot = new GameObject(environmentName).transform;
            var floorPlan  = new GameObject("Floor Plan").transform;

            prefabRoot.position = center;
            floorPlan.SetParent(prefabRoot, false);
            foreach (var post in posts)
            {
                post.transform.SetParent(floorPlan, true);
                var wall = post.Wall;
                if (wall != null)
                {
                    wall.SetParent(floorPlan, true);
                }
            }

            var planesRoot = new GameObject("Planes").transform;

            planesRoot.SetParent(prefabRoot, false);
            foreach (var plane in environment.planes)
            {
                var synthesizedPlane = ((GameObject)PrefabUtility.InstantiatePrefab(simulatedPlanePrefab, planesRoot)).GetComponent <SynthesizedPlane>();
                synthesizedPlane.transform.SetWorldPose(plane.pose);
                synthesizedPlane.SetMRPlaneData(plane.vertices, plane.center, plane.extents);
                synthesizedPlane.transform.SetParent(planesRoot, true);
                var renderer = synthesizedPlane.GetComponentInChildren <Renderer>();
                renderer.AddMaterial(simulatedPlaneMaterial);
            }

            var meshCapture = environment.MeshCapture;

            if (meshCapture.Meshes.Count > 0)
            {
                var meshesRoot = new GameObject("Meshes").transform;
                meshesRoot.SetParent(prefabRoot, false);
                var meshPath = EditorUtility.SaveFilePanelInProject(k_SaveMeshDialogTitle, environmentName, "asset", string.Empty);
                var index    = 0;
                foreach (var segment in meshCapture.Meshes)
                {
                    var meshFilter = UnityObject.Instantiate(meshPrefab, meshesRoot);
                    var mesh       = new Mesh {
                        name = $"Mesh Segment {index}"
                    };
                    mesh.SetVertices(segment.Vertices);
                    mesh.SetIndices(segment.Indices, MeshTopology.Triangles, 0);
                    mesh.SetNormals(segment.Normals);
                    meshFilter.transform.SetWorldPose(segment.Pose);
                    meshFilter.sharedMesh = mesh;
                    if (index == 0)
                    {
                        AssetDatabase.CreateAsset(mesh, meshPath);
                        AssetDatabase.SetMainObject(mesh, meshPath);
                    }
                    else
                    {
                        AssetDatabase.AddObjectToAsset(mesh, meshPath);
                    }

                    index++;
                }

                AssetDatabase.SaveAssets();
            }

            var environmentBounds    = BoundsUtils.GetBounds(prefabRoot);
            var prefabRootGameObject = prefabRoot.gameObject;
            var environmentSettings  = prefabRootGameObject.AddComponent <MARSEnvironmentSettings>();
            var environmentInfo      = environmentSettings.EnvironmentInfo;

            environmentInfo.EnvironmentBounds = environmentBounds;
            center = environmentBounds.center;
            var extents = environmentBounds.extents * 0.9f; // Reduce offset to avoid sim starting pose warning

            environmentInfo.DefaultCameraPivot = center;
            var cameraPose = new Pose(center + extents, Quaternion.LookRotation(-extents));

            environmentInfo.DefaultCameraWorldPose = cameraPose;
            environmentInfo.DefaultCameraSize      = environmentBounds.size.magnitude;
            environmentSettings.SetSimulationStartingPose(cameraPose, false);
            environmentSettings.UpdatePrefabInfo();
            UnityObject.Instantiate(simulatedLightingPrefab, prefabRoot);

            var prefab = PrefabUtility.SaveAsPrefabAssetAndConnect(prefabRootGameObject, path, InteractionMode.AutomatedAction);

            UnityObject.DestroyImmediate(prefabRootGameObject);
            AssetDatabase.SetLabels(prefab, new[] { MARSEnvironmentManager.EnvironmentLabel });
            ModuleLoaderCore.instance.GetModule <MARSEnvironmentManager>().UpdateSimulatedEnvironmentCandidates();
            callback(prefab);

            CollectionPool <List <Post>, Post> .RecycleCollection(posts);
        }