public unsafe void Update(MyShadowVolume[] volumes, ref MyShadowsSettings settings, float shadowmapResolution)
        {
            bool stabilize = true;
            float cascadesNearClip = 1f;
            float shadowChangeDelayMultiplier = 180;
            const float directionDifferenceThreshold = 0.0175f;

            float backOffset = MyRender11.RenderSettings.ShadowQuality.BackOffset();
            float shadowmapSize = MyRender11.RenderSettings.ShadowQuality.ShadowCascadeResolution();

            Array.Resize(ref m_shadowCascadeSplitDepths, volumes.Length + 1);
            Array.Resize(ref m_shadowCascadeUpdatePositions, volumes.Length);
            Array.Resize(ref m_shadowCascadeFramesSinceLightUpdate, volumes.Length);
            Array.Resize(ref m_shadowCascadeLightDirections, volumes.Length);

            for (int cascadeIndex = 0; cascadeIndex < volumes.Length; ++cascadeIndex)
                m_shadowCascadeSplitDepths[cascadeIndex] = MyRender11.RenderSettings.ShadowQuality.ShadowCascadeSplit(cascadeIndex);

            double unitWidth = 1.0 / MyRender11.Environment.Matrices.Projection.M11;
            double unitHeight = 1.0 / MyRender11.Environment.Matrices.Projection.M22;

            Vector3D* untransformedVertices = stackalloc Vector3D[4];
            untransformedVertices[0] = new Vector3D(-unitWidth, -unitHeight, -1);
            untransformedVertices[1] = new Vector3D(-unitWidth, unitHeight, -1);
            untransformedVertices[2] = new Vector3D(unitWidth, unitHeight, -1);
            untransformedVertices[3] = new Vector3D(unitWidth, -unitHeight, -1);

            MatrixD* cascadesMatrices = stackalloc MatrixD[volumes.Length];

            for (int cascadeIndex = 0; cascadeIndex < volumes.Length; ++cascadeIndex)
            {
                ++m_shadowCascadeFramesSinceLightUpdate[cascadeIndex];

                if (m_shadowCascadeFramesSinceLightUpdate[cascadeIndex] > cascadeIndex * shadowChangeDelayMultiplier ||
                    MyRender11.Environment.Data.EnvironmentLight.SunLightDirection.Dot(m_shadowCascadeLightDirections[cascadeIndex]) < (1 - directionDifferenceThreshold))
                {
                    m_shadowCascadeLightDirections[cascadeIndex] = MyRender11.Environment.Data.EnvironmentLight.SunLightDirection;
                    m_shadowCascadeFramesSinceLightUpdate[cascadeIndex] = 0;
                }
            }

            for (int cascadeIndex = 0; cascadeIndex < volumes.Length; ++cascadeIndex)
            {
                for (int vertexIndex = 0; vertexIndex < 4; ++vertexIndex)
                {
                    m_frustumVerticesWS[vertexIndex] = untransformedVertices[vertexIndex] * m_shadowCascadeSplitDepths[cascadeIndex];
                    m_frustumVerticesWS[vertexIndex + 4] = untransformedVertices[vertexIndex] * m_shadowCascadeSplitDepths[cascadeIndex + 1];
                }

                bool skipCascade = MyCommon.FrameCounter % (ulong)m_shadowCascadeUpdateIntervals[cascadeIndex].Item1 != (ulong)m_shadowCascadeUpdateIntervals[cascadeIndex].Item2;
                bool forceUpdate = m_shadowCascadeSplitDepths[cascadeIndex] > 1000f && Vector3D.DistanceSquared(m_shadowCascadeUpdatePositions[cascadeIndex], MyRender11.Environment.Matrices.CameraPosition) > Math.Pow(1000, 2);
                // 
                if (!forceUpdate && skipCascade && !settings.Data.UpdateCascadesEveryFrame)
                    continue;
                //if (settings.ShadowCascadeFrozen[cascadeIndex])
                //    continue;

                m_shadowCascadeUpdatePositions[cascadeIndex] = MyRender11.Environment.Matrices.CameraPosition;

                MatrixD invView = MyRender11.Environment.Matrices.InvView;
                Vector3D.Transform(m_frustumVerticesWS, ref invView, m_frustumVerticesWS);

                var bSphere = BoundingSphereD.CreateFromPoints(m_frustumVerticesWS);
                if (stabilize)
                {
                    bSphere.Center = bSphere.Center.Round();
                    bSphere.Radius = Math.Ceiling(bSphere.Radius);
                }

                var shadowCameraPosWS = bSphere.Center + m_shadowCascadeLightDirections[cascadeIndex] * (bSphere.Radius + cascadesNearClip);

                var lightView = VRageMath.MatrixD.CreateLookAt(shadowCameraPosWS, shadowCameraPosWS - m_shadowCascadeLightDirections[cascadeIndex], Math.Abs(Vector3.UnitY.Dot(m_shadowCascadeLightDirections[cascadeIndex])) < 0.99f ? Vector3.UnitY : Vector3.UnitX);
                var offset = bSphere.Radius + cascadesNearClip + backOffset;

                Vector3D vMin = new Vector3D(-bSphere.Radius, -bSphere.Radius, cascadesNearClip);
                Vector3D vMax = new Vector3D(bSphere.Radius, bSphere.Radius, offset + bSphere.Radius);

                var cascadeProjection = MatrixD.CreateOrthographicOffCenter(vMin.X, vMax.X, vMin.Y, vMax.Y, vMax.Z, vMin.Z);
                cascadesMatrices[cascadeIndex] = lightView * cascadeProjection;

                var transformed = Vector3D.Transform(Vector3D.Zero, cascadesMatrices[cascadeIndex]) * shadowmapSize / 2;
                var smOffset = (transformed.Round() - transformed) * 2 / shadowmapSize;

                // stabilize 1st cascade only
                if (stabilize)
                {
                    cascadeProjection.M41 += smOffset.X;
                    cascadeProjection.M42 += smOffset.Y;
                    cascadesMatrices[cascadeIndex] = lightView * cascadeProjection;
                }

                Matrix matrixTranslation = Matrix.CreateTranslation(MyRender11.Environment.Matrices.CameraPosition);
                cascadesMatrices[cascadeIndex] = matrixTranslation * cascadesMatrices[cascadeIndex];
                volumes[cascadeIndex].SetMatrixWorldAt0ToShadow(cascadesMatrices[cascadeIndex]);
            }
        }
        public void Update(MyShadowVolume[] volumes, ref MyShadowsSettings settings, float shadowmapResolution)
        {
            // Update sun position:
            if (!settings.NewData.FreezeSunDirection)
            {
                Vector3D currentSunLightDir = MyRender11.Environment.Data.EnvironmentLight.SunLightDirection;
                Vector3D prevSunLightDir = m_sunDirection;
                currentSunLightDir.Normalize();
                prevSunLightDir.Normalize();
                double diffAngle = 360.0 / Math.PI * Math.Acos(Vector3D.Dot(prevSunLightDir, currentSunLightDir));
                if (diffAngle >= settings.NewData.SunAngleThreshold)
                    m_sunDirection = MyRender11.Environment.Data.EnvironmentLight.SunLightDirection;
            }

            double unitWidth = 1.0/MyRender11.Environment.Matrices.Projection.M11;
            double unitHeight = 1.0/MyRender11.Environment.Matrices.Projection.M22;
            for (int cascadeIndex = 0; cascadeIndex < volumes.Length; ++cascadeIndex)
            {
                // Update primitive vertices that are inside of frustrum matrix:
                float baseDist = settings.Cascades[cascadeIndex].FullCoverageDepth;
                float baseCut = (cascadeIndex == 0) ? 0 : settings.Cascades[cascadeIndex - 1].FullCoverageDepth;
                Vector3D[] verts;
                float extDepth = settings.Cascades[cascadeIndex].ExtendedCoverageDepth;

                if (!settings.NewData.StabilizeRotation)
                    verts = MyCsmPlacementStrategyUtil.CreateCutPyramidExt(unitWidth, unitHeight, baseDist, baseCut, extDepth);
                else
                {
                    double coneRadius = Math.Sqrt((unitWidth * unitWidth) + (unitHeight * unitHeight));
                    verts = MyCsmPlacementStrategyUtil.CreateCutConeExt(coneRadius, baseDist, baseCut, extDepth);
                }
                double shadowVolumeSize = GetStableShadowVolumeSize(verts, shadowmapResolution);

                MatrixD invView = MyRender11.Environment.Matrices.InvView;
                Vector3D.Transform(verts, ref invView, verts);

                float zOffset = settings.NewData.ZOffset;

                MatrixD shadowMatrixWorld;
                if (!settings.NewData.StabilizeMovement)
                    shadowMatrixWorld = CreateRigidLightMatrix(verts, m_sunDirection, zOffset);
                else
                    shadowMatrixWorld = CreateStableLightMatrix(verts, m_sunDirection, zOffset, shadowVolumeSize, shadowmapResolution);

                Matrix matrixTranslation = Matrix.CreateTranslation(MyRender11.Environment.Matrices.CameraPosition);
                Matrix shadowMatrixWorldAt0 = matrixTranslation*shadowMatrixWorld;
                volumes[cascadeIndex].SetMatrixWorldAt0ToShadow(shadowMatrixWorldAt0);
            }
        }