Пример #1
0
 public VoxelUpdateInfo(VoxelUpdateInfo super, byte xi, byte yi, byte zi)
     : this()
 {
     size = super.size / VoxelBlock.CHILD_DIMENSION;
     detailLevel = (byte)(super.detailLevel + 1);
     control = super.control;
     x = super.x * VoxelBlock.CHILD_DIMENSION + xi;
     y = super.y * VoxelBlock.CHILD_DIMENSION + yi;
     z = super.z * VoxelBlock.CHILD_DIMENSION + zi;
     for (byte xii = 0; xii < DIMENSION; ++xii) {
         for (byte yii = 0; yii < DIMENSION; ++yii) {
             for (byte zii = 0; zii < DIMENSION; ++zii) {
                 blocks[xii, yii, zii] = super.getSub(VoxelBlock.CHILD_COUNT_POWER, (byte)(xii + xi + VoxelBlock.CHILD_DIMENSION - 1), (byte)(yii + yi + VoxelBlock.CHILD_DIMENSION - 1), (byte)(zii + zi + VoxelBlock.CHILD_DIMENSION - 1));
                 renderers[xii, yii, zii] = super.renderers[(int)((xii + xi + 1) * 0.5), (int)((yii + yi + 1) * 0.5), (int)((zii + zi + 1) * 0.5)];
                 if (renderers[xii, yii, zii] == null || renderers[xii, yii, zii].old) {
                     renderers[xii, yii, zii] = super.blocks[(int)((xii + xi + 1) * 0.5), (int)((yii + yi + 1) * 0.5), (int)((zii + zi + 1) * 0.5)].getRenderer(0, 0, 0, 0);
                     if (renderers[xii, yii, zii] == null || renderers[xii, yii, zii].old)
                         renderers[xii, yii, zii] = blocks[xii, yii, zii].getRenderer(0, 0, 0, 0);
                     if (renderers[xii, yii, zii] != null && renderers[xii, yii, zii].old)
                         renderers[xii, yii, zii] = null;
                 }
             }
         }
     }
 }
Пример #2
0
        public VoxelRenderer(VoxelIndex index, VoxelTree control)
            : this(index, control, new Vector3(
				index.x * control.sizes[index.depth],
				index.y * control.sizes[index.depth],
				index.z * control.sizes[index.depth]))
        {
        }
Пример #3
0
        public void Test() {
            var voxelTree = new VoxelTree(Vector3.zero, Vector3.one * size);

            voxelTree.GetRoot().AddChild(OctreeNode.ChildIndex.LeftAboveBack).SetItem(4);
            voxelTree.GetRoot().AddChild(OctreeNode.ChildIndex.RightAboveForward).SetItem(5);


        }
Пример #4
0
 public CubeModifier(VoxelTree control, Vector3 worldPosition, Vector3 worldDimensions, VoxelHolder value, bool updateMesh)
     : base(control, updateMesh)
 {
     this.value = value;
     Vector3 dimensions = worldDimensions / control.voxelSize();
     min = control.transform.InverseTransformPoint(worldPosition) / control.voxelSize() - dimensions /2 - Vector3.one * (control.voxelSize() / 2);
     max = min + dimensions;
     apply();
 }
Пример #5
0
 public SphereModifier(VoxelTree control, Vector3 worldPosition, float worldRadius, VoxelHolder value, bool updateMesh)
     : base(control, updateMesh)
 {
     this.value = value;
     Vector3 radiusCube = new Vector3(worldRadius, worldRadius, worldRadius) / control.voxelSize();
     min = control.transform.InverseTransformPoint(worldPosition) / control.voxelSize() - radiusCube - Vector3.one * (control.voxelSize() / 2);
     max = min + radiusCube * 2;
     apply();
 }
Пример #6
0
 public SphereModifier(VoxelTree control, Vector3 worldPosition, float worldRadius, VoxelHolder value, bool updateMesh)
     : base(control, updateMesh)
 {
     this.value = value;
     Vector3 radiusCube = new Vector3(worldRadius, worldRadius, worldRadius) / control.voxelSize();
     Vector3 min = control.transform.InverseTransformPoint(worldPosition) / control.voxelSize() - radiusCube -Vector3.one *0.5f;
     Vector3 max = min + radiusCube * 2;
     center = (min + max) / 2;
     radius = center.x - min.x;
     minDis = (radius - 1);
     maxDis = (radius + 1);
     setMinMax(min, max);
     //			apply();
 }
