public void UpdateTempStorage(VoxelStorageContext context)
        {
            storageUints       = (tempStorageCounter + 31) / 32;
            tempStorageCounter = 0;

            var resolution = ClipMapResolution;
            int fragments  = (int)(resolution.X * resolution.Y * resolution.Z) * ClipMapCount;

            if (VoxelUtils.DisposeBufferBySpecs(FragmentsBuffer, storageUints * fragments) && storageUints * fragments > 0)
            {
                FragmentsBuffer = Stride.Graphics.Buffer.Typed.New(context.device, storageUints * fragments, PixelFormat.R32_UInt, true);
            }
        }
Beispiel #2
0
        public void UpdateTexture(VoxelStorageContext context, ref IVoxelStorageTexture texture, Stride.Graphics.PixelFormat pixelFormat, int LayoutSize)
        {
            if (texture is not VoxelStorageTextureClipmap clipmap)
            {
                clipmap = new VoxelStorageTextureClipmap();
            }

            Vector3 ClipMapTextureResolution = new Vector3(ClipMapResolution.X, ClipMapResolution.Y * ClipMapCount * LayoutSize, ClipMapResolution.Z);
            Vector3 MipMapResolution         = new Vector3(ClipMapResolution.X / 2, ClipMapResolution.Y / 2 * LayoutSize, ClipMapResolution.Z / 2);

            if (VoxelUtils.DisposeTextureBySpecs(clipmap.ClipMaps, ClipMapTextureResolution, pixelFormat))
            {
                clipmap.ClipMaps = Texture.New3D(context.device, (int)ClipMapTextureResolution.X, (int)ClipMapTextureResolution.Y, (int)ClipMapTextureResolution.Z, new MipMapCount(false), pixelFormat, TextureFlags.ShaderResource | TextureFlags.UnorderedAccess);
            }

            if (VoxelUtils.DisposeTextureBySpecs(clipmap.MipMaps, MipMapResolution, pixelFormat))
            {
                if (clipmap.TempMipMaps != null)
                {
                    for (int i = 0; i < clipmap.TempMipMaps.Length; i++)
                    {
                        clipmap.TempMipMaps[i].Dispose();
                    }
                }

                Vector3 MipMapResolutionMax = MipMapResolution;

                clipmap.MipMaps = Texture.New3D(context.device, (int)MipMapResolution.X, (int)MipMapResolution.Y, (int)MipMapResolution.Z, new MipMapCount(true), pixelFormat, TextureFlags.ShaderResource | TextureFlags.UnorderedAccess);

                clipmap.TempMipMaps = new Texture[MipMapCount];

                for (int i = 0; i < clipmap.TempMipMaps.Length; i++)
                {
                    clipmap.TempMipMaps[i] = Texture.New3D(context.device, (int)MipMapResolutionMax.X, (int)MipMapResolutionMax.Y, (int)MipMapResolutionMax.Z, false, pixelFormat, TextureFlags.ShaderResource | TextureFlags.UnorderedAccess);

                    MipMapResolutionMax /= 2;
                }
            }

            clipmap.DownsampleFinerClipMaps = DownsampleFinerClipMaps;
            clipmap.ClipMapResolution       = ClipMapResolution;
            clipmap.ClipMapCount            = ClipMapCount;
            clipmap.LayoutSize        = LayoutSize;
            clipmap.VoxelSize         = context.RealVoxelSize();
            clipmap.VolumeTranslation = new Int3((int)context.VoxelSpaceTranslation.X, (int)context.VoxelSpaceTranslation.Y, (int)context.VoxelSpaceTranslation.Z);

            Array.Copy(MippingOffset, clipmap.MippingOffset, MippingOffset.Length);
            Array.Copy(PerMapOffsetScale, clipmap.PerMapOffsetScale, PerMapOffsetScale.Length);

            texture = clipmap;
        }
