working on visual mode

This commit is contained in:
codeimp 2008-12-09 15:45:24 +00:00
parent 71282714ac
commit fe5d5a2001
12 changed files with 381 additions and 64 deletions

View file

@ -652,6 +652,7 @@
<EmbeddedResource Include="Resources\Crosshair.png" />
<EmbeddedResource Include="Resources\CrosshairBusy.png" />
<Content Include="Resources\DB2.ico" />
<Compile Include="VisualModes\IVisualPickable.cs" />
<Compile Include="General\BinaryHeap.cs" />
<Compile Include="Geometry\Plane.cs" />
<Compile Include="Geometry\ProjectedFrustum2D.cs" />

View file

@ -109,6 +109,7 @@
<Compile Include="VisualModes\BaseVisualSector.cs" />
<Compile Include="ClassicModes\DragVerticesMode.cs" />
<Compile Include="ClassicModes\LinedefsMode.cs" />
<Compile Include="VisualModes\BaseVisualThing.cs" />
<Compile Include="VisualModes\VisualCeiling.cs" />
<Compile Include="VisualModes\VisualFloor.cs" />
<Compile Include="VisualModes\VisualLower.cs" />

View file

@ -100,6 +100,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
return new BaseVisualSector(s);
}
// This creates a visual thing
protected override VisualThing CreateVisualThing(Thing t)
{
return new BaseVisualThing(t);
}
// This locks the target so that it isn't changed until unlocked
public void LockTarget()
{
@ -122,24 +128,34 @@ namespace CodeImp.DoomBuilder.BuilderModes
VisualPickResult newtarget = PickObject(start, start + delta);
// Object changed?
if(newtarget.geometry != target.geometry)
if(newtarget.picked != target.picked)
{
// Any result?
if(newtarget.geometry != null)
if(newtarget.picked != null)
{
if(newtarget.geometry.Sidedef != null)
VisualGeometry prevgeo = null;
if((target.picked != null) && (target.picked is VisualGeometry))
prevgeo = (target.picked as VisualGeometry);
// Geometry picked?
if(newtarget.picked is VisualGeometry)
{
if((target.geometry != null) && (target.geometry.Sidedef == null)) General.Interface.HideInfo();
General.Interface.ShowLinedefInfo(newtarget.geometry.Sidedef.Line);
}
else if(newtarget.geometry.Sidedef == null)
{
if((target.geometry == null) || (target.geometry.Sidedef != null)) General.Interface.HideInfo();
General.Interface.ShowSectorInfo(newtarget.geometry.Sector.Sector);
}
else
{
General.Interface.HideInfo();
VisualGeometry pickedgeo = (newtarget.picked as VisualGeometry);
if(pickedgeo.Sidedef != null)
{
if((prevgeo != null) && (prevgeo.Sidedef == null)) General.Interface.HideInfo();
General.Interface.ShowLinedefInfo(pickedgeo.Sidedef.Line);
}
else if(pickedgeo.Sidedef == null)
{
if((prevgeo == null) || (prevgeo.Sidedef != null)) General.Interface.HideInfo();
General.Interface.ShowSectorInfo(pickedgeo.Sector.Sector);
}
else
{
General.Interface.HideInfo();
}
}
}
else
@ -155,9 +171,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
// This changes the target's height
private void ChangeTargetHeight(int amount)
{
if(target.geometry is BaseVisualGeometrySector)
if(target.picked is BaseVisualGeometrySector)
{
BaseVisualGeometrySector vgs = (target.geometry as BaseVisualGeometrySector);
BaseVisualGeometrySector vgs = (target.picked as BaseVisualGeometrySector);
vgs.ChangeHeight(amount);
// Rebuild sector
@ -240,25 +256,25 @@ namespace CodeImp.DoomBuilder.BuilderModes
[BeginAction("visualselect", BaseAction = true)]
public void BeginSelect()
{
if(target.geometry != null) (target.geometry as BaseVisualGeometry).OnSelectBegin();
if(target.picked != null) (target.picked as BaseVisualGeometry).OnSelectBegin();
}
[EndAction("visualselect", BaseAction = true)]
public void EndSelect()
{
if(target.geometry != null) (target.geometry as BaseVisualGeometry).OnSelectEnd();
if(target.picked != null) (target.picked as BaseVisualGeometry).OnSelectEnd();
}
[BeginAction("visualedit", BaseAction = true)]
public void BeginEdit()
{
if(target.geometry != null) (target.geometry as BaseVisualGeometry).OnEditBegin();
if(target.picked != null) (target.picked as BaseVisualGeometry).OnEditBegin();
}
[EndAction("visualedit", BaseAction = true)]
public void EndEdit()
{
if(target.geometry != null) (target.geometry as BaseVisualGeometry).OnEditEnd();
if(target.picked != null) (target.picked as BaseVisualGeometry).OnEditEnd();
}
[BeginAction("raisesector8")]

View file

@ -0,0 +1,67 @@
#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 CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.VisualModes;
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
{
internal class BaseVisualThing : VisualThing
{
#region ================== Constants
#endregion
#region ================== Variables
#endregion
#region ================== Properties
#endregion
#region ================== Constructor / Disposer
// Constructor
public BaseVisualThing(Thing t) : base(t)
{
}
#endregion
#region ================== Methods
#endregion
}
}

View file

@ -0,0 +1,48 @@
#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 interface IVisualPickable
{
bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir);
bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray);
}
}

View file

@ -44,21 +44,24 @@ namespace CodeImp.DoomBuilder.VisualModes
// Members
private List<Linedef> lines;
private List<Thing> things;
#endregion
#region ================== Properties
public List<Linedef> Lines { get { return lines; } }
public List<Thing> Things { get { return things; } }
#endregion
#region ================== Constructor
// Constructor for empty block
internal VisualBlockEntry()
{
lines = new List<Linedef>();
lines = new List<Linedef>(2);
things = new List<Thing>(2);
}
#endregion

View file

@ -271,6 +271,20 @@ namespace CodeImp.DoomBuilder.VisualModes
return entries;
}
// This puts a thing in the blockmap
public void AddThingsSet(ICollection<Thing> things)
{
foreach(Thing t in things) AddThing(t);
}
// This puts a thing in the blockmap
public void AddThing(Thing t)
{
Point p = GetBlockCoordinates(t.Position);
VisualBlockEntry block = GetBlock(p);
block.Things.Add(t);
}
// This puts a whole set of linedefs in the blocks they cross
public void AddLinedefsSet(ICollection<Linedef> lines)
{

View file

@ -40,7 +40,7 @@ using CodeImp.DoomBuilder.Rendering;
namespace CodeImp.DoomBuilder.VisualModes
{
public abstract class VisualGeometry : IComparable<VisualGeometry>
public abstract class VisualGeometry : IVisualPickable, IComparable<VisualGeometry>
{
#region ================== Variables

View file

@ -76,8 +76,10 @@ namespace CodeImp.DoomBuilder.VisualModes
// Map
protected VisualBlockMap blockmap;
protected Dictionary<Thing, VisualThing> allthings;
protected Dictionary<Sector, VisualSector> allsectors;
protected List<VisualBlockEntry> visibleblocks;
protected List<VisualThing> visiblethings;
protected Dictionary<Sector, VisualSector> visiblesectors;
protected List<VisualGeometry> visiblegeometry;
@ -105,11 +107,13 @@ namespace CodeImp.DoomBuilder.VisualModes
this.camanglez = Angle2D.PI;
this.blockmap = new VisualBlockMap();
this.allsectors = new Dictionary<Sector, VisualSector>(General.Map.Map.Sectors.Count);
this.allthings = new Dictionary<Thing,VisualThing>(General.Map.Map.Things.Count);
this.visibleblocks = new List<VisualBlockEntry>();
this.visiblesectors = new Dictionary<Sector, VisualSector>(50);
this.visiblegeometry = new List<VisualGeometry>(200);
this.visiblethings = new List<VisualThing>(100);
}
// Disposer
public override void Dispose()
{
@ -122,7 +126,9 @@ namespace CodeImp.DoomBuilder.VisualModes
visiblesectors = null;
visiblegeometry = null;
visibleblocks = null;
visiblethings = null;
allsectors = null;
allthings = null;
blockmap = null;
// Done
@ -199,9 +205,11 @@ namespace CodeImp.DoomBuilder.VisualModes
base.OnUndoEnd();
allsectors.Clear();
allthings.Clear();
visiblesectors.Clear();
visibleblocks.Clear();
visiblegeometry.Clear();
visiblethings.Clear();
// Make new blockmap
if(blockmap != null)
@ -230,10 +238,12 @@ namespace CodeImp.DoomBuilder.VisualModes
base.OnRedoEnd();
allsectors.Clear();
allthings.Clear();
visiblesectors.Clear();
visibleblocks.Clear();
visiblegeometry.Clear();
visiblethings.Clear();
// Make new blockmap
if(blockmap != null)
{
@ -335,7 +345,7 @@ namespace CodeImp.DoomBuilder.VisualModes
#endregion
#region ================== Visibility Culling
// This preforms visibility culling
private void DoCulling()
{
@ -349,8 +359,10 @@ namespace CodeImp.DoomBuilder.VisualModes
// Fill visiblity collections
visiblesectors = new Dictionary<Sector, VisualSector>(visiblesectors.Count);
visiblegeometry = new List<VisualGeometry>(visiblegeometry.Capacity);
visiblethings = new List<VisualThing>(visiblethings.Capacity);
foreach(VisualBlockEntry block in visibleblocks)
{
// Lines
foreach(Linedef ld in block.Lines)
{
// Line not already processed?
@ -358,7 +370,7 @@ namespace CodeImp.DoomBuilder.VisualModes
{
// Add line if not added yet
visiblelines.Add(ld, ld);
// Which side of the line is the camera on?
if(ld.SideOfLine(campos2d) < 0)
{
@ -372,8 +384,27 @@ namespace CodeImp.DoomBuilder.VisualModes
}
}
}
// Things
foreach(Thing t in block.Things)
{
VisualThing vt;
if(allthings.ContainsKey(t))
{
vt = allthings[t];
}
else
{
// Create new visual thing
vt = CreateVisualThing(t);
allthings.Add(t, vt);
}
visiblethings.Add(vt);
}
}
// Find camera sector
Linedef nld = MapSet.NearestLinedef(visiblelines.Values, campos2d);
if(nld != null)
@ -419,7 +450,7 @@ namespace CodeImp.DoomBuilder.VisualModes
private void ProcessSidedefCulling(Sidedef sd)
{
VisualSector vs;
// Find the visualsector and make it if needed
if(allsectors.ContainsKey(sd.Sector))
{
@ -432,7 +463,7 @@ namespace CodeImp.DoomBuilder.VisualModes
vs = CreateVisualSector(sd.Sector);
allsectors.Add(sd.Sector, vs);
}
// Add to visible sectors if not added yet
if(!visiblesectors.ContainsKey(sd.Sector))
{
@ -475,7 +506,7 @@ namespace CodeImp.DoomBuilder.VisualModes
Vector3D delta = to - from;
// Setup no result
result.geometry = null;
result.picked = null;
result.hitpos = new Vector3D();
result.u_ray = 1.0f;
@ -485,14 +516,14 @@ namespace CodeImp.DoomBuilder.VisualModes
// Make collections
Dictionary<Linedef, Linedef> lines = new Dictionary<Linedef, Linedef>(blocks.Count * 10);
Dictionary<Sector, VisualSector> sectors = new Dictionary<Sector, VisualSector>(blocks.Count * 10);
List<VisualGeometry> potentialgeometry = new List<VisualGeometry>(blocks.Count * 10);
List<IVisualPickable> pickables = new List<IVisualPickable>(blocks.Count * 10);
// Add geometry from the camera sector
if((camsector != null) && allsectors.ContainsKey(camsector))
{
VisualSector vs = allsectors[camsector];
sectors.Add(camsector, vs);
potentialgeometry.AddRange(vs.FixedGeometry);
foreach(VisualGeometry g in vs.FixedGeometry) pickables.Add(g);
}
// Go for all lines to see which ones we intersect
@ -531,16 +562,18 @@ namespace CodeImp.DoomBuilder.VisualModes
if(!sectors.ContainsKey(ld.Front.Sector))
{
sectors.Add(ld.Front.Sector, vs);
potentialgeometry.AddRange(vs.FixedGeometry);
foreach(VisualGeometry g in vs.FixedGeometry) pickables.Add(g);
}
// Add sidedef if on the front side
if(side < 0.0f)
{
int previndex = potentialgeometry.Count;
potentialgeometry.AddRange(vs.GetSidedefGeometry(ld.Front));
for(int i = previndex; i < potentialgeometry.Count; i++)
potentialgeometry[i].SetPickResults(intersect, u);
List<VisualGeometry> sidedefgeo = vs.GetSidedefGeometry(ld.Front);
foreach(VisualGeometry g in sidedefgeo)
{
g.SetPickResults(intersect, u);
pickables.Add(g);
}
}
}
}
@ -557,16 +590,18 @@ namespace CodeImp.DoomBuilder.VisualModes
if(!sectors.ContainsKey(ld.Back.Sector))
{
sectors.Add(ld.Back.Sector, vs);
potentialgeometry.AddRange(vs.FixedGeometry);
foreach(VisualGeometry g in vs.FixedGeometry) pickables.Add(g);
}
// Add sidedef if on the front side
if(side > 0.0f)
{
int previndex = potentialgeometry.Count;
potentialgeometry.AddRange(vs.GetSidedefGeometry(ld.Back));
for(int i = previndex; i < potentialgeometry.Count; i++)
potentialgeometry[i].SetPickResults(intersect, u);
List<VisualGeometry> sidedefgeo = vs.GetSidedefGeometry(ld.Back);
foreach(VisualGeometry g in sidedefgeo)
{
g.SetPickResults(intersect, u);
pickables.Add(g);
}
}
}
}
@ -580,24 +615,24 @@ namespace CodeImp.DoomBuilder.VisualModes
// This is still too much for accurate intersection testing, so we do a fast reject pass first.
Vector3D direction = to - from;
direction = direction.GetNormal();
List<VisualGeometry> likelygeometry = new List<VisualGeometry>(potentialgeometry.Count);
foreach(VisualGeometry g in potentialgeometry)
List<IVisualPickable> potentialpicks = new List<IVisualPickable>(pickables.Count);
foreach(IVisualPickable p in pickables)
{
if(g.PickFastReject(from, to, direction)) likelygeometry.Add(g);
if(p.PickFastReject(from, to, direction)) potentialpicks.Add(p);
}
// Now we do an accurate intersection test for all resulting geometry
// We keep only the closest hit!
foreach(VisualGeometry g in likelygeometry)
foreach(IVisualPickable p in potentialpicks)
{
float u = result.u_ray;
if(g.PickAccurate(from, to, direction, ref u))
if(p.PickAccurate(from, to, direction, ref u))
{
// Closer than previous find?
if((u > 0.0f) && (u < result.u_ray))
{
result.u_ray = u;
result.geometry = g;
result.picked = p;
}
}
}
@ -615,6 +650,9 @@ namespace CodeImp.DoomBuilder.VisualModes
// This creates a visual sector
protected abstract VisualSector CreateVisualSector(Sector s);
// This creates a visual thing
protected abstract VisualThing CreateVisualThing(Thing t);
// This returns a visual sector
protected VisualSector GetVisualSector(Sector s)
@ -632,6 +670,7 @@ namespace CodeImp.DoomBuilder.VisualModes
protected virtual void FillBlockMap()
{
blockmap.AddLinedefsSet(General.Map.Map.Linedefs);
blockmap.AddThingsSet(General.Map.Map.Things);
}
// Processing

View file

@ -41,7 +41,7 @@ namespace CodeImp.DoomBuilder.VisualModes
public struct VisualPickResult
{
// Members
public VisualGeometry geometry;
public IVisualPickable picked;
public float u_ray;
public Vector3D hitpos;
}

View file

@ -69,6 +69,7 @@ namespace CodeImp.DoomBuilder.VisualModes
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; } }
public Sector Sector { get { return sector; } }

View file

@ -40,44 +40,171 @@ using CodeImp.DoomBuilder.Rendering;
namespace CodeImp.DoomBuilder.VisualModes
{
public abstract class VisualThing : IComparable<VisualThing>
public abstract class VisualThing : IVisualPickable, ID3DResource, IComparable<VisualThing>
{
#region ================== Constants
#endregion
#region ================== Variables
// Thing
private Thing thing;
// Texture
private ImageData texture;
// Geometry
private WorldVertex[] spritevertices;
private WorldVertex[] cagevertices;
private VertexBuffer geobuffer;
private bool updategeo;
private int spritetriangles;
private int cagetriangles;
// Disposing
private bool isdisposed = false;
#endregion
#region ================== Properties
internal VertexBuffer GeometryBuffer { get { return geobuffer; } }
internal bool NeedsUpdateGeo { get { return updategeo; } }
internal int SpriteTriangles { get { return spritetriangles; } }
internal int CageTriangles { get { return cagetriangles; } }
internal int CageOffset { get { return spritevertices.Length; } }
public Thing Thing { get { return thing; } }
public bool IsDisposed { get { return isdisposed; } }
#endregion
#region ================== Constructor / Destructor
// Constructor
public VisualThing(Thing t)
{
// Initialize
this.thing = t;
// Register as resource
General.Map.Graphics.RegisterResource(this);
}
// Disposer
public virtual void Dispose()
{
// Not already disposed?
if(!isdisposed)
{
// Clean up
if(geobuffer != null) geobuffer.Dispose();
geobuffer = null;
// Unregister resource
General.Map.Graphics.UnregisterResource(this);
// Done
isdisposed = true;
}
}
#endregion
#region ================== Methods
// This is called before a device is reset
// (when resized or display adapter was changed)
public void UnloadResource()
{
// Trash geometry buffer
if(geobuffer != null) geobuffer.Dispose();
geobuffer = null;
updategeo = true;
}
// This is called resets when the device is reset
// (when resized or display adapter was changed)
public void ReloadResource()
{
// Make new geometry
//Update();
}
// This compares for sorting by sprite
public int CompareTo(VisualThing other)
{
return 0;
return Math.Sign(this.texture.LongName - other.texture.LongName);
}
// This sets the vertices for the thing sprite
protected void SetSpriteVertices(ICollection<WorldVertex> verts)
{
// Copy vertices
spritevertices = new WorldVertex[verts.Count];
verts.CopyTo(spritevertices, 0);
spritetriangles = spritevertices.Length / 3;
updategeo = true;
}
// This sets the vertices for the thing cage
protected void SetCageVertices(ICollection<WorldVertex> verts)
{
// Copy vertices
cagevertices = new WorldVertex[verts.Count];
verts.CopyTo(cagevertices, 0);
cagetriangles = cagevertices.Length / 3;
updategeo = true;
}
// This updates the visual sector
public void Update()
{
// Trash geometry buffer
if(geobuffer != null) geobuffer.Dispose();
geobuffer = null;
// Count the number of vertices there are
int numverts = spritevertices.Length + cagevertices.Length;
// Any vertics?
if(numverts > 0)
{
// Make a new buffer
geobuffer = new VertexBuffer(General.Map.Graphics.Device, WorldVertex.Stride * numverts,
Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default);
// Fill the buffer
DataStream bufferstream = geobuffer.Lock(0, WorldVertex.Stride * numverts, LockFlags.Discard);
bufferstream.WriteRange<WorldVertex>(spritevertices);
bufferstream.WriteRange<WorldVertex>(cagevertices);
geobuffer.Unlock();
bufferstream.Dispose();
}
// Done
updategeo = false;
}
/// <summary>
/// This is called when the thing must be tested for line intersection. This should reject
/// as fast as possible to rule out all geometry that certainly does not touch the line.
/// </summary>
public virtual bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir)
{
return false;
}
/// <summary>
/// This is called when the thing must be tested for line intersection. This should perform
/// accurate hit detection and set u_ray to the position on the ray where this hits the geometry.
/// </summary>
public virtual bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray)
{
return false;
}
#endregion
}
}