Merge remote-tracking branch 'udb/master'

This commit is contained in:
spherallic 2023-08-21 20:41:15 +02:00
commit b56ca5e018
58 changed files with 1777 additions and 888 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 471 B

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 729 B

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 631 B

After

Width:  |  Height:  |  Size: 651 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 B

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 334 B

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 649 B

After

Width:  |  Height:  |  Size: 669 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 682 B

After

Width:  |  Height:  |  Size: 702 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 B

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 B

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 567 B

After

Width:  |  Height:  |  Size: 587 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 509 B

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 510 B

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 722 B

After

Width:  |  Height:  |  Size: 742 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 781 B

After

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 738 B

After

Width:  |  Height:  |  Size: 758 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 751 B

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 B

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 B

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

After

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 732 B

After

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 621 B

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 B

After

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 587 B

After

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 432 B

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 B

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 B

After

Width:  |  Height:  |  Size: 328 B

View file

@ -160,7 +160,7 @@ namespace CodeImp.DoomBuilder.Data
if (filedata != null)
{
// Get a reader for the data
bitmap = ImageDataFormat.TryLoadImage(filedata, probableformat, General.Map.Data.Palette);
bitmap = ImageDataFormat.TryLoadImage(filedata, probableformat, General.Map.Data.Palette, out offsetx, out offsety);
// Not loaded?
if (bitmap == null)

View file

@ -112,6 +112,8 @@ namespace CodeImp.DoomBuilder.Data
// Store source properteis
sourcesize = new Size(overridden.Width, overridden.Height);
sourcescale = overridden.Scale;
offsetx = overridden.OffsetX;
offsety = overridden.OffsetY;
}
}

View file

@ -48,6 +48,8 @@ namespace CodeImp.DoomBuilder.Data
protected long longname;
protected int width;
protected int height;
protected int offsetx;
protected int offsety;
protected Vector2D scale;
protected bool worldpanning;
private bool usecolorcorrection;
@ -141,6 +143,8 @@ namespace CodeImp.DoomBuilder.Data
public int MipMapLevels { get { return mipmaplevels; } set { mipmaplevels = value; } }
public virtual int Width { get { return width; } }
public virtual int Height { get { return height; } }
public int OffsetX { get { return offsetx; } }
public int OffsetY { get { return offsety; } }
//mxd. Scaled texture size is integer in ZDoom.
public virtual float ScaledWidth { get { return (float)Math.Round(width * scale.x); } }
public virtual float ScaledHeight { get { return (float)Math.Round(height * scale.y); } }

View file

@ -47,7 +47,7 @@ namespace CodeImp.DoomBuilder.Data
SetName(resourcename);
// Temporarily load resource from memory
Stream bitmapdata = assembly.GetManifestResourceStream(resourcename);
Stream bitmapdata = assembly.GetManifestResourceStream(resourcename);
Bitmap bmp = (Bitmap)Image.FromStream(bitmapdata);
// Get width and height from image

View file

