// 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.CeilingChanged) { sd.CeilingChanged = 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); } }
// 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); if (level == null) { level = new SectorLevel(sd.Ceiling); data.AddSectorLevel(level); } // Update level sd.Ceiling.CopyProperties(level); level.type = SectorLevelType.Light; }
public bool Setup(Effect3DFloor extrafloor) { Vector2D vl, vr; Sidedef sourceside = extrafloor.Linedef.Front; this.extrafloor = extrafloor; int lightvalue = Sidedef.Fields.GetValue("light", 0); bool lightabsolute = Sidedef.Fields.GetValue("lightabsolute", false); Vector2D tscale = new Vector2D(sourceside.Fields.GetValue("scalex_mid", 1.0f), sourceside.Fields.GetValue("scaley_mid", 1.0f)); 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); // Texture given? if ((sourceside.MiddleTexture.Length > 0) && (sourceside.MiddleTexture[0] != '-')) { // Load texture base.Texture = General.Map.Data.GetTextureImage(sourceside.LongMiddleTexture); if (base.Texture == null) { base.Texture = General.Map.Data.MissingTexture3D; setuponloadedtexture = sourceside.LongMiddleTexture; } else { if (!base.Texture.IsImageLoaded) { setuponloadedtexture = sourceside.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 + toffset1 + toffset2; tof = tof / tscale; 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 + Sidedef.Line.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); // 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 = 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; PixelColor wallbrightness = PixelColor.FromInt(mode.CalculateBrightness(lightlevel)); PixelColor wallcolor = PixelColor.Modulate(sd.Ceiling.colorbelow, wallbrightness); 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>(1); polygons.Add(poly); foreach (Effect3DFloor ef in sd.ExtraFloors) { // Same 3D floor and other floors that are not translucent will clip my walls if ((ef.Alpha == 255) || (ef.Linedef.Front.Sector == extrafloor.Linedef.Front.Sector)) { 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 List <WorldVertex> verts = CreatePolygonVertices(polygons, tp, sd, lightvalue, lightabsolute); if (verts.Count > 0) { 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]; PixelColor c = PixelColor.FromInt(v.c); v.c = c.WithAlpha(alpha).ToInt(); verts[i] = v; } } this.RenderPass = RenderPass.Alpha; } else { this.RenderPass = RenderPass.Mask; } base.SetVertices(verts); return(true); } } return(false); }
// This creates vertices from a wall polygon and applies lighting protected List <WorldVertex> CreatePolygonVertices(List <WallPolygon> poly, TexturePlane tp, SectorData sd, int lightvalue, bool lightabsolute) { List <WallPolygon> polygons = new List <WallPolygon>(poly); List <WorldVertex> verts = new List <WorldVertex>(); // Go for all levels to build geometry for (int i = sd.LightLevels.Count - 1; i >= 0; i--) { SectorLevel l = sd.LightLevels[i]; if ((l != sd.Floor) && (l != sd.Ceiling) && (l.type != SectorLevelType.Floor)) { // Go for all polygons int num = polygons.Count; for (int pi = 0; pi < num; pi++) { // Split by plane WallPolygon p = polygons[pi]; WallPolygon np = SplitPoly(ref p, l.plane, false); if (np.Count > 0) { // Determine color int lightlevel = lightabsolute ? lightvalue : l.brightnessbelow + lightvalue; PixelColor wallbrightness = PixelColor.FromInt(mode.CalculateBrightness(lightlevel)); PixelColor wallcolor = PixelColor.Modulate(l.colorbelow, wallbrightness); np.color = wallcolor.WithAlpha(255).ToInt(); if (p.Count == 0) { polygons[pi] = np; } else { polygons[pi] = p; polygons.Add(np); } } else { polygons[pi] = p; } } } } // Go for all polygons to make geometry foreach (WallPolygon p in polygons) { // Find texture coordinates for each vertex in the polygon List <Vector2D> texc = new List <Vector2D>(p.Count); foreach (Vector3D v in p) { texc.Add(tp.GetTextureCoordsAt(v)); } // Now we create triangles from the polygon. // The polygon is convex and clockwise, so this is a piece of cake. if (p.Count >= 3) { for (int k = 1; k < (p.Count - 1); k++) { verts.Add(new WorldVertex(p[0], p.color, texc[0])); verts.Add(new WorldVertex(p[k], p.color, texc[k])); verts.Add(new WorldVertex(p[k + 1], p.color, texc[k + 1])); } } } return(verts); }
// This creates vertices from a wall polygon and applies lighting protected List <WorldVertex> CreatePolygonVertices(WallPolygon poly, TexturePlane tp, SectorData sd, int lightvalue, bool lightabsolute) { List <WallPolygon> polylist = new List <WallPolygon>(1); polylist.Add(poly); return(CreatePolygonVertices(polylist, tp, sd, lightvalue, lightabsolute)); }
// This makes sure we are updated with the source linedef information public override void Update() { 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.Tag == 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 builds the geometry. Returns false when no geometry created. public override bool Setup() { Vector2D vl, vr; int lightvalue = Sidedef.Fields.GetValue("light", 0); bool lightabsolute = Sidedef.Fields.GetValue("lightabsolute", false); 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)); // Texture given? if ((Sidedef.MiddleTexture.Length > 0) && (Sidedef.MiddleTexture[0] != '-')) { // 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(); } // Texture given? if ((Sidedef.MiddleTexture.Length > 0) && (Sidedef.MiddleTexture[0] != '-')) { // Load texture base.Texture = General.Map.Data.GetTextureImage(Sidedef.LongMiddleTexture); if (base.Texture == null) { base.Texture = General.Map.Data.MissingTexture3D; 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; float geotop = (float)Math.Min(Sidedef.Sector.CeilHeight, Sidedef.Other.Sector.CeilHeight); float geobottom = (float)Math.Max(Sidedef.Sector.FloorHeight, Sidedef.Other.Sector.FloorHeight); 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 - (float)(geotop - geobottom); } tp.trb.x = tp.tlt.x + Sidedef.Line.Length; tp.trb.y = tp.tlt.y + ((float)Sidedef.Sector.CeilHeight - ((float)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, (float)Sidedef.Sector.CeilHeight); tp.vrb = new Vector3D(vr.x, vr.y, (float)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; PixelColor wallbrightness = PixelColor.FromInt(mode.CalculateBrightness(lightlevel)); PixelColor wallcolor = PixelColor.Modulate(sd.Ceiling.colorbelow, wallbrightness); 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 if (Sidedef.Fields.ContainsKey("wrapmidtex")) { repeatmidtex = Sidedef.Fields.GetValue("wrapmidtex", false); } else { repeatmidtex = Sidedef.Line.IsFlagSet("wrapmidtex"); } if (!repeatmidtex) { // First determine the visible portion of the texture float textop, texbottom; // Determine top portion height if (Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag)) { textop = geobottom + tof.y + tsz.y; } else { textop = geotop + tof.y; } // Calculate bottom portion height texbottom = textop - 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); } if (poly.Count > 2) { // 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(poly, tp, sd, lightvalue, lightabsolute); if (verts.Count > 0) { // Apply alpha to vertices byte alpha = SetLinedefRenderstyle(true); if (alpha < 255) { for (int i = 0; i < verts.Count; i++) { WorldVertex v = verts[i]; PixelColor c = PixelColor.FromInt(v.c); v.c = c.WithAlpha(alpha).ToInt(); verts[i] = v; } } base.SetVertices(verts); return(true); } } } return(false); }
// This builds the thing geometry. Returns false when nothing was created. public virtual bool Setup() { PixelColor sectorcolor = new PixelColor(255, 255, 255, 255); // Must have a width and height! if ((info.Radius < 0.1f) || (info.Height < 0.1f)) { return(false); } // Find the sector in which the thing resides Thing.DetermineSector(mode.BlockMap); if (sprite != null) { if (Thing.Sector != null) { SectorData sd = mode.GetSectorData(Thing.Sector); SectorLevel level = sd.GetLevelAbove(new Vector3D(Thing.Position.x, Thing.Position.y, Thing.Position.z + Thing.Sector.FloorHeight)); if (level != null) { // Use sector brightness for color shading PixelColor areabrightness = PixelColor.FromInt(mode.CalculateBrightness(level.brightnessbelow)); PixelColor areacolor = PixelColor.Modulate(level.colorbelow, areabrightness); sectorcolor = areacolor.WithAlpha(255); } } // Check if the texture is loaded sprite.LoadImage(); isloaded = sprite.IsImageLoaded; if (isloaded) { float offsetx = 0.0f; float offsety = 0.0f; base.Texture = sprite; // Determine sprite size and offset float radius = sprite.ScaledWidth * 0.5f; float height = sprite.ScaledHeight; if (sprite is SpriteImage) { offsetx = (sprite as SpriteImage).OffsetX - radius; offsety = (sprite as SpriteImage).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; offsetx *= info.SpriteScale.Width; offsety *= info.SpriteScale.Height; // Make vertices WorldVertex[] verts = new WorldVertex[6]; verts[0] = new WorldVertex(-radius + offsetx, 0.0f, 0.0f + offsety, sectorcolor.ToInt(), 0.0f, 1.0f); verts[1] = new WorldVertex(-radius + offsetx, 0.0f, height + offsety, sectorcolor.ToInt(), 0.0f, 0.0f); verts[2] = new WorldVertex(+radius + offsetx, 0.0f, height + offsety, sectorcolor.ToInt(), 1.0f, 0.0f); verts[3] = verts[0]; verts[4] = verts[2]; verts[5] = new WorldVertex(+radius + offsetx, 0.0f, 0.0f + offsety, sectorcolor.ToInt(), 1.0f, 1.0f); SetVertices(verts); } else { base.Texture = General.Map.Data.Hourglass3D; // Determine sprite size float radius = Math.Min(info.Radius, info.Height / 2f); float height = Math.Min(info.Radius * 2f, info.Height); // Make vertices WorldVertex[] verts = new WorldVertex[6]; verts[0] = new WorldVertex(-radius, 0.0f, 0.0f, sectorcolor.ToInt(), 0.0f, 1.0f); verts[1] = new WorldVertex(-radius, 0.0f, height, sectorcolor.ToInt(), 0.0f, 0.0f); verts[2] = new WorldVertex(+radius, 0.0f, height, sectorcolor.ToInt(), 1.0f, 0.0f); verts[3] = verts[0]; verts[4] = verts[2]; verts[5] = new WorldVertex(+radius, 0.0f, 0.0f, sectorcolor.ToInt(), 1.0f, 1.0f); SetVertices(verts); } } // Determine position Vector3D pos = Thing.Position; if (Thing.Type == 9501) { // 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) { // 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); if (Thing.Position.z > 0) { pos.z = sd.Ceiling.plane.GetZ(Thing.Position) - info.Height; } else { pos.z = Thing.Sector.CeilHeight - info.Height; } } pos.z -= Thing.Position.z; // Check if below floor if ((Thing.Sector != null) && (pos.z < Thing.Sector.FloorHeight)) { // Put thing on the floor SectorData sd = mode.GetSectorData(Thing.Sector); pos.z = sd.Floor.plane.GetZ(Thing.Position); } } else { // Stand on floor if (Thing.Sector != null) { SectorData sd = mode.GetSectorData(Thing.Sector); if (Thing.Position.z == 0) { pos.z = sd.Floor.plane.GetZ(Thing.Position); } else { pos.z = Thing.Sector.FloorHeight; } } pos.z += Thing.Position.z; // Check if above ceiling if ((Thing.Sector != null) && ((pos.z + info.Height) > Thing.Sector.CeilHeight)) { // Put thing against ceiling SectorData sd = mode.GetSectorData(Thing.Sector); pos.z = sd.Ceiling.plane.GetZ(Thing.Position) - info.Height; } } // Apply settings SetPosition(pos); SetCageSize(info.Radius, info.Height); SetCageColor(Thing.Color); // Keep info for object picking cageradius2 = info.Radius * Angle2D.SQRT2; cageradius2 = cageradius2 * cageradius2; pos2d = pos; boxp1 = new Vector3D(pos.x - info.Radius, pos.y - info.Radius, pos.z); boxp2 = new Vector3D(pos.x + info.Radius, pos.y + info.Radius, pos.z + info.Height); // Done changed = false; return(true); }
// Constructor protected SectorEffect(SectorData data) { this.data = data; }
// This (re)builds the visual sector, calculating all geometry from scratch public void Rebuild() { // Forget old geometry base.ClearGeometry(); // Get sector data SectorData data = GetSectorData(); if (!data.Updated) { data.Update(); } // Create floor floor = floor ?? new VisualFloor(mode, this); if (floor.Setup(data.Floor, null)) { base.AddGeometry(floor); } // Create ceiling ceiling = ceiling ?? new VisualCeiling(mode, this); if (ceiling.Setup(data.Ceiling, null)) { base.AddGeometry(ceiling); } // Create 3D floors for (int i = 0; i < data.ExtraFloors.Count; i++) { Effect3DFloor ef = data.ExtraFloors[i]; // Create a floor VisualFloor vf = (i < extrafloors.Count) ? extrafloors[i] : new VisualFloor(mode, this); if (vf.Setup(ef.Ceiling, ef)) { base.AddGeometry(vf); } if (i >= extrafloors.Count) { extrafloors.Add(vf); } // Create a ceiling VisualCeiling vc = (i < extraceilings.Count) ? extraceilings[i] : new VisualCeiling(mode, this); if (vc.Setup(ef.Floor, ef)) { base.AddGeometry(vc); } if (i >= extraceilings.Count) { extraceilings.Add(vc); } } // Go for all sidedefs Dictionary <Sidedef, VisualSidedefParts> oldsides = sides ?? new Dictionary <Sidedef, VisualSidedefParts>(1); sides = new Dictionary <Sidedef, VisualSidedefParts>(base.Sector.Sidedefs.Count); foreach (Sidedef sd in base.Sector.Sidedefs) { // VisualSidedef already exists? VisualSidedefParts parts = oldsides.ContainsKey(sd) ? oldsides[sd] : new VisualSidedefParts(); // Doublesided or singlesided? if (sd.Other != null) { // Create upper part VisualUpper vu = parts.upper ?? new VisualUpper(mode, this, sd); if (vu.Setup()) { base.AddGeometry(vu); } // Create lower part VisualLower vl = parts.lower ?? new VisualLower(mode, this, sd); if (vl.Setup()) { base.AddGeometry(vl); } // Create middle part VisualMiddleDouble vm = parts.middledouble ?? new VisualMiddleDouble(mode, this, sd); if (vm.Setup()) { base.AddGeometry(vm); } // Create 3D wall parts SectorData osd = mode.GetSectorData(sd.Other.Sector); if (!osd.Updated) { osd.Update(); } List <VisualMiddle3D> middles = parts.middle3d ?? new List <VisualMiddle3D>(osd.ExtraFloors.Count); for (int i = 0; i < osd.ExtraFloors.Count; i++) { Effect3DFloor ef = osd.ExtraFloors[i]; VisualMiddle3D vm3 = (i < middles.Count) ? middles[i] : new VisualMiddle3D(mode, this, sd); if (vm3.Setup(ef)) { base.AddGeometry(vm3); } if (i >= middles.Count) { middles.Add(vm3); } } // Store sides.Add(sd, new VisualSidedefParts(vu, vl, vm, middles)); } else { // Create middle part VisualMiddleSingle vm = parts.middlesingle ?? new VisualMiddleSingle(mode, this, sd); if (vm.Setup()) { base.AddGeometry(vm); } // Store sides.Add(sd, new VisualSidedefParts(vm)); } } // Done changed = false; }
// 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); // Check if this effect still exists int sectortag = linedef.Args[0] + (linedef.Args[4] << 8); if (linedef.IsDisposed || (linedef.Action != 160) || (sectortag != data.Sector.Tag)) { // When the effect is no longer exists, we must remove it and update sectors } 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 int argtype = (linedef.Args[1] & 0x03); if (argtype != 0) { vavoomtype = false; alpha = linedef.Args[3]; sd.Ceiling.CopyProperties(floor); sd.Floor.CopyProperties(ceiling); floor.type = SectorLevelType.Floor; floor.plane = sd.Ceiling.plane.GetInverted(); ceiling.type = SectorLevelType.Ceiling; ceiling.plane = sd.Floor.plane.GetInverted(); // A 3D floor's color is always that of the sector it is placed in floor.color = 0; } else { vavoomtype = true; /* * sd.Ceiling.CopyProperties(floor); * sd.Floor.CopyProperties(ceiling); */ 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 ceiling.color = 0; } // Apply alpha floor.alpha = alpha; ceiling.alpha = alpha; // Do not adjust light? (works only for non-vavoom types) if (((linedef.Args[2] & 1) != 0) && !vavoomtype) { floor.brightnessbelow = -1; floor.colorbelow = PixelColor.FromInt(0); ceiling.color = 0; ceiling.brightnessbelow = -1; ceiling.colorbelow = PixelColor.FromInt(0); } }
// This builds the geometry. Returns false when no geometry created. public override bool Setup() { Vector2D vl, vr; int lightvalue = Sidedef.Fields.GetValue("light", 0); bool lightabsolute = Sidedef.Fields.GetValue("lightabsolute", false); 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.MiddleTexture.Length > 0) && (Sidedef.MiddleTexture[0] != '-')) { // Load texture base.Texture = General.Map.Data.GetTextureImage(Sidedef.LongMiddleTexture); if (base.Texture == null) { base.Texture = General.Map.Data.MissingTexture3D; 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 - (float)(Sidedef.Sector.CeilHeight - Sidedef.Sector.FloorHeight); } tp.trb.x = tp.tlt.x + Sidedef.Line.Length; tp.trb.y = tp.tlt.y + ((float)Sidedef.Sector.CeilHeight - ((float)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, (float)Sidedef.Sector.CeilHeight); tp.vrb = new Vector3D(vr.x, vr.y, (float)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; PixelColor wallbrightness = PixelColor.FromInt(mode.CalculateBrightness(lightlevel)); PixelColor wallcolor = PixelColor.Modulate(sd.Ceiling.colorbelow, wallbrightness); poly.color = wallcolor.WithAlpha(255).ToInt(); // Process the polygon and create vertices List <WorldVertex> verts = CreatePolygonVertices(poly, tp, sd, lightvalue, lightabsolute); if (verts.Count > 0) { base.SetVertices(verts); return(true); } } return(false); }
// This makes sure we are updated with the source linedef information public override void Update() { Linedef l = linedef; // Find the vertex furthest from the line Vertex foundv = null; float founddist = -1.0f; foreach (Sidedef sd in data.Sector.Sidedefs) { Vertex v = sd.IsFront ? sd.Line.Start : sd.Line.End; float d = l.DistanceToSq(v.Position, false); if (d > founddist) { foundv = v; founddist = d; } } // Align floor with back of line if ((l.Args[0] == 1) && (l.Front.Sector == data.Sector) && (l.Back != null)) { Vector3D v1 = new Vector3D(l.Start.Position.x, l.Start.Position.y, l.Back.Sector.FloorHeight); Vector3D v2 = new Vector3D(l.End.Position.x, l.End.Position.y, l.Back.Sector.FloorHeight); Vector3D v3 = new Vector3D(foundv.Position.x, foundv.Position.y, data.Sector.FloorHeight); if (l.SideOfLine(v3) < 0.0f) { data.Floor.plane = new Plane(v1, v2, v3, true); } else { data.Floor.plane = new Plane(v2, v1, v3, true); } SectorData sd = data.Mode.GetSectorData(l.Back.Sector); sd.AddUpdateSector(data.Sector, true); } // Align floor with front of line else if ((l.Args[0] == 2) && (l.Back.Sector == data.Sector) && (l.Front != null)) { Vector3D v1 = new Vector3D(l.Start.Position.x, l.Start.Position.y, l.Front.Sector.FloorHeight); Vector3D v2 = new Vector3D(l.End.Position.x, l.End.Position.y, l.Front.Sector.FloorHeight); Vector3D v3 = new Vector3D(foundv.Position.x, foundv.Position.y, data.Sector.FloorHeight); if (l.SideOfLine(v3) < 0.0f) { data.Floor.plane = new Plane(v1, v2, v3, true); } else { data.Floor.plane = new Plane(v2, v1, v3, true); } SectorData sd = data.Mode.GetSectorData(l.Front.Sector); sd.AddUpdateSector(data.Sector, true); } // Align ceiling with back of line if ((l.Args[1] == 1) && (l.Front.Sector == data.Sector) && (l.Back != null)) { Vector3D v1 = new Vector3D(l.Start.Position.x, l.Start.Position.y, l.Back.Sector.CeilHeight); Vector3D v2 = new Vector3D(l.End.Position.x, l.End.Position.y, l.Back.Sector.CeilHeight); Vector3D v3 = new Vector3D(foundv.Position.x, foundv.Position.y, data.Sector.CeilHeight); if (l.SideOfLine(v3) > 0.0f) { data.Ceiling.plane = new Plane(v1, v2, v3, false); } else { data.Ceiling.plane = new Plane(v2, v1, v3, false); } SectorData sd = data.Mode.GetSectorData(l.Back.Sector); sd.AddUpdateSector(data.Sector, true); } // Align ceiling with front of line else if ((l.Args[1] == 2) && (l.Back.Sector == data.Sector) && (l.Front != null)) { Vector3D v1 = new Vector3D(l.Start.Position.x, l.Start.Position.y, l.Front.Sector.CeilHeight); Vector3D v2 = new Vector3D(l.End.Position.x, l.End.Position.y, l.Front.Sector.CeilHeight); Vector3D v3 = new Vector3D(foundv.Position.x, foundv.Position.y, data.Sector.CeilHeight); if (l.SideOfLine(v3) > 0.0f) { data.Ceiling.plane = new Plane(v1, v2, v3, false); } else { data.Ceiling.plane = new Plane(v2, v1, v3, false); } SectorData sd = data.Mode.GetSectorData(l.Front.Sector); sd.AddUpdateSector(data.Sector, true); } }