mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-27 14:12:16 +00:00
36f391653a
Added Color Picker plugin Point, Flicker and Pulse light animation should now look almost exactly as seen in GZDoom. Fixed a bug when editing thing properties in Visual mode didn't update visual thing. Fixed incorrect doom-style walls shading when sector has "lightcolor" property set. Fixed "Index was outside the bounds of the array in CalculateNormalsAndShading()" error. GZDoomBuilder will now show error message prior to failing.
872 lines
25 KiB
C#
872 lines
25 KiB
C#
|
|
#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.Actions;
|
|
using CodeImp.DoomBuilder.Geometry;
|
|
using System.Drawing;
|
|
using SlimDX;
|
|
using CodeImp.DoomBuilder.Editing;
|
|
|
|
#endregion
|
|
|
|
namespace CodeImp.DoomBuilder.VisualModes
|
|
{
|
|
/// <summary>
|
|
/// Provides specialized functionality for a visual (3D) Doom Builder editing mode.
|
|
/// </summary>
|
|
public abstract class VisualMode : EditMode
|
|
{
|
|
#region ================== Constants
|
|
|
|
private const double MOVE_SPEED_MULTIPLIER = 0.001d;
|
|
|
|
#endregion
|
|
|
|
#region ================== Variables
|
|
|
|
// 3D Mode thing
|
|
protected Thing modething;
|
|
|
|
// Graphics
|
|
protected IRenderer3D renderer;
|
|
private Renderer3D renderer3d;
|
|
|
|
// Options
|
|
private bool processgeometry;
|
|
private bool processthings;
|
|
|
|
// Input
|
|
private bool keyforward;
|
|
private bool keybackward;
|
|
private bool keyleft;
|
|
private bool keyright;
|
|
private bool keyup;
|
|
private bool keydown;
|
|
|
|
//mxd
|
|
private bool visibleThingsUpdated;
|
|
private List<VisualThing> selectedThings;
|
|
|
|
// 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;
|
|
|
|
//mxd
|
|
public List<VisualThing> SelectedVisualThings {
|
|
get {
|
|
if (visibleThingsUpdated) {
|
|
visibleThingsUpdated = false;
|
|
selectedThings = new List<VisualThing>();
|
|
foreach (VisualThing t in visiblethings) {
|
|
if (t.Selected)
|
|
selectedThings.Add(t);
|
|
}
|
|
}
|
|
return selectedThings;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Properties
|
|
|
|
public bool ProcessGeometry { get { return processgeometry; } set { processgeometry = value; } }
|
|
public bool ProcessThings { get { return processthings; } set { processthings = value; } }
|
|
public VisualBlockMap BlockMap { get { return blockmap; } }
|
|
|
|
// Rendering
|
|
public IRenderer3D Renderer { get { return renderer; } }
|
|
|
|
#endregion
|
|
|
|
#region ================== Constructor / Disposer
|
|
|
|
/// <summary>
|
|
/// Provides specialized functionality for a visual (3D) Doom Builder editing mode.
|
|
/// </summary>
|
|
public VisualMode()
|
|
{
|
|
// Initialize
|
|
this.renderer = General.Map.Renderer3D;
|
|
this.renderer3d = (Renderer3D)General.Map.Renderer3D;
|
|
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);
|
|
this.processgeometry = true;
|
|
this.processthings = true;
|
|
}
|
|
|
|
// Disposer
|
|
public override void Dispose()
|
|
{
|
|
// Not already disposed?
|
|
if(!isdisposed)
|
|
{
|
|
// Clean up
|
|
foreach(KeyValuePair<Sector, VisualSector> s in allsectors) s.Value.Dispose();
|
|
blockmap.Dispose();
|
|
visiblesectors = null;
|
|
visiblegeometry = null;
|
|
visibleblocks = null;
|
|
visiblethings = null;
|
|
allsectors = null;
|
|
allthings = null;
|
|
blockmap = null;
|
|
|
|
// Done
|
|
base.Dispose();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Start / Stop
|
|
|
|
// Mode is engaged
|
|
public override void OnEngage()
|
|
{
|
|
base.OnEngage();
|
|
|
|
General.Map.VisualCamera.PositionAtThing();
|
|
|
|
// Update the used textures
|
|
General.Map.Data.UpdateUsedTextures();
|
|
|
|
// Fill the blockmap
|
|
FillBlockMap();
|
|
|
|
// Start special input mode
|
|
General.Interface.EnableProcessing();
|
|
General.Interface.StartExclusiveMouseInput();
|
|
}
|
|
|
|
// Mode is disengaged
|
|
public override void OnDisengage()
|
|
{
|
|
base.OnDisengage();
|
|
|
|
// Dispose
|
|
foreach(KeyValuePair<Sector, VisualSector> vs in allsectors)
|
|
if(vs.Value != null) vs.Value.Dispose();
|
|
|
|
// Dispose
|
|
foreach(KeyValuePair<Thing, VisualThing> vt in allthings)
|
|
if(vt.Value != null) vt.Value.Dispose();
|
|
|
|
// Apply camera position to thing
|
|
General.Map.VisualCamera.ApplyToThing();
|
|
|
|
// Do not leave the sector on the camera
|
|
General.Map.VisualCamera.Sector = null;
|
|
|
|
// Stop special input mode
|
|
General.Interface.DisableProcessing();
|
|
General.Interface.StopExclusiveMouseInput();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Events
|
|
|
|
public override bool OnUndoBegin()
|
|
{
|
|
renderer.SetCrosshairBusy(true);
|
|
General.Interface.RedrawDisplay();
|
|
return base.OnUndoBegin();
|
|
}
|
|
|
|
public override void OnUndoEnd()
|
|
{
|
|
base.OnUndoEnd();
|
|
ResourcesReloadedPartial();
|
|
renderer.SetCrosshairBusy(false);
|
|
}
|
|
|
|
public override bool OnRedoBegin()
|
|
{
|
|
renderer.SetCrosshairBusy(true);
|
|
General.Interface.RedrawDisplay();
|
|
return base.OnRedoBegin();
|
|
}
|
|
|
|
public override void OnRedoEnd()
|
|
{
|
|
base.OnRedoEnd();
|
|
ResourcesReloadedPartial();
|
|
renderer.SetCrosshairBusy(false);
|
|
}
|
|
|
|
public override void OnReloadResources()
|
|
{
|
|
base.OnReloadResources();
|
|
ResourcesReloaded();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Input
|
|
|
|
// Mouse input
|
|
public override void OnMouseInput(Vector2D delta)
|
|
{
|
|
base.OnMouseInput(delta);
|
|
General.Map.VisualCamera.ProcessMouseInput(delta);
|
|
}
|
|
|
|
[BeginAction("moveforward", BaseAction = true)]
|
|
public virtual void BeginMoveForward()
|
|
{
|
|
keyforward = true;
|
|
}
|
|
|
|
[EndAction("moveforward", BaseAction = true)]
|
|
public virtual void EndMoveForward()
|
|
{
|
|
keyforward = false;
|
|
}
|
|
|
|
[BeginAction("movebackward", BaseAction = true)]
|
|
public virtual void BeginMoveBackward()
|
|
{
|
|
keybackward = true;
|
|
}
|
|
|
|
[EndAction("movebackward", BaseAction = true)]
|
|
public virtual void EndMoveBackward()
|
|
{
|
|
keybackward = false;
|
|
}
|
|
|
|
[BeginAction("moveleft", BaseAction = true)]
|
|
public virtual void BeginMoveLeft()
|
|
{
|
|
keyleft = true;
|
|
}
|
|
|
|
[EndAction("moveleft", BaseAction = true)]
|
|
public virtual void EndMoveLeft()
|
|
{
|
|
keyleft = false;
|
|
}
|
|
|
|
[BeginAction("moveright", BaseAction = true)]
|
|
public virtual void BeginMoveRight()
|
|
{
|
|
keyright = true;
|
|
}
|
|
|
|
[EndAction("moveright", BaseAction = true)]
|
|
public virtual void EndMoveRight()
|
|
{
|
|
keyright = false;
|
|
}
|
|
|
|
[BeginAction("moveup", BaseAction = true)]
|
|
public virtual void BeginMoveUp()
|
|
{
|
|
keyup = true;
|
|
}
|
|
|
|
[EndAction("moveup", BaseAction = true)]
|
|
public virtual void EndMoveUp()
|
|
{
|
|
keyup = false;
|
|
}
|
|
|
|
[BeginAction("movedown", BaseAction = true)]
|
|
public virtual void BeginMoveDown()
|
|
{
|
|
keydown = true;
|
|
}
|
|
|
|
[EndAction("movedown", BaseAction = true)]
|
|
public virtual void EndMoveDown()
|
|
{
|
|
keydown = false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Visibility Culling
|
|
|
|
// This preforms visibility culling
|
|
protected void DoCulling()
|
|
{
|
|
Dictionary<Linedef, Linedef> visiblelines = new Dictionary<Linedef, Linedef>(200);
|
|
Vector2D campos2d = (Vector2D)General.Map.VisualCamera.Position;
|
|
float viewdist = General.Settings.ViewDistance;
|
|
|
|
// Make collections
|
|
visiblesectors = new Dictionary<Sector, VisualSector>(visiblesectors.Count);
|
|
visiblegeometry = new List<VisualGeometry>(visiblegeometry.Capacity);
|
|
visiblethings = new List<VisualThing>(visiblethings.Capacity);
|
|
|
|
//mxd
|
|
visibleThingsUpdated = true;
|
|
|
|
// Get the blocks within view range
|
|
visibleblocks = blockmap.GetFrustumRange(renderer.Frustum2D);
|
|
|
|
// Fill collections with geometry and things
|
|
foreach(VisualBlockEntry block in visibleblocks)
|
|
{
|
|
if(processgeometry)
|
|
{
|
|
// Lines
|
|
foreach(Linedef ld in block.Lines)
|
|
{
|
|
// Line not already processed?
|
|
if(!visiblelines.ContainsKey(ld))
|
|
{
|
|
// Add line if not added yet
|
|
visiblelines.Add(ld, ld);
|
|
|
|
// Which side of the line is the camera on?
|
|
if(ld.SideOfLine(campos2d) < 0)
|
|
{
|
|
// Do front of line
|
|
if(ld.Front != null) ProcessSidedefCulling(ld.Front);
|
|
}
|
|
else
|
|
{
|
|
// Do back of line
|
|
if(ld.Back != null) ProcessSidedefCulling(ld.Back);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(processthings)
|
|
{
|
|
// Things
|
|
foreach(Thing t in block.Things)
|
|
{
|
|
VisualThing vt;
|
|
|
|
// Not filtered out?
|
|
if(General.Map.ThingsFilter.IsThingVisible(t))
|
|
{
|
|
if(allthings.ContainsKey(t))
|
|
{
|
|
vt = allthings[t];
|
|
}
|
|
else
|
|
{
|
|
// Create new visual thing
|
|
vt = CreateVisualThing(t);
|
|
allthings.Add(t, vt);
|
|
}
|
|
|
|
if(vt != null)
|
|
{
|
|
visiblethings.Add(vt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(processgeometry)
|
|
{
|
|
// Find camera sector
|
|
Linedef nld = MapSet.NearestLinedef(visiblelines.Values, campos2d);
|
|
if(nld != null)
|
|
{
|
|
General.Map.VisualCamera.Sector = 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)
|
|
{
|
|
General.Map.VisualCamera.Sector = GetCameraSectorFromLinedef(nld);
|
|
if(General.Map.VisualCamera.Sector != null)
|
|
{
|
|
foreach(Sidedef sd in General.Map.VisualCamera.Sector.Sidedefs)
|
|
{
|
|
float side = sd.Line.SideOfLine(campos2d);
|
|
if(((side < 0) && sd.IsFront) ||
|
|
((side > 0) && !sd.IsFront))
|
|
ProcessSidedefCulling(sd);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Too far away from the map to see anything
|
|
General.Map.VisualCamera.Sector = null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Map is empty
|
|
General.Map.VisualCamera.Sector = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// This finds and adds visible sectors
|
|
private void ProcessSidedefCulling(Sidedef sd)
|
|
{
|
|
VisualSector vs;
|
|
|
|
// Find the visualsector and make it if needed
|
|
if(allsectors.ContainsKey(sd.Sector))
|
|
{
|
|
// Take existing visualsector
|
|
vs = allsectors[sd.Sector];
|
|
}
|
|
else
|
|
{
|
|
// Make new visualsector
|
|
vs = CreateVisualSector(sd.Sector);
|
|
if(vs != null) allsectors.Add(sd.Sector, vs);
|
|
}
|
|
|
|
if(vs != null)
|
|
{
|
|
// 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(General.Map.VisualCamera.Position) < 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 ================== Object Picking
|
|
|
|
// This picks an object from the scene
|
|
public VisualPickResult PickObject(Vector3D from, Vector3D to)
|
|
{
|
|
VisualPickResult result = new VisualPickResult();
|
|
Line2D ray2d = new Line2D(from, to);
|
|
Vector3D delta = to - from;
|
|
|
|
// Setup no result
|
|
result.picked = null;
|
|
result.hitpos = new Vector3D();
|
|
result.u_ray = 1.0f;
|
|
|
|
// Find all blocks we are intersecting
|
|
List<VisualBlockEntry> blocks = blockmap.GetLineBlocks(from, to);
|
|
|
|
// 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<IVisualPickable> pickables = new List<IVisualPickable>(blocks.Count * 10);
|
|
|
|
// Add geometry from the camera sector
|
|
if((General.Map.VisualCamera.Sector != null) && allsectors.ContainsKey(General.Map.VisualCamera.Sector))
|
|
{
|
|
VisualSector vs = allsectors[General.Map.VisualCamera.Sector];
|
|
sectors.Add(General.Map.VisualCamera.Sector, vs);
|
|
foreach(VisualGeometry g in vs.FixedGeometry) pickables.Add(g);
|
|
}
|
|
|
|
// Go for all lines to see which ones we intersect
|
|
// We will collect geometry from the sectors and sidedefs
|
|
foreach(VisualBlockEntry b in blocks)
|
|
{
|
|
foreach(Linedef ld in b.Lines)
|
|
{
|
|
// Make sure we don't test a line twice
|
|
if(!lines.ContainsKey(ld))
|
|
{
|
|
lines.Add(ld, ld);
|
|
|
|
// Intersecting?
|
|
float u;
|
|
if(ld.Line.GetIntersection(ray2d, out u))
|
|
{
|
|
// Check on which side we are
|
|
float side = ld.SideOfLine(ray2d.v1);
|
|
|
|
// Calculate intersection point
|
|
Vector3D intersect = from + delta * u;
|
|
|
|
// We must add the sectors of both sides of the line
|
|
// If we wouldn't, then aiming at a sector that is just within range
|
|
// could result in an incorrect hit (because the far line of the
|
|
// sector may not be included in this loop)
|
|
if(ld.Front != null)
|
|
{
|
|
// Find the visualsector
|
|
if(allsectors.ContainsKey(ld.Front.Sector))
|
|
{
|
|
VisualSector vs = allsectors[ld.Front.Sector];
|
|
|
|
// Add sector if not already added
|
|
if(!sectors.ContainsKey(ld.Front.Sector))
|
|
{
|
|
sectors.Add(ld.Front.Sector, vs);
|
|
foreach(VisualGeometry g in vs.FixedGeometry)
|
|
{
|
|
// Must have content
|
|
if(g.Triangles > 0)
|
|
pickables.Add(g);
|
|
}
|
|
}
|
|
|
|
// Add sidedef if on the front side
|
|
if(side < 0.0f)
|
|
{
|
|
List<VisualGeometry> sidedefgeo = vs.GetSidedefGeometry(ld.Front);
|
|
foreach(VisualGeometry g in sidedefgeo)
|
|
{
|
|
// Must have content
|
|
if(g.Triangles > 0)
|
|
{
|
|
g.SetPickResults(intersect, u);
|
|
pickables.Add(g);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add back side also
|
|
if(ld.Back != null)
|
|
{
|
|
// Find the visualsector
|
|
if(allsectors.ContainsKey(ld.Back.Sector))
|
|
{
|
|
VisualSector vs = allsectors[ld.Back.Sector];
|
|
|
|
// Add sector if not already added
|
|
if(!sectors.ContainsKey(ld.Back.Sector))
|
|
{
|
|
sectors.Add(ld.Back.Sector, vs);
|
|
foreach(VisualGeometry g in vs.FixedGeometry)
|
|
{
|
|
// Must have content
|
|
if(g.Triangles > 0)
|
|
pickables.Add(g);
|
|
}
|
|
}
|
|
|
|
// Add sidedef if on the front side
|
|
if(side > 0.0f)
|
|
{
|
|
List<VisualGeometry> sidedefgeo = vs.GetSidedefGeometry(ld.Back);
|
|
foreach(VisualGeometry g in sidedefgeo)
|
|
{
|
|
// Must have content
|
|
if(g.Triangles > 0)
|
|
{
|
|
g.SetPickResults(intersect, u);
|
|
pickables.Add(g);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add all the visible things
|
|
foreach(VisualThing vt in visiblethings)
|
|
pickables.Add(vt);
|
|
|
|
// Now we have a list of potential geometry that lies along the trace line.
|
|
// We still don't know what geometry actually hits, but we ruled out that which doesn't get even close.
|
|
// 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<IVisualPickable> potentialpicks = new List<IVisualPickable>(pickables.Count);
|
|
foreach(IVisualPickable p in pickables)
|
|
{
|
|
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(IVisualPickable p in potentialpicks)
|
|
{
|
|
float u = result.u_ray;
|
|
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.picked = p;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Setup final result
|
|
result.hitpos = from + to * result.u_ray;
|
|
|
|
// Done
|
|
return result;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Processing
|
|
|
|
/// <summary>
|
|
/// This disposes all resources. Needed geometry will be rebuild automatically.
|
|
/// </summary>
|
|
protected virtual void ResourcesReloaded()
|
|
{
|
|
// Dispose
|
|
foreach(KeyValuePair<Sector, VisualSector> vs in allsectors)
|
|
if(vs.Value != null) vs.Value.Dispose();
|
|
|
|
foreach(KeyValuePair<Thing, VisualThing> vt in allthings)
|
|
if(vt.Value != null) vt.Value.Dispose();
|
|
|
|
// Clear collections
|
|
allsectors.Clear();
|
|
allthings.Clear();
|
|
visiblesectors.Clear();
|
|
visibleblocks.Clear();
|
|
visiblegeometry.Clear();
|
|
visiblethings.Clear();
|
|
|
|
// Make new blockmap
|
|
FillBlockMap();
|
|
|
|
// Visibility culling (this re-creates the needed resources)
|
|
DoCulling();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This disposes orphaned resources and resources on changed geometry.
|
|
/// This usually happens when geometry is changed by undo, redo, cut or paste actions
|
|
/// and uses the marks to check what needs to be reloaded.
|
|
/// </summary>
|
|
protected virtual void ResourcesReloadedPartial()
|
|
{
|
|
Dictionary<Sector, VisualSector> newsectors = new Dictionary<Sector,VisualSector>(allsectors.Count);
|
|
|
|
// Neighbour sectors must be updated as well
|
|
foreach(Sector s in General.Map.Map.Sectors)
|
|
{
|
|
if(s.Marked)
|
|
{
|
|
foreach(Sidedef sd in s.Sidedefs)
|
|
if(sd.Other != null) sd.Other.Marked = true;
|
|
}
|
|
}
|
|
|
|
// Go for all sidedefs to mark sectors that need updating
|
|
foreach(Sidedef sd in General.Map.Map.Sidedefs)
|
|
if(sd.Marked) sd.Sector.Marked = true;
|
|
|
|
// Go for all vertices to mark linedefs that need updating
|
|
foreach(Vertex v in General.Map.Map.Vertices)
|
|
{
|
|
if(v.Marked)
|
|
{
|
|
foreach(Linedef ld in v.Linedefs)
|
|
ld.Marked = true;
|
|
}
|
|
}
|
|
|
|
// Go for all linedefs to mark sectors that need updating
|
|
foreach(Linedef ld in General.Map.Map.Linedefs)
|
|
{
|
|
if(ld.Marked)
|
|
{
|
|
if(ld.Front != null) ld.Front.Sector.Marked = true;
|
|
if(ld.Back != null) ld.Back.Sector.Marked = true;
|
|
}
|
|
}
|
|
|
|
// Dispose if source was disposed or marked
|
|
foreach(KeyValuePair<Sector, VisualSector> vs in allsectors)
|
|
{
|
|
if(vs.Value != null)
|
|
{
|
|
if(vs.Key.IsDisposed || vs.Key.Marked)
|
|
vs.Value.Dispose();
|
|
else
|
|
newsectors.Add(vs.Key, vs.Value);
|
|
}
|
|
}
|
|
|
|
// Things depend on the sector they are in and because we can't
|
|
// easily determine which ones changed, we dispose all things
|
|
foreach(KeyValuePair<Thing, VisualThing> vt in allthings)
|
|
if(vt.Value != null) vt.Value.Dispose();
|
|
|
|
// Apply new lists
|
|
allsectors = newsectors;
|
|
allthings = new Dictionary<Thing, VisualThing>(allthings.Count);
|
|
|
|
// Clear visibility collections
|
|
visiblesectors.Clear();
|
|
visibleblocks.Clear();
|
|
visiblegeometry.Clear();
|
|
visiblethings.Clear();
|
|
|
|
// Make new blockmap
|
|
FillBlockMap();
|
|
|
|
// Visibility culling (this re-creates the needed resources)
|
|
DoCulling();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implement this to create an instance of your VisualSector implementation.
|
|
/// </summary>
|
|
protected abstract VisualSector CreateVisualSector(Sector s);
|
|
|
|
/// <summary>
|
|
/// Implement this to create an instance of your VisualThing implementation.
|
|
/// </summary>
|
|
protected abstract VisualThing CreateVisualThing(Thing t);
|
|
|
|
/// <summary>
|
|
/// This returns the VisualSector for the given Sector.
|
|
/// </summary>
|
|
public VisualSector GetVisualSector(Sector s) { return allsectors[s]; }
|
|
|
|
/// <summary>
|
|
/// This returns the VisualThing for the given Thing.
|
|
/// </summary>
|
|
public VisualThing GetVisualThing(Thing t) { return allthings[t]; }
|
|
|
|
/// <summary>
|
|
/// Returns True when a VisualSector has been created for the specified Sector.
|
|
/// </summary>
|
|
public bool VisualSectorExists(Sector s) { return allsectors.ContainsKey(s) && (allsectors[s] != null); }
|
|
|
|
/// <summary>
|
|
/// Returns True when a VisualThing has been created for the specified Thing.
|
|
/// </summary>
|
|
public bool VisualThingExists(Thing t) { return allthings.ContainsKey(t) && (allthings[t] != null); }
|
|
|
|
/// <summary>
|
|
/// This is called when the blockmap needs to be refilled, because it was invalidated.
|
|
/// This usually happens when geometry is changed by undo, redo, cut or paste actions.
|
|
/// Lines and Things are added to the block map by the base implementation.
|
|
/// </summary>
|
|
protected virtual void FillBlockMap()
|
|
{
|
|
if(blockmap != null) blockmap.Dispose();
|
|
blockmap = new VisualBlockMap();
|
|
|
|
blockmap.AddLinedefsSet(General.Map.Map.Linedefs);
|
|
blockmap.AddThingsSet(General.Map.Map.Things);
|
|
blockmap.AddSectorsSet(General.Map.Map.Sectors);
|
|
}
|
|
|
|
/// <summary>
|
|
/// While this mode is active, this is called continuously to process whatever needs processing.
|
|
/// </summary>
|
|
public override void OnProcess(double deltatime)
|
|
{
|
|
double multiplier;
|
|
|
|
base.OnProcess(deltatime);
|
|
|
|
// Camera vectors
|
|
Vector3D camvec = Vector3D.FromAngleXYZ(General.Map.VisualCamera.AngleXY, General.Map.VisualCamera.AngleZ);
|
|
Vector3D camvecstrafe = Vector3D.FromAngleXY(General.Map.VisualCamera.AngleXY + Angle2D.PIHALF);
|
|
Vector3D cammovemul = General.Map.VisualCamera.MoveMultiplier;
|
|
Vector3D camdeltapos = new Vector3D();
|
|
Vector3D upvec = new Vector3D(0.0f, 0.0f, 1.0f);
|
|
|
|
// Move the camera
|
|
if(General.Interface.ShiftState) multiplier = MOVE_SPEED_MULTIPLIER * 2.0f; else multiplier = MOVE_SPEED_MULTIPLIER;
|
|
if(keyforward) camdeltapos += camvec * cammovemul * (float)((double)General.Settings.MoveSpeed * multiplier * deltatime);
|
|
if(keybackward) camdeltapos -= camvec * cammovemul * (float)((double)General.Settings.MoveSpeed * multiplier * deltatime);
|
|
if(keyleft) camdeltapos -= camvecstrafe * cammovemul * (float)((double)General.Settings.MoveSpeed * multiplier * deltatime);
|
|
if(keyright) camdeltapos += camvecstrafe * cammovemul * (float)((double)General.Settings.MoveSpeed * multiplier * deltatime);
|
|
if(keyup) camdeltapos += upvec * cammovemul * (float)((double)General.Settings.MoveSpeed * multiplier * deltatime);
|
|
if(keydown) camdeltapos += -upvec * cammovemul * (float)((double)General.Settings.MoveSpeed * multiplier * deltatime);
|
|
|
|
// Move the camera
|
|
General.Map.VisualCamera.ProcessMovement(camdeltapos);
|
|
|
|
// Apply new camera matrices
|
|
renderer.PositionAndLookAt(General.Map.VisualCamera.Position, General.Map.VisualCamera.Target);
|
|
|
|
// Visibility culling
|
|
DoCulling();
|
|
|
|
// Update labels in main window
|
|
General.MainWindow.UpdateCoordinates((Vector2D)General.Map.VisualCamera.Position);
|
|
|
|
// Now redraw
|
|
General.Interface.RedrawDisplay();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Actions
|
|
|
|
#endregion
|
|
}
|
|
}
|