Пример #7
0
 public VoxelRenderer(VoxelIndex index, VoxelTree control, Vector3 localPosition)
 {
     this.index = index;
     this.position = localPosition;
     this.control = control;
     size = 0;
     ++rendCount;
     VERTS = new Vector3[0];
     NORMS = new Vector3[0];
     TRIS = new int[0];
     lock(control.renderers) {
         control.renderers[index] = this;
     }
 }
Пример #8
0
 public VoxelUpdateInfo(float size, VoxelHolder main, VoxelTree control)
     : this()
 {
     this.size = size;
     this.detailLevel = 0;
     this.control = control;
     x = y = z = 0;
     for (byte xi = 0; xi < DIMENSION; ++xi) {
         for (byte yi = 0; yi < DIMENSION; ++yi) {
             for (byte zi = 0; zi < DIMENSION; ++zi) {
                 blocks[xi, yi, zi] = Voxel.empty;
             }
         }
     }
     blocks[1, 1, 1] = main;
 }
Пример #9
0
 public GenMeshJob(VoxelBlock block, VoxelTree control, byte detailLevel)
 {
     this.block = block;
     this.control = control;
     this.detailLevel = detailLevel;
 }
Пример #10
0
 public UpdateCheckJob(VoxelBlock block, VoxelTree control, byte detailLevel)
 {
     this.block = block;
     this.control = control;
     this.detailLevel = detailLevel;
     control.addUpdateCheckJob();
 }
Пример #11
0
 public LinkRenderersJob(VoxelTree control)
 {
     this.control = control;
 }
Пример #12
0
 public void setFromSister(VoxelUpdateInfo sister, byte xi, byte yi, byte zi)
 {
     size = sister.size;
     detailLevel = sister.detailLevel;
     control = sister.control;
     x = sister.x + xi - 1;
     y = sister.y + yi - 1;
     z = sister.z + zi - 1;
     for (byte xii = (byte)(1 - (xi + 1) / 2); xii < DIMENSION - (xi / 2); ++xii) {
         for (byte yii = (byte)(1 - (yi + 1) / 2); yii < DIMENSION - (yi / 2); ++yii) {
             for (byte zii = (byte)(1 - (zi + 1) / 2); zii < DIMENSION - (zi / 2); ++zii) {
                 blocks[xii, yii, zii] = sister.blocks[xii + xi - 1, yii + yi - 1, zii + zi - 1];
                 renderers[xii, yii, zii] = sister.renderers[xii + xi - 1, yii + yi - 1, zii + zi - 1];
             }
         }
     }
 }
Пример #13
0
        public void clearSuperRenderers(byte detailLevel, int x, int y, int z, VoxelTree control)
        {
            if (detailLevel > 0) {
                short factor = (short)(1 << (detailLevel - CHILD_COUNT_POWER));
                byte xi = (byte)(x / factor);
                byte yi = (byte)(y / factor);
                byte zi = (byte)(z / factor);

                if (renderer != null && filledWithSubRenderers(false)) {
                    control.enqueueJob(new DropRendererJob(renderer));
                    renderer = null;
                }
                ((VoxelBlock)children[xi, yi, zi]).clearSuperRenderers((byte)(detailLevel - CHILD_COUNT_POWER), x - xi * factor, y - yi * factor, z - zi * factor, control);
            }
        }