Beispiel #3
0
        public void Render(VoxelStorageContext storageContext, RenderDrawContext drawContext, RenderView view)
        {
            RenderView voxelizationView = view;
            Int2       ViewSize         = VoxelizationViewSizes[view];

            if (VoxelUtils.DisposeTextureBySpecs(MSAARenderTarget, new Vector3(ViewSize.X, ViewSize.Y, 1), PixelFormat.R8G8B8A8_UNorm, MultisampleCount))
            {
                MSAARenderTarget = Texture.New(storageContext.device, TextureDescription.New2D(ViewSize.X, ViewSize.Y, new MipMapCount(false), PixelFormat.R8G8B8A8_UNorm, TextureFlags.RenderTarget, 1, GraphicsResourceUsage.Default, MultisampleCount), null);
            }

            drawContext.CommandList.ResetTargets();
            if (MSAARenderTarget != null)
            {
                drawContext.CommandList.SetRenderTarget(null, MSAARenderTarget);
            }

            var renderSystem = drawContext.RenderContext.RenderSystem;

            drawContext.CommandList.SetViewport(new Viewport(0, 0, ViewSize.X, ViewSize.Y));

            renderSystem.Draw(drawContext, voxelizationView, renderSystem.RenderStages[voxelizationView.RenderStages[0].Index]);
        }
Beispiel #4
0
 public abstract void PrepareOutputStorage(VoxelStorageContext context, IVoxelStorage storage);
 public override void PrepareLocalStorage(VoxelStorageContext context, IVoxelStorage storage)
 {
     BufferOffset = storage.RequestTempStorage(32);
 }
 public override void PrepareOutputStorage(VoxelStorageContext context, IVoxelStorage storage)
 {
     storage.UpdateTexture(context, ref CoverageTex, Graphics.PixelFormat.R11G11B10_Float, 1);
 }
