//mxd private int GetLevelColor(SectorLevel src) { PixelColor brightness = PixelColor.FromInt(mode.CalculateBrightness(src.brightnessbelow)); PixelColor color = PixelColor.Modulate(src.colorbelow, brightness); return(color.WithAlpha(255).ToInt()); }
//mxd. Compare light properties public bool LightPropertiesMatch(SectorLevel other) { return(this.type == other.type && this.lighttype == other.lighttype && this.alpha == other.alpha && this.splitsides == other.splitsides && this.color == other.color && this.brightnessbelow == other.brightnessbelow && this.colorbelow.ToInt() == other.colorbelow.ToInt() && this.disablelighting == other.disablelighting && this.restrictlighting == other.restrictlighting && this.resetlighting == other.resetlighting); }
public void Update() { // Create ceiling glow effect? data.CeilingGlow = GetGlowData(false); if (data.CeilingGlow != null) { // Create ceiling level? if (ceillevel == null) { ceillevel = new SectorLevel(data.Ceiling) { type = SectorLevelType.Glow, disablelighting = true }; data.AddSectorLevel(ceillevel); } // Update ceiling level ceillevel.brightnessbelow = -1; // We need this plane for clipping only, ceillevel.color = 0; // so we need to reset all shading and coloring ceillevel.plane = data.Ceiling.plane; ceillevel.plane.Offset -= data.CeilingGlow.Height; data.CeilingGlowPlane = ceillevel.plane; } // Create floor glow effect? data.FloorGlow = GetGlowData(true); if (data.FloorGlow != null) { // Create floor level? if (floorlevel == null) { floorlevel = new SectorLevel(data.Floor) { type = SectorLevelType.Glow, disablelighting = true }; data.AddSectorLevel(floorlevel); } // Update floor level floorlevel.plane = data.Floor.plane.GetInverted(); floorlevel.plane.Offset += data.FloorGlow.Height; if (floorlevel.plane.Offset < data.Ceiling.plane.Offset) { floorlevel.brightnessbelow = -1; // We need this plane for clipping only, floorlevel.color = 0; // so we need to reset all shading and coloring floorlevel.colorbelow = new PixelColor(0, 0, 0, 0); } else { // If glow plane is above real ceiling, apply ceiling colouring floorlevel.brightnessbelow = data.Ceiling.brightnessbelow; floorlevel.color = data.Ceiling.color; floorlevel.colorbelow = data.Ceiling.colorbelow; } data.FloorGlowPlane = floorlevel.plane; } }
// Copy properties public void CopyProperties(SectorLevel target) { target.sector = this.sector; target.type = this.type; target.lighttype = this.lighttype; //mxd target.plane = this.plane; target.alpha = this.alpha; target.color = this.color; target.brightnessbelow = this.brightnessbelow; target.colorbelow = this.colorbelow; target.affectedbyglow = this.affectedbyglow; //mxd target.disablelighting = this.disablelighting; //mxd target.restrictlighting = this.restrictlighting; //mxd target.resetlighting = this.resetlighting; //mxd target.splitsides = this.splitsides; //mxd }
// This returns the level below the given point public SectorLevel GetLevelBelow(Vector3D pos) { SectorLevel found = null; float dist = float.MaxValue; foreach (SectorLevel l in lightlevels) { float d = pos.z - l.plane.GetZ(pos); if ((d > 0.0f) && (d < dist)) { dist = d; found = l; } } return(found); }
// This returns the level above the given point public SectorLevel GetLevelAbove(Vector3D pos) { SectorLevel found = null; double dist = double.MaxValue; foreach (SectorLevel l in lightlevels) { double d = l.plane.GetZ(pos) - pos.z; if ((d > 0.0f) && (d < dist)) { dist = d; found = l; } } return(found); }
//mxd. This returns the level above the given point or the level given point is located on public SectorLevel GetLevelAboveOrAt(Vector3D pos) { SectorLevel found = null; float dist = float.MaxValue; foreach (SectorLevel l in lightlevels) { float d = l.plane.GetZ(pos) - pos.z; if ((d >= 0.0f) && (d < dist)) { dist = d; found = l; } } return(found); }
// This returns the level above the given point public SectorLevel GetCeilingAbove(Vector3D pos) { SectorLevel found = null; float dist = float.MaxValue; foreach (SectorLevel l in lightlevels) { if (l.type == SectorLevelType.Ceiling) { float d = l.plane.GetZ(pos) - pos.z; if ((d > 0.0f) && (d < dist)) { dist = d; found = l; } } } return(found); }
// This returns the floor below the given point public SectorLevel GetFloorBelow(Vector3D pos) { SectorLevel found = null; double dist = double.MaxValue; foreach (SectorLevel l in lightlevels) { if (l.type == SectorLevelType.Floor) { double d = pos.z - l.plane.GetZ(pos); if ((d > 0.0f) && (d < dist)) { dist = d; found = l; } } } return(found); }
//mxd private int GetLevelColor(SectorLevel src, SectorLevel target) { PixelColor brightness; if (lightfloorabsolute && target == floor) { brightness = PixelColor.FromInt(mode.CalculateBrightness(lightfloor)); } else if (lightceilingabsolute && target == ceiling) { brightness = PixelColor.FromInt(mode.CalculateBrightness(lightceiling)); } else { brightness = PixelColor.FromInt(mode.CalculateBrightness(src.brightnessbelow)); } PixelColor color = PixelColor.Modulate(src.colorbelow, brightness); return(color.WithAlpha(255).ToInt()); }
// This makes sure we are updated with the source linedef information public override void Update() { SectorData sd = data.Mode.GetSectorData(linedef.Front.Sector); if (!sd.Updated) { sd.Update(); } sd.AddUpdateSector(data.Sector, false); // Create top level? if (toplevel == null) { toplevel = new SectorLevel(sd.Ceiling); data.AddSectorLevel(toplevel); } // Update top level sd.Ceiling.CopyProperties(toplevel); toplevel.lighttype = (LightLevelType)General.Clamp(linedef.Args[1], 0, 2); //mxd toplevel.type = SectorLevelType.Light; //mxd. Create bottom level? if (toplevel.lighttype == LightLevelType.TYPE1) { // Create bottom level? Skip this step if there's a different light level between toplevel and bottomlevel if (bottomlevel == null) { bottomlevel = new SectorLevel(data.Ceiling); data.AddSectorLevel(bottomlevel); } // Update bottom level data.Ceiling.CopyProperties(bottomlevel); bottomlevel.type = SectorLevelType.Light; bottomlevel.lighttype = LightLevelType.TYPE1_BOTTOM; bottomlevel.plane = sd.Floor.plane.GetInverted(); } }
// Constructor public SectorData(BaseVisualMode mode, Sector s) { // Initialize this.mode = mode; this.sector = s; this.updated = false; this.floorchanged = false; this.ceilingchanged = false; this.lightlevels = new List <SectorLevel>(2); this.extrafloors = new List <Effect3DFloor>(1); this.alleffects = new List <SectorEffect>(1); this.updatesectors = new Dictionary <Sector, bool>(2); this.floor = new SectorLevel(sector, SectorLevelType.Floor); this.floorbase = new SectorLevel(sector, SectorLevelType.Floor); //mxd this.ceiling = new SectorLevel(sector, SectorLevelType.Ceiling); this.ceilingbase = new SectorLevel(sector, SectorLevelType.Ceiling); //mxd this.glowingflateffect = new EffectGlowingFlat(this); //mxd // Add ceiling and floor lightlevels.Add(floor); lightlevels.Add(ceiling); BasicSetup(); }
public virtual bool Setup(SectorLevel level, Effect3DFloor extrafloor) { this.level = level; this.extrafloor = extrafloor; return(false); }
internal static float GetLowerThingZ(BaseVisualMode mode, SectorData sd, VisualThing thing) { Vector3D pos = thing.Thing.Position; float thingheight = thing.Thing.Height; bool absolute = thing.Info.AbsoluteZ; bool hangs = thing.Info.Hangs; if (absolute && hangs) { General.Interface.DisplayStatus(StatusType.Warning, "Sorry, can't have both 'absolute' and 'hangs' flags..."); return(pos.z); } // Get things, which bounding boxes intersect with target thing IEnumerable <Thing> intersectingthings = GetIntersectingThings(mode, thing.Thing); float fz = (absolute ? 0 : sd.Floor.plane.GetZ(pos)); float cz = sd.Ceiling.plane.GetZ(pos); if (hangs) { // Transform to floor-aligned position Vector3D floorpos = new Vector3D(pos, (cz - fz) - pos.z - thingheight); float lowertingz = GetNextLowerThingZ(mode, intersectingthings, floorpos.z, thingheight); float lowerfloorz = float.MaxValue; // Do it only when there are extrafloors if (sd.LightLevels.Count > 2) { // Unlike sd.ExtraFloors, these are sorted by height for (int i = sd.LightLevels.Count - 1; i > -1; i--) { SectorLevel level = sd.LightLevels[i]; if (level.type == SectorLevelType.Light || level.type == SectorLevelType.Glow) { continue; // Skip lights and glows } float z = level.plane.GetZ(floorpos) - fz; if (level.type == SectorLevelType.Ceiling) { z -= thingheight; } if (z < floorpos.z) { lowerfloorz = z; break; } } } float floorz = cz - fz; // Floor height when counted from ceiling if (lowerfloorz != float.MaxValue && lowertingz != float.MinValue) { // Transform back to ceiling-aligned position return(cz - fz - Math.Min(Math.Max(lowerfloorz, lowertingz), floorz) - thingheight); } if (lowerfloorz != float.MaxValue) { // Transform back to ceiling-aligned position return(cz - fz - Math.Min(lowerfloorz, floorz) - thingheight); } if (lowertingz != float.MinValue) { // Transform back to ceiling-aligned position return(cz - fz - Math.Min(lowertingz, floorz) - thingheight); } return(floorz - thingheight); // Align to real floor } else { float lowertingz = GetNextLowerThingZ(mode, intersectingthings, (absolute ? pos.z - fz : pos.z), thingheight); float lowerfloorz = float.MaxValue; // Do it only when there are extrafloors if (sd.LightLevels.Count > 2) { // Unlike sd.ExtraFloors, these are sorted by height for (int i = sd.LightLevels.Count - 1; i > -1; i--) { SectorLevel level = sd.LightLevels[i]; if (level.type == SectorLevelType.Light || level.type == SectorLevelType.Glow) { continue; // Skip lights and glows } float z = level.plane.GetZ(pos) - fz; if (level.type == SectorLevelType.Ceiling) { z -= thingheight; } if (z < pos.z) { lowerfloorz = z; break; } } } float floorz = sd.Floor.plane.GetZ(pos); // Floor-aligned relative target thing z float floorpos = 0; if (lowerfloorz != float.MaxValue && lowertingz != float.MinValue) { floorpos = Math.Max(Math.Max(lowerfloorz, lowertingz), floorz); } if (lowerfloorz != float.MaxValue) { floorpos = Math.Max(lowerfloorz, floorz); } if (lowertingz != float.MinValue) { floorpos = Math.Max(lowertingz, floorz); } return(absolute ? floorpos + floorz : floorpos); // Convert to absolute position if necessary } }
// Copy constructor public SectorLevel(SectorLevel source) { source.CopyProperties(this); }
// 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); }
// 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); }
// This builds the geometry. Returns false when no geometry created. public override bool Setup(SectorLevel level, Effect3DFloor extrafloor) { return(Setup(level, extrafloor, innerside)); }
// When no geometry has been changed and no effects have been added or removed, // you can call this again to update existing effects. The effects will update // the existing SectorLevels to match with any changes. public void Update() { if (isupdating || updated) { return; } isupdating = true; // Set floor/ceiling to their original setup BasicSetup(); // Update all effects foreach (SectorEffect e in alleffects) { e.Update(); } //mxd. Do complicated light level shenanigans only when there are extrafloors if (lightlevels.Count > 2) { // Sort the levels SectorLevelComparer comparer = new SectorLevelComparer(sector); lightlevels.Sort(0, lightlevels.Count, comparer); // Now that we know the levels in this sector (and in the right order) // we can determine the lighting in between and on the levels. SectorLevel stored = ceilingbase; //mxd. Special cases... if (lightlevels[lightlevels.Count - 1].disablelighting) { lightlevels[lightlevels.Count - 1].colorbelow = stored.colorbelow; lightlevels[lightlevels.Count - 1].brightnessbelow = stored.brightnessbelow; lightlevels[lightlevels.Count - 1].color = GetLevelColor(stored, lightlevels[lightlevels.Count - 1]); } //mxd. Cast light properties from top to bottom for (int i = lightlevels.Count - 2; i >= 0; i--) { SectorLevel l = lightlevels[i]; SectorLevel pl = lightlevels[i + 1]; // Glow levels don't cast light if (pl.type == SectorLevelType.Glow && lightlevels.Count > i + 2) { pl = lightlevels[i + 2]; } if (l.lighttype == LightLevelType.TYPE1) { stored = pl; } // Use stored light params when "disablelighting" flag is set else if (l.disablelighting) { l.colorbelow = stored.colorbelow; l.brightnessbelow = stored.brightnessbelow; l.color = GetLevelColor(stored, l); } else if (l.restrictlighting) { if (!pl.restrictlighting && pl != ceiling) { stored = pl; } l.color = GetLevelColor(stored, l); // This is the bottom side of extrafloor with "restrict lighting" flag. Make it cast stored light props. if (l.type == SectorLevelType.Ceiling) { // Special case: 2 intersecting extrafloors with "restrictlighting" flag... if (pl.restrictlighting && pl.type == SectorLevelType.Floor && pl.sector.Index != l.sector.Index) { // Use light and color settings from previous layer l.colorbelow = pl.colorbelow; l.brightnessbelow = pl.brightnessbelow; l.color = GetLevelColor(pl, l); // Also colorize previous layer using next higher level color if (i + 2 < lightlevels.Count) { pl.color = GetLevelColor(lightlevels[i + 2], pl); } } else { l.colorbelow = stored.colorbelow; l.brightnessbelow = stored.brightnessbelow; } } } // Bottom TYPE1 border requires special handling... else if (l.lighttype == LightLevelType.TYPE1_BOTTOM) { // Use brightness and color from previous light level when it's between TYPE1 and TYPE1_BOTTOM levels if (pl.type == SectorLevelType.Light && pl.lighttype != LightLevelType.TYPE1) { l.brightnessbelow = pl.brightnessbelow; l.colorbelow = pl.colorbelow; } // Use brightness and color from the light level above TYPE1 level else if (stored.type == SectorLevelType.Light) { l.brightnessbelow = stored.brightnessbelow; l.colorbelow = stored.colorbelow; } // Otherwise light values from the real ceiling are used } else if (l.lighttype == LightLevelType.UNKNOWN) { // Use stored light level when previous one has "disablelighting" flag // or is the lower boundary of an extrafloor with "restrictlighting" flag SectorLevel src = (pl.disablelighting || (pl.restrictlighting && pl.type == SectorLevelType.Ceiling) ? stored : pl); // Don't change real ceiling light when previous level has "disablelighting" flag // Don't change anything when light properties were reset before hitting floor (otherwise floor UDMF brightness will be lost) if ((src == ceilingbase && l == ceiling) || (src == ceiling && l == floor && src.LightPropertiesMatch(ceilingbase))) { continue; } // Transfer color and brightness if previous level has them if (src.colorbelow.a > 0 && src.brightnessbelow != -1) { // Only surface brightness is retained when a glowing flat is used as extrafloor texture if (!l.affectedbyglow) { l.color = GetLevelColor(src, l); } // Transfer brightnessbelow and colorbelow if current level is not extrafloor top if (!(l.extrafloor && l.type == SectorLevelType.Floor)) { l.brightnessbelow = src.brightnessbelow; l.colorbelow = src.colorbelow; } } // Store bottom extrafloor level if it doesn't have "restrictlighting" or "restrictlighting" flags set if (l.extrafloor && l.type == SectorLevelType.Ceiling && !l.restrictlighting && !l.disablelighting) { stored = l; } } // Reset lighting? if (l.resetlighting) { stored = ceilingbase; } } } //mxd. Apply ceiling glow effect? if (CeilingGlow != null && CeilingGlow.Fullbright) { ceiling.color = PixelColor.INT_WHITE; } //mxd. Apply floor glow effect? if (FloorGlow != null) { // Update floor color if (FloorGlow.Fullbright) { floor.color = PixelColor.INT_WHITE; } // Update brightness floor.brightnessbelow = (FloorGlow.Fullbright ? 255 : Math.Max(128, floor.brightnessbelow)); if (floor.colorbelow.ToInt() == 0) { byte bb = (byte)floor.brightnessbelow; floor.colorbelow = new PixelColor(255, bb, bb, bb); } } //mxd floor.affectedbyglow = (FloorGlow != null); ceiling.affectedbyglow = (CeilingGlow != null); floorchanged = false; ceilingchanged = false; updated = true; isupdating = false; }
// This adds a sector level public void AddSectorLevel(SectorLevel level) { // Note: Inserting before the end so that the ceiling stays // at the end and the floor at the beginning lightlevels.Insert(lightlevels.Count - 1, level); }
// This makes sure we are updated with the source linedef information public override void Update() { SectorData sd = data.Mode.GetSectorData(linedef.Front.Sector); if (!sd.Updated) { sd.Update(); } sd.AddUpdateSector(data.Sector, true); if (floor == null) { floor = new SectorLevel(sd.Floor); data.AddSectorLevel(floor); } if (ceiling == null) { ceiling = new SectorLevel(sd.Ceiling); data.AddSectorLevel(ceiling); } // For non-vavoom types, we must switch the level types if (linedef.Args[1] != (int)FloorTypes.VavoomStyle) { //mxd. check for Swimmable/RenderInside/RenderAdditive flags renderadditive = (linedef.Args[2] & (int)Flags.RenderAdditive) == (int)Flags.RenderAdditive; renderinside = ((((linedef.Args[1] & (int)FloorTypes.Swimmable) == (int)FloorTypes.Swimmable) && (linedef.Args[1] & (int)FloorTypes.NonSolid) != (int)FloorTypes.NonSolid)) || ((linedef.Args[1] & (int)FloorTypes.RenderInside) == (int)FloorTypes.RenderInside); ignorebottomheight = ((linedef.Args[2] & (int)Flags.IgnoreBottomHeight) == (int)Flags.IgnoreBottomHeight); vavoomtype = false; alpha = General.Clamp(linedef.Args[3], 0, 255); sd.Ceiling.CopyProperties(floor); sd.Floor.CopyProperties(ceiling); floor.type = SectorLevelType.Floor; floor.plane = sd.Ceiling.plane.GetInverted(); ceiling.type = SectorLevelType.Ceiling; ceiling.plane = (ignorebottomheight ? sd.Ceiling.plane : sd.Floor.plane.GetInverted()); //mxd. Use upper plane when "ignorebottomheight" flag is set //mxd clipsides = (!renderinside && !renderadditive && alpha > 254 && !ignorebottomheight); // A 3D floor's color is always that of the sector it is placed in // (unless it's affected by glow) - mxd if (sd.CeilingGlow == null || !sd.CeilingGlow.Fullbright) { floor.color = 0; } } else { vavoomtype = true; renderadditive = false; //mxd clipsides = true; //mxd floor.type = SectorLevelType.Ceiling; floor.plane = sd.Ceiling.plane; ceiling.type = SectorLevelType.Floor; ceiling.plane = sd.Floor.plane; alpha = 255; // A 3D floor's color is always that of the sector it is placed in // (unless it's affected by glow) - mxd if (sd.FloorGlow == null || !sd.FloorGlow.Fullbright) { ceiling.color = 0; } } // Apply alpha floor.alpha = alpha; ceiling.alpha = alpha; //mxd floor.extrafloor = true; ceiling.extrafloor = true; floor.splitsides = !clipsides; ceiling.splitsides = (!clipsides && !ignorebottomheight); // if "ignorebottomheight" flag is set, both ceiling and floor will be at the same level and sidedef clipping with floor level will fail resulting in incorrect light props transfer in some cases //mxd. Check slopes, cause GZDoom can't handle sloped translucent 3d floors... sloped3dfloor = ((alpha < 255 || renderadditive) && (Angle2D.RadToDeg(ceiling.plane.Normal.GetAngleZ()) != 270 || Angle2D.RadToDeg(floor.plane.Normal.GetAngleZ()) != 90)); // Do not adjust light? (works only for non-vavoom types) if (!vavoomtype) { bool disablelighting = ((linedef.Args[2] & (int)Flags.DisableLighting) == (int)Flags.DisableLighting); //mxd bool restrictlighting = ((linedef.Args[2] & (int)Flags.RestrictLighting) == (int)Flags.RestrictLighting); //mxd floor.resetlighting = ((linedef.Args[2] & (int)Flags.ResetLighting) == (int)Flags.ResetLighting); //mxd if (disablelighting || restrictlighting) { floor.restrictlighting = restrictlighting; //mxd floor.disablelighting = disablelighting; //mxd if (disablelighting) //mxd { floor.color = 0; floor.brightnessbelow = -1; floor.colorbelow = PixelColor.FromInt(0); } ceiling.disablelighting = disablelighting; //mxd ceiling.restrictlighting = restrictlighting; //mxd ceiling.color = 0; ceiling.brightnessbelow = -1; ceiling.colorbelow = PixelColor.FromInt(0); } } }
//mxd public bool Setup(SectorLevel level, Effect3DFloor extrafloor, bool innerside) { Sector s = level.sector; Vector2D texscale; this.innerside = innerside; base.Setup(level, extrafloor); // Fetch ZDoom fields float rotate = Angle2D.DegToRad(s.Fields.GetValue("rotationfloor", 0.0f)); Vector2D offset = new Vector2D(s.Fields.GetValue("xpanningfloor", 0.0f), s.Fields.GetValue("ypanningfloor", 0.0f)); Vector2D scale = new Vector2D(s.Fields.GetValue("xscalefloor", 1.0f), s.Fields.GetValue("yscalefloor", 1.0f)); //Load floor texture if (s.LongFloorTexture != MapSet.EmptyLongName) { base.Texture = General.Map.Data.GetFlatImage(s.LongFloorTexture); if (base.Texture == null || base.Texture is UnknownImage) { base.Texture = General.Map.Data.UnknownTexture3D; setuponloadedtexture = s.LongFloorTexture; } else if (!base.Texture.IsImageLoaded) { setuponloadedtexture = s.LongFloorTexture; } } else { // Use missing texture base.Texture = General.Map.Data.MissingTexture3D; setuponloadedtexture = 0; } // Determine texture scale if (base.Texture.IsImageLoaded) { texscale = new Vector2D(1.0f / base.Texture.ScaledWidth, 1.0f / base.Texture.ScaledHeight); } else { texscale = new Vector2D(1.0f / 64.0f, 1.0f / 64.0f); } // Determine brightness int color = PixelColor.FromInt(level.color).WithAlpha((byte)General.Clamp(level.alpha, 0, 255)).ToInt(); //mxd. Top extrafloor level should calculate fogdensity //from the brightness of the level above it int targetbrightness; if (extrafloor != null && extrafloor.VavoomType && !level.disablelighting) { targetbrightness = 0; SectorData sd = mode.GetSectorData(this.Sector.Sector); for (int i = 0; i < sd.LightLevels.Count - 1; i++) { if (sd.LightLevels[i] == level) { targetbrightness = sd.LightLevels[i + 1].brightnessbelow; break; } } } else { targetbrightness = level.brightnessbelow; } //mxd. Determine fog density fogfactor = CalculateFogFactor(targetbrightness); // Make vertices ReadOnlyCollection <Vector2D> triverts = Sector.Sector.Triangles.Vertices; WorldVertex[] verts = new WorldVertex[triverts.Count]; for (int i = 0; i < triverts.Count; i++) { // Color shading verts[i].c = color; //mxd // Vertex coordinates verts[i].x = triverts[i].x; verts[i].y = triverts[i].y; verts[i].z = level.plane.GetZ(triverts[i]); // Texture coordinates Vector2D pos = triverts[i]; pos = pos.GetRotated(rotate); pos.y = -pos.y; pos = (pos + offset) * scale * texscale; verts[i].u = pos.x; verts[i].v = pos.y; } // The sector triangulation created clockwise triangles that // are right up for the floor. For the ceiling we must flip // the triangles upside down. if ((extrafloor != null) && !extrafloor.VavoomType && !innerside) { SwapTriangleVertices(verts); } // Determine render pass if (extrafloor != null) { if (extrafloor.Sloped3dFloor) //mxd { this.RenderPass = RenderPass.Mask; } else if (extrafloor.RenderAdditive) //mxd { this.RenderPass = RenderPass.Additive; } else if (level.alpha < 255) { this.RenderPass = RenderPass.Alpha; } else { this.RenderPass = RenderPass.Mask; } } else { this.RenderPass = RenderPass.Solid; } //mxd. Update sky render flag UpdateSkyRenderFlag(); // Apply vertices base.SetVertices(verts); return(verts.Length > 0); }
//mxd public bool Setup(SectorLevel level, Effect3DFloor extrafloor, bool innerside) { Sector s = level.sector; Vector2D texscale; this.innerside = innerside; //mxd base.Setup(level, extrafloor); // Fetch ZDoom fields float rotate = Angle2D.DegToRad(s.Fields.GetValue("rotationceiling", 0.0f)); Vector2D offset = new Vector2D(s.Fields.GetValue("xpanningceiling", 0.0f), s.Fields.GetValue("ypanningceiling", 0.0f)); Vector2D scale = new Vector2D(s.Fields.GetValue("xscaleceiling", 1.0f), s.Fields.GetValue("yscaleceiling", 1.0f)); //Load ceiling texture if (s.LongCeilTexture != MapSet.EmptyLongName) { base.Texture = General.Map.Data.GetFlatImage(s.LongCeilTexture); if (base.Texture == null || base.Texture is UnknownImage) { base.Texture = General.Map.Data.UnknownTexture3D; setuponloadedtexture = s.LongCeilTexture; } else if (!base.Texture.IsImageLoaded) { setuponloadedtexture = s.LongCeilTexture; } } else { // Use missing texture base.Texture = General.Map.Data.MissingTexture3D; setuponloadedtexture = 0; } // Determine texture scale if (base.Texture.IsImageLoaded) { texscale = new Vector2D(1.0f / base.Texture.ScaledWidth, 1.0f / base.Texture.ScaledHeight); } else { texscale = new Vector2D(1.0f / 64.0f, 1.0f / 64.0f); } // Determine brightness byte alpha = (byte)General.Clamp(level.alpha, 0, 255); int color = PixelColor.FromInt(level.color).WithAlpha(alpha).ToInt(); int targetbrightness; if (extrafloor != null && !extrafloor.VavoomType && !level.disablelighting) { //mxd. Top extrafloor level should calculate fogdensity from the brightness of the level above it if (!innerside) { targetbrightness = 0; SectorData sd = mode.GetSectorData(this.Sector.Sector); for (int i = 0; i < sd.LightLevels.Count - 1; i++) { if (sd.LightLevels[i] == level) { targetbrightness = sd.LightLevels[i + 1].brightnessbelow; break; } } } //mxd. Inner extrafloor ceilings must be colored using control sector's color and brightness else { targetbrightness = level.brightnessbelow; SectorData sd = mode.GetSectorData(this.Sector.Sector); for (int i = 0; i < sd.LightLevels.Count; i++) { if (sd.LightLevels[i] == level) { if (i > 0) { color = PixelColor.FromInt(sd.LightLevels[i - 1].color).WithAlpha(alpha).ToInt(); } break; } } } } else { targetbrightness = level.brightnessbelow; } //mxd. Determine fog density fogfactor = CalculateFogFactor(targetbrightness); // Make vertices ReadOnlyCollection <Vector2D> triverts = Sector.Sector.Triangles.Vertices; WorldVertex[] verts = new WorldVertex[triverts.Count]; for (int i = 0; i < triverts.Count; i++) { // Color shading verts[i].c = color; //mxd // Vertex coordinates verts[i].x = triverts[i].x; verts[i].y = triverts[i].y; verts[i].z = level.plane.GetZ(triverts[i]); // Texture coordinates Vector2D pos = triverts[i]; pos = pos.GetRotated(rotate); pos.y = -pos.y; pos = (pos + offset) * scale * texscale; verts[i].u = pos.x; verts[i].v = pos.y; } // The sector triangulation created clockwise triangles that // are right up for the floor. For the ceiling we must flip // the triangles upside down. if (extrafloor == null || extrafloor.VavoomType || innerside) { SwapTriangleVertices(verts); } // Determine render pass if (extrafloor != null) { if (extrafloor.Sloped3dFloor) //mxd { this.RenderPass = RenderPass.Mask; } else if (extrafloor.RenderAdditive) //mxd { this.RenderPass = RenderPass.Additive; } else if (level.alpha < 255) { this.RenderPass = RenderPass.Alpha; } else { this.RenderPass = RenderPass.Mask; } } else { this.RenderPass = RenderPass.Solid; } //mxd. Update sky render flag bool isrenderedassky = renderassky; renderassky = (level.sector.CeilTexture == General.Map.Config.SkyFlatName); if (isrenderedassky != renderassky && Sector.Sides != null) { // Upper sidedef geometry may need updating... foreach (Sidedef side in level.sector.Sidedefs) { VisualSidedefParts parts = Sector.GetSidedefParts(side); // Upper side can exist in either our, or the neightbouring sector, right? if (parts.upper != null && parts.upper.Triangles > 0) { parts.upper.UpdateSkyRenderFlag(); } else if (side.Other != null && side.Other.Sector != null && side.Other.Sector.CeilTexture == General.Map.Config.SkyFlatName) { // Update upper side of the neightbouring sector BaseVisualSector other = (BaseVisualSector)mode.GetVisualSector(side.Other.Sector); if (other != null && other.Sides != null) { parts = other.GetSidedefParts(side.Other); if (parts.upper != null && parts.upper.Triangles > 0) { parts.upper.UpdateSkyRenderFlag(); } } } } } // Apply vertices base.SetVertices(verts); return(verts.Length > 0); }