Ejemplo n.º 1
0
        private static float GetAlignedThingZ(BaseVisualMode mode, Thing t, float targtthingheight)
        {
            ThingTypeInfo info = General.Map.Data.GetThingInfoEx(t.Type);

            if (info != null)
            {
                if (info.AbsoluteZ && info.Hangs)
                {
                    return(t.Position.z);                                             // Not sure what to do here...
                }
                if (info.AbsoluteZ)
                {
                    // Transform to floor-aligned position
                    SectorData nsd = mode.GetSectorData(t.Sector);
                    return(t.Position.z - nsd.Floor.plane.GetZ(t.Position) + t.Height);
                }

                if (info.Hangs)
                {
                    // Transform to floor-aligned position. Align top of target thing to the bottom of the hanging thing
                    SectorData nsd = mode.GetSectorData(t.Sector);
                    return((nsd.Ceiling.plane.GetZ(t.Position) - nsd.Floor.plane.GetZ(t.Position)) - t.Position.z - t.Height - targtthingheight);
                }
            }

            return(t.Position.z + t.Height);
        }
Ejemplo n.º 2
0
        // This builds the thing geometry. Returns false when nothing was created.
        public bool Setup()
        {
            // Find the sector in which the thing resides
            Thing.DetermineSector(mode.BlockMap);

            //mxd. If the thing is inside a sector, apply DECORATE/UDMF alpha/renderstyle overrides
            byte alpha = 255;

            if (Thing.Sector != null)
            {
                string renderstyle = info.RenderStyle;
                alpha = info.AlphaByte;

                if (General.Map.UDMF)
                {
                    if (Thing.IsFlagSet("translucent"))
                    {
                        renderstyle = "translucent";
                        alpha       = 64;
                    }
                    else if (Thing.IsFlagSet("invisible"))
                    {
                        renderstyle = "none";
                        alpha       = 0;
                    }
                    else if (Thing.Fields.ContainsKey("renderstyle"))
                    {
                        renderstyle = Thing.Fields.GetValue("renderstyle", renderstyle);
                    }

                    if ((renderstyle == "add" || renderstyle == "translucent" || renderstyle == "subtract" || renderstyle == "stencil") &&
                        Thing.Fields.ContainsKey("alpha"))
                    {
                        alpha = (byte)(General.Clamp(Thing.Fields.GetValue("alpha", info.Alpha), 0f, 1f) * 255);
                    }
                }
                else if (General.Map.HEXEN)
                {
                    if (Thing.IsFlagSet("2048"))
                    {
                        renderstyle = "translucent";
                        alpha       = 64;
                    }
                    else if (Thing.IsFlagSet("4096"))
                    {
                        renderstyle = "none";
                        alpha       = 0;
                    }
                }

                // Set appropriate RenderPass
                switch (renderstyle)
                {
                case "translucent":
                case "subtract":
                case "stencil":
                    RenderPass = RenderPass.Alpha;
                    break;

                case "add":
                    RenderPass = RenderPass.Additive;
                    break;

                case "none":
                    RenderPass = RenderPass.Mask;
                    alpha      = 0;
                    break;

                // Many render styles are not supported yet...
                default:
                    RenderPass = RenderPass.Mask;
                    alpha      = 255;
                    break;
                }
            }

            // Don't bother when alpha is unchanged, unless Additive RenderStyle is used
            if (RenderPass != RenderPass.Additive && alpha == 255)
            {
                RenderPass = RenderPass.Mask;
            }

            int sectorcolor = new PixelColor(alpha, 255, 255, 255).ToInt();

            fogfactor = 0f;             //mxd

            //mxd. Check thing size
            float thingradius = Thing.Size;         // Thing.Size has ThingRadius arg override applied

            thingheight = Thing.Height;             // Thing.Height has ThingHeight arg override applied

            if (thingradius < 0.1f || thingheight < 0.1f)
            {
                thingradius = FIXED_RADIUS;
                thingheight = FIXED_RADIUS;
                sizeless    = true;
            }
            else
            {
                sizeless = false;
            }

            Plane floor   = new Plane();           //mxd
            Plane ceiling = new Plane();           //mxd

            if (Thing.Sector != null)
            {
                SectorData sd = mode.GetSectorData(Thing.Sector);
                floor   = sd.Floor.plane;               //mxd
                ceiling = sd.Ceiling.plane;             //mxd

                if (!info.Bright)
                {
                    Vector3D    thingpos = new Vector3D(Thing.Position.x, Thing.Position.y, Thing.Position.z + sd.Floor.plane.GetZ(Thing.Position));
                    SectorLevel level    = sd.GetLevelAboveOrAt(thingpos);

                    //mxd. Let's use point on floor plane instead of Thing.Sector.FloorHeight;
                    if (nointeraction && level == null && sd.LightLevels.Count > 0)
                    {
                        level = sd.LightLevels[sd.LightLevels.Count - 1];
                    }

                    //mxd. Use the light level of the highest surface when a thing is above highest sector level.
                    if (level != null)
                    {
                        // TECH: In GZDoom, ceiling glow doesn't affect thing brightness
                        // Use sector brightness for color shading
                        int brightness = level.brightnessbelow;

                        // Level is glowing
                        if (level.affectedbyglow && level.type == SectorLevelType.Floor)
                        {
                            // Extrafloor glow doesn't affect thing brightness
                            if (level.sector == Thing.Sector)
                            {
                                float planez = level.plane.GetZ(thingpos);

                                // Get glow brightness
                                int         glowbrightness = sd.FloorGlow.Brightness / 2;
                                SectorLevel nexthigher     = sd.GetLevelAbove(new Vector3D(thingpos, planez));

                                // Interpolate thing brightness between glow and regular ones
                                if (nexthigher != null)
                                {
                                    float higherz = nexthigher.plane.GetZ(thingpos);
                                    float delta   = General.Clamp(1.0f - (thingpos.z - planez) / (higherz - planez), 0f, 1f);
                                    brightness = (int)((glowbrightness + level.sector.Brightness / 2) * delta + nexthigher.sector.Brightness * (1.0f - delta));
                                }
                            }
                        }
                        // Level below this one is glowing. Only possible for floor glow(?)
                        else if (level.type == SectorLevelType.Glow)
                        {
                            // Interpolate thing brightness between glow and regular ones
                            if (sd.Floor != null && sd.FloorGlow != null)
                            {
                                // Get glow brightness
                                float glowz  = level.plane.GetZ(thingpos);
                                float floorz = floor.GetZ(thingpos);
                                float delta  = General.Clamp((thingpos.z - floorz) / (glowz - floorz), 0f, 1f);

                                brightness = (int)((sd.FloorGlow.Brightness / 2 + sd.Floor.sector.Brightness / 2) * (1.0f - delta) + sd.Floor.sector.Brightness * delta);
                            }
                        }

                        PixelColor areabrightness = PixelColor.FromInt(mode.CalculateBrightness(brightness));
                        PixelColor areacolor      = PixelColor.Modulate(level.colorbelow, areabrightness);
                        sectorcolor = areacolor.WithAlpha(alpha).ToInt();

                        //mxd. Calculate fogfactor
                        fogfactor = VisualGeometry.CalculateFogFactor(level.sector.FogMode, brightness);
                    }
                }
                //TECH: even Bright Thing frames are affected by custom fade...
                else
                {
                    Vector3D    thingpos = new Vector3D(Thing.Position.x, Thing.Position.y, Thing.Position.z + sd.Floor.plane.GetZ(Thing.Position));
                    SectorLevel level    = sd.GetLevelAboveOrAt(thingpos);

                    if (level != null && level.sector.FogMode > SectorFogMode.CLASSIC)
                    {
                        //mxd. Calculate fogfactor
                        fogfactor = VisualGeometry.CalculateFogFactor(level.sector.FogMode, level.brightnessbelow);
                    }
                }
            }

            //mxd. Create verts for all sprite angles
            WorldVertex[][] allverts   = new WorldVertex[info.SpriteFrame.Length][];
            Vector2D[]      alloffsets = new Vector2D[info.SpriteFrame.Length];
            base.textures = new ImageData[info.SpriteFrame.Length];
            isloaded      = true;

            for (int i = 0; i < sprites.Length; i++)
            {
                Vector2D offsets = new Vector2D();

                // Check if the texture is loaded
                ImageData sprite = sprites[i];
                sprite.LoadImage();
                if (sprite.IsImageLoaded)
                {
                    base.textures[i] = sprite;

                    // Determine sprite size and offset
                    float        radius    = sprite.ScaledWidth * 0.5f;
                    float        height    = sprite.ScaledHeight;
                    ISpriteImage spriteimg = sprite as ISpriteImage;
                    if (spriteimg != null)
                    {
                        offsets.x = radius - spriteimg.OffsetX;
                        offsets.y = spriteimg.OffsetY - height;
                    }

                    // Scale by thing type/actor scale
                    // We do this after the offset x/y determination above, because that is entirely in sprite pixels space
                    radius    *= info.SpriteScale.Width;
                    height    *= info.SpriteScale.Height;
                    offsets.x *= info.SpriteScale.Width;
                    offsets.y *= info.SpriteScale.Height;

                    // Make vertices
                    WorldVertex[] verts = new WorldVertex[6];

                    //mxd. Sprite mirroring
                    float ul = (info.SpriteFrame[i].Mirror ? 1f : 0f);
                    float ur = (info.SpriteFrame[i].Mirror ? 0f : 1f);

                    if (sizeless)                    //mxd
                    {
                        float hh = height / 2;
                        verts[0] = new WorldVertex(-radius + offsets.x, 0.0f, offsets.y - hh, sectorcolor, ul, 1.0f);
                        verts[1] = new WorldVertex(-radius + offsets.x, 0.0f, hh + offsets.y, sectorcolor, ul, 0.0f);
                        verts[2] = new WorldVertex(+radius + offsets.x, 0.0f, hh + offsets.y, sectorcolor, ur, 0.0f);
                        verts[3] = verts[0];
                        verts[4] = verts[2];
                        verts[5] = new WorldVertex(+radius + offsets.x, 0.0f, offsets.y - hh, sectorcolor, ur, 1.0f);
                    }
                    else
                    {
                        verts[0] = new WorldVertex(-radius + offsets.x, 0.0f, offsets.y, sectorcolor, ul, 1.0f);
                        verts[1] = new WorldVertex(-radius + offsets.x, 0.0f, height + offsets.y, sectorcolor, ul, 0.0f);
                        verts[2] = new WorldVertex(+radius + offsets.x, 0.0f, height + offsets.y, sectorcolor, ur, 0.0f);
                        verts[3] = verts[0];
                        verts[4] = verts[2];
                        verts[5] = new WorldVertex(+radius + offsets.x, 0.0f, offsets.y, sectorcolor, ur, 1.0f);
                    }
                    allverts[i] = verts;
                }
                else
                {
                    isloaded         = false;
                    base.textures[i] = General.Map.Data.Hourglass3D;

                    // Determine sprite size
                    float radius = Math.Min(thingradius, thingheight / 2f);
                    float height = Math.Min(thingradius * 2f, thingheight);

                    //mxd. Determine sprite offsets
                    offsets.x = radius;
                    offsets.y = height / 2;

                    // Make vertices
                    WorldVertex[] verts = new WorldVertex[6];
                    verts[0]    = new WorldVertex(-radius, 0.0f, 0.0f, sectorcolor, 0.0f, 1.0f);
                    verts[1]    = new WorldVertex(-radius, 0.0f, height, sectorcolor, 0.0f, 0.0f);
                    verts[2]    = new WorldVertex(+radius, 0.0f, height, sectorcolor, 1.0f, 0.0f);
                    verts[3]    = verts[0];
                    verts[4]    = verts[2];
                    verts[5]    = new WorldVertex(+radius, 0.0f, 0.0f, sectorcolor, 1.0f, 1.0f);
                    allverts[i] = verts;
                }

                //mxd. Store offsets
                alloffsets[i] = offsets;
            }

            //mxd
            SetVertices(allverts, alloffsets /*, floor, ceiling*/);

            // Determine position
            Vector3D pos = Thing.Position;

            if (Thing.Type == 9501)
            {
                if (Thing.Sector != null)                //mxd
                {
                    // This is a special thing that needs special positioning
                    SectorData sd = mode.GetSectorData(Thing.Sector);
                    pos.z = sd.Ceiling.sector.CeilHeight + Thing.Position.z;
                }
            }
            else if (Thing.Type == 9500)
            {
                if (Thing.Sector != null)                //mxd
                {
                    // This is a special thing that needs special positioning
                    SectorData sd = mode.GetSectorData(Thing.Sector);
                    pos.z = sd.Floor.sector.FloorHeight + Thing.Position.z;
                }
            }
            else if (info.AbsoluteZ)
            {
                // Absolute Z position
                pos.z = Thing.Position.z;
            }
            else if (info.Hangs)
            {
                // Hang from ceiling
                if (Thing.Sector != null)
                {
                    SectorData sd   = mode.GetSectorData(Thing.Sector);
                    float      maxz = sd.Ceiling.plane.GetZ(Thing.Position) - info.Height;
                    pos.z = maxz;

                    if (Thing.Position.z > 0 || nointeraction)
                    {
                        pos.z -= Thing.Position.z;
                    }

                    // Check if below floor
                    if (!nointeraction)
                    {
                        float minz = sd.Floor.plane.GetZ(Thing.Position);
                        if (pos.z < minz)
                        {
                            pos.z = Math.Min(minz, maxz);
                        }
                    }
                }
            }
            else
            {
                // Stand on floor
                if (Thing.Sector != null)
                {
                    SectorData sd   = mode.GetSectorData(Thing.Sector);
                    float      minz = sd.Floor.plane.GetZ(Thing.Position);
                    pos.z = minz;

                    if (Thing.Position.z > 0 || nointeraction)
                    {
                        pos.z += Thing.Position.z;
                    }

                    // Check if above ceiling
                    if (!nointeraction)
                    {
                        float maxz = sd.Ceiling.plane.GetZ(Thing.Position) - info.Height;
                        if (pos.z > maxz)
                        {
                            pos.z = Math.Max(minz, maxz);
                        }
                    }
                }
            }

            // Apply settings
            SetPosition(pos);
            SetCageColor(Thing.Color);

            // Keep info for object picking
            cageradius2 = thingradius * Angle2D.SQRT2;
            cageradius2 = cageradius2 * cageradius2;
            pos2d       = pos;

            if (sizeless)            //mxd
            {
                boxp1 = new Vector3D(pos.x - thingradius, pos.y - thingradius, pos.z - thingradius / 2);
                boxp2 = new Vector3D(pos.x + thingradius, pos.y + thingradius, pos.z + thingradius / 2);
            }
            else
            {
                boxp1 = new Vector3D(pos.x - thingradius, pos.y - thingradius, pos.z);
                boxp2 = new Vector3D(pos.x + thingradius, pos.y + thingradius, pos.z + thingheight);
            }

            // Done
            changed = false;
            return(true);
        }