Beispiel #7
0
        public virtual void Collect(RenderContext Context, Shadows.IShadowMapRenderer ShadowMapRenderer)
        {
            renderVoxelVolumes    = Context.VisibilityGroup.Tags.Get(CurrentRenderVoxelVolumes);
            renderVoxelVolumeData = Context.VisibilityGroup.Tags.Get(CurrentProcessedVoxelVolumes);

            if (renderVoxelVolumes == null || renderVoxelVolumes.Count == 0)
            {
                return;
            }

            if (Context.RenderSystem.GraphicsDevice.Features.CurrentProfile < GraphicsProfile.Level_11_0)
            {
                throw new ArgumentOutOfRangeException("Graphics Profile Level 11 or higher required for Voxelization.");
            }

            //Setup per volume passes and texture allocations
            foreach (var pair in renderVoxelVolumes)
            {
                var dataVolume = pair.Value;
                var bounds     = dataVolume.VolumeSize;

                ProcessedVoxelVolume processedVolume;
                if (!renderVoxelVolumeData.TryGetValue(pair.Key, out processedVolume))
                {
                    processedVolume = new ProcessedVoxelVolume();
                    renderVoxelVolumeData.Add(pair.Key, processedVolume);
                }


                //Setup matrix
                Vector3 matScale = dataVolume.VolumeSize;
                Vector3 matTrans = dataVolume.VolumeTranslation;

                Matrix corMatrix = Matrix.Scaling(matScale) * Matrix.Translation(matTrans);
                VoxelStorageContext storageContext = new VoxelStorageContext
                {
                    device    = Context.GraphicsDevice,
                    Extents   = bounds,
                    VoxelSize = dataVolume.AproxVoxelSize,
                    Matrix    = corMatrix
                };

                if (dataVolume.VoxelGridSnapping)
                {
                    matTrans  /= storageContext.RealVoxelSize();
                    matTrans.X = MathF.Floor(matTrans.X);
                    matTrans.Y = MathF.Floor(matTrans.Y);
                    matTrans.Z = MathF.Floor(matTrans.Z);
                    matTrans  *= storageContext.RealVoxelSize();

                    corMatrix             = Matrix.Scaling(matScale) * Matrix.Translation(matTrans);
                    storageContext.Matrix = corMatrix;
                }
                storageContext.Translation           = matTrans;
                storageContext.VoxelSpaceTranslation = matTrans / storageContext.RealVoxelSize();

                //Update storage
                dataVolume.Storage.UpdateFromContext(storageContext);

                //Transfer voxelization info
                processedVolume.VisualizeVoxels        = dataVolume.VisualizeVoxels;
                processedVolume.Storage                = dataVolume.Storage;
                processedVolume.StorageContext         = storageContext;
                processedVolume.VoxelizationMethod     = dataVolume.VoxelizationMethod;
                processedVolume.VoxelVisualization     = dataVolume.VoxelVisualization;
                processedVolume.VisualizationAttribute = dataVolume.VisualizationAttribute;
                processedVolume.Voxelize               = dataVolume.Voxelize;
                processedVolume.OutputAttributes       = dataVolume.Attributes;

                processedVolume.Attributes.Clear();
                processedVolume.passList.Clear();
                processedVolume.groupedPasses.Clear();

                processedVolume.passList.defaultVoxelizationMethod = dataVolume.VoxelizationMethod;


                //Create final list of attributes (including temporary ones)
                foreach (var attr in dataVolume.Attributes)
                {
                    attr.CollectAttributes(processedVolume.Attributes, VoxelizationStage.Initial, true);
                }

                //Allocate textures and space in the temporary buffer
                foreach (var attr in processedVolume.Attributes)
                {
                    attr.Attribute.PrepareLocalStorage(storageContext, dataVolume.Storage);
                    if (attr.Output)
                    {
                        attr.Attribute.PrepareOutputStorage(storageContext, dataVolume.Storage);
                    }
                    else
                    {
                        attr.Attribute.ClearOutputStorage();
                    }
                }
                dataVolume.Storage.UpdateTempStorage(storageContext);

                //Create list of voxelization passes that need to be done
                dataVolume.Storage.CollectVoxelizationPasses(processedVolume, storageContext);

                //Group voxelization passes where the RenderStage can be shared
                //TODO: Group identical attributes
                for (int i = 0; i < processedVolume.passList.passes.Count; i++)
                {
                    bool added = false;
                    var  passA = processedVolume.passList.passes[i];
                    for (int group = 0; group < processedVolume.groupedPasses.Count; group++)
                    {
                        var passB = processedVolume.groupedPasses[group][0];
                        if (
                            passB.storer.CanShareRenderStage(passA.storer) &&
                            passB.method.CanShareRenderStage(passA.method) &&
                            passB.AttributesDirect.SequenceEqual(passA.AttributesDirect) &&
                            passB.AttributesIndirect.SequenceEqual(passA.AttributesIndirect) &&
                            passB.AttributesTemp.SequenceEqual(passA.AttributesTemp)
                            )
                        {
                            processedVolume.groupedPasses[group].Add(passA);
                            added = true;
                            break;
                        }
                    }
                    if (!added)
                    {
                        List <VoxelizationPass> newGroup = new List <VoxelizationPass>
                        {
                            passA
                        };
                        processedVolume.groupedPasses.Add(newGroup);
                    }
                }

                if (VoxelStages.Count < processedVolume.groupedPasses.Count)
                {
                    throw new ArgumentOutOfRangeException(processedVolume.groupedPasses.Count.ToString() + " Render Stages required for voxelization, only " + VoxelStages.Count.ToString() + " provided.");
                }

                //Finish preparing the passes, collecting views and setting up shader sources and shadows
                for (int group = 0; group < processedVolume.groupedPasses.Count; group++)
                {
                    foreach (var pass in processedVolume.groupedPasses[group])
                    {
                        pass.renderStage = VoxelStages[group];
                        pass.source      = pass.storer.GetVoxelizationShader(pass, processedVolume);
                        pass.view.RenderStages.Add(pass.renderStage);

                        Context.RenderSystem.Views.Add(pass.view);
                        Context.VisibilityGroup.TryCollect(pass.view);

                        if (pass.requireShadows)
                        {
                            ShadowMapRenderer?.RenderViewsWithShadows.Add(pass.view);
                        }
                    }
                }
            }
        }
 public int PrepareLocalStorage(VoxelStorageContext context, IVoxelStorage storage, int channels, int layoutCount)
 {
     return(storage.RequestTempStorage(TempStorageFormat.GetBits(channels) * layoutCount));
 }
