UltimateZoneBuilder/Source/Plugins/BuilderModes/VisualModes/SectorData.cs

409 lines
12 KiB
C#
Raw Normal View History

2010-09-02 20:42:38 +00:00
#region === Copyright (c) 2010 Pascal van der Heiden ===
using System.Collections.Generic;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
2010-09-02 20:42:38 +00:00
{
internal class SectorData
{
#region ================== Variables
2010-09-03 15:12:07 +00:00
2010-09-11 20:14:36 +00:00
// VisualMode
private readonly BaseVisualMode mode;
2010-09-11 20:14:36 +00:00
2010-09-02 20:42:38 +00:00
// Sector for which this data is
private readonly Sector sector;
2010-09-03 15:12:07 +00:00
2010-09-11 20:14:36 +00:00
// Levels have been updated?
private bool updated;
2010-09-02 20:42:38 +00:00
2010-09-11 20:14:36 +00:00
// This prevents recursion
private bool isupdating;
2010-09-03 15:12:07 +00:00
2010-09-11 20:14:36 +00:00
// All planes in the sector that cast or are affected by light
private readonly List<SectorLevel> lightlevels;
2010-09-10 13:17:38 +00:00
2010-09-11 20:14:36 +00:00
// Effects
private readonly List<SectorEffect> alleffects;
private readonly List<Effect3DFloor> extrafloors;
2010-09-11 20:14:36 +00:00
// Sectors that must be updated when this sector is changed
// The boolean value is the 'includeneighbours' of the UpdateSectorGeometry function which
// indicates if the sidedefs of neighbouring sectors should also be rebuilt.
private readonly Dictionary<Sector, bool> updatesectors;
2010-09-03 15:12:07 +00:00
// Original floor and ceiling levels
private readonly SectorLevel floor;
private readonly SectorLevel ceiling;
2010-09-03 15:12:07 +00:00
// This helps keeping track of changes
// otherwise we update ceiling/floor too much
private bool floorchanged;
private bool ceilingchanged;
2010-09-02 20:42:38 +00:00
#endregion
2010-09-03 15:12:07 +00:00
2010-09-02 20:42:38 +00:00
#region ================== Properties
2010-09-03 15:12:07 +00:00
2010-09-02 20:42:38 +00:00
public Sector Sector { get { return sector; } }
2010-09-11 20:14:36 +00:00
public bool Updated { get { return updated; } }
public bool FloorChanged { get { return floorchanged; } set { floorchanged |= value; } }
public bool CeilingChanged { get { return ceilingchanged; } set { ceilingchanged |= value; } }
2010-09-10 13:17:38 +00:00
public List<SectorLevel> LightLevels { get { return lightlevels; } }
2010-09-11 20:14:36 +00:00
public List<Effect3DFloor> ExtraFloors { get { return extrafloors; } }
2010-09-03 15:12:07 +00:00
public SectorLevel Floor { get { return floor; } }
public SectorLevel Ceiling { get { return ceiling; } }
2010-09-11 20:14:36 +00:00
public BaseVisualMode Mode { get { return mode; } }
public Dictionary<Sector, bool> UpdateAlso { get { return updatesectors; } }
2010-09-03 15:12:07 +00:00
2010-09-02 20:42:38 +00:00
#endregion
2010-09-03 15:12:07 +00:00
2010-09-02 20:42:38 +00:00
#region ================== Constructor / Destructor
2010-09-03 15:12:07 +00:00
2010-09-02 20:42:38 +00:00
// Constructor
2010-09-03 15:12:07 +00:00
public SectorData(BaseVisualMode mode, Sector s)
2010-09-02 20:42:38 +00:00
{
// Initialize
2010-09-11 20:14:36 +00:00
this.mode = mode;
2010-09-02 20:42:38 +00:00
this.sector = s;
2010-09-11 20:14:36 +00:00
this.updated = false;
this.floorchanged = false;
this.ceilingchanged = false;
2010-09-10 13:17:38 +00:00
this.lightlevels = new List<SectorLevel>(2);
2010-09-11 20:14:36 +00:00
this.extrafloors = new List<Effect3DFloor>(1);
this.alleffects = new List<SectorEffect>(1);
this.updatesectors = new Dictionary<Sector, bool>(2);
this.floor = new SectorLevel(sector, SectorLevelType.Floor);
this.ceiling = new SectorLevel(sector, SectorLevelType.Ceiling);
2010-09-11 20:14:36 +00:00
BasicSetup();
2010-09-03 15:12:07 +00:00
2010-09-11 20:14:36 +00:00
// Add ceiling and floor
lightlevels.Add(floor);
lightlevels.Add(ceiling);
}
#endregion
#region ================== Public Methods
// 3D Floor effect
public void AddEffect3DFloor(Linedef sourcelinedef)
{
Effect3DFloor e = new Effect3DFloor(this, sourcelinedef);
extrafloors.Add(e);
alleffects.Add(e);
}
// Brightness level effect
public void AddEffectBrightnessLevel(Linedef sourcelinedef)
{
EffectBrightnessLevel e = new EffectBrightnessLevel(this, sourcelinedef);
alleffects.Add(e);
}
// Line slope effect
public void AddEffectLineSlope(Linedef sourcelinedef)
{
EffectLineSlope e = new EffectLineSlope(this, sourcelinedef);
alleffects.Add(e);
}
//mxd. Plane copy slope effect
public void AddEffectPlaneClopySlope(Linedef sourcelinedef, bool front)
{
EffectPlaneCopySlope e = new EffectPlaneCopySlope(this, sourcelinedef, front);
alleffects.Add(e);
}
2010-09-11 20:14:36 +00:00
// Copy slope effect
public void AddEffectCopySlope(Thing sourcething)
{
EffectCopySlope e = new EffectCopySlope(this, sourcething);
alleffects.Add(e);
}
// Thing line slope effect
public void AddEffectThingLineSlope(Thing sourcething)
{
EffectThingLineSlope e = new EffectThingLineSlope(this, sourcething);
alleffects.Add(e);
}
2010-09-14 19:14:44 +00:00
// Thing slope effect
public void AddEffectThingSlope(Thing sourcething)
{
EffectThingSlope e = new EffectThingSlope(this, sourcething);
alleffects.Add(e);
}
2010-09-14 19:14:44 +00:00
// Thing vertex slope effect
public void AddEffectThingVertexSlope(List<Thing> sourcethings, bool slopefloor)
{
EffectThingVertexSlope e = new EffectThingVertexSlope(this, sourcethings, slopefloor);
alleffects.Add(e);
}
//mxd. Add UDMF vertex offset effect
public void AddEffectVertexOffset()
{
EffectUDMFVertexOffset e = new EffectUDMFVertexOffset(this);
alleffects.Add(e);
}
2010-09-11 20:14:36 +00:00
// This adds a sector for updating
public void AddUpdateSector(Sector s, bool includeneighbours)
{
updatesectors[s] = includeneighbours;
}
// This adds a sector level
public void AddSectorLevel(SectorLevel level)
{
// Note: Inserting before the end so that the ceiling stays
// at the end and the floor at the beginning
lightlevels.Insert(lightlevels.Count - 1, level);
}
// This resets this sector data and all sectors that require updating after me
public void Reset()
{
if(isupdating)
return;
isupdating = true;
// This is set to false so that this sector is rebuilt the next time it is needed!
updated = false;
2010-09-14 19:14:44 +00:00
// The visual sector associated is now outdated
if(mode.VisualSectorExists(sector))
{
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(sector);
vs.UpdateSectorGeometry(false);
}
// Also reset the sectors that depend on this sector
2010-09-11 20:14:36 +00:00
foreach(KeyValuePair<Sector, bool> s in updatesectors)
{
SectorData sd = mode.GetSectorData(s.Key);
sd.Reset();
}
isupdating = false;
}
// This sets up the basic floor and ceiling, as they would be in normal Doom circumstances
private void BasicSetup()
{
//mxd
if(sector.FloorSlope.GetLengthSq() > 0 && !float.IsNaN(sector.FloorSlopeOffset / sector.FloorSlope.z))
{
// Sloped plane
floor.plane = new Plane(sector.FloorSlope, sector.FloorSlopeOffset);
}
else
{
// Normal (flat) floor plane
floor.plane = new Plane(new Vector3D(0, 0, 1), -sector.FloorHeight);
}
if(sector.CeilSlope.GetLengthSq() > 0 && !float.IsNaN(sector.CeilSlopeOffset / sector.CeilSlope.z))
{
// Sloped plane
ceiling.plane = new Plane(sector.CeilSlope, sector.CeilSlopeOffset);
}
else
{
// Normal (flat) ceiling plane
ceiling.plane = new Plane(new Vector3D(0, 0, -1), sector.CeilHeight);
}
2010-09-03 15:12:07 +00:00
// 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);
// Determine colors & light levels
2010-09-03 15:12:07 +00:00
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));
PixelColor floorcolor = PixelColor.Modulate(lightcolor, floorbrightness);
PixelColor ceilingcolor = PixelColor.Modulate(lightcolor, ceilingbrightness);
floor.color = floorcolor.WithAlpha(255).ToInt();
2010-09-06 06:09:22 +00:00
floor.brightnessbelow = sector.Brightness;
floor.colorbelow = lightcolor.WithAlpha(255);
2010-09-03 15:12:07 +00:00
ceiling.color = ceilingcolor.WithAlpha(255).ToInt();
ceiling.brightnessbelow = sector.Brightness;
2010-09-06 06:09:22 +00:00
ceiling.colorbelow = lightcolor.WithAlpha(255);
2010-09-03 06:03:28 +00:00
}
2010-09-06 06:09:22 +00:00
//mxd
public void UpdateForced()
{
updated = false;
Update();
}
2010-09-11 20:14:36 +00:00
// When no geometry has been changed and no effects have been added or removed,
// you can call this again to update existing effects. The effects will update
// the existing SectorLevels to match with any changes.
public void Update()
2010-09-03 06:03:28 +00:00
{
if(isupdating || updated) return;
2010-09-11 20:14:36 +00:00
isupdating = true;
2010-09-09 21:16:16 +00:00
2010-09-11 20:14:36 +00:00
// Set floor/ceiling to their original setup
BasicSetup();
2010-09-09 06:13:20 +00:00
2010-09-11 20:14:36 +00:00
// Update all effects
foreach(SectorEffect e in alleffects)
e.Update();
2010-09-03 06:03:28 +00:00
// Sort the levels (only if there are more than 2 sector levels - mxd)
if (lightlevels.Count > 2)
{
SectorLevelComparer comparer = new SectorLevelComparer(sector);
lightlevels.Sort(0, lightlevels.Count, comparer); //mxd. Was lightlevels.Sort(1, lightlevels.Count - 2, comparer);
}
//mxd. 3d floors can be above the real ceiling, so let's find it first...
int startindex = lightlevels.Count - 2;
for(int i = lightlevels.Count - 2; i >= 0; i--)
{
if(lightlevels[i].type == SectorLevelType.Ceiling && lightlevels[i].sector.Index == sector.Index)
{
startindex = i;
break;
}
}
2010-09-06 06:09:22 +00:00
// Now that we know the levels in this sector (and in the right order) we
// can determine the lighting in between and on the levels.
2010-09-07 09:35:45 +00:00
// Start from the absolute ceiling and go down to 'cast' the lighting
for(int i = startindex; i >= 0; i--)
2010-09-07 09:35:45 +00:00
{
2010-09-10 13:17:38 +00:00
SectorLevel l = lightlevels[i];
SectorLevel pl = lightlevels[i + 1];
//mxd. If the real floor has "lightfloor" value and the 3d floor above it doesn't cast down light, use real floor's brightness
if(General.Map.UDMF && l == floor && lightlevels.Count > 2 && (pl.disablelighting || pl.restrictlighting) && l.sector.Fields.ContainsKey("lightfloor"))
{
int light = l.sector.Fields.GetValue("lightfloor", pl.brightnessbelow);
pl.brightnessbelow = (l.sector.Fields.GetValue("lightfloorabsolute", false) ? light : l.sector.Brightness + light);
}
2010-09-07 09:35:45 +00:00
// Set color when no color is specified, or when a 3D floor is placed above the absolute floor
2010-09-10 13:17:38 +00:00
if((l.color == 0) || ((l == floor) && (lightlevels.Count > 2)))
2010-09-07 09:35:45 +00:00
{
PixelColor floorbrightness = PixelColor.FromInt(mode.CalculateBrightness(pl.brightnessbelow));
PixelColor floorcolor = PixelColor.Modulate(pl.colorbelow, floorbrightness);
l.color = floorcolor.WithAlpha(255).ToInt();
}
if(l.colorbelow.a == 0) l.colorbelow = pl.colorbelow;
if(l.brightnessbelow == -1) l.brightnessbelow = pl.brightnessbelow;
2010-09-07 09:35:45 +00:00
}
2010-09-14 19:14:44 +00:00
floorchanged = false;
ceilingchanged = false;
updated = true;
2010-09-11 20:14:36 +00:00
isupdating = false;
2010-09-03 06:03:28 +00:00
}
2010-09-06 06:09:22 +00:00
// This returns the level above the given point
public SectorLevel GetLevelAbove(Vector3D pos)
{
SectorLevel found = null;
float dist = float.MaxValue;
2010-09-10 13:17:38 +00:00
foreach(SectorLevel l in lightlevels)
2010-09-06 06:09:22 +00:00
{
float d = l.plane.GetZ(pos) - pos.z;
if((d > 0.0f) && (d < dist))
{
dist = d;
found = l;
}
}
return found;
}
2010-09-08 06:09:03 +00:00
// This returns the level above the given point
public SectorLevel GetCeilingAbove(Vector3D pos)
{
SectorLevel found = null;
float dist = float.MaxValue;
2010-09-10 13:17:38 +00:00
foreach(SectorLevel l in lightlevels)
2010-09-08 06:09:03 +00:00
{
if(l.type == SectorLevelType.Ceiling)
{
float d = l.plane.GetZ(pos) - pos.z;
if((d > 0.0f) && (d < dist))
{
dist = d;
found = l;
}
}
}
return found;
}
2010-09-06 06:09:22 +00:00
// This returns the level below the given point
public SectorLevel GetLevelBelow(Vector3D pos)
{
SectorLevel found = null;
float dist = float.MaxValue;
2010-09-10 13:17:38 +00:00
foreach(SectorLevel l in lightlevels)
2010-09-06 06:09:22 +00:00
{
float d = pos.z - l.plane.GetZ(pos);
if((d > 0.0f) && (d < dist))
{
dist = d;
found = l;
}
}
return found;
}
2010-09-08 06:09:03 +00:00
// This returns the floor below the given point
public SectorLevel GetFloorBelow(Vector3D pos)
{
SectorLevel found = null;
float dist = float.MaxValue;
2010-09-10 13:17:38 +00:00
foreach(SectorLevel l in lightlevels)
2010-09-08 06:09:03 +00:00
{
if(l.type == SectorLevelType.Floor)
{
float d = pos.z - l.plane.GetZ(pos);
if((d > 0.0f) && (d < dist))
{
dist = d;
found = l;
}
}
}
return found;
}
2010-09-03 15:12:07 +00:00
2010-09-02 20:42:38 +00:00
#endregion
}
}