Beispiel #1
0
        public void ReadRange(MyStorageData target, MyStorageDataTypeFlags dataToRead, int lodIndex, ref Vector3I lodVoxelRangeMin, ref Vector3I lodVoxelRangeMax, ref MyVoxelRequestFlags requestFlags)
        {
            ProfilerShort.Begin(GetType().Name + ".ReadRange");

            if ((dataToRead & MyStorageDataTypeFlags.Content) != 0)
            {
                target.ClearContent(0);
            }

            if (requestFlags.HasFlags(MyVoxelRequestFlags.AdviseCache) && lodIndex == 0 && CachedWrites)
            {
                ReadRangeAdviseCache(target, dataToRead, ref lodVoxelRangeMin, ref lodVoxelRangeMax);
                ProfilerShort.End();
                return;
            }

            if (CachedWrites && lodIndex <= VoxelChunk.SizeBits && m_cachedChunks.Count > 0)
            {
                // read occlusion separate
                if (dataToRead.Requests(MyStorageDataTypeEnum.Occlusion))
                {
                    using (m_storageLock.AcquireSharedUsing())
                        ReadRangeInternal(target, ref Vector3I.Zero, MyStorageDataTypeFlags.Occlusion, lodIndex, ref lodVoxelRangeMin, ref lodVoxelRangeMax, ref requestFlags);

                    dataToRead ^= MyStorageDataTypeFlags.Occlusion;
                }

                if (m_tmpChunks == null) m_tmpChunks = new List<VoxelChunk>();

                var lodDiff = VoxelChunk.SizeBits - lodIndex;

                // We fetch which chunks overlap our current range from the chunk tree, then we read all data from storage and apply those changes
                var querybb = new BoundingBox(lodVoxelRangeMin << lodIndex, lodVoxelRangeMax << lodIndex);

                using (m_cacheLock.AcquireSharedUsing())
                    m_cacheMap.OverlapAllBoundingBox(ref querybb, m_tmpChunks, 0, false);

                if (m_tmpChunks.Count > 0)
                {
                    var chunkMin = lodVoxelRangeMin >> lodDiff;
                    var chunkMax = lodVoxelRangeMax >> lodDiff;

                    bool readFromStorage = false;

                    if ((chunkMax - chunkMin + 1).Size > m_tmpChunks.Count)
                    {
                        using (m_storageLock.AcquireSharedUsing())
                            ReadRangeInternal(target, ref Vector3I.Zero, dataToRead, lodIndex, ref lodVoxelRangeMin, ref lodVoxelRangeMax, ref requestFlags);
                        readFromStorage = true;
                    }

                    for (int i = 0; i < m_tmpChunks.Count; ++i)
                    {
                        var chunk = m_tmpChunks[i];
                        var pos = chunk.Coords;

                        var celPos = pos << lodDiff;

                        var lodCkStart = pos << lodDiff;
                        lodCkStart = Vector3I.Max(lodCkStart, lodVoxelRangeMin);

                        var targetOffset = lodCkStart - lodVoxelRangeMin;

                        var lodCkEnd = ((pos + 1) << lodDiff) - 1;
                        lodCkEnd = Vector3I.Min(lodCkEnd, lodVoxelRangeMax);

                        lodCkStart -= celPos;
                        lodCkEnd -= celPos;

                        if ((chunk.Cached & dataToRead) != dataToRead && !readFromStorage)
                        {
                            using (m_storageLock.AcquireSharedUsing())
                                if ((chunk.Cached & dataToRead) != dataToRead)
                                    ReadDatForChunk(chunk, dataToRead);
                        }

                        using (chunk.Lock.AcquireSharedUsing())
                            chunk.ReadLod(target, !readFromStorage ? dataToRead : dataToRead & chunk.Cached, ref targetOffset, lodIndex, ref lodCkStart, ref lodCkEnd);
                    }

                    m_tmpChunks.Clear();
                    ProfilerShort.End();
                    return;
                }
            }

            // all else
            using (m_storageLock.AcquireSharedUsing())
                ReadRangeInternal(target, ref Vector3I.Zero, dataToRead, lodIndex, ref lodVoxelRangeMin, ref lodVoxelRangeMax, ref requestFlags);

            ProfilerShort.End();
        }
        public MyIsoMesh Precalc(IMyStorage storage, int lod, Vector3I voxelStart, Vector3I voxelEnd, bool generateMaterials, bool useAmbient, bool doNotCheck = false)
        {
            // change range so normal can be computed at edges (expand by 1 in all directions)
            voxelStart -= 1;
            voxelEnd   += 1;

            if (storage == null)
            {
                return(null);
            }

            using (storage.Pin())
            {
                if (storage.Closed)
                {
                    return(null);
                }

                MyVoxelRequestFlags request = MyVoxelRequestFlags.ContentChecked; // | (doNotCheck ? MyVoxelRequestFlags.DoNotCheck : 0);
                //if (lod == 0 && generateMaterials) request |= MyVoxelRequestFlags.AdviseCache;

                bool readAmbient = false;

                if (generateMaterials && storage.DataProvider != null && storage.DataProvider.ProvidesAmbient)
                {
                    readAmbient = true;
                }

                m_cache.Resize(voxelStart, voxelEnd);
                if (readAmbient)
                {
                    m_cache.StoreOcclusion = true;
                }

                storage.ReadRange(m_cache, MyStorageDataTypeFlags.Content, lod, ref voxelStart, ref voxelEnd, ref request);

                if (request.HasFlags(MyVoxelRequestFlags.EmptyContent) || request.HasFlags(MyVoxelRequestFlags.FullContent) ||
                    (!request.HasFlags(MyVoxelRequestFlags.ContentChecked) && !m_cache.ContainsIsoSurface()))
                {
                    //if(generateMaterials && lod == 0) Debugger.Break();
                    //storage.DebugDrawChunk(voxelStart, voxelEnd);
                    return(null);
                }

                var    center           = (storage.Size / 2) * MyVoxelConstants.VOXEL_SIZE_IN_METRES;
                var    voxelSize        = MyVoxelConstants.VOXEL_SIZE_IN_METRES * (1 << lod);
                var    vertexCellOffset = voxelStart - AffectedRangeOffset;
                double numCellsHalf     = 0.5 * (m_cache.Size3D.X - 3);
                var    posOffset        = ((Vector3D)vertexCellOffset + numCellsHalf) * (double)voxelSize;

                if (generateMaterials)
                {
                    // 255 is the new black
                    m_cache.ClearMaterials(255);
                }

                if (readAmbient)
                {
                    m_cache.Clear(MyStorageDataTypeEnum.Occlusion, 0);
                }

                IsoMesher mesher = new IsoMesher();
                ProfilerShort.Begin("Dual Contouring");
                unsafe
                {
                    fixed(byte *content = m_cache[MyStorageDataTypeEnum.Content])
                    fixed(byte *material = m_cache[MyStorageDataTypeEnum.Material])
                    {
                        var size3d = m_cache.Size3D;

                        Debug.Assert(size3d.X == size3d.Y && size3d.Y == size3d.Z);
                        mesher.Calculate(size3d.X, content, material, m_buffer, useAmbient, posOffset - center);
                    }
                }

                if (generateMaterials)
                {
                    request = 0;

                    request |= MyVoxelRequestFlags.SurfaceMaterial;
                    request |= MyVoxelRequestFlags.ConsiderContent;

                    var req = readAmbient ? MyStorageDataTypeFlags.Material | MyStorageDataTypeFlags.Occlusion : MyStorageDataTypeFlags.Material;

                    storage.ReadRange(m_cache, req, lod, ref voxelStart, ref voxelEnd, ref request);

                    FixCacheMaterial(voxelStart, voxelEnd);
                    unsafe
                    {
                        fixed(byte *content = m_cache[MyStorageDataTypeEnum.Content])
                        fixed(byte *material = m_cache[MyStorageDataTypeEnum.Material])
                        {
                            int materialOverride = request.HasFlags(MyVoxelRequestFlags.OneMaterial) ? m_cache.Material(0) : -1;
                            var size3d           = m_cache.Size3D;

                            Debug.Assert(size3d.X == size3d.Y && size3d.Y == size3d.Z);

                            if (readAmbient)
                                fixed(byte *ambient = m_cache[MyStorageDataTypeEnum.Occlusion])
                                mesher.CalculateMaterials(size3d.X, content, material, ambient, materialOverride);
                            else
                            {
                                mesher.CalculateMaterials(size3d.X, content, material, null, materialOverride);
                            }
                        }
                    }
                }
                else
                {
                    m_cache.ClearMaterials(0);
                }

                mesher.Finish(m_buffer);
                ProfilerShort.End();

                if (m_buffer.VerticesCount == 0 || m_buffer.Triangles.Count == 0)
                {
                    return(null);
                }

                ProfilerShort.Begin("Geometry post-processing");
                {
                    var positions   = m_buffer.Positions.GetInternalArray();
                    var vertexCells = m_buffer.Cells.GetInternalArray();
                    var materials   = m_buffer.Materials.GetInternalArray();
                    var ambients    = m_buffer.Ambient.GetInternalArray();
                    for (int i = 0; i < m_buffer.VerticesCount; i++)
                    {
                        Debug.Assert(positions[i].IsInsideInclusive(ref Vector3.MinusOne, ref Vector3.One));
                        vertexCells[i] += vertexCellOffset;
                        Debug.Assert(vertexCells[i].IsInsideInclusive(voxelStart + 1, voxelEnd - 1));
                        Debug.Assert(materials[i] != MyVoxelConstants.NULL_MATERIAL);
                        Debug.Assert(ambients[i] >= 0 && ambients[i] <= 1);
                    }

                    m_buffer.PositionOffset = posOffset;
                    m_buffer.PositionScale  = new Vector3((float)(numCellsHalf * voxelSize));
                    m_buffer.CellStart      = voxelStart + 1;
                    m_buffer.CellEnd        = voxelEnd - 1;
                }
                ProfilerShort.End();

                // Replace filled mesh with new one.
                // This way prevents allocation of meshes which then end up empty.
                var buffer = m_buffer;
                m_buffer = new MyIsoMesh();
                return(buffer);
            }
        }
