// 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; } }