UltimateZoneBuilder/Source/Plugins/BuilderEffects/Interface/DirectionalShadingForm.cs
2020-05-21 14:20:02 +02:00

262 lines
8.4 KiB
C#
Executable file

#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.VisualModes;
using CodeImp.DoomBuilder.Windows;
#endregion
namespace CodeImp.DoomBuilder.BuilderEffects
{
public partial class DirectionalShadingForm : DelayedForm
{
#region ================== Variables
private Dictionary<Sector, Vector3D> sectors; // <sector, floor normal>
private Dictionary<Sidedef, Vector3D> sides; // <side, side normal>
private Dictionary<Sector, int> sectorbrightness;
private Dictionary<Sector, int> sectorcolors;
private Dictionary<Sidedef, int> sidebrightness;
private IEnumerable<VisualSector> visualsectors;
private Vector3D sunvector;
private bool blockupdate;
#endregion
#region ================== Constructor
private DirectionalShadingForm() { }
public DirectionalShadingForm(IEnumerable<Sector> selectedsectors, IEnumerable<Sidedef> selectedsides, IEnumerable<VisualSector> selectedvisualsectors)
{
InitializeComponent();
// Store sectors, collect floor normals
visualsectors = selectedvisualsectors;
sectors = new Dictionary<Sector, Vector3D>();
sectorbrightness = new Dictionary<Sector, int>();
sectorcolors = new Dictionary<Sector, int>();
foreach(Sector s in selectedsectors)
{
sectors.Add(s, Sector.GetFloorPlane(s).Normal);
sectorcolors[s] = UniFields.GetInteger(s.Fields, "lightcolor", PixelColor.INT_WHITE_NO_ALPHA);
// Store initial brightness
if(s.Fields.GetValue("lightfloorabsolute", false))
sectorbrightness[s] = UniFields.GetInteger(s.Fields, "lightfloor");
else
sectorbrightness[s] = s.Brightness;
}
// Store sidedefs, collect side normals
sides = new Dictionary<Sidedef, Vector3D>();
sidebrightness = new Dictionary<Sidedef, int>();
foreach(Sidedef sd in selectedsides)
{
Vector3D normal = Vector3D.FromAngleXY(sd.Line.Angle + Angle2D.PIHALF);
if(sd == sd.Line.Front) normal *= -1;
sides.Add(sd, normal);
// Store initial brightness
if(sd.Fields.GetValue("lightabsolute", false))
sidebrightness[sd] = UniFields.GetInteger(sd.Fields, "light");
else
sidebrightness[sd] = sd.Sector.Brightness;
}
// Create undo
string sectorscount = (sectors.Count > 0 ? (sectors.Count > 1 ? " sectors" : " sector") : "");
string sidescount = (!string.IsNullOrEmpty(sectorscount) ? " and " : "");
sidescount += (sides.Count > 0 ? (sides.Count > 1 ? " sidedefs" : " sidedef") : "");
General.Map.UndoRedo.ClearAllRedos();
General.Map.UndoRedo.CreateUndo("Apply directional shading to " + sectorscount + sidescount);
// Prepare your fields!
foreach(Sector s in sectors.Keys) s.Fields.BeforeFieldsChange();
foreach(Sidedef sd in sides.Keys) sd.Fields.BeforeFieldsChange();
// Load settings
blockupdate = true;
int angle = General.Settings.ReadPluginSetting("directionalshading.sunangle", 45);
sunangle.Angle = angle;
sunangletb.Text = angle.ToString();
lightamount.Value = General.Settings.ReadPluginSetting("directionalshading.lightamount", 64);
lightcolor.Color = PixelColor.FromInt(General.Settings.ReadPluginSetting("directionalshading.lightcolor", 0xFDEBD7));
shadeamount.Value = General.Settings.ReadPluginSetting("directionalshading.shadeamount", 16);
shadecolor.Color = PixelColor.FromInt(General.Settings.ReadPluginSetting("directionalshading.shadecolor", 0xABC8EB));
blockupdate = false;
OnSunAngleChanged();
}
#endregion
#region ================== Methods
private void UpdateShading()
{
// Update sector shading
foreach(KeyValuePair<Sector, Vector3D> group in sectors)
{
// Calculate light amount
double anglediff = Vector3D.DotProduct(group.Value, sunvector);
int targetlight;
PixelColor targetcolor;
// Calculate light and light color when surface normal is rotated towards the sun vector
if(anglediff >= 0.5f)
{
double lightmul = (anglediff - 0.5f) * 2.0f;
targetlight = (int)Math.Round(lightamount.Value * lightmul);
targetcolor = InterpolationTools.InterpolateColor(shadecolor.Color, lightcolor.Color, anglediff);
}
// Otherwise calculate shade and shade color
else
{
double lightmul = (0.5f - anglediff) * -2.0f;
targetlight = (int)Math.Round(shadeamount.Value * lightmul);
targetcolor = InterpolationTools.InterpolateColor(shadecolor.Color, lightcolor.Color, anglediff);
}
// Apply settings
if(group.Key.Fields.GetValue("lightfloorabsolute", false))
UniFields.SetInteger(group.Key.Fields, "lightfloor", General.Clamp(targetlight + sectorbrightness[group.Key], 0, 255), 0);
else
UniFields.SetInteger(group.Key.Fields, "lightfloor", General.Clamp(targetlight, -255, 255), 0);
// Apply sector color
int c = (targetcolor.ToInt() & 0x00FFFFFF);
// Restore initial color?
if(c == PixelColor.INT_WHITE_NO_ALPHA) c = sectorcolors[group.Key];
// Apply color
UniFields.SetInteger(group.Key.Fields, "lightcolor", c, PixelColor.INT_WHITE_NO_ALPHA);
// Mark for update
group.Key.UpdateNeeded = true;
}
// Update sidedef shading
foreach(KeyValuePair<Sidedef, Vector3D> group in sides)
{
// Calculate light amount
double anglediff = Vector3D.DotProduct(group.Value, sunvector);
int targetlight;
// Calculate light and light color when surface normal is rotated towards the sun vector
if(anglediff >= 0.5f)
{
double lightmul = (anglediff - 0.5f) * 2.0f;
targetlight = (int)Math.Round(lightamount.Value * lightmul);
}
// Otherwise calculate shade and shade color
else
{
double lightmul = (0.5f - anglediff) * -2.0f;
targetlight = (int)Math.Round(shadeamount.Value * lightmul);
}
// Apply settings
if(group.Key.Fields.GetValue("lightabsolute", false))
UniFields.SetInteger(group.Key.Fields, "light", General.Clamp(targetlight + sidebrightness[group.Key], 0, 255), 0);
else
UniFields.SetInteger(group.Key.Fields, "light", General.Clamp(targetlight, -255, 255), 0);
// Mark for update
group.Key.Sector.UpdateNeeded = true;
}
// Update map
General.Map.Map.Update();
General.Map.IsChanged = true;
// Update view
if(visualsectors != null)
{
foreach(var vs in visualsectors) vs.UpdateSectorData();
}
else if(sectors.Count > 0)
{
General.Interface.RedrawDisplay();
}
}
private void OnSunAngleChanged()
{
// TODO: Altitude?
sunvector = Vector3D.FromAngleXYZ(Angle2D.DegToRad(sunangle.Angle + 90), Angle2D.DegToRad(45));
UpdateShading();
}
#endregion
#region ================== Events
private void sunangle_AngleChanged(object sender, EventArgs e)
{
if(blockupdate) return;
blockupdate = true;
sunangletb.Text = sunangle.Angle.ToString();
blockupdate = false;
OnSunAngleChanged();
}
private void sunangletb_WhenTextChanged(object sender, EventArgs e)
{
if(blockupdate) return;
blockupdate = true;
sunangle.Angle = sunangletb.GetResult(sunangle.Angle);
blockupdate = false;
OnSunAngleChanged();
}
private void OnShadingChanged(object sender, EventArgs e)
{
if(!blockupdate) UpdateShading();
}
private void apply_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
Close();
}
private void cancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
Close();
}
private void DirectionalShadingForm_FormClosing(object sender, FormClosingEventArgs e)
{
if(this.DialogResult == DialogResult.OK)
{
// Save settings
General.Settings.WritePluginSetting("directionalshading.sunangle", sunangle.Angle);
General.Settings.WritePluginSetting("directionalshading.lightamount", lightamount.Value);
General.Settings.WritePluginSetting("directionalshading.lightcolor", lightcolor.Color.ToInt());
General.Settings.WritePluginSetting("directionalshading.shadeamount", shadeamount.Value);
General.Settings.WritePluginSetting("directionalshading.shadecolor", shadecolor.Color.ToInt());
}
else
{
// Undo changes
General.Map.UndoRedo.WithdrawUndo();
}
}
#endregion
}
}