visual mode extreme makeover! (internally at least) now using proper view frustum for culling and lots of other improvements to render the map more efficiently

This commit is contained in:
codeimp 2008-11-30 02:17:19 +00:00
parent 5dcc9d584d
commit 43a0e69de7
23 changed files with 587 additions and 234 deletions

View file

@ -650,8 +650,10 @@
<None Include="Resources\Close.png" />
<Compile Include="Editing\EditingManager.cs" />
<Content Include="Resources\DB2.ico" />
<Compile Include="Geometry\ProjectedFrustum2D.cs" />
<Compile Include="VisualModes\Clipper.cs" />
<Compile Include="VisualModes\VisualBlockEntry.cs" />
<Compile Include="VisualModes\VisualSidedef.cs" />
<None Include="Resources\Script2.png" />
<None Include="Resources\ScriptCompile.png" />
<None Include="Resources\ScriptConstant.xpm" />

View file

@ -217,8 +217,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Go for all drawn points
foreach(DrawnVertex v in points)
{
Vector2D delta = mousemappos - v.pos;
if(delta.GetLengthSq() < (vrange * vrange))
if(Vector2D.DistanceSq(mousemappos, v.pos) < (vrange * vrange))
{
p.pos = v.pos;
p.stitch = true;

View file

@ -63,6 +63,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Load floor texture
base.Texture = General.Map.Data.GetFlatImage(s.LongCeilTexture);
if(base.Texture == null) base.Texture = General.Map.Data.MissingTexture3D;
// Make vertices
verts = new WorldVertex[s.Triangles.Vertices.Count];

View file

@ -62,6 +62,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Load floor texture
base.Texture = General.Map.Data.GetFlatImage(s.LongFloorTexture);
if(base.Texture == null) base.Texture = General.Map.Data.MissingTexture3D;
// Make vertices
verts = new WorldVertex[s.Triangles.Vertices.Count];

View file

@ -39,7 +39,7 @@ using CodeImp.DoomBuilder.VisualModes;
namespace CodeImp.DoomBuilder.BuilderModes
{
internal class VisualLower : VisualGeometry
internal class VisualLower : VisualSidedef
{
#region ================== Constants
@ -56,7 +56,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
#region ================== Constructor / Disposer
// Constructor
public VisualLower(Sidedef s)
public VisualLower(Sidedef s) : base(s)
{
WorldVertex[] verts;
float geotop;
@ -75,6 +75,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
// Load texture
base.Texture = General.Map.Data.GetTextureImage(s.LongLowTexture);
if(base.Texture == null) base.Texture = General.Map.Data.MissingTexture3D;
}
else
{

View file

@ -39,7 +39,7 @@ using CodeImp.DoomBuilder.VisualModes;
namespace CodeImp.DoomBuilder.BuilderModes
{
internal class VisualMiddle : VisualGeometry
internal class VisualMiddle : VisualSidedef
{
#region ================== Constants
@ -56,7 +56,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
#region ================== Constructor / Disposer
// Constructor
public VisualMiddle(Sidedef s)
public VisualMiddle(Sidedef s) : base(s)
{
WorldVertex[] verts;
float geotop;
@ -84,6 +84,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
// Load texture
base.Texture = General.Map.Data.GetTextureImage(s.LongMiddleTexture);
if(base.Texture == null) base.Texture = General.Map.Data.MissingTexture3D;
}
else
{

View file

@ -39,7 +39,7 @@ using CodeImp.DoomBuilder.VisualModes;
namespace CodeImp.DoomBuilder.BuilderModes
{
internal class VisualUpper : VisualGeometry
internal class VisualUpper : VisualSidedef
{
#region ================== Constants
@ -56,7 +56,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
#region ================== Constructor / Disposer
// Constructor
public VisualUpper(Sidedef s)
public VisualUpper(Sidedef s) : base(s)
{
WorldVertex[] verts;
float geotop;
@ -75,6 +75,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
// Load texture
base.Texture = General.Map.Data.GetTextureImage(s.LongHighTexture);
if(base.Texture == null) base.Texture = General.Map.Data.MissingTexture3D;
}
else
{

View file

@ -1208,6 +1208,16 @@ namespace CodeImp.DoomBuilder
#region ================== Methods
// This updates everything after the configuration or settings have been changed
internal void UpdateConfiguration()
{
// Update map
map.UpdateConfiguration();
// Update settings
renderer3d.CreateProjection();
}
// This changes thing filter
internal void ChangeThingFilter(ThingsFilter newfilter)
{

View file

@ -1,4 +1,4 @@
#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
@ -34,7 +34,8 @@ namespace CodeImp.DoomBuilder.Geometry
public const float PIHALF = (float)Math.PI * 0.5f;
public const float PI2 = (float)Math.PI * 2f;
public const float PIDEG = 57.295779513082320876798154814105f;
public const float SQRT2 = 1.4142135623730950488016887242097f;
#endregion
#region ================== Methods

View file

@ -215,6 +215,12 @@ namespace CodeImp.DoomBuilder.Geometry
public float GetLength() { return Line2D.GetLength(v2.x - v1.x, v2.y - v1.y); }
public float GetLengthSq() { return Line2D.GetLengthSq(v2.x - v1.x, v2.y - v1.y); }
// Output
public override string ToString()
{
return "(" + v1 + ") - (" + v2 + ")";
}
public bool GetIntersection(float x3, float y3, float x4, float y4)
{
return Line2D.GetIntersection(v1, v2, x3, y3, x4, y4);

View file

@ -0,0 +1,156 @@
#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 CodeImp.DoomBuilder.VisualModes;
#endregion
namespace CodeImp.DoomBuilder.Geometry
{
public class ProjectedFrustum2D
{
#region ================== Variables
// Frustum settings
private float near;
private float far;
private float fov;
private Vector2D pos;
private float xyangle;
private float zangle;
// Frustum lines
private Line2D[] lines;
// Circle
private Vector2D center;
private float radius;
#endregion
#region ================== Properties
public float Near { get { return near; } }
public float Far { get { return far; } }
public float Fov { get { return fov; } }
public Vector2D Position { get { return pos; } }
public float XYAngle { get { return xyangle; } }
public float ZAngle { get { return zangle; } }
public Line2D[] Lines { get { return lines; } }
public Vector2D Center { get { return center; } }
public float Radius { get { return radius; } }
#endregion
#region ================== Constructor / Destructor
// Constructor
public ProjectedFrustum2D(Vector2D pos, float xyangle, float zangle, float near, float far, float fov)
{
Vector2D[] forwards = new Vector2D[4];
Vector2D[] downwards = new Vector2D[4];
Vector2D[] corners = new Vector2D[4];
// Initialize
this.pos = pos;
this.xyangle = xyangle;
this.zangle = zangle;
this.near = near;
this.far = far;
this.fov = fov;
// Make the corners for a forward frustum
// The corners are in this order: Left-Far, Right-Far, Left-Near, Right-Near
float fovhalf = fov * 0.5f;
float fovhalfcos = (float)Math.Cos(fovhalf);
float farsidelength = far / fovhalfcos;
float nearsidelength = near / fovhalfcos;
forwards[0] = pos + Vector2D.FromAngle(xyangle - fovhalf, farsidelength);
forwards[1] = pos + Vector2D.FromAngle(xyangle + fovhalf, farsidelength);
forwards[2] = pos + Vector2D.FromAngle(xyangle - fovhalf, nearsidelength);
forwards[3] = pos + Vector2D.FromAngle(xyangle + fovhalf, nearsidelength);
// Make the corners for a downward frustum
// The corners are in the same order as above
//float farradius = far * (float)Math.Tan(fovhalf) * Angle2D.SQRT2;
float farradius = far * 0.5f * Angle2D.SQRT2;
downwards[0] = pos + Vector2D.FromAngle(xyangle - Angle2D.PI * 0.25f, farradius);
downwards[1] = pos + Vector2D.FromAngle(xyangle + Angle2D.PI * 0.25f, farradius);
downwards[2] = pos + Vector2D.FromAngle(xyangle - Angle2D.PI * 0.75f, farradius);
downwards[3] = pos + Vector2D.FromAngle(xyangle + Angle2D.PI * 0.75f, farradius);
// Interpolate between the two to make the final corners depending on the z angle
float d = Math.Abs((float)Math.Sin(zangle));
corners[0] = forwards[0] * (1.0f - d) + downwards[0] * d;
corners[1] = forwards[1] * (1.0f - d) + downwards[1] * d;
corners[2] = forwards[2] * (1.0f - d) + downwards[2] * d;
corners[3] = forwards[3] * (1.0f - d) + downwards[3] * d;
// Make the frustum lines
// Note that the lines all have their right side inside the frustum!
lines = new Line2D[4];
lines[0] = new Line2D(corners[2], corners[0]);
lines[1] = new Line2D(corners[1], corners[3]);
lines[2] = new Line2D(corners[3], corners[2]);
lines[3] = new Line2D(corners[0], corners[1]);
// Calculate the circle center
center = (corners[0] + corners[1] + corners[2] + corners[3]) * 0.25f;
// Calculate the radius from the center to the farthest corner
float radius2 = 0.0f;
for(int i = 0; i < corners.Length; i++)
{
float distance2 = Vector2D.DistanceSq(center, corners[i]);
if(distance2 > radius2) radius2 = distance2;
}
radius = (float)Math.Sqrt(radius2);
}
#endregion
#region ================== Methods
// This checks if a specified circle is intersecting the frustum
// NOTE: This checks only against the actual frustum and does not use the frustum circle!
public bool IntersectCircle(Vector2D circlecenter, float circleradius)
{
// Go for all frustum lines
for(int i = 0; i < lines.Length; i++)
{
// Check on which side the circle center lies
if(lines[i].GetSideOfLine(circlecenter) < 0)
{
// Center is outside the frustum
// If the circle is not overlapping, it is not intersecting.
if(lines[i].GetDistanceToLineSq(circlecenter, false) > (circleradius * circleradius)) return false;
}
}
// Intersecting!
return true;
}
#endregion
}
}

View file

@ -221,6 +221,20 @@ namespace CodeImp.DoomBuilder.Geometry
return -(float)Math.Atan2(-(a.y - b.y), (a.x - b.x)) + (float)Math.PI * 0.5f;
}
// This returns the square distance between two points
public static float DistanceSq(Vector2D a, Vector2D b)
{
Vector2D d = a - b;
return d.GetLengthSq();
}
// This returns the distance between two points
public static float Distance(Vector2D a, Vector2D b)
{
Vector2D d = a - b;
return d.GetLength();
}
#endregion
#region ================== Methods

View file

@ -217,8 +217,7 @@ namespace CodeImp.DoomBuilder.Geometry
Vector2D xy = new Vector2D(x, y);
// Calculate and return the angle
return -(float)Math.Atan2(xy.GetLength(), z) + (float)Math.PI * 0.5f;
//return -(float)Math.Atan2(xy.GetLength(), z) - (float)Math.PI * 0.5f;
return (float)Math.Atan2(xy.GetLength(), z) + (float)Math.PI * 0.5f;
}
// This calculates the length

View file

@ -243,14 +243,11 @@ namespace CodeImp.DoomBuilder.Map
// This updates the line when changes have been made
public void UpdateCache()
{
Vector2D delta;
float l, t, r, b;
// Update if needed
if(updateneeded)
{
// Delta vector
delta = end.Position - start.Position;
Vector2D delta = end.Position - start.Position;
// Recalculate values
lengthsq = delta.GetLengthSq();
@ -258,10 +255,10 @@ namespace CodeImp.DoomBuilder.Map
if(length > 0f) lengthinv = 1f / length; else lengthinv = 1f / 0.0000000001f;
if(lengthsq > 0f) lengthsqinv = 1f / lengthsq; else lengthsqinv = 1f / 0.0000000001f;
angle = delta.GetAngle();
l = Math.Min(start.Position.x, end.Position.x);
t = Math.Min(start.Position.y, end.Position.y);
r = Math.Max(start.Position.x, end.Position.x);
b = Math.Max(start.Position.y, end.Position.y);
float l = Math.Min(start.Position.x, end.Position.x);
float t = Math.Min(start.Position.y, end.Position.y);
float r = Math.Max(start.Position.x, end.Position.x);
float b = Math.Max(start.Position.y, end.Position.y);
rect = new RectangleF(l, t, r - l, b - t);
// Updated

View file

@ -296,15 +296,13 @@ namespace CodeImp.DoomBuilder.Map
// This returns the distance from given coordinates
public float DistanceToSq(Vector2D p)
{
Vector2D delta = p - (Vector2D)pos;
return delta.GetLengthSq();
return Vector2D.DistanceSq(p, pos);
}
// This returns the distance from given coordinates
public float DistanceTo(Vector2D p)
{
Vector2D delta = p - (Vector2D)pos;
return delta.GetLength();
return Vector2D.Distance(p, pos);
}
#endregion

View file

@ -154,8 +154,7 @@ namespace CodeImp.DoomBuilder.Map
// This returns the distance from given coordinates
public float DistanceTo(Vector2D p)
{
Vector2D delta = p - pos;
return delta.GetLength();
return Vector2D.Distance(p, pos);
}
// This finds the line closest to the specified position

View file

@ -41,11 +41,19 @@ namespace CodeImp.DoomBuilder.Rendering
{
public interface IRenderer3D
{
// Properties
ProjectedFrustum2D Frustum2D { get; }
// General methods
void PositionAndLookAt(Vector3D pos, Vector3D lookat);
// Presenting methods
void Finish();
bool Start();
void StartGeometry();
void FinishGeometry();
void RenderGeometry(VisualSector s);
void PositionAndLookAt(Vector3D pos, Vector3D lookat);
// Rendering methods
void RenderGeometry(VisualGeometry g);
}
}

View file

@ -41,7 +41,6 @@ namespace CodeImp.DoomBuilder.Rendering
#region ================== Constants
private const float PROJ_NEAR_PLANE = 1f;
private const float PROJ_FAR_PLANE = 5000f;
#endregion
@ -53,10 +52,18 @@ namespace CodeImp.DoomBuilder.Rendering
private Matrix billboard;
private Matrix viewproj;
// Frustum
private ProjectedFrustum2D frustum;
// Geometry, grouped by texture
private Dictionary<ImageData, List<VisualGeometry>> geometry;
#endregion
#region ================== Properties
public ProjectedFrustum2D Frustum2D { get { return frustum; } }
#endregion
#region ================== Constructor / Disposer
@ -66,6 +73,10 @@ namespace CodeImp.DoomBuilder.Rendering
{
// Initialize
CreateProjection();
// Dummy frustum
frustum = new ProjectedFrustum2D(new Vector2D(), 0.0f, 0.0f, PROJ_NEAR_PLANE,
General.Settings.ViewDistance, Angle2D.DegToRad((float)General.Settings.VisualFOV));
// We have no destructor
GC.SuppressFinalize(this);
@ -107,15 +118,22 @@ namespace CodeImp.DoomBuilder.Rendering
// This creates the projection
internal void CreateProjection()
{
// Calculate FOV
float fov = Angle2D.DegToRad((float)General.Settings.VisualFOV);
// Calculate aspect
float aspect = (float)General.Map.Graphics.RenderTarget.ClientSize.Width /
(float)General.Map.Graphics.RenderTarget.ClientSize.Height;
// The DirectX PerspectiveFovRH matrix method calculates the scaling in X and Y as follows:
// yscale = 1 / tan(fovY / 2)
// xscale = yscale / aspect
// The fov specified in the method is the FOV over Y, but we want the user to specify the FOV
// over X, so calculate what it would be over Y first;
float fov = Angle2D.DegToRad((float)General.Settings.VisualFOV);
float reversefov = 1.0f / (float)Math.Tan(fov / 2.0f);
float reversefovy = reversefov * aspect;
float fovy = (float)Math.Atan(1.0f / reversefovy) * 2.0f;
// Make the projection matrix
projection = Matrix.PerspectiveFovRH(fov, aspect, PROJ_NEAR_PLANE, PROJ_FAR_PLANE);
projection = Matrix.PerspectiveFovRH(fovy, aspect, PROJ_NEAR_PLANE, General.Settings.ViewDistance);
// Apply matrices
ApplyMatrices();
@ -131,12 +149,16 @@ namespace CodeImp.DoomBuilder.Rendering
delta = lookat - pos;
anglexy = delta.GetAngleXY();
anglez = delta.GetAngleZ();
// Create frustum
frustum = new ProjectedFrustum2D(pos, anglexy, anglez, PROJ_NEAR_PLANE,
General.Settings.ViewDistance, Angle2D.DegToRad((float)General.Settings.VisualFOV));
// Make the view matrix
view = Matrix.LookAtRH(D3DDevice.V3(pos), D3DDevice.V3(lookat), new Vector3(0f, 0f, 1f));
// Make the billboard matrix
billboard = Matrix.RotationYawPitchRoll(0f, anglexy, anglez - Angle2D.PIHALF);
billboard = Matrix.RotationYawPitchRoll(0f, anglexy, anglez - Angle2D.PI);
}
// This applies the matrices
@ -195,14 +217,57 @@ namespace CodeImp.DoomBuilder.Rendering
graphics.Shaders.World3D.Begin();
graphics.Shaders.World3D.WorldViewProj = viewproj;
graphics.Shaders.World3D.BeginPass(0);
// Make collection
geometry = new Dictionary<ImageData, List<VisualGeometry>>();
}
// This ends rendering world geometry
public void FinishGeometry()
{
// We now render the actual geometry collected
foreach(KeyValuePair<ImageData, List<VisualGeometry>> group in geometry)
{
ImageData curtexture;
// What texture to use?
if((group.Key != null) && group.Key.IsImageLoaded && !group.Key.IsDisposed)
curtexture = group.Key;
else
curtexture = General.Map.Data.Hourglass3D;
// Create Direct3D texture if still needed
if((curtexture.Texture == null) || curtexture.Texture.Disposed)
curtexture.CreateTexture();
// Apply texture
graphics.Device.SetTexture(0, curtexture.Texture);
graphics.Shaders.World3D.Texture1 = curtexture.Texture;
graphics.Shaders.World3D.ApplySettings();
// Go for all geometry that uses this texture
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)
{
// Render!
graphics.Device.SetStreamSource(0, g.Sector.GeometryBuffer, 0, WorldVertex.Stride);
graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, g.VertexOffset, g.Triangles);
}
}
}
// Remove references
graphics.Shaders.World3D.Texture1 = null;
// Done
graphics.Shaders.World3D.EndPass();
graphics.Shaders.World3D.End();
geometry = null;
}
// This finishes rendering
@ -218,52 +283,20 @@ namespace CodeImp.DoomBuilder.Rendering
#region ================== Geometry
// This renders a visual sector's geometry
public void RenderGeometry(VisualSector s)
public void RenderGeometry(VisualGeometry g)
{
ImageData lasttexture = null;
// Update the sector if needed
if(s.NeedsUpdateGeo) s.Update();
// Only render when a vertexbuffer exists
if(s.GeometryBuffer != null)
// Must have a texture!
if(g.Texture != null)
{
// Set the buffer
graphics.Device.SetStreamSource(0, s.GeometryBuffer, 0, WorldVertex.Stride);
// Go for all geometry in this sector
foreach(VisualGeometry g in s.GeometryList)
// Texture group not yet collected?
if(!geometry.ContainsKey(g.Texture))
{
ImageData curtexture;
// What texture to use?
if((g.Texture != null) && g.Texture.IsImageLoaded && !g.Texture.IsDisposed)
curtexture = g.Texture;
else
curtexture = General.Map.Data.Hourglass3D;
// Change texture?
if(curtexture != lasttexture)
{
// Now using this texture
lasttexture = curtexture;
// Create Direct3D texture if still needed
if((curtexture.Texture == null) || curtexture.Texture.Disposed)
curtexture.CreateTexture();
// Apply texture
graphics.Device.SetTexture(0, curtexture.Texture);
graphics.Shaders.World3D.Texture1 = curtexture.Texture;
graphics.Shaders.World3D.ApplySettings();
}
// Render it!
graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, g.VertexOffset, g.Triangles);
// Create texture group
geometry.Add(g.Texture, new List<VisualGeometry>());
}
// Remove references
graphics.Shaders.World3D.Texture1 = null;
// Add geometry to texture group
geometry[g.Texture].Add(g);
}
}

View file

@ -44,6 +44,7 @@ namespace CodeImp.DoomBuilder.VisualModes
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
@ -96,8 +97,16 @@ namespace CodeImp.DoomBuilder.VisualModes
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((float)((p.X << BLOCK_SIZE_SHIFT) + (BLOCK_SIZE >> 1)),
(float)((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 ulong GetBlockKey(Point p)
{
return unchecked( ((ulong)(uint)p.X << 32) + (ulong)(uint)p.Y );
@ -146,6 +155,39 @@ namespace CodeImp.DoomBuilder.VisualModes
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;
}
#endregion
#region ================== Advanced Methods

View file

@ -46,10 +46,9 @@ namespace CodeImp.DoomBuilder.VisualModes
#region ================== Constants
private const float ANGLE_FROM_MOUSE = 0.0001f;
private const float MAX_ANGLEZ_LOW = 100f / Angle2D.PIDEG;
private const float MAX_ANGLEZ_HIGH = (360f - 100f) / Angle2D.PIDEG;
public const float MAX_ANGLEZ_LOW = 100f / Angle2D.PIDEG;
public const float MAX_ANGLEZ_HIGH = (360f - 100f) / Angle2D.PIDEG;
private const float CAMERA_SPEED = 6f;
private const double SECTOR_UPDATE_INTERVAL = 100d;
#endregion
@ -66,10 +65,7 @@ namespace CodeImp.DoomBuilder.VisualModes
private Vector3D campos;
private Vector3D camtarget;
private float camanglexy, camanglez;
// Processing
private double lastsectorupdatetime;
private Sector viewstartsector;
private Sector camsector;
// Input
private bool keyforward;
@ -80,7 +76,9 @@ namespace CodeImp.DoomBuilder.VisualModes
// Map
protected VisualBlockMap blockmap;
protected Dictionary<Sector, VisualSector> allsectors;
protected List<VisualBlockEntry> visibleblocks;
protected Dictionary<Sector, VisualSector> visiblesectors;
protected List<VisualGeometry> visiblegeometry;
#endregion
@ -88,6 +86,7 @@ namespace CodeImp.DoomBuilder.VisualModes
public Vector3D CameraPosition { get { return campos; } set { campos = value; } }
public Vector3D CameraTarget { get { return camtarget; } }
public Sector CameraSector { get { return camsector; } }
#endregion
@ -105,7 +104,9 @@ namespace CodeImp.DoomBuilder.VisualModes
this.camanglez = Angle2D.PI;
this.blockmap = new VisualBlockMap();
this.allsectors = new Dictionary<Sector, VisualSector>(General.Map.Map.Sectors.Count);
this.visibleblocks = new List<VisualBlockEntry>();
this.visiblesectors = new Dictionary<Sector, VisualSector>(50);
this.visiblegeometry = new List<VisualGeometry>(200);
}
// Disposer
@ -118,6 +119,8 @@ namespace CodeImp.DoomBuilder.VisualModes
foreach(KeyValuePair<Sector, VisualSector> s in allsectors) s.Value.Dispose();
blockmap.Dispose();
visiblesectors = null;
visiblegeometry = null;
visibleblocks = null;
allsectors = null;
blockmap = null;
@ -274,152 +277,126 @@ namespace CodeImp.DoomBuilder.VisualModes
// This preforms visibility culling
private void DoCulling()
{
Dictionary<Linedef, Linedef> visiblelines = new Dictionary<Linedef, Linedef>(200);
Vector2D campos2d = (Vector2D)campos;
float viewdist = General.Settings.ViewDistance;
// Get the blocks within view range and make a collection of all nearby linedefs
RectangleF viewrect = new RectangleF(campos.x - viewdist, campos.y - viewdist, viewdist * 2, viewdist * 2);
List<VisualBlockEntry> blocks = blockmap.GetSquareRange(viewrect);
List<Linedef> nearbylines = new List<Linedef>(blocks.Count);
foreach(VisualBlockEntry b in blocks) nearbylines.AddRange(b.Lines);
// Get the blocks within view range
visibleblocks = blockmap.GetFrustumRange(renderer.Frustum2D);
// Find the sector to begin with
if(General.Clock.CurrentTime > (lastsectorupdatetime + SECTOR_UPDATE_INTERVAL))
{
lastsectorupdatetime = General.Clock.CurrentTime;
viewstartsector = FindStartSector((Vector2D)campos, nearbylines);
}
// Find visible sectors
// Fill visiblity collections
visiblesectors = new Dictionary<Sector, VisualSector>(visiblesectors.Count);
if(viewstartsector != null) ProcessVisibleSectors(viewstartsector, (Vector2D)campos);
visiblegeometry = new List<VisualGeometry>(visiblegeometry.Capacity);
foreach(VisualBlockEntry block in visibleblocks)
{
foreach(Linedef ld in block.Lines)
{
// Add line if not added yet
visiblelines[ld] = ld;
// Which side of the line is the camera on?
if(ld.SideOfLine(campos2d) < 0)
{
// Do front of line
if(ld.Front != null) ProcessSidedef(ld.Front);
}
else
{
// Do back of line
if(ld.Back != null) ProcessSidedef(ld.Back);
}
}
}
// Find camera sector
Linedef nld = MapSet.NearestLinedef(visiblelines.Values, campos2d);
if(nld != null)
{
camsector = GetCameraSectorFromLinedef(nld);
}
else
{
// Exceptional case: no lines found in any nearby blocks!
// This could happen in the middle of an extremely large sector and in this case
// the above code will not have found any sectors/sidedefs for rendering.
// Here we handle this special case with brute-force. Let's find the sector
// the camera is in by searching the entire map and render that sector only.
nld = General.Map.Map.NearestLinedef(campos2d);
if(nld != null)
{
camsector = GetCameraSectorFromLinedef(nld);
if(camsector != null)
{
foreach(Sidedef sd in camsector.Sidedefs)
{
float side = sd.Line.SideOfLine(campos2d);
if(((side < 0) && sd.IsFront) ||
((side > 0) && !sd.IsFront))
ProcessSidedef(sd);
}
}
else
{
// Too far away from the map to see anything
camsector = null;
}
}
else
{
// Map is empty
camsector = null;
}
}
}
// This finds and adds visible sectors
private void ProcessVisibleSectors(Sector start, Vector2D campos)
private void ProcessSidedef(Sidedef sd)
{
Stack<Sector> todo = new Stack<Sector>(50);
Dictionary<Sector, Sector> stackedsectors = new Dictionary<Sector, Sector>(50);
Clipper clipper = new Clipper(campos);
float viewdist2 = General.Settings.ViewDistance * General.Settings.ViewDistance;
// TODO: Use sector markings instead of the stackedsectors dictionary?
// This algorithm uses a breadth-first search for visible sectors
// Continue until no more sectors to process
todo.Push(start);
stackedsectors.Add(start, start);
while(todo.Count > 0)
VisualSector vs;
// Find the visualsector and make it if needed
if(allsectors.ContainsKey(sd.Sector))
{
Sector s = todo.Pop();
VisualSector vs;
// Find the basesector and make it if needed
if(allsectors.ContainsKey(s))
{
// Take existing visualsector
vs = allsectors[s];
}
else
{
// Make new visualsector
vs = CreateVisualSector(s);
allsectors.Add(s, vs);
}
// Add sector to visibility list
visiblesectors.Add(s, vs);
// Go for all sidedefs in the sector
foreach(Sidedef sd in s.Sidedefs)
{
// Camera on the front of this side?
float side = sd.Line.SideOfLine(campos);
if(((side > 0) && sd.IsFront) ||
((side < 0) && !sd.IsFront))
{
// Sidedef blocking the view?
if((sd.Other == null) ||
(sd.Other.Sector.FloorHeight >= (sd.Sector.CeilHeight - 0.0001f)) ||
(sd.Other.Sector.CeilHeight <= (sd.Sector.FloorHeight + 0.0001f)) ||
(sd.Other.Sector.FloorHeight >= (sd.Other.Sector.CeilHeight - 0.0001f)))
{
// This blocks the view
//clipper.InsertRange(sd.Line.Start.Position, sd.Line.End.Position);
}
}
}
// Go for all sidedefs in the sector
foreach(Sidedef sd in s.Sidedefs)
{
// Doublesided and not referring to same sector?
if((sd.Other != null) && (sd.Other.Sector != sd.Sector))
{
// Get the other sector
Sector os = sd.Other.Sector;
// Sector not added yet?
if(!stackedsectors.ContainsKey(os))
{
// Within view range?
if(sd.Line.DistanceToSq(campos, true) < viewdist2)
{
// Can we see this sector?
//if(clipper.TestRange(sd.Line.Start.Position, sd.Line.End.Position))
{
// Process this sector as well
todo.Push(os);
stackedsectors.Add(os, os);
}
}
}
}
}
}
// Done
clipper.Dispose();
}
// This finds the nearest sector to the camera
private Sector FindStartSector(Vector2D campos, List<Linedef> lines)
{
float side;
Linedef l;
// Get nearest linedef
l = MapSet.NearestLinedef(lines, campos);
if(l != null)
{
// Check if we are on front or back side
side = l.SideOfLine(campos);
if(side > 0)
{
// Is there a sidedef here?
if(l.Back != null)
return l.Back.Sector;
else if(l.Front != null)
return l.Front.Sector;
else
return null;
}
else
{
// Is there a sidedef here?
if(l.Front != null)
return l.Front.Sector;
else if(l.Back != null)
return l.Back.Sector;
else
return null;
}
// Take existing visualsector
vs = allsectors[sd.Sector];
}
else
return null;
{
// Make new visualsector
vs = CreateVisualSector(sd.Sector);
allsectors.Add(sd.Sector, vs);
}
// Add to visible sectors if not added yet
if(!visiblesectors.ContainsKey(sd.Sector))
{
visiblesectors.Add(sd.Sector, vs);
visiblegeometry.AddRange(vs.FixedGeometry);
}
// Add sidedef geometry
visiblegeometry.AddRange(vs.GetSidedefGeometry(sd));
}
// This returns the camera sector from linedef
private Sector GetCameraSectorFromLinedef(Linedef ld)
{
if(ld.SideOfLine(campos) < 0)
{
if(ld.Front != null)
return ld.Front.Sector;
else
return null;
}
else
{
if(ld.Back != null)
return ld.Back.Sector;
else
return null;
}
}
#endregion
#region ================== Processing
@ -472,8 +449,8 @@ namespace CodeImp.DoomBuilder.VisualModes
public override void OnRedrawDisplay()
{
// Render all visible sectors
foreach(KeyValuePair<Sector, VisualSector> vs in visiblesectors)
renderer.RenderGeometry(vs.Value);
foreach(VisualGeometry g in visiblegeometry)
renderer.RenderGeometry(g);
}
#endregion
@ -487,6 +464,7 @@ namespace CodeImp.DoomBuilder.VisualModes
foreach(KeyValuePair<Sector, VisualSector> s in allsectors) s.Value.Dispose();
allsectors.Clear();
visiblesectors.Clear();
visiblegeometry.Clear();
}
#endregion

View file

@ -49,7 +49,9 @@ namespace CodeImp.DoomBuilder.VisualModes
#region ================== Variables
// Geometry
private List<VisualGeometry> geolist;
private List<VisualGeometry> fixedgeometry;
private List<VisualGeometry> allgeometry;
private Dictionary<Sidedef, List<VisualGeometry>> sidedefgeometry;
private VertexBuffer geobuffer;
private bool updategeo;
@ -63,7 +65,8 @@ namespace CodeImp.DoomBuilder.VisualModes
#region ================== Properties
internal List<VisualGeometry> GeometryList { get { return geolist; } }
internal List<VisualGeometry> FixedGeometry { get { return fixedgeometry; } }
internal List<VisualGeometry> AllGeometry { get { return allgeometry; } }
internal VertexBuffer GeometryBuffer { get { return geobuffer; } }
internal bool NeedsUpdateGeo { get { return updategeo; } set { updategeo |= value; } }
public bool IsDisposed { get { return isdisposed; } }
@ -78,7 +81,9 @@ namespace CodeImp.DoomBuilder.VisualModes
{
// Initialize
this.sector = s;
geolist = new List<VisualGeometry>();
allgeometry = new List<VisualGeometry>();
fixedgeometry = new List<VisualGeometry>();
sidedefgeometry = new Dictionary<Sidedef, List<VisualGeometry>>();
// Register as resource
General.Map.Graphics.RegisterResource(this);
@ -91,7 +96,7 @@ namespace CodeImp.DoomBuilder.VisualModes
if(!isdisposed)
{
// Clean up
foreach(VisualGeometry g in geolist) g.Sector = null;
foreach(VisualGeometry g in allgeometry) g.Sector = null;
if(geobuffer != null) geobuffer.Dispose();
geobuffer = null;
@ -136,11 +141,8 @@ namespace CodeImp.DoomBuilder.VisualModes
if(geobuffer != null) geobuffer.Dispose();
geobuffer = null;
// Sort the geo list by texture
geolist.Sort();
// Count the number of vertices there are
foreach(VisualGeometry g in geolist) if(g.Vertices != null) numverts += g.Vertices.Length;
foreach(VisualGeometry g in allgeometry) if(g.Vertices != null) numverts += g.Vertices.Length;
// Any vertics?
if(numverts > 0)
@ -151,7 +153,7 @@ namespace CodeImp.DoomBuilder.VisualModes
// Fill the buffer
bufferstream = geobuffer.Lock(0, WorldVertex.Stride * numverts, LockFlags.Discard);
foreach(VisualGeometry g in geolist)
foreach(VisualGeometry g in allgeometry)
{
if((g.Vertices != null) && (g.Vertices.Length > 0))
{
@ -168,20 +170,47 @@ namespace CodeImp.DoomBuilder.VisualModes
updategeo = false;
}
// This adds geometry
/// <summary>
/// This adds geometry for this sector. If the geometry inherits from VisualSidedef then it
/// will be added to the SidedefGeometry, otherwise it will be added as FixedGeometry.
/// </summary>
public void AddGeometry(VisualGeometry geo)
{
updategeo = true;
geo.Sector = this;
geolist.Add(geo);
allgeometry.Add(geo);
if(geo is VisualSidedef)
{
VisualSidedef geoside = (geo as VisualSidedef);
if(!sidedefgeometry.ContainsKey(geoside.Sidedef))
sidedefgeometry[geoside.Sidedef] = new List<VisualGeometry>(3);
sidedefgeometry[geoside.Sidedef].Add(geoside);
}
else
{
fixedgeometry.Add(geo);
}
}
/// <summary>
/// This removes all geometry.
/// </summary>
public void ClearGeometry()
{
foreach(VisualGeometry g in allgeometry) g.Sector = null;
allgeometry.Clear();
fixedgeometry.Clear();
sidedefgeometry.Clear();
updategeo = true;
}
// This removes all geometry
public void ClearGeometry()
// This gets the geometry list for the specified sidedef
public List<VisualGeometry> GetSidedefGeometry(Sidedef sd)
{
foreach(VisualGeometry g in geolist) g.Sector = null;
geolist.Clear();
updategeo = true;
if(sidedefgeometry.ContainsKey(sd))
return sidedefgeometry[sd];
else
return new List<VisualGeometry>();
}
#endregion

View file

@ -0,0 +1,77 @@
#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.Windows.Forms;
using System.IO;
using System.Reflection;
using System.Drawing;
using System.ComponentModel;
using CodeImp.DoomBuilder.Map;
using SlimDX.Direct3D9;
using SlimDX;
using CodeImp.DoomBuilder.Geometry;
using System.Drawing.Imaging;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Rendering;
#endregion
namespace CodeImp.DoomBuilder.VisualModes
{
public abstract class VisualSidedef : VisualGeometry
{
#region ================== Constants
#endregion
#region ================== Variables
// Original sidedef
private Sidedef sidedef;
#endregion
#region ================== Properties
public Sidedef Sidedef { get { return sidedef; } }
#endregion
#region ================== Constructor / Disposer
// Constructor
public VisualSidedef(Sidedef sd)
{
// Initialize
this.sidedef = sd;
}
#endregion
#region ================== Methods
#endregion
}
}

View file

@ -1820,7 +1820,7 @@ namespace CodeImp.DoomBuilder.Windows
// Setup and reload stuff
if(General.Map.ScriptEditor != null) General.Map.ScriptEditor.Editor.RefreshSettings();
General.Map.Graphics.SetupSettings();
General.Map.Map.UpdateConfiguration();
General.Map.UpdateConfiguration();
General.Map.ReloadResources();
}