Beispiel #9
0
 public void Render(VoxelStorageContext storageContext, RenderDrawContext drawContext, RenderView view)
 {
 }
Beispiel #10
0
        public virtual void Draw(RenderDrawContext drawContext, Shadows.IShadowMapRenderer ShadowMapRenderer)
        {
            if (renderVoxelVolumes == null || renderVoxelVolumes.Count == 0)
            {
                return;
            }

            if (drawContext.GraphicsDevice.Features.CurrentProfile < GraphicsProfile.Level_11_0)
            {
                return;
            }

            var context = drawContext;

            using (drawContext.PushRenderTargetsAndRestore())
            {
                // Draw all shadow views generated for the current view
                foreach (var processedVolumeKeyValue in renderVoxelVolumeData)
                {
                    var processedVolume = processedVolumeKeyValue.Value;
                    if (!processedVolume.Voxelize)
                    {
                        continue;
                    }

                    VoxelStorageContext storageContext = processedVolume.StorageContext;

                    using (drawContext.QueryManager.BeginProfile(Color.Black, PassesVoxelizationProfilingKey))
                    {
                        foreach (VoxelizationPass pass in processedVolume.passList.passes)
                        {
                            RenderView voxelizeRenderView = pass.view;

                            if (pass.requireShadows)
                            {
                                //Render Shadow Maps
                                RenderView oldView = drawContext.RenderContext.RenderView;

                                drawContext.RenderContext.RenderView = voxelizeRenderView;
                                ShadowMapRenderer.Draw(drawContext);
                                drawContext.RenderContext.RenderView = oldView;
                            }

                            //Render/Collect voxel fragments
                            using (drawContext.QueryManager.BeginProfile(Color.Black, FragmentVoxelizationProfilingKey))
                            {
                                using (drawContext.PushRenderTargetsAndRestore())
                                {
                                    pass.method.Render(storageContext, context, pass.view);
                                }
                            }
                        }
                        foreach (VoxelizationPass pass in processedVolume.passList.passes)
                        {
                            pass.method.Reset();
                        }
                    }

                    //Fill and write to voxel volume
                    using (drawContext.QueryManager.BeginProfile(Color.Black, BufferProcessingVoxelizationProfilingKey))
                    {
                        processedVolume.Storage.PostProcess(storageContext, context, processedVolume);
                    }

                    //Mipmap
                    using (drawContext.QueryManager.BeginProfile(Color.Black, MipmappingVoxelizationProfilingKey))
                    {
                        foreach (var attr in processedVolume.Attributes)
                        {
                            if (attr.Output)
                            {
                                attr.Attribute.PostProcess(context);
                            }
                        }
                    }
                }
            }
        }
 virtual public int PrepareLocalStorage(VoxelStorageContext context, IVoxelStorage storage)
 {
     return(StorageMethod.PrepareLocalStorage(context, storage, 4, LayoutCount));
 }
 virtual public void PrepareOutputStorage(VoxelStorageContext context, IVoxelStorage storage)
 {
     storage.UpdateTexture(context, ref storageTex, StorageFormatToPixelFormat(), LayoutCount);
 }
Beispiel #13
0
 public abstract void PrepareLocalStorage(VoxelStorageContext context, IVoxelStorage storage);
Beispiel #14
0
        public void PostProcess(VoxelStorageContext storageContext, RenderDrawContext drawContext, ProcessedVoxelVolume data)
        {
            if (Math.Max(Math.Max(ClipMapResolution.X, ClipMapResolution.Y), ClipMapResolution.Z) < 32)
            {
                return;
            }
            if (FragmentsBuffer == null)
            {
                return;
            }
            var context = drawContext.RenderContext;

            if (ClearBuffer == null)
            {
                ClearBuffer = new Stride.Rendering.ComputeEffect.ComputeEffectShader(context)
                {
                    ShaderSourceName = "ClearBuffer"
                };
                BufferToTexture = new Stride.Rendering.ComputeEffect.ComputeEffectShader(context)
                {
                    ShaderSourceName = "BufferToTextureEffect"
                };
                BufferToTextureColumns = new Stride.Rendering.ComputeEffect.ComputeEffectShader(context)
                {
                    ShaderSourceName = "BufferToTextureColumnsEffect"
                };
            }

            bool VoxelsAreIndependent = true;

            List <VoxelAttribute>  IndirectVoxels = new List <VoxelAttribute>();
            List <VoxelAttribute>  TempVoxels     = new List <VoxelAttribute>();
            ShaderSourceCollection Indirect       = new ShaderSourceCollection();
            ShaderSourceCollection Temp           = new ShaderSourceCollection();

            //Assign sample indices and check whether voxels can be calculated independently
            int sampleIndex = 0;

            foreach (var attr in data.Attributes)
            {
                attr.Attribute.LocalSamplerID = sampleIndex;
                VoxelsAreIndependent         &= !attr.Attribute.RequiresColumns();
                sampleIndex++;
            }

            //Populate ShaderSourceCollections and temp lists
            foreach (var attr in data.Attributes)
            {
                if (attr.Stage != VoxelizationStage.Post)
                {
                    continue;
                }
                if (attr.Output)
                {
                    Indirect.Add(attr.Attribute.GetVoxelizationShader());
                    IndirectVoxels.Add(attr.Attribute);
                }
                else
                {
                    Temp.Add(attr.Attribute.GetVoxelizationShader());
                    TempVoxels.Add(attr.Attribute);
                }
            }

            var BufferWriter = VoxelsAreIndependent ? BufferToTexture : BufferToTextureColumns;

            for (int i = 0; i < IndirectVoxels.Count; i++)
            {
                var attr = IndirectVoxels[i];
                attr.UpdateVoxelizationLayout($"AttributesIndirect[{i}]");
            }
            for (int i = 0; i < TempVoxels.Count; i++)
            {
                var attr = TempVoxels[i];
                attr.UpdateVoxelizationLayout($"AttributesTemp[{i}]");
            }
            foreach (var attr in data.Attributes)
            {
                attr.Attribute.ApplyVoxelizationParameters(BufferWriter.Parameters);
            }


            int processYSize = VoxelsAreIndependent ? (int)ClipMapResolution.Y : 1;

            processYSize *= (UpdatesPerFrame == UpdateMethods.SingleClipmap) ? 1 : ClipMapCount;

            BufferWriter.ThreadGroupCounts = VoxelsAreIndependent ? new Int3(32, 32, 32) : new Int3(32, 1, 32);
            BufferWriter.ThreadNumbers     = new Int3((int)ClipMapResolution.X / BufferWriter.ThreadGroupCounts.X, processYSize / BufferWriter.ThreadGroupCounts.Y, (int)ClipMapResolution.Z / BufferWriter.ThreadGroupCounts.Z);

            BufferWriter.Parameters.Set(BufferToTextureKeys.VoxelFragments, FragmentsBuffer);
            BufferWriter.Parameters.Set(BufferToTextureKeys.clipMapResolution, ClipMapResolution);
            BufferWriter.Parameters.Set(BufferToTextureKeys.storageUints, storageUints);

            BufferWriter.Parameters.Set(BufferToTextureKeys.clipOffset, (uint)(UpdatesPerFrame == UpdateMethods.SingleClipmap ? ClipMapCurrent : 0));


            //Modifiers are stored within attributes, yet need to be able to query their results.
            //Ideally a stage stream could resolve this, however due to the lack of pointers, there would be a cyclic dependency of AttributesList->Attribute->Modifier->AttributesList->...
            //So instead the results will be stored within a second array that only contains float4s. Unfortunately the only way to iterate through the AttributesList is by foreach, which
            //makes it difficult to access the results array (AttributeLocalSamples) by index. So instead it's just all done through this macro...
            string IndirectReadAndStoreMacro = "";
            string IndirectStoreMacro        = "";

            for (int i = 0; i < Temp.Count; i++)
            {
                string iStr           = i.ToString();
                string sampleIndexStr = TempVoxels[i].LocalSamplerID.ToString();
                IndirectReadAndStoreMacro += $"AttributesTemp[{iStr}].InitializeFromBuffer(VoxelFragments, VoxelFragmentsIndex + {TempVoxels[i].BufferOffset}, uint2({TempVoxels[i].BufferOffset} + initialVoxelFragmentsIndex, yStride));\n" +
                                             $"streams.LocalSample[{sampleIndexStr}] = AttributesTemp[{iStr}].SampleLocal();\n\n";
                IndirectStoreMacro += $"streams.LocalSample[{sampleIndexStr}] = AttributesTemp[{iStr}].SampleLocal();\n";
            }
            for (int i = 0; i < Indirect.Count; i++)
            {
                string iStr           = i.ToString();
                string sampleIndexStr = IndirectVoxels[i].LocalSamplerID.ToString();
                IndirectReadAndStoreMacro += $"AttributesIndirect[{iStr}].InitializeFromBuffer(VoxelFragments, VoxelFragmentsIndex + {IndirectVoxels[i].BufferOffset}, uint2({IndirectVoxels[i].BufferOffset} + initialVoxelFragmentsIndex, yStride));\n" +
                                             $"streams.LocalSample[{sampleIndexStr}] = AttributesIndirect[{iStr}].SampleLocal();\n\n";
                IndirectStoreMacro += $"streams.LocalSample[{sampleIndexStr}] = AttributesIndirect[{iStr}].SampleLocal();\n";
            }



            BufferWriter.Parameters.Set(BufferToTextureKeys.AttributesIndirect, Indirect);
            BufferWriter.Parameters.Set(BufferToTextureKeys.AttributesTemp, Temp);
            BufferWriter.Parameters.Set(BufferToTextureKeys.IndirectReadAndStoreMacro, IndirectReadAndStoreMacro);
            BufferWriter.Parameters.Set(BufferToTextureKeys.IndirectStoreMacro, IndirectStoreMacro);

            ((RendererBase)BufferWriter).Draw(drawContext);



            ClearBuffer.Parameters.Set(ClearBufferKeys.buffer, FragmentsBuffer);

            if (UpdatesPerFrame != UpdateMethods.SingleClipmap)
            {
                //Clear all
                ClearBuffer.ThreadNumbers     = new Int3(1024, 1, 1);
                ClearBuffer.ThreadGroupCounts = new Int3(FragmentsBuffer.ElementCount / 1024, 1, 1);
                ClearBuffer.Parameters.Set(ClearBufferKeys.offset, 0);
            }
            else
            {
                //Clear next clipmap buffer
                ClearBuffer.ThreadNumbers     = new Int3(1024, 1, 1);
                ClearBuffer.ThreadGroupCounts = new Int3((int)(ClipMapResolution.X * ClipMapResolution.Y * ClipMapResolution.Z * storageUints) / 1024, 1, 1);
                ClearBuffer.Parameters.Set(ClearBufferKeys.offset, (int)(((ClipMapCurrent + 1) % ClipMapCount) * ClipMapResolution.X * ClipMapResolution.Y * ClipMapResolution.Z * storageUints));
            }
            ((RendererBase)ClearBuffer).Draw(drawContext);
        }
 public override void PrepareOutputStorage(VoxelStorageContext context, IVoxelStorage storage)
 {
     VoxelLayout.PrepareOutputStorage(context, storage);
 }
 public override void PrepareLocalStorage(VoxelStorageContext context, IVoxelStorage storage)
 {
     BufferOffset = VoxelLayout.PrepareLocalStorage(context, storage);
 }
 public override void PrepareOutputStorage(VoxelStorageContext context, IVoxelStorage storage)
 {
     storage.UpdateTexture(context, ref SolidityTex, Graphics.PixelFormat.R8_UNorm, 1);
 }
