// 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) { 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.SetBufferData(vb, buffernumvertices, VertexFormat.Flat); // 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 { 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.SetBufferData(vb, buffernumvertices, VertexFormat.Flat); } // Start refilling the buffer with sector geometry int vertexoffset = 0; foreach (SurfaceEntry e in theseentries) { if (!resourcesunloaded) { // Fill buffer General.Map.Graphics.SetBufferSubdata(vb, vertexoffset, e.floorvertices); General.Map.Graphics.SetBufferSubdata(vb, vertexoffset + e.floorvertices.Length, e.ceilvertices); } // Set the new location in the buffer e.vertexoffset = vertexoffset; // Move on vertexoffset += verticesperentry; } if (!resourcesunloaded) { 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; } }
// 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.numvertices)) { FreeSurfaces(entries); entries.Clear(); } if ((entries.Count == 0) && (update.numvertices > 0)) { #if SDEBUG if ((update.floorvertices == null) || (update.ceilvertices == 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.numvertices; 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.ceilvertices = new FlatVertex[vertsinentry]; Array.Copy(update.floorvertices, update.numvertices - vertsremaining, e.floorvertices, 0, vertsinentry); Array.Copy(update.ceilvertices, update.numvertices - vertsremaining, e.ceilvertices, 0, vertsinentry); e.floortexture = update.floortexture; e.ceiltexture = update.ceiltexture; e.desaturation = update.desaturation; entries.Add(e); vertsremaining -= vertsinentry; } } else { // We re-use the same entries, just copy over the updated data int vertsremaining = update.numvertices; foreach (SurfaceEntry e in entries) { if (update.floorvertices != null) { Array.Copy(update.floorvertices, update.numvertices - vertsremaining, e.floorvertices, 0, e.numvertices); e.floortexture = update.floortexture; } if (update.ceilvertices != null) { Array.Copy(update.ceilvertices, update.numvertices - vertsremaining, e.ceilvertices, 0, e.numvertices); e.ceiltexture = update.ceiltexture; } e.desaturation = update.desaturation; vertsremaining -= e.numvertices; } } entries.totalvertices = update.numvertices; // Time to update or create the buffers foreach (SurfaceEntry e in entries) { SurfaceBufferSet set = GetSet(e.numvertices); // Update bounding box e.UpdateBBox(); if (!resourcesunloaded) { VertexBuffer vb = set.buffers[e.bufferindex]; General.Map.Graphics.SetBufferSubdata(vb, e.vertexoffset, e.floorvertices); General.Map.Graphics.SetBufferSubdata(vb, e.vertexoffset + e.floorvertices.Length, e.ceilvertices); } } }
// This updates the text if needed public void Update(float translatex, float translatey, float scalex, float scaley) { // Check if transformation changed and needs to be updated if (transformcoords && (translatex != lasttranslatex || translatey != lasttranslatey || scalex != lastscalex || scaley != lastscaley)) { lasttranslatex = translatex; //mxd lasttranslatey = translatey; //mxd lastscalex = scalex; //mxd lastscaley = scaley; //mxd updateneeded = true; } // Update if needed if (updateneeded || textureupdateneeded) { // Only build when there are any vertices if (text.Length > 0) { // Transform? Vector2D abspos = (transformcoords ? location.GetTransformed(translatex, translatey, scalex, scaley) : location); // Update text and texture sizes if (textsize.IsEmpty || texturesize.IsEmpty) { textorigin = new PointF(4, 3); textrect = new RectangleF(textorigin, General.Interface.MeasureString(text, font)); textrect.Width = (float)Math.Round(textrect.Width); textrect.Height = (float)Math.Round(textrect.Height); bgrect = new RectangleF(0, 0, textrect.Width + textorigin.X * 2, textrect.Height + textorigin.Y * 2); // Store calculated text size... textsize = new SizeF(textrect.Width + textorigin.X * 2, textrect.Height + textorigin.Y * 2); // Make PO2 image, for speed and giggles... texturesize = new Size(General.NextPowerOf2((int)textsize.Width), General.NextPowerOf2((int)textsize.Height)); switch (alignx) { case TextAlignmentX.Center: bgrect.X = (texturesize.Width - bgrect.Width) / 2; break; case TextAlignmentX.Right: bgrect.X = texturesize.Width - bgrect.Width; break; } switch (aligny) { case TextAlignmentY.Middle: bgrect.Y = (texturesize.Height - bgrect.Height) / 2; break; case TextAlignmentY.Bottom: bgrect.Y = texturesize.Height - bgrect.Height; break; } textrect.X += bgrect.X; textrect.Y += bgrect.Y; } // Align the text horizontally float beginx = 0; switch (alignx) { case TextAlignmentX.Left: beginx = abspos.x; break; case TextAlignmentX.Center: beginx = abspos.x - texturesize.Width * 0.5f; break; case TextAlignmentX.Right: beginx = abspos.x - texturesize.Width; break; } // Align the text vertically float beginy = 0; switch (aligny) { case TextAlignmentY.Top: beginy = abspos.y; break; case TextAlignmentY.Middle: beginy = abspos.y - texturesize.Height * 0.5f; break; case TextAlignmentY.Bottom: beginy = abspos.y - texturesize.Height; break; } //mxd. Skip when not on screen... RectangleF abssize = new RectangleF(beginx, beginy, texturesize.Width, texturesize.Height); Size windowsize = General.Map.Graphics.RenderTarget.ClientSize; skiprendering = (abssize.Right < 0.1f) || (abssize.Left > windowsize.Width) || (abssize.Bottom < 0.1f) || (abssize.Top > windowsize.Height); if (skiprendering) { return; } //mxd. Update texture if needed if (textureupdateneeded) { // Get rid of old texture if (texture != null) { texture.Dispose(); texture = null; } // Create label image Bitmap img = CreateLabelImage(text, font, color, backcolor, drawbg, textrect, bgrect, texturesize, textorigin); //texturesize = img.Size; // Create texture MemoryStream memstream = new MemoryStream((img.Size.Width * img.Size.Height * 4) + 4096); img.Save(memstream, ImageFormat.Bmp); memstream.Seek(0, SeekOrigin.Begin); texture = Texture.FromStream(General.Map.Graphics.Device, memstream, (int)memstream.Length, img.Size.Width, img.Size.Height, 1, Usage.None, Format.Unknown, Pool.Managed, General.Map.Graphics.PostFilter, General.Map.Graphics.MipGenerateFilter, 0); } //mxd. Create the buffer if (textbuffer == null || textbuffer.Disposed) { textbuffer = new VertexBuffer(General.Map.Graphics.Device, 4 * FlatVertex.Stride, Usage.Dynamic | Usage.WriteOnly, VertexFormat.None, Pool.Default); } //mxd. Lock the buffer using (DataStream stream = textbuffer.Lock(0, 4 * FlatVertex.Stride, LockFlags.Discard | LockFlags.NoSystemLock)) { FlatQuad quad = new FlatQuad(PrimitiveType.TriangleStrip, beginx, beginy, beginx + texturesize.Width, beginy + texturesize.Height); stream.WriteRange(quad.Vertices); } // Done filling the vertex buffer textbuffer.Unlock(); } else { // No faces in polygon textsize = SizeF.Empty; //mxd texturesize = Size.Empty; //mxd skiprendering = true; //mxd } // Text updated updateneeded = false; textureupdateneeded = false; //mxd } }
// 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.numvertices)) { FreeSurfaces(entries); entries.Clear(); } if ((entries.Count == 0) && (update.numvertices > 0)) { #if DEBUG if ((update.floorvertices == null) || (update.ceilvertices == 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.numvertices; 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.ceilvertices = new FlatVertex[vertsinentry]; Array.Copy(update.floorvertices, update.numvertices - vertsremaining, e.floorvertices, 0, vertsinentry); Array.Copy(update.ceilvertices, update.numvertices - vertsremaining, e.ceilvertices, 0, vertsinentry); e.floortexture = update.floortexture; e.ceiltexture = update.ceiltexture; entries.Add(e); vertsremaining -= vertsinentry; } } else { // We re-use the same entries, just copy over the updated data int vertsremaining = update.numvertices; foreach (SurfaceEntry e in entries) { if (update.floorvertices != null) { Array.Copy(update.floorvertices, update.numvertices - vertsremaining, e.floorvertices, 0, e.numvertices); e.floortexture = update.floortexture; } if (update.ceilvertices != null) { Array.Copy(update.ceilvertices, update.numvertices - vertsremaining, e.ceilvertices, 0, e.numvertices); e.ceiltexture = update.ceiltexture; } vertsremaining -= e.numvertices; } } entries.totalvertices = update.numvertices; // Time to update or create the buffers foreach (SurfaceEntry e in entries) { SurfaceBufferSet set = GetSet(e.numvertices); // 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.ceilvertices); } } }
// This sets up the thing cage private void SetupThingCage() { const int totalvertices = 36; WorldVertex[] tv = new WorldVertex[totalvertices]; float x0 = -1.0f; float x1 = 1.0f; float y0 = -1.0f; float y1 = 1.0f; float z0 = 0.0f; float z1 = 1.0f; float u0 = 0.0f; float u1 = 1.0f; float v0 = 0.0f; float v1 = 1.0f; int c = -1; // Front tv[0] = new WorldVertex(x0, y0, z0, c, u0, v0); tv[1] = new WorldVertex(x0, y0, z1, c, u0, v1); tv[2] = new WorldVertex(x1, y0, z0, c, u1, v0); tv[3] = new WorldVertex(x1, y0, z0, c, u1, v0); tv[4] = new WorldVertex(x0, y0, z1, c, u0, v1); tv[5] = new WorldVertex(x1, y0, z1, c, u1, v1); // Right tv[6] = new WorldVertex(x1, y0, z0, c, u0, v0); tv[7] = new WorldVertex(x1, y0, z1, c, u0, v1); tv[8] = new WorldVertex(x1, y1, z0, c, u1, v0); tv[9] = new WorldVertex(x1, y1, z0, c, u1, v0); tv[10] = new WorldVertex(x1, y0, z1, c, u0, v1); tv[11] = new WorldVertex(x1, y1, z1, c, u1, v1); // Back tv[12] = new WorldVertex(x1, y1, z0, c, u0, v0); tv[13] = new WorldVertex(x1, y1, z1, c, u0, v1); tv[14] = new WorldVertex(x0, y1, z0, c, u1, v0); tv[15] = new WorldVertex(x0, y1, z0, c, u1, v0); tv[16] = new WorldVertex(x1, y1, z1, c, u0, v1); tv[17] = new WorldVertex(x0, y1, z1, c, u1, v1); // Left tv[18] = new WorldVertex(x0, y1, z0, c, u0, v1); tv[19] = new WorldVertex(x0, y1, z1, c, u0, v0); tv[20] = new WorldVertex(x0, y0, z1, c, u1, v0); tv[21] = new WorldVertex(x0, y1, z0, c, u1, v0); tv[22] = new WorldVertex(x0, y0, z1, c, u0, v1); tv[23] = new WorldVertex(x0, y0, z0, c, u1, v1); // Top tv[24] = new WorldVertex(x0, y0, z1, c, u0, v0); tv[25] = new WorldVertex(x0, y1, z1, c, u0, v1); tv[26] = new WorldVertex(x1, y0, z1, c, u1, v0); tv[27] = new WorldVertex(x1, y0, z1, c, u1, v0); tv[28] = new WorldVertex(x0, y1, z1, c, u0, v1); tv[29] = new WorldVertex(x1, y1, z1, c, u1, v1); // Bottom tv[30] = new WorldVertex(x1, y0, z0, c, u1, v0); tv[31] = new WorldVertex(x0, y1, z0, c, u0, v1); tv[32] = new WorldVertex(x0, y0, z0, c, u0, v0); tv[33] = new WorldVertex(x1, y0, z0, c, u1, v0); tv[34] = new WorldVertex(x1, y1, z0, c, u1, v1); tv[35] = new WorldVertex(x0, y1, z0, c, u0, v1); // Create vertexbuffer thingcage = new VertexBuffer(General.Map.Graphics.Device, WorldVertex.Stride * totalvertices, Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default); DataStream bufferstream = thingcage.Lock(0, WorldVertex.Stride * totalvertices, LockFlags.Discard); bufferstream.WriteRange <WorldVertex>(tv); thingcage.Unlock(); bufferstream.Dispose(); }
// This updates the text if needed internal void Update(float translatex, float translatey, float scalex, float scaley) { FlatVertex[] verts; RectangleF absview; float beginx = 0; float beginy = 0; bool colorcode = false; int characters = 0; byte[] textbytes; DataStream stream; // Check if transformation changed and needs to be updated if (transformcoords) { if ((translatex != lasttranslatex) || (translatey != lasttranslatey) || (scalex != lastscalex) || (scaley != lastscaley)) { updateneeded = true; } } // Update if needed if (updateneeded) { // Only build when there are any vertices if (text.Length > 0) { // Do we have to make a new buffer? if ((textbuffer == null) || (text.Length > capacity)) { // Dispose previous if (textbuffer != null) { textbuffer.Dispose(); } // Determine new capacity if (capacity < text.Length) { capacity = text.Length; } // Create the buffer textbuffer = new VertexBuffer(General.Map.Graphics.Device, capacity * 12 * FlatVertex.Stride, Usage.Dynamic | Usage.WriteOnly, VertexFormat.None, Pool.Default); } // Transform? if (transformcoords) { // Calculate absolute coordinates Vector2D lt = new Vector2D(rect.Left, rect.Top); Vector2D rb = new Vector2D(rect.Right, rect.Bottom); lt = lt.GetTransformed(translatex, translatey, scalex, scaley); rb = rb.GetTransformed(translatex, translatey, scalex, scaley); absview = new RectangleF(lt.x, lt.y, rb.x - lt.x, rb.y - lt.y); } else { // Fixed coordinates absview = rect; } // Calculate text dimensions size = General.Map.Graphics.Font.GetTextSize(text, scale); // Align the text horizontally switch (alignx) { case TextAlignmentX.Left: beginx = absview.X; break; case TextAlignmentX.Center: beginx = absview.X + (absview.Width - size.Width) * 0.5f; break; case TextAlignmentX.Right: beginx = absview.X + absview.Width - size.Width; break; } // Align the text vertically switch (aligny) { case TextAlignmentY.Top: beginy = absview.Y; break; case TextAlignmentY.Middle: beginy = absview.Y + (absview.Height - size.Height) * 0.5f; break; case TextAlignmentY.Bottom: beginy = absview.Y + absview.Height - size.Height; break; } // Get the ASCII bytes for the text textbytes = Encoding.ASCII.GetBytes(text); // Lock the buffer stream = textbuffer.Lock(0, capacity * 12 * FlatVertex.Stride, LockFlags.Discard | LockFlags.NoSystemLock); // Go for all chars in text to create the backgrounds float textx = beginx; foreach (byte b in textbytes) { General.Map.Graphics.Font.SetupVertices(stream, b, scale, backcolor.ToInt(), ref textx, beginy, size.Height, 0.5f); } // Go for all chars in text to create the text textx = beginx; foreach (byte b in textbytes) { General.Map.Graphics.Font.SetupVertices(stream, b, scale, color.ToInt(), ref textx, beginy, size.Height, 0.0f); } // Done filling the vertex buffer textbuffer.Unlock(); stream.Dispose(); // Calculate number of triangles numfaces = text.Length * 4; } else { // No faces in polygon numfaces = 0; size = new SizeF(0f, 0f); } // Text updated updateneeded = false; } }
// This adds or updates sector geometry into a buffer. // Always specify the entry when a previous entry was already given for that sector! // Sector must set the floorvertices and ceilvertices members on the entry. // Returns the new surface entry for the stored geometry, floorvertices and ceilvertices will be preserved. public SurfaceEntry UpdateSurfaces(SurfaceEntry entry) { if (entry.floorvertices.Length != entry.ceilvertices.Length) { General.Fail("Floor vertices has different length from ceiling vertices!"); } int numvertices = entry.floorvertices.Length; // Free entry when number of vertices have changed if ((entry.numvertices != numvertices) && (entry.numvertices != -1)) { FreeSurfaces(entry); } // Check if we can render this at all if (numvertices > 0) { SurfaceBufferSet set = GetSet(numvertices); // Update bounding box entry.UpdateBBox(); // Check if we need a new entry if (entry.numvertices == -1) { EnsureFreeBufferSpace(set, 1); SurfaceEntry nentry = set.holes[set.holes.Count - 1]; set.holes.RemoveAt(set.holes.Count - 1); nentry.ceilvertices = entry.ceilvertices; nentry.floorvertices = entry.floorvertices; nentry.floortexture = entry.floortexture; nentry.ceiltexture = entry.ceiltexture; nentry.bbox = entry.bbox; set.entries.Add(nentry); entry = nentry; } if (!resourcesunloaded) { // Lock the buffer DataStream bstream; VertexBuffer vb = set.buffers[entry.bufferindex]; if (vb.Tag == null) { // Note: DirectX warns me that I am not using LockFlags.Discard or LockFlags.NoOverwrite here, // but we don't care (we don't have much of a choice since we want to update our data) bstream = vb.Lock(0, set.buffersizes[entry.bufferindex] * FlatVertex.Stride, LockFlags.None); vb.Tag = bstream; lockedbuffers.Add(vb); } else { bstream = (DataStream)vb.Tag; } // Write the vertices to buffer bstream.Seek(entry.vertexoffset * FlatVertex.Stride, SeekOrigin.Begin); bstream.WriteRange(entry.floorvertices); bstream.WriteRange(entry.ceilvertices); } } return(entry); }