/// <summary>
        /// if area is null then area is retained
        /// </summary>
        public void MakeHole(List <ShapeDataAbstract> shapes, Area area)
        {
            bool appyArea = area != null;

            byte areaValue = 0;

            if (appyArea)
            {
                areaValue = GetAreaValue(area);
            }

            for (int i = 0; i < shapes.Count; i++)
            {
                DataCompact[] compactData = TakeCompactData();
                var           shape       = shapes[i];

                if (shape is ShapeDataSphere)
                {
                    ShapeDataSphere castedShape = shape as ShapeDataSphere;
                    AppendSpherePrivate(compactData, castedShape.bounds.center, castedShape.bounds.extents.x, 0f, false, true);
                }
                else if (shape is ShapeDataCapsule)
                {
                    ShapeDataCapsule castedShape = shape as ShapeDataCapsule;
                    AppendCapsulePrivate(compactData, castedShape.sphereA, castedShape.sphereB, castedShape.capsileRadius, true, areaValue);
                }
                else if (shape is ShapeDataBox)
                {
                    ShapeDataBox castedShape = shape as ShapeDataBox;
                    AppendMeshConvexPrivate(compactData, ColliderCollector.cubeVerts, ColliderCollector.cubeTris, castedShape.boxMatrix, areaValue, true);
                    //Debug.LogWarning("dont forget to flip Y in ShapeDataBox");
                }
                else
                {
                    Debug.LogError("no support for current area modifyer in shape collector");
                }

                Vector3 realChunkPos = template.realOffsetedPosition;
                Vector3 offset       = template.halfVoxelOffset;

                for (int x = 0; x < sizeX; x++)
                {
                    for (int z = 0; z < sizeZ; z++)
                    {
                        int         index = GetIndex(x, z);
                        DataCompact mask  = compactData[index];

                        if (mask.pass != -1 && arrayData[index].next != -2)
                        {
                            int prevIndex = -1;
                            int curIndex  = index;

                            while (true)
                            {
                                if (curIndex < 0)
                                {
                                    break;
                                }

                                Data curNode = arrayData[curIndex];

                                if ((mask.min > curNode.max | mask.max < curNode.min) == false)   //if current mask in not higher or lower than current node
                                {
                                    if (SomeMath.InRangeExclusive(mask.min, curNode.min, curNode.max))
                                    {
                                        arrayData[curIndex].max  = mask.min;
                                        arrayData[curIndex].pass = mask.pass;
                                        if (appyArea)
                                        {
                                            arrayData[curIndex].area = areaValue;
                                        }

                                        if (SomeMath.InRangeExclusive(mask.max, curNode.min, curNode.max))
                                        {
                                            int freeIndex = GetFreeIndex();
                                            arrayData[freeIndex]     = new Data(mask.max, curNode.max, curNode.pass, curNode.next, curNode.area);
                                            arrayData[curIndex].next = freeIndex;
                                            break;
                                        }
                                        prevIndex = curIndex;
                                        curIndex  = arrayData[curIndex].next;
                                    }
                                    else if (SomeMath.InRangeExclusive(mask.max, curNode.min, curNode.max))
                                    {
                                        //top of mask inside current shape
                                        arrayData[curIndex].min = mask.max;
                                        break;//this nothing can be intersected after that
                                    }
                                    else
                                    {
                                        if (curNode.next == -1)                //if there no nodes after that
                                        {
                                            if (prevIndex == -1)               //if it first node
                                            {
                                                arrayData[curIndex].next = -2; //make it invalid
                                            }
                                            else
                                            {
                                                arrayData[prevIndex].next = -1;
                                            }
                                            break;
                                        }
                                        //if some nodes after that
                                        else
                                        {
                                            //shift next thata to current index
                                            //NOTE: curIndex is not changed to check new data at this index
                                            ReturnFreeIndex(curNode.next);
                                            arrayData[curIndex] = arrayData[curNode.next];

                                            continue;
                                        }
                                    }
                                }

                                prevIndex = curIndex;
                                curIndex  = curNode.next;
                            }
                        }
                    }
                }
                GenericPoolArray <DataCompact> .ReturnToPool(ref compactData);
            }
        }