Beispiel #3
0
        public void ReadRange(MyStorageData target, MyStorageDataTypeFlags dataToRead, int lodIndex, ref Vector3I lodVoxelRangeMin, ref Vector3I lodVoxelRangeMax, ref MyVoxelRequestFlags requestFlags)
        {
            ProfilerShort.Begin(GetType().Name + ".ReadRange");

            if ((dataToRead & MyStorageDataTypeFlags.Content) != 0)
            {
                target.ClearContent(0);
            }

            if (requestFlags.HasFlags(MyVoxelRequestFlags.AdviseCache) && lodIndex == 0 && CachedWrites)
            {
                ReadRangeAdviseCache(target, dataToRead, ref lodVoxelRangeMin, ref lodVoxelRangeMax);
                ProfilerShort.End();
                return;
            }

            if (CachedWrites && lodIndex <= VoxelChunk.SizeBits && m_cachedChunks.Count > 0)
            {
                // read occlusion separate
                if (dataToRead.Requests(MyStorageDataTypeEnum.Occlusion))
                {
                    using (m_storageLock.AcquireSharedUsing())
                        ReadRangeInternal(target, ref Vector3I.Zero, MyStorageDataTypeFlags.Occlusion, lodIndex, ref lodVoxelRangeMin, ref lodVoxelRangeMax, ref requestFlags);

                    dataToRead ^= MyStorageDataTypeFlags.Occlusion;
                }

                if (m_tmpChunks == null)
                {
                    m_tmpChunks = new List <VoxelChunk>();
                }

                var lodDiff = VoxelChunk.SizeBits - lodIndex;

                // We fetch which chunks overlap our current range from the chunk tree, then we read all data from storage and apply those changes
                var querybb = new BoundingBox(lodVoxelRangeMin << lodIndex, lodVoxelRangeMax << lodIndex);

                using (m_cacheLock.AcquireSharedUsing())
                    m_cacheMap.OverlapAllBoundingBox(ref querybb, m_tmpChunks, 0, false);

                if (m_tmpChunks.Count > 0)
                {
                    var chunkMin = lodVoxelRangeMin >> lodDiff;
                    var chunkMax = lodVoxelRangeMax >> lodDiff;

                    bool readFromStorage = false;

                    if ((chunkMax - chunkMin + 1).Size > m_tmpChunks.Count)
                    {
                        using (m_storageLock.AcquireSharedUsing())
                            ReadRangeInternal(target, ref Vector3I.Zero, dataToRead, lodIndex, ref lodVoxelRangeMin, ref lodVoxelRangeMax, ref requestFlags);
                        readFromStorage = true;
                    }

                    for (int i = 0; i < m_tmpChunks.Count; ++i)
                    {
                        var chunk = m_tmpChunks[i];
                        var pos   = chunk.Coords;

                        var celPos = pos << lodDiff;

                        var lodCkStart = pos << lodDiff;
                        lodCkStart = Vector3I.Max(lodCkStart, lodVoxelRangeMin);

                        var targetOffset = lodCkStart - lodVoxelRangeMin;

                        var lodCkEnd = ((pos + 1) << lodDiff) - 1;
                        lodCkEnd = Vector3I.Min(lodCkEnd, lodVoxelRangeMax);

                        lodCkStart -= celPos;
                        lodCkEnd   -= celPos;

                        if ((chunk.Cached & dataToRead) != dataToRead && !readFromStorage)
                        {
                            using (m_storageLock.AcquireSharedUsing())
                                if ((chunk.Cached & dataToRead) != dataToRead)
                                {
                                    ReadDatForChunk(chunk, dataToRead);
                                }
                        }

                        using (chunk.Lock.AcquireSharedUsing())
                            chunk.ReadLod(target, !readFromStorage ? dataToRead : dataToRead & chunk.Cached, ref targetOffset, lodIndex, ref lodCkStart, ref lodCkEnd);
                    }

                    m_tmpChunks.Clear();
                    ProfilerShort.End();
                    return;
                }
            }

            // all else
            using (m_storageLock.AcquireSharedUsing())
                ReadRangeInternal(target, ref Vector3I.Zero, dataToRead, lodIndex, ref lodVoxelRangeMin, ref lodVoxelRangeMax, ref requestFlags);

            ProfilerShort.End();
        }
        private unsafe void ReadRange(
            MyStorageData target,
            ref Vector3I targetWriteOffset,
            MyStorageDataTypeFlags types,
            int treeHeight,
            Dictionary<UInt64, MyOctreeNode> nodes,
            Dictionary<UInt64, IMyOctreeLeafNode> leaves,
            int lodIndex,
            ref Vector3I minInLod,
            ref Vector3I maxInLod,
            ref MyVoxelRequestFlags flags)
        {
            int stackIdx = 0;
            int stackSize = MySparseOctree.EstimateStackSize(treeHeight);
            MyCellCoord* stack = stackalloc MyCellCoord[stackSize];
            MyCellCoord data = new MyCellCoord(treeHeight + LeafLodCount, ref Vector3I.Zero);
            stack[stackIdx++] = data;
            MyCellCoord cell = new MyCellCoord();

            var octreeType = types.Requests(MyStorageDataTypeEnum.Content) ? MyStorageDataTypeEnum.Content : MyStorageDataTypeEnum.Material;

            FillOutOfBounds(target, octreeType, ref targetWriteOffset, lodIndex, minInLod, maxInLod);

            while (stackIdx > 0)
            {
                Debug.Assert(stackIdx <= stackSize);
                data = stack[--stackIdx];

                cell.Lod = Math.Max(data.Lod - LeafLodCount, 0);
                cell.CoordInLod = data.CoordInLod;

                int lodDiff;
                IMyOctreeLeafNode leaf;
                if (leaves.TryGetValue(cell.PackId64(), out leaf))
                {
                    lodDiff = data.Lod - lodIndex;
                    var rangeMinInDataLod = minInLod >> lodDiff;
                    var rangeMaxInDataLod = maxInLod >> lodDiff;
                    if (data.CoordInLod.IsInsideInclusive(ref rangeMinInDataLod, ref rangeMaxInDataLod))
                    {
                        var nodePosInLod = data.CoordInLod << lodDiff;
                        var writeOffset = nodePosInLod - minInLod;
                        Vector3I.Max(ref writeOffset, ref Vector3I.Zero, out writeOffset);
                        writeOffset += targetWriteOffset;
                        var lodSizeMinusOne = new Vector3I((1 << lodDiff) - 1);

                        Vector3I minInLeaf = minInLod - nodePosInLod;
                        Vector3I maxInLeaf = maxInLod - nodePosInLod;
                        if (!minInLeaf.IsInsideInclusive(Vector3I.Zero, lodSizeMinusOne) || !maxInLeaf.IsInsideInclusive(Vector3I.Zero, lodSizeMinusOne))
                        {
                            minInLeaf = Vector3I.Clamp(minInLod - nodePosInLod, Vector3I.Zero, lodSizeMinusOne);
                            maxInLeaf = Vector3I.Clamp(maxInLod - nodePosInLod, Vector3I.Zero, lodSizeMinusOne);
                            // No Optimizations when not reading entirelly from the provider because the octree can't take it.
                            flags &= MyVoxelRequestFlags.SurfaceMaterial | MyVoxelRequestFlags.ConsiderContent;
                        }

                        var cont = flags.HasFlags(MyVoxelRequestFlags.ConsiderContent);

                        leaf.ReadRange(target, types, ref writeOffset, lodIndex, ref minInLeaf, ref maxInLeaf, ref flags);
                        // Occlusion is not stored in tree, we just read from provider
                        if (!leaf.ReadOnly && types.Requests(MyStorageDataTypeEnum.Occlusion))
                        {
                            minInLeaf += nodePosInLod;
                            maxInLeaf += nodePosInLod;
                            ReadFromProvider(target, MyStorageDataTypeFlags.Occlusion, ref writeOffset, lodIndex, ref minInLeaf, ref maxInLeaf, ref flags);
                        }

                        Debug.Assert(flags.HasFlags(MyVoxelRequestFlags.ConsiderContent) == cont);
                    }
                    continue;
                }

                cell.Lod -= 1;
                lodDiff = data.Lod - 1 - lodIndex;

                MyOctreeNode node;
                if (nodes.TryGetValue(cell.PackId64(), out node) == false)
                {
                    Debug.Fail("invalid querry for node");
                    continue;
                }
                var min = minInLod >> lodDiff;
                var max = maxInLod >> lodDiff;
                var nodePositionInChild = data.CoordInLod << 1;
                min -= nodePositionInChild;
                max -= nodePositionInChild;
                for (int i = 0; i < MyOctreeNode.CHILD_COUNT; ++i)
                {
                    Vector3I childPosRelative;
                    ComputeChildCoord(i, out childPosRelative);
                    if (!childPosRelative.IsInsideInclusive(ref min, ref max))
                        continue;
                    if (lodIndex < data.Lod && node.HasChild(i))
                    {
                        Debug.Assert(stackIdx < stackSize);
                        stack[stackIdx++] = new MyCellCoord(data.Lod - 1, nodePositionInChild + childPosRelative);
                    }
                    else
                    {
                        // No Optimizations when not reading entirelly from the provider because the octree can't take it.
                        flags &= MyVoxelRequestFlags.SurfaceMaterial | MyVoxelRequestFlags.ConsiderContent;

                        var childMin = nodePositionInChild + childPosRelative;
                        childMin <<= lodDiff;
                        var writeOffset = childMin - minInLod;
                        Vector3I.Max(ref writeOffset, ref Vector3I.Zero, out writeOffset);
                        writeOffset += targetWriteOffset;
                        var nodeData = node.GetData(i);
                        if (lodDiff == 0)
                        {
                            target.Set(octreeType, ref writeOffset, nodeData);
                        }
                        else
                        {
                            var childMax = childMin + ((1 << lodDiff) - 1);
                            Vector3I.Max(ref childMin, ref minInLod, out childMin);
                            Vector3I.Min(ref childMax, ref maxInLod, out childMax);
                            for (int z = childMin.Z; z <= childMax.Z; ++z)
                            for (int y = childMin.Y; y <= childMax.Y; ++y)
                            for (int x = childMin.X; x <= childMax.X; ++x)
                            {
                                Vector3I write = writeOffset;
                                write.X += x - childMin.X;
                                write.Y += y - childMin.Y;
                                write.Z += z - childMin.Z;
                                target.Set(octreeType, ref write, nodeData);
                            }
                        }
                    }
                }
            }
        }