mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-26 13:51:40 +00:00
- added SectorMaker (unfinished)
- changed a lot in data management - less memory usage by texture browsers
This commit is contained in:
parent
5a5f113855
commit
1ada9addf3
16 changed files with 814 additions and 408 deletions
|
@ -95,6 +95,7 @@
|
|||
<Compile Include="Geometry\Angle2D.cs" />
|
||||
<Compile Include="Geometry\LinedefsTracePath.cs" />
|
||||
<Compile Include="Geometry\LinedefAngleSorter.cs" />
|
||||
<Compile Include="Geometry\SectorMaker.cs" />
|
||||
<Compile Include="Geometry\Triangulator.cs" />
|
||||
<Compile Include="Geometry\EarClipVertex.cs" />
|
||||
<Compile Include="Geometry\Line2D.cs" />
|
||||
|
@ -302,7 +303,7 @@
|
|||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Map\Linedef.cs" />
|
||||
<Compile Include="Map\LinedefSide.cs" />
|
||||
<Compile Include="Geometry\LinedefSide.cs" />
|
||||
<Compile Include="Map\MapOptions.cs" />
|
||||
<Compile Include="Map\MapSet.cs" />
|
||||
<Compile Include="Data\DataLocation.cs" />
|
||||
|
|
|
@ -193,7 +193,8 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
|
|||
if(!frontsdone[i])
|
||||
{
|
||||
// Make sector here
|
||||
Sector newsector = map.MakeSector(ld, true);
|
||||
SectorMaker maker = new SectorMaker();
|
||||
Sector newsector = maker.MakeAt(ld, true);
|
||||
if(newsector != null)
|
||||
{
|
||||
// Go for all sidedefs in this new sector
|
||||
|
|
|
@ -51,7 +51,6 @@ namespace CodeImp.DoomBuilder.Config
|
|||
private float visualmousesensy;
|
||||
private float visualviewrange;
|
||||
private int imagebrightness;
|
||||
private bool backgroundload;
|
||||
private bool qualitydisplay;
|
||||
|
||||
#endregion
|
||||
|
@ -67,7 +66,6 @@ namespace CodeImp.DoomBuilder.Config
|
|||
public float VisualMouseSensX { get { return visualmousesensx; } internal set { visualmousesensx = value; } }
|
||||
public float VisualMouseSensY { get { return visualmousesensy; } internal set { visualmousesensy = value; } }
|
||||
public float VisualViewRange { get { return visualviewrange; } internal set { visualviewrange = value; } }
|
||||
public bool BackgroundLoading { get { return backgroundload; } internal set { backgroundload = value; } }
|
||||
public bool QualityDisplay { get { return qualitydisplay; } internal set { qualitydisplay = value; } }
|
||||
|
||||
#endregion
|
||||
|
@ -100,7 +98,6 @@ namespace CodeImp.DoomBuilder.Config
|
|||
visualmousesensy = cfg.ReadSetting("visualmousesensy", 40f);
|
||||
visualviewrange = cfg.ReadSetting("visualviewrange", 1000f);
|
||||
imagebrightness = cfg.ReadSetting("imagebrightness", 3);
|
||||
backgroundload = cfg.ReadSetting("backgroundload", true);
|
||||
qualitydisplay = cfg.ReadSetting("qualitydisplay", true);
|
||||
|
||||
// Success
|
||||
|
@ -125,7 +122,6 @@ namespace CodeImp.DoomBuilder.Config
|
|||
cfg.WriteSetting("visualmousesensy", visualmousesensy);
|
||||
cfg.WriteSetting("visualviewrange", visualviewrange);
|
||||
cfg.WriteSetting("imagebrightness", imagebrightness);
|
||||
cfg.WriteSetting("backgroundload", backgroundload);
|
||||
cfg.WriteSetting("qualitydisplay", qualitydisplay);
|
||||
|
||||
// Save settings configuration
|
||||
|
|
|
@ -60,6 +60,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
private Dictionary<long, ImageData> sprites;
|
||||
|
||||
// Background loading
|
||||
private LinkedList<ImageData> loadlist;
|
||||
private Thread backgroundloader;
|
||||
|
||||
// Special images
|
||||
|
@ -78,9 +79,23 @@ namespace CodeImp.DoomBuilder.Data
|
|||
public List<string> TextureNames { get { return texturenames; } }
|
||||
public List<string> FlatNames { get { return flatnames; } }
|
||||
public bool IsDisposed { get { return isdisposed; } }
|
||||
public bool IsLoading { get { return (backgroundloader != null) && backgroundloader.IsAlive; } }
|
||||
public ImageData MissingTexture3D { get { return missingtexture3d; } }
|
||||
|
||||
public bool IsLoading
|
||||
{
|
||||
get
|
||||
{
|
||||
if(loadlist != null)
|
||||
{
|
||||
return (backgroundloader != null) && backgroundloader.IsAlive && (loadlist.Count > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Constructor / Disposer
|
||||
|
@ -134,6 +149,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
// This loads all data resources
|
||||
internal void Load(DataLocationList locations)
|
||||
{
|
||||
int texcount, flatcount, spritecount;
|
||||
DataReader c;
|
||||
|
||||
// Create collections
|
||||
|
@ -143,6 +159,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
sprites = new Dictionary<long, ImageData>();
|
||||
texturenames = new List<string>();
|
||||
flatnames = new List<string>();
|
||||
loadlist = new LinkedList<ImageData>();
|
||||
|
||||
// Go for all locations
|
||||
foreach(DataLocation dl in locations)
|
||||
|
@ -187,17 +204,20 @@ namespace CodeImp.DoomBuilder.Data
|
|||
}
|
||||
|
||||
// Load stuff
|
||||
General.WriteLogLine("Loading palette...");
|
||||
LoadPalette();
|
||||
General.WriteLogLine("Loading textures...");
|
||||
LoadTextures();
|
||||
General.WriteLogLine("Loading flats...");
|
||||
LoadFlats();
|
||||
General.WriteLogLine("Loading sprites...");
|
||||
LoadSprites();
|
||||
texcount = LoadTextures();
|
||||
flatcount = LoadFlats();
|
||||
spritecount = LoadSprites();
|
||||
|
||||
// Sort names
|
||||
texturenames.Sort();
|
||||
flatnames.Sort();
|
||||
|
||||
// Start background loading
|
||||
StartBackgroundLoader();
|
||||
|
||||
// Output info
|
||||
General.WriteLogLine("Loaded " + texcount + " textures, " + flatcount + " flats, " + spritecount + " sprites");
|
||||
}
|
||||
|
||||
// This unloads all data
|
||||
|
@ -215,6 +235,15 @@ namespace CodeImp.DoomBuilder.Data
|
|||
// Dispose containers
|
||||
foreach(DataReader c in containers) c.Dispose();
|
||||
containers.Clear();
|
||||
|
||||
// Trash collections
|
||||
containers = null;
|
||||
textures = null;
|
||||
flats = null;
|
||||
sprites = null;
|
||||
texturenames = null;
|
||||
flatnames = null;
|
||||
loadlist = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -269,21 +298,19 @@ namespace CodeImp.DoomBuilder.Data
|
|||
// If a loader is already running, stop it first
|
||||
if(backgroundloader != null) StopBackgroundLoader();
|
||||
|
||||
// Only do background loading when preferred
|
||||
if(General.Settings.BackgroundLoading)
|
||||
{
|
||||
// Start a low priority thread to load images in background
|
||||
General.WriteLogLine("Starting background resource loading...");
|
||||
backgroundloader = new Thread(new ThreadStart(BackgroundLoad));
|
||||
backgroundloader.Name = "BackgroundLoader";
|
||||
backgroundloader.Priority = ThreadPriority.Lowest;
|
||||
backgroundloader.Start();
|
||||
}
|
||||
// Start a low priority thread to load images in background
|
||||
General.WriteLogLine("Starting background resource loading...");
|
||||
backgroundloader = new Thread(new ThreadStart(BackgroundLoad));
|
||||
backgroundloader.Name = "BackgroundLoader";
|
||||
backgroundloader.Priority = ThreadPriority.Lowest;
|
||||
backgroundloader.Start();
|
||||
}
|
||||
|
||||
// This stops background loading
|
||||
private void StopBackgroundLoader()
|
||||
{
|
||||
LinkedListNode<ImageData> n;
|
||||
|
||||
General.WriteLogLine("Stopping background resource loading...");
|
||||
if(backgroundloader != null)
|
||||
{
|
||||
|
@ -291,83 +318,126 @@ namespace CodeImp.DoomBuilder.Data
|
|||
backgroundloader.Interrupt();
|
||||
backgroundloader.Join();
|
||||
|
||||
// Reset load states on all images in the list
|
||||
n = loadlist.First;
|
||||
while(n != null)
|
||||
{
|
||||
n.Value.LoadState = ImageData.LOADSTATE_NONE;
|
||||
n.Value.LoadingTicket = null;
|
||||
n = n.Next;
|
||||
}
|
||||
loadlist.Clear();
|
||||
|
||||
// Done
|
||||
backgroundloader = null;
|
||||
General.MainWindow.UpdateStatusIcon();
|
||||
}
|
||||
}
|
||||
|
||||
// The background loader
|
||||
private void BackgroundLoad()
|
||||
{
|
||||
int starttime = General.Clock.GetCurrentTime();
|
||||
int deltatime;
|
||||
|
||||
try
|
||||
{
|
||||
// Load all lists
|
||||
LoadImagesList(textures);
|
||||
LoadImagesList(flats);
|
||||
LoadImagesList(sprites);
|
||||
do
|
||||
{
|
||||
// Get next item
|
||||
ImageData image = null;
|
||||
lock(loadlist)
|
||||
{
|
||||
// Anything to do?
|
||||
if(loadlist.Count > 0)
|
||||
{
|
||||
// Fetch image
|
||||
image = loadlist.First.Value;
|
||||
image.LoadingTicket = null;
|
||||
loadlist.RemoveFirst();
|
||||
|
||||
// Load or unload this image?
|
||||
switch(image.LoadState)
|
||||
{
|
||||
// Load image
|
||||
case ImageData.LOADSTATE_LOAD:
|
||||
image.LoadImage();
|
||||
//image.CreateTexture(); // Impossible from different thread
|
||||
break;
|
||||
|
||||
// Unload image
|
||||
case ImageData.LOADSTATE_TRASH:
|
||||
image.UnloadImage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Did we do something?
|
||||
if(image != null)
|
||||
{
|
||||
// Wait a bit and update icon
|
||||
General.MainWindow.UpdateStatusIcon();
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wait longer to release CPU resources
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
}
|
||||
while(true);
|
||||
}
|
||||
catch(ThreadInterruptedException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Done
|
||||
deltatime = General.Clock.GetCurrentTime() - starttime;
|
||||
General.WriteLogLine("Background resource loading completed in " + deltatime + "ms");
|
||||
General.WriteLogLine("Loaded " + textures.Count + " textures, " + flats.Count + " flats, " + sprites.Count + " sprites");
|
||||
backgroundloader = null;
|
||||
// This adds an image for background loading or unloading
|
||||
public void BackgroundLoadImage(ImageData img, bool load)
|
||||
{
|
||||
int loadstate = load ? ImageData.LOADSTATE_LOAD : ImageData.LOADSTATE_TRASH;
|
||||
|
||||
lock(loadlist)
|
||||
{
|
||||
// Already in the list?
|
||||
if(img.LoadingTicket != null)
|
||||
{
|
||||
// Just change the state
|
||||
img.LoadState = loadstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set load state and add to list
|
||||
img.LoadState = loadstate;
|
||||
img.LoadingTicket = loadlist.AddLast(img);
|
||||
}
|
||||
}
|
||||
|
||||
// Update icon
|
||||
General.MainWindow.UpdateStatusIcon();
|
||||
}
|
||||
|
||||
// This loads a list of ImageData
|
||||
private void LoadImagesList(Dictionary<long, ImageData> list)
|
||||
// This removes an image from background loading
|
||||
// This does not work for images that are being unloaded!
|
||||
public void BackgroundCancelImage(ImageData img)
|
||||
{
|
||||
Dictionary<long, ImageData>.Enumerator walker;
|
||||
bool moveresult = false;
|
||||
bool interrupted = false;
|
||||
|
||||
do
|
||||
// Queued?
|
||||
if(img.LoadingTicket != null)
|
||||
{
|
||||
// Get enumerator
|
||||
lock(list)
|
||||
// Not being trashed?
|
||||
if(img.LoadState != ImageData.LOADSTATE_TRASH)
|
||||
{
|
||||
walker = list.GetEnumerator();
|
||||
moveresult = walker.MoveNext();
|
||||
}
|
||||
|
||||
// Continue until at end of list
|
||||
while(moveresult)
|
||||
{
|
||||
lock(list)
|
||||
lock(loadlist)
|
||||
{
|
||||
// Load image
|
||||
walker.Current.Value.LoadImage();
|
||||
//walker.Current.Value.CreateTexture(); // Impossible from different thread
|
||||
}
|
||||
|
||||
// Wait a bit
|
||||
Thread.Sleep(1);
|
||||
|
||||
lock(list)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Move to next item
|
||||
moveresult = walker.MoveNext();
|
||||
}
|
||||
catch(InvalidOperationException)
|
||||
{
|
||||
// List was modified, restart!
|
||||
interrupted = true;
|
||||
break;
|
||||
}
|
||||
// Remove it from queue
|
||||
LinkedListNode<ImageData> ticket = img.LoadingTicket;
|
||||
img.LoadingTicket = null;
|
||||
loadlist.Remove(ticket);
|
||||
}
|
||||
|
||||
// Update icon
|
||||
General.MainWindow.UpdateStatusIcon();
|
||||
}
|
||||
}
|
||||
while(interrupted);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -398,11 +468,12 @@ namespace CodeImp.DoomBuilder.Data
|
|||
#region ================== Textures
|
||||
|
||||
// This loads the textures
|
||||
private void LoadTextures()
|
||||
private int LoadTextures()
|
||||
{
|
||||
ICollection<ImageData> images;
|
||||
PatchNames pnames = new PatchNames();
|
||||
PatchNames newpnames;
|
||||
int counter = 0;
|
||||
|
||||
// Go for all opened containers
|
||||
foreach(DataReader dr in containers)
|
||||
|
@ -425,6 +496,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
if(!textures.ContainsKey(img.LongName)) texturenames.Add(img.Name);
|
||||
textures.Remove(img.LongName);
|
||||
textures.Add(img.LongName, img);
|
||||
counter++;
|
||||
|
||||
// Also add as flat when using mixed resources
|
||||
if(General.Map.Config.MixTexturesFlats)
|
||||
|
@ -436,6 +508,9 @@ namespace CodeImp.DoomBuilder.Data
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output info
|
||||
return counter;
|
||||
}
|
||||
|
||||
// This returns a specific patch stream
|
||||
|
@ -466,19 +541,16 @@ namespace CodeImp.DoomBuilder.Data
|
|||
// This returns an image by long
|
||||
public ImageData GetTextureImage(long longname)
|
||||
{
|
||||
lock(textures)
|
||||
// Does this texture exist?
|
||||
if(textures.ContainsKey(longname))
|
||||
{
|
||||
// Does this texture exist?
|
||||
if(textures.ContainsKey(longname))
|
||||
{
|
||||
// Return texture
|
||||
return textures[longname];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return null image
|
||||
return new NullImage();
|
||||
}
|
||||
// Return texture
|
||||
return textures[longname];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return null image
|
||||
return new NullImage();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -521,10 +593,11 @@ namespace CodeImp.DoomBuilder.Data
|
|||
#region ================== Flats
|
||||
|
||||
// This loads the flats
|
||||
private void LoadFlats()
|
||||
private int LoadFlats()
|
||||
{
|
||||
ICollection<ImageData> images;
|
||||
|
||||
int counter = 0;
|
||||
|
||||
// Go for all opened containers
|
||||
foreach(DataReader dr in containers)
|
||||
{
|
||||
|
@ -539,6 +612,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
if(!flats.ContainsKey(img.LongName)) flatnames.Add(img.Name);
|
||||
flats.Remove(img.LongName);
|
||||
flats.Add(img.LongName, img);
|
||||
counter++;
|
||||
|
||||
// Also add as texture when using mixed resources
|
||||
if(General.Map.Config.MixTexturesFlats)
|
||||
|
@ -550,6 +624,9 @@ namespace CodeImp.DoomBuilder.Data
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output info
|
||||
return counter;
|
||||
}
|
||||
|
||||
// This returns a specific flat stream
|
||||
|
@ -580,19 +657,16 @@ namespace CodeImp.DoomBuilder.Data
|
|||
// This returns an image by long
|
||||
public ImageData GetFlatImage(long longname)
|
||||
{
|
||||
lock(flats)
|
||||
// Does this flat exist?
|
||||
if(flats.ContainsKey(longname))
|
||||
{
|
||||
// Does this flat exist?
|
||||
if(flats.ContainsKey(longname))
|
||||
{
|
||||
// Return flat
|
||||
return flats[longname];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return null image
|
||||
return new NullImage();
|
||||
}
|
||||
// Return flat
|
||||
return flats[longname];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return null image
|
||||
return new NullImage();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -635,7 +709,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
#region ================== Sprites
|
||||
|
||||
// This loads the sprites
|
||||
private void LoadSprites()
|
||||
private int LoadSprites()
|
||||
{
|
||||
Stream spritedata = null;
|
||||
SpriteImage image;
|
||||
|
@ -665,6 +739,9 @@ namespace CodeImp.DoomBuilder.Data
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output info
|
||||
return sprites.Count;
|
||||
}
|
||||
|
||||
// This returns an image by long
|
||||
|
@ -674,41 +751,38 @@ namespace CodeImp.DoomBuilder.Data
|
|||
long longname = Lump.MakeLongName(name);
|
||||
SpriteImage image;
|
||||
|
||||
lock(sprites)
|
||||
// Sprite already loaded?
|
||||
if(sprites.ContainsKey(longname))
|
||||
{
|
||||
// Sprite already loaded?
|
||||
if(sprites.ContainsKey(longname))
|
||||
// Return exiting sprite
|
||||
return sprites[longname];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Go for all opened containers
|
||||
for(int i = containers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// Return exiting sprite
|
||||
return sprites[longname];
|
||||
// This contain provides this sprite?
|
||||
spritedata = containers[i].GetSpriteData(name);
|
||||
if(spritedata != null) break;
|
||||
}
|
||||
|
||||
// Found anything?
|
||||
if(spritedata != null)
|
||||
{
|
||||
// Make new sprite image
|
||||
image = new SpriteImage(name);
|
||||
|
||||
// Add to collection
|
||||
sprites.Add(longname, image);
|
||||
|
||||
// Return result
|
||||
return image;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Go for all opened containers
|
||||
for(int i = containers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// This contain provides this sprite?
|
||||
spritedata = containers[i].GetSpriteData(name);
|
||||
if(spritedata != null) break;
|
||||
}
|
||||
|
||||
// Found anything?
|
||||
if(spritedata != null)
|
||||
{
|
||||
// Make new sprite image
|
||||
image = new SpriteImage(name);
|
||||
|
||||
// Add to collection
|
||||
sprites.Add(longname, image);
|
||||
|
||||
// Return result
|
||||
return image;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return null image
|
||||
return new NullImage();
|
||||
}
|
||||
// Return null image
|
||||
return new NullImage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,10 @@ namespace CodeImp.DoomBuilder.Data
|
|||
{
|
||||
#region ================== Constants
|
||||
|
||||
internal const int LOADSTATE_NONE = 0;
|
||||
internal const int LOADSTATE_LOAD = 1;
|
||||
internal const int LOADSTATE_TRASH = 2;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Variables
|
||||
|
@ -50,6 +54,11 @@ namespace CodeImp.DoomBuilder.Data
|
|||
protected float scaledheight;
|
||||
protected bool usecolorcorrection;
|
||||
|
||||
// Background loading
|
||||
private LinkedListNode<ImageData> loadingticket;
|
||||
private int loadstate; // true when loading, false when unloading
|
||||
private bool temporary;
|
||||
|
||||
// GDI bitmap
|
||||
protected Bitmap bitmap;
|
||||
|
||||
|
@ -74,6 +83,9 @@ namespace CodeImp.DoomBuilder.Data
|
|||
public Texture Texture { get { lock(this) { return texture; } } }
|
||||
public bool IsLoaded { get { return (bitmap != null); } }
|
||||
public bool IsDisposed { get { return isdisposed; } }
|
||||
internal bool Temporary { get { return temporary; } set { temporary = value; } }
|
||||
internal int LoadState { get { return loadstate; } set { loadstate = value; } }
|
||||
internal LinkedListNode<ImageData> LoadingTicket { get { return loadingticket; } set { loadingticket = value; } }
|
||||
public int Width { get { return width; } }
|
||||
public int Height { get { return height; } }
|
||||
public float ScaledWidth { get { return scaledwidth; } }
|
||||
|
@ -132,6 +144,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
{
|
||||
if(bitmap != null) bitmap.Dispose();
|
||||
bitmap = null;
|
||||
loadstate = ImageData.LOADSTATE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,6 +176,9 @@ namespace CodeImp.DoomBuilder.Data
|
|||
}
|
||||
bitmap.UnlockBits(bmpdata);
|
||||
}
|
||||
|
||||
// Done, reset load state
|
||||
loadstate = ImageData.LOADSTATE_NONE;
|
||||
}
|
||||
|
||||
// This creates the 2D pixel data
|
||||
|
|
|
@ -25,10 +25,11 @@ using CodeImp.DoomBuilder.Geometry;
|
|||
using CodeImp.DoomBuilder.Rendering;
|
||||
using SlimDX.Direct3D;
|
||||
using System.Drawing;
|
||||
using CodeImp.DoomBuilder.Map;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CodeImp.DoomBuilder.Map
|
||||
namespace CodeImp.DoomBuilder.Geometry
|
||||
{
|
||||
/// <summary>
|
||||
/// This is used to indicate a side of a line without the need for a sidedef.
|
|
@ -69,6 +69,13 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
|
||||
#region ================== Methods
|
||||
|
||||
// This merges a polygon into this one
|
||||
public void Add(Polygon p)
|
||||
{
|
||||
// Initialize
|
||||
foreach(EarClipVertex v in p) base.AddLast(v);
|
||||
}
|
||||
|
||||
// Point inside the polygon?
|
||||
// See: http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/
|
||||
public bool Intersect(Vector2D p)
|
||||
|
|
376
Source/Geometry/SectorMaker.cs
Normal file
376
Source/Geometry/SectorMaker.cs
Normal file
|
@ -0,0 +1,376 @@
|
|||
|
||||
#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.Geometry;
|
||||
using CodeImp.DoomBuilder.Rendering;
|
||||
using SlimDX.Direct3D;
|
||||
using System.Drawing;
|
||||
using CodeImp.DoomBuilder.Map;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CodeImp.DoomBuilder.Geometry
|
||||
{
|
||||
/// <summary>
|
||||
/// This makes a sector from all surrounding lines from a given coordinate.
|
||||
/// Automatically finds the sidedef/sector properties from surrounding sectors/sidedefs.
|
||||
/// </summary>
|
||||
public class SectorMaker
|
||||
{
|
||||
#region ================== Constants
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Variables
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Properties
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Constructor / Destructor
|
||||
|
||||
/// <summary>
|
||||
/// This makes a sector from all surrounding lines from a given coordinate.
|
||||
/// Automatically finds the sidedef/sector properties from surrounding sectors/sidedefs.
|
||||
/// </summary>
|
||||
public SectorMaker()
|
||||
{
|
||||
// Initialize
|
||||
|
||||
// We have no destructor
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Methods
|
||||
|
||||
/// <summary>
|
||||
/// This makes a sector at the given coordinates, or returns null
|
||||
/// when sector could not be created.
|
||||
/// </summary>
|
||||
public Sector MakeAt(Vector2D pos)
|
||||
{
|
||||
// Find the nearest line and determine side, then use the other method to create the sector
|
||||
Linedef l = General.Map.Map.NearestLinedef(pos);
|
||||
return MakeAt(l, (l.SideOfLine(pos) <= 0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This makes a sector starting at the given line and side, or
|
||||
/// returns null when sector could not be created.
|
||||
/// </summary>
|
||||
public Sector MakeAt(Linedef line, bool front)
|
||||
{
|
||||
List<LinedefSide> alllines = new List<LinedefSide>();
|
||||
|
||||
// STEP 1: Find the outer lines
|
||||
Polygon p = FindOuterLines(line, front, alllines);
|
||||
if(p != null)
|
||||
{
|
||||
// STEP 2: Find the inner lines
|
||||
FindInnerLines(p, alllines);
|
||||
|
||||
// STEP 3: Make the sector
|
||||
return MakeSector(alllines);
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Pathfinding
|
||||
|
||||
// This finds the inner lines of the sector and adds them to the sector polygon
|
||||
private void FindInnerLines(Polygon p, List<LinedefSide> alllines)
|
||||
{
|
||||
Vertex foundv;
|
||||
bool vvalid, findmore;
|
||||
Linedef foundline;
|
||||
float foundangle = 0f;
|
||||
bool foundlinefront;
|
||||
|
||||
do
|
||||
{
|
||||
findmore = false;
|
||||
|
||||
// Go for all vertices to find the right-most vertex inside the polygon
|
||||
foundv = null;
|
||||
foreach(Vertex v in General.Map.Map.Vertices)
|
||||
{
|
||||
// More to the right?
|
||||
if((foundv == null) || (v.X >= foundv.X))
|
||||
{
|
||||
// Vertex is inside the polygon?
|
||||
if(p.Intersect(v.Position))
|
||||
{
|
||||
// Vertex has lines attached?
|
||||
if(v.Linedefs.Count > 0)
|
||||
{
|
||||
// Go for all lines to see if the vertex is not of the polygon itsself
|
||||
vvalid = true;
|
||||
foreach(LinedefSide ls in alllines)
|
||||
{
|
||||
if((ls.Line.Start == v) || (ls.Line.End == v))
|
||||
{
|
||||
vvalid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Valid vertex?
|
||||
if(vvalid) foundv = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Found a vertex inside the polygon?
|
||||
if(foundv != null)
|
||||
{
|
||||
// Find the attached linedef with the smallest angle to the right
|
||||
float targetangle = Angle2D.PIHALF;
|
||||
foundline = null;
|
||||
foreach(Linedef l in foundv.Linedefs)
|
||||
{
|
||||
// We need an angle unrelated to line direction, so correct for that
|
||||
float lineangle = l.Angle;
|
||||
if(l.End == foundv) lineangle += Angle2D.PI;
|
||||
|
||||
// Better result?
|
||||
float deltaangle = Angle2D.Difference(targetangle, lineangle);
|
||||
if((foundline == null) || (deltaangle < foundangle))
|
||||
{
|
||||
foundline = l;
|
||||
foundangle = deltaangle;
|
||||
}
|
||||
}
|
||||
|
||||
// We already know that each linedef will go from this vertex
|
||||
// to the left, because this is the right-most vertex in this area.
|
||||
// If the line goes to the right, that means the other vertex of that
|
||||
// line must lie outside this area and the mapper made an error.
|
||||
// Should I check for this error and fail to create a sector in
|
||||
// that case or ignore it and create a malformed sector (possibly
|
||||
// breaking another sector also)?
|
||||
|
||||
// Find the side at which to start pathfinding
|
||||
Vector2D testpos = new Vector2D(100.0f, 0.0f);
|
||||
foundlinefront = (foundline.SideOfLine(foundv.Position + testpos) < 0.0f);
|
||||
|
||||
// Find inner path
|
||||
List<LinedefSide> innerlines = FindInnerMostPath(foundline, foundlinefront);
|
||||
if(innerlines != null)
|
||||
{
|
||||
// Make polygon
|
||||
LinedefTracePath tracepath = new LinedefTracePath(innerlines);
|
||||
Polygon innerpoly = tracepath.MakePolygon();
|
||||
|
||||
// Check if the front of the line is outside the polygon
|
||||
if(!innerpoly.Intersect(foundline.GetSidePoint(foundlinefront)))
|
||||
{
|
||||
// Valid island found!
|
||||
alllines.AddRange(innerlines);
|
||||
p.Add(innerpoly);
|
||||
findmore = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Continue until no more holes found
|
||||
while(findmore);
|
||||
}
|
||||
|
||||
// This finds the outer lines of the sector as a polygon
|
||||
// Returns null when no valid outer polygon can be found
|
||||
private Polygon FindOuterLines(Linedef line, bool front, List<LinedefSide> alllines)
|
||||
{
|
||||
// Find inner path
|
||||
alllines = FindInnerMostPath(line, front);
|
||||
if(alllines != null)
|
||||
{
|
||||
// Make polygon
|
||||
LinedefTracePath tracepath = new LinedefTracePath(alllines);
|
||||
Polygon poly = tracepath.MakePolygon();
|
||||
|
||||
// Check if the front of the line is inside the polygon
|
||||
if(poly.Intersect(line.GetSidePoint(front)))
|
||||
{
|
||||
// Valid polygon!
|
||||
return poly;
|
||||
}
|
||||
}
|
||||
|
||||
// Path is invalid for sector outer lines
|
||||
return null;
|
||||
}
|
||||
|
||||
// This finds the inner path from the beginning of a line to the end of the line.
|
||||
// Returns null when no path could be found.
|
||||
private List<LinedefSide> FindInnerMostPath(Linedef start, bool front)
|
||||
{
|
||||
List<LinedefSide> path = new List<LinedefSide>();
|
||||
Dictionary<Linedef, int> tracecount = new Dictionary<Linedef, int>();
|
||||
Linedef nextline = start;
|
||||
bool nextfront = front;
|
||||
|
||||
do
|
||||
{
|
||||
// Add line to path
|
||||
path.Add(new LinedefSide(nextline, nextfront));
|
||||
if(!tracecount.ContainsKey(nextline)) tracecount.Add(nextline, 1); else tracecount[nextline]++;
|
||||
|
||||
// Determine next vertex to use
|
||||
Vertex v = nextfront ? nextline.End : nextline.Start;
|
||||
|
||||
// Get list of linedefs and sort by angle
|
||||
List<Linedef> lines = new List<Linedef>(v.Linedefs);
|
||||
LinedefAngleSorter sorter = new LinedefAngleSorter(nextline, nextfront, v);
|
||||
lines.Sort(sorter);
|
||||
|
||||
// Source line is the only one?
|
||||
if(lines.Count == 1)
|
||||
{
|
||||
// Are we allowed to trace along this line again?
|
||||
if(tracecount[nextline] < 2)
|
||||
{
|
||||
// Turn around and go back along the other side of the line
|
||||
nextfront = !nextfront;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No more lines, trace ends here
|
||||
path = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Trace along the next line
|
||||
Linedef prevline = nextline;
|
||||
if(lines[0] == nextline) nextline = lines[1]; else nextline = lines[0];
|
||||
|
||||
// Check if front side changes
|
||||
if((prevline.Start == nextline.Start) ||
|
||||
(prevline.End == nextline.End)) nextfront = !nextfront;
|
||||
}
|
||||
}
|
||||
// Continue as long as we have not reached the start yet
|
||||
// or we have no next line to trace
|
||||
while((path != null) && (nextline != start));
|
||||
|
||||
// Return path (null when trace failed)
|
||||
return path;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Sector Making
|
||||
|
||||
// This makes the sector from the given lines and sides
|
||||
private Sector MakeSector(List<LinedefSide> alllines)
|
||||
{
|
||||
Sidedef source = null;
|
||||
Sector newsector = General.Map.Map.CreateSector();
|
||||
|
||||
// Check if any of the sides already has a sidedef
|
||||
// Then we use information from that sidedef to make the others
|
||||
foreach(LinedefSide ls in alllines)
|
||||
{
|
||||
if(ls.Front)
|
||||
{
|
||||
if(ls.Line.Front != null)
|
||||
{
|
||||
source = ls.Line.Front;
|
||||
source.Sector.CopyPropertiesTo(newsector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ls.Line.Back != null)
|
||||
{
|
||||
source = ls.Line.Back;
|
||||
source.Sector.CopyPropertiesTo(newsector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we couldn't find anything, try the other sides
|
||||
if(source == null)
|
||||
{
|
||||
foreach(LinedefSide ls in alllines)
|
||||
{
|
||||
if(ls.Front)
|
||||
{
|
||||
if(ls.Line.Back != null)
|
||||
{
|
||||
source = ls.Line.Back;
|
||||
source.Sector.CopyPropertiesTo(newsector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ls.Line.Front != null)
|
||||
{
|
||||
source = ls.Line.Front;
|
||||
source.Sector.CopyPropertiesTo(newsector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go for all sides to make sidedefs
|
||||
foreach(LinedefSide ls in alllines)
|
||||
{
|
||||
if(ls.Front)
|
||||
{
|
||||
// Create sidedef is needed and ensure it points to the new sector
|
||||
if(ls.Line.Front == null) General.Map.Map.CreateSidedef(ls.Line, true, newsector);
|
||||
if(ls.Line.Front.Sector != newsector) ls.Line.Front.ChangeSector(newsector);
|
||||
if(source != null) source.CopyPropertiesTo(ls.Line.Front); else source = ls.Line.Front;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create sidedef is needed and ensure it points to the new sector
|
||||
if(ls.Line.Back == null) General.Map.Map.CreateSidedef(ls.Line, false, newsector);
|
||||
if(ls.Line.Back.Sector != newsector) ls.Line.Back.ChangeSector(newsector);
|
||||
if(source != null) source.CopyPropertiesTo(ls.Line.Back); else source = ls.Line.Back;
|
||||
}
|
||||
|
||||
// Update line
|
||||
ls.Line.ApplySidedFlags();
|
||||
}
|
||||
|
||||
// Return the new sector
|
||||
return newsector;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -199,6 +199,9 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
General.Settings.WriteSetting("browserwindow.sizewidth", lastsize.Width);
|
||||
General.Settings.WriteSetting("browserwindow.sizeheight", lastsize.Height);
|
||||
General.Settings.WriteSetting("browserwindow.windowstate", windowstate);
|
||||
|
||||
// Clean up
|
||||
browser.CleanUp();
|
||||
}
|
||||
|
||||
// Static method to browse for flats
|
||||
|
|
37
Source/Interface/ImageBrowserControl.Designer.cs
generated
37
Source/Interface/ImageBrowserControl.Designer.cs
generated
|
@ -30,11 +30,11 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.splitter = new System.Windows.Forms.SplitContainer();
|
||||
this.list = new CodeImp.DoomBuilder.Interface.OptimizedListView();
|
||||
this.images = new System.Windows.Forms.ImageList(this.components);
|
||||
this.objectname = new System.Windows.Forms.TextBox();
|
||||
this.label = new System.Windows.Forms.Label();
|
||||
this.refreshtimer = new System.Windows.Forms.Timer(this.components);
|
||||
this.list = new CodeImp.DoomBuilder.Interface.OptimizedListView();
|
||||
this.splitter.Panel1.SuspendLayout();
|
||||
this.splitter.Panel2.SuspendLayout();
|
||||
this.splitter.SuspendLayout();
|
||||
|
@ -62,24 +62,9 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
this.splitter.TabIndex = 0;
|
||||
this.splitter.TabStop = false;
|
||||
//
|
||||
// list
|
||||
//
|
||||
this.list.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.list.LargeImageList = this.images;
|
||||
this.list.Location = new System.Drawing.Point(0, 0);
|
||||
this.list.MultiSelect = false;
|
||||
this.list.Name = "list";
|
||||
this.list.OwnerDraw = true;
|
||||
this.list.Size = new System.Drawing.Size(518, 312);
|
||||
this.list.Sorting = System.Windows.Forms.SortOrder.Ascending;
|
||||
this.list.TabIndex = 1;
|
||||
this.list.UseCompatibleStateImageBehavior = false;
|
||||
this.list.DrawItem += new System.Windows.Forms.DrawListViewItemEventHandler(this.list_DrawItem);
|
||||
this.list.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.list_ItemSelectionChanged);
|
||||
//
|
||||
// images
|
||||
//
|
||||
this.images.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
|
||||
this.images.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
|
||||
this.images.ImageSize = new System.Drawing.Size(64, 64);
|
||||
this.images.TransparentColor = System.Drawing.Color.Transparent;
|
||||
//
|
||||
|
@ -107,13 +92,27 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
this.refreshtimer.Interval = 500;
|
||||
this.refreshtimer.Tick += new System.EventHandler(this.refreshtimer_Tick);
|
||||
//
|
||||
// ImageBrowser
|
||||
// list
|
||||
//
|
||||
this.list.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.list.LargeImageList = this.images;
|
||||
this.list.Location = new System.Drawing.Point(0, 0);
|
||||
this.list.MultiSelect = false;
|
||||
this.list.Name = "list";
|
||||
this.list.OwnerDraw = true;
|
||||
this.list.Size = new System.Drawing.Size(518, 312);
|
||||
this.list.TabIndex = 1;
|
||||
this.list.UseCompatibleStateImageBehavior = false;
|
||||
this.list.DrawItem += new System.Windows.Forms.DrawListViewItemEventHandler(this.list_DrawItem);
|
||||
this.list.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.list_ItemSelectionChanged);
|
||||
//
|
||||
// ImageBrowserControl
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 14F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.splitter);
|
||||
this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.Name = "ImageBrowser";
|
||||
this.Name = "ImageBrowserControl";
|
||||
this.Size = new System.Drawing.Size(518, 346);
|
||||
this.splitter.Panel1.ResumeLayout(false);
|
||||
this.splitter.Panel2.ResumeLayout(false);
|
||||
|
|
|
@ -38,6 +38,13 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
{
|
||||
internal partial class ImageBrowserControl : UserControl
|
||||
{
|
||||
#region ================== Constants
|
||||
|
||||
// Maximum loaded items
|
||||
private const int MAX_LOADED_ITEMS = 200;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Delegates / Events
|
||||
|
||||
public delegate void SelectedItemChangedDelegate();
|
||||
|
@ -54,6 +61,9 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
// All items
|
||||
private List<ImageBrowserItem> items;
|
||||
|
||||
// Loaded items
|
||||
private LinkedList<ImageBrowserItem> loadeditems;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Properties
|
||||
|
@ -71,11 +81,12 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
// Initialize
|
||||
InitializeComponent();
|
||||
items = new List<ImageBrowserItem>();
|
||||
loadeditems = new LinkedList<ImageBrowserItem>();
|
||||
|
||||
// Move textbox with label
|
||||
objectname.Left = label.Right + label.Margin.Right + objectname.Margin.Left;
|
||||
}
|
||||
|
||||
|
||||
// This applies the color settings
|
||||
public void ApplyColorSettings()
|
||||
{
|
||||
|
@ -87,6 +98,37 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
}
|
||||
}
|
||||
|
||||
// This cleans everything up (we can't override Dispose?)
|
||||
public virtual void CleanUp()
|
||||
{
|
||||
// Stop refresh timer
|
||||
refreshtimer.Enabled = false;
|
||||
|
||||
// Begin updating list
|
||||
updating = true;
|
||||
list.SuspendLayout();
|
||||
list.BeginUpdate();
|
||||
|
||||
// Go for all items
|
||||
foreach(ImageBrowserItem i in list.Items)
|
||||
{
|
||||
// Queue image for unloading if only temporary
|
||||
if(i.icon.IsLoaded && i.icon.Temporary) General.Map.Data.BackgroundLoadImage(i.icon, false);
|
||||
|
||||
// Dispose item
|
||||
i.Dispose();
|
||||
}
|
||||
|
||||
// Trash list items
|
||||
list.Clear();
|
||||
loadeditems.Clear();
|
||||
|
||||
// Done updating list
|
||||
updating = false;
|
||||
list.EndUpdate();
|
||||
list.ResumeLayout();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Rendering
|
||||
|
@ -97,18 +139,25 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
if(!updating) e.Graphics.DrawImageUnscaled((e.Item as ImageBrowserItem).GetImage(e.Bounds), e.Bounds);
|
||||
}
|
||||
|
||||
// Resfresher
|
||||
// Refresher
|
||||
private void refreshtimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
// Continue refreshing only when still loading data
|
||||
refreshtimer.Enabled = General.Map.Data.IsLoading;
|
||||
|
||||
// Go for all items
|
||||
foreach(ImageBrowserItem i in list.Items)
|
||||
{
|
||||
// Bounds within view?
|
||||
if(i.Bounds.IntersectsWith(list.ClientRectangle))
|
||||
{
|
||||
// Remove from loaded list if in there
|
||||
if(i.LoadedTicket != null) loadeditems.Remove(i.LoadedTicket);
|
||||
|
||||
// Image not loaded?
|
||||
if(!i.icon.IsLoaded && !i.IsImageLoaded)
|
||||
{
|
||||
// Queue for background loading
|
||||
General.Map.Data.BackgroundLoadImage(i.icon, true);
|
||||
}
|
||||
|
||||
// Items needs to be redrawn?
|
||||
if(i.CheckRedrawNeeded(i.Bounds))
|
||||
{
|
||||
|
@ -118,10 +167,35 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
// Refresh item in list
|
||||
list.RedrawItems(i.Index, i.Index, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Queue for unloading if only temporary
|
||||
if(i.icon.IsLoaded && i.icon.Temporary) General.Map.Data.BackgroundLoadImage(i.icon, false);
|
||||
}
|
||||
|
||||
// Add to loaded list
|
||||
i.LoadedTicket = loadeditems.AddLast(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// When queued for loading, remove it from queue
|
||||
General.Map.Data.BackgroundCancelImage(i.icon);
|
||||
}
|
||||
}
|
||||
|
||||
// More items laoded than allowed?
|
||||
if(loadeditems.Count > MAX_LOADED_ITEMS)
|
||||
{
|
||||
// Unload items
|
||||
for(int i = 0; i < (loadeditems.Count - MAX_LOADED_ITEMS); i++)
|
||||
{
|
||||
loadeditems.First.Value.ReleaseImage();
|
||||
loadeditems.First.Value.LoadedTicket = null;
|
||||
loadeditems.RemoveFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Events
|
||||
|
@ -130,7 +204,7 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
private void objectname_TextChanged(object sender, EventArgs e)
|
||||
{
|
||||
// Update list
|
||||
RefillList();
|
||||
RefillList(false);
|
||||
|
||||
// No item selected?
|
||||
if(list.SelectedItems.Count == 0)
|
||||
|
@ -236,7 +310,8 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
if(list.Items.Count > 0)
|
||||
{
|
||||
list.SelectedItems.Clear();
|
||||
lvi = list.FindNearestItem(SearchDirectionHint.Down, new Point(1, -100000));
|
||||
//lvi = list.FindNearestItem(SearchDirectionHint.Down, new Point(1, -100000));
|
||||
lvi = list.Items[0];
|
||||
if(lvi != null)
|
||||
{
|
||||
lvi.Selected = true;
|
||||
|
@ -264,13 +339,10 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
public void EndAdding()
|
||||
{
|
||||
// Fill list with items
|
||||
RefillList();
|
||||
RefillList(true);
|
||||
|
||||
// Start updating if needed
|
||||
refreshtimer.Enabled = General.Map.Data.IsLoading;
|
||||
|
||||
// Select first item
|
||||
SelectFirstItem();
|
||||
// Start updating
|
||||
refreshtimer.Enabled = true;
|
||||
}
|
||||
|
||||
// This adds an item
|
||||
|
@ -283,7 +355,7 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
}
|
||||
|
||||
// This fills the list based on the objectname filter
|
||||
private void RefillList()
|
||||
private void RefillList(bool selectfirst)
|
||||
{
|
||||
List<ListViewItem> showitems = new List<ListViewItem>();
|
||||
|
||||
|
@ -296,7 +368,7 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
// Group property of items will be set to null, we will restore it later
|
||||
list.Items.Clear();
|
||||
|
||||
// Go for all items NOT in the list
|
||||
// Go for all items
|
||||
foreach(ImageBrowserItem i in items)
|
||||
{
|
||||
// Add item if valid
|
||||
|
@ -311,6 +383,9 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
// Fill list
|
||||
list.Items.AddRange(showitems.ToArray());
|
||||
|
||||
// Select first item?
|
||||
if(selectfirst) SelectFirstItem();
|
||||
|
||||
// Done updating list
|
||||
updating = false;
|
||||
list.EndUpdate();
|
||||
|
|
|
@ -45,17 +45,21 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
|
||||
// Group
|
||||
private ListViewGroup listgroup;
|
||||
private LinkedListNode<ImageBrowserItem> loadedticked;
|
||||
|
||||
// Image cache
|
||||
private Image image;
|
||||
private Image normalimage;
|
||||
private Image selectedimage;
|
||||
private bool imageloaded;
|
||||
private bool imageselected;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Properties
|
||||
|
||||
public ListViewGroup ListGroup { get { return listgroup; } set { listgroup = value; } }
|
||||
public LinkedListNode<ImageBrowserItem> LoadedTicket { get { return loadedticked; } set { loadedticked = value; } }
|
||||
public bool IsImageLoaded { get { return imageloaded; } }
|
||||
public bool HasImage { get { return (normalimage != null); } }
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -70,6 +74,15 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
this.Tag = tag;
|
||||
}
|
||||
|
||||
// Disposer
|
||||
public void Dispose()
|
||||
{
|
||||
ReleaseImage();
|
||||
loadedticked = null;
|
||||
icon = null;
|
||||
listgroup = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Methods
|
||||
|
@ -77,67 +90,82 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
// This checks if a redraw is needed
|
||||
public bool CheckRedrawNeeded(Rectangle bounds)
|
||||
{
|
||||
return ((image == null) || (image.Size != bounds.Size) ||
|
||||
(this.Selected != imageselected) || (icon.IsLoaded && !imageloaded));
|
||||
return (normalimage == null) || (selectedimage == null) || (icon.IsLoaded && !imageloaded);
|
||||
}
|
||||
|
||||
// This draws the images
|
||||
private Image DrawImage(Rectangle bounds, bool selected)
|
||||
{
|
||||
Brush forecolor;
|
||||
Brush backcolor;
|
||||
|
||||
// Make a new image and graphics to draw with
|
||||
Image image = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb);
|
||||
Graphics g = Graphics.FromImage(image);
|
||||
g.CompositingQuality = CompositingQuality.HighSpeed;
|
||||
g.InterpolationMode = InterpolationMode.Bilinear;
|
||||
g.SmoothingMode = SmoothingMode.HighQuality;
|
||||
g.PixelOffsetMode = PixelOffsetMode.None;
|
||||
|
||||
// Determine coordinates
|
||||
SizeF textsize = g.MeasureString(this.Text, this.ListView.Font, bounds.Width);
|
||||
Size bordersize = new Size((bounds.Width - 64) >> 1, (bounds.Height - 64 - (int)textsize.Height) >> 1);
|
||||
Rectangle imagerect = new Rectangle(bordersize.Width, bordersize.Height, 64, 64);
|
||||
PointF textpos = new PointF(((float)bounds.Width - textsize.Width) * 0.5f, bounds.Height - textsize.Height - 2);
|
||||
|
||||
// Determine colors
|
||||
if(selected)
|
||||
{
|
||||
// Highlighted
|
||||
backcolor = new LinearGradientBrush(new Point(0, 0), new Point(0, bounds.Height),
|
||||
AdjustedColor(SystemColors.Highlight, 0.2f),
|
||||
AdjustedColor(SystemColors.Highlight, -0.1f));
|
||||
forecolor = SystemBrushes.HighlightText;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal
|
||||
backcolor = new SolidBrush(base.ListView.BackColor);
|
||||
forecolor = new SolidBrush(base.ListView.ForeColor);
|
||||
}
|
||||
|
||||
// Draw!
|
||||
g.FillRectangle(backcolor, 0, 0, bounds.Width, bounds.Height);
|
||||
g.DrawImage(icon.Bitmap, General.MakeZoomedRect(icon.Bitmap.Size, imagerect));
|
||||
g.DrawString(this.Text, this.ListView.Font, forecolor, textpos);
|
||||
|
||||
// Done
|
||||
g.Dispose();
|
||||
return image;
|
||||
}
|
||||
|
||||
// This requests the cached image and redraws it if needed
|
||||
public Image GetImage(Rectangle bounds)
|
||||
{
|
||||
Brush forecolor;
|
||||
Brush backcolor;
|
||||
|
||||
// Do we need to redraw?
|
||||
if(CheckRedrawNeeded(bounds))
|
||||
{
|
||||
// Keep settings
|
||||
this.imageloaded = icon.IsLoaded;
|
||||
this.imageselected = this.Selected;
|
||||
|
||||
// Trash old image
|
||||
if(image != null) image.Dispose();
|
||||
// Keep image loaded state
|
||||
imageloaded = icon.IsLoaded;
|
||||
|
||||
// Make a new image and graphics to draw with
|
||||
image = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb);
|
||||
Graphics g = Graphics.FromImage(image);
|
||||
g.CompositingQuality = CompositingQuality.HighSpeed;
|
||||
g.InterpolationMode = InterpolationMode.Bilinear;
|
||||
g.SmoothingMode = SmoothingMode.HighQuality;
|
||||
g.PixelOffsetMode = PixelOffsetMode.None;
|
||||
|
||||
// Determine coordinates
|
||||
SizeF textsize = g.MeasureString(this.Text, this.ListView.Font, bounds.Width);
|
||||
Size bordersize = new Size((bounds.Width - 64) >> 1, (bounds.Height - 64 - (int)textsize.Height) >> 1);
|
||||
Rectangle imagerect = new Rectangle(bordersize.Width, bordersize.Height, 64, 64);
|
||||
PointF textpos = new PointF(((float)bounds.Width - textsize.Width) * 0.5f, bounds.Height - textsize.Height - 2);
|
||||
|
||||
// Determine colors
|
||||
if(this.Selected)
|
||||
{
|
||||
// Highlighted
|
||||
backcolor = new LinearGradientBrush(new Point(0, 0), new Point(0, bounds.Height),
|
||||
AdjustedColor(SystemColors.Highlight, 0.2f),
|
||||
AdjustedColor(SystemColors.Highlight, -0.1f));
|
||||
forecolor = SystemBrushes.HighlightText;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal
|
||||
backcolor = new SolidBrush(base.ListView.BackColor);
|
||||
forecolor = new SolidBrush(base.ListView.ForeColor);
|
||||
}
|
||||
|
||||
// Draw!
|
||||
g.FillRectangle(backcolor, 0, 0, bounds.Width, bounds.Height);
|
||||
g.DrawImage(icon.Bitmap, General.MakeZoomedRect(icon.Bitmap.Size, imagerect));
|
||||
g.DrawString(this.Text, this.ListView.Font, forecolor, textpos);
|
||||
|
||||
// Done
|
||||
g.Dispose();
|
||||
// Redraw both images
|
||||
if(normalimage != null) normalimage.Dispose();
|
||||
if(selectedimage != null) selectedimage.Dispose();
|
||||
normalimage = DrawImage(bounds, false);
|
||||
selectedimage = DrawImage(bounds, true);
|
||||
}
|
||||
|
||||
// Return image
|
||||
return image;
|
||||
if(this.Selected) return selectedimage; else return normalimage;
|
||||
}
|
||||
|
||||
// This releases image resources
|
||||
public void ReleaseImage()
|
||||
{
|
||||
if(normalimage != null) normalimage.Dispose();
|
||||
if(selectedimage != null) selectedimage.Dispose();
|
||||
normalimage = null;
|
||||
selectedimage = null;
|
||||
}
|
||||
|
||||
// This brightens or darkens a color
|
||||
|
|
13
Source/Interface/PreferencesForm.Designer.cs
generated
13
Source/Interface/PreferencesForm.Designer.cs
generated
|
@ -34,7 +34,6 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
System.Windows.Forms.GroupBox groupBox1;
|
||||
System.Windows.Forms.Label label1;
|
||||
this.qualitydisplay = new System.Windows.Forms.CheckBox();
|
||||
this.backgroundload = new System.Windows.Forms.CheckBox();
|
||||
this.imagebrightnesslabel = new System.Windows.Forms.Label();
|
||||
this.imagebrightness = new System.Windows.Forms.TrackBar();
|
||||
this.colorsgroup1 = new System.Windows.Forms.GroupBox();
|
||||
|
@ -123,7 +122,6 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
// groupBox1
|
||||
//
|
||||
groupBox1.Controls.Add(this.qualitydisplay);
|
||||
groupBox1.Controls.Add(this.backgroundload);
|
||||
groupBox1.Controls.Add(this.imagebrightnesslabel);
|
||||
groupBox1.Controls.Add(this.imagebrightness);
|
||||
groupBox1.Controls.Add(label1);
|
||||
|
@ -144,16 +142,6 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
this.qualitydisplay.Text = "High quality display";
|
||||
this.qualitydisplay.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// backgroundload
|
||||
//
|
||||
this.backgroundload.AutoSize = true;
|
||||
this.backgroundload.Location = new System.Drawing.Point(25, 102);
|
||||
this.backgroundload.Name = "backgroundload";
|
||||
this.backgroundload.Size = new System.Drawing.Size(197, 18);
|
||||
this.backgroundload.TabIndex = 9;
|
||||
this.backgroundload.Text = "Load all texture and flats in memory";
|
||||
this.backgroundload.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// imagebrightnesslabel
|
||||
//
|
||||
this.imagebrightnesslabel.Location = new System.Drawing.Point(17, 75);
|
||||
|
@ -743,7 +731,6 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
private System.Windows.Forms.GroupBox colorsgroup1;
|
||||
private System.Windows.Forms.CheckBox blackbrowsers;
|
||||
private System.Windows.Forms.CheckBox qualitydisplay;
|
||||
private System.Windows.Forms.CheckBox backgroundload;
|
||||
private System.Windows.Forms.Label imagebrightnesslabel;
|
||||
private System.Windows.Forms.TrackBar imagebrightness;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,6 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
|
||||
// Interface
|
||||
imagebrightness.Value = General.Settings.ImageBrightness;
|
||||
backgroundload.Checked = General.Settings.BackgroundLoading;
|
||||
qualitydisplay.Checked = General.Settings.QualityDisplay;
|
||||
|
||||
// Fill list of actions
|
||||
|
@ -292,7 +291,6 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
{
|
||||
// Apply interface
|
||||
General.Settings.ImageBrightness = imagebrightness.Value;
|
||||
General.Settings.BackgroundLoading = backgroundload.Checked;
|
||||
General.Settings.QualityDisplay = qualitydisplay.Checked;
|
||||
|
||||
// Apply control keys to actions
|
||||
|
|
|
@ -88,15 +88,23 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
|
||||
// Start adding
|
||||
browser.BeginAdding();
|
||||
|
||||
// Add all used textures
|
||||
foreach(ImageData img in General.Map.Data.Textures)
|
||||
if(useditems.ContainsKey(img.LongName))
|
||||
browser.Add(img.Name, img, img, used);
|
||||
|
||||
// Add all available textures
|
||||
// Add all available textures and mark the images for temporary loading
|
||||
foreach(ImageData img in General.Map.Data.Textures)
|
||||
{
|
||||
browser.Add(img.Name, img, img, avail);
|
||||
img.Temporary = true;
|
||||
}
|
||||
|
||||
// Add all used textures and mark the images for permanent loading
|
||||
foreach(ImageData img in General.Map.Data.Textures)
|
||||
{
|
||||
if(useditems.ContainsKey(img.LongName))
|
||||
{
|
||||
browser.Add(img.Name, img, img, used);
|
||||
img.Temporary = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Done adding
|
||||
browser.EndAdding();
|
||||
|
@ -199,6 +207,9 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
General.Settings.WriteSetting("browserwindow.sizewidth", lastsize.Width);
|
||||
General.Settings.WriteSetting("browserwindow.sizeheight", lastsize.Height);
|
||||
General.Settings.WriteSetting("browserwindow.windowstate", windowstate);
|
||||
|
||||
// Clean up
|
||||
browser.CleanUp();
|
||||
}
|
||||
|
||||
// Static method to browse for texture
|
||||
|
|
|
@ -777,114 +777,6 @@ namespace CodeImp.DoomBuilder.Map
|
|||
|
||||
#region ================== Geometry Tools
|
||||
|
||||
/// <summary>
|
||||
/// This automagically makes a sector, starting at one side of a line.
|
||||
/// Returns the sector reference when created, return null when not created.
|
||||
/// </summary>
|
||||
public Sector MakeSector(Linedef line, bool front)
|
||||
{
|
||||
// Find inner path
|
||||
List<LinedefSide> path = FindInnerMostPath(line, front);
|
||||
if(path != null)
|
||||
{
|
||||
// Make polygon
|
||||
LinedefTracePath tracepath = new LinedefTracePath(path);
|
||||
Polygon poly = tracepath.MakePolygon();
|
||||
|
||||
// Check if the front of the line is inside the polygon
|
||||
if(poly.Intersect(line.GetSidePoint(front)))
|
||||
{
|
||||
Sidedef source = null;
|
||||
Sector newsector = CreateSector();
|
||||
|
||||
// Check if any of the sides already has a sidedef
|
||||
// Then we use information from that sidedef to make the others
|
||||
foreach(LinedefSide ls in path)
|
||||
{
|
||||
if(ls.Front)
|
||||
{
|
||||
if(ls.Line.Front != null)
|
||||
{
|
||||
source = ls.Line.Front;
|
||||
source.Sector.CopyPropertiesTo(newsector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ls.Line.Back != null)
|
||||
{
|
||||
source = ls.Line.Back;
|
||||
source.Sector.CopyPropertiesTo(newsector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we couldn't find anything, try the other sides
|
||||
if(source == null)
|
||||
{
|
||||
foreach(LinedefSide ls in path)
|
||||
{
|
||||
if(ls.Front)
|
||||
{
|
||||
if(ls.Line.Back != null)
|
||||
{
|
||||
source = ls.Line.Back;
|
||||
source.Sector.CopyPropertiesTo(newsector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ls.Line.Front != null)
|
||||
{
|
||||
source = ls.Line.Front;
|
||||
source.Sector.CopyPropertiesTo(newsector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go for all sides to make sidedefs
|
||||
foreach(LinedefSide ls in path)
|
||||
{
|
||||
if(ls.Front)
|
||||
{
|
||||
// Create sidedef is needed and ensure it points to the new sector
|
||||
if(ls.Line.Front == null) CreateSidedef(ls.Line, true, newsector);
|
||||
if(ls.Line.Front.Sector != newsector) ls.Line.Front.ChangeSector(newsector);
|
||||
if(source != null) source.CopyPropertiesTo(ls.Line.Front); else source = ls.Line.Front;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create sidedef is needed and ensure it points to the new sector
|
||||
if(ls.Line.Back == null) CreateSidedef(ls.Line, false, newsector);
|
||||
if(ls.Line.Back.Sector != newsector) ls.Line.Back.ChangeSector(newsector);
|
||||
if(source != null) source.CopyPropertiesTo(ls.Line.Back); else source = ls.Line.Back;
|
||||
}
|
||||
|
||||
// Update line
|
||||
ls.Line.ApplySidedFlags();
|
||||
}
|
||||
|
||||
// Return the new sector
|
||||
return newsector;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Outside the map, can't create a sector
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Impossible to find a path!
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// This joins overlapping lines together
|
||||
// Returns the number of joins made
|
||||
public static int JoinOverlappingLines(ICollection<Linedef> lines)
|
||||
|
@ -1445,65 +1337,6 @@ namespace CodeImp.DoomBuilder.Map
|
|||
if(vc.Value.Linedefs.Count == 0) vertices.Remove(vc);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This finds the inner path from the beginning of a line to the end of the line.
|
||||
/// Returns null when no path could be found.
|
||||
/// </summary>
|
||||
public List<LinedefSide> FindInnerMostPath(Linedef start, bool front)
|
||||
{
|
||||
List<LinedefSide> path = new List<LinedefSide>();
|
||||
Dictionary<Linedef, int> tracecount = new Dictionary<Linedef, int>(linedefs.Count);
|
||||
Linedef nextline = start;
|
||||
bool nextfront = front;
|
||||
|
||||
do
|
||||
{
|
||||
// Add line to path
|
||||
path.Add(new LinedefSide(nextline, nextfront));
|
||||
if(!tracecount.ContainsKey(nextline)) tracecount.Add(nextline, 1); else tracecount[nextline]++;
|
||||
|
||||
// Determine next vertex to use
|
||||
Vertex v = nextfront ? nextline.End : nextline.Start;
|
||||
|
||||
// Get list of linedefs and sort by angle
|
||||
List<Linedef> lines = new List<Linedef>(v.Linedefs);
|
||||
LinedefAngleSorter sorter = new LinedefAngleSorter(nextline, nextfront, v);
|
||||
lines.Sort(sorter);
|
||||
|
||||
// Source line is the only one?
|
||||
if(lines.Count == 1)
|
||||
{
|
||||
// Are we allowed to trace along this line again?
|
||||
if(tracecount[nextline] < 2)
|
||||
{
|
||||
// Turn around and go back along the other side of the line
|
||||
nextfront = !nextfront;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No more lines, trace ends here
|
||||
path = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Trace along the next line
|
||||
Linedef prevline = nextline;
|
||||
if(lines[0] == nextline) nextline = lines[1]; else nextline = lines[0];
|
||||
|
||||
// Check if front side changes
|
||||
if((prevline.Start == nextline.Start) ||
|
||||
(prevline.End == nextline.End)) nextfront = !nextfront;
|
||||
}
|
||||
}
|
||||
// Continue as long as we have not reached the start yet
|
||||
// or we have no next line to trace
|
||||
while((path != null) && (nextline != start));
|
||||
|
||||
// Return path (null when trace failed)
|
||||
return path;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue