From c6f043d908f55883192e140e1427ed9ac7941644 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Fri, 26 May 2023 13:26:15 +0200
Subject: [PATCH 1/4] Implement proper texture skewing in Visual Mode

---
 Build/Configurations/Includes/SRB222_misc.cfg |   4 +
 Source/Core/Config/GameConfiguration.cs       |   8 +
 .../BuilderModes/VisualModes/VisualLower.cs   |  97 ++++++---
 .../VisualModes/VisualMiddle3D.cs             |  67 ++++--
 .../VisualModes/VisualMiddleDouble.cs         | 197 ++++++++++++++----
 .../VisualModes/VisualMiddleSingle.cs         |  87 ++++++--
 .../BuilderModes/VisualModes/VisualUpper.cs   |  93 +++++++--
 7 files changed, 423 insertions(+), 130 deletions(-)

diff --git a/Build/Configurations/Includes/SRB222_misc.cfg b/Build/Configurations/Includes/SRB222_misc.cfg
index 65ce70c60..d2128ad9a 100644
--- a/Build/Configurations/Includes/SRB222_misc.cfg
+++ b/Build/Configurations/Includes/SRB222_misc.cfg
@@ -595,6 +595,8 @@ speciallinedefs
 	impassableflag = 1;
 	upperunpeggedflag = 8;
 	lowerunpeggedflag = 16;
+	slopeskewflag = 32;
+	nomidtextureskewflag = 128;
 	repeatmidtextureflag = 1024;
 	pegmidtextureflag = 256;
 }
@@ -609,6 +611,8 @@ speciallinedefs_udmf
 	lowerunpeggedflag = "dontpegbottom";
 	repeatmidtextureflag = "wrapmidtex";
 	pegmidtextureflag = "midpeg";
+	slopeskewflag = "skewtd";
+	nomidtextureskewflag = "noskew";
 }
 
 scriptlumpnames
diff --git a/Source/Core/Config/GameConfiguration.cs b/Source/Core/Config/GameConfiguration.cs
index a30ef88e3..7ab4968a8 100755
--- a/Source/Core/Config/GameConfiguration.cs
+++ b/Source/Core/Config/GameConfiguration.cs
@@ -78,6 +78,8 @@ namespace CodeImp.DoomBuilder.Config
 		private readonly string upperunpeggedflag;
 		private readonly string lowerunpeggedflag;
 		private readonly string pegmidtextureflag;
+		private readonly string slopeskewflag;
+		private readonly string nomidtextureskewflag;
 		private readonly bool mixtexturesflats;
 		private readonly bool generalizedactions;
 		private readonly bool generalizedeffects;
@@ -242,6 +244,8 @@ namespace CodeImp.DoomBuilder.Config
 		public string UpperUnpeggedFlag { get { return upperunpeggedflag; } }
 		public string LowerUnpeggedFlag { get { return lowerunpeggedflag; } }
 		public string PegMidtextureFlag { get { return pegmidtextureflag; } }
+		public string SlopeSkewFlag { get { return slopeskewflag; } }
+		public string NoMidtextureSkewFlag { get { return nomidtextureskewflag; } }
 		public bool MixTexturesFlats { get { return mixtexturesflats; } }
 		public bool GeneralizedActions { get { return generalizedactions; } }
 		public bool GeneralizedEffects { get { return generalizedeffects; } }
@@ -521,6 +525,10 @@ namespace CodeImp.DoomBuilder.Config
 				pegmidtextureflag = ((int)obj == 0) ? lowerunpeggedflag : ((int)obj).ToString(CultureInfo.InvariantCulture);
 			else
 				pegmidtextureflag = obj.ToString();
+			obj = cfg.ReadSettingObject("slopeskewflag", 0);
+			if (obj is int) slopeskewflag = ((int)obj).ToString(CultureInfo.InvariantCulture); else slopeskewflag = obj.ToString();
+			obj = cfg.ReadSettingObject("nomidtextureskewflag", 0);
+			if (obj is int) nomidtextureskewflag = ((int)obj).ToString(CultureInfo.InvariantCulture); else nomidtextureskewflag = obj.ToString();
 
 			// Get texture and flat sources
 			textureranges = cfg.ReadSetting("textures", new Hashtable());
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualLower.cs b/Source/Plugins/BuilderModes/VisualModes/VisualLower.cs
index 325d38549..16fa8dfb8 100755
--- a/Source/Plugins/BuilderModes/VisualModes/VisualLower.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualLower.cs
@@ -156,40 +156,89 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			// the values are stored in a TexturePlane)
 			// NOTE: I use a small bias for the floor height, because if the difference in
 			// height is 0 then the TexturePlane doesn't work!
-			TexturePlane tp = new TexturePlane();
+			Vector3D vlt, vlb, vrt, vrb;
+			Vector2D tlt, tlb, trt, trb;
+
 			double floorbias = (Sidedef.Other.Sector.FloorHeight == Sidedef.Sector.FloorHeight) ? 1.0 : 0.0;
-			if(Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag))
+			double planefloorbias = Math.Abs(osd.Floor.plane.GetZ(vr) - sd.Floor.plane.GetZ(vr)) < 0.5 ? 1.0 : 0.0;
+			double texturevpeg = 0;
+
+			bool lowerunpegged = Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag);
+			bool slopeskew = Sidedef.Line.IsFlagSet(General.Map.Config.SlopeSkewFlag);
+
+			if (lowerunpegged)
 			{
-				if(Sidedef.Sector.HasSkyCeiling && Sidedef.Other.Sector.HasSkyCeiling) 
+				if (slopeskew)
 				{
-					// mxd. Replicate Doom texture offset glitch when front and back sector's ceilings are sky
-					tp.tlt.y = (double)Sidedef.Other.Sector.CeilHeight - Sidedef.Other.Sector.FloorHeight;
-				} 
+					texturevpeg = sd.Ceiling.plane.GetZ(vl) - osd.Floor.plane.GetZ(vl);
+				}
+				//if(Sidedef.Sector.HasSkyCeiling && Sidedef.Other.Sector.HasSkyCeiling) 
+				//{
+				//	// mxd. Replicate Doom texture offset glitch when front and back sector's ceilings are sky
+				//	texturevpeg = (double)Sidedef.Other.Sector.CeilHeight - Sidedef.Other.Sector.FloorHeight;
+				//} 
 				else 
 				{
 					// When lower unpegged is set, the lower texture is bound to the bottom
-					tp.tlt.y = (double) Sidedef.Sector.CeilHeight - Sidedef.Other.Sector.FloorHeight;
+					texturevpeg = (double) Sidedef.Sector.CeilHeight - Sidedef.Other.Sector.FloorHeight;
 				}
 			}
