Visual Mode: added "Lower Floor/Ceiling to adjacent sector" and "Raise Floor/Ceiling to adjacent sector" actions.

UDMF texture offsets are now used when splitting a linedef in UDMF map format.
Some changes in auto texture alignment of newly drawn lines.
This commit is contained in:
MaxED 2013-04-02 12:19:25 +00:00
parent c6378809d5
commit 4a6897a6ed
4 changed files with 416 additions and 72 deletions

View file

@ -1431,50 +1431,15 @@ namespace CodeImp.DoomBuilder.Geometry
}
//mxd
if(autoAlignTextureOffsets) {
if(autoAlignTextureOffsets && !General.Map.UDMF) {
//Auto-align new lines
if(newlines.Count > 1) {
float curLength = 0f;
float totalLength = 0f;
foreach(Linedef l in newlines)
totalLength += l.Length;
bool reversed = newlines[0].End != newlines[1].Start;
foreach(Linedef l in newlines) {
if(l.Front != null) {
ImageData texture = null;
if(l.Front.MiddleRequired() && l.Front.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.MiddleTexture)) {
texture = General.Map.Data.GetFlatImage(l.Front.MiddleTexture);
} else if(l.Front.HighRequired() && l.Front.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.HighTexture)) {
texture = General.Map.Data.GetFlatImage(l.Front.HighTexture);
} else if(l.Front.LowRequired() && l.Front.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.LowTexture)) {
texture = General.Map.Data.GetFlatImage(l.Front.LowTexture);
}
if(texture != null)
l.Front.OffsetX = (int)Math.Round((reversed ? totalLength - curLength - l.Length : curLength));// % texture.Width);
}
if(l.Back != null) {
ImageData texture = null;
if(l.Back.MiddleRequired() && l.Back.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.MiddleTexture)) {
texture = General.Map.Data.GetFlatImage(l.Back.MiddleTexture);
} else if(l.Back.HighRequired() && l.Back.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.HighTexture)) {
texture = General.Map.Data.GetFlatImage(l.Back.HighTexture);
} else if(l.Back.LowRequired() && l.Back.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.LowTexture)) {
texture = General.Map.Data.GetFlatImage(l.Back.LowTexture);
}
if(texture != null)
l.Back.OffsetX = (int)Math.Round((reversed ? totalLength - curLength - l.Length : curLength));// % texture.Width);
}
curLength += l.Length;
}
autoAlignTexturesOnSides(newlines, totalLength, (newlines[0].End != newlines[1].Start));
}
}
@ -1491,38 +1456,6 @@ namespace CodeImp.DoomBuilder.Geometry
}
}
foreach(Linedef l in changedLines) {
if(l.Front != null) {
ImageData texture = null;
if(l.Front.MiddleRequired() && l.Front.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.MiddleTexture)) {
texture = General.Map.Data.GetFlatImage(l.Front.MiddleTexture);
} else if(l.Front.HighRequired() && l.Front.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.HighTexture)) {
texture = General.Map.Data.GetFlatImage(l.Front.HighTexture);
} else if(l.Front.LowRequired() && l.Front.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.LowTexture)) {
texture = General.Map.Data.GetFlatImage(l.Front.LowTexture);
}
if (texture != null)
l.Front.OffsetX %= texture.Width;
}
if(l.Back != null) {
ImageData texture = null;
if(l.Back.MiddleRequired() && l.Back.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.MiddleTexture)) {
texture = General.Map.Data.GetFlatImage(l.Back.MiddleTexture);
} else if(l.Back.HighRequired() && l.Back.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.HighTexture)) {
texture = General.Map.Data.GetFlatImage(l.Back.HighTexture);
} else if(l.Back.LowRequired() && l.Back.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.LowTexture)) {
texture = General.Map.Data.GetFlatImage(l.Back.LowTexture);
}
if (texture != null)
l.Back.OffsetX %= texture.Width;
}
}
// Mark new geometry only
General.Map.Map.ClearMarkedLinedefs(false);
General.Map.Map.ClearMarkedVertices(false);
@ -1533,6 +1466,45 @@ namespace CodeImp.DoomBuilder.Geometry
return true;
}
//mxd
private static void autoAlignTexturesOnSides(List<Linedef> lines, float totalLength, bool reversed) {
float curLength = 0f;
foreach(Linedef l in lines) {
if(l.Front != null) {
ImageData texture = null;
if(l.Front.MiddleRequired() && l.Front.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.MiddleTexture)) {
texture = General.Map.Data.GetFlatImage(l.Front.MiddleTexture);
} else if(l.Front.HighRequired() && l.Front.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.HighTexture)) {
texture = General.Map.Data.GetFlatImage(l.Front.HighTexture);
} else if(l.Front.LowRequired() && l.Front.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.LowTexture)) {
texture = General.Map.Data.GetFlatImage(l.Front.LowTexture);
}
if(texture != null)
l.Front.OffsetX = (int)Math.Round((reversed ? totalLength - curLength - l.Length : curLength)) % texture.Width;
}
if(l.Back != null) {
ImageData texture = null;
if(l.Back.MiddleRequired() && l.Back.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.MiddleTexture)) {
texture = General.Map.Data.GetFlatImage(l.Back.MiddleTexture);
} else if(l.Back.HighRequired() && l.Back.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.HighTexture)) {
texture = General.Map.Data.GetFlatImage(l.Back.HighTexture);
} else if(l.Back.LowRequired() && l.Back.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.LowTexture)) {
texture = General.Map.Data.GetFlatImage(l.Back.LowTexture);
}
if(texture != null)
l.Back.OffsetX = (int)Math.Round((reversed ? totalLength - curLength - l.Length : curLength)) % texture.Width;
}
curLength += l.Length;
}
}
#endregion
#region ================== Flat Floodfill
@ -1777,7 +1749,7 @@ namespace CodeImp.DoomBuilder.Geometry
#endregion
#region Thing Alignment (mxd)
#region ================== Thing Alignment (mxd)
public static bool TryAlignThingToLine(Thing t, Linedef l) {
if(l.Back == null) {

View file

@ -27,6 +27,8 @@ using CodeImp.DoomBuilder.Rendering;
using SlimDX.Direct3D9;
using System.Drawing;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Types;
using CodeImp.DoomBuilder.Data;
#endregion
@ -816,7 +818,8 @@ namespace CodeImp.DoomBuilder.Map
nsd.Marked = front.Marked;
// Make texture offset adjustments
nsd.OffsetX += (int)Vector2D.Distance(this.start.Position, this.end.Position);
if(nsd.OffsetX != 0 || !General.Map.UDMF) //mxd
nsd.OffsetX += (int)Vector2D.Distance(this.start.Position, this.end.Position);
}
// Copy back sidedef if exists
@ -828,7 +831,18 @@ namespace CodeImp.DoomBuilder.Map
nsd.Marked = back.Marked;
// Make texture offset adjustments
back.OffsetX += (int)Vector2D.Distance(nl.start.Position, nl.end.Position);
//mxd
int distance = (int)Vector2D.Distance(nl.start.Position, nl.end.Position);
if(back.OffsetX != 0 || General.Map.UDMF)
if(distance != 0) applyTextureOffsetUDMF(back, distance);
else
back.OffsetX += distance;
}
//mxd. Both sides of line are required, so we do it here...
if(nl.Front != null && General.Map.UDMF) {
int distance = (int)Vector2D.Distance(this.start.Position, this.end.Position);
if(distance != 0) applyTextureOffsetUDMF(nl.Front, distance);
}
// Return result
@ -1055,6 +1069,73 @@ namespace CodeImp.DoomBuilder.Map
colorPresetIndex = -1;
}
//mxd
private void applyTextureOffsetUDMF(Sidedef side, int distance) {
float scaleTop = side.Fields.GetValue("scalex_top", 1.0f);
float scaleMid = side.Fields.GetValue("scalex_mid", 1.0f);
float scaleLow = side.Fields.GetValue("scalex_bottom", 1.0f);
//top
if(side.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(side.HighTexture)) {
ImageData texture = General.Map.Data.GetFlatImage(side.HighTexture);
if(side.Fields.ContainsKey("offsetx_top")) {
float value = side.Fields.GetValue("offsetx_top", 0f);
float offset = (float)(Math.Round((value + distance) * scaleTop) % texture.Width);
if(offset != 0)
side.Fields["offsetx_top"].Value = offset;
else
side.Fields.Remove("offsetx_top");
} else if(side.HighRequired()) {
float offset = (float)(Math.Round(distance * scaleTop) % texture.Width);
if(offset != 0)
side.Fields.Add("offsetx_top", new UniValue(UniversalType.Float, offset));
}
}
//middle
if(side.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(side.MiddleTexture)){
ImageData texture = General.Map.Data.GetFlatImage(side.MiddleTexture);
if(side.Fields.ContainsKey("offsetx_mid")) {
float value = side.Fields.GetValue("offsetx_mid", 0f);
float offset = (float)(Math.Round((value + distance) * scaleMid) % texture.Width);
if(offset != 0)
side.Fields["offsetx_mid"].Value = offset;
else
side.Fields.Remove("offsetx_mid");
} else if(side.MiddleRequired()) {
float offset = (float)(Math.Round(distance * scaleMid) % texture.Width);
if(offset != 0)
side.Fields.Add("offsetx_mid", new UniValue(UniversalType.Float, offset));
}
}
//bottom
if(side.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(side.LowTexture)){
ImageData texture = General.Map.Data.GetFlatImage(side.LowTexture);
if(side.Fields.ContainsKey("offsetx_bottom")) {
float value = side.Fields.GetValue("offsetx_bottom", 0f);
float offset = (float)(Math.Round((value + distance) * scaleLow) % texture.Width);
if(offset != 0)
side.Fields["offsetx_bottom"].Value = offset;
else
side.Fields.Remove("offsetx_bottom");
} else if(side.LowRequired()) {
float offset = (float)(Math.Round(distance * scaleLow) % texture.Width);
if(offset != 0)
side.Fields.Add("offsetx_bottom", new UniValue(UniversalType.Float, offset));
}
}
}
// String representation
public override string ToString()
{

View file

@ -494,6 +494,30 @@ raisesector1
repeat = true;
}
//mxd
lowersectortonearest
{
title = "Lower Floor/Ceiling to adjacent sector";
category = "visual";
description = "Lowers the targeted or selected floors/ceilings to match the height of adjacent sector. Also drops selected things to floor.";
allowkeys = true;
allowmouse = true;
allowscroll = true;
default = 34; //PgDn
}
//mxd
raisesectortonearest
{
title = "Raise Floor/Ceiling to adjacent sector";
category = "visual";
description = "Raises the targeted or selected floors/ceilings to match the height of adjacent sector. Also aligns selected things to ceiling.";
allowkeys = true;
allowmouse = true;
allowscroll = true;
default = 33; //PgUp
}
showvisualthings
{
title = "Show Things";

View file

@ -1624,6 +1624,273 @@ namespace CodeImp.DoomBuilder.BuilderModes
PostAction();
}
//mxd
[BeginAction("raisesectortonearest")]
public void RaiseSectorToNearest() {
Dictionary<Sector, VisualFloor> floors = new Dictionary<Sector, VisualFloor>();
Dictionary<Sector, VisualCeiling> ceilings = new Dictionary<Sector, VisualCeiling>();
List<BaseVisualThing> things = new List<BaseVisualThing>();
bool undoGroupCreated = false;
string alignFailDescription = string.Empty;
//get selection
if(selectedobjects.Count == 0) {
IVisualEventReceiver i = (target.picked as IVisualEventReceiver);
if(i is VisualFloor) {
VisualFloor vf = i as VisualFloor;
floors.Add(vf.Level.sector, vf);
} else if(i is VisualCeiling) {
VisualCeiling vc = i as VisualCeiling;
ceilings.Add(vc.Level.sector, vc);
} else if(i is BaseVisualThing) {
things.Add(i as BaseVisualThing);
}
} else {
foreach(IVisualEventReceiver i in selectedobjects) {
if(i is VisualFloor) {
VisualFloor vf = i as VisualFloor;
floors.Add(vf.Level.sector, vf);
} else if(i is VisualCeiling) {
VisualCeiling vc = i as VisualCeiling;
ceilings.Add(vc.Level.sector, vc);
} else if(i is BaseVisualThing) {
things.Add(i as BaseVisualThing);
}
}
}
//process floors...
int maxSelectedHeight = int.MinValue;
int minSelectedCeilingHeight = int.MaxValue;
int targetHeight = int.MaxValue;
//get maximum floor height from selection
foreach(KeyValuePair<Sector, VisualFloor> group in floors) {
if(group.Key.FloorHeight > maxSelectedHeight)
maxSelectedHeight = group.Key.FloorHeight;
if(group.Key.CeilHeight < minSelectedCeilingHeight)
minSelectedCeilingHeight = group.Key.CeilHeight;
}
//get next higher floor from surrounding unselected sectors
foreach(KeyValuePair<Sector, VisualFloor> group in floors) {
foreach(Sidedef side in group.Key.Sidedefs) {
if(side.Other == null || ceilings.ContainsKey(side.Other.Sector) || floors.ContainsKey(side.Other.Sector))
continue;
if(side.Other.Sector.FloorHeight > maxSelectedHeight && side.Other.Sector.FloorHeight < targetHeight && side.Other.Sector.FloorHeight <= minSelectedCeilingHeight)
targetHeight = side.Other.Sector.FloorHeight;
}
}
//change floor height
if(targetHeight != int.MaxValue) {
PreAction(UndoGroup.SectorHeightChange);
undoGroupCreated = true;
foreach(KeyValuePair<Sector, VisualFloor> group in floors) {
if(targetHeight != group.Key.FloorHeight)
group.Value.OnChangeTargetHeight(targetHeight - group.Key.FloorHeight);
}
} else if(floors.Count > 0) {
alignFailDescription = floors.Count > 1 ? "floors" : "floor";
}
//ceilings...
maxSelectedHeight = int.MinValue;
targetHeight = int.MaxValue;
//get highest ceiling height from selection
foreach(KeyValuePair<Sector, VisualCeiling> group in ceilings) {
if(group.Key.CeilHeight > maxSelectedHeight)
maxSelectedHeight = group.Key.CeilHeight;
}
//get next higher ceiling from surrounding unselected sectors
foreach(KeyValuePair<Sector, VisualCeiling> group in ceilings) {
foreach(Sidedef side in group.Key.Sidedefs) {
if(side.Other == null || ceilings.ContainsKey(side.Other.Sector) || floors.ContainsKey(side.Other.Sector))
continue;
if(side.Other.Sector.CeilHeight < targetHeight && side.Other.Sector.CeilHeight > maxSelectedHeight)
targetHeight = side.Other.Sector.CeilHeight;
}
}
//change ceiling height
if(targetHeight != int.MaxValue) {
if(!undoGroupCreated) {
PreAction(UndoGroup.SectorHeightChange);
undoGroupCreated = true;
}
foreach(KeyValuePair<Sector, VisualCeiling> group in ceilings) {
if(targetHeight != group.Key.CeilHeight)
group.Value.OnChangeTargetHeight(targetHeight - group.Key.CeilHeight);
}
} else if(ceilings.Count > 0) {
if(!string.IsNullOrEmpty(alignFailDescription))
alignFailDescription += " and ";
alignFailDescription += ceilings.Count > 1 ? "ceilings" : "ceiling";
}
//and things. Just align them to ceiling
if(General.Map.FormatInterface.HasThingHeight) {
foreach(BaseVisualThing vt in things) {
if(vt.Thing.Sector == null) continue;
ThingTypeInfo ti = General.Map.Data.GetThingInfo(vt.Thing.Type);
int zvalue = (int)(vt.Thing.Sector.FloorHeight + vt.Thing.Position.z);
if(zvalue != vt.Thing.Sector.CeilHeight - ti.Height) {
if(!undoGroupCreated) {
PreAction(UndoGroup.SectorHeightChange);
undoGroupCreated = true;
}
vt.OnChangeTargetHeight((int)(vt.Thing.Sector.CeilHeight - ti.Height) - zvalue);
}
}
}
if(!string.IsNullOrEmpty(alignFailDescription))
General.Interface.DisplayStatus(StatusType.Warning, "Unable to align selected " + alignFailDescription + "!");
PostAction();
}
//mxd
[BeginAction("lowersectortonearest")]
public void LowerSectorToNearest() {
Dictionary<Sector, VisualFloor> floors = new Dictionary<Sector, VisualFloor>();
Dictionary<Sector, VisualCeiling> ceilings = new Dictionary<Sector, VisualCeiling>();
List<BaseVisualThing> things = new List<BaseVisualThing>();
bool undoGroupCreated = false;
string alignFailDescription = string.Empty;
//get selection
if(selectedobjects.Count == 0) {
IVisualEventReceiver i = (target.picked as IVisualEventReceiver);
if(i is VisualFloor) {
VisualFloor vf = i as VisualFloor;
floors.Add(vf.Level.sector, vf);
} else if(i is VisualCeiling) {
VisualCeiling vc = i as VisualCeiling;
ceilings.Add(vc.Level.sector, vc);
} else if(i is BaseVisualThing) {
things.Add(i as BaseVisualThing);
}
}else{
foreach(IVisualEventReceiver i in selectedobjects) {
if(i is VisualFloor) {
VisualFloor vf = i as VisualFloor;
floors.Add(vf.Level.sector, vf);
} else if(i is VisualCeiling) {
VisualCeiling vc = i as VisualCeiling;
ceilings.Add(vc.Level.sector, vc);
} else if(i is BaseVisualThing) {
things.Add(i as BaseVisualThing);
}
}
}
//process floors...
int minSelectedHeight = int.MaxValue;
int targetHeight = int.MinValue;
//get minimum floor height from selection
foreach(KeyValuePair<Sector, VisualFloor> group in floors) {
if(group.Key.FloorHeight < minSelectedHeight)
minSelectedHeight = group.Key.FloorHeight;
}
//get next floor lower height from surrounding unselected sectors
foreach(KeyValuePair<Sector, VisualFloor> group in floors){
foreach(Sidedef side in group.Key.Sidedefs) {
if(side.Other == null || ceilings.ContainsKey(side.Other.Sector) || floors.ContainsKey(side.Other.Sector))
continue;
if(side.Other.Sector.FloorHeight > targetHeight && side.Other.Sector.FloorHeight < minSelectedHeight)
targetHeight = side.Other.Sector.FloorHeight;
}
}
//change floor height
if(targetHeight != int.MinValue) {
PreAction(UndoGroup.SectorHeightChange);
undoGroupCreated = true;
foreach(KeyValuePair<Sector, VisualFloor> group in floors) {
if(targetHeight != group.Key.FloorHeight)
group.Value.OnChangeTargetHeight(targetHeight - group.Key.FloorHeight);
}
} else if(floors.Count > 0) {
alignFailDescription = floors.Count > 1 ? "floors" : "floor";
}
//ceilings...
minSelectedHeight = int.MaxValue;
int maxSelectedFloorHeight = int.MinValue;
targetHeight = int.MinValue;
//get minimum ceiling and maximum floor heights from selection
foreach(KeyValuePair<Sector, VisualCeiling> group in ceilings) {
if(group.Key.CeilHeight < minSelectedHeight)
minSelectedHeight = group.Key.CeilHeight;
if(group.Key.FloorHeight > maxSelectedFloorHeight)
maxSelectedFloorHeight = group.Key.FloorHeight;
}
//get next lower ceiling height from surrounding unselected sectors
foreach(KeyValuePair<Sector, VisualCeiling> group in ceilings) {
foreach(Sidedef side in group.Key.Sidedefs) {
if(side.Other == null || ceilings.ContainsKey(side.Other.Sector) || floors.ContainsKey(side.Other.Sector))
continue;
if(side.Other.Sector.CeilHeight > targetHeight && side.Other.Sector.CeilHeight < minSelectedHeight && side.Other.Sector.CeilHeight >= maxSelectedFloorHeight)
targetHeight = side.Other.Sector.CeilHeight;
}
}
//change ceiling height
if(targetHeight != int.MinValue) {
if(!undoGroupCreated) {
PreAction(UndoGroup.SectorHeightChange);
undoGroupCreated = true;
}
foreach(KeyValuePair<Sector, VisualCeiling> group in ceilings) {
if(targetHeight != group.Key.CeilHeight)
group.Value.OnChangeTargetHeight(targetHeight - group.Key.CeilHeight);
}
} else if(ceilings.Count > 0) {
if(!string.IsNullOrEmpty(alignFailDescription))
alignFailDescription += " and ";
alignFailDescription += ceilings.Count > 1 ? "ceilings" : "ceiling";
}
//and things. Just drop them to ground
if(General.Map.FormatInterface.HasThingHeight){
foreach(BaseVisualThing vt in things) {
if(vt.Thing.Sector == null) continue;
ThingTypeInfo ti = General.Map.Data.GetThingInfo(vt.Thing.Type);
if(vt.Thing.Position.z != 0){
if(!undoGroupCreated) {
PreAction(UndoGroup.SectorHeightChange);
undoGroupCreated = true;
}
vt.OnChangeTargetHeight((int)-vt.Thing.Position.z);
}
}
}
if(!string.IsNullOrEmpty(alignFailDescription))
General.Interface.DisplayStatus(StatusType.Warning, "Unable to align selected " + alignFailDescription + "!");
PostAction();
}
[BeginAction("showvisualthings")]
public void ShowVisualThings()
{