Added, Game configurations: added "ignoreddirectories" parameter. It lists directory names to be ignored when loading PK3/PK7/Directory resources.

Added, Game configurations: added "ignoredextensions" parameter. It lists file extensions to be ignored when loading PK3/PK7/Directory resources.
Updated: sector triangulation logic now works ~20% faster.
Changed: a case when a pk3/pk7 archive contains several entries with identical filename is now treated as a warning, not as an error.
Fixed, Visual mode: absolute floor/ceiling brightness should not be affected by brightness transfer effects (like 3d floors).
Fixed, Draw Lines mode: in some cases unclosed sectors were created when several points were successively drawn at the same location.
Updated documentation.
This commit is contained in:
MaxED 2016-10-24 19:19:11 +00:00
parent 0b853c9d30
commit 9908e41197
8 changed files with 158 additions and 67 deletions

View file

@ -27,6 +27,12 @@ bottomboundary = -32768;
longtexturenames = false;
//mxd. These directory names are ignored when loading PK3/PK7/Directory resources
ignoreddirectories = ".svn .git";
//mxd. Files with these extensions are ignored when loading PK3/PK7/Directory resources
ignoredextensions = "wad pk3 pk7 bak backup1 backup2 backup3 zip rar 7z";
// Things used by the editor
thingtypes

View file