Пример #14
0
        public void setToHeightmap(byte detailLevel, int x, int y, int z, ref float[,] map, byte material, VoxelTree control)
        {
            if (detailLevel <= CHILD_COUNT_POWER) {
                for (int xi = 0; xi < CHILD_DIMENSION; ++xi) {
                    for (int zi = 0; zi < CHILD_DIMENSION; ++zi) {
                        for (int yi = 0; yi < CHILD_DIMENSION; ++yi) {
                            if (yi + y >= map[x + xi, z + zi])
                                break;
                            else if (material == byte.MaxValue) {
                                children[xi, yi, zi] = Voxel.empty;
                            } else {
                                if (yi + y >= map[x + xi, z + zi] - 1) {
                                    byte opacity = (byte)((map[x + xi, z + zi] - yi - y) * byte.MaxValue);
                                    if (opacity > control.isoLevel || children[xi, yi, zi].averageOpacity() <= opacity)
                                        children[xi, yi, zi] = new Voxel(material, opacity);
                                } else {
                                    children[xi, yi, zi] = new Voxel(material, byte.MaxValue);
                                }
                            }
                        }
                    }
                }
            } else {
                int multiplier = (1 << (detailLevel - CHILD_COUNT_POWER));
                for (int xi = 0; xi < CHILD_DIMENSION; ++xi) {
                    for (int zi = 0; zi < CHILD_DIMENSION; ++zi) {
                        int xMax = x + (xi + 1) * multiplier;
                        int zMax = z + (zi + 1) * multiplier;
                        float yMin = float.MaxValue;
                        float yMax = 0;
                        for (int xPos = x + xi * multiplier; xPos < xMax; ++xPos) {
                            for (int zPos = z + zi * multiplier; zPos < zMax; ++zPos) {
                                if (map[xPos, zPos] < yMin) yMin = map[xPos, zPos];
                                if (map[xPos, zPos] > yMax) yMax = map[xPos, zPos];
                            }
                        }

                        int firstUnsolidBlock = Mathf.Min(((int)(yMin - y)) / multiplier, CHILD_DIMENSION);
                        int lastUnsolidBlock = Mathf.Min(((int)(yMax - y)) / multiplier, CHILD_DIMENSION - 1);
                        int yi = 0;
                        for (; yi < firstUnsolidBlock; ++yi) {
                            if (material == byte.MaxValue)
                                children[xi, yi, zi] = Voxel.empty;
                            else
                                children[xi, yi, zi] = new Voxel(material, byte.MaxValue);
                        }
                        if (lastUnsolidBlock < 0) continue;
                        for (; yi <= lastUnsolidBlock; ++yi) {
                            if (children[xi, yi, zi].GetType() == typeof(Voxel))
                                children[xi, yi, zi] = new VoxelBlock((Voxel)children[xi, yi, zi]);
                            ((VoxelBlock)children[xi, yi, zi]).setToHeightmap((byte)(detailLevel - CHILD_COUNT_POWER), x + xi * multiplier, y + yi * multiplier, z + zi * multiplier, ref map, material, control);
                        }
                    }
                }
            }
            control.dirty = true;
        }
Пример #15
0
 public void clearSubRenderers(bool clearSelf, VoxelTree control)
 {
     if (clearSelf && renderer != null) {
         //control.enqueueJob(new DropRendererJob(renderer));
         renderer.clear();
         renderer = null;
         return;
     }
     for (byte xi = 0; xi < CHILD_DIMENSION; ++xi) {
         for (byte yi = 0; yi < CHILD_DIMENSION; ++yi) {
             for (byte zi = 0; zi < CHILD_DIMENSION; ++zi) {
                 if (children[xi, yi, zi].GetType() != typeof(Voxel))
                     ((VoxelBlock)children[xi, yi, zi]).clearSubRenderers(true, control);
             }
         }
     }
 }
Пример #16
0
 public void clearSubRenderers(VoxelTree control)
 {
     clearSubRenderers(true, control);
 }
