mirror of
https://git.do.srb2.org/STJr/ZoneBuilder.git
synced 2025-02-11 10:11:39 +00:00
Rewrite VisualBlockMap to use a quad tree
This commit is contained in:
parent
12ea1aa313
commit
b0b1693a62
6 changed files with 306 additions and 445 deletions
|
@ -3945,14 +3945,7 @@ namespace CodeImp.DoomBuilder.Map
|
||||||
/// <summary>This returns a sector if given coordinates are inside one.</summary>
|
/// <summary>This returns a sector if given coordinates are inside one.</summary>
|
||||||
public Sector GetSectorByCoordinates(Vector2D pos, VisualBlockMap blockmap)
|
public Sector GetSectorByCoordinates(Vector2D pos, VisualBlockMap blockmap)
|
||||||
{
|
{
|
||||||
// Find nearest sectors using the blockmap
|
return blockmap.GetSectorAt(pos);
|
||||||
List<Sector> possiblesectors = blockmap.GetBlock(blockmap.GetBlockCoordinates(pos)).Sectors;
|
|
||||||
foreach(Sector s in possiblesectors)
|
|
||||||
{
|
|
||||||
if(s.Intersect(pos)) return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//mxd
|
//mxd
|
||||||
|
|
|
@ -294,19 +294,7 @@ namespace CodeImp.DoomBuilder.Map
|
||||||
// This determines which sector the thing is in and links it
|
// This determines which sector the thing is in and links it
|
||||||
public void DetermineSector(VisualBlockMap blockmap)
|
public void DetermineSector(VisualBlockMap blockmap)
|
||||||
{
|
{
|
||||||
// Find nearest sectors using the blockmap
|
sector = blockmap.GetSectorAt(pos);
|
||||||
List<Sector> possiblesectors = blockmap.GetBlock(blockmap.GetBlockCoordinates(pos)).Sectors;
|
|
||||||
|
|
||||||
// Check in which sector we are
|
|
||||||
sector = null;
|
|
||||||
foreach(Sector s in possiblesectors)
|
|
||||||
{
|
|
||||||
if(s.Intersect(pos))
|
|
||||||
{
|
|
||||||
sector = s;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This determines which sector the thing is in by looking at the BSP tree and links it
|
// This determines which sector the thing is in by looking at the BSP tree and links it
|
||||||
|
|
|
@ -25,33 +25,15 @@ namespace CodeImp.DoomBuilder.VisualModes
|
||||||
{
|
{
|
||||||
public sealed class VisualBlockEntry
|
public sealed class VisualBlockEntry
|
||||||
{
|
{
|
||||||
#region ================== Variables
|
public List<Linedef> Lines { get; set; }
|
||||||
|
public List<Thing> Things { get; set; }
|
||||||
|
public List<Sector> Sectors { get; set; }
|
||||||
|
|
||||||
// Members
|
|
||||||
private List<Linedef> lines;
|
|
||||||
private List<Thing> things;
|
|
||||||
private List<Sector> sectors;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region ================== Properties
|
|
||||||
|
|
||||||
public List<Linedef> Lines { get { return lines; } }
|
|
||||||
public List<Thing> Things { get { return things; } }
|
|
||||||
public List<Sector> Sectors { get { return sectors; } }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region ================== Constructor
|
|
||||||
|
|
||||||
// Constructor for empty block
|
|
||||||
internal VisualBlockEntry()
|
internal VisualBlockEntry()
|
||||||
{
|
{
|
||||||
lines = new List<Linedef>(2);
|
Lines = new List<Linedef>(2);
|
||||||
things = new List<Thing>(2);
|
Things = new List<Thing>(2);
|
||||||
sectors = new List<Sector>(2);
|
Sectors = new List<Sector>(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ using System.Collections.Generic;
|
||||||
using CodeImp.DoomBuilder.Map;
|
using CodeImp.DoomBuilder.Map;
|
||||||
using CodeImp.DoomBuilder.Geometry;
|
using CodeImp.DoomBuilder.Geometry;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -28,395 +30,294 @@ namespace CodeImp.DoomBuilder.VisualModes
|
||||||
{
|
{
|
||||||
public sealed class VisualBlockMap
|
public sealed class VisualBlockMap
|
||||||
{
|
{
|
||||||
#region ================== Constants
|
|
||||||
|
|
||||||
public const int BLOCK_SIZE_SHIFT = 7;
|
|
||||||
public const int BLOCK_SIZE = 1 << BLOCK_SIZE_SHIFT;
|
|
||||||
public const float BLOCK_RADIUS = BLOCK_SIZE * Angle2D.SQRT2;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region ================== Variables
|
|
||||||
|
|
||||||
// Blocks
|
|
||||||
private Dictionary<ulong, VisualBlockEntry> blockmap;
|
|
||||||
|
|
||||||
// State
|
|
||||||
private bool isdisposed;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region ================== Properties
|
|
||||||
|
|
||||||
public bool IsDisposed { get { return isdisposed; } }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region ================== Constructor / Disposer
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
internal VisualBlockMap()
|
|
||||||
{
|
|
||||||
// Initialize
|
|
||||||
blockmap = new Dictionary<ulong,VisualBlockEntry>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disposer
|
|
||||||
internal void Dispose()
|
|
||||||
{
|
|
||||||
// Not already disposed?
|
|
||||||
if(!isdisposed)
|
|
||||||
{
|
|
||||||
// Clean up
|
|
||||||
blockmap = null;
|
|
||||||
|
|
||||||
// Done
|
|
||||||
isdisposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region ================== Methods
|
|
||||||
|
|
||||||
// This returns the block coordinates
|
|
||||||
public Point GetBlockCoordinates(Vector2D v)
|
|
||||||
{
|
|
||||||
return new Point((int)v.x >> BLOCK_SIZE_SHIFT,
|
|
||||||
(int)v.y >> BLOCK_SIZE_SHIFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This returns the block center in world coordinates
|
|
||||||
public Vector2D GetBlockCenter(Point p)
|
|
||||||
{
|
|
||||||
return new Vector2D((p.X << BLOCK_SIZE_SHIFT) + (BLOCK_SIZE >> 1),
|
|
||||||
(p.Y << BLOCK_SIZE_SHIFT) + (BLOCK_SIZE >> 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// This returns the key for a block at the given coordinates
|
|
||||||
// TODO: Could we just use the Point struct as key?
|
|
||||||
private static ulong GetBlockKey(Point p)
|
|
||||||
{
|
|
||||||
return unchecked( ((ulong)(uint)p.X << 32) + (uint)p.Y );
|
|
||||||
}
|
|
||||||
|
|
||||||
// This returns the block with the given coordinates
|
|
||||||
// Creates the block if it doesn't exist yet
|
|
||||||
public VisualBlockEntry GetBlock(Point p)
|
|
||||||
{
|
|
||||||
ulong k = GetBlockKey(p);
|
|
||||||
if(blockmap.ContainsKey(k))
|
|
||||||
{
|
|
||||||
return blockmap[k];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (blockmap[k] = new VisualBlockEntry());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This clears the blockmap
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
blockmap = new Dictionary<ulong,VisualBlockEntry>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This returns a range of blocks in a square
|
|
||||||
public List<VisualBlockEntry> GetSquareRange(RectangleF rect)
|
|
||||||
{
|
|
||||||
// Calculate block coordinates
|
|
||||||
Point lt = GetBlockCoordinates(new Vector2D(rect.Left, rect.Top));
|
|
||||||
Point rb = GetBlockCoordinates(new Vector2D(rect.Right, rect.Bottom));
|
|
||||||
|
|
||||||
// Go through the range to make a list
|
|
||||||
int entriescount = (rb.X - lt.X) * (rb.Y - lt.Y);
|
|
||||||
List<VisualBlockEntry> entries = new List<VisualBlockEntry>(entriescount);
|
|
||||||
for(int x = lt.X; x <= rb.X; x++)
|
|
||||||
{
|
|
||||||
for(int y = lt.Y; y <= rb.Y; y++)
|
|
||||||
{
|
|
||||||
entries.Add(GetBlock(new Point(x, y)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return list
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This returns a range of blocks in a frustum
|
|
||||||
public List<VisualBlockEntry> GetFrustumRange(ProjectedFrustum2D frustum)
|
|
||||||
{
|
|
||||||
// Make square range from frustum circle
|
|
||||||
// This will be the range in which we will test blocks
|
|
||||||
Point lt = GetBlockCoordinates(frustum.Center - frustum.Radius);
|
|
||||||
Point rb = GetBlockCoordinates(frustum.Center + frustum.Radius);
|
|
||||||
|
|
||||||
// Constants we need
|
|
||||||
float blockfrustumdistance2 = (frustum.Radius * frustum.Radius) + (BLOCK_RADIUS * BLOCK_RADIUS);
|
|
||||||
|
|
||||||
// Go through the range to make a list
|
|
||||||
int entriescount = (rb.X - lt.X) * (rb.Y - lt.Y);
|
|
||||||
List<VisualBlockEntry> entries = new List<VisualBlockEntry>(entriescount);
|
|
||||||
for(int x = lt.X; x <= rb.X; x++)
|
|
||||||
{
|
|
||||||
for(int y = lt.Y; y <= rb.Y; y++)
|
|
||||||
{
|
|
||||||
// First check if the block circle is intersecting the frustum circle
|
|
||||||
Point block = new Point(x, y);
|
|
||||||
Vector2D blockcenter = GetBlockCenter(block);
|
|
||||||
if(Vector2D.DistanceSq(frustum.Center, blockcenter) < blockfrustumdistance2)
|
|
||||||
{
|
|
||||||
// Add the block if the block circle is inside the frustum
|
|
||||||
if(frustum.IntersectCircle(blockcenter, BLOCK_RADIUS)) entries.Add(GetBlock(block));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return list
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This returns all blocks along the given line
|
// This returns all blocks along the given line
|
||||||
public List<VisualBlockEntry> GetLineBlocks(Vector2D v1, Vector2D v2)
|
public List<VisualBlockEntry> GetLineBlocks(Vector2D v1, Vector2D v2)
|
||||||
{
|
{
|
||||||
// Estimate number of blocks we will go through and create list
|
int x0 = (int)Math.Floor(Math.Min(v1.x, v2.x));
|
||||||
int entriescount = (int)(Vector2D.ManhattanDistance(v1, v2) * 2.0f) / BLOCK_SIZE;
|
int y0 = (int)Math.Floor(Math.Min(v1.y, v2.y));
|
||||||
List<VisualBlockEntry> entries = new List<VisualBlockEntry>(entriescount);
|
int x1 = (int)Math.Floor(Math.Max(v1.x, v2.x)) + 1;
|
||||||
|
int y1 = (int)Math.Floor(Math.Max(v1.y, v2.y)) + 1;
|
||||||
|
|
||||||
// Find start and end block
|
var result = new List<VisualBlockEntry>();
|
||||||
Point pos = GetBlockCoordinates(v1);
|
root.GetBlocks(new Rectangle(x0, y0, x1 - x0, y1 - y0), ref result);
|
||||||
Point end = GetBlockCoordinates(v2);
|
return result;
|
||||||
|
|
||||||
// Add this block
|
|
||||||
entries.Add(GetBlock(pos));
|
|
||||||
|
|
||||||
// Moving outside the block?
|
|
||||||
if(pos != end)
|
|
||||||
{
|
|
||||||
// Calculate current block edges
|
|
||||||
float cl = pos.X * BLOCK_SIZE;
|
|
||||||
float cr = (pos.X + 1) * BLOCK_SIZE;
|
|
||||||
float ct = pos.Y * BLOCK_SIZE;
|
|
||||||
float cb = (pos.Y + 1) * BLOCK_SIZE;
|
|
||||||
|
|
||||||
// Line directions
|
|
||||||
int dirx = Math.Sign(v2.x - v1.x);
|
|
||||||
int diry = Math.Sign(v2.y - v1.y);
|
|
||||||
|
|
||||||
// Calculate offset and delta movement over x
|
|
||||||
float posx, deltax;
|
|
||||||
if(dirx >= 0)
|
|
||||||
{
|
|
||||||
posx = (cr - v1.x) / (v2.x - v1.x);
|
|
||||||
deltax = BLOCK_SIZE / (v2.x - v1.x);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Calculate offset and delta movement over x
|
|
||||||
posx = (v1.x - cl) / (v1.x - v2.x);
|
|
||||||
deltax = BLOCK_SIZE / (v1.x - v2.x);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate offset and delta movement over y
|
public List<VisualBlockEntry> GetBlocks(RectangleF box)
|
||||||
float posy, deltay;
|
|
||||||
if(diry >= 0)
|
|
||||||
{
|
{
|
||||||
posy = (cb - v1.y) / (v2.y - v1.y);
|
var result = new List<VisualBlockEntry>();
|
||||||
deltay = BLOCK_SIZE / (v2.y - v1.y);
|
root.GetBlocks(ToRectangle(box), ref result);
|
||||||
}
|
return result;
|
||||||
else
|
|
||||||
{
|
|
||||||
posy = (v1.y - ct) / (v1.y - v2.y);
|
|
||||||
deltay = BLOCK_SIZE / (v1.y - v2.y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue while not reached the end
|
public List<VisualBlockEntry> GetBlocks(Vector2D pos)
|
||||||
while(pos != end)
|
|
||||||
{
|
{
|
||||||
// Check in which direction to move
|
var result = new List<VisualBlockEntry>();
|
||||||
if(posx < posy)
|
root.GetBlocks(new Point((int)Math.Floor(pos.x), (int)Math.Floor(pos.y)), ref result);
|
||||||
{
|
return result;
|
||||||
// Move horizontally
|
|
||||||
posx += deltax;
|
|
||||||
if(pos.X != end.X) pos.X += dirx;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Move vertically
|
|
||||||
posy += deltay;
|
|
||||||
if(pos.Y != end.Y) pos.Y += diry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add lines to this block
|
// This returns a range of blocks in a frustum
|
||||||
entries.Add(GetBlock(pos));
|
public List<VisualBlockEntry> GetFrustumRange(ProjectedFrustum2D frustum2D)
|
||||||
}
|
{
|
||||||
|
var frustum = new Frustum();
|
||||||
|
frustum.planes = new Plane[4]
|
||||||
|
{
|
||||||
|
new Plane(frustum2D.Lines[0]),
|
||||||
|
new Plane(frustum2D.Lines[1]),
|
||||||
|
new Plane(frustum2D.Lines[2]),
|
||||||
|
new Plane(frustum2D.Lines[3])
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = new List<VisualBlockEntry>();
|
||||||
|
root.GetBlocks(frustum, ref result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return list
|
public Sector GetSectorAt(Vector2D pos)
|
||||||
return entries;
|
{
|
||||||
|
foreach (VisualBlockEntry e in GetBlocks(pos))
|
||||||
|
{
|
||||||
|
foreach (Sector s in e.Sectors)
|
||||||
|
{
|
||||||
|
if (s.Intersect(pos))
|
||||||
|
{
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This puts a thing in the blockmap
|
public void Clear()
|
||||||
public void AddThingsSet(ICollection<Thing> things)
|
|
||||||
{
|
{
|
||||||
foreach(Thing t in things) AddThing(t);
|
root = new Node(new Rectangle(MapMinX, MapMinY, MapMaxX - MapMinX, MapMaxY - MapMinY));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This puts a thing in the blockmap
|
|
||||||
public void AddThing(Thing t)
|
|
||||||
{
|
|
||||||
//mxd
|
|
||||||
Point p1 = GetBlockCoordinates(new Vector2D(t.Position.x - t.Size, t.Position.y - t.Size));
|
|
||||||
Point p2 = GetBlockCoordinates(new Vector2D(t.Position.x + t.Size, t.Position.y + t.Size));
|
|
||||||
for(int x = p1.X; x <= p2.X; x++)
|
|
||||||
{
|
|
||||||
for(int y = p1.Y; y <= p2.Y; y++)
|
|
||||||
{
|
|
||||||
VisualBlockEntry block = GetBlock(new Point(x, y));
|
|
||||||
block.Things.Add(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This puts a secotr in the blockmap
|
|
||||||
public void AddSectorsSet(ICollection<Sector> sectors)
|
public void AddSectorsSet(ICollection<Sector> sectors)
|
||||||
{
|
{
|
||||||
foreach (Sector s in sectors) AddSector(s);
|
foreach (Sector s in sectors) AddSector(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This puts a sector in the blockmap
|
|
||||||
public void AddSector(Sector s)
|
|
||||||
{
|
|
||||||
Point p1 = GetBlockCoordinates(new Vector2D(s.BBox.Left, s.BBox.Top));
|
|
||||||
Point p2 = GetBlockCoordinates(new Vector2D(s.BBox.Right, s.BBox.Bottom));
|
|
||||||
for(int x = p1.X; x <= p2.X; x++)
|
|
||||||
{
|
|
||||||
for(int y = p1.Y; y <= p2.Y; y++)
|
|
||||||
{
|
|
||||||
VisualBlockEntry block = GetBlock(new Point(x, y));
|
|
||||||
block.Sectors.Add(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This puts a whole set of linedefs in the blocks they cross
|
|
||||||
public void AddLinedefsSet(ICollection<Linedef> lines)
|
public void AddLinedefsSet(ICollection<Linedef> lines)
|
||||||
{
|
{
|
||||||
foreach(Linedef l in lines) AddLinedef(l);
|
foreach (Linedef line in lines) AddLinedef(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddThingsSet(ICollection<Thing> things)
|
||||||
|
{
|
||||||
|
foreach (Thing t in things) AddThing(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddSector(Sector sector)
|
||||||
|
{
|
||||||
|
root.GetEntry(ToRectangle(sector.BBox)).Sectors.Add(sector);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This puts a single linedef in all blocks it crosses
|
|
||||||
public void AddLinedef(Linedef line)
|
public void AddLinedef(Linedef line)
|
||||||
{
|
{
|
||||||
// Get coordinates
|
int x0 = (int)Math.Floor(Math.Min(line.Start.Position.x, line.End.Position.x));
|
||||||
Vector2D v1 = line.Start.Position;
|
int y0 = (int)Math.Floor(Math.Min(line.Start.Position.y, line.End.Position.y));
|
||||||
Vector2D v2 = line.End.Position;
|
int x1 = (int)Math.Floor(Math.Max(line.Start.Position.x, line.End.Position.x)) + 1;
|
||||||
|
int y1 = (int)Math.Floor(Math.Max(line.Start.Position.y, line.End.Position.y)) + 1;
|
||||||
|
root.GetEntry(new Rectangle(x0, y0, x1 - x0, y1 - y0)).Lines.Add(line);
|
||||||
|
}
|
||||||
|
|
||||||
// Find start and end block
|
public void AddThing(Thing thing)
|
||||||
Point pos = GetBlockCoordinates(v1);
|
{
|
||||||
Point end = GetBlockCoordinates(v2);
|
int x0 = (int)Math.Floor(thing.Position.x - thing.Size);
|
||||||
|
int x1 = (int)Math.Floor(thing.Position.x + thing.Size) + 1;
|
||||||
|
int y0 = (int)Math.Floor(thing.Position.y - thing.Size);
|
||||||
|
int y1 = (int)Math.Floor(thing.Position.y + thing.Size) + 1;
|
||||||
|
root.GetEntry(new Rectangle(x0, y0, x1 - x0, y1 - y0)).Things.Add(thing);
|
||||||
|
}
|
||||||
|
|
||||||
// Horizontal straight line?
|
internal void Dispose()
|
||||||
if(pos.Y == end.Y)
|
|
||||||
{
|
{
|
||||||
// Simple loop
|
Clear();
|
||||||
int dirx = Math.Sign(v2.x - v1.x);
|
}
|
||||||
for(int x = pos.X; x != end.X; x += dirx)
|
|
||||||
|
static Rectangle ToRectangle(RectangleF bbox)
|
||||||
{
|
{
|
||||||
GetBlock(new Point(x, pos.Y)).Lines.Add(line);
|
int x0 = (int)Math.Floor(bbox.Left);
|
||||||
|
int y0 = (int)Math.Floor(bbox.Top);
|
||||||
|
int x1 = (int)Math.Floor(bbox.Right) + 1;
|
||||||
|
int y1 = (int)Math.Floor(bbox.Bottom) + 1;
|
||||||
|
return new Rectangle(x0, y0, x1 - x0, y1 - y0);
|
||||||
}
|
}
|
||||||
GetBlock(end).Lines.Add(line);
|
|
||||||
}
|
const int MapMinX = -32768;
|
||||||
// Vertical straight line?
|
const int MapMinY = -32768;
|
||||||
else if(pos.X == end.X)
|
const int MapMaxX = 32768;
|
||||||
|
const int MapMaxY = 32768;
|
||||||
|
const int MaxLevels = 8;
|
||||||
|
|
||||||
|
Node root = new Node(new Rectangle(MapMinX, MapMinY, MapMaxX - MapMinX, MapMaxY - MapMinY));
|
||||||
|
|
||||||
|
struct Plane
|
||||||
{
|
{
|
||||||
// Simple loop
|
public Plane(Line2D line)
|
||||||
int diry = Math.Sign(v2.y - v1.y);
|
|
||||||
for(int y = pos.Y; y != end.Y; y += diry)
|
|
||||||
{
|
{
|
||||||
GetBlock(new Point(pos.X, y)).Lines.Add(line);
|
Vector2D dir = line.v2 - line.v1;
|
||||||
|
A = -dir.y;
|
||||||
|
B = dir.x;
|
||||||
|
D = -(line.v1.x * A + line.v1.y * B);
|
||||||
}
|
}
|
||||||
GetBlock(end).Lines.Add(line);
|
|
||||||
|
public float A, B, D;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Frustum
|
||||||
|
{
|
||||||
|
public Plane[] planes;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
enum Visibility { Inside, Intersecting, Outside };
|
||||||
|
|
||||||
|
public Node(Rectangle bbox)
|
||||||
|
{
|
||||||
|
this.bbox = bbox;
|
||||||
|
extents = new Vector2D(bbox.Width * 0.5f, bbox.Height * 0.5f);
|
||||||
|
center = new Vector2D(bbox.X + extents.x, bbox.Y + extents.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetBlocks(Frustum frustum, ref List<VisualBlockEntry> list)
|
||||||
|
{
|
||||||
|
Visibility vis = TestVisibility(frustum);
|
||||||
|
if (vis == Visibility.Inside)
|
||||||
|
{
|
||||||
|
GetAllBlocks(ref list);
|
||||||
|
}
|
||||||
|
else if (vis == Visibility.Intersecting)
|
||||||
|
{
|
||||||
|
if (visualBlock != null)
|
||||||
|
list.Add(visualBlock);
|
||||||
|
|
||||||
|
if (topLeft != null)
|
||||||
|
{
|
||||||
|
topLeft.GetBlocks(frustum, ref list);
|
||||||
|
topRight.GetBlocks(frustum, ref list);
|
||||||
|
bottomLeft.GetBlocks(frustum, ref list);
|
||||||
|
bottomRight.GetBlocks(frustum, ref list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetAllBlocks(ref List<VisualBlockEntry> list)
|
||||||
|
{
|
||||||
|
if (visualBlock != null)
|
||||||
|
list.Add(visualBlock);
|
||||||
|
|
||||||
|
if (topLeft != null)
|
||||||
|
{
|
||||||
|
topLeft.GetAllBlocks(ref list);
|
||||||
|
topRight.GetAllBlocks(ref list);
|
||||||
|
bottomLeft.GetAllBlocks(ref list);
|
||||||
|
bottomRight.GetAllBlocks(ref list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Visibility TestVisibility(Frustum frustum)
|
||||||
|
{
|
||||||
|
Visibility result = Visibility.Inside;
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
Visibility vis = TestFrustumLineVisibility(frustum.planes[i]);
|
||||||
|
if (vis == Visibility.Outside)
|
||||||
|
return Visibility.Outside;
|
||||||
|
else if (vis == Visibility.Intersecting)
|
||||||
|
result = Visibility.Intersecting;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Visibility TestFrustumLineVisibility(Plane plane)
|
||||||
|
{
|
||||||
|
float e = extents.x * Math.Abs(plane.A) + extents.y * Math.Abs(plane.B);
|
||||||
|
float s = center.x * plane.A + center.y * plane.B + plane.D;
|
||||||
|
if (s - e > 0.0)
|
||||||
|
return Visibility.Inside;
|
||||||
|
else if (s + e < 0)
|
||||||
|
return Visibility.Outside;
|
||||||
else
|
else
|
||||||
{
|
return Visibility.Intersecting;
|
||||||
// Add lines to this block
|
|
||||||
GetBlock(pos).Lines.Add(line);
|
|
||||||
|
|
||||||
// Moving outside the block?
|
|
||||||
if(pos != end)
|
|
||||||
{
|
|
||||||
// Calculate current block edges
|
|
||||||
float cl = pos.X * BLOCK_SIZE;
|
|
||||||
float cr = (pos.X + 1) * BLOCK_SIZE;
|
|
||||||
float ct = pos.Y * BLOCK_SIZE;
|
|
||||||
float cb = (pos.Y + 1) * BLOCK_SIZE;
|
|
||||||
|
|
||||||
// Line directions
|
|
||||||
int dirx = Math.Sign(v2.x - v1.x);
|
|
||||||
int diry = Math.Sign(v2.y - v1.y);
|
|
||||||
|
|
||||||
// Calculate offset and delta movement over x
|
|
||||||
float posx, deltax;
|
|
||||||
if(dirx == 0)
|
|
||||||
{
|
|
||||||
posx = float.MaxValue;
|
|
||||||
deltax = float.MaxValue;
|
|
||||||
}
|
|
||||||
else if(dirx > 0)
|
|
||||||
{
|
|
||||||
posx = (cr - v1.x) / (v2.x - v1.x);
|
|
||||||
deltax = BLOCK_SIZE / (v2.x - v1.x);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Calculate offset and delta movement over x
|
|
||||||
posx = (v1.x - cl) / (v1.x - v2.x);
|
|
||||||
deltax = BLOCK_SIZE / (v1.x - v2.x);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate offset and delta movement over y
|
public void GetBlocks(Point pos, ref List<VisualBlockEntry> list)
|
||||||
float posy, deltay;
|
|
||||||
if(diry == 0)
|
|
||||||
{
|
{
|
||||||
posy = float.MaxValue;
|
if (visualBlock != null)
|
||||||
deltay = float.MaxValue;
|
list.Add(visualBlock);
|
||||||
}
|
|
||||||
else if(diry > 0)
|
|
||||||
{
|
|
||||||
posy = (cb - v1.y) / (v2.y - v1.y);
|
|
||||||
deltay = BLOCK_SIZE / (v2.y - v1.y);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
posy = (v1.y - ct) / (v1.y - v2.y);
|
|
||||||
deltay = BLOCK_SIZE / (v1.y - v2.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue while not reached the end
|
if (topLeft != null)
|
||||||
while(pos != end)
|
|
||||||
{
|
{
|
||||||
// Check in which direction to move
|
if (topLeft.bbox.Contains(pos)) topLeft.GetBlocks(pos, ref list);
|
||||||
if(posx < posy)
|
if (topRight.bbox.Contains(pos)) topRight.GetBlocks(pos, ref list);
|
||||||
{
|
if (bottomLeft.bbox.Contains(pos)) bottomLeft.GetBlocks(pos, ref list);
|
||||||
// Move horizontally
|
if (bottomRight.bbox.Contains(pos)) bottomRight.GetBlocks(pos, ref list);
|
||||||
posx += deltax;
|
|
||||||
if(pos.X != end.X) pos.X += dirx;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Move vertically
|
|
||||||
posy += deltay;
|
|
||||||
if(pos.Y != end.Y) pos.Y += diry;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add lines to this block
|
|
||||||
GetBlock(pos).Lines.Add(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
public void GetBlocks(Rectangle box, ref List<VisualBlockEntry> list)
|
||||||
|
{
|
||||||
|
if (visualBlock != null)
|
||||||
|
list.Add(visualBlock);
|
||||||
|
|
||||||
|
if (topLeft != null)
|
||||||
|
{
|
||||||
|
if (topLeft.bbox.IntersectsWith(box)) topLeft.GetBlocks(box, ref list);
|
||||||
|
if (topRight.bbox.IntersectsWith(box)) topRight.GetBlocks(box, ref list);
|
||||||
|
if (bottomLeft.bbox.IntersectsWith(box)) bottomLeft.GetBlocks(box, ref list);
|
||||||
|
if (bottomRight.bbox.IntersectsWith(box)) bottomRight.GetBlocks(box, ref list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public VisualBlockEntry GetEntry(Rectangle box, int level = 0)
|
||||||
|
{
|
||||||
|
if (level == MaxLevels)
|
||||||
|
{
|
||||||
|
if (visualBlock == null)
|
||||||
|
visualBlock = new VisualBlockEntry();
|
||||||
|
return visualBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topLeft == null)
|
||||||
|
CreateChildren();
|
||||||
|
|
||||||
|
if (topLeft.bbox.Contains(box)) return topLeft.GetEntry(box, level + 1);
|
||||||
|
if (topRight.bbox.Contains(box)) return topRight.GetEntry(box, level + 1);
|
||||||
|
if (bottomLeft.bbox.Contains(box)) return bottomLeft.GetEntry(box, level + 1);
|
||||||
|
if (bottomRight.bbox.Contains(box)) return bottomRight.GetEntry(box, level + 1);
|
||||||
|
|
||||||
|
if (visualBlock == null)
|
||||||
|
visualBlock = new VisualBlockEntry();
|
||||||
|
return visualBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateChildren()
|
||||||
|
{
|
||||||
|
int x0 = bbox.X;
|
||||||
|
int x1 = bbox.X + bbox.Width / 2;
|
||||||
|
int x2 = bbox.X + bbox.Width;
|
||||||
|
int y0 = bbox.Y;
|
||||||
|
int y1 = bbox.Y + bbox.Height / 2;
|
||||||
|
int y2 = bbox.Y + bbox.Height;
|
||||||
|
topLeft = new Node(new Rectangle(x0, y0, x1 - x0, y1 - y0));
|
||||||
|
topRight = new Node(new Rectangle(x1, y0, x2 - x1, y1 - y0));
|
||||||
|
bottomLeft = new Node(new Rectangle(x0, y1, x1 - x0, y2 - y1));
|
||||||
|
bottomRight = new Node(new Rectangle(x1, y1, x2 - x1, y2 - y1));
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle bbox;
|
||||||
|
Vector2D extents;
|
||||||
|
Vector2D center;
|
||||||
|
|
||||||
|
Node topLeft;
|
||||||
|
Node topRight;
|
||||||
|
Node bottomLeft;
|
||||||
|
Node bottomRight;
|
||||||
|
VisualBlockEntry visualBlock;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -600,14 +600,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
||||||
// Get nearby things
|
// Get nearby things
|
||||||
List<Thing> neighbours = new List<Thing>();
|
List<Thing> neighbours = new List<Thing>();
|
||||||
RectangleF bbox = new RectangleF(thing.Position.x - thing.Size, thing.Position.y - thing.Size, thing.Size * 2, thing.Size * 2);
|
RectangleF bbox = new RectangleF(thing.Position.x - thing.Size, thing.Position.y - thing.Size, thing.Size * 2, thing.Size * 2);
|
||||||
Point p1 = mode.BlockMap.GetBlockCoordinates(new Vector2D(bbox.Left, bbox.Top));
|
foreach (var block in mode.BlockMap.GetBlocks(bbox))
|
||||||
Point p2 = mode.BlockMap.GetBlockCoordinates(new Vector2D(bbox.Right, bbox.Bottom));
|
|
||||||
for(int x = p1.X; x <= p2.X; x++)
|
|
||||||
{
|
{
|
||||||
for(int y = p1.Y; y <= p2.Y; y++)
|
neighbours.AddRange(block.Things);
|
||||||
{
|
|
||||||
neighbours.AddRange(mode.BlockMap.GetBlock(new Point(x, y)).Things);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect things intersecting with target thing
|
// Collect things intersecting with target thing
|
||||||
|
|
|
@ -885,8 +885,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
||||||
Vertex v = sd.IsFront ? sd.Line.End : sd.Line.Start;
|
Vertex v = sd.IsFront ? sd.Line.End : sd.Line.Start;
|
||||||
|
|
||||||
// Check if a thing is at this vertex
|
// Check if a thing is at this vertex
|
||||||
VisualBlockEntry b = blockmap.GetBlock(blockmap.GetBlockCoordinates(v.Position));
|
foreach (VisualBlockEntry block in blockmap.GetBlocks(v.Position))
|
||||||
foreach (Thing t in b.Things)
|
{
|
||||||
|
foreach (Thing t in block.Things)
|
||||||
{
|
{
|
||||||
if ((Vector2D)t.Position == v.Position)
|
if ((Vector2D)t.Position == v.Position)
|
||||||
{
|
{
|
||||||
|
@ -898,6 +899,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Slope any floor vertices?
|
// Slope any floor vertices?
|
||||||
if (slopefloorthings.Count > 0)
|
if (slopefloorthings.Count > 0)
|
||||||
|
|
Loading…
Reference in a new issue