@ -81,7 +81,6 @@ skills
damagetypes = "None BFGSplash Drowning Slime";
</pre>
<br />
<b class="fat">internalsoundnames</b> (string) - <span class="red">GZDB only</span>.<br />
Space-separated list of built-in logical sound names. These names won't trigger an editor warning when they are not bound to actual sounds in SNDINFO.<br />
<br />
@ -90,7 +89,22 @@ damagetypes = "None BFGSplash Drowning Slime";
internalsoundnames = "*death *xdeath *wimpydeath *crazydeath *burndeath";
</pre>
<br />
<b class="fat">ignoredextensions</b> (string) - <span class="red">GZDB only</span>.<br />
Space-separated list of file extensions. Files with these extensions will be ignored when loading PK3/PK7/Directory resources.<br />
<br />
<strong>Example:</strong>
<pre>
ignoredextensions = "wad pk3 pk7 bak backup1 backup2 backup3 zip rar 7z";
</pre>
<br />
<b class="fat">ignoreddirectories</b> (string) - <span class="red">GZDB only</span>.<br />
Space-separated list of directory names. These directory names are ignored when loading PK3/PK7/Directory resources. This applies to top level directories only.<br />
<br />
<strong>Example:</strong>
<pre>
ignoreddirectories = ".svn .git";
</pre>
<br />
<b class="fat">linetagindicatesectors</b> (boolean)<br />
When <b>true</b>, Doom Builder will highlight sectors associated with the same tag number when a line is highlighted. This is only really useful for Doom format maps, because Hexen format and UDMF format has no single tag on linedefs (in those formats, the arguments of the linedef's action can be tags, which also works to highlight sectors).<br />
Default value is <b>false</b>.<br />

View file

@ -160,6 +160,10 @@ namespace CodeImp.DoomBuilder.Config
//mxd. Internal sounds. These logical sound names won't trigger a warning when they are not bound to actual sounds in SOUNDINFO.
private HashSet<string> internalsoundnames;
//mxd. Stuff to ignore
private HashSet<string> ignoreddirectories;
private HashSet<string> ignoredextensions;
// Defaults
private readonly List<DefinedTextureSet> texturesets;
@ -291,6 +295,10 @@ namespace CodeImp.DoomBuilder.Config
//mxd. Internal sounds
internal HashSet<string> InternalSoundNames { get { return internalsoundnames; } }
//mxd. Stuff to ignore
internal HashSet<string> IgnoredFileExtensions { get { return ignoredextensions; } }
internal HashSet<string> IgnoredDirectoryNames { get { return ignoreddirectories; } }
// Defaults
internal List<DefinedTextureSet> TextureSets { get { return texturesets; } }
public List<ThingsFilter> ThingsFilters { get { return thingfilters; } }
@ -438,6 +446,10 @@ namespace CodeImp.DoomBuilder.Config
damagetypes = new HashSet<string>(cfg.ReadSetting("damagetypes", "None").Split(splitter, StringSplitOptions.RemoveEmptyEntries), StringComparer.OrdinalIgnoreCase);
internalsoundnames = new HashSet<string>(cfg.ReadSetting("internalsoundnames", string.Empty).Split(splitter, StringSplitOptions.RemoveEmptyEntries), StringComparer.OrdinalIgnoreCase);
//mxd. Load stuff to ignore
ignoreddirectories = new HashSet<string>(cfg.ReadSetting("ignoreddirectories", string.Empty).Split(splitter, StringSplitOptions.RemoveEmptyEntries), StringComparer.OrdinalIgnoreCase);
ignoredextensions = new HashSet<string>(cfg.ReadSetting("ignoredextensions", string.Empty).Split(splitter, StringSplitOptions.RemoveEmptyEntries), StringComparer.OrdinalIgnoreCase);
// Things
LoadThingFlags();
LoadDefaultThingFlags();

View file

@ -97,7 +97,7 @@ namespace CodeImp.DoomBuilder.Data
archive = null;
// Make files list
files = new DirectoryFilesList(fileentries);
files = new DirectoryFilesList(dl.GetDisplayName(), fileentries);
// Initialize without path (because we use paths relative to the PK3 file)
Initialize();

View file

@ -374,18 +374,18 @@ namespace CodeImp.DoomBuilder.Geometry
Vertex found = null;
// Go for all sides to find the right-most side
foreach(KeyValuePair<Sidedef, bool> sd in sides)
foreach(Sidedef sd in sides.Keys)
{
// First found?
if((found == null) && !ignores.ContainsKey(sd.Key.Line.Start)) found = sd.Key.Line.Start;
if((found == null) && !ignores.ContainsKey(sd.Key.Line.End)) found = sd.Key.Line.End;
if((found == null) && !ignores.ContainsKey(sd.Line.Start)) found = sd.Line.Start;
if((found == null) && !ignores.ContainsKey(sd.Line.End)) found = sd.Line.End;
// Compare?
if(found != null)
{
// Check if more to the right than the previous found
if((sd.Key.Line.Start.Position.x > found.Position.x) && !ignores.ContainsKey(sd.Key.Line.Start)) found = sd.Key.Line.Start;
if((sd.Key.Line.End.Position.x > found.Position.x) && !ignores.ContainsKey(sd.Key.Line.End)) found = sd.Key.Line.End;
if((sd.Line.Start.Position.x > found.Position.x) && !ignores.ContainsKey(sd.Line.Start)) found = sd.Line.Start;
if((sd.Line.End.Position.x > found.Position.x) && !ignores.ContainsKey(sd.Line.End)) found = sd.Line.End;
}
}
@ -779,11 +779,16 @@ namespace CodeImp.DoomBuilder.Geometry
// This checks if a given ear is a valid (no intersections from reflex vertices)
private static bool CheckValidEar(EarClipVertex[] t, LinkedList<EarClipVertex> reflexes)
{
//mxd
Vector2D pos0 = t[0].Position;
Vector2D pos1 = t[1].Position;
Vector2D pos2 = t[2].Position;
// Go for all reflex vertices
foreach(EarClipVertex rv in reflexes)
{
// Not one of the triangle corners?
if((rv.Position != t[0].Position) && (rv.Position != t[1].Position) && (rv.Position != t[2].Position))
if((rv.Position != pos0) && (rv.Position != pos1) && (rv.Position != pos2))
{
// Return false on intersection
if(PointInsideTriangle(t, rv.MainListNode)) return false;
@ -797,11 +802,12 @@ namespace CodeImp.DoomBuilder.Geometry
// This returns the 3-vertex array triangle for an ear
private static EarClipVertex[] GetTriangle(EarClipVertex v)
{
EarClipVertex[] t = new EarClipVertex[3];
t[0] = (v.MainListNode.Previous == null) ? v.MainListNode.List.Last.Value : v.MainListNode.Previous.Value;
t[1] = v;
t[2] = (v.MainListNode.Next == null) ? v.MainListNode.List.First.Value : v.MainListNode.Next.Value;
return t;
return new []
{
(v.MainListNode.Previous == null) ? v.MainListNode.List.Last.Value : v.MainListNode.Previous.Value,
v,
(v.MainListNode.Next == null) ? v.MainListNode.List.First.Value : v.MainListNode.Next.Value
};
}
// This checks if a vertex is reflex (corner > 180 deg) or convex (corner < 180 deg)
@ -820,18 +826,23 @@ namespace CodeImp.DoomBuilder.Geometry
// If the triangle has no area, there can never be a point inside
if(TriangleHasArea(t))
{
float lineside01 = Line2D.GetSideOfLine(t[0].Position, t[1].Position, p.Value.Position);
float lineside12 = Line2D.GetSideOfLine(t[1].Position, t[2].Position, p.Value.Position);
float lineside20 = Line2D.GetSideOfLine(t[2].Position, t[0].Position, p.Value.Position);
//mxd
Vector2D pos0 = t[0].Position;
Vector2D pos1 = t[1].Position;
Vector2D pos2 = t[2].Position;
float lineside01 = Line2D.GetSideOfLine(pos0, pos1, p.Value.Position);
float lineside12 = Line2D.GetSideOfLine(pos1, pos2, p.Value.Position);
float lineside20 = Line2D.GetSideOfLine(pos2, pos0, p.Value.Position);
float u_on_line = 0.5f;
// If point p is on the line of an edge, find out where on the edge segment p is.
if(lineside01 == 0.0f)
u_on_line = Line2D.GetNearestOnLine(t[0].Position, t[1].Position, p.Value.Position);
u_on_line = Line2D.GetNearestOnLine(pos0, pos1, p.Value.Position);
else if(lineside12 == 0.0f)
u_on_line = Line2D.GetNearestOnLine(t[1].Position, t[2].Position, p.Value.Position);
u_on_line = Line2D.GetNearestOnLine(pos1, pos2, p.Value.Position);
else if(lineside20 == 0.0f)
u_on_line = Line2D.GetNearestOnLine(t[2].Position, t[0].Position, p.Value.Position);
u_on_line = Line2D.GetNearestOnLine(pos2, pos0, p.Value.Position);
// If any of the lineside results are 0 then that means the point p lies on that edge and we
// need to test if the lines adjacent to the point p are in the triangle or not.
@ -877,9 +888,10 @@ namespace CodeImp.DoomBuilder.Geometry
// Line is inside triangle, because p2 is
return true;
}
// Test if p2 is on an edge of the triangle and if it is we would
// like to know where on the edge segment p2 is
else if(s01 == 0.0f)
if(s01 == 0.0f)
{
p2_on_edge = Line2D.GetNearestOnLine(t[0].Position, t[1].Position, p2);
p1_on_same_edge = Line2D.GetSideOfLine(t[0].Position, t[1].Position, p1);
@ -912,13 +924,10 @@ namespace CodeImp.DoomBuilder.Geometry
Line2D t20 = new Line2D(t[2].Position, t[0].Position);
float pu, pt;
// Test intersections
t01.GetIntersection(p, out pu, out pt);
if(!float.IsNaN(pu) && (pu >= 0.0f) && (pu <= 1.0f) && (pt >= 0.0f) && (pt <= 1.0f)) return true;
t12.GetIntersection(p, out pu, out pt);
if(!float.IsNaN(pu) && (pu >= 0.0f) && (pu <= 1.0f) && (pt >= 0.0f) && (pt <= 1.0f)) return true;
t20.GetIntersection(p, out pu, out pt);
if(!float.IsNaN(pu) && (pu >= 0.0f) && (pu <= 1.0f) && (pt >= 0.0f) && (pt <= 1.0f)) return true;
//mxd. Test intersections
if(t01.GetIntersection(p, out pu, out pt)) return true;
if(t12.GetIntersection(p, out pu, out pt)) return true;
if(t20.GetIntersection(p, out pu, out pt)) return true;
return false;
}
@ -926,24 +935,33 @@ namespace CodeImp.DoomBuilder.Geometry
// This checks if the triangle has an area greater than 0
private static bool TriangleHasArea(EarClipVertex[] t)
{
return ((t[0].Position.x * (t[1].Position.y - t[2].Position.y) +
t[1].Position.x * (t[2].Position.y - t[0].Position.y) +
t[2].Position.x * (t[0].Position.y - t[1].Position.y)) != 0.0f);
Vector2D tp0 = t[0].Position;
Vector2D tp1 = t[1].Position;
Vector2D tp2 = t[2].Position;
return ((tp0.x * (tp1.y - tp2.y) +
tp1.x * (tp2.y - tp0.y) +
tp2.x * (tp0.y - tp1.y)) != 0.0f);
}
// This adds an array of vertices
private static void AddTriangleToList(EarClipVertex[] triangle, List<Vector2D> verticeslist, List<Sidedef> sidedefslist, bool last)
{
// Create triangle
verticeslist.Add(triangle[0].Position);
sidedefslist.Add(triangle[0].Sidedef);
verticeslist.Add(triangle[1].Position);
sidedefslist.Add(triangle[1].Sidedef);
verticeslist.Add(triangle[2].Position);
if(!last) sidedefslist.Add(null); else sidedefslist.Add(triangle[2].Sidedef);
//mxd
EarClipVertex v0 = triangle[0];
EarClipVertex v1 = triangle[1];
EarClipVertex v2 = triangle[2];
// Create triangle
verticeslist.Add(v0.Position);
sidedefslist.Add(v0.Sidedef);
verticeslist.Add(v1.Position);
sidedefslist.Add(v1.Sidedef);
verticeslist.Add(v2.Position);
sidedefslist.Add(!last ? null : v2.Sidedef);
// Modify the first earclipvertex of this triangle, it no longer lies along a sidedef
triangle[0].Sidedef = null;
v0.Sidedef = null;
}
#endregion

View file

@ -29,11 +29,6 @@ namespace CodeImp.DoomBuilder.IO
{
#region ================== Constants (mxd)
private static HashSet<string> EXLUDE_EXTENSIONS = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"wad", "pk3", "pk7", "bak", "backup1", "backup2", "backup3", "zip", "rar", "7z"
};
#endregion
#region ================== Variables
@ -68,17 +63,26 @@ namespace CodeImp.DoomBuilder.IO
wadentries.Add(file);
continue;
}
if(EXLUDE_EXTENSIONS.Contains(e.extension)) continue;
if(entries.ContainsKey(e.filepathname))
throw new IOException("Multiple files with the same filename in the same directory are not allowed. See: \"" + e.filepathname + "\"");
if(General.Map.Config.IgnoredFileExtensions.Contains(e.extension)) continue;
bool skipfolder = false;
foreach(string ef in General.Map.Config.IgnoredDirectoryNames)
{
if(e.path.StartsWith(ef + Path.DirectorySeparatorChar))
{
skipfolder = true;
break;
}
}
if(skipfolder) continue;
entries.Add(e.filepathname, e);
}
}
// Constructor for custom list
public DirectoryFilesList(ICollection<DirectoryFileEntry> sourceentries)
public DirectoryFilesList(string resourcename, ICollection<DirectoryFileEntry> sourceentries)
{
entries = new Dictionary<string, DirectoryFileEntry>(sourceentries.Count, StringComparer.OrdinalIgnoreCase);
wadentries = new List<string>();
@ -89,10 +93,26 @@ namespace CodeImp.DoomBuilder.IO
wadentries.Add(e.filepathname);
continue;
}
if(EXLUDE_EXTENSIONS.Contains(e.extension)) continue;
if(General.Map.Config.IgnoredFileExtensions.Contains(e.extension)) continue;
bool skipfolder = false;
foreach(string ef in General.Map.Config.IgnoredDirectoryNames)
{
if(e.path.StartsWith(ef + Path.DirectorySeparatorChar))
{
skipfolder = true;
break;
}
}
if(skipfolder) continue;
if(entries.ContainsKey(e.filepathname))
throw new IOException("Multiple files with the same filename in the same directory are not allowed. See: \"" + e.filepathname + "\"");
{
General.ErrorLogger.Add(ErrorType.Warning, "Resource \"" + resourcename + "\" contains multiple files with the same filename. See: \"" + e.filepathname + "\"");
continue;
}
entries.Add(e.filepathname, e);
}
}