Пример #17
0
        public void NeighboursTest()
        {
            var grandChildCoords = new Coords(new[] {
                new OctreeChildCoords(1, 1, 1),
                new OctreeChildCoords(0, 1, 0)
            });

            Assert.AreEqual(2, grandChildCoords.Length);
            Assert.AreEqual(new OctreeChildCoords(1, 1, 1), grandChildCoords.GetCoord(0));
            Assert.AreEqual(new OctreeChildCoords(0, 1, 0), grandChildCoords.GetCoord(1));
            var neighbourCoords = VoxelTree.GetNeighbourCoords(grandChildCoords, NeighbourSide.Right);

            if (neighbourCoords != null)
            {
                var rightOfGrandChildCoords = neighbourCoords.Value;

                Assert.AreEqual(2, rightOfGrandChildCoords.Length);

                Assert.AreEqual(new OctreeChildCoords(1, 1, 1), rightOfGrandChildCoords.GetCoord(0));
                Assert.AreEqual(new OctreeChildCoords(1, 1, 0), rightOfGrandChildCoords.GetCoord(1));

                Assert.NotNull(VoxelTree.GetNeighbourCoords(rightOfGrandChildCoords, NeighbourSide.Left));

                Assert.AreNotEqual(VoxelTree.GetNeighbourCoords(rightOfGrandChildCoords, NeighbourSide.Left),
                                   rightOfGrandChildCoords);

                var furtherRightCoords = VoxelTree.GetNeighbourCoords(rightOfGrandChildCoords, NeighbourSide.Right);

                // uh oh, we just went out of bounds
//          Assert.AreEqual(2, furtherRightCoords.Length);
                Assert.IsNull(furtherRightCoords);
            }

//            Assert.AreEqual(new OctreeChildCoords(0, 1, 1), furtherRightCoords.GetCoord(0));
//            Assert.AreEqual(new OctreeChildCoords(0, 1, 0), furtherRightCoords.GetCoord(1));

//            furtherRightCoords = VoxelNode.GetNeighbourCoords(furtherRightCoords, NeighbourSide.Right);

//            Assert.IsNotNull(furtherRightCoords, "furtherLeftCoords");
            // not null because it will just look at another voxelTree instead

            var coords = VoxelTree.GetNeighbourCoords(grandChildCoords, NeighbourSide.Left);

            if (coords != null)
            {
                var leftOfGrandChildCoords = coords.Value;

                Assert.AreEqual(VoxelTree.GetNeighbourCoords(leftOfGrandChildCoords, NeighbourSide.Right), grandChildCoords);


                Assert.AreEqual(2, leftOfGrandChildCoords.Length);
                // and back again
                Assert.AreEqual(new OctreeChildCoords(0, 1, 1), leftOfGrandChildCoords.GetCoord(0));
                Assert.AreEqual(new OctreeChildCoords(1, 1, 0), leftOfGrandChildCoords.GetCoord(1));
            }

            var aboveGrandChildCoords = VoxelTree.GetNeighbourCoords(grandChildCoords, NeighbourSide.Above);

//            Assert.IsNotNull(aboveGrandChildCoords); // not null, just another voxelTree!
            Assert.IsNull(aboveGrandChildCoords);             // null, oob

            coords = VoxelTree.GetNeighbourCoords(grandChildCoords, NeighbourSide.Below);
            if (coords != null)
            {
                var belowGrandChildCoords = coords.Value;
                Assert.AreEqual(VoxelTree.GetNeighbourCoords(belowGrandChildCoords, NeighbourSide.Above), grandChildCoords);

                Assert.AreEqual(2, belowGrandChildCoords.Length);

                Assert.AreEqual(new OctreeChildCoords(1, 1, 1), belowGrandChildCoords.GetCoord(0));
                Assert.AreEqual(new OctreeChildCoords(0, 0, 0), belowGrandChildCoords.GetCoord(1));

                coords = VoxelTree.GetNeighbourCoords(belowGrandChildCoords, NeighbourSide.Below);
                if (coords != null)
                {
                    var furterBelowGrandChildCoords = coords.Value;
                    Assert.AreEqual(VoxelTree.GetNeighbourCoords(furterBelowGrandChildCoords, NeighbourSide.Above),
                                    belowGrandChildCoords);

                    Assert.AreEqual(2, furterBelowGrandChildCoords.Length);

                    Assert.AreEqual(new OctreeChildCoords(1, 0, 1), furterBelowGrandChildCoords.GetCoord(0));
                    Assert.AreEqual(new OctreeChildCoords(0, 1, 0), furterBelowGrandChildCoords.GetCoord(1));
                }

                // 111, 010, behind, z-1
                // 110, 011
                coords = VoxelTree.GetNeighbourCoords(grandChildCoords, NeighbourSide.Back);
                if (coords != null)
                {
                    var behindGrandChildCoords = coords.Value;
                    Assert.AreEqual(VoxelTree.GetNeighbourCoords(behindGrandChildCoords, NeighbourSide.Forward),
                                    grandChildCoords);

                    Assert.AreEqual(2, belowGrandChildCoords.Length);

                    Assert.AreEqual(new OctreeChildCoords(1, 1, 0), behindGrandChildCoords.GetCoord(0));
                    Assert.AreEqual(new OctreeChildCoords(0, 1, 1), behindGrandChildCoords.GetCoord(1));
                }

                coords = VoxelTree.GetNeighbourCoords(grandChildCoords, NeighbourSide.Forward);
                if (coords != null)
                {
                    var inFrontOfGrandChildCoords = coords.Value;
                    Assert.AreEqual(VoxelTree.GetNeighbourCoords(inFrontOfGrandChildCoords, NeighbourSide.Back),
                                    grandChildCoords);

                    Assert.AreEqual(2, belowGrandChildCoords.Length);

                    Assert.AreEqual(new OctreeChildCoords(1, 1, 1), inFrontOfGrandChildCoords.GetCoord(0));
                    Assert.AreEqual(new OctreeChildCoords(0, 1, 1), inFrontOfGrandChildCoords.GetCoord(1));
                }
            }

            const int incorrectSide = 6;

            Assert.Throws <ArgumentOutOfRangeException>(
                () => VoxelTree.GetNeighbourCoords(grandChildCoords, (NeighbourSide)incorrectSide));
            Assert.Throws <ArgumentOutOfRangeException>(
                () => VoxelTree.GetNeighbourCoords(grandChildCoords, (NeighbourSide)incorrectSide + 1));

            var rootCoords = new Coords();

            Assert.IsNull(VoxelTree.GetNeighbourCoords(rootCoords, NeighbourSide.Above));
            Assert.IsNull(VoxelTree.GetNeighbourCoords(rootCoords, NeighbourSide.Below));
            Assert.IsNull(VoxelTree.GetNeighbourCoords(rootCoords, NeighbourSide.Forward));
            Assert.IsNull(VoxelTree.GetNeighbourCoords(rootCoords, NeighbourSide.Back));
            Assert.IsNull(VoxelTree.GetNeighbourCoords(rootCoords, NeighbourSide.Right));
            Assert.IsNull(VoxelTree.GetNeighbourCoords(rootCoords, NeighbourSide.Left));
        }