-			tp.trb.x = tp.tlt.x + Math.Round(Sidedef.Line.Length); //mxd. (G)ZDoom snaps texture coordinates to integral linedef length
-			tp.trb.y = tp.tlt.y + (Sidedef.Other.Sector.FloorHeight - (Sidedef.Sector.FloorHeight + floorbias));
-			
+			tlt.x = tlb.x = 0;
+			trt.x = trb.x = Math.Round(Sidedef.Line.Length); //mxd. (G)ZDoom snaps texture coordinates to integral linedef length
+			tlt.y = trt.y = texturevpeg;
+			tlb.y = trb.y = texturevpeg + (Sidedef.Other.Sector.FloorHeight - (Sidedef.Sector.FloorHeight + floorbias));
+
+			if (!slopeskew)
+			{
+				// Unskewed
+				tlb.y -= sd.Floor.plane.GetZ(vl) - Sidedef.Sector.FloorHeight;
+				trb.y -= sd.Floor.plane.GetZ(vr) - Sidedef.Sector.FloorHeight;
+				tlt.y -= osd.Floor.plane.GetZ(vl) - Sidedef.Other.Sector.FloorHeight;
+				trt.y -= osd.Floor.plane.GetZ(vr) - Sidedef.Other.Sector.FloorHeight;
+			}
+			else if (lowerunpegged)
+			{
+				// Skewed by bottom
+				tlb.y = texturevpeg + osd.Floor.plane.GetZ(vl) - sd.Floor.plane.GetZ(vl);
+				trb.y = tlb.y;
+				tlt.y = tlb.y - (osd.Floor.plane.GetZ(vl) - sd.Floor.plane.GetZ(vl));
+				trt.y = trb.y - (osd.Floor.plane.GetZ(vr) - sd.Floor.plane.GetZ(vr));
+			}
+			else
+			{
+				// Skewed by top
+				tlb.y = texturevpeg + osd.Floor.plane.GetZ(vl) - sd.Floor.plane.GetZ(vl);
+				trb.y = texturevpeg + osd.Floor.plane.GetZ(vr) - sd.Floor.plane.GetZ(vr);
+			}
+
+			if (Math.Abs(trb.y - trt.y) < 0.5f) trb.y = trt.y - 1.0f;
+
 			// Apply texture offset
-			tp.tlt += tof;
-			tp.trb += tof;
-			
+			tlt += tof;
+			tlb += tof;
+			trb += tof;
+			trt += tof;
+
 			// Transform pixel coordinates to texture coordinates
-			tp.tlt /= tsz;
-			tp.trb /= tsz;
-			
-			// Left top and right bottom of the geometry that
-			tp.vlt = new Vector3D(vl.x, vl.y, Sidedef.Other.Sector.FloorHeight);
-			tp.vrb = new Vector3D(vr.x, vr.y, Sidedef.Sector.FloorHeight + floorbias);
-			
-			// Make the right-top coordinates
-			tp.trt = new Vector2D(tp.trb.x, tp.tlt.y);
-			tp.vrt = new Vector3D(tp.vrb.x, tp.vrb.y, tp.vlt.z);
-			
+			tlt /= tsz;
+			tlb /= tsz;
+			trb /= tsz;
+			trt /= tsz;
+
+			// Geometry coordinates
+			vlt = new Vector3D(vl.x, vl.y, osd.Floor.plane.GetZ(vl));
+			vlb = new Vector3D(vl.x, vl.y, sd.Floor.plane.GetZ(vl));
+			vrb = new Vector3D(vr.x, vr.y, sd.Floor.plane.GetZ(vr) + planefloorbias);
+			vrt = new Vector3D(vr.x, vr.y, osd.Floor.plane.GetZ(vr));
+
+			TexturePlane tp = new TexturePlane();
+			tp.tlt = lowerunpegged ? tlb : tlt;
+			tp.trb = trb;
+			tp.trt = trt;
+			tp.vlt = lowerunpegged ? vlb : vlt;
+			tp.vrb = vrb;
+			tp.vrt = vrt;
+
 			// Create initial polygon, which is just a quad between floor and ceiling
 			WallPolygon poly = new WallPolygon();
 			poly.Add(new Vector3D(vl.x, vl.y, vlzf));
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualMiddle3D.cs b/Source/Plugins/BuilderModes/VisualModes/VisualMiddle3D.cs
index aa8645348..bcd389efe 100755
--- a/Source/Plugins/BuilderModes/VisualModes/VisualMiddle3D.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualMiddle3D.cs
@@ -162,7 +162,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			Vector2D tsz = new Vector2D(Math.Ceiling(base.Texture.ScaledWidth / tscale.x), Math.Ceiling(base.Texture.ScaledHeight / tscale.y));
 			
 			// Get texture offsets
-			Vector2D tof = new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY) + new Vector2D(sourceside.OffsetX, sourceside.OffsetY);
+			//Vector2D tof = new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY) + new Vector2D(sourceside.OffsetX, sourceside.OffsetY);
+			Vector2D tof = new Vector2D(Sidedef.OffsetX, 0.0f) + new Vector2D(0.0f, sourceside.OffsetY);
 
 			tof = tof + toffset1 + toffset2;
 