Ejemplo n.º 3
0
 // This retreives the sector data for this sector
 public SectorData GetSectorData()
 {
     return(mode.GetSectorData(this.Sector));
 }
Ejemplo n.º 4
0
        // This builds the thing geometry. Returns false when nothing was created.
        public bool Setup()
        {
            // Find the sector in which the thing resides
            Thing.DetermineSector(mode.BlockMap);

            //mxd. If the thing is inside a sector, apply DECORATE/UDMF alpha/renderstyle overrides
            byte alpha = 255;

            if (Thing.Sector != null)
            {
                string renderstyle = info.RenderStyle.ToLowerInvariant();
                alpha = info.AlphaByte;

                if (General.Map.UDMF)
                {
                    if (Thing.IsFlagSet("translucent"))
                    {
                        renderstyle = "translucent";
                        alpha       = 64;
                    }
                    else if (Thing.IsFlagSet("invisible"))
                    {
                        renderstyle = "none";
                        alpha       = 0;
                    }
                    else if (Thing.Fields.ContainsKey("renderstyle"))
                    {
                        renderstyle = Thing.Fields.GetValue("renderstyle", renderstyle).ToLowerInvariant();
                    }

                    if ((renderstyle == "add" || renderstyle == "translucent" || renderstyle == "subtract" || renderstyle == "translucentstencil") &&
                        Thing.Fields.ContainsKey("alpha"))
                    {
                        alpha = (byte)(General.Clamp(Thing.Fields.GetValue("alpha", info.Alpha), 0.0, 1.0) * 255.0);
                    }
                    else if (renderstyle == "soultrans")
                    {
                        // Lost Soul trasparency is controlled by a CVAR (see https://zdoom.org/wiki/CVARs:Display#transsouls), let's use the default 0.75 here
                        alpha = 192;
                    }
                    else if (renderstyle == "shadow")
                    {
                        alpha        = 76;                  // about 0.3
                        stencilColor = PixelColor.FromInt(PixelColor.INT_BLACK);
                    }

                    if (renderstyle.EndsWith("stencil"))
                    {
                        stencilColor   = PixelColor.FromInt(UniFields.GetInteger(Thing.Fields, "fillcolor", 0));
                        stencilColor.a = 255; // 0xFF alpha means nothing was read. 0x00 alpha means there was a valid fillcolor.
                    }
                    else if (renderstyle != "shadow")
                    {
                        stencilColor.a = 0;
                    }
                }
                else if (General.Map.HEXEN)
                {
                    if (Thing.IsFlagSet("2048"))
                    {
                        renderstyle = "translucent";
                        alpha       = 64;
                    }
                    else if (Thing.IsFlagSet("4096"))
                    {
                        renderstyle = "none";
                        alpha       = 0;
                    }
                }

                // Set appropriate RenderPass
                switch (renderstyle)
                {
                case "translucent":
                case "subtract":
                case "soultrans":
                case "translucentstencil":
                case "shadow":
                    RenderPass = RenderPass.Alpha;
                    break;

                case "add":
                case "addstencil":
                    RenderPass = RenderPass.Additive;
                    break;

                case "none":
                    RenderPass = RenderPass.Mask;
                    alpha      = 0;
                    break;

                // Many render styles are not supported yet...
                default:
                    RenderPass = RenderPass.Mask;
                    alpha      = 255;
                    break;
                }
            }

            int sectorcolor = new PixelColor(alpha, 255, 255, 255).ToInt();

            fogfactor = 0f;             //mxd

            //mxd. Check thing size
            float thingradius = Thing.Size;         // Thing.Size has ThingRadius arg override applied

            thingheight = Thing.Height;             // Thing.Height has ThingHeight arg override applied

            if (thingradius < 0.1f || thingheight < 0.1f)
            {
                thingradius = FIXED_RADIUS;
                thingheight = FIXED_RADIUS;
                sizeless    = true;
            }
            else
            {
                sizeless = false;
            }

            if (Thing.Sector != null)
            {
                SectorData sd    = mode.GetSectorData(Thing.Sector);
                Plane      floor = sd.Floor.plane;            //mxd

                if (!info.Bright)
                {
                    Vector3D    thingpos = new Vector3D(Thing.Position.x, Thing.Position.y, Thing.Position.z + sd.Floor.plane.GetZ(Thing.Position));
                    SectorLevel level    = sd.GetLevelAboveOrAt(thingpos);

                    //mxd. Let's use point on floor plane instead of Thing.Sector.FloorHeight;
                    if (nointeraction && level == null && sd.LightLevels.Count > 0)
                    {
                        level = sd.LightLevels[sd.LightLevels.Count - 1];
                    }

                    //mxd. Use the light level of the highest surface when a thing is above highest sector level.
                    if (level != null)
                    {
                        // TECH: In GZDoom, ceiling glow doesn't affect thing brightness
                        // Use sector brightness for color shading
                        int brightness = level.brightnessbelow;

                        //mxd. Apply lightfloor value
                        // According to Graf, this is incorrect behaviour...
                        // TECH: In (G)ZDoom, this is ignored when ceiling texture is sky or a thing is below a 3D floor
                        // It's probably more involved than this, but for now let's do it only when there are no 3d floors in Thing.Sector...

                        /*if(General.Map.UDMF && sd.LightLevels.Count == 2 && Thing.Sector.CeilTexture != General.Map.Config.SkyFlatName)
                         * {
                         *      if(sd.Sector.Fields.GetValue("lightfloorabsolute", false))
                         *              brightness = UniFields.GetInteger(sd.Sector.Fields, "lightfloor");
                         *      else
                         *              brightness += UniFields.GetInteger(sd.Sector.Fields, "lightfloor");
                         * }*/

                        // Level is glowing
                        if (level.affectedbyglow && level.type == SectorLevelType.Floor)
                        {
                            // Extrafloor glow doesn't affect thing brightness
                            if (level.sector == Thing.Sector)
                            {
                                double planez = level.plane.GetZ(thingpos);

                                // Get glow brightness
                                int         glowbrightness = sd.FloorGlow.Brightness / 2;
                                SectorLevel nexthigher     = sd.GetLevelAbove(new Vector3D(thingpos, planez));

                                // Interpolate thing brightness between glow and regular ones
                                if (nexthigher != null)
                                {
                                    double higherz = nexthigher.plane.GetZ(thingpos);
                                    double delta   = General.Clamp(1.0f - (thingpos.z - planez) / (higherz - planez), 0f, 1f);
                                    brightness = (int)((glowbrightness + level.sector.Brightness / 2) * delta + nexthigher.sector.Brightness * (1.0f - delta));
                                }
                            }
                        }
                        // Level below this one is glowing. Only possible for floor glow(?)
                        else if (level.type == SectorLevelType.Glow)
                        {
                            // Interpolate thing brightness between glow and regular ones
                            if (sd.Floor != null && sd.FloorGlow != null)
                            {
                                // Get glow brightness
                                double glowz  = level.plane.GetZ(thingpos);
                                double floorz = floor.GetZ(thingpos);
                                double delta  = General.Clamp((thingpos.z - floorz) / (glowz - floorz), 0f, 1f);

                                brightness = (int)((sd.FloorGlow.Brightness / 2 + sd.Floor.sector.Brightness / 2) * (1.0f - delta) + sd.Floor.sector.Brightness * delta);
                            }
                        }

                        PixelColor areabrightness = PixelColor.FromInt(mode.CalculateBrightness(brightness));
                        PixelColor areacolor      = PixelColor.Modulate(level.colorbelow, areabrightness);

                        // [ZZ] if sector is using Doom64 lighting, apply thing color here.
                        sectorcolor = PixelColor.Modulate(sd.ColorSprites, areacolor).WithAlpha(alpha).ToInt();

                        //mxd. Calculate fogfactor
                        fogfactor = VisualGeometry.CalculateFogFactor(level.sector, brightness);
                    }
                }
                //TECH: even Bright Thing frames are affected by custom fade...
                else
                {
                    Vector3D    thingpos = new Vector3D(Thing.Position.x, Thing.Position.y, Thing.Position.z + sd.Floor.plane.GetZ(Thing.Position));
                    SectorLevel level    = sd.GetLevelAboveOrAt(thingpos);

                    if (level != null && level.sector.FogMode > SectorFogMode.CLASSIC)
                    {
                        //mxd. Calculate fogfactor
                        fogfactor = VisualGeometry.CalculateFogFactor(level.sector, level.brightnessbelow);
                    }
                }
            }

            //mxd. Create verts for all sprite angles
            WorldVertex[][] allverts   = new WorldVertex[info.SpriteFrame.Length][];
            Vector2D[]      alloffsets = new Vector2D[info.SpriteFrame.Length];
            base.textures = new ImageData[info.SpriteFrame.Length];
            isloaded      = true;

            for (int i = 0; i < sprites.Length; i++)
            {
                Vector2D offsets = new Vector2D();

                // Check if the texture is loaded
                ImageData sprite = sprites[i];
                if (!sprite.IsImageLoaded && !sprite.LoadFailed)
                {
                    sprite.LoadImageNow();
                }
                if (sprite.IsImageLoaded)
                {
                    base.textures[i] = sprite;

                    // Determine sprite size and offset
                    float        radius    = sprite.ScaledWidth * 0.5f;
                    float        height    = sprite.ScaledHeight;
                    ISpriteImage spriteimg = sprite as ISpriteImage;
                    if (spriteimg != null)
                    {
                        offsets.x = radius - spriteimg.OffsetX;
                        offsets.y = spriteimg.OffsetY - height;
                    }

                    // Scale by thing type/actor scale
                    // We do this after the offset x/y determination above, because that is entirely in sprite pixels space
                    radius    *= info.SpriteScale.Width;
                    height    *= info.SpriteScale.Height;
                    offsets.x *= info.SpriteScale.Width;
                    offsets.y *= info.SpriteScale.Height;

                    // Make vertices
                    WorldVertex[] verts = new WorldVertex[6];

                    //mxd. Sprite mirroring
                    float ul = (info.SpriteFrame[i].Mirror ? 1f : 0f);
                    float ur = (info.SpriteFrame[i].Mirror ? 0f : 1f);

                    if (sizeless)                    //mxd
                    {
                        float hh = height / 2;
                        verts[0] = new WorldVertex((float)(-radius + offsets.x), 0.0f, (float)(offsets.y - hh), sectorcolor, ul, 1.0f);
                        verts[1] = new WorldVertex((float)(-radius + offsets.x), 0.0f, (float)(hh + offsets.y), sectorcolor, ul, 0.0f);
                        verts[2] = new WorldVertex((float)(+radius + offsets.x), 0.0f, (float)(hh + offsets.y), sectorcolor, ur, 0.0f);
                        verts[3] = verts[0];
                        verts[4] = verts[2];
                        verts[5] = new WorldVertex((float)(+radius + offsets.x), 0.0f, (float)(offsets.y - hh), sectorcolor, ur, 1.0f);
                    }
                    else
                    {
                        verts[0] = new WorldVertex((float)(-radius + offsets.x), 0.0f, (float)offsets.y, sectorcolor, ul, 1.0f);
                        verts[1] = new WorldVertex((float)(-radius + offsets.x), 0.0f, (float)(height + offsets.y), sectorcolor, ul, 0.0f);
                        verts[2] = new WorldVertex((float)(+radius + offsets.x), 0.0f, (float)(height + offsets.y), sectorcolor, ur, 0.0f);
                        verts[3] = verts[0];
                        verts[4] = verts[2];
                        verts[5] = new WorldVertex((float)(+radius + offsets.x), 0.0f, (float)offsets.y, sectorcolor, ur, 1.0f);
                    }
                    allverts[i] = verts;
                }
                else
                {
                    isloaded         = false;
                    base.textures[i] = sprite;

                    // Determine sprite size
                    float radius = Math.Min(thingradius, thingheight / 2f);
                    float height = Math.Min(thingradius * 2f, thingheight);

                    //mxd. Determine sprite offsets
                    offsets.x = radius;
                    offsets.y = height / 2;

                    // Make vertices
                    WorldVertex[] verts = new WorldVertex[6];
                    verts[0]    = new WorldVertex(-radius, 0.0f, 0.0f, sectorcolor, 0.0f, 1.0f);
                    verts[1]    = new WorldVertex(-radius, 0.0f, height, sectorcolor, 0.0f, 0.0f);
                    verts[2]    = new WorldVertex(+radius, 0.0f, height, sectorcolor, 1.0f, 0.0f);
                    verts[3]    = verts[0];
                    verts[4]    = verts[2];
                    verts[5]    = new WorldVertex(+radius, 0.0f, 0.0f, sectorcolor, 1.0f, 1.0f);
                    allverts[i] = verts;
                }

                //mxd. Store offsets
                alloffsets[i] = offsets;
            }

            //mxd
            SetVertices(allverts, alloffsets /*, floor, ceiling*/);

            // Determine position
            Vector3D pos = Thing.Position;

            if (Thing.Type == 9501)
            {
                if (Thing.Sector != null)                //mxd
                {
                    // This is a special thing that needs special positioning
                    SectorData sd = mode.GetSectorData(Thing.Sector);
                    pos.z = sd.Ceiling.sector.CeilHeight + Thing.Position.z;
                }
            }
            else if (Thing.Type == 9500)
            {
                if (Thing.Sector != null)                //mxd
                {
                    // This is a special thing that needs special positioning
                    SectorData sd = mode.GetSectorData(Thing.Sector);
                    pos.z = sd.Floor.sector.FloorHeight + Thing.Position.z;
                }
            }
            else if (info.AbsoluteZ)
            {
                // Absolute Z position
                pos.z = Thing.Position.z;
            }
            else if (info.Hangs)
            {
                // Hang from ceiling
                if (Thing.Sector != null)
                {
                    SectorData sd   = mode.GetSectorData(Thing.Sector);
                    double     maxz = sd.Ceiling.plane.GetZ(Thing.Position) - info.Height;
                    pos.z = maxz;

                    if (Thing.Position.z > 0 || nointeraction)
                    {
                        pos.z -= Thing.Position.z;
                    }

                    // Check if below floor
                    if (!nointeraction)
                    {
                        double minz = sd.Floor.plane.GetZ(Thing.Position);
                        if (pos.z < minz)
                        {
                            pos.z = Math.Min(minz, maxz);
                        }
                    }
                }
            }
            else
            {
                // Stand on floor
                if (Thing.Sector != null)
                {
                    SectorData sd   = mode.GetSectorData(Thing.Sector);
                    double     minz = sd.Floor.plane.GetZ(Thing.Position);
                    pos.z = minz;

                    if (Thing.Position.z > 0 || nointeraction)
                    {
                        pos.z += Thing.Position.z;
                    }

                    // Check if above ceiling
                    if (!nointeraction)
                    {
                        double maxz = sd.Ceiling.plane.GetZ(Thing.Position) - info.Height;
                        if (pos.z > maxz)
                        {
                            pos.z = Math.Max(minz, maxz);
                        }
                    }
                }
            }

            // Apply settings
            SetPosition(pos);
            SetCageColor(Thing.Color);

            // Keep info for object picking
            cageradius2 = thingradius * Angle2D.SQRT2;
            cageradius2 = cageradius2 * cageradius2;
            pos2d       = pos;

            if (sizeless)            //mxd
            {
                boxp1 = new Vector3D(pos.x - thingradius, pos.y - thingradius, pos.z - thingradius / 2);
                boxp2 = new Vector3D(pos.x + thingradius, pos.y + thingradius, pos.z + thingradius / 2);
            }
            else
            {
                boxp1 = new Vector3D(pos.x - thingradius, pos.y - thingradius, pos.z);
                boxp2 = new Vector3D(pos.x + thingradius, pos.y + thingradius, pos.z + thingheight);
            }

            // Done
            changed = false;
            return(true);
        }
        // Texture offset change
        public virtual void OnChangeTextureOffset(int horizontal, int vertical, bool doSurfaceAngleCorrection)
        {
            if (horizontal == 0 && vertical == 0)
            {
                return;                                              //mxd
            }
            //mxd
            if (!General.Map.UDMF)
            {
                General.Interface.DisplayStatus(StatusType.Warning, "Floor/ceiling texture offsets cannot be changed in this map format!");
                return;
            }

            if ((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
            {
                undoticket = mode.CreateUndo("Change texture offsets");
            }

            //mxd
            if (doSurfaceAngleCorrection)
            {
                Point p     = new Point(horizontal, vertical);
                float angle = Angle2D.RadToDeg(General.Map.VisualCamera.AngleXY);
                if (GeometryType == VisualGeometryType.CEILING)
                {
                    angle += level.sector.Fields.GetValue("rotationceiling", 0f);
                }
                else
                {
                    angle += level.sector.Fields.GetValue("rotationfloor", 0f);
                }

                angle = General.ClampAngle(angle);

                if (angle > 315 || angle < 46)
                {
                    //already correct
                }
                else if (angle > 225)
                {
                    vertical   = p.X;
                    horizontal = -p.Y;
                }
                else if (angle > 135)
                {
                    horizontal = -p.X;
                    vertical   = -p.Y;
                }
                else
                {
                    vertical   = -p.X;
                    horizontal = p.Y;
                }
            }

            // Apply offsets
            MoveTextureOffset(new Point(-horizontal, -vertical));

            // Update sector geometry
            Sector s = GetControlSector();

            if (s.Index != Sector.Sector.Index)
            {
                s.UpdateNeeded = true;
                s.UpdateCache();
                mode.GetSectorData(s).Update();
                BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(s);
                vs.UpdateSectorGeometry(false);
                vs.Rebuild();
            }

            Sector.Sector.UpdateNeeded = true;
            Sector.Sector.UpdateCache();
            Sector.UpdateSectorGeometry(false);
            Sector.Rebuild();
        }