Пример #18
0
 public static bool isRenderLod(float x, float y, float z, float size, VoxelTree control)
 {
     if (!control.useLod)
         return size == control.sizes[control.maxDetail];
     return getDistSquare(control.getLocalCamPosition(), new Vector3(x + 0.5f, y + 0.5f, z + 0.5f), size) >= size * size * control.getLodDetail();
 }
Пример #19
0
        public void updateAll(int x, int y, int z, byte detailLevel, VoxelTree control, bool force = false)
        {
            // check if this is a high enough detail level.  If not, call the childrens' update methods
            if (!isRenderSize(control.sizes[detailLevel], control) && (!isRenderLod(x, y, z, control.sizes[detailLevel], control))) {
                for (byte xi = 0; xi < CHILD_DIMENSION; ++xi) {
                    for (byte yi = 0; yi < CHILD_DIMENSION; ++yi) {
                        for (byte zi = 0; zi < CHILD_DIMENSION; ++zi) {
                            //VoxelUpdateInfo childInfo = new VoxelUpdateInfo(info, xi, yi, zi);
                            if (children[xi, yi, zi].GetType() == typeof(Voxel)) {
                                //if (!childInfo.isSolid())
                                children[xi, yi, zi] = new VoxelBlock((Voxel)children[xi, yi, zi]);
                                //else
                                //continue;
                            }
                            UpdateCheckJob job = new UpdateCheckJob((VoxelBlock)children[xi, yi, zi], control, (byte)(detailLevel + 1));
                            job.setOffset((byte)(x * CHILD_DIMENSION + xi), (byte)(y * CHILD_DIMENSION + yi), (byte)(z * CHILD_DIMENSION + zi));
                            control.enqueueCheck(job);
                        }
                    }
                }
                if (renderer != null) {
                    //GameObject.Destroy(renderer.ob);
                    //lock (myControl) {
                    //	myControl.enqueueJob(new DropRendererJob(renderer));
                    //	renderer = null;
                    //}
                    renderer.old = true;
                }
                return;
            }

            // check if we already have a mesh
            if (renderer == null) {
                //clearSubRenderers();
                renderer = new VoxelRenderer(new VoxelIndex(x, y, z, detailLevel), control);
                //info.renderers[1, 1, 1] = renderer;
            } else {
                renderer.old = false;
                if (!force) return;
            }

            // We should generate a mesh
            GenMeshJob updateJob = new GenMeshJob(this, control, detailLevel);
            updateJob.setOffset(x, y, z);
            control.enqueueUpdate(updateJob);
        }