@@ -184,35 +185,59 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			// We choose here.
 			double sourcetopheight = extrafloor.VavoomType ? sourceside.Sector.FloorHeight : sourceside.Sector.CeilHeight;
 			double sourcebottomheight = extrafloor.VavoomType ? sourceside.Sector.CeilHeight : sourceside.Sector.FloorHeight;
-			
+
 			// Determine texture coordinates plane as they would be in normal circumstances.
 			// We can then use this plane to find any texture coordinate we need.
 			// The logic here is the same as in the original VisualMiddleSingle (except that
 			// the values are stored in a TexturePlane)
 			// NOTE: I use a small bias for the floor height, because if the difference in
 			// height is 0 then the TexturePlane doesn't work!
-			TexturePlane tp = new TexturePlane();
-			double floorbias = (sourcetopheight == sourcebottomheight) ? 1.0f : 0.0f;
+			Vector3D vlt, vlb, vrt, vrb;
+			Vector2D tlt, tlb, trt, trb;
+			bool lowerunpegged = sourceside.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag);
+			bool slopeskew = sourceside.Line.IsFlagSet(General.Map.Config.SlopeSkewFlag);
+
+			double topheight = slopeskew ? extrafloor.Ceiling.plane.GetZ(vl) : sourcetopheight;
+			double bottomheight = slopeskew ? extrafloor.Floor.plane.GetZ(vl) : sourcebottomheight;
+			double topheight2 = slopeskew ? extrafloor.Ceiling.plane.GetZ(vr) : sourcetopheight;
+			double bottomheight2 = slopeskew ? extrafloor.Floor.plane.GetZ(vr) : sourcebottomheight;
+			//float floorbias = (topheight == bottomheight) ? 1.0f : 0.0f;
+			//float floorbias2 = (topheight2 == bottomheight2) ? 1.0f : 0.0f;
+
+			tlt.x = tlb.x = 0;
+			trt.x = trb.x = Sidedef.Line.Length;
+			// For SRB2, invert Lower Unpegged behavior for non-skewed 3D floors
+			tlt.y = !(lowerunpegged ^ slopeskew) ? 0 : -(topheight - bottomheight);
+			trt.y = !(lowerunpegged ^ slopeskew) ? 0 : -(topheight2 - bottomheight2);
+			tlb.y = !(!lowerunpegged ^ slopeskew) ? 0 : (topheight - bottomheight);
+			trb.y = !(!lowerunpegged ^ slopeskew) ? 0 : (topheight2 - bottomheight2);
 
-			tp.trb.x = tp.tlt.x + Math.Round(Sidedef.Line.Length); //mxd. (G)ZDoom snaps texture coordinates to integral linedef length
-			tp.trb.y = tp.tlt.y + (sourcetopheight - sourcebottomheight) + floorbias;
-			
 			// Apply texture offset
-			tp.tlt += tof;
-			tp.trb += tof;
-			
+			tlt += tof;
+			tlb += tof;
+			trb += tof;
+			trt += tof;
+
 			// Transform pixel coordinates to texture coordinates
-			tp.tlt /= tsz;
-			tp.trb /= tsz;
-			
-			// Left top and right bottom of the geometry that
-			tp.vlt = new Vector3D(vl.x, vl.y, sourcetopheight);
-			tp.vrb = new Vector3D(vr.x, vr.y, sourcebottomheight + floorbias);
-			
-			// Make the right-top coordinates
-			tp.trt = new Vector2D(tp.trb.x, tp.tlt.y);
-			tp.vrt = new Vector3D(tp.vrb.x, tp.vrb.y, tp.vlt.z);
-			
+			tlt /= tsz;
+			tlb /= tsz;
+			trb /= tsz;
+			trt /= tsz;
+
+			// Geometry coordinates
+			vlt = new Vector3D(vl.x, vl.y, topheight);
+			vlb = new Vector3D(vl.x, vl.y, bottomheight);
+			vrb = new Vector3D(vr.x, vr.y, bottomheight2);
+			vrt = new Vector3D(vr.x, vr.y, topheight2);
+
+			TexturePlane tp = new TexturePlane();
+			tp.tlt = lowerunpegged ? tlb : tlt;
+			tp.trb = trb;
+			tp.trt = trt;
+			tp.vlt = lowerunpegged ? vlb : vlt;
+			tp.vrb = vrb;
+			tp.vrt = vrt;
+
 			//mxd. Get ceiling and floor heights. Use our and neighbour sector's data
 			SectorData sdo = mode.GetSectorData(Sidedef.Other.Sector);
 
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs
index e958a2d8c..56bd62863 100755
--- a/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs
@@ -153,35 +153,139 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			// the values are stored in a TexturePlane)
 			// NOTE: I use a small bias for the floor height, because if the difference in
 			// height is 0 then the TexturePlane doesn't work!
-			TexturePlane tp = new TexturePlane();
+			Vector3D vlt, vlb, vrt, vrb;
+			Vector2D tlt, tlb, trt, trb;
 			double floorbias = (Sidedef.Sector.CeilHeight == Sidedef.Sector.FloorHeight) ? 1.0 : 0.0;
 			double geotop = Math.Min(Sidedef.Sector.CeilHeight, Sidedef.Other.Sector.CeilHeight);
 			double geobottom = Math.Max(Sidedef.Sector.FloorHeight, Sidedef.Other.Sector.FloorHeight);
-			double zoffset = Sidedef.Sector.CeilHeight - Sidedef.Other.Sector.CeilHeight; //mxd
+			double geoplanetop = Math.Min(sd.Ceiling.plane.GetZ(vl), osd.Ceiling.plane.GetZ(vl));
+			double geoplanebottom = Math.Max(sd.Floor.plane.GetZ(vl), osd.Floor.plane.GetZ(vl));
 
