// This builds the geometry. Returns false when no geometry created.
        public override bool Setup()
        {
            //mxd
            if (Sidedef.LongMiddleTexture == MapSet.EmptyLongName)
            {
                base.SetVertices(null);
                return(false);
            }

            Vector2D vl, vr;

            //mxd. lightfog flag support
            int  lightvalue;
            bool lightabsolute;

            GetLightValue(out lightvalue, out lightabsolute);

            Vector2D tscale = new Vector2D(Sidedef.Fields.GetValue("scalex_mid", 1.0f),
                                           Sidedef.Fields.GetValue("scaley_mid", 1.0f));
            Vector2D tscaleAbs = new Vector2D(Math.Abs(tscale.x), Math.Abs(tscale.y));
            Vector2D toffset   = new Vector2D(Sidedef.Fields.GetValue("offsetx_mid", 0.0f),
                                              Sidedef.Fields.GetValue("offsety_mid", 0.0f));

            // Left and right vertices for this sidedef
            if (Sidedef.IsFront)
            {
                vl = new Vector2D(Sidedef.Line.Start.Position.x, Sidedef.Line.Start.Position.y);
                vr = new Vector2D(Sidedef.Line.End.Position.x, Sidedef.Line.End.Position.y);
            }
            else
            {
                vl = new Vector2D(Sidedef.Line.End.Position.x, Sidedef.Line.End.Position.y);
                vr = new Vector2D(Sidedef.Line.Start.Position.x, Sidedef.Line.Start.Position.y);
            }

            // Load sector data
            SectorData sd  = mode.GetSectorData(Sidedef.Sector);
            SectorData osd = mode.GetSectorData(Sidedef.Other.Sector);

            if (!osd.Updated)
            {
                osd.Update();
            }

            // Load texture
            if (Sidedef.LongMiddleTexture != MapSet.EmptyLongName)
            {
                base.Texture = General.Map.Data.GetTextureImage(Sidedef.LongMiddleTexture);
                if (base.Texture == null || base.Texture is UnknownImage)
                {
                    base.Texture         = General.Map.Data.UnknownTexture3D;
                    setuponloadedtexture = Sidedef.LongMiddleTexture;
                }
                else if (!base.Texture.IsImageLoaded)
                {
                    setuponloadedtexture = Sidedef.LongMiddleTexture;
                }
            }
            else
            {
                // Use missing texture
                base.Texture         = General.Map.Data.MissingTexture3D;
                setuponloadedtexture = 0;
            }

            // Get texture scaled size
            Vector2D tsz = new Vector2D(base.Texture.ScaledWidth, base.Texture.ScaledHeight);

            tsz = tsz / tscale;

            // Get texture offsets
            Vector2D tof = new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY);

            tof = tof + toffset;

            // biwa. Also take the ForceWorldPanning MAPINFO entry into account
            if (General.Map.Config.ScaledTextureOffsets && (!base.Texture.WorldPanning && !General.Map.Data.MapInfo.ForceWorldPanning))
            {
                tof = tof / tscaleAbs;
                tof = tof * base.Texture.Scale;
            }

            // 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();
            float        floorbias = (Sidedef.Sector.CeilHeight == Sidedef.Sector.FloorHeight) ? 1.0f : 0.0f;
            float        geotop    = Math.Min(Sidedef.Sector.CeilHeight, Sidedef.Other.Sector.CeilHeight);
            float        geobottom = Math.Max(Sidedef.Sector.FloorHeight, Sidedef.Other.Sector.FloorHeight);
            float        zoffset   = Sidedef.Sector.CeilHeight - Sidedef.Other.Sector.CeilHeight;    //mxd

            // When lower unpegged is set, the middle texture is bound to the bottom
            if (Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag))
            {
                tp.tlt.y = tsz.y - (geotop - geobottom);
            }

            if (zoffset > 0)
            {
                tp.tlt.y -= zoffset;                                      //mxd
            }
            tp.trb.x = tp.tlt.x + (float)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));

            // Apply texture offset
            tp.tlt += tof;
            tp.trb += 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);

            // Keep top and bottom planes for intersection testing
            top    = sd.Ceiling.plane;
            bottom = sd.Floor.plane;

            // 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)));
            poly.Add(new Vector3D(vl.x, vl.y, sd.Ceiling.plane.GetZ(vl)));
            poly.Add(new Vector3D(vr.x, vr.y, sd.Ceiling.plane.GetZ(vr)));
            poly.Add(new Vector3D(vr.x, vr.y, sd.Floor.plane.GetZ(vr)));

            // Determine initial color
            int lightlevel = lightabsolute ? lightvalue : sd.Ceiling.brightnessbelow + lightvalue;

            //mxd. This calculates light with doom-style wall shading
            PixelColor wallbrightness = PixelColor.FromInt(mode.CalculateBrightness(lightlevel, Sidedef));
            PixelColor wallcolor      = PixelColor.Modulate(sd.Ceiling.colorbelow, wallbrightness);

            fogfactor  = CalculateFogFactor(lightlevel);
            poly.color = wallcolor.WithAlpha(255).ToInt();

            // Cut off the part below the other floor and above the other ceiling
            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;
            }

            if (!repeatmidtex)
            {
                // First determine the visible portion of the texture
                float textop;

                // Determine top portion height
                if (Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag))
                {
                    textop = geobottom + tof.y + Math.Abs(tsz.y);
                }
                else
                {
                    textop = geotop + tof.y;
                }

                // Calculate bottom portion height
                float texbottom = textop - Math.Abs(tsz.y);

                // Create crop planes (we also need these for intersection testing)
                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);
            }

            //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
            //ClipExtraFloors(polygons, osd.ExtraFloors, true); //mxd

            //if(polygons.Count > 0)
            //{
            // Keep top and bottom planes for intersection testing
            top    = osd.Ceiling.plane;
            bottom = osd.Floor.plane;

            // Process the polygon and create vertices
            List <WorldVertex> verts = CreatePolygonVertices(polygons, tp, sd, lightvalue, lightabsolute);

            if (verts.Count > 2)
            {
                // Apply alpha to vertices
                byte alpha = SetLinedefRenderstyle(true);
                if (alpha < 255)
                {
                    for (int i = 0; i < verts.Count; i++)
                    {
                        WorldVertex v = verts[i];
                        v.c      = PixelColor.FromInt(v.c).WithAlpha(alpha).ToInt();
                        verts[i] = v;
                    }
                }

                base.SetVertices(verts);
                return(true);
            }
            //}

            base.SetVertices(null);             //mxd
            return(false);
        }
        // This builds the geometry. Returns false when no geometry created.
        public override bool Setup()
        {
            if (!IsFogBoundary())
            {
                return(false);
            }

            //mxd. lightfog flag support
            int  lightvalue;
            bool lightabsolute;

            GetLightValue(out lightvalue, out lightabsolute);

            // Left and right vertices for this sidedef
            Vector2D vl, vr;

            if (Sidedef.IsFront)
            {
                vl = new Vector2D(Sidedef.Line.Start.Position.x, Sidedef.Line.Start.Position.y);
                vr = new Vector2D(Sidedef.Line.End.Position.x, Sidedef.Line.End.Position.y);
            }
            else
            {
                vl = new Vector2D(Sidedef.Line.End.Position.x, Sidedef.Line.End.Position.y);
                vr = new Vector2D(Sidedef.Line.Start.Position.x, Sidedef.Line.Start.Position.y);
            }

            // Load sector data
            SectorData sd  = mode.GetSectorData(Sidedef.Sector);
            SectorData osd = mode.GetSectorData(Sidedef.Other.Sector);

            if (!osd.Updated)
            {
                osd.Update();
            }

            // Set texture
            base.Texture = General.Map.Data.BlackTexture;

            // Determine texture coordinates plane as they would be in normal circumstances.
            TexturePlane tp        = new TexturePlane();
            float        floorbias = (Sidedef.Sector.CeilHeight == Sidedef.Sector.FloorHeight) ? 1.0f : 0.0f;
            float        zoffset   = Sidedef.Sector.CeilHeight - Sidedef.Other.Sector.CeilHeight;    //mxd

            if (zoffset > 0)
            {
                tp.tlt.y -= zoffset;                         //mxd
            }
            tp.trb.x = tp.tlt.x + Sidedef.Line.Length;
            tp.trb.y = tp.tlt.y + (Sidedef.Sector.CeilHeight - (Sidedef.Sector.FloorHeight + floorbias));

            // 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);

            // Keep top and bottom planes for intersection testing
            top    = sd.Ceiling.plane;
            bottom = sd.Floor.plane;

            // 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)));
            poly.Add(new Vector3D(vl.x, vl.y, sd.Ceiling.plane.GetZ(vl)));
            poly.Add(new Vector3D(vr.x, vr.y, sd.Ceiling.plane.GetZ(vr)));
            poly.Add(new Vector3D(vr.x, vr.y, sd.Floor.plane.GetZ(vr)));

            // Determine initial color
            int lightlevel = sd.Ceiling.brightnessbelow + lightvalue;

            // Calculate fog density
            fogfactor  = CalculateFogFactor(lightlevel);
            poly.color = PixelColor.INT_WHITE;

            // Cut off the part below the other floor and above the other ceiling
            CropPoly(ref poly, osd.Ceiling.plane, true);
            CropPoly(ref poly, osd.Floor.plane, true);

            List <WallPolygon> polygons = new List <WallPolygon> {
                poly
            };

            // Keep top and bottom planes for intersection testing
            top    = osd.Ceiling.plane;
            bottom = osd.Floor.plane;

            // Process the polygon and create vertices
            List <WorldVertex> verts = CreatePolygonVertices(polygons, tp, sd, lightvalue, lightabsolute);

            if (verts.Count > 2)
            {
                base.SetVertices(verts);
                return(true);
            }

            base.SetVertices(null);
            return(false);
        }
        // This builds the geometry. Returns false when no geometry created.
        public override bool Setup()
        {
            Vector2D vl, vr;

            //mxd. Apply sky hack?
            UpdateSkyRenderFlag();

            //mxd. lightfog flag support
            int  lightvalue;
            bool lightabsolute;

            GetLightValue(out lightvalue, out lightabsolute);

            Vector2D tscale = new Vector2D(Sidedef.Fields.GetValue("scalex_mid", 1.0f),
                                           Sidedef.Fields.GetValue("scaley_mid", 1.0f));
            Vector2D toffset = new Vector2D(Sidedef.Fields.GetValue("offsetx_mid", 0.0f),
                                            Sidedef.Fields.GetValue("offsety_mid", 0.0f));

            // Left and right vertices for this sidedef
            if (Sidedef.IsFront)
            {
                vl = new Vector2D(Sidedef.Line.Start.Position.x, Sidedef.Line.Start.Position.y);
                vr = new Vector2D(Sidedef.Line.End.Position.x, Sidedef.Line.End.Position.y);
            }
            else
            {
                vl = new Vector2D(Sidedef.Line.End.Position.x, Sidedef.Line.End.Position.y);
                vr = new Vector2D(Sidedef.Line.Start.Position.x, Sidedef.Line.Start.Position.y);
            }

            // Load sector data
            SectorData sd = mode.GetSectorData(Sidedef.Sector);

            // Texture given?
            if (Sidedef.LongMiddleTexture != MapSet.EmptyLongName)
            {
                // Load texture
                base.Texture = General.Map.Data.GetTextureImage(Sidedef.LongMiddleTexture);
                if (base.Texture == null || base.Texture is UnknownImage)
                {
                    base.Texture         = General.Map.Data.UnknownTexture3D;
                    setuponloadedtexture = Sidedef.LongMiddleTexture;
                }
                else
                {
                    if (!base.Texture.IsImageLoaded)
                    {
                        setuponloadedtexture = Sidedef.LongMiddleTexture;
                    }
                }
            }
            else
            {
                // Use missing texture
                base.Texture         = General.Map.Data.MissingTexture3D;
                setuponloadedtexture = 0;
            }

            // Get texture scaled size
            Vector2D tsz = new Vector2D(base.Texture.ScaledWidth, base.Texture.ScaledHeight);

            tsz = tsz / tscale;

            // Get texture offsets
            Vector2D tof = new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY);

            tof = tof + toffset;
            tof = tof / tscale;
            if (General.Map.Config.ScaledTextureOffsets && !base.Texture.WorldPanning)
            {
                tof = tof * base.Texture.Scale;
            }

            // 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();
            float        floorbias = (Sidedef.Sector.CeilHeight == Sidedef.Sector.FloorHeight) ? 1.0f : 0.0f;

            if (Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag))
            {
                // When lower unpegged is set, the middle texture is bound to the bottom
                tp.tlt.y = tsz.y - (Sidedef.Sector.CeilHeight - Sidedef.Sector.FloorHeight);
            }
            tp.trb.x = tp.tlt.x + (float)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));

            // Apply texture offset
            tp.tlt += tof;
            tp.trb += 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);

            // Get ceiling and floor heights
            float fl = sd.Floor.plane.GetZ(vl);
            float fr = sd.Floor.plane.GetZ(vr);
            float cl = sd.Ceiling.plane.GetZ(vl);
            float cr = sd.Ceiling.plane.GetZ(vr);

            // Anything to see?
            if (((cl - fl) > 0.01f) || ((cr - fr) > 0.01f))
            {
                // Keep top and bottom planes for intersection testing
                top    = sd.Ceiling.plane;
                bottom = sd.Floor.plane;

                // Create initial polygon, which is just a quad between floor and ceiling
                WallPolygon poly = new WallPolygon();
                poly.Add(new Vector3D(vl.x, vl.y, fl));
                poly.Add(new Vector3D(vl.x, vl.y, cl));
                poly.Add(new Vector3D(vr.x, vr.y, cr));
                poly.Add(new Vector3D(vr.x, vr.y, fr));

                // Determine initial color
                int lightlevel = lightabsolute ? lightvalue : sd.Ceiling.brightnessbelow + lightvalue;

                //mxd. This calculates light with doom-style wall shading
                PixelColor wallbrightness = PixelColor.FromInt(mode.CalculateBrightness(lightlevel, Sidedef));
                PixelColor wallcolor      = PixelColor.Modulate(sd.Ceiling.colorbelow, wallbrightness);
                fogfactor  = CalculateFogFactor(lightlevel);
                poly.color = wallcolor.WithAlpha(255).ToInt();

                // Cut out pieces that overlap 3D floors in this sector
                List <WallPolygon> polygons = new List <WallPolygon> {
                    poly
                };
                ClipExtraFloors(polygons, sd.ExtraFloors, false);                 //mxd

                if (polygons.Count > 0)
                {
                    // Process the polygon and create vertices
                    List <WorldVertex> verts = CreatePolygonVertices(polygons, tp, sd, lightvalue, lightabsolute);
                    if (verts.Count > 2)
                    {
                        base.SetVertices(verts);
                        return(true);
                    }
                }
            }

            base.SetVertices(null);             //mxd
            return(false);
        }
        public bool Setup(Effect3DFloor extrafloor)
        {
            Sidedef sourceside = extrafloor.Linedef.Front;

            this.extrafloor = extrafloor;

            //mxd. Extrafloor may've become invalid during undo/redo...
            if (sourceside == null)
            {
                base.SetVertices(null);
                return(false);
            }

            Vector2D vl, vr;

            //mxd. lightfog flag support
            int  lightvalue;
            bool lightabsolute;

            GetLightValue(out lightvalue, out lightabsolute);

            Vector2D tscale = new Vector2D(sourceside.Fields.GetValue("scalex_mid", 1.0f),
                                           sourceside.Fields.GetValue("scaley_mid", 1.0f));
            Vector2D tscaleAbs = new Vector2D(Math.Abs(tscale.x), Math.Abs(tscale.y));
            Vector2D toffset1  = new Vector2D(Sidedef.Fields.GetValue("offsetx_mid", 0.0f),
                                              Sidedef.Fields.GetValue("offsety_mid", 0.0f));
            Vector2D toffset2 = new Vector2D(sourceside.Fields.GetValue("offsetx_mid", 0.0f),
                                             sourceside.Fields.GetValue("offsety_mid", 0.0f));

            // Left and right vertices for this sidedef
            if (Sidedef.IsFront)
            {
                vl = new Vector2D(Sidedef.Line.Start.Position.x, Sidedef.Line.Start.Position.y);
                vr = new Vector2D(Sidedef.Line.End.Position.x, Sidedef.Line.End.Position.y);
            }
            else
            {
                vl = new Vector2D(Sidedef.Line.End.Position.x, Sidedef.Line.End.Position.y);
                vr = new Vector2D(Sidedef.Line.Start.Position.x, Sidedef.Line.Start.Position.y);
            }

            // Load sector data
            SectorData sd = mode.GetSectorData(Sidedef.Sector);

            //mxd. which texture we must use?
            long texturelong = 0;

            if ((sourceside.Line.Args[2] & (int)Effect3DFloor.Flags.UseUpperTexture) != 0)
            {
                if (Sidedef.LongHighTexture != MapSet.EmptyLongName)
                {
                    texturelong = Sidedef.LongHighTexture;
                }
            }
            else if ((sourceside.Line.Args[2] & (int)Effect3DFloor.Flags.UseLowerTexture) != 0)
            {
                if (Sidedef.LongLowTexture != MapSet.EmptyLongName)
                {
                    texturelong = Sidedef.LongLowTexture;
                }
            }
            else if (sourceside.LongMiddleTexture != MapSet.EmptyLongName)
            {
                texturelong = sourceside.LongMiddleTexture;
            }

            // Texture given?
            if (texturelong != 0)
            {
                // Load texture
                base.Texture = General.Map.Data.GetTextureImage(texturelong);
                if (base.Texture == null || base.Texture is UnknownImage)
                {
                    base.Texture         = General.Map.Data.UnknownTexture3D;
                    setuponloadedtexture = texturelong;
                }
                else if (!base.Texture.IsImageLoaded)
                {
                    setuponloadedtexture = texturelong;
                }
            }
            else
            {
                // Use missing texture
                base.Texture         = General.Map.Data.MissingTexture3D;
                setuponloadedtexture = 0;
            }

            // Get texture scaled size
            Vector2D tsz = new Vector2D(base.Texture.ScaledWidth, base.Texture.ScaledHeight);

            tsz = tsz / tscale;

            // Get texture offsets
            Vector2D tof = new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY) + new Vector2D(sourceside.OffsetX, sourceside.OffsetY);

            tof = tof + toffset1 + toffset2;
            tof = tof / tscaleAbs;
            if (General.Map.Config.ScaledTextureOffsets && !base.Texture.WorldPanning)
            {
                tof = tof * base.Texture.Scale;
            }

            // For Vavoom type 3D floors the ceiling is lower than floor and they are reversed.
            // We choose here.
            float sourcetopheight    = extrafloor.VavoomType ? sourceside.Sector.FloorHeight : sourceside.Sector.CeilHeight;
            float 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();
            float        floorbias = (sourcetopheight == sourcebottomheight) ? 1.0f : 0.0f;

            tp.trb.x = tp.tlt.x + (float)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;

            // 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);

            //mxd. Get ceiling and floor heights. Use our and neighbour sector's data
            SectorData sdo = mode.GetSectorData(Sidedef.Other.Sector);

            float flo = sdo.Floor.plane.GetZ(vl);
            float fro = sdo.Floor.plane.GetZ(vr);
            float clo = sdo.Ceiling.plane.GetZ(vl);
            float cro = sdo.Ceiling.plane.GetZ(vr);

            float fle = sd.Floor.plane.GetZ(vl);
            float fre = sd.Floor.plane.GetZ(vr);
            float cle = sd.Ceiling.plane.GetZ(vl);
            float cre = sd.Ceiling.plane.GetZ(vr);

            float fl = flo > fle ? flo : fle;
            float fr = fro > fre ? fro : fre;
            float cl = clo < cle ? clo : cle;
            float cr = cro < cre ? cro : cre;

            // Anything to see?
            if (((cl - fl) > 0.01f) || ((cr - fr) > 0.01f))
            {
                // Keep top and bottom planes for intersection testing
                top    = extrafloor.Floor.plane;
                bottom = extrafloor.Ceiling.plane;

                // Create initial polygon, which is just a quad between floor and ceiling
                WallPolygon poly = new WallPolygon();
                poly.Add(new Vector3D(vl.x, vl.y, fl));
                poly.Add(new Vector3D(vl.x, vl.y, cl));
                poly.Add(new Vector3D(vr.x, vr.y, cr));
                poly.Add(new Vector3D(vr.x, vr.y, fr));

                // Determine initial color
                int lightlevel = lightabsolute ? lightvalue : sd.Ceiling.brightnessbelow + lightvalue;

                //mxd. This calculates light with doom-style wall shading
                PixelColor wallbrightness = PixelColor.FromInt(mode.CalculateBrightness(lightlevel, Sidedef));
                PixelColor wallcolor      = PixelColor.Modulate(sd.Ceiling.colorbelow, wallbrightness);
                fogfactor  = CalculateFogFactor(lightlevel);
                poly.color = wallcolor.WithAlpha(255).ToInt();

                // Cut off the part above the 3D floor and below the 3D ceiling
                CropPoly(ref poly, extrafloor.Floor.plane, false);
                CropPoly(ref poly, extrafloor.Ceiling.plane, false);

                // Cut out pieces that overlap 3D floors in this sector
                List <WallPolygon> polygons = new List <WallPolygon> {
                    poly
                };
                bool translucent = (extrafloor.RenderAdditive || extrafloor.Alpha < 255);
                foreach (Effect3DFloor ef in sd.ExtraFloors)
                {
                    //mxd. Our poly should be clipped when our ond other extrafloors are both solid or both translucent,
                    // or when only our extrafloor is translucent.
                    // Our poly should not be clipped when our extrafloor is translucent and the other one isn't and both have renderinside setting.
                    bool othertranslucent = (ef.RenderAdditive || ef.Alpha < 255);
                    if (translucent && !othertranslucent && !ef.ClipSidedefs)
                    {
                        continue;
                    }
                    if (ef.ClipSidedefs == extrafloor.ClipSidedefs || ef.ClipSidedefs)
                    {
                        //TODO: find out why ef can be not updated at this point
                        //TODO: [this crashed on me once when performing auto-align on myriad of textures on BoA C1M0]
                        if (ef.Floor == null || ef.Ceiling == null)
                        {
                            ef.Update();
                        }

                        int num = polygons.Count;
                        for (int pi = 0; pi < num; pi++)
                        {
                            // Split by floor plane of 3D floor
                            WallPolygon p  = polygons[pi];
                            WallPolygon np = SplitPoly(ref p, ef.Ceiling.plane, true);

                            if (np.Count > 0)
                            {
                                // Split part below floor by the ceiling plane of 3D floor
                                // and keep only the part below the ceiling (front)
                                SplitPoly(ref np, ef.Floor.plane, true);

                                if (p.Count == 0)
                                {
                                    polygons[pi] = np;
                                }
                                else
                                {
                                    polygons[pi] = p;
                                    polygons.Add(np);
                                }
                            }
                            else
                            {
                                polygons[pi] = p;
                            }
                        }
                    }
                }

                // Process the polygon and create vertices
                if (polygons.Count > 0)
                {
                    List <WorldVertex> verts = CreatePolygonVertices(polygons, tp, sd, lightvalue, lightabsolute);
                    if (verts.Count > 2)
                    {
                        if (extrafloor.Sloped3dFloor)
                        {
                            this.RenderPass = RenderPass.Mask;                                                   //mxd
                        }
                        else if (extrafloor.RenderAdditive)
                        {
                            this.RenderPass = RenderPass.Additive;                                                         //mxd
                        }
                        else if ((extrafloor.Alpha < 255) || Texture.IsTranslucent)
                        {
                            this.RenderPass = RenderPass.Alpha;                                                                                 // [ZZ] translucent texture should trigger Alpha pass
                        }
                        else
                        {
                            this.RenderPass = RenderPass.Mask;
                        }

                        if (extrafloor.Alpha < 255)
                        {
                            // Apply alpha to vertices
                            byte alpha = (byte)General.Clamp(extrafloor.Alpha, 0, 255);
                            if (alpha < 255)
                            {
                                for (int i = 0; i < verts.Count; i++)
                                {
                                    WorldVertex v = verts[i];
                                    v.c      = PixelColor.FromInt(v.c).WithAlpha(alpha).ToInt();
                                    verts[i] = v;
                                }
                            }
                        }

                        base.SetVertices(verts);
                        return(true);
                    }
                }
            }

            base.SetVertices(null);             //mxd
            return(false);
        }
        // This builds the geometry. Returns false when no geometry created.
        public override bool Setup()
        {
            Vector2D vl, vr;

            // Left and right vertices for this sidedef
            if (Sidedef.IsFront)
            {
                vl = new Vector2D(Sidedef.Line.Start.Position.x, Sidedef.Line.Start.Position.y);
                vr = new Vector2D(Sidedef.Line.End.Position.x, Sidedef.Line.End.Position.y);
            }
            else
            {
                vl = new Vector2D(Sidedef.Line.End.Position.x, Sidedef.Line.End.Position.y);
                vr = new Vector2D(Sidedef.Line.Start.Position.x, Sidedef.Line.Start.Position.y);
            }

            // Load sector data
            SectorData sd  = Sector.GetSectorData();
            SectorData osd = mode.GetSectorData(Sidedef.Other.Sector);

            if (!osd.Updated)
            {
                osd.Update();
            }

            //mxd
            double vlzf  = sd.Floor.plane.GetZ(vl);
            double vrzf  = sd.Floor.plane.GetZ(vr);
            double ovlzf = osd.Floor.plane.GetZ(vl);
            double ovrzf = osd.Floor.plane.GetZ(vr);

            //mxd. Side is visible when our sector's floor is lower than the other's at any vertex
            if (!(vlzf < ovlzf || vrzf < ovrzf))
            {
                base.SetVertices(null);
                return(false);
            }

            // Apply sky hack?
            UpdateSkyRenderFlag();

            //mxd. lightfog flag support
            int  lightvalue;
            bool lightabsolute;

            GetLightValue(out lightvalue, out lightabsolute);

            Vector2D tscale = new Vector2D(Sidedef.Fields.GetValue("scalex_bottom", 1.0),
                                           Sidedef.Fields.GetValue("scaley_bottom", 1.0));
            Vector2D tscaleAbs = new Vector2D(Math.Abs(tscale.x), Math.Abs(tscale.y));
            Vector2D toffset   = new Vector2D(Sidedef.Fields.GetValue("offsetx_bottom", 0.0),
                                              Sidedef.Fields.GetValue("offsety_bottom", 0.0));

            // Texture given?
            if (Sidedef.LongLowTexture != MapSet.EmptyLongName)
            {
                // Load texture
                base.Texture = General.Map.Data.GetTextureImage(Sidedef.LongLowTexture);
                if (base.Texture == null || base.Texture is UnknownImage)
                {
                    base.Texture         = General.Map.Data.UnknownTexture3D;
                    setuponloadedtexture = Sidedef.LongLowTexture;
                }
                else if (!base.Texture.IsImageLoaded)
                {
                    setuponloadedtexture = Sidedef.LongLowTexture;
                }
            }
            else
            {
                // Use missing texture
                base.Texture         = General.Map.Data.MissingTexture3D;
                setuponloadedtexture = 0;
            }

            // Get texture scaled size. Round up, because that's apparently what GZDoom does
            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);

            tof = tof + toffset;

            // biwa. Also take the ForceWorldPanning MAPINFO entry into account
            if (General.Map.Config.ScaledTextureOffsets && (!base.Texture.WorldPanning && !General.Map.Data.MapInfo.ForceWorldPanning))
            {
                tof = tof / tscaleAbs;
                tof = tof * base.Texture.Scale;

                // If the texture gets replaced with a "hires" texture it adds more fuckery
                if (base.Texture is HiResImage)
                {
                    tof *= tscaleAbs;
                }

                // Round up, since that's apparently what GZDoom does. Not sure if this is the right place or if it also has to be done earlier
                tof = new Vector2D(Math.Ceiling(tof.x), Math.Ceiling(tof.y));
            }

            // 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 = (Sidedef.Other.Sector.FloorHeight == Sidedef.Sector.FloorHeight) ? 1.0 : 0.0;

            if (Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag))
            {
                if (Sidedef.Sector.CeilTexture == General.Map.Config.SkyFlatName && Sidedef.Other.Sector.CeilTexture == General.Map.Config.SkyFlatName)
                {
                    // 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;
                }
                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;
                }
            }
            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));

            // Apply texture offset
            tp.tlt += tof;
            tp.trb += 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);

            // 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));
            poly.Add(new Vector3D(vl.x, vl.y, sd.Ceiling.plane.GetZ(vl)));
            poly.Add(new Vector3D(vr.x, vr.y, sd.Ceiling.plane.GetZ(vr)));
            poly.Add(new Vector3D(vr.x, vr.y, vrzf));

            // Determine initial color
            int lightlevel = lightabsolute ? lightvalue : sd.Ceiling.brightnessbelow + lightvalue;

            //mxd. This calculates light with doom-style wall shading
            PixelColor wallbrightness = PixelColor.FromInt(mode.CalculateBrightness(lightlevel, Sidedef));
            PixelColor wallcolor      = PixelColor.Modulate(sd.Ceiling.colorbelow, wallbrightness);

            fogfactor  = CalculateFogFactor(lightlevel);
            poly.color = wallcolor.WithAlpha(255).ToInt();

            // Cut off the part above the other floor
            CropPoly(ref poly, osd.Floor.plane, false);

            //INFO: Makes sence only when ceiling plane is lower than floor plane. Also ZDoom clips ceiling instead here.
            if (ovlzf > osd.Ceiling.plane.GetZ(vl) || ovrzf > osd.Ceiling.plane.GetZ(vr))
            {
                CropPoly(ref poly, osd.Ceiling.plane, true);
            }

            // Cut out pieces that overlap 3D floors in this sector
            List <WallPolygon> polygons = new List <WallPolygon> {
                poly
            };

            ClipExtraFloors(polygons, sd.ExtraFloors, false);             //mxd

            if (polygons.Count > 0)
            {
                // Keep top and bottom planes for intersection testing
                Vector2D linecenter = Sidedef.Line.GetCenterPoint();                 //mxd. Our sector's ceiling can be lower than the other sector's floor!
                top    = (osd.Floor.plane.GetZ(linecenter) < sd.Ceiling.plane.GetZ(linecenter) ? osd.Floor.plane : sd.Ceiling.plane);
                bottom = sd.Floor.plane;

                // Process the polygon and create vertices
                List <WorldVertex> verts = CreatePolygonVertices(polygons, tp, sd, lightvalue, lightabsolute);
                if (verts.Count > 2)
                {
                    base.SetVertices(verts);
                    return(true);
                }
            }

            base.SetVertices(null);             //mxd
            return(false);
        }