mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2025-01-31 04:40:55 +00:00
optimized visual mode even further: visible geometry is grouped by texture and sorted by sector (to optimize out any redundant vertex source stream switches)
This commit is contained in:
parent
43a0e69de7
commit
a2407ebc3b
4 changed files with 291 additions and 23 deletions
|
@ -650,6 +650,7 @@
|
|||
<None Include="Resources\Close.png" />
|
||||
<Compile Include="Editing\EditingManager.cs" />
|
||||
<Content Include="Resources\DB2.ico" />
|
||||
<Compile Include="General\BinaryHeap.cs" />
|
||||
<Compile Include="Geometry\ProjectedFrustum2D.cs" />
|
||||
<Compile Include="VisualModes\Clipper.cs" />
|
||||
<Compile Include="VisualModes\VisualBlockEntry.cs" />
|
||||
|
|
260
Source/General/BinaryHeap.cs
Normal file
260
Source/General/BinaryHeap.cs
Normal file
|
@ -0,0 +1,260 @@
|
|||
|
||||
#region ================== Copyright (c) 2007 Pascal vd Heiden
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
|
||||
* This program is released under GNU General Public License
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Namespaces
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CodeImp.DoomBuilder
|
||||
{
|
||||
internal class BinaryHeap<T> : IEnumerable<T>, ICollection<T> where T : IComparable<T>
|
||||
{
|
||||
#region ================== Variables
|
||||
|
||||
// This will keep all items
|
||||
private List<T> heap;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Properties
|
||||
|
||||
public int Count { get { return heap.Count; } }
|
||||
public virtual bool IsReadOnly { get { return false; } }
|
||||
public T Root { get { if(heap.Count > 0) return heap[0]; else return default(T); } }
|
||||
public T this[int index] { get { return ItemAt(index); } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Constructor / Destructor
|
||||
|
||||
// Constructor
|
||||
public BinaryHeap()
|
||||
{
|
||||
// Initialize with default capacity
|
||||
heap = new List<T>();
|
||||
}
|
||||
|
||||
// Constructor
|
||||
public BinaryHeap(int capacity)
|
||||
{
|
||||
// Initialize with specified capacity
|
||||
heap = new List<T>(capacity);
|
||||
}
|
||||
|
||||
// Destructor
|
||||
~BinaryHeap()
|
||||
{
|
||||
// Clean up
|
||||
heap = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Methods
|
||||
|
||||
// Methods to find our way through the heap
|
||||
private int ParentOf(int index) { return (index - 1) >> 1; }
|
||||
private int LeftOf(int index) { return (index << 1) + 1; }
|
||||
private int RightOf(int index) { return (index << 1) + 2; }
|
||||
|
||||
// This swaps two items in place
|
||||
protected virtual void SwapItems(int index1, int index2)
|
||||
{
|
||||
// Swap items
|
||||
T tempitem = heap[index1];
|
||||
heap[index1] = heap[index2];
|
||||
heap[index2] = tempitem;
|
||||
}
|
||||
|
||||
// This adds an item to the list
|
||||
// This is an O(log n) operation, where n is Count
|
||||
public virtual void Add(T item)
|
||||
{
|
||||
int index = heap.Count;
|
||||
|
||||
// Add to the end of the heap
|
||||
heap.Add(item);
|
||||
|
||||
// Continue until the item is at the top
|
||||
// or compares higher to the parent item
|
||||
while((index > 0) && (heap[index].CompareTo(heap[ParentOf(index)]) > 0))
|
||||
{
|
||||
// Swap with parent item
|
||||
SwapItems(index, ParentOf(index));
|
||||
index = ParentOf(index);
|
||||
}
|
||||
}
|
||||
|
||||
// Finds and returns the index of an item
|
||||
// This is an O(n) operation, where n is Count
|
||||
public virtual int IndexOf(T item)
|
||||
{
|
||||
return heap.IndexOf(item);
|
||||
}
|
||||
|
||||
// This returns the item at the given index
|
||||
// This is an O(1) operation
|
||||
public virtual T ItemAt(int index)
|
||||
{
|
||||
return heap[index];
|
||||
}
|
||||
|
||||
// This removes an item from the list
|
||||
// This is an O(log n) operation, where n is Count
|
||||
public virtual void RemoveAt(int index)
|
||||
{
|
||||
int newindex = index;
|
||||
|
||||
// Replace with last item
|
||||
heap[index] = heap[heap.Count - 1];
|
||||
heap.RemoveAt(heap.Count - 1);
|
||||
|
||||
// Continue while item has at least a left child
|
||||
while(LeftOf(index) < heap.Count)
|
||||
{
|
||||
// Right childs also available?
|
||||
if(RightOf(index) < heap.Count)
|
||||
{
|
||||
// Compare with both childs
|
||||
// NOTE: Using newindex as indexer in the second line to ensure the lowest of both is chosen
|
||||
if(heap[index].CompareTo(heap[LeftOf(index)]) < 0) newindex = LeftOf(index);
|
||||
if(heap[newindex].CompareTo(heap[RightOf(index)]) < 0) newindex = RightOf(index);
|
||||
}
|
||||
// Only left child available
|
||||
else
|
||||
{
|
||||
// Compare with left child
|
||||
if(heap[index].CompareTo(heap[LeftOf(index)]) < 0) newindex = LeftOf(index);
|
||||
}
|
||||
|
||||
// Item should move down?
|
||||
if(newindex != index)
|
||||
{
|
||||
// Swap the items
|
||||
SwapItems(index, newindex);
|
||||
index = newindex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Item is fine where it is, we're done
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This removes the root item from the list
|
||||
// This is an O(log n) operation, where n is Count
|
||||
public virtual void RemoveRoot()
|
||||
{
|
||||
// Remove the root item
|
||||
RemoveAt(0);
|
||||
}
|
||||
|
||||
// This removes a specific item from the list
|
||||
// This is an O(n) operation, where n is Count
|
||||
public virtual bool Remove(T item)
|
||||
{
|
||||
// Find the item in the heap
|
||||
int index = IndexOf(item);
|
||||
if(index > -1)
|
||||
{
|
||||
// Remove the item from the heap
|
||||
RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No such item
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// This clears the heap
|
||||
public virtual void Clear()
|
||||
{
|
||||
// Clear the heap
|
||||
heap.Clear();
|
||||
}
|
||||
|
||||
// This checks if the heap contains a specific item
|
||||
// This is an O(n) operation, where n is Count
|
||||
public virtual bool Contains(T item)
|
||||
{
|
||||
return (IndexOf(item) > -1);
|
||||
}
|
||||
|
||||
// This copies all items to an array
|
||||
public virtual void CopyTo(T[] array)
|
||||
{
|
||||
// Copy items
|
||||
heap.CopyTo(array);
|
||||
}
|
||||
|
||||
// This copies all items to an array
|
||||
public virtual void CopyTo(T[] array, int arrayindex)
|
||||
{
|
||||
// Copy items
|
||||
heap.CopyTo(array, arrayindex);
|
||||
}
|
||||
|
||||
// This copies all items to an array
|
||||
public virtual void CopyTo(int index, T[] array, int arrayindex, int count)
|
||||
{
|
||||
// Copy items
|
||||
heap.CopyTo(index, array, arrayindex, count);
|
||||
}
|
||||
|
||||
// Implemented to display the list
|
||||
// This is an O(n) operation, where n is Count
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder str = new StringBuilder(heap.Count * 5);
|
||||
|
||||
// Go for all items
|
||||
for(int i = 0; i < heap.Count; i++)
|
||||
{
|
||||
// Append item to string
|
||||
if(i > 0) str.Append(", ");
|
||||
str.Append(heap[i]);
|
||||
}
|
||||
|
||||
// Return the string
|
||||
return str.ToString();
|
||||
}
|
||||
|
||||
// This returns an enumerator
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return heap.GetEnumerator();
|
||||
}
|
||||
|
||||
// This returns an enumerator
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return heap.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ using CodeImp.DoomBuilder.Geometry;
|
|||
using SlimDX.Direct3D9;
|
||||
using CodeImp.DoomBuilder.Data;
|
||||
using CodeImp.DoomBuilder.VisualModes;
|
||||
using CodeImp.DoomBuilder.Map;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -56,7 +57,7 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
private ProjectedFrustum2D frustum;
|
||||
|
||||
// Geometry, grouped by texture
|
||||
private Dictionary<ImageData, List<VisualGeometry>> geometry;
|
||||
private Dictionary<ImageData, BinaryHeap<VisualGeometry>> geometry;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -219,14 +220,14 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
graphics.Shaders.World3D.BeginPass(0);
|
||||
|
||||
// Make collection
|
||||
geometry = new Dictionary<ImageData, List<VisualGeometry>>();
|
||||
geometry = new Dictionary<ImageData, BinaryHeap<VisualGeometry>>();
|
||||
}
|
||||
|
||||
// This ends rendering world geometry
|
||||
public void FinishGeometry()
|
||||
{
|
||||
// We now render the actual geometry collected
|
||||
foreach(KeyValuePair<ImageData, List<VisualGeometry>> group in geometry)
|
||||
foreach(KeyValuePair<ImageData, BinaryHeap<VisualGeometry>> group in geometry)
|
||||
{
|
||||
ImageData curtexture;
|
||||
|
||||
|
@ -246,18 +247,32 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
graphics.Shaders.World3D.ApplySettings();
|
||||
|
||||
// Go for all geometry that uses this texture
|
||||
VisualSector sector = null;
|
||||
foreach(VisualGeometry g in group.Value)
|
||||
{
|
||||
// Update the sector if needed
|
||||
if(g.Sector.NeedsUpdateGeo) g.Sector.Update();
|
||||
|
||||
// Only render when a vertexbuffer exists
|
||||
if(g.Sector.GeometryBuffer != null)
|
||||
// Changing sector?
|
||||
if(!object.ReferenceEquals(g.Sector, sector))
|
||||
{
|
||||
// Render!
|
||||
graphics.Device.SetStreamSource(0, g.Sector.GeometryBuffer, 0, WorldVertex.Stride);
|
||||
graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, g.VertexOffset, g.Triangles);
|
||||
}
|
||||
// Update the sector if needed
|
||||
if(g.Sector.NeedsUpdateGeo) g.Sector.Update();
|
||||
|
||||
// Only do this sector when a vertexbuffer is created
|
||||
if(g.Sector.GeometryBuffer != null)
|
||||
{
|
||||
// Change current sector
|
||||
sector = g.Sector;
|
||||
|
||||
// Set stream source
|
||||
graphics.Device.SetStreamSource(0, sector.GeometryBuffer, 0, WorldVertex.Stride);
|
||||
}
|
||||
else
|
||||
{
|
||||
sector = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Render!
|
||||
if(sector != null) graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, g.VertexOffset, g.Triangles);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,7 +307,7 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
if(!geometry.ContainsKey(g.Texture))
|
||||
{
|
||||
// Create texture group
|
||||
geometry.Add(g.Texture, new List<VisualGeometry>());
|
||||
geometry.Add(g.Texture, new BinaryHeap<VisualGeometry>());
|
||||
}
|
||||
|
||||
// Add geometry to texture group
|
||||
|
|
|
@ -110,16 +110,8 @@ namespace CodeImp.DoomBuilder.VisualModes
|
|||
// This compares for sorting
|
||||
public int CompareTo(VisualGeometry other)
|
||||
{
|
||||
long thislongname, otherlongname;
|
||||
|
||||
// Get long names
|
||||
if(this.texture != null) thislongname = this.texture.LongName; else thislongname = 0;
|
||||
if(other.texture != null) otherlongname = other.texture.LongName; else otherlongname = 0;
|
||||
|
||||
// Compare names
|
||||
if(thislongname > otherlongname) return 1;
|
||||
else if(thislongname < otherlongname) return -1;
|
||||
else return 0;
|
||||
// Compare sectors
|
||||
return this.sector.Sector.Index - other.sector.Sector.Index;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
Loading…
Reference in a new issue