@ -35,20 +35,6 @@ namespace CodeImp.DoomBuilder.Data
public sealed class SpriteImage : ImageData, ISpriteImage
{
#region ================== Variables
private int offsetx;
private int offsety;
#endregion
#region ================== Properties
public int OffsetX { get { return offsetx; } }
public int OffsetY { get { return offsety; } }
#endregion
#region ================== Constructor / Disposer
// Constructor

View file

@ -52,12 +52,14 @@ namespace CodeImp.DoomBuilder.Data
#region ================== Constructor / Disposer
// Constructor
public TEXTURESImage(string name, string virtualpath, int width, int height, float scalex, float scaley,
public TEXTURESImage(string name, string virtualpath, int width, int height, int offsetx, int offsety, float scalex, float scaley,
bool worldpanning, TextureNamespace texturenamespace, bool optional, bool nulltexture)
{
// Initialize
this.width = width;
this.height = height;
this.offsetx = offsetx;
this.offsety = offsety;
this.scale.x = scalex;
this.scale.y = scaley;
this.worldpanning = worldpanning;

3
Source/Core/Rendering/IRenderer2D.cs Executable file → Normal file
View file

@ -52,7 +52,7 @@ namespace CodeImp.DoomBuilder.Rendering
// Rendering management methods
bool StartPlotter(bool clear);
bool StartThings(bool clear);
bool StartOverlay(bool clear);
bool StartOverlay(bool clear, int layernum = 0);
void Finish();
void SetPresentation(Presentation present);
void Present();
@ -69,6 +69,7 @@ namespace CodeImp.DoomBuilder.Rendering
void PlotVerticesSet(ICollection<Vertex> vertices, bool checkMode = true);
void RenderThing(Thing t, PixelColor c, float alpha);
void RenderThingSet(ICollection<Thing> things, float alpha);
void RenderThingSet(ICollection<Thing> things, PixelColor c, float alpha);
void RenderSRB2Extras();
void RenderRectangle(RectangleF rect, float bordersize, PixelColor c, bool transformrect);
void RenderRectangleFilled(RectangleF rect, PixelColor c, bool transformrect);

63
Source/Core/Rendering/Renderer2D.cs Executable file → Normal file
View file

@ -65,7 +65,7 @@ namespace CodeImp.DoomBuilder.Rendering
private Plotter gridplotter;
private Plotter plotter;
private Texture thingstex;
private Texture overlaytex;
private List<Texture> overlaytex;
private Texture surfacetex;
// Rendertarget sizes
@ -181,11 +181,22 @@ namespace CodeImp.DoomBuilder.Rendering
public void SetPresentation(Presentation present)
{
this.present = new Presentation(present);
// We might have to create additional overlay textures
int numoverlaylayers = present.layers.Count(l => l.layer == RendererLayer.Overlay);
if(numoverlaylayers > overlaytex.Count)
{
Texture t = new Texture(windowsize.Width, windowsize.Height, TextureFormat.Rgba8);
graphics.ClearTexture(General.Colors.Background.WithAlpha(0).ToColorValue(), t);
overlaytex.Add(t);
}
}
// This draws the image on screen
public void Present()
{
int currentoverlaylayer = 0;
General.Plugins.OnPresentDisplayBegin();
// Start drawing
@ -281,10 +292,11 @@ namespace CodeImp.DoomBuilder.Rendering
// OVERLAY
case RendererLayer.Overlay:
graphics.SetShader(aapass);
graphics.SetTexture(overlaytex);
graphics.SetTexture(overlaytex[currentoverlaylayer]);
graphics.SetSamplerState(TextureAddress.Wrap);
SetDisplay2DSettings(1f / overlaytex.Width, 1f / overlaytex.Height, FSAA_FACTOR, layer.alpha, false, true);
SetDisplay2DSettings(1f / overlaytex[currentoverlaylayer].Width, 1f / overlaytex[currentoverlaylayer].Height, FSAA_FACTOR, layer.alpha, false, true);
graphics.Draw(PrimitiveType.TriangleStrip, 0, 2);
currentoverlaylayer++;
break;
// SURFACE
@ -292,7 +304,7 @@ namespace CodeImp.DoomBuilder.Rendering
graphics.SetShader(aapass);
graphics.SetTexture(surfacetex);
graphics.SetSamplerState(TextureAddress.Wrap);
SetDisplay2DSettings(1f / overlaytex.Width, 1f / overlaytex.Height, FSAA_FACTOR, layer.alpha, false, true);
SetDisplay2DSettings(1f / surfacetex.Width, 1f / surfacetex.Height, FSAA_FACTOR, layer.alpha, false, true);
graphics.Draw(PrimitiveType.TriangleStrip, 0, 2);
break;
}
@ -338,12 +350,12 @@ namespace CodeImp.DoomBuilder.Rendering
public void DestroyRendertargets()
{
// Trash rendertargets
if(plotter != null) plotter.Dispose();
if(thingstex != null) thingstex.Dispose();
if(overlaytex != null) overlaytex.Dispose();
if(surfacetex != null) surfacetex.Dispose();
if(gridplotter != null) gridplotter.Dispose();
if(screenverts != null) screenverts.Dispose();
if (plotter != null) plotter.Dispose();
if (thingstex != null) thingstex.Dispose();
if (overlaytex != null) for(int i=0; i < overlaytex.Count; i++) { overlaytex[i].Dispose(); overlaytex[i] = null; } ;
if (surfacetex != null) surfacetex.Dispose();
if (gridplotter != null) gridplotter.Dispose();
if (screenverts != null) screenverts.Dispose();
thingstex = null;
gridplotter = null;
screenverts = null;
@ -371,13 +383,23 @@ namespace CodeImp.DoomBuilder.Rendering
plotter = new Plotter(windowsize.Width, windowsize.Height);
gridplotter = new Plotter(windowsize.Width, windowsize.Height);
thingstex = new Texture(windowsize.Width, windowsize.Height, TextureFormat.Rgba8);
overlaytex = new Texture(windowsize.Width, windowsize.Height, TextureFormat.Rgba8);
surfacetex = new Texture(windowsize.Width, windowsize.Height, TextureFormat.Rgba8);
if (present == null)
{
overlaytex = new List<Texture>() { new Texture(windowsize.Width, windowsize.Height, TextureFormat.Rgba8) };
}
else
{
overlaytex = new List<Texture>();
for (int i = 0; i < present.layers.Count(l => l.layer == RendererLayer.Overlay); i++)
overlaytex.Add(new Texture(windowsize.Width, windowsize.Height, TextureFormat.Rgba8));
}
// Clear rendertargets
graphics.ClearTexture(General.Colors.Background.WithAlpha(0).ToColorValue(), thingstex);
graphics.ClearTexture(General.Colors.Background.WithAlpha(0).ToColorValue(), overlaytex);
foreach(Texture t in overlaytex) graphics.ClearTexture(General.Colors.Background.WithAlpha(0).ToColorValue(), t);
// Create vertex buffers
screenverts = new VertexBuffer();
thingsvertices = new VertexBuffer();
@ -733,7 +755,7 @@ namespace CodeImp.DoomBuilder.Rendering
}
// This begins a drawing session
public bool StartOverlay(bool clear)
public bool StartOverlay(bool clear, int layernum = 0)
{
if(renderlayer != RenderLayers.None)
{
@ -747,10 +769,10 @@ namespace CodeImp.DoomBuilder.Rendering
renderlayer = RenderLayers.Overlay;
// Rendertargets available?
if(overlaytex != null)
if(overlaytex != null && layernum >= 0 && layernum < overlaytex.Count)
{
// Set the rendertarget to the things texture
graphics.StartRendering(clear, General.Colors.Background.WithAlpha(0).ToColorValue(), overlaytex, false);
graphics.StartRendering(clear, General.Colors.Background.WithAlpha(0).ToColorValue(), overlaytex[layernum], false);
// Ready for rendering
UpdateTransformations();
@ -1589,6 +1611,13 @@ namespace CodeImp.DoomBuilder.Rendering
RenderArrows(LinksCollector.GetSRB2Lines(), true, false);
}
// This adds a thing in the things buffer for rendering
public void RenderThingSet(ICollection<Thing> things, PixelColor c, float alpha)
{
RenderThingsBatch(things, alpha, false, c);
}
#endregion
#region ================== Surface

View file

@ -164,7 +164,6 @@ namespace CodeImp.DoomBuilder.VisualModes
triangles = vertices.Length / 3;
CalculateNormals(); //mxd
PerformAutoSelection(); //mxd
}
else
{
@ -285,7 +284,7 @@ namespace CodeImp.DoomBuilder.VisualModes
}
//mxd
protected abstract void PerformAutoSelection();
public abstract void PerformAutoSelection();
#endregion
}

View file

@ -249,7 +249,7 @@ namespace CodeImp.DoomBuilder.ZDoom
float scaley = ((yscale == 0.0f) ? General.Map.Config.DefaultTextureScale : 1f / yscale);
// Make texture
TEXTURESImage tex = new TEXTURESImage(name, virtualpath, width, height, scalex, scaley, worldpanning, texturenamespace, optional, nulltexture);
TEXTURESImage tex = new TEXTURESImage(name, virtualpath, width, height, xoffset, yoffset, scalex, scaley, worldpanning, texturenamespace, optional, nulltexture);
// Add patches
foreach(PatchStructure p in patches) tex.AddPatch(new TexturePatch(p));//mxd

File diff suppressed because it is too large Load diff

View file

@ -87,11 +87,15 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
public readonly SurfaceTextureInfo Floor;
public readonly SurfaceTextureInfo Ceiling;
public readonly Vertex FirstVertex;
public readonly Vector2D PreviousFirstVertexPosition;
public SectorTextureInfo(Sector s)
{
// Get transform properties
Floor.Offset = new Vector2D(UniFields.GetFloat(s.Fields, "xpanningfloor", 0.0), UniFields.GetFloat(s.Fields, "ypanningfloor", 0.0));
FirstVertex = s.Sidedefs.First<Sidedef>().Line.Start;
PreviousFirstVertexPosition = FirstVertex.Position;
// Get transform properties
Floor.Offset = new Vector2D(UniFields.GetFloat(s.Fields, "xpanningfloor", 0.0), UniFields.GetFloat(s.Fields, "ypanningfloor", 0.0));
Ceiling.Offset = new Vector2D(UniFields.GetFloat(s.Fields, "xpanningceiling", 0.0), UniFields.GetFloat(s.Fields, "ypanningceiling", 0.0));
Floor.Scale = new Vector2D(UniFields.GetFloat(s.Fields, "xscalefloor", 1.0), -UniFields.GetFloat(s.Fields, "yscalefloor", 1.0));
Ceiling.Scale = new Vector2D(UniFields.GetFloat(s.Fields, "xscaleceiling", 1.0), -UniFields.GetFloat(s.Fields, "yscaleceiling", 1.0));
@ -105,7 +109,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Surface name
Floor.Part = "floor";
Ceiling.Part = "ceiling";
}
}
private static Size GetTextureSize(long hash)
{
@ -879,15 +884,20 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
foreach(KeyValuePair<Sector, SectorTextureInfo> group in selectedsectors)
{
group.Key.Fields.BeforeFieldsChange();
Sector eachSector = group.Key;
SectorTextureInfo eachSectorTexInfo = group.Value;
Vector2D newFirstVertexPosition = new Vector2D( Math.Round(eachSectorTexInfo.FirstVertex.Position.x, General.Map.FormatInterface.VertexDecimals),
Math.Round(eachSectorTexInfo.FirstVertex.Position.y, General.Map.FormatInterface.VertexDecimals) );
// Apply transforms
UpdateTextureTransform(group.Key.Fields, group.Value.Ceiling /*, transformceiloffsets, rotateceiloffsets, scaleceiloffsets */);
UpdateTextureTransform(group.Key.Fields, group.Value.Floor /*, transformflooroffsets, rotateflooroffsets, scaleflooroffsets */);
eachSector.Fields.BeforeFieldsChange();
// Update cache
group.Key.UpdateNeeded = true;
group.Key.UpdateCache();
// Apply transforms
UpdateTextureTransform(eachSector.Fields, eachSectorTexInfo.Ceiling, newFirstVertexPosition, eachSectorTexInfo.PreviousFirstVertexPosition);
UpdateTextureTransform(eachSector.Fields, eachSectorTexInfo.Floor, newFirstVertexPosition, eachSectorTexInfo.PreviousFirstVertexPosition);
// Update cache
eachSector.UpdateNeeded = true;
eachSector.UpdateCache();
}
// Map was changed
@ -895,18 +905,40 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
//mxd. This updates texture transforms in given UniFields
private void UpdateTextureTransform(UniFields fields, SurfaceTextureInfo si /*, bool transformoffsets, bool rotateoffsets, bool scaleoffsets */)
private void UpdateTextureTransform(UniFields fields, SurfaceTextureInfo si, Vector2D newReferencePosition, Vector2D previousReferencePosition)
{
if ((si.Part == "floor" && pinfloortextures) || (si.Part == "ceiling" && pinceilingtextures))
{
double texrotation = Angle2D.PI2 - rotation;
if (si.Scale.x != 0 && si.Scale.y != 0)
{
double selectionRotationRad = Angle2D.PI2 - rotation;
double newSurfaceRotationRad = selectionRotationRad + si.Rotation;
double trotation = texrotation + si.Rotation;
Vector2D o = ((referencepoint - selectionbasecenter).GetRotated(-trotation) + selectionbasecenter + this.offset - this.baseoffset).GetRotated(trotation);
Vector2D textureSize = new Vector2D ((double)si.TextureSize.Width / si.Scale.x,
(double)si.TextureSize.Height / -si.Scale.y);
fields["xpanning" + si.Part] = new UniValue(UniversalType.Float, Math.Round(-o.x + si.Offset.x, General.Map.FormatInterface.VertexDecimals));
fields["ypanning" + si.Part] = new UniValue(UniversalType.Float, Math.Round(o.y + si.Offset.y, General.Map.FormatInterface.VertexDecimals));
fields["rotation" + si.Part] = new UniValue(UniversalType.Float, General.ClampAngle(Math.Round(Angle2D.RadToDeg(trotation), General.Map.FormatInterface.VertexDecimals)));
double previousSurfaceRotationRad = Angle2D.PI2 - si.Rotation;
//Set the new surface texture rotation
double newSurfaceRotationDegrees = General.ClampAngle(Math.Round(Angle2D.RadToDeg(newSurfaceRotationRad), General.Map.FormatInterface.VertexDecimals));
fields["rotation" + si.Part] = new UniValue(UniversalType.Float, newSurfaceRotationDegrees);
//Find the offset required to place the texture origin point at the reference vector
Vector2D globalOffsetForNewReferencePoint = ConvertToOffsetCoordinates(newReferencePosition);
Vector2D surfaceOffsetForNewReferencePoint = GetClampedOffsetVector(globalOffsetForNewReferencePoint.GetRotated(-newSurfaceRotationRad), textureSize);
//find an "origin point offset" using the previous texture offset, relative to our reference vertex
Vector2D rotatedPreviousReferencePosition = GetClampedOffsetVector(previousReferencePosition.GetRotated(-previousSurfaceRotationRad), textureSize);
Vector2D previousSurfaceOffset = ConvertToOffsetCoordinates(GetClampedOffsetVector(si.Offset, textureSize));
Vector2D localSurfaceAdjustment = rotatedPreviousReferencePosition - previousSurfaceOffset;
//Adjust our offset by applying using the "origin point offset" to the offset for our reference vertex
Vector2D adjustedSurfaceOffset = GetClampedOffsetVector(surfaceOffsetForNewReferencePoint - ConvertToOffsetCoordinates(localSurfaceAdjustment), textureSize);
//Set the new texture offset
fields["xpanning" + si.Part] = new UniValue(UniversalType.Float, adjustedSurfaceOffset.x);
fields["ypanning" + si.Part] = new UniValue(UniversalType.Float, adjustedSurfaceOffset.y);
}
}
else
{
@ -919,6 +951,25 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
}
private Vector2D ConvertToOffsetCoordinates(Vector2D v)
{
return new Vector2D(-v.x, v.y);
}
private Vector2D GetClampedOffsetVector(Vector2D v, Vector2D textureSize)
{
Vector2D roundedV = new Vector2D(
Math.Round(v.x, General.Map.FormatInterface.VertexDecimals),
Math.Round(v.y, General.Map.FormatInterface.VertexDecimals));
Vector2D roundedTextureSize = new Vector2D(
Math.Round(textureSize.x, General.Map.FormatInterface.VertexDecimals),
Math.Round(textureSize.y, General.Map.FormatInterface.VertexDecimals));
return new Vector2D(roundedV.x % roundedTextureSize.x,
roundedV.y % roundedTextureSize.y);
}
//mxd. This restores texture transforms for all sectors
private void RestoreTextureTransform()
{
@ -1300,8 +1351,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
if (General.Map.UDMF)
{
foreach (Sector s in General.Map.Map.GetSectorsFromLinedefs(selectedlines))
if(!s.Fields.ContainsKey(MapSet.VIRTUAL_SECTOR_FIELD)) // Ignore sectors that have the VIRTUAL_SECTOR_FIELD UDMF field created when cloning the MapSet when copying
selectedsectors.Add(s, new SectorTextureInfo(s));
{
if (!s.Fields.ContainsKey(MapSet.VIRTUAL_SECTOR_FIELD)) // Ignore sectors that have the VIRTUAL_SECTOR_FIELD UDMF field created when cloning the MapSet when copying
{
selectedsectors.Add(s, new SectorTextureInfo(s));
}
}
}
// Array to keep original coordinates

View file

@ -101,7 +101,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public virtual void SelectNeighbours(bool select, bool withSameTexture, bool withSameHeight) { } //mxd
//mxd
override protected void PerformAutoSelection()
override public void PerformAutoSelection()
{
if(!performautoselection) return;
if(Triangles > 0)

View file

@ -94,7 +94,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
#region ================== Methods
//mxd
override protected void PerformAutoSelection()
public override void PerformAutoSelection()
{
if(!performautoselection) return;
if(Triangles > 0)

View file

@ -1515,8 +1515,53 @@ namespace CodeImp.DoomBuilder.BuilderModes
// (Re)create special effects
RebuildElementData();
//mxd. Update event lines
renderer.SetEventLines(LinksCollector.GetHelperShapes(General.Map.ThingsFilter.VisibleThings, blockmap));
// Objects are only selected when they are created, so for objects that are selected we have to make sure
// that they are created immediately. Otherwise the selection order will not be correct, or the objects
// will not be selected at all if they are out of the user's camera range when entering visual mode
// See https://github.com/jewalky/UltimateDoomBuilder/issues/938
if (useSelectionFromClassicMode)
{
foreach (Sector s in General.Map.Map.GetSelectedSectors(true))
{
BaseVisualSector bvs = CreateBaseVisualSector(s);
bvs.Ceiling.PerformAutoSelection();
bvs.Floor.PerformAutoSelection();
}
// Things are automatically selected on creation
foreach (Thing t in General.Map.Map.GetSelectedThings(true))
CreateVisualThing(t);
// For linedefs it's a bit more complicated...
foreach (Linedef ld in General.Map.Map.GetSelectedLinedefs(true))
{
foreach (Sidedef sd in new Sidedef[] { ld.Front, ld.Back })
{
if (sd != null)
{
if (!allsectors.ContainsKey(sd.Sector))
CreateBaseVisualSector(sd.Sector).Rebuild(); // We have to rebuild the sector so that potential 3D floors get created
VisualSidedefParts vsp = ((BaseVisualSector)allsectors[sd.Sector]).Sides[sd];
vsp.upper?.PerformAutoSelection();
vsp.middlesingle?.PerformAutoSelection();
vsp.middledouble?.PerformAutoSelection();
vsp.lower?.PerformAutoSelection();
if (vsp.middle3d != null)
foreach (VisualMiddle3D vm in vsp.middle3d)
vm.PerformAutoSelection();
if (vsp.middleback != null)
foreach (VisualMiddleBack vm in vsp.middleback)
vm.PerformAutoSelection();
}
}
}
}
//mxd. Update event lines
renderer.SetEventLines(LinksCollector.GetHelperShapes(General.Map.ThingsFilter.VisibleThings, blockmap));
// [ZZ] this enables calling of this object from the outside world. Only after properly initialized pls.
base.OnEngage();

View file

@ -218,10 +218,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
double thingz = Thing.IsFlipped ? sd.Ceiling.plane.GetZ(Thing.Position) - Thing.Position.z - Thing.Height : Thing.Position.z + sd.Floor.plane.GetZ(Thing.Position);
Vector3D thingpos = new Vector3D(Thing.Position.x, Thing.Position.y, thingz);
SectorLevel level = sd.GetLevelAboveOrAt(thingpos);
// If is thing's height is flush to a 3D floor top it's not rendered at the brightness of the 3D floor, so take the level above that.
// It's actually a bit more intricate, since GZDoom can render multiple vertical brightness levels for each thing, which UDB can't,
// so this is more of a workaround than a real solution
// See https://github.com/jewalky/UltimateDoomBuilder/issues/940
//SectorLevel level = sd.GetLevelAboveOrAt(thingpos);
SectorLevel level = sd.GetLevelAbove(thingpos);
//mxd. Let's use point on floor plane instead of Thing.Sector.FloorHeight;
if(nointeraction && level == null && sd.LightLevels.Count > 0) level = sd.LightLevels[sd.LightLevels.Count - 1];
if (nointeraction && level == null && sd.LightLevels.Count > 0) level = sd.LightLevels[sd.LightLevels.Count - 1];
//mxd. Use the light level of the highest surface when a thing is above highest sector level.
if(level != null)
@ -324,12 +330,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Determine sprite size and offset
float radius = sprite.ScaledWidth * 0.5f;
float height = sprite.ScaledHeight;
ISpriteImage spriteimg = sprite as ISpriteImage;
if(spriteimg != null)
{
offsets.x = radius - spriteimg.OffsetX;
offsets.y = spriteimg.OffsetY - height;
}
offsets.x = radius - (sprite.OffsetX == int.MinValue ? 0 : sprite.OffsetX);
offsets.y = (sprite.OffsetY == int.MinValue ? 0 : sprite.OffsetY) - height;
// Scale by thing type/actor scale
// We do this after the offset x/y determination above, because that is entirely in sprite pixels space

View file

@ -386,11 +386,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
string skewtype = Sidedef.Fields.GetValue("skew_bottom_type", "none");
if ((skewtype == "front" || skewtype == "back") && Texture != null)
if ((skewtype == "front_floor" || skewtype == "front_ceiling" || skewtype == "back_floor" || skewtype == "back_ceiling") && Texture != null)
{
double leftz, rightz;
if (skewtype == "front")
if (skewtype == "front_floor")
{
if (Sidedef.IsFront)
{
@ -405,7 +405,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
rightz = plane.GetZ(Sidedef.Line.Start.Position);
}
}
else // "back"
else if(skewtype == "back_floor")
{
if (Sidedef.IsFront)
{
@ -419,7 +419,36 @@ namespace CodeImp.DoomBuilder.BuilderModes
leftz = plane.GetZ(Sidedef.Line.End.Position);
rightz = plane.GetZ(Sidedef.Line.Start.Position);
}
}
else if(skewtype == "front_ceiling")
{
if (Sidedef.IsFront)
{
Plane plane = Sector.GetSectorData().Ceiling.plane;
leftz = plane.GetZ(Sidedef.Line.Start.Position);
rightz = plane.GetZ(Sidedef.Line.End.Position);
}
else
{
Plane plane = mode.GetSectorData(Sidedef.Other.Sector).Ceiling.plane;
leftz = plane.GetZ(Sidedef.Line.End.Position);
rightz = plane.GetZ(Sidedef.Line.Start.Position);
}
}
else // Back ceiling
{
if (Sidedef.IsFront)
{
Plane plane = mode.GetSectorData(Sidedef.Other.Sector).Ceiling.plane;
leftz = plane.GetZ(Sidedef.Line.Start.Position);
rightz = plane.GetZ(Sidedef.Line.End.Position);
}
else
{
Plane plane = Sector.GetSectorData().Ceiling.plane;
leftz = plane.GetZ(Sidedef.Line.End.Position);
rightz = plane.GetZ(Sidedef.Line.Start.Position);
}
}
skew = new Vector2f(

View file

@ -378,11 +378,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
string skewtype = Sidedef.Fields.GetValue("skew_top_type", "none");
if ((skewtype == "front" || skewtype == "back") && Texture != null)
if ((skewtype == "front_floor" || skewtype == "front_ceiling" || skewtype == "back_floor" || skewtype == "back_ceiling") && Texture != null)
{
double leftz, rightz;
if (skewtype == "front")
if (skewtype == "front_ceiling")
{
if (Sidedef.IsFront)
{
@ -397,7 +397,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
rightz = plane.GetZ(Sidedef.Line.Start.Position);
}
}
else // "back"
else if (skewtype == "back_ceiling")
{
if (Sidedef.IsFront)
{
@ -411,7 +411,36 @@ namespace CodeImp.DoomBuilder.BuilderModes
leftz = plane.GetZ(Sidedef.Line.End.Position);
rightz = plane.GetZ(Sidedef.Line.Start.Position);
}
}
else if(skewtype == "front_floor")
{
if(Sidedef.IsFront)
{
Plane plane = Sector.GetSectorData().Floor.plane;
leftz = plane.GetZ(Sidedef.Line.Start.Position);
rightz = plane.GetZ(Sidedef.Line.End.Position);
}
else
{
Plane plane = mode.GetSectorData(Sidedef.Other.Sector).Floor.plane;
leftz = plane.GetZ(Sidedef.Line.End.Position);
rightz = plane.GetZ(Sidedef.Line.Start.Position);
}
}
else // Back floor
{
if (Sidedef.IsFront)
{
Plane plane = mode.GetSectorData(Sidedef.Other.Sector).Floor.plane;
leftz = plane.GetZ(Sidedef.Line.Start.Position);
rightz = plane.GetZ(Sidedef.Line.End.Position);
}
else
{
Plane plane = Sector.GetSectorData().Floor.plane;
leftz = plane.GetZ(Sidedef.Line.End.Position);
rightz = plane.GetZ(Sidedef.Line.Start.Position);
}
}
skew = new Vector2f(

View file

@ -41,6 +41,12 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
// Make sure the class is public, because only public classes can be seen
// by the core.
//
internal class ToastMessages
{
public static readonly string SOUNDPROPAGATIONMODE = "soundpropagationmode";
}
public class BuilderPlug : Plug
{
#region ================== Constants
@ -170,6 +176,9 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
// Keep a static reference
me = this;
// Register toasts
General.ToastManager.RegisterToast(ToastMessages.SOUNDPROPAGATIONMODE, "Sound propagation mode", "Toasts related to sound propagation mode");
}
public override void OnMapOpenBegin()

View file

@ -0,0 +1,221 @@
#region ================== Copyright (c) 2023 Boris Iwanski
/*
* This program is free software: you can redistribute it and/or modify
*
* it under the terms of the GNU General Public License as published by
*
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program.If not, see<http://www.gnu.org/licenses/>.
*/
#endregion
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Map;
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
internal class LeakFinder
{
public SoundNode Start { get; }
public SoundNode End { get; }
public List<SoundNode> Nodes { get; }
public HashSet<Sector> Sectors { get; }
public bool Finished { get; internal set; }
private ConcurrentDictionary<Linedef, SoundNode> linedefs2nodes;
private int numblockingnodes;
public LeakFinder(Sector source, Vector2D sourceposition, Sector destination, Vector2D destinationposition, HashSet<Sector> sectors)
{
if (!sectors.Contains(source) || !sectors.Contains(destination))
throw new ArgumentException("Sound propagation domain does not contain both the start and end sectors");
End = new SoundNode(destinationposition);
Start = new SoundNode(sourceposition, End) { G = 0 };
Sectors = sectors;
Finished = false;
Nodes = new List<SoundNode>() { Start, End };
linedefs2nodes = new ConcurrentDictionary<Linedef, SoundNode>();
GenerateNodes(sectors);
PopulateStartEndNeighbors(source, Start);
PopulateStartEndNeighbors(destination, End);
}
/// <summary>
/// Checks if the linedef is valid for passing sound.
/// </summary>
/// <param name="linedef">The linedef to check</param>
/// <returns>true if sound can travel through the linedef, false if not</returns>
private bool CheckLinedefValidity(Linedef linedef)
{
if (linedef.Back == null)
return false;
if (linedef.Front.Sector == linedef.Back.Sector)
return false;
if (SoundPropagationDomain.IsSoundBlockedByHeight(linedef))
return false;
return Sectors.Contains(linedef.Front.Sector) && Sectors.Contains(linedef.Back.Sector);
}
/// <summary>
/// Generates all nodes for the A* search algorithm.
/// </summary>
/// <param name="sectors">sectors to generate the nodes from</param>
private void GenerateNodes(HashSet<Sector> sectors)
{
// Create sound nodes for all valid linedefs in all given sectors
foreach(Sector s in sectors)
{
IEnumerable<Sidedef> sidedefs = s.Sidedefs.Where(sd => CheckLinedefValidity(sd.Line));
foreach(Sidedef sd in sidedefs)
{
if(!linedefs2nodes.ContainsKey(sd.Line))
{
linedefs2nodes[sd.Line] = new SoundNode(sd.Line, End);
Nodes.Add(linedefs2nodes[sd.Line]);
}
}
}
// We need the number of blocking nodes for safety checking
numblockingnodes = linedefs2nodes.Values.Count(n => n.IsBlocking);
// Set the neighbors for each node. The amount of interconnections can be very high in complex maps
// (for example there are nearly 3.9 million in Sunder map 20), so do it in parallel for speed
Parallel.ForEach(linedefs2nodes.Keys, ld =>
{
foreach (Sidedef sd in ld.Front.Sector.Sidedefs)
{
if (sd.Line != ld && CheckLinedefValidity(sd.Line))
linedefs2nodes[ld].Neighbors.Add(linedefs2nodes[sd.Line]);
}
foreach (Sidedef sd in ld.Back.Sector.Sidedefs)
{
if (sd.Line != ld && CheckLinedefValidity(sd.Line))
linedefs2nodes[ld].Neighbors.Add(linedefs2nodes[sd.Line]);
}
});
#if DEBUG
int bla = linedefs2nodes.Values.Sum(n => n.Neighbors.Count);
Console.WriteLine($"There are {linedefs2nodes.Keys.Count} nodes with {bla} interconnections.");
#endif
}
/// <summary>
/// Populates a sound node's neightbors to the linedefs of a sector. This is required for the start and end sound nodes.
/// </summary>
/// <param name="sector">The sector which linedef's sound nodes are used</param>
/// <param name="node">The sound node to add the neighbors to</param>
private void PopulateStartEndNeighbors(Sector sector, SoundNode node)
{
foreach(Sidedef sd in sector.Sidedefs)
{
if(CheckLinedefValidity(sd.Line) && linedefs2nodes.ContainsKey(sd.Line))
{
node.Neighbors.Add(linedefs2nodes[sd.Line]);
linedefs2nodes[sd.Line].Neighbors.Add(node);
}
}
}
/// <summary>
/// Finds a sound leak between the start and end sound nodes.
/// </summary>
/// <returns>true if a leak was found, false if no leak was found</returns>
public bool FindLeak()
{
Finished = false;
// Basic A* search. The twist is that sound blocking lines: we can only pass through one of them,
// and A* doesn't backtrack, so it can fail to find a path even if there is a possible one. If that
// happens we set the sound blocking node we traveled through to be ignored, and start again. We repeat
// that until a path was found, or all blocking nodes are set to be ignored (which shouldn't happen)
while (true)
{
List<SoundNode> openset = new List<SoundNode>() { Start };
while (openset.Count > 0)
{
// Find the node with the lowest F score. Doing it that way seems to be fastest
SoundNode current = openset[0];
for (int i = 1; i < openset.Count; i++)
{
if (openset[i].F < current.F)
current = openset[i];
}
// We're done if the node with the lowest F score is the end node
if (current == End)
{
Finished = true;
return true;
}
// Remove the current node from the open set
openset.Remove(current);
// Compute new values for the current node's neighbors
current.ProcessNeighbors(openset, Start);
}
// If we got here we didn't find a path. So we have to start over
int currentnumblockingnodes = 0;
// Reset all nodes
foreach(SoundNode sn in Nodes)
{
// Set the sound nodes that block sound and were visited (the G value was set to something) to be skipped.
if(sn.IsBlocking && sn.G != double.MaxValue)
{
sn.IsSkip = true;
currentnumblockingnodes++;
}
// We need to reset the sound node's G and F values
sn.Reset();
}
// All blocking sound nodes are being skipped, so no path is possible
if (currentnumblockingnodes == numblockingnodes)
{
Finished = true;
return false;
}
// Don't forget the reset the start node to its special values
Start.G = 0.0;
Start.F = Start.H;
}
}
}
}

View file

@ -70,6 +70,16 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap SoundPropagationIcon {
get {
object obj = ResourceManager.GetObject("SoundPropagationIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>

View file

@ -112,15 +112,18 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="ColorManagement" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ColorManagement.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="SoundPropagationIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\SoundPropagationIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Status0" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Status0.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>

View file

@ -54,3 +54,25 @@ soundpropagationcolorconfiguration
allowmouse = true;
allowscroll = true;
}
setleakfinderstart
{
title = "Set leak finder start sector";
category = "soundpropagationmode";
description = "Sets the starting sector for the sound leak finder";
allowkeys = true;
allowmouse = true;
allowscroll = true;
default = 65619; // Shift+S
}
setleakfinderend
{
title = "Set leak finder end sector";
category = "soundpropagationmode";
description = "Sets the ending sector for the sound leak finder";
allowkeys = true;
allowmouse = true;
allowscroll = true;
default = 65605; // Shift+E
}

View file

@ -0,0 +1,8 @@
class SoundPropagationMode
group general
"This mode shows between which sectors sound can travel. Highlight a sector to see where the sound can travel freely (default color: green), and which sectors are behind a single sound blocking line (default color: yellow). Sound can not travel to (default) gray colored sectors. If no sector is highlighted each sound propagation zone is shown in a different color"
"Hold <k>builder_pan_view</k> and move the mouse to pan the view"
"Press <k>builder_classicselect</k> to toggle the sound blocking flag on the highlighted line"
"Press <k>soundpropagationmode_setleakfinderstart</k> to set the start sector, and <k>soundpropagationmode_setleakfinderend</k> to set the end sector to find a sound leak between them. Depending on the map size finding the leak can take some time."
"Press <k>builder_clearselection</k> to clear the start and end sectors for finding a sound leak"
"Press <k>soundpropagationmode_soundpropagationcolorconfiguration</k> or use the button in the tool bar to configure the colors"

View file

@ -0,0 +1,148 @@
#region ================== Copyright (c) 2023 Boris Iwanski
/*
* This program is free software: you can redistribute it and/or modify
*
* it under the terms of the GNU General Public License as published by
*
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program.If not, see<http://www.gnu.org/licenses/>.
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
internal class SoundNode
{
public Vector2D Position { get; set; }
public List<SoundNode> Neighbors { get; set; }
public SoundNode From { get; set; }
public double G { get; set; }
public double H { get; }
public double F { get; set; } // It's G + H, but computing it on the fly is too expensive
public bool IsBlocking { get; }
public bool IsSkip { get; set; }
public SoundNode(Vector2D position)
{
Position = position;
G = double.MaxValue;
H = double.MaxValue;
IsBlocking = false;
IsSkip = false;
Neighbors = new List<SoundNode>();
}
public SoundNode(Vector2D position, SoundNode destination): this(position)
{
H = Vector2D.Distance(Position, destination.Position);
}
public SoundNode(Linedef linedef, SoundNode destination) : this(linedef.Line.GetCoordinatesAt(0.5), destination)
{
IsBlocking = linedef.IsFlagSet(SoundPropagationMode.BlockSoundFlag);
}
/// <summary>
/// Recomputes the values for the sound node's neighbors
/// </summary>
/// <param name="openset">The open set, the add the neighbor to if necessary</param>
/// <param name="start">The start sound node</param>
public void ProcessNeighbors(List<SoundNode> openset, SoundNode start)
{
bool blockinginpath = HasBlockingInPath(start);
foreach (SoundNode neighbor in Neighbors)
{
// Skip neighbors that are blocking if there's already a blocking sound node in the path
// Also skip neighbors that are set to be skipped
if ((neighbor.IsBlocking && blockinginpath) || neighbor.IsSkip)
continue;
double newg = G + Vector2D.Distance(Position, neighbor.Position);
// Compute new values if the path is better
if (newg < neighbor.G)
{
neighbor.From = this;
neighbor.G = newg;
neighbor.F = neighbor.G + neighbor.H;
if (!openset.Contains(neighbor))
openset.Add(neighbor);
}
}
}
/// <summary>
/// Checks if the path from this sound node to the start sound node has a blocking sound node
/// </summary>
/// <param name="start">The start sound node</param>
/// <returns>true if there is a blocking sound node in the path, false if there isn't</returns>
private bool HasBlockingInPath(SoundNode start)
{
SoundNode current = this;
while(current != start)
{
if (current.IsBlocking)
return true;
current = current.From;
}
return false;
}
/// <summary>
/// Resets the sound node's G and F values, and the sound node that leads here
/// </summary>
public void Reset()
{
From = null;
G = double.MaxValue;
F = double.MaxValue;
}
/// <summary>
/// Renders the path from this node to the beginning. Traces the path from this sound node back to the start sound node
/// </summary>
/// <param name="renderer">The Renderer2D to render with</param>
internal void RenderPath(IRenderer2D renderer)
{
SoundNode current = this;
// If the current node is null we have reached the beginning
while(current != null)
{
// Do not render the start and end sound nodes
if (current != this && current.From != null)
{
RectangleF rectangle = new RectangleF((float)(current.Position.x - 4 / renderer.Scale), (float)(current.Position.y - 4 / renderer.Scale), 8 / renderer.Scale, 8 / renderer.Scale);
renderer.RenderRectangleFilled(rectangle, PixelColor.FromColor(Color.Red), true);
}
if(current.From != null)
renderer.RenderLine(current.Position, current.From.Position, 1.0f, PixelColor.FromColor(Color.Red), true);
// One step back
current = current.From;
}
}
}
}

View file

@ -118,6 +118,7 @@
<Compile Include="Interface\SoundEnvironmentPanel.Designer.cs">
<DependentUpon>SoundEnvironmentPanel.cs</DependentUpon>
</Compile>
<Compile Include="LeakFinder.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@ -125,6 +126,7 @@
</Compile>
<Compile Include="SoundEnvironment.cs" />
<Compile Include="SoundEnvironmentMode.cs" />
<Compile Include="SoundNode.cs" />
<Compile Include="SoundPropagationDomain.cs" />
<Compile Include="Windows\ColorConfiguration.cs">
<SubType>Form</SubType>
@ -193,6 +195,9 @@
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Hints.cfg" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View file

@ -108,7 +108,7 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
}
}
private static bool IsSoundBlockedByHeight(Linedef ld)
public static bool IsSoundBlockedByHeight(Linedef ld)
{
if(ld.Back == null || ld.Front == null) return false;

View file

@ -19,12 +19,17 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Actions;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Windows;
using System.Diagnostics;
#endregion
@ -53,9 +58,19 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
private List<Thing> huntingThings;
private List<SoundPropagationDomain> propagationdomains;
private Dictionary<Sector, SoundPropagationDomain> sector2domain;
private LeakFinder leakfinder;
private PixelColor doublesidedcolor;
// The blockmap makes is used to make finding lines faster
BlockMap<BlockEntry> blockmap;
private BlockMap<BlockEntry> blockmap;
private Sector leakstartsector;
private Sector leakendsector;
private Vector2D leakstartposition;
private Vector2D leakendposition;
private TextLabel leakstartlabel;
private TextLabel leakendlabel;
private BackgroundWorker worker;
#endregion
@ -161,7 +176,6 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
{
if(!General.Map.ThingsFilter.VisibleThings.Contains(thing)) continue;
if(thing.IsFlagSet(General.Map.UDMF ? "ambush" : "8")) continue;
if(thing.Sector == null) thing.DetermineSector();
if(thing.Sector != null && noisysectors.ContainsKey(thing.Sector.Index)) huntingThings.Add(thing);
}
}
@ -175,6 +189,7 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
RectangleF area = MapSet.CreateArea(General.Map.Map.Vertices);
blockmap = new BlockMap<BlockEntry>(area);
blockmap.AddLinedefsSet(General.Map.Map.Linedefs);
blockmap.AddSectorsSet(General.Map.Map.Sectors);
}
#endregion
@ -206,6 +221,8 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
sector2domain = new Dictionary<Sector, SoundPropagationDomain>();
BuilderPlug.Me.BlockingLinedefs = new List<Linedef>();
doublesidedcolor = General.Colors.Linedefs.WithAlpha(General.Settings.DoubleSidedAlphaByte);
UpdateData();
General.Interface.AddButton(BuilderPlug.Me.MenusForm.ColorConfiguration);
@ -213,19 +230,42 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
CustomPresentation presentation = new CustomPresentation();
presentation.AddLayer(new PresentLayer(RendererLayer.Background, BlendingMode.Mask, General.Settings.BackgroundAlpha));
presentation.AddLayer(new PresentLayer(RendererLayer.Grid, BlendingMode.Mask));
presentation.AddLayer(new PresentLayer(RendererLayer.Overlay, BlendingMode.Alpha, 1.0f, true));
presentation.AddLayer(new PresentLayer(RendererLayer.Overlay, BlendingMode.Alpha, 1.0f, true)); // First overlay (0)
presentation.AddLayer(new PresentLayer(RendererLayer.Things, BlendingMode.Alpha, 1.0f));
presentation.AddLayer(new PresentLayer(RendererLayer.Geometry, BlendingMode.Alpha, 1.0f, true));
presentation.AddLayer(new PresentLayer(RendererLayer.Overlay, BlendingMode.Alpha, 1.0f, true)); // Second overlay (1)
renderer.SetPresentation(presentation);
leakstartlabel = new TextLabel
{
TransformCoords = true,
AlignX = TextAlignmentX.Center,
AlignY = TextAlignmentY.Middle,
Color = General.Colors.Selection,
BackColor = General.Colors.Background,
Text = "S"
};
leakendlabel = new TextLabel
{
TransformCoords = true,
AlignX = TextAlignmentX.Center,
AlignY = TextAlignmentY.Middle,
Color = General.Colors.Selection,
BackColor = General.Colors.Background,
Text = "E"
};
// Create the blockmap
CreateBlockmap();
// To show things that will wake up we need to know the sector they are in
Parallel.ForEach(General.Map.Map.Things, t => t.DetermineSector(blockmap));
// Convert geometry selection to sectors only
General.Map.Map.ConvertSelection(SelectionType.Sectors);
UpdateSoundPropagation();
General.Interface.RedrawDisplay();
}
// Mode disengages
@ -236,14 +276,22 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
// Hide highlight info
General.Interface.HideInfo();
if (worker != null)
{
worker.CancelAsync();
worker.Dispose();
}
}
// This redraws the display
public override void OnRedrawDisplay()
{
List<SoundPropagationDomain> renderedspds = new List<SoundPropagationDomain>();
if(BuilderPlug.Me.DataIsDirty) UpdateData();
if (BuilderPlug.Me.DataIsDirty) UpdateData();
// We don't care for the actualy surfaces, but without this the render targets will not be recreated
// when the window is resized
renderer.RedrawSurface();
// Render lines and vertices
@ -253,50 +301,60 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
// Plot lines by hand, so that no coloring (line specials, 3D floors etc.) distracts from
// the sound propagation. Also don't draw the line's normal. They are not needed here anyway
// and can make it harder to see the sound environment propagation
foreach(Linedef ld in General.Map.Map.Linedefs)
if (General.Settings.ParallelizedLinedefPlotting)
{
PixelColor c = (ld.IsFlagSet(General.Map.Config.ImpassableFlag) ?
General.Colors.Linedefs : General.Colors.Linedefs.WithAlpha(General.Settings.DoubleSidedAlphaByte));
renderer.PlotLine(ld.Start.Position, ld.End.Position, c, BuilderPlug.LINE_LENGTH_SCALER);
Parallel.ForEach(General.Map.Map.Linedefs, ld =>
{
PixelColor c = ld.IsFlagSet(General.Map.Config.ImpassableFlag) ? General.Colors.Linedefs : doublesidedcolor;
renderer.PlotLine(ld.Start.Position, ld.End.Position, c, BuilderPlug.LINE_LENGTH_SCALER);
});
}
else
{
foreach (Linedef ld in General.Map.Map.Linedefs)
{
PixelColor c = ld.IsFlagSet(General.Map.Config.ImpassableFlag) ? General.Colors.Linedefs : doublesidedcolor;
renderer.PlotLine(ld.Start.Position, ld.End.Position, c, BuilderPlug.LINE_LENGTH_SCALER);
}
}
// Since there will usually be way less blocking linedefs than total linedefs, it's presumably
// faster to draw them on their own instead of checking if each linedef is in BlockingLinedefs
foreach(Linedef ld in BuilderPlug.Me.BlockingLinedefs)
foreach (Linedef ld in BuilderPlug.Me.BlockingLinedefs)
renderer.PlotLine(ld.Start.Position, ld.End.Position, BuilderPlug.Me.BlockSoundColor, BuilderPlug.LINE_LENGTH_SCALER);
//mxd. Render highlighted line
if(highlightedline != null)
if (highlightedline != null)
renderer.PlotLine(highlightedline.Start.Position, highlightedline.End.Position, General.Colors.Highlight, BuilderPlug.LINE_LENGTH_SCALER);
renderer.Finish();
}
// Render things
if(renderer.StartThings(true))
if (renderer.StartThings(true))
{
renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, General.Settings.HiddenThingsAlpha);
renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, General.Settings.InactiveThingsAlpha);
foreach(Thing thing in huntingThings)
renderer.RenderThing(thing, General.Colors.Selection, General.Settings.ActiveThingsAlpha);
renderer.RenderThingSet(huntingThings, General.Colors.Selection, General.Settings.ActiveThingsAlpha);
renderer.Finish();
}
if(renderer.StartOverlay(true))
// The sound propagation domain overlay
if (renderer.StartOverlay(true))
{
// Render highlighted domain and domains adjacent to it
if(highlighted != null && !highlighted.IsDisposed)
if (highlighted != null && !highlighted.IsDisposed)
{
renderer.RenderGeometry(overlayGeometry, null, true); //mxd
SoundPropagationDomain spd = sector2domain[highlighted];
renderer.RenderGeometry(spd.Level1Geometry, null, true);
foreach(Sector s in spd.AdjacentSectors)
foreach (Sector s in spd.AdjacentSectors)
{
SoundPropagationDomain aspd = sector2domain[s];
if(!renderedspds.Contains(aspd))
if (!renderedspds.Contains(aspd))
{
renderer.RenderGeometry(aspd.Level2Geometry, null, true);
renderedspds.Add(aspd);
@ -308,13 +366,28 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
else
{
//mxd. Render all domains using domain colors
foreach(SoundPropagationDomain spd in propagationdomains)
foreach (SoundPropagationDomain spd in propagationdomains)
renderer.RenderHighlight(spd.Level1Geometry, spd.Color);
}
renderer.Finish();
}
// The sound leak overlay. This is done so that the path and labels are drawn at the very top
if (renderer.StartOverlay(true, 1))
{
if (leakfinder != null && leakfinder.Finished)
leakfinder.End.RenderPath(renderer);
if (leakstartsector != null)
renderer.RenderText(leakstartlabel);
if (leakendsector != null)
renderer.RenderText(leakendlabel);
renderer.Finish();
}
renderer.Present();
}
@ -331,9 +404,26 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
// Update
ResetSoundPropagation();
FindSoundLeak();
General.Interface.RedrawDisplay();
}
public override bool OnUndoBegin()
{
base.OnUndoBegin();
if (worker != null)
{
worker.CancelAsync();
worker.Dispose();
worker = null;
}
return true;
}
//mxd
public override void OnUndoEnd()
{
@ -342,11 +432,28 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
// Recreate the blockmap
CreateBlockmap();
// To show things that will wake up we need to know the sector they are in
Parallel.ForEach(General.Map.Map.Things, t => t.DetermineSector(blockmap));
// Update
ResetSoundPropagation();
General.Interface.RedrawDisplay();
}
public override bool OnRedoBegin()
{
base.OnRedoBegin();
if (worker != null)
{
worker.CancelAsync();
worker.Dispose();
worker = null;
}
return true;
}
//mxd
public override void OnRedoEnd()
{
@ -355,6 +462,9 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
// Recreate the blockmap
CreateBlockmap();
// To show things that will wake up we need to know the sector they are in
Parallel.ForEach(General.Map.Map.Things, t => t.DetermineSector(blockmap));
// Update
ResetSoundPropagation();
General.Interface.RedrawDisplay();
@ -441,7 +551,7 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
General.Interface.ShowSectorInfo(highlighted);
else
General.Interface.HideInfo();
// Redraw display
General.Interface.RedrawDisplay();
}
@ -457,6 +567,79 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
Highlight(null);
}
/// <summary>
/// Starts finding a sound leak. Finding the actual leak is done by a background worker that's started from this method
/// </summary>
private void FindSoundLeak()
{
leakfinder = null;
// Do not show an error if either start or end is not set, since that'll happen when you start out
if (leakstartsector == null || leakendsector == null)
return;
if (leakendsector == leakstartsector)
{
General.ToastManager.ShowToast(ToastMessages.SOUNDPROPAGATIONMODE, ToastType.WARNING, "Sound propagation", "Stard and end position for sound leak are in the same sector");
return;
}
HashSet<Sector> sectors = new HashSet<Sector>(sector2domain[leakstartsector].Sectors);
// Mash all sectors from the leak start sector's domain and the adjacent domains into one hash set
foreach (Sector s in sector2domain[leakstartsector].AdjacentSectors)
sectors.UnionWith(sector2domain[s].Sectors);
// If the leak end sector isn't in the list of sectors there's no way sound can travel between the start and end
if (!sectors.Contains(leakendsector))
{
General.ToastManager.ShowToast(ToastMessages.SOUNDPROPAGATIONMODE, ToastType.WARNING, "Sound propagation", "Sound can not travel between the selected start and end positions");
return;
}
if (worker != null)
{
worker.CancelAsync();
worker.Dispose();
}
General.Interface.DisplayStatus(StatusType.Busy, "Searching for sound leak...");
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += FindSoundLeakStart;
worker.RunWorkerCompleted += FindSoundLeakFinished;
worker.RunWorkerAsync(sectors);
}
/// <summary>
/// Method for the background worker that finds a sound leak.
/// </summary>
/// <param name="sender">The sender</param>
/// <param name="e">The event arguments</param>
private void FindSoundLeakStart(object sender, DoWorkEventArgs e)
{
Stopwatch sw = Stopwatch.StartNew();
leakfinder = new LeakFinder(leakstartsector, leakstartposition, leakendsector, leakendposition, (HashSet<Sector>)e.Argument);
if (leakfinder.FindLeak() == false)
General.ToastManager.ShowToast(ToastMessages.SOUNDPROPAGATIONMODE, ToastType.WARNING, "Sound propagation", "Could not find a leak between the selected start and end positions, even though there should be one. This is weird");
else
General.Interface.DisplayStatus(StatusType.Info, string.Format(@"Searching for sound leak finished. Elapsed time: {0:mm\:ss\.ff}", sw.Elapsed));
}
/// <summary>
/// Method that's called when finding a sound leak finished.
/// </summary>
/// <param name="sender">The sender</param>
/// <param name="e">The event arguments</param>
private void FindSoundLeakFinished(object sender, RunWorkerCompletedEventArgs e)
{
General.Interface.RedrawDisplay();
}
#endregion
#region ================== Actions
@ -466,11 +649,48 @@ namespace CodeImp.DoomBuilder.SoundPropagationMode
{
using(ColorConfiguration cc = new ColorConfiguration())
{
if(cc.ShowDialog((Form)General.Interface) == DialogResult.OK)
if (cc.ShowDialog((Form)General.Interface) == DialogResult.OK)
General.Interface.RedrawDisplay();
}
}
[BeginAction("setleakfinderstart")]
public void SetLeakFinderStartSector()
{
leakstartsector = highlighted;
leakstartposition = mousemappos;
leakstartlabel.Location = mousemappos;
leakfinder = null;
// Redraw to show the label
General.Interface.RedrawDisplay();
FindSoundLeak();
}
[BeginAction("setleakfinderend")]
public void SetLeakFinderEndSector()
{
leakendsector = highlighted;
leakendposition = mousemappos;
leakendlabel.Location = mousemappos;
leakfinder = null;
// Redraw to show the label
General.Interface.RedrawDisplay();
FindSoundLeak();
}
[BeginAction("clearselection", BaseAction = true)]
public void ClearLeakFinder()
{
leakendsector = leakstartsector = null;
leakfinder = null;
General.Interface.RedrawDisplay();
}
#endregion
}
}

View file

@ -1,202 +1,207 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CodeImp.DoomBuilder.SoundPropagationMode</RootNamespace>
<AssemblyName>SoundPropagationMode</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>3.5</OldToolsVersion>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>false</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>false</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug + Profiler|x86' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<DefineConstants>DEBUG;TRACE;PROFILE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release + Profiler|x86' ">
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<Prefer32Bit>false</Prefer32Bit>
<DefineConstants>TRACE;DEBUG</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<Prefer32Bit>false</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>false</Optimize>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug + Profiler|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<Prefer32Bit>false</Prefer32Bit>
<DefineConstants>TRACE;DEBUG;PROFILE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release + Profiler|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<Prefer32Bit>false</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="BuilderPlug.cs" />
<Compile Include="Interface\SoundEnvironmentPanel.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Interface\SoundEnvironmentPanel.Designer.cs">
<DependentUpon>SoundEnvironmentPanel.cs</DependentUpon>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="SoundEnvironment.cs" />
<Compile Include="SoundEnvironmentMode.cs" />
<Compile Include="SoundPropagationDomain.cs" />
<Compile Include="Windows\ColorConfiguration.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\ColorConfiguration.designer.cs">
<DependentUpon>ColorConfiguration.cs</DependentUpon>
</Compile>
<Compile Include="Windows\MenusForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\MenusForm.designer.cs">
<DependentUpon>MenusForm.cs</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SoundPropagationMode.cs" />
<Compile Include="Windows\ReverbsPickerForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\ReverbsPickerForm.Designer.cs">
<DependentUpon>ReverbsPickerForm.cs</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\BuilderMono.csproj">
<Project>{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}</Project>
<Name>Builder</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Actions.cfg" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\ColorManagement.png" />
<EmbeddedResource Include="Windows\ColorConfiguration.resx">
<DependentUpon>ColorConfiguration.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\MenusForm.resx">
<DependentUpon>MenusForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Resources\SoundPropagationIcon.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Interface\SoundEnvironmentPanel.resx">
<DependentUpon>SoundEnvironmentPanel.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Resources\ZDoomSoundEnvironment.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Warning.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Status0.png" />
<EmbeddedResource Include="Windows\ReverbsPickerForm.resx">
<DependentUpon>ReverbsPickerForm.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CodeImp.DoomBuilder.SoundPropagationMode</RootNamespace>
<AssemblyName>SoundPropagationMode</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>3.5</OldToolsVersion>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>false</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>false</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug + Profiler|x86' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<DefineConstants>DEBUG;TRACE;PROFILE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release + Profiler|x86' ">
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<Prefer32Bit>false</Prefer32Bit>
<DefineConstants>TRACE;DEBUG</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<Prefer32Bit>false</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>false</Optimize>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug + Profiler|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<Prefer32Bit>false</Prefer32Bit>
<DefineConstants>TRACE;DEBUG;PROFILE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release + Profiler|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<Prefer32Bit>false</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="BuilderPlug.cs" />
<Compile Include="Interface\SoundEnvironmentPanel.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Interface\SoundEnvironmentPanel.Designer.cs">
<DependentUpon>SoundEnvironmentPanel.cs</DependentUpon>
</Compile>
<Compile Include="LeakFinder.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="SoundEnvironment.cs" />
<Compile Include="SoundEnvironmentMode.cs" />
<Compile Include="SoundNode.cs" />
<Compile Include="SoundPropagationDomain.cs" />
<Compile Include="Windows\ColorConfiguration.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\ColorConfiguration.designer.cs">
<DependentUpon>ColorConfiguration.cs</DependentUpon>
</Compile>
<Compile Include="Windows\MenusForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\MenusForm.designer.cs">
<DependentUpon>MenusForm.cs</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SoundPropagationMode.cs" />
<Compile Include="Windows\ReverbsPickerForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\ReverbsPickerForm.Designer.cs">
<DependentUpon>ReverbsPickerForm.cs</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\BuilderMono.csproj">
<Project>{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}</Project>
<Name>Builder</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Actions.cfg" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\ColorManagement.png" />
<EmbeddedResource Include="Windows\ColorConfiguration.resx">
<DependentUpon>ColorConfiguration.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\MenusForm.resx">
<DependentUpon>MenusForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Resources\SoundPropagationIcon.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Interface\SoundEnvironmentPanel.resx">
<DependentUpon>SoundEnvironmentPanel.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Resources\ZDoomSoundEnvironment.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Warning.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Status0.png" />
<EmbeddedResource Include="Windows\ReverbsPickerForm.resx">
<DependentUpon>ReverbsPickerForm.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<None Include="Resources\Hints.cfg" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
-->
</Project>