Beispiel #18
0
 public void PostProcess(VoxelStorageContext context, RenderDrawContext drawContext, ProcessedVoxelVolume data)
 {
 }
Beispiel #19
0
        public void UpdateFromContext(VoxelStorageContext context)
        {
            var virtualResolution = context.Resolution();
            var largestDimension  = (double)Math.Max(virtualResolution.X, Math.Max(virtualResolution.Y, virtualResolution.Z));

            ClipMapCount = (int)Math.Log(largestDimension / Math.Min(largestDimension, (double)ClipResolution), 2) + 1;

            ClipMapCurrent++;
            if (ClipMapCurrent >= ClipMapCount)
            {
                ClipMapCurrent = 0;
            }

            float FinestClipMapScale = (float)Math.Pow(2, ClipMapCount - 1);

            ClipMapResolution = new Vector3(virtualResolution.X, virtualResolution.Y, virtualResolution.Z) / FinestClipMapScale;
            MipMapCount       = (int)Math.Floor(Math.Log(Math.Min(ClipMapResolution.X, Math.Min(ClipMapResolution.Y, ClipMapResolution.Z)), 2));

            int voxelScale = 1;

            for (int i = 0; i < (ClipMapCount + MipMapCount); i++)
            {
                Vector3 SnappedVolumeTranslation = context.VoxelSpaceTranslation;
                SnappedVolumeTranslation.X = MathF.Floor(SnappedVolumeTranslation.X / voxelScale) * voxelScale;
                SnappedVolumeTranslation.Y = MathF.Floor(SnappedVolumeTranslation.Y / voxelScale) * voxelScale;
                SnappedVolumeTranslation.Z = MathF.Floor(SnappedVolumeTranslation.Z / voxelScale) * voxelScale;

                if (ShouldUpdateClipIndex(i))
                {
                    PerMapSnappingOffset[i] = -SnappedVolumeTranslation *context.RealVoxelSize();

                    MippingOffsetTranslation[i] = new Int3((int)SnappedVolumeTranslation.X, (int)SnappedVolumeTranslation.Y, (int)SnappedVolumeTranslation.Z);
                }

                voxelScale *= 2;
            }

            float extentScale = (float)Math.Pow(2f, ClipMapCount - 1);

            voxelScale = 1;

            for (int i = 0; i < (ClipMapCount + MipMapCount); i++)
            {
                if (ShouldUpdateClipIndex(i))
                {
                    Vector3 offset = (PerMapSnappingOffset[i]) * extentScale / context.Extents + 0.5f;
                    PerMapOffsetScale[i] = new Vector4(offset, (1.0f / context.Extents.X) * extentScale);
                }

                if (i + 1 == ClipMapCurrent || ShouldUpdateClipIndex(i))
                {
                    MippingOffset[i] = (Vector3)((MippingOffsetTranslation[i] - MippingOffsetTranslation[i + 1]) / voxelScale);
                }

                if (i < ClipMapCount - 1)
                {
                    extentScale /= 2;
                }
                voxelScale *= 2;
            }
        }