View file

@ -545,6 +545,14 @@ namespace CodeImp.DoomBuilder.BuilderModes
pos.y > General.Map.Config.TopBoundary || pos.y < General.Map.Config.BottomBoundary)
return false;
//mxd. Avoid zero-length lines...
if(points.Count > 0)
{
Vector2D delta = points[points.Count - 1].pos - pos;
if((Math.Abs(delta.x) <= 0.001f) && (Math.Abs(delta.y) <= 0.001f))
return true;
}
DrawnVertex newpoint = new DrawnVertex();
newpoint.pos = pos;
newpoint.stitch = stitch;

View file

@ -56,6 +56,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
private bool floorchanged;
private bool ceilingchanged;
//mxd. Absolute lights are not affected by brightness transfers...
private bool lightfloorabsolute;
private bool lightceilingabsolute;
private int lightfloor;
private int lightceiling;
#endregion
#region ================== Properties
@ -288,17 +294,17 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Fetch ZDoom fields
int color = sector.Fields.GetValue("lightcolor", -1);
int flight = sector.Fields.GetValue("lightfloor", 0);
bool fabs = sector.Fields.GetValue("lightfloorabsolute", false);
int clight = sector.Fields.GetValue("lightceiling", 0);
bool cabs = sector.Fields.GetValue("lightceilingabsolute", false);
lightfloor = sector.Fields.GetValue("lightfloor", 0);
lightfloorabsolute = sector.Fields.GetValue("lightfloorabsolute", false);
lightceiling = sector.Fields.GetValue("lightceiling", 0);
lightceilingabsolute = sector.Fields.GetValue("lightceilingabsolute", false);
// Determine colors & light levels
PixelColor lightcolor = PixelColor.FromInt(color);
if(!fabs) flight = sector.Brightness + flight;
if(!cabs) clight = sector.Brightness + clight;
PixelColor floorbrightness = PixelColor.FromInt(mode.CalculateBrightness(flight));
PixelColor ceilingbrightness = PixelColor.FromInt(mode.CalculateBrightness(clight));
if(!lightfloorabsolute) lightfloor = sector.Brightness + lightfloor;
if(!lightceilingabsolute) lightceiling = sector.Brightness + lightceiling;
PixelColor floorbrightness = PixelColor.FromInt(mode.CalculateBrightness(lightfloor));
PixelColor ceilingbrightness = PixelColor.FromInt(mode.CalculateBrightness(lightceiling));
PixelColor floorcolor = PixelColor.Modulate(lightcolor, floorbrightness);
PixelColor ceilingcolor = PixelColor.Modulate(lightcolor, ceilingbrightness);
floor.color = floorcolor.WithAlpha(255).ToInt();
@ -357,7 +363,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
lightlevels[lightlevels.Count - 1].colorbelow = stored.colorbelow;
lightlevels[lightlevels.Count - 1].brightnessbelow = stored.brightnessbelow;
lightlevels[lightlevels.Count - 1].color = GetLevelColor(stored);
lightlevels[lightlevels.Count - 1].color = GetLevelColor(stored, lightlevels[lightlevels.Count - 1]);
}
//mxd. Cast light properties from top to bottom
@ -378,12 +384,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
l.colorbelow = stored.colorbelow;
l.brightnessbelow = stored.brightnessbelow;
l.color = GetLevelColor(stored);
l.color = GetLevelColor(stored, l);
}
else if(l.restrictlighting)
{
if(!pl.restrictlighting && pl != ceiling) stored = pl;
l.color = GetLevelColor(stored);
l.color = GetLevelColor(stored, l);
// This is the bottom side of extrafloor with "restrict lighting" flag. Make it cast stored light props.
if(l.type == SectorLevelType.Ceiling)
@ -394,10 +400,10 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Use light and color settings from previous layer
l.colorbelow = pl.colorbelow;
l.brightnessbelow = pl.brightnessbelow;
l.color = GetLevelColor(pl);
l.color = GetLevelColor(pl, l);
// Also colorize previous layer using next higher level color
if(i + 2 < lightlevels.Count) pl.color = GetLevelColor(lightlevels[i + 2]);
if(i + 2 < lightlevels.Count) pl.color = GetLevelColor(lightlevels[i + 2], pl);
}
else
{
@ -439,7 +445,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
if(src.colorbelow.a > 0 && src.brightnessbelow != -1)
{
// Only surface brightness is retained when a glowing flat is used as extrafloor texture
if(!l.affectedbyglow) l.color = GetLevelColor(src);
if(!l.affectedbyglow) l.color = GetLevelColor(src, l);
// Transfer brightnessbelow and colorbelow if current level is not extrafloor top
if(!(l.extrafloor && l.type == SectorLevelType.Floor))
@ -594,9 +600,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
//mxd
private int GetLevelColor(SectorLevel src)
private int GetLevelColor(SectorLevel src, SectorLevel target)
{
PixelColor brightness = PixelColor.FromInt(mode.CalculateBrightness(src.brightnessbelow));
PixelColor brightness;
if(lightfloorabsolute && target == floor)
brightness = PixelColor.FromInt(mode.CalculateBrightness(lightfloor));
else if(lightceilingabsolute && target == ceiling)
brightness = PixelColor.FromInt(mode.CalculateBrightness(lightceiling));
else
brightness = PixelColor.FromInt(mode.CalculateBrightness(src.brightnessbelow));
PixelColor color = PixelColor.Modulate(src.colorbelow, brightness);
return color.WithAlpha(255).ToInt();
}