Пример #20
0
        public void setToHeightmap(byte detailLevel, int x, int y, int z, ref float[,] map, byte[,] mats, VoxelTree control)
        {
            if (detailLevel <= CHILD_COUNT_POWER) {
                for (int xi = 0; xi < CHILD_DIMENSION; ++xi) {
                    for (int zi = 0; zi < CHILD_DIMENSION; ++zi) {
                        for (int yi = 0; yi < CHILD_DIMENSION; ++yi) {
                            if (yi + y >= map[x + xi, z + zi])
                                break;
                            else if (yi + y >= map[x + xi, z + zi] - 1) {
                                if (mats[x + xi, z + zi] == byte.MaxValue)
                                    children[xi, yi, zi] = Voxel.empty;
                                else
                                    children[xi, yi, zi] = new Voxel(mats[x + xi, z + zi], (byte)((map[x + xi, z + zi] - yi - y) * byte.MaxValue));
                            } else {
                                if (mats[x + xi, z + zi] == byte.MaxValue)
                                    children[xi, yi, zi] = Voxel.empty;
                                else
                                    children[xi, yi, zi] = new Voxel(mats[x + xi, z + zi], byte.MaxValue);
                            }
                        }
                    }
                }
            } else {
                int multiplier = (1 << (detailLevel - CHILD_COUNT_POWER));
                for (int xi = 0; xi < CHILD_DIMENSION; ++xi) {
                    for (int zi = 0; zi < CHILD_DIMENSION; ++zi) {
                        int xMax = x + (xi + 1) * multiplier;
                        int zMax = z + (zi + 1) * multiplier;
                        float yMin = float.MaxValue;
                        float yMax = 0;
                        bool multipleMaterials = false;
                        byte material = mats[x, z];
                        for (int xPos = x + xi * multiplier; xPos < xMax; ++xPos) {
                            for (int zPos = z + zi * multiplier; zPos < zMax; ++zPos) {
                                if (map[xPos, zPos] < yMin) yMin = map[xPos, zPos];
                                if (map[xPos, zPos] > yMax) yMax = map[xPos, zPos];
                                if (mats[xPos, zPos] != material) multipleMaterials = true;
                            }
                        }

                        if (multipleMaterials) yMin = 0;
                        int firstUnsolidBlock = Mathf.Min(((int)(yMin - y)) / multiplier, CHILD_DIMENSION);
                        int lastUnsolidBlock = Mathf.Min(((int)(yMax - y)) / multiplier, CHILD_DIMENSION - 1);
                        int yi = 0;
                        for (; yi < firstUnsolidBlock; ++yi) {
                            if (mats[x + xi * multiplier, z + zi * multiplier] == byte.MaxValue)
                                children[xi, yi, zi] = Voxel.empty;
                            else
                                children[xi, yi, zi] = new Voxel(mats[x + xi * multiplier, z + zi * multiplier], byte.MaxValue);
                        }
                        if (lastUnsolidBlock < 0) continue;
                        for (; yi <= lastUnsolidBlock; ++yi) {
                            VoxelBlock newChild = new VoxelBlock();
                            newChild.setToHeightmap((byte)(detailLevel - CHILD_COUNT_POWER), x + xi * multiplier, y + yi * multiplier, z + zi * multiplier, ref map, mats, control);
                            children[xi, yi, zi] = newChild;
                        }
                    }
                }
            }
            control.dirty = true;
        }
Пример #21
0
 protected Modifier(VoxelTree control, bool updateMesh)
 {
     this.control = control;
     this.updateMesh = updateMesh;
 }
Пример #22
0
 public static bool isRenderSize(float size, VoxelTree control)
 {
     return control.sizes[control.maxDetail - VoxelRenderer.VOXEL_COUNT_POWER] == size;
 }
Пример #23
0
 public void set(byte detailLevel, int x, int y, int z, Voxel value, VoxelTree control)
 {
     if (detailLevel > 0) {
         short factor = (short)(1 << (detailLevel - CHILD_COUNT_POWER));
         byte xi = (byte)(x / factor);
         byte yi = (byte)(y / factor);
         byte zi = (byte)(z / factor);
         if (detailLevel == CHILD_COUNT_POWER) {
             children[xi, yi, zi] = value;
         } else {
             if (children[xi, yi, zi].GetType() == typeof(Voxel)) {
                 if (children[xi, yi, zi].Equals(value)) { ++skippedSubdivisions; return; }
                 children[xi, yi, zi] = new VoxelBlock((Voxel)children[xi, yi, zi]);
             }
             ((VoxelBlock)children[xi, yi, zi]).set((byte)(detailLevel - CHILD_COUNT_POWER), x - xi * factor, y - yi * factor, z - zi * factor, value, control);
         }
     } else
         set(value);
 }