// This frees the given surface entry
 public void FreeSurfaces(SurfaceEntryCollection entries)
 {
     foreach (SurfaceEntry e in entries)
     {
         if ((e.VerticesCount > 0) && (e.BufferIndex > -1))
         {
             SurfaceBufferSet set = sets[e.VerticesCount];
             set.entries.Remove(e);
             SurfaceEntry newentry = new SurfaceEntry(e);
             set.holes.Add(newentry);
         }
         e.VerticesCount = -1;
         e.BufferIndex   = -1;
     }
 }
        // Updating sector surface geometry should go in this order;
        // - Triangulate sectors
        // - Call FreeSurfaces to remove entries that have changed number of vertices
        // - Call AllocateBuffers
        // - Call UpdateSurfaces to add/update entries
        // - Call UnlockBuffers

        // This (re)allocates the buffers based on an analysis of the map
        // The map must be updated (triangulated) before calling this
        public void AllocateBuffers()
        {
            // Make analysis of sector geometry
            Dictionary <int, int> sectorverts = new Dictionary <int, int>();

            foreach (Sector s in General.Map.Map.Sectors)
            {
                if (s.Triangles != null)
                {
                    int numvertices = s.Triangles.Vertices.Count;
                    while (numvertices > 0)
                    {
                        // Determine for how many vertices in this entry
                        int vertsinentry = (numvertices > MAX_VERTICES_PER_SECTOR) ? MAX_VERTICES_PER_SECTOR : numvertices;

                        // We count the number of sectors that have specific number of vertices
                        if (!sectorverts.ContainsKey(vertsinentry))
                        {
                            sectorverts.Add(vertsinentry, 0);
                        }
                        sectorverts[vertsinentry]++;

                        numvertices -= vertsinentry;
                    }
                }
            }

            // Now (re)allocate the needed buffers
            foreach (KeyValuePair <int, int> sv in sectorverts)
            {
                // Zero vertices can't be drawn
                if (sv.Key > 0)
                {
                    SurfaceBufferSet set = GetSet(sv.Key);

                    // Calculte how many free entries we need
                    int neededentries     = sv.Value;
                    int freeentriesneeded = neededentries - set.entries.Count;

                    // Allocate the space needed
                    EnsureFreeBufferSpace(set, freeentriesneeded);
                }
            }
        }
        // This gets or creates a set for a specific number of vertices
        private SurfaceBufferSet GetSet(int numvertices)
        {
            SurfaceBufferSet set;

            // Get or create the set
            if (!sets.ContainsKey(numvertices))
            {
                set             = new SurfaceBufferSet();
                set.numvertices = numvertices;
                set.buffers     = new List <VertexBuffer>();
                set.buffersizes = new List <int>();
                set.entries     = new List <SurfaceEntry>();
                set.holes       = new List <SurfaceEntry>();
                sets.Add(numvertices, set);
            }
            else
            {
                set = sets[numvertices];
            }

            return(set);
        }
        // This renders the sorted sector surfaces
        internal void RenderSectorSurfaces(D3DDevice graphics)
        {
            if (!resourcesunloaded)
            {
                graphics.Shaders.Display2D.Begin();
                foreach (KeyValuePair <ImageData, List <SurfaceEntry> > imgsurfaces in surfaces)
                {
                    // Set texture
                    graphics.Shaders.Display2D.Texture1 = imgsurfaces.Key.Texture;
                    if (!graphics.Shaders.Enabled)
                    {
                        graphics.Device.SetTexture(0, imgsurfaces.Key.Texture);
                    }

                    graphics.Shaders.Display2D.BeginPass(1);

                    // Go for all surfaces
                    VertexBuffer lastbuffer = null;
                    foreach (SurfaceEntry entry in imgsurfaces.Value)
                    {
                        // Set the vertex buffer
                        SurfaceBufferSet set = sets[entry.VerticesCount];
                        if (set.buffers[entry.BufferIndex] != lastbuffer)
                        {
                            lastbuffer = set.buffers[entry.BufferIndex];
                            graphics.Device.SetStreamSource(0, lastbuffer, 0, FlatVertex.Stride);
                        }

                        // Draw
                        graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, entry.VertexOffset + (entry.VerticesCount * surfacevertexoffsetmul), entry.VerticesCount / 3);
                    }

                    graphics.Shaders.Display2D.EndPass();
                }
                graphics.Shaders.Display2D.End();
            }
        }
        // This adds or updates sector geometry into a buffer.
        // Modiies the list of SurfaceEntries with the new surface entry for the stored geometry.
        public void UpdateSurfaces(SurfaceEntryCollection entries, SurfaceUpdate update)
        {
            // Free entries when number of vertices has changed
            if ((entries.Count > 0) && (entries.TotalVertices != update.VerticesCount))
            {
                FreeSurfaces(entries);
                entries.Clear();
            }

            if ((entries.Count == 0) && (update.VerticesCount > 0))
            {
                                #if DEBUG
                if ((update.FloorVertices == null) || (update.CeilingVertices == null))
                {
                    General.Fail("We need both floor and ceiling vertices when the number of vertices changes!");
                }
                                #endif

                // If we have no entries yet, we have to make them now
                int vertsremaining = update.VerticesCount;
                while (vertsremaining > 0)
                {
                    // Determine for how many vertices in this entry
                    int vertsinentry = (vertsremaining > MAX_VERTICES_PER_SECTOR) ? MAX_VERTICES_PER_SECTOR : vertsremaining;

                    // Lookup the set that holds entries for this number of vertices
                    SurfaceBufferSet set = GetSet(vertsinentry);

                    // Make sure we can get a new entry in this set
                    EnsureFreeBufferSpace(set, 1);

                    // Get a new entry in this set
                    SurfaceEntry e = set.holes[set.holes.Count - 1];
                    set.holes.RemoveAt(set.holes.Count - 1);
                    set.entries.Add(e);

                    // Fill the entry data
                    e.FloorVertices   = new FlatVertex[vertsinentry];
                    e.CeilingVertices = new FlatVertex[vertsinentry];
                    Array.Copy(update.FloorVertices, update.VerticesCount - vertsremaining, e.FloorVertices, 0, vertsinentry);
                    Array.Copy(update.CeilingVertices, update.VerticesCount - vertsremaining, e.CeilingVertices, 0, vertsinentry);
                    e.FloorTileIndex   = update.FloorTileIndex;
                    e.CeilingTileIndex = update.CeilingTileIndex;

                    entries.Add(e);
                    vertsremaining -= vertsinentry;
                }
            }
            else
            {
                // We re-use the same entries, just copy over the updated data
                int vertsremaining = update.VerticesCount;
                foreach (SurfaceEntry e in entries)
                {
                    if (update.FloorVertices != null)
                    {
                        Array.Copy(update.FloorVertices, update.VerticesCount - vertsremaining, e.FloorVertices, 0, e.VerticesCount);
                        e.FloorTileIndex = update.FloorTileIndex;
                    }

                    if (update.CeilingVertices != null)
                    {
                        Array.Copy(update.CeilingVertices, update.VerticesCount - vertsremaining, e.CeilingVertices, 0, e.VerticesCount);
                        e.CeilingTileIndex = update.CeilingTileIndex;
                    }

                    vertsremaining -= e.VerticesCount;
                }
            }

            entries.TotalVertices = update.VerticesCount;

            // Time to update or create the buffers
            foreach (SurfaceEntry e in entries)
            {
                SurfaceBufferSet set = GetSet(e.VerticesCount);

                // Update bounding box
                e.UpdateBBox();

                if (!resourcesunloaded)
                {
                    // Lock the buffer
                    DataStream   bstream;
                    VertexBuffer vb = set.buffers[e.BufferIndex];
                    if (vb.Tag == null)
                    {
                        // Note: DirectX warns me that I am not using LockFlags.Discard or LockFlags.NoOverwrite here,
                        // but we don't have much of a choice since we want to update our data and not destroy other data
                        bstream = vb.Lock(0, set.buffersizes[e.BufferIndex] * FlatVertex.Stride, LockFlags.None);
                        vb.Tag  = bstream;
                        lockedbuffers.Add(vb);
                    }
                    else
                    {
                        bstream = (DataStream)vb.Tag;
                    }

                    // Write the vertices to buffer
                    bstream.Seek(e.VertexOffset * FlatVertex.Stride, SeekOrigin.Begin);
                    bstream.WriteRange(e.FloorVertices);
                    bstream.WriteRange(e.CeilingVertices);
                }
            }
        }
        // This ensures there is enough space for a given number of free entries (also adds new bufers if needed)
        private void EnsureFreeBufferSpace(SurfaceBufferSet set, int freeentries)
        {
            DataStream   bstream = null;
            VertexBuffer vb      = null;

            // Check if we have to add entries
            int addentries = freeentries - set.holes.Count;

            // Begin resizing buffers starting with the last in this set
            int bufferindex = set.buffers.Count - 1;

            // Calculate the maximum number of entries we can put in a new buffer
            // Note that verticesperentry is the number of vertices multiplied by 2, because
            // we have to store both the floor and ceiling
            int verticesperentry    = set.numvertices * 2;
            int maxentriesperbuffer = MAX_VERTICES_PER_BUFFER / verticesperentry;

            // Make a new bufer when the last one is full
            if ((bufferindex > -1) && (set.buffersizes[bufferindex] >= (maxentriesperbuffer * verticesperentry)))
            {
                bufferindex = -1;
            }

            while (addentries > 0)
            {
                // Create a new buffer?
                if ((bufferindex == -1) || (bufferindex > (set.buffers.Count - 1)))
                {
                    // Determine the number of entries we will be making this buffer for
                    int bufferentries = (addentries > maxentriesperbuffer) ? maxentriesperbuffer : addentries;

                    // Calculate the number of vertices that will be
                    int buffernumvertices = bufferentries * verticesperentry;

                    if (!resourcesunloaded)
                    {
                        // Make the new buffer!
                        vb = new VertexBuffer(General.Map.Graphics.Device, FlatVertex.Stride * buffernumvertices,
                                              Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default);

                        // Add it.
                        set.buffers.Add(vb);
                    }
                    else
                    {
                        // We can't make a vertexbuffer right now
                        set.buffers.Add(null);
                    }

                    // Also add available entries as holes, because they are not used yet.
                    set.buffersizes.Add(buffernumvertices);
                    for (int i = 0; i < bufferentries; i++)
                    {
                        set.holes.Add(new SurfaceEntry(set.numvertices, set.buffers.Count - 1, i * verticesperentry));
                    }

                    // Done
                    addentries -= bufferentries;
                }
                // Reallocate a buffer
                else
                {
                    // Trash the old buffer
                    if (set.buffers[bufferindex].Tag != null)
                    {
                        bstream = (DataStream)set.buffers[bufferindex].Tag;
                        set.buffers[bufferindex].Unlock();
                        bstream.Dispose();
                        set.buffers[bufferindex].Tag = null;
                    }

                    if ((set.buffers[bufferindex] != null) && !resourcesunloaded)
                    {
                        set.buffers[bufferindex].Dispose();
                    }

                    // Get the entries that are in this buffer only
                    List <SurfaceEntry> theseentries = new List <SurfaceEntry>();
                    foreach (SurfaceEntry e in set.entries)
                    {
                        if (e.BufferIndex == bufferindex)
                        {
                            theseentries.Add(e);
                        }
                    }

                    // Determine the number of entries we will be making this buffer for
                    int bufferentries = ((theseentries.Count + addentries) > maxentriesperbuffer) ? maxentriesperbuffer : (theseentries.Count + addentries);

                    // Calculate the number of vertices that will be
                    int buffernumvertices = bufferentries * verticesperentry;

                    if (!resourcesunloaded)
                    {
                        // Make the new buffer and lock it
                        vb = new VertexBuffer(General.Map.Graphics.Device, FlatVertex.Stride * buffernumvertices,
                                              Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default);
                        bstream = vb.Lock(0, FlatVertex.Stride * theseentries.Count * verticesperentry, LockFlags.Discard);
                    }

                    // Start refilling the buffer with sector geometry
                    int vertexoffset = 0;
                    foreach (SurfaceEntry e in theseentries)
                    {
                        if (!resourcesunloaded)
                        {
                            // Fill buffer
                            bstream.WriteRange(e.FloorVertices);
                            bstream.WriteRange(e.CeilingVertices);
                        }

                        // Set the new location in the buffer
                        e.VertexOffset = vertexoffset;

                        // Move on
                        vertexoffset += verticesperentry;
                    }

                    if (!resourcesunloaded)
                    {
                        // Unlock buffer
                        vb.Unlock();
                        bstream.Dispose();
                        set.buffers[bufferindex] = vb;
                    }
                    else
                    {
                        // No vertex buffer at this time, sorry
                        set.buffers[bufferindex] = null;
                    }

                    // Set the new buffer and add available entries as holes, because they are not used yet.
                    set.buffersizes[bufferindex] = buffernumvertices;
                    set.holes.Clear();
                    for (int i = 0; i < bufferentries - theseentries.Count; i++)
                    {
                        set.holes.Add(new SurfaceEntry(set.numvertices, bufferindex, i * verticesperentry + vertexoffset));
                    }

                    // Done
                    addentries -= bufferentries;
                }

                // Always continue in next (new) buffer
                bufferindex = set.buffers.Count;
            }
        }