2009-04-19 18:07:22 +00:00
|
|
|
|
|
|
|
#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.Generic;
|
|
|
|
using CodeImp.DoomBuilder.Map;
|
|
|
|
using CodeImp.DoomBuilder.Geometry;
|
|
|
|
using System.Drawing;
|
2019-10-19 14:52:02 +00:00
|
|
|
using System.Diagnostics;
|
|
|
|
using System.Linq;
|
2009-04-19 18:07:22 +00:00
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
namespace CodeImp.DoomBuilder.VisualModes
|
|
|
|
{
|
2019-12-25 23:39:15 +00:00
|
|
|
public sealed class VisualBlockMap
|
|
|
|
{
|
|
|
|
// This returns all blocks along the given line
|
|
|
|
public List<VisualBlockEntry> GetLineBlocks(Vector2D v1, Vector2D v2)
|
|
|
|
{
|
|
|
|
int x0 = (int)Math.Floor(Math.Min(v1.x, v2.x));
|
|
|
|
int y0 = (int)Math.Floor(Math.Min(v1.y, v2.y));
|
|
|
|
int x1 = (int)Math.Floor(Math.Max(v1.x, v2.x)) + 1;
|
|
|
|
int y1 = (int)Math.Floor(Math.Max(v1.y, v2.y)) + 1;
|
|
|
|
|
|
|
|
var result = new List<VisualBlockEntry>();
|
|
|
|
root.GetBlocks(new Rectangle(x0, y0, x1 - x0, y1 - y0), ref result);
|
|
|
|
return result;
|
2019-12-20 03:50:43 +00:00
|
|
|
}
|
2009-04-19 18:07:22 +00:00
|
|
|
|
2019-12-25 23:39:15 +00:00
|
|
|
public List<VisualBlockEntry> GetBlocks(RectangleF box)
|
|
|
|
{
|
|
|
|
var result = new List<VisualBlockEntry>();
|
|
|
|
root.GetBlocks(ToRectangle(box), ref result);
|
|
|
|
return result;
|
2019-12-20 03:50:43 +00:00
|
|
|
}
|
2019-12-25 23:39:15 +00:00
|
|
|
|
|
|
|
public List<VisualBlockEntry> GetBlocks(Vector2D pos)
|
|
|
|
{
|
|
|
|
var result = new List<VisualBlockEntry>();
|
|
|
|
root.GetBlocks(new Point((int)Math.Floor(pos.x), (int)Math.Floor(pos.y)), ref result);
|
|
|
|
return result;
|
2019-12-20 03:50:43 +00:00
|
|
|
}
|
2009-04-19 18:07:22 +00:00
|
|
|
|
2019-12-25 23:39:15 +00:00
|
|
|
// This returns a range of blocks in a frustum
|
|
|
|
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;
|
|
|
|
}
|
2009-04-19 18:07:22 +00:00
|
|
|
|
2019-12-25 23:39:15 +00:00
|
|
|
public Sector GetSectorAt(Vector2D pos)
|
|
|
|
{
|
2021-04-05 17:15:14 +00:00
|
|
|
List<Sector> sectors = new List<Sector>(1);
|
|
|
|
|
|
|
|
foreach (VisualBlockEntry e in GetBlocks(pos))
|
2019-12-25 23:39:15 +00:00
|
|
|
foreach (Sector s in e.Sectors)
|
|
|
|
if (s.Intersect(pos))
|
2021-04-05 17:15:14 +00:00
|
|
|
sectors.Add(s);
|
|
|
|
|
|
|
|
if (sectors.Count == 0)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
else if (sectors.Count == 1)
|
|
|
|
{
|
|
|
|
return sectors[0];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Having multiple intersections indicates that there are self-referencing sectors in this spot.
|
|
|
|
// In this case we have to check which side of the nearest linedef pos is on, and then use that sector
|
|
|
|
HashSet<Linedef> linedefs = new HashSet<Linedef>(sectors[0].Sidedefs.Count * sectors.Count);
|
|
|
|
|
|
|
|
foreach (Sector s in sectors)
|
|
|
|
foreach (Sidedef sd in s.Sidedefs)
|
|
|
|
linedefs.Add(sd.Line);
|
|
|
|
|
|
|
|
Linedef nearest = MapSet.NearestLinedef(linedefs, pos);
|
|
|
|
double d = nearest.SideOfLine(pos);
|
|
|
|
|
|
|
|
if (d <= 0.0 && nearest.Front != null)
|
|
|
|
return nearest.Front.Sector;
|
|
|
|
else if (nearest.Back != null)
|
|
|
|
return nearest.Back.Sector;
|
|
|
|
}
|
|
|
|
|
2019-12-25 23:39:15 +00:00
|
|
|
return null;
|
|
|
|
}
|
2009-04-19 18:07:22 +00:00
|
|
|
|
2019-12-25 23:39:15 +00:00
|
|
|
public void Clear()
|
|
|
|
{
|
2019-12-29 10:03:42 +00:00
|
|
|
root = new Node(new Rectangle(General.Map.Config.LeftBoundary, General.Map.Config.BottomBoundary, General.Map.Config.RightBoundary - General.Map.Config.LeftBoundary, General.Map.Config.TopBoundary - General.Map.Config.BottomBoundary));
|
2019-12-25 23:39:15 +00:00
|
|
|
}
|
2009-04-19 18:07:22 +00:00
|
|
|
|
|
|
|
public void AddSectorsSet(ICollection<Sector> sectors)
|
|
|
|
{
|
2019-12-25 23:39:15 +00:00
|
|
|
foreach (Sector s in sectors) AddSector(s);
|
2009-04-19 18:07:22 +00:00
|
|
|
}
|
|
|
|
|
2019-12-25 23:39:15 +00:00
|
|
|
public void AddLinedefsSet(ICollection<Linedef> lines)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void AddLinedef(Linedef line)
|
|
|
|
{
|
|
|
|
int x0 = (int)Math.Floor(Math.Min(line.Start.Position.x, line.End.Position.x));
|
|
|
|
int y0 = (int)Math.Floor(Math.Min(line.Start.Position.y, line.End.Position.y));
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void AddThing(Thing thing)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void Dispose()
|
2009-04-19 18:07:22 +00:00
|
|
|
{
|
2019-12-25 23:39:15 +00:00
|
|
|
Clear();
|
2009-04-19 18:07:22 +00:00
|
|
|
}
|
2019-12-25 23:39:15 +00:00
|
|
|
|
|
|
|
static Rectangle ToRectangle(RectangleF bbox)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
const int MaxLevels = 8;
|
|
|
|
|
2019-12-29 10:03:42 +00:00
|
|
|
Node root = new Node(new Rectangle(General.Map.Config.LeftBoundary, General.Map.Config.BottomBoundary, General.Map.Config.RightBoundary - General.Map.Config.LeftBoundary, General.Map.Config.TopBoundary - General.Map.Config.BottomBoundary));
|
2019-12-25 23:39:15 +00:00
|
|
|
|
|
|
|
struct Plane
|
|
|
|
{
|
|
|
|
public Plane(Line2D line)
|
|
|
|
{
|
|
|
|
Vector2D dir = line.v2 - line.v1;
|
|
|
|
A = -dir.y;
|
|
|
|
B = dir.x;
|
|
|
|
D = -(line.v1.x * A + line.v1.y * B);
|
|
|
|
}
|
|
|
|
|
2020-05-21 12:20:02 +00:00
|
|
|
public double A, B, D;
|
2019-12-25 23:39:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2020-05-21 12:20:02 +00:00
|
|
|
double e = extents.x * Math.Abs(plane.A) + extents.y * Math.Abs(plane.B);
|
|
|
|
double s = center.x * plane.A + center.y * plane.B + plane.D;
|
2019-12-25 23:39:15 +00:00
|
|
|
if (s - e > 0.0)
|
|
|
|
return Visibility.Inside;
|
|
|
|
else if (s + e < 0)
|
|
|
|
return Visibility.Outside;
|
|
|
|
else
|
|
|
|
return Visibility.Intersecting;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void GetBlocks(Point pos, ref List<VisualBlockEntry> list)
|
|
|
|
{
|
|
|
|
if (visualBlock != null)
|
|
|
|
list.Add(visualBlock);
|
|
|
|
|
|
|
|
if (topLeft != null)
|
|
|
|
{
|
|
|
|
if (topLeft.bbox.Contains(pos)) topLeft.GetBlocks(pos, ref list);
|
|
|
|
if (topRight.bbox.Contains(pos)) topRight.GetBlocks(pos, ref list);
|
|
|
|
if (bottomLeft.bbox.Contains(pos)) bottomLeft.GetBlocks(pos, ref list);
|
|
|
|
if (bottomRight.bbox.Contains(pos)) bottomRight.GetBlocks(pos, ref list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2009-04-19 18:07:22 +00:00
|
|
|
}
|