-			// When peg midtexture is set, the middle texture is bound to the bottom
-			if(Sidedef.Line.IsFlagSet(General.Map.Config.PegMidtextureFlag))
-				tp.tlt.y = tsz.y - (geotop - geobottom);
-			
-			if(zoffset > 0) tp.tlt.y -= zoffset; //mxd
-			tp.trb.x = tp.tlt.x + Math.Round(Sidedef.Line.Length); //mxd. (G)ZDoom snaps texture coordinates to integral linedef length
-			tp.trb.y = tp.tlt.y + (Sidedef.Sector.CeilHeight - (Sidedef.Sector.FloorHeight + floorbias));
+			bool lowerunpegged = Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag);
+			bool pegmidtexture = Sidedef.Line.IsFlagSet(General.Map.Config.PegMidtextureFlag);
+			bool nomidtextureskew = Sidedef.Line.IsFlagSet(General.Map.Config.NoMidtextureSkewFlag);
+
+			double textop = nomidtextureskew ? geotop : geoplanetop;
+			double texbottom = nomidtextureskew ? geobottom : geoplanebottom;
 
-			// Apply texture offset
-			tp.tlt += tof;
-			tp.trb += tof;
+			if (General.Map.UDMF)
+			{
+				repeatmidtex = Sidedef.IsFlagSet("wrapmidtex") || Sidedef.Line.IsFlagSet("wrapmidtex"); //mxd
+				repetitions = repeatmidtex ? Sidedef.Fields.GetValue("repeatcnt", 0) + 1 : 1;
+			}
+			else if (General.Map.HEXEN)
+			{
+				repeatmidtex = Sidedef.Line.Action == 121 && (Sidedef.Line.Args[1] & 16) == 16;
+				repetitions = 1;
+			}
+			else
+			{
+				repeatmidtex = false;
+				repetitions = 1;
+			}
+
+			// First determine the visible portion of the texture
+			if (!RepeatIndefinitely)
+			{
+				// Determine top portion height
+				if (Sidedef.Line.IsFlagSet(General.Map.Config.PegMidtextureFlag))
+					textop = texbottom + tof.y + repetitions * Math.Abs(tsz.y);
+				else
+					textop += tof.y;
+
+				// Calculate bottom portion height
+				texbottom = textop - repetitions * Math.Abs(tsz.y);
+			}
+			else
+			{
+				if (Sidedef.Line.IsFlagSet(General.Map.Config.PegMidtextureFlag))
+				{
+					//textop = topheight;
+					texbottom += tof.y;
+				}
+				else
+				{
+					textop += tof.y;
+					//texbottom = bottomheight;
+				}
+			}
+
+			double h = Math.Min(textop, geoplanetop);
+			double l = Math.Max(texbottom, geoplanebottom);
+			double texturevpeg;
+
+			// When lower unpegged is set, the middle texture is bound to the bottom
+			if (pegmidtexture)
+				texturevpeg = repetitions * Math.Abs(tsz.y) - h + texbottom;
+			else
+				texturevpeg = textop - h;
 
+			tlt.x = tlb.x = tof.x;
+			trt.x = trb.x = tof.x + Sidedef.Line.Length;
+			tlt.y = trt.y = texturevpeg;
+			tlb.y = trb.y = texturevpeg + h - (l + floorbias);
+
+			double rh = h;
+			double rl = l;
+
+			// Correct to account for slopes
+			double midtextureslant;
+
+			if (nomidtextureskew)
+				midtextureslant = 0;
+			else if (pegmidtexture)
+				midtextureslant = osd.Floor.plane.GetZ(vl) < sd.Floor.plane.GetZ(vl)
+						  ? sd.Floor.plane.GetZ(vr) - sd.Floor.plane.GetZ(vl)
+						  : osd.Floor.plane.GetZ(vr) - osd.Floor.plane.GetZ(vl);
+			else
+				midtextureslant = sd.Ceiling.plane.GetZ(vl) < osd.Ceiling.plane.GetZ(vl)
+						  ? sd.Ceiling.plane.GetZ(vr) - sd.Ceiling.plane.GetZ(vl)
+						  : osd.Ceiling.plane.GetZ(vr) - osd.Ceiling.plane.GetZ(vl);
+
+			double newtextop = textop + midtextureslant;
+			double newtexbottom = texbottom + midtextureslant;
+
+			double highcut = geotop + (sd.Ceiling.plane.GetZ(vl) < osd.Ceiling.plane.GetZ(vl)
+						  ? sd.Ceiling.plane.GetZ(vr) - sd.Ceiling.plane.GetZ(vl)
+						  : osd.Ceiling.plane.GetZ(vr) - osd.Ceiling.plane.GetZ(vl));
+			double lowcut = geobottom + (osd.Floor.plane.GetZ(vl) < sd.Floor.plane.GetZ(vl)
+						  ? sd.Floor.plane.GetZ(vr) - sd.Floor.plane.GetZ(vl)
+						  : osd.Floor.plane.GetZ(vr) - osd.Floor.plane.GetZ(vl));
+
+			// Texture stuff
+			rh = Math.Min(highcut, newtextop);
+			rl = Math.Max(newtexbottom, lowcut);
+
+			double newtexturevpeg;
+
+			// When lower unpegged is set, the middle texture is bound to the bottom
+			if (pegmidtexture)
+				newtexturevpeg = repetitions * Math.Abs(tsz.y) - rh + newtexbottom;
+			else
+				newtexturevpeg = newtextop - rh;
+
+			trt.y = newtexturevpeg;
+			trb.y = newtexturevpeg + rh - (rl + floorbias);
+			
 			// Transform pixel coordinates to texture coordinates
-			tp.tlt /= tsz;
-			tp.trb /= tsz;
+			tlt /= tsz;
+			tlb /= tsz;
+			trb /= tsz;
+			trt /= tsz;
 
-			// Left top and right bottom of the geometry that
-			tp.vlt = new Vector3D(vl.x, vl.y, Sidedef.Sector.CeilHeight);
-			tp.vrb = new Vector3D(vr.x, vr.y, Sidedef.Sector.FloorHeight + floorbias);
+			// Geometry coordinates
+			vlt = new Vector3D(vl.x, vl.y, h);
+			vlb = new Vector3D(vl.x, vl.y, l);
+			vrb = new Vector3D(vr.x, vr.y, rl);
+			vrt = new Vector3D(vr.x, vr.y, rh);
 
