//mxd public override void OnChangeScale(int incrementX, int incrementY) { // Only do this when not done yet in this call // Because we may be able to select the same 3D floor multiple times through multiple sectors SectorData sd = mode.GetSectorData(level.sector); if (!sd.CeilingChanged) { sd.CeilingChanged = true; base.OnChangeScale(incrementX, incrementY); } }
//mxd public override void OnChangeTextureRotation(float angle) { // Only do this when not done yet in this call // Because we may be able to select the same 3D floor multiple times through multiple sectors SectorData sd = mode.GetSectorData(level.sector); if (!sd.FloorChanged) { sd.FloorChanged = true; base.OnChangeTextureRotation(angle); } }
// Constructor public EffectThingLineSlope(SectorData data, Thing sourcething, Sidedef sourcesidedef) : base(data) { thing = sourcething; sidedef = sourcesidedef; // New effect added: This sector needs an update! if (data.Mode.VisualSectorExists(data.Sector)) { BaseVisualSector vs = (BaseVisualSector)data.Mode.GetVisualSector(data.Sector); vs.UpdateSectorGeometry(true); } }
//mxd public override void OnChangeTextureOffset(int horizontal, int vertical, bool doSurfaceAngleCorrection) { // Only do this when not done yet in this call // Because we may be able to select the same 3D floor multiple times through multiple sectors SectorData sd = mode.GetSectorData(level.sector); if (!sd.FloorChanged) { sd.FloorChanged = true; base.OnChangeTextureOffset(horizontal, vertical, doSurfaceAngleCorrection); } }
// Call to change the height public override void OnChangeTargetHeight(int amount) { // Only do this when not done yet in this call // Because we may be able to select the same 3D floor multiple times through multiple sectors SectorData sd = mode.GetSectorData(level.sector); if (!sd.FloorChanged) { sd.FloorChanged = true; base.OnChangeTargetHeight(amount); } }
// Constructor public EffectThingVertexSlope(SectorData data, List <Thing> sourcethings, bool floor) : base(data) { things = sourcethings; slopefloor = floor; // New effect added: This sector needs an update! if (data.Mode.VisualSectorExists(data.Sector)) { BaseVisualSector vs = (BaseVisualSector)data.Mode.GetVisualSector(data.Sector); vs.UpdateSectorGeometry(true); } }
public EffectPlaneCopySlope(SectorData data, Linedef sourcelinedef, bool front) : base(data) { this.linedef = sourcelinedef; this.front = front; // New effect added: This sector needs an update! if (data.Mode.VisualSectorExists(data.Sector)) { BaseVisualSector vs = (BaseVisualSector)data.Mode.GetVisualSector(data.Sector); vs.UpdateSectorGeometry(true); } }
// This changes the texture protected override void SetTexture(string texturename) { this.Sidedef.SetTextureHigh(texturename); General.Map.Data.UpdateUsedTextures(); this.Setup(); //mxd. Other sector also may require updating SectorData sd = mode.GetSectorData(Sidedef.Sector); if (sd.ExtraFloors.Count > 0) { ((BaseVisualSector)mode.GetVisualSector(Sidedef.Sector)).Rebuild(); } }
// 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); // Transfer ceiling brightness, keep sector color PixelColor lightcolor = PixelColor.FromInt(data.Sector.Fields.GetValue("lightcolor", -1)); PixelColor brightness = PixelColor.FromInt(General.Map.Renderer3D.CalculateBrightness(sd.Sector.Brightness)); data.Ceiling.color = PixelColor.Modulate(lightcolor, brightness).WithAlpha(255).ToInt(); }
// This makes sure we are updated with the source linedef information public override void Update() { //mxd. Skip if arg0 is 0. if (thing.Args[0] == 0) { return; } ThingData td = data.Mode.GetThingData(thing); Thing t = thing; Linedef ld = sidedef.Line; if (ld != null) { if (t.Type == 9500) { SectorData sd = data.Mode.GetSectorData(sidedef.Sector); Vector3D v1 = new Vector3D(ld.Start.Position.x, ld.Start.Position.y, sd.Floor.plane.GetZ(ld.Start.Position)); Vector3D v2 = new Vector3D(ld.End.Position.x, ld.End.Position.y, sd.Floor.plane.GetZ(ld.End.Position)); Vector3D v3 = new Vector3D(t.Position.x, t.Position.y, t.Position.z + sd.Floor.plane.GetZ(t.Position)); sd.AddUpdateSector(data.Sector, true); if (!sd.Updated) { sd.Update(); } td.AddUpdateSector(sidedef.Sector, true); sd.Floor.plane = new Plane(v1, v2, v3, true); } else if (t.Type == 9501) { SectorData sd = data.Mode.GetSectorData(sidedef.Sector); Vector3D v1 = new Vector3D(ld.Start.Position.x, ld.Start.Position.y, sd.Ceiling.plane.GetZ(ld.Start.Position)); Vector3D v2 = new Vector3D(ld.End.Position.x, ld.End.Position.y, sd.Ceiling.plane.GetZ(ld.End.Position)); Vector3D v3 = new Vector3D(t.Position.x, t.Position.y, t.Position.z + sd.Ceiling.plane.GetZ(t.Position)); sd.AddUpdateSector(data.Sector, true); if (!sd.Updated) { sd.Update(); } td.AddUpdateSector(sidedef.Sector, true); sd.Ceiling.plane = new Plane(v1, v2, v3, false); } } }
//mxd public override void OnTextureFit(FitTextureOptions options) { if (!General.Map.UDMF) { return; } if (string.IsNullOrEmpty(Sidedef.MiddleTexture) || Sidedef.MiddleTexture == "-" || !Texture.IsImageLoaded) { return; } FitTexture(options); Setup(); // Update linked effects SectorData sd = mode.GetSectorDataEx(Sector.Sector); if (sd != null) { sd.Reset(true); } }
// 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(); } }
//mxd. call this to update sector and things in it when Sector.Fields are changed override public void UpdateSectorData() { //update sector data SectorData data = GetSectorData(); data.UpdateForced(); //update sector Rebuild(); //update things in this sector foreach (Thing t in General.Map.Map.Things) { if (t.Sector == this.Sector) { if (mode.VisualThingExists(t)) { // Update thing BaseVisualThing vt = (BaseVisualThing)mode.GetVisualThing(t); vt.Rebuild(); } } } }
// This makes sure we are updated with the source linedef information public override void Update() { // Find tagged sector Sector sourcesector = null; foreach (Sector s in General.Map.Map.Sectors) { if (s.Tags.Contains(thing.Args[0])) { sourcesector = s; break; } } if (sourcesector != null) { SectorData sourcesectordata = data.Mode.GetSectorData(sourcesector); if (!sourcesectordata.Updated) { sourcesectordata.Update(); } switch (thing.Type) { case 9510: data.Floor.plane = sourcesectordata.Floor.plane; break; case 9511: data.Ceiling.plane = sourcesectordata.Ceiling.plane; break; } sourcesectordata.AddUpdateSector(data.Sector, true); } }
// 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); }
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 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); }
// Constructor public EffectGlowingFlat(SectorData sourcedata) { data = sourcedata; }
internal static double GetHigherThingZ(BaseVisualMode mode, SectorData sd, VisualThing thing) { Vector3D pos = thing.Thing.Position; double 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); double fz = (absolute ? 0 : sd.Floor.plane.GetZ(pos)); double cz = sd.Ceiling.plane.GetZ(pos); if (hangs) { // Transform to floor-aligned position Vector3D floorpos = new Vector3D(pos, (cz - fz) - pos.z - thingheight); double highertingz = GetNextHigherThingZ(mode, intersectingthings, floorpos.z, thingheight); double higherfloorz = double.MinValue; // Do it only when there are extrafloors if (sd.LightLevels.Count > 2) { // Unlike sd.ExtraFloors, these are sorted by height foreach (SectorLevel level in sd.LightLevels) { if (level.type == SectorLevelType.Light || level.type == SectorLevelType.Glow) { continue; // Skip lights and glows } double z = level.plane.GetZ(floorpos) - fz; if (level.type == SectorLevelType.Ceiling) { z -= thingheight; } if (z > floorpos.z) { higherfloorz = z; break; } } } if (higherfloorz != float.MinValue && highertingz != float.MaxValue) { // Transform back to ceiling-aligned position return(cz - fz - Math.Max(Math.Min(higherfloorz, highertingz), 0) - thingheight); } if (higherfloorz != float.MinValue) { // Transform back to ceiling-aligned position return(Math.Max(cz - fz - higherfloorz - thingheight, 0)); } if (highertingz != float.MaxValue) { // Transform back to ceiling-aligned position return(Math.Max(cz - fz - highertingz - thingheight, 0)); } return(0); // Align to real ceiling } else { double highertingz = GetNextHigherThingZ(mode, intersectingthings, (absolute ? pos.z - fz : pos.z), thingheight); double higherfloorz = double.MinValue; // Do it only when there are extrafloors if (sd.LightLevels.Count > 2) { // Unlike sd.ExtraFloors, these are sorted by height foreach (SectorLevel level in sd.LightLevels) { if (level.type == SectorLevelType.Light || level.type == SectorLevelType.Glow) { continue; // Skip lights and glows } double z = level.plane.GetZ(pos) - fz; if (level.type == SectorLevelType.Ceiling) { z -= thingheight; } if (z > pos.z) { higherfloorz = z; break; } } } double floorz = sd.Floor.plane.GetZ(pos); double ceilpos = cz - floorz - thingheight; // Ceiling-aligned relative target thing z if (higherfloorz != double.MinValue && highertingz != double.MaxValue) { ceilpos = Math.Min(ceilpos, Math.Min(higherfloorz, highertingz)); } if (higherfloorz != double.MinValue) { ceilpos = Math.Min(ceilpos, higherfloorz); } if (highertingz != double.MaxValue) { ceilpos = Math.Min(ceilpos, highertingz); } return(absolute ? ceilpos + floorz : ceilpos); // Convert to absolute position if necessary } }
// This makes sure we are updated with the source linedef information public override void Update() { ThingData td = data.Mode.GetThingData(thing); Thing t = thing; // Floor slope thing if (t.Type == 9502) { t.DetermineSector(data.Mode.BlockMap); if (t.Sector != null) { //mxd. Vertex zheight overrides this effect if (General.Map.UDMF && t.Sector.Sidedefs.Count == 3) { foreach (Sidedef side in t.Sector.Sidedefs) { if (!double.IsNaN(side.Line.Start.ZFloor) || !double.IsNaN(side.Line.End.ZFloor)) { return; } } } double angle = Angle2D.DoomToReal((int)Angle2D.RadToDeg(t.Angle)); double vangle = Angle2D.DegToRad(General.Clamp(t.Args[0], 0, 180)); //mxd. Don't underestimate user stupidity (or curiosity)! Vector2D point = new Vector2D(t.Position.x + Math.Cos(angle) * Math.Sin(vangle), t.Position.y + Math.Sin(angle) * Math.Sin(vangle)); Vector2D perpendicular = new Line2D(t.Position, point).GetPerpendicular(); Vector3D v1 = new Vector3D(t.Position.x, t.Position.y, t.Position.z + t.Sector.FloorHeight); Vector3D v2 = new Vector3D( point.x + perpendicular.x, point.y + perpendicular.y, t.Position.z + t.Sector.FloorHeight + Math.Cos(vangle) ); Vector3D v3 = new Vector3D( point.x - perpendicular.x, point.y - perpendicular.y, t.Position.z + t.Sector.FloorHeight + Math.Cos(vangle) ); SectorData sd = data.Mode.GetSectorData(t.Sector); sd.AddUpdateSector(data.Sector, true); if (!sd.Updated) { sd.Update(); } td.AddUpdateSector(t.Sector, true); sd.Floor.plane = new Plane(v1, v2, v3, true); } } // Ceiling slope thing else if (t.Type == 9503) { t.DetermineSector(data.Mode.BlockMap); if (t.Sector != null) { //mxd. Vertex zheight overrides this effect if (General.Map.UDMF && t.Sector.Sidedefs.Count == 3) { foreach (Sidedef side in t.Sector.Sidedefs) { if (!double.IsNaN(side.Line.Start.ZCeiling) || !double.IsNaN(side.Line.End.ZCeiling)) { return; } } } double angle = Angle2D.DoomToReal((int)Angle2D.RadToDeg(t.Angle)); double vangle = Angle2D.DegToRad(General.Clamp(t.Args[0], 0, 180)); //mxd. Don't underestimate user stupidity (or curiosity)! Vector2D point = new Vector2D(t.Position.x + Math.Cos(angle) * Math.Sin(vangle), t.Position.y + Math.Sin(angle) * Math.Sin(vangle)); Vector2D perpendicular = new Line2D(t.Position, point).GetPerpendicular(); Vector3D v1 = new Vector3D(t.Position.x, t.Position.y, t.Position.z + t.Sector.CeilHeight); Vector3D v2 = new Vector3D( point.x + perpendicular.x, point.y + perpendicular.y, t.Position.z + t.Sector.CeilHeight + Math.Cos(vangle) ); Vector3D v3 = new Vector3D( point.x - perpendicular.x, point.y - perpendicular.y, t.Position.z + t.Sector.CeilHeight + Math.Cos(vangle) ); SectorData sd = data.Mode.GetSectorData(t.Sector); sd.AddUpdateSector(data.Sector, true); if (!sd.Updated) { sd.Update(); } td.AddUpdateSector(t.Sector, true); sd.Ceiling.plane = new Plane(v1, v2, v3, 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); }
//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); }
// 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() { //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; tof = tof / tscaleAbs; 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; 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 repeatmidtex = Sidedef.IsFlagSet("wrapmidtex") || Sidedef.Line.IsFlagSet("wrapmidtex"); //mxd 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() { 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); }
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 } }
// This makes sure we are updated with the source linedef information public override void Update() { //mxd. Skip if arg0 is 0. if (thing.Args[0] == 0) { return; } ThingData td = data.Mode.GetThingData(thing); Thing t = thing; // Find the tagged line Linedef ld = null; foreach (Linedef l in General.Map.Map.Linedefs) { if (l.Tags.Contains(t.Args[0])) { ld = l; break; } } if (ld != null) { if (t.Type == 9500) { // Slope the floor from the linedef to thing t.DetermineSector(data.Mode.BlockMap); if (t.Sector != null) { Vector3D v3 = new Vector3D(t.Position.x, t.Position.y, t.Position.z + t.Sector.FloorHeight); if (ld.SideOfLine(t.Position) < 0.0f) { Vector3D v1 = new Vector3D(ld.Start.Position.x, ld.Start.Position.y, ld.Front.Sector.FloorHeight); Vector3D v2 = new Vector3D(ld.End.Position.x, ld.End.Position.y, ld.Front.Sector.FloorHeight); SectorData sd = data.Mode.GetSectorData(ld.Front.Sector); sd.AddUpdateSector(data.Sector, true); if (!sd.Updated) { sd.Update(); } td.AddUpdateSector(ld.Front.Sector, true); sd.Floor.plane = new Plane(v1, v2, v3, true); } else { Vector3D v1 = new Vector3D(ld.Start.Position.x, ld.Start.Position.y, ld.Back.Sector.FloorHeight); Vector3D v2 = new Vector3D(ld.End.Position.x, ld.End.Position.y, ld.Back.Sector.FloorHeight); SectorData sd = data.Mode.GetSectorData(ld.Back.Sector); sd.AddUpdateSector(data.Sector, true); if (!sd.Updated) { sd.Update(); } td.AddUpdateSector(ld.Back.Sector, true); sd.Floor.plane = new Plane(v2, v1, v3, true); } } } else if (t.Type == 9501) { // Slope the ceiling from the linedef to thing t.DetermineSector(data.Mode.BlockMap); if (t.Sector != null) { td.AddUpdateSector(t.Sector, true); Vector3D v3 = new Vector3D(t.Position.x, t.Position.y, t.Position.z + t.Sector.CeilHeight); if (ld.SideOfLine(t.Position) < 0.0f) { Vector3D v1 = new Vector3D(ld.Start.Position.x, ld.Start.Position.y, ld.Front.Sector.CeilHeight); Vector3D v2 = new Vector3D(ld.End.Position.x, ld.End.Position.y, ld.Front.Sector.CeilHeight); SectorData sd = data.Mode.GetSectorData(ld.Front.Sector); sd.AddUpdateSector(data.Sector, true); td.AddUpdateSector(ld.Front.Sector, true); if (!sd.Updated) { sd.Update(); } sd.Ceiling.plane = new Plane(v1, v2, v3, false); } else { Vector3D v1 = new Vector3D(ld.Start.Position.x, ld.Start.Position.y, ld.Back.Sector.CeilHeight); Vector3D v2 = new Vector3D(ld.End.Position.x, ld.End.Position.y, ld.Back.Sector.CeilHeight); SectorData sd = data.Mode.GetSectorData(ld.Back.Sector); sd.AddUpdateSector(data.Sector, true); td.AddUpdateSector(ld.Back.Sector, true); if (!sd.Updated) { sd.Update(); } sd.Ceiling.plane = new Plane(v2, v1, v3, false); } } } } }
// This gets sectors which surround given sectors internal static IEnumerable <Sector> GetSectorsAround(BaseVisualMode mode, IEnumerable <Sector> selected) { HashSet <int> processedsectors = new HashSet <int>(); HashSet <Vertex> verts = new HashSet <Vertex>(); List <Sector> result = new List <Sector>(); foreach (Sector s in selected) { processedsectors.Add(s.Index); foreach (Sidedef side in s.Sidedefs) { if (!verts.Contains(side.Line.Start)) { verts.Add(side.Line.Start); } if (!verts.Contains(side.Line.End)) { verts.Add(side.Line.End); } } } foreach (Vertex v in verts) { foreach (Linedef l in v.Linedefs) { if (l.Front != null && l.Front.Sector != null && !processedsectors.Contains(l.Front.Sector.Index)) { result.Add(l.Front.Sector); processedsectors.Add(l.Front.Sector.Index); // Add extrafloors as well SectorData sd = mode.GetSectorDataEx(l.Front.Sector); if (sd != null && sd.ExtraFloors.Count > 0) { foreach (Effect3DFloor effect in sd.ExtraFloors) { if (!processedsectors.Contains(effect.Linedef.Front.Sector.Index)) { result.Add(effect.Linedef.Front.Sector); processedsectors.Add(effect.Linedef.Front.Sector.Index); } } } } if (l.Back != null && l.Back.Sector != null && !processedsectors.Contains(l.Back.Sector.Index)) { result.Add(l.Back.Sector); processedsectors.Add(l.Back.Sector.Index); // Add extrafloors as well SectorData sd = mode.GetSectorDataEx(l.Back.Sector); if (sd != null && sd.ExtraFloors.Count > 0) { foreach (Effect3DFloor effect in sd.ExtraFloors) { if (!processedsectors.Contains(effect.Linedef.Front.Sector.Index)) { result.Add(effect.Linedef.Front.Sector); processedsectors.Add(effect.Linedef.Front.Sector.Index); } } } } } } return(result); }
//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); }
// This makes sure we are updated with the source linedef information public override void Update() { Sector sourcesector = null; SectorData sourcesectordata = null; bool updatesides = false; // Copy slopes from tagged sectors //check which arguments we must use int floorArg = (front ? 0 : 2); int ceilingArg = (front ? 1 : 3); //find sector to align floor to if (linedef.Args[floorArg] > 0) { foreach (Sector s in General.Map.Map.Sectors) { if (s.Tags.Contains(linedef.Args[floorArg])) { sourcesector = s; break; } } if (sourcesector != null) { sourcesectordata = data.Mode.GetSectorData(sourcesector); if (!sourcesectordata.Updated) { sourcesectordata.Update(); } data.Floor.plane = sourcesectordata.Floor.plane; sourcesectordata.AddUpdateSector(data.Sector, true); updatesides = true; } } if (linedef.Args[ceilingArg] > 0) { //find sector to align ceiling to if (linedef.Args[ceilingArg] != linedef.Args[floorArg]) { sourcesector = null; foreach (Sector s in General.Map.Map.Sectors) { if (s.Tags.Contains(linedef.Args[ceilingArg])) { sourcesector = s; break; } } if (sourcesector != null) { sourcesectordata = data.Mode.GetSectorData(sourcesector); if (!sourcesectordata.Updated) { sourcesectordata.Update(); } data.Ceiling.plane = sourcesectordata.Ceiling.plane; sourcesectordata.AddUpdateSector(data.Sector, true); updatesides = true; } } else if (sourcesector != null) //ceiling uses the same sector as floor { data.Ceiling.plane = sourcesectordata.Ceiling.plane; } } //check the flags... bool copyFloor = false; bool copyCeiling = false; if (linedef.Args[4] > 0 && linedef.Args[4] != 3 && linedef.Args[4] != 12) { if (front) { copyFloor = (linedef.Args[4] & 2) == 2; copyCeiling = (linedef.Args[4] & 8) == 8; } else { copyFloor = (linedef.Args[4] & 1) == 1; copyCeiling = (linedef.Args[4] & 4) == 4; } } // Copy slope across the line if ((copyFloor || copyCeiling) && linedef.Front != null && linedef.Back != null) { // Get appropriate source sector data sourcesectordata = data.Mode.GetSectorData(front ? linedef.Back.Sector : linedef.Front.Sector); if (!sourcesectordata.Updated) { sourcesectordata.Update(); } //copy floor slope? if (copyFloor) { data.Floor.plane = sourcesectordata.Floor.plane; sourcesectordata.AddUpdateSector(data.Sector, true); } //copy ceiling slope? if (copyCeiling) { data.Ceiling.plane = sourcesectordata.Ceiling.plane; sourcesectordata.AddUpdateSector(data.Sector, true); } updatesides = true; } // Update outer sidedef geometry if (updatesides) { UpdateSectorSides(data.Sector); // Update sectors with PlaneCopySlope Effect... List <SectorData> toupdate = new List <SectorData>(); foreach (Sector s in data.UpdateAlso.Keys) { SectorData osd = data.Mode.GetSectorDataEx(s); if (osd == null) { continue; } foreach (SectorEffect e in osd.Effects) { if (e is EffectPlaneCopySlope) { toupdate.Add(osd); break; } } } // Do it in 2 steps, because SectorData.Reset() may change SectorData.UpdateAlso collection... foreach (SectorData sd in toupdate) { // Update PlaneCopySlope Effect... sd.Reset(false); // Update outer sides... UpdateSectorSides(sd.Sector); } } }