-			// Make the right-top coordinates
-			tp.trt = new Vector2D(tp.trb.x, tp.tlt.y);
-			tp.vrt = new Vector3D(tp.vrb.x, tp.vrb.y, tp.vlt.z);
+			TexturePlane tp = new TexturePlane();
+			tp.tlt = pegmidtexture ? tlb : tlt;
+			tp.trb = trb;
+			tp.trt = trt;
+			tp.vlt = pegmidtexture ? vlb : vlt;
+			tp.vrb = vrb;
+			tp.vrt = vrt;
 
 			// Keep top and bottom planes for intersection testing
 			top = sd.Ceiling.plane;
@@ -207,38 +311,43 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			CropPoly(ref poly, osd.Ceiling.plane, true);
 			CropPoly(ref poly, osd.Floor.plane, true);
 
-			// Determine if we should repeat the middle texture. In UDMF this is done with a flag, in Hexen with
-			// a argument to the 121:Line_SetIdentification. See https://www.zdoom.org/w/index.php?title=Line_SetIdentification
-			if (General.Map.UDMF)
-				repeatmidtex = Sidedef.IsFlagSet("wrapmidtex") || Sidedef.Line.IsFlagSet("wrapmidtex"); //mxd
-			else if (General.Map.HEXEN)
-				repeatmidtex = Sidedef.Line.Action == 121 && (Sidedef.Line.Args[1] & 16) == 16;
-			else
-				repeatmidtex = false;
+			// First determine the visible portion of the texture
+			// NOTE: this is done earlier for SRB2 since it's needed earlier
 
-			if(!repeatmidtex) 
+			// Create crop planes (we also need these for intersection testing)
+			if (!nomidtextureskew)
 			{
-				// First determine the visible portion of the texture
-				double textop;
-
-				// Determine top portion height
-				if(Sidedef.Line.IsFlagSet(General.Map.Config.PegMidtextureFlag))
-					textop = geobottom + tof.y + Math.Abs(tsz.y);
-				else
-					textop = geotop + tof.y;
+				if (pegmidtexture)
+				{
+					bottomclipplane = sd.Floor.plane.GetZ(vl) > osd.Floor.plane.GetZ(vl) ? sd.Floor.plane : osd.Floor.plane;
+					Vector3D up = new Vector3D(0f, 0f, texbottom - bottomclipplane.GetZ(vl));
+					bottomclipplane.Offset -= Vector3D.DotProduct(up, bottomclipplane.Normal);
 
-				// Calculate bottom portion height
-				double texbottom = textop - Math.Abs(tsz.y);
+					topclipplane = bottomclipplane.GetInverted();
+					Vector3D up2 = new Vector3D(0f, 0f, textop - texbottom);
+					topclipplane.Offset -= Vector3D.DotProduct(up2, topclipplane.Normal);
+				}
+				else //Skew according to ceiling
+				{
+					topclipplane = sd.Ceiling.plane.GetZ(vl) < osd.Ceiling.plane.GetZ(vl) ? sd.Ceiling.plane : osd.Ceiling.plane;
+					Vector3D up = new Vector3D(0f, 0f, textop - topclipplane.GetZ(vl));
+					topclipplane.Offset -= Vector3D.DotProduct(up, topclipplane.Normal);
 
-				// Create crop planes (we also need these for intersection testing)
+					bottomclipplane = topclipplane.GetInverted();
+					Vector3D down = new Vector3D(0f, 0f, texbottom - textop);
+					bottomclipplane.Offset -= Vector3D.DotProduct(down, bottomclipplane.Normal);
+				}
+			}
+			else
+			{
 				topclipplane = new Plane(new Vector3D(0, 0, -1), textop);
 				bottomclipplane = new Plane(new Vector3D(0, 0, 1), -texbottom);
-
-				// Crop polygon by these heights
-				CropPoly(ref poly, topclipplane, true);
-				CropPoly(ref poly, bottomclipplane, true);
 			}
 
+			// Crop polygon by these heights
+			CropPoly(ref poly, topclipplane, true);
+			CropPoly(ref poly, bottomclipplane, true);
+
 			//mxd. In(G)ZDoom, middle sidedef parts are not clipped by extrafloors of any type...
 			List<WallPolygon> polygons = new List<WallPolygon> { poly };
 			//ClipExtraFloors(polygons, sd.ExtraFloors, true); //mxd
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualMiddleSingle.cs b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleSingle.cs
index c9d75e5b0..c045e30a7 100755
--- a/Source/Plugins/BuilderModes/VisualModes/VisualMiddleSingle.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleSingle.cs
@@ -139,31 +139,76 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			// the values are stored in a TexturePlane)
 			// NOTE: I use a small bias for the floor height, because if the difference in
 			// height is 0 then the TexturePlane doesn't work!
-			TexturePlane tp = new TexturePlane();
-			double floorbias = (Sidedef.Sector.CeilHeight == Sidedef.Sector.FloorHeight) ? 1.0 : 0.0;
-			if(Sidedef.Line.IsFlagSet(General.Map.Config.PegMidtextureFlag))
+			Vector3D vlt, vlb, vrt, vrb;
+			Vector2D tlt, tlb, trt, trb;
+			double texturevpeg = 0;
+			double floorbias = (Sidedef.Sector.CeilHeight == Sidedef.Sector.FloorHeight) ? 1.0f : 0.0f;
+
+			bool lowerunpegged = Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag);
+			bool nomidtextureskew = Sidedef.Line.IsFlagSet(General.Map.Config.NoMidtextureSkewFlag);
+
+			if (lowerunpegged)
 			{
-				// When peg midtexture is set, the middle texture is bound to the bottom
-				tp.tlt.y = tsz.y - (Sidedef.Sector.CeilHeight - Sidedef.Sector.FloorHeight);
+				// When lower unpegged is set, the middle texture is bound to the bottom
+				if (!nomidtextureskew)
+				{
+					texturevpeg = tsz.y - (sd.Ceiling.plane.GetZ(vl) - sd.Floor.plane.GetZ(vl));
+				}
+				else
+				{
+					texturevpeg = tsz.y - (Sidedef.Sector.CeilHeight - Sidedef.Sector.FloorHeight);
+				}
 			}
-			tp.trb.x = tp.tlt.x + Math.Round(Sidedef.Line.Length); //mxd. (G)ZDoom snaps texture coordinates to integral linedef length
-			tp.trb.y = tp.tlt.y + (Sidedef.Sector.CeilHeight - (Sidedef.Sector.FloorHeight + floorbias));
-			
+
+			tlt.x = tlb.x = 0;
+			trt.x = trb.x = Sidedef.Line.Length;
+			tlt.y = trt.y = texturevpeg;
+			tlb.y = trb.y = texturevpeg + (Sidedef.Sector.CeilHeight - (Sidedef.Sector.FloorHeight + floorbias));
+
+			// Texture correction for slopes
+			if (nomidtextureskew)
+			{
+				tlt.y += Sidedef.Sector.CeilHeight - sd.Ceiling.plane.GetZ(vl);
+				trt.y += Sidedef.Sector.CeilHeight - sd.Ceiling.plane.GetZ(vr);
+				tlb.y += Sidedef.Sector.FloorHeight - sd.Floor.plane.GetZ(vl);
+				trb.y += Sidedef.Sector.FloorHeight - sd.Floor.plane.GetZ(vr);
+			}
+			else if (lowerunpegged)
+			{
+				tlt.y = tlb.y + sd.Floor.plane.GetZ(vl) - sd.Ceiling.plane.GetZ(vl);
+				trt.y = trb.y + sd.Floor.plane.GetZ(vr) - sd.Ceiling.plane.GetZ(vr);
+			}
+			else
+			{
+				tlb.y = tlt.y - (sd.Floor.plane.GetZ(vl) - sd.Ceiling.plane.GetZ(vl));
+				trb.y = trt.y - (sd.Floor.plane.GetZ(vr) - sd.Ceiling.plane.GetZ(vr));
+			}
+
 			// Apply texture offset
-			tp.tlt += tof;
-			tp.trb += tof;
-			
+			tlt += tof;
+			tlb += tof;
+			trb += tof;
+			trt += tof;
+
 			// Transform pixel coordinates to texture coordinates
-			tp.tlt /= tsz;
-			tp.trb /= tsz;
-			
-			// Left top and right bottom of the geometry that
-			tp.vlt = new Vector3D(vl.x, vl.y, Sidedef.Sector.CeilHeight);
-			tp.vrb = new Vector3D(vr.x, vr.y, Sidedef.Sector.FloorHeight + floorbias);
-			
-			// Make the right-top coordinates
-			tp.trt = new Vector2D(tp.trb.x, tp.tlt.y);
-			tp.vrt = new Vector3D(tp.vrb.x, tp.vrb.y, tp.vlt.z);
+			tlt /= tsz;
+			tlb /= tsz;
+			trb /= tsz;
+			trt /= tsz;
+
+			// Geometry coordinates
+			vlt = new Vector3D(vl.x, vl.y, sd.Ceiling.plane.GetZ(vl));
+			vlb = new Vector3D(vl.x, vl.y, sd.Floor.plane.GetZ(vl) + floorbias);
+			vrb = new Vector3D(vr.x, vr.y, sd.Floor.plane.GetZ(vr));
+			vrt = new Vector3D(vr.x, vr.y, sd.Ceiling.plane.GetZ(vr));
+
+			TexturePlane tp = new TexturePlane();
+			tp.tlt = lowerunpegged ? tlb : tlt;
+			tp.trb = trb;
+			tp.trt = trt;
+			tp.vlt = lowerunpegged ? vlb : vlt;
+			tp.vrb = vrb;
+			tp.vrt = vrt;
 
 			// Get ceiling and floor heights
 			double fl = sd.Floor.plane.GetZ(vl);
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualUpper.cs b/Source/Plugins/BuilderModes/VisualModes/VisualUpper.cs
index 3e09a375c..4b300b68c 100755
--- a/Source/Plugins/BuilderModes/VisualModes/VisualUpper.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualUpper.cs
@@ -154,32 +154,85 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			// the values are stored in a TexturePlane)
 			// NOTE: I use a small bias for the floor height, because if the difference in
 			// height is 0 then the TexturePlane doesn't work!
-			TexturePlane tp = new TexturePlane();
+			Vector3D vlt, vlb, vrt, vrb;
+			Vector2D tlt, tlb, trt, trb;
 			double ceilbias = (Sidedef.Other.Sector.CeilHeight == Sidedef.Sector.CeilHeight) ? 1.0 : 0.0;
-			if(!Sidedef.Line.IsFlagSet(General.Map.Config.UpperUnpeggedFlag))
+			double planeceilbias = (Math.Abs(osd.Ceiling.plane.GetZ(vr) - sd.Ceiling.plane.GetZ(vr)) < 0.5) ? 1.0 : 0.0;
+			double texturevpeg = 0;
+
+			bool upperunpegged = Sidedef.Line.IsFlagSet(General.Map.Config.UpperUnpeggedFlag);
+			bool slopeskew = Sidedef.Line.IsFlagSet(General.Map.Config.SlopeSkewFlag);
+
+			if (!upperunpegged)
 			{
 				// When lower unpegged is set, the lower texture is bound to the bottom
-				tp.tlt.y = tsz.y - (Sidedef.Sector.CeilHeight - Sidedef.Other.Sector.CeilHeight);
+				if (slopeskew)
+				{
+					texturevpeg = osd.Ceiling.plane.GetZ(vl) + tsz.y - sd.Ceiling.plane.GetZ(vl);
+				}
+				else
+				{
+					texturevpeg = tsz.y - ((float)Sidedef.Sector.CeilHeight - Sidedef.Other.Sector.CeilHeight);
+				}
 			}
-			tp.trb.x = tp.tlt.x + Math.Round(Sidedef.Line.Length); //mxd. (G)ZDoom snaps texture coordinates to integral linedef length
-			tp.trb.y = tp.tlt.y + (Sidedef.Sector.CeilHeight - (Sidedef.Other.Sector.CeilHeight + ceilbias));
-			
+			tlt.x = tlb.x = 0;
+			trt.x = trb.x = Sidedef.Line.Length;
+			tlt.y = trt.y = texturevpeg;
+			tlb.y = trb.y = texturevpeg + (Sidedef.Sector.CeilHeight - (Sidedef.Other.Sector.CeilHeight + ceilbias));
+
+			// Adjust texture y value for sloped walls
+			if (!slopeskew)
+			{
+				// Unskewed
+				tlt.y -= sd.Ceiling.plane.GetZ(vl) - Sidedef.Sector.CeilHeight;
+				trt.y -= sd.Ceiling.plane.GetZ(vr) - Sidedef.Sector.CeilHeight;
+				tlb.y -= osd.Ceiling.plane.GetZ(vl) - Sidedef.Other.Sector.CeilHeight;
+				trb.y -= osd.Ceiling.plane.GetZ(vr) - Sidedef.Other.Sector.CeilHeight;
+			}
+			else if (upperunpegged)
+			{
+				// Skewed by top
+				tlb.y = texturevpeg + sd.Ceiling.plane.GetZ(vl) - osd.Ceiling.plane.GetZ(vl);
+				trb.y = texturevpeg + sd.Ceiling.plane.GetZ(vr) - osd.Ceiling.plane.GetZ(vr);
+			}
+			else
+			{
+				// Skewed by bottom
+				tlb.y = texturevpeg + sd.Ceiling.plane.GetZ(vl) - osd.Ceiling.plane.GetZ(vl);
+				trb.y = tlb.y;
+				tlt.y = tlb.y - (sd.Ceiling.plane.GetZ(vl) - osd.Ceiling.plane.GetZ(vl));
+				trt.y = trb.y - (sd.Ceiling.plane.GetZ(vr) - osd.Ceiling.plane.GetZ(vr));
+			}
+
+
+			if (Math.Abs(trb.y - trt.y) < 0.5f) trb.y = trt.y - 1.0f;
+
 			// Apply texture offset
-			tp.tlt += tof;
-			tp.trb += tof;
-			
+			tlt += tof;
+			tlb += tof;
+			trb += tof;
+			trt += tof;
+
 			// Transform pixel coordinates to texture coordinates
-			tp.tlt /= tsz;
-			tp.trb /= tsz;
-			
-			// Left top and right bottom of the geometry that
-			tp.vlt = new Vector3D(vl.x, vl.y, Sidedef.Sector.CeilHeight);
-			tp.vrb = new Vector3D(vr.x, vr.y, Sidedef.Other.Sector.CeilHeight + ceilbias);
-			
-			// Make the right-top coordinates
-			tp.trt = new Vector2D(tp.trb.x, tp.tlt.y);
-			tp.vrt = new Vector3D(tp.vrb.x, tp.vrb.y, tp.vlt.z);
-			
+			tlt /= tsz;
+			tlb /= tsz;
+			trb /= tsz;
+			trt /= tsz;
+
+			// Geometry coordinates
+			vlt = new Vector3D(vl.x, vl.y, sd.Ceiling.plane.GetZ(vl));
+			vlb = new Vector3D(vl.x, vl.y, osd.Ceiling.plane.GetZ(vl));
+			vrb = new Vector3D(vr.x, vr.y, osd.Ceiling.plane.GetZ(vr) + planeceilbias);
+			vrt = new Vector3D(vr.x, vr.y, sd.Ceiling.plane.GetZ(vr));
+
+			TexturePlane tp = new TexturePlane();
+			tp.tlt = Sidedef.Line.IsFlagSet(General.Map.Config.UpperUnpeggedFlag) ? tlt : tlb;
+			tp.trb = trb;
+			tp.trt = trt;
+			tp.vlt = Sidedef.Line.IsFlagSet(General.Map.Config.UpperUnpeggedFlag) ? vlt : vlb;
+			tp.vrb = vrb;
+			tp.vrt = vrt;
+
 			// Create initial polygon, which is just a quad between floor and ceiling
 			WallPolygon poly = new WallPolygon();
 			poly.Add(new Vector3D(vl.x, vl.y, sd.Floor.plane.GetZ(vl)));
-- 
GitLab


From 4d9f6ee72980374782e7a798e95dacbdaefcb7f0 Mon Sep 17 00:00:00 2001
From: Dante Helmore <frostu8@protonmail.com>
Date: Sun, 16 Feb 2025 18:33:08 -0800
Subject: [PATCH 2/4] restore texture repeats

---
 Build/Configurations/Includes/RingRacers_misc.cfg        | 4 ++++
 .../BuilderModes/VisualModes/VisualMiddleDouble.cs       | 9 ++++++---
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/Build/Configurations/Includes/RingRacers_misc.cfg b/Build/Configurations/Includes/RingRacers_misc.cfg
index 5f479834d..ac92eec7e 100644
--- a/Build/Configurations/Includes/RingRacers_misc.cfg
+++ b/Build/Configurations/Includes/RingRacers_misc.cfg
@@ -904,6 +904,8 @@ speciallinedefs
 	lowerunpeggedflag = 16;
 	repeatmidtextureflag = 1024;
 	pegmidtextureflag = 256;
+	slopeskewflag = 32;
+	nomidtextureskewflag = 128;
 }
 
 speciallinedefs_udmf
@@ -916,6 +918,8 @@ speciallinedefs_udmf
 	lowerunpeggedflag = "dontpegbottom";
 	repeatmidtextureflag = "wrapmidtex";
 	pegmidtextureflag = "midpeg";
+	slopeskewflag = "skewtd";
+	nomidtextureskewflag = "noskew";
 }
 
 // Texture sources
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs
index 56bd62863..d4521336b 100755
--- a/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs
@@ -38,6 +38,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		#region ================== Variables
 
+		private int repetitions;
 		private bool repeatmidtex;
 		private Plane topclipplane;
 		private Plane bottomclipplane;
@@ -46,6 +47,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		#region ================== Properties
 
+		public bool RepeatIndefinitely { get { return repeatmidtex; } }
+
 		#endregion
 
 		#region ================== Constructor / Setup
@@ -425,11 +428,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
                 % imageWidth);
 
             int oy;
-            if (repeatmidtex)
+            if (RepeatIndefinitely)
             {
                 bool pegbottom = Sidedef.Line.IsFlagSet(General.Map.Config.PegMidtextureFlag);
-				double zoffset = (pegbottom ? Sidedef.Sector.FloorHeight : Sidedef.Sector.CeilHeight);
-                oy = (int)Math.Floor(((pickintersect.z - zoffset) * UniFields.GetFloat(Sidedef.Fields, "scaley_mid", 1.0f) / texscale.y
+				double zoffset = (pegbottom ? bottomclipplane.GetZ(pickintersect) : topclipplane.GetZ(pickintersect));
+                oy = (int)Math.Ceiling(((pickintersect.z - zoffset) * UniFields.GetFloat(Sidedef.Fields, "scaley_mid", 1.0f) / texscale.y
                     - ((Sidedef.OffsetY - UniFields.GetFloat(Sidedef.Fields, "offsety_mid")) / imgscale.y))
                     % imageHeight);
             }
-- 
GitLab


From 191bceaf3f7d0e08c169d54d006d279ebd788c54 Mon Sep 17 00:00:00 2001
From: Dante Helmore <frostu8@protonmail.com>
Date: Sun, 16 Feb 2025 19:39:14 -0800
Subject: [PATCH 3/4] fix `RepeatIndefinitely` property

---
 Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs
index d4521336b..43c04b34d 100755
--- a/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs
@@ -47,7 +47,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		#region ================== Properties
 
-		public bool RepeatIndefinitely { get { return repeatmidtex; } }
+		private bool RepeatIndefinitely { get { return repeatmidtex && repetitions == 1; } }
 
 		#endregion
 
-- 
GitLab


From 125a07dc850383a6004399da2eb64ce13f686011 Mon Sep 17 00:00:00 2001
From: Dante Helmore <frostu8@protonmail.com>
Date: Mon, 17 Feb 2025 13:45:18 -0800
Subject: [PATCH 4/4] split floor bias for VisualMiddleDouble

---
 .../VisualModes/VisualMiddleDouble.cs         | 22 ++++++++++---------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs
index 43c04b34d..589c59c1a 100755
--- a/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualMiddleDouble.cs
@@ -158,7 +158,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			// height is 0 then the TexturePlane doesn't work!
 			Vector3D vlt, vlb, vrt, vrb;
 			Vector2D tlt, tlb, trt, trb;
-			double floorbias = (Sidedef.Sector.CeilHeight == Sidedef.Sector.FloorHeight) ? 1.0 : 0.0;
 			double geotop = Math.Min(Sidedef.Sector.CeilHeight, Sidedef.Other.Sector.CeilHeight);
 			double geobottom = Math.Max(Sidedef.Sector.FloorHeight, Sidedef.Other.Sector.FloorHeight);
 			double geoplanetop = Math.Min(sd.Ceiling.plane.GetZ(vl), osd.Ceiling.plane.GetZ(vl));
@@ -223,13 +222,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			else
 				texturevpeg = textop - h;
 
+			// If the difference in height is zero, TexturePlane works funkily
+			double leftfloorbias = Math.Abs(h - l) <= double.Epsilon ? 1.0 : 0.0;
+
 			tlt.x = tlb.x = tof.x;
 			trt.x = trb.x = tof.x + Sidedef.Line.Length;
 			tlt.y = trt.y = texturevpeg;
-			tlb.y = trb.y = texturevpeg + h - (l + floorbias);
-
-			double rh = h;
-			double rl = l;
+			tlb.y = trb.y = texturevpeg + h - (l + leftfloorbias);
 
 			// Correct to account for slopes
 			double midtextureslant;
@@ -256,8 +255,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
 						  : osd.Floor.plane.GetZ(vr) - osd.Floor.plane.GetZ(vl));
 
 			// Texture stuff
-			rh = Math.Min(highcut, newtextop);
-			rl = Math.Max(newtexbottom, lowcut);
+			double rh = Math.Min(highcut, newtextop);
+			double rl = Math.Max(newtexbottom, lowcut);
 
 			double newtexturevpeg;
 
@@ -267,8 +266,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			else
 				newtexturevpeg = newtextop - rh;
 
+			// If the difference in height is zero, TexturePlane works funkily
+			double rightfloorbias = Math.Abs(rh - rl) <= double.Epsilon ? 1.0 : 0.0;
+
 			trt.y = newtexturevpeg;
-			trb.y = newtexturevpeg + rh - (rl + floorbias);
+			trb.y = newtexturevpeg + rh - (rl + rightfloorbias);
 			
 			// Transform pixel coordinates to texture coordinates
 			tlt /= tsz;
@@ -278,8 +280,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 			// Geometry coordinates
 			vlt = new Vector3D(vl.x, vl.y, h);
-			vlb = new Vector3D(vl.x, vl.y, l);
-			vrb = new Vector3D(vr.x, vr.y, rl);
+			vlb = new Vector3D(vl.x, vl.y, l + leftfloorbias);
+			vrb = new Vector3D(vr.x, vr.y, rl + rightfloorbias);
 			vrt = new Vector3D(vr.x, vr.y, rh);
 
 			TexturePlane tp = new TexturePlane();
-- 
GitLab