//mxd. Normals calculation algorithm taken from OpenGl wiki private void CalculateNormals() { if (triangles == 0) { return; } BoundingBoxSizes bbs = new BoundingBoxSizes(vertices[0]); for (int i = 0; i < triangles; i++) { int startindex = i * 3; WorldVertex p1 = vertices[startindex]; WorldVertex p2 = vertices[startindex + 1]; WorldVertex p3 = vertices[startindex + 2]; Vector3f U = new Vector3f(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z); Vector3f V = new Vector3f(p3.x - p1.x, p3.y - p1.y, p3.z - p1.z); p1.nx = p2.nx = p3.nx = -(U.Y * V.Z - U.Z * V.Y); p1.ny = p2.ny = p3.ny = -(U.Z * V.X - U.X * V.Z); p1.nz = p2.nz = p3.nz = -(U.X * V.Y - U.Y * V.X); vertices[startindex] = p1; vertices[startindex + 1] = p2; vertices[startindex + 2] = p3; BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, p1); BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, p2); BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, p3); } boundingBox = BoundingBoxTools.CalculateBoundingPlane(bbs); }
public static void UpdateBoundingBoxSizes(ref BoundingBoxSizes bbs, WorldVertex v) { if (v.x < bbs.MinX) { bbs.MinX = (int)v.x; } else if (v.x > bbs.MaxX) { bbs.MaxX = (int)v.x; } if (v.z < bbs.MinZ) { bbs.MinZ = (int)v.z; } else if (v.z > bbs.MaxZ) { bbs.MaxZ = (int)v.z; } if (v.y < bbs.MinY) { bbs.MinY = (int)v.y; } else if (v.y > bbs.MaxY) { bbs.MaxY = (int)v.y; } }
// Returns index of added vert private static int AddVertex(WorldVertex v, List <WorldVertex> verts, List <int> indices, Dictionary <long, int> hashes) { long hash; unchecked // Overflow is fine, just wrap { hash = 2166136261; hash = (hash * 16777619) ^ v.x.GetHashCode(); hash = (hash * 16777619) ^ v.y.GetHashCode(); hash = (hash * 16777619) ^ v.z.GetHashCode(); hash = (hash * 16777619) ^ v.u.GetHashCode(); hash = (hash * 16777619) ^ v.v.GetHashCode(); } if (hashes.ContainsKey(hash)) { indices.Add(hashes[hash]); return(hashes[hash]); } else { verts.Add(v); hashes.Add(hash, verts.Count - 1); indices.Add(verts.Count - 1); return(verts.Count - 1); } }
public void WriteWorldVertex(WorldVertex v) { _stream.WriteFloat3(v.Position.XYZ); _stream.WriteFloat2(v.Texcoord); _stream.WriteFloat3(v.Normal); _stream.WriteFloat3(v.Tangent.XYZ); _stream.WriteFloat3(v.Binormal); }
// Shameless GZDoom rip-off private static void AddFace(List <WorldVertex> verts, List <int> indices, Dictionary <long, int> hashes, Vector3D v1, Vector3D v2, Vector3D v3, Vector3D v4, Vector3D pivot, int colorIndex) { float pu0 = (colorIndex % 16) / 16f; float pu1 = pu0 + 0.001f; float pv0 = (colorIndex / 16) / 16f; float pv1 = pv0 + 0.001f; WorldVertex wv1 = new WorldVertex { x = v1.x - pivot.x, y = -v1.y + pivot.y, z = -v1.z + pivot.z, c = -1, u = pu0, v = pv0 }; int i1 = AddVertex(wv1, verts, indices, hashes); WorldVertex wv2 = new WorldVertex { x = v2.x - pivot.x, y = -v2.y + pivot.y, z = -v2.z + pivot.z, c = -1, u = pu1, v = pv1 }; AddVertex(wv2, verts, indices, hashes); WorldVertex wv4 = new WorldVertex { x = v4.x - pivot.x, y = -v4.y + pivot.y, z = -v4.z + pivot.z, c = -1, u = pu0, v = pv0 }; int i4 = AddVertex(wv4, verts, indices, hashes); WorldVertex wv3 = new WorldVertex { x = v3.x - pivot.x, y = -v3.y + pivot.y, z = -v3.z + pivot.z, c = -1, u = pu1, v = pv1 }; AddVertex(wv3, verts, indices, hashes); indices.Add(i1); indices.Add(i4); }
// This swaps triangles so that the plane faces the other way protected static void SwapTriangleVertices(WorldVertex[] verts) { // Swap some vertices to flip all triangles for (int i = 0; i < verts.Length; i += 3) { // Swap WorldVertex v = verts[i]; verts[i] = verts[i + 1]; verts[i + 1] = v; } }
// This builds the geometry. Returns false when no geometry created. public override bool Setup() { WorldVertex[] verts; Sector s = base.Sector.Sector; int brightness; // Load floor texture base.Texture = General.Map.Data.GetFlatImage(s.LongFloorTexture); if (base.Texture == null) { brightness = mode.CalculateBrightness(256); base.Texture = General.Map.Data.MissingTexture3D; setuponloadedtexture = s.LongFloorTexture; } else { brightness = mode.CalculateBrightness(s.Brightness); if (!base.Texture.IsImageLoaded) { setuponloadedtexture = s.LongFloorTexture; } } // Make vertices verts = new WorldVertex[s.Triangles.Vertices.Count]; for (int i = 0; i < s.Triangles.Vertices.Count; i++) { // Use sector brightness for color shading verts[i].c = brightness; // Grid aligned texture coordinates if (base.Texture.IsImageLoaded) { verts[i].u = s.Triangles.Vertices[i].x / base.Texture.ScaledWidth; verts[i].v = -s.Triangles.Vertices[i].y / base.Texture.ScaledHeight; } else { verts[i].u = s.Triangles.Vertices[i].x / 64; verts[i].v = -s.Triangles.Vertices[i].y / 64; } // Vertex coordinates verts[i].x = s.Triangles.Vertices[i].x; verts[i].y = s.Triangles.Vertices[i].y; verts[i].z = (float)s.FloorHeight; } // Apply vertices base.SetVertices(verts); return(verts.Length > 0); }
// This builds the geometry. Returns false when no geometry created. public override bool Setup() { Sector s = base.Sector.Sector; // Load ceiling image Texture = General.Map.Data.GetImageData(s.CeilingTileIndex); if (Texture is UnknownImage) { // Use missing texture //Texture = General.Map.Data.MissingTexture3D; setuponloadedtexture = -1; } else if (!Texture.IsImageLoaded) { setuponloadedtexture = s.CeilingTileIndex; } // Make vertices WorldVertex[] verts = new WorldVertex[s.FloorVertices.Length]; if (s.CeilingSloped) { for (int i = 0; i < s.CeilingVertices.Length; i++) { var v = s.CeilingVertices[i]; verts[i] = new WorldVertex(v, s.CeilingPlane.GetZ(v.x, v.y)); } } else { for (int i = 0; i < s.CeilingVertices.Length; i++) { verts[i] = new WorldVertex(s.CeilingVertices[i], s.CeilingHeight); } } // The sector triangulation created clockwise triangles that // are right up for the floor. For the ceiling we must flip the triangles upside down. for (int i = 0; i < verts.Length; i += 3) { // Swap some vertices to flip the triangle WorldVertex v = verts[i]; verts[i] = verts[i + 1]; verts[i + 1] = v; } // Apply vertices base.SetVertices(verts); return(verts.Length > 0); }
// This builds the geometry. Returns false when no geometry created. public override bool Setup() { Sector s = base.Sector.Sector; // Load floor image Texture = General.Map.Data.GetImageData(s.FloorTileIndex); if (Texture is UnknownImage) { // Use missing texture //Texture = General.Map.Data.MissingTexture3D; setuponloadedtexture = -1; } else if (!Texture.IsImageLoaded) { setuponloadedtexture = s.FloorTileIndex; } // Make vertices WorldVertex[] verts = new WorldVertex[s.FloorVertices.Length]; if (s.FloorSloped) { for (int i = 0; i < s.FloorVertices.Length; i++) { var v = s.FloorVertices[i]; verts[i] = new WorldVertex(v, s.FloorPlane.GetZ(v.x, v.y)); } } else { for (int i = 0; i < s.FloorVertices.Length; i++) { verts[i] = new WorldVertex(s.FloorVertices[i], s.FloorHeight); } } // Apply vertices base.SetVertices(verts); return(verts.Length > 0); }
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 builds the geometry. Returns false when no geometry created. public override bool Setup(SectorLevel level, Effect3DFloor extrafloor) { WorldVertex[] verts; Sector s = level.sector; Vector2D texscale; 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 floor texture base.Texture = General.Map.Data.GetFlatImage(s.LongCeilTexture); if (base.Texture == null) { base.Texture = General.Map.Data.MissingTexture3D; setuponloadedtexture = s.LongCeilTexture; } else { if (!base.Texture.IsImageLoaded) { setuponloadedtexture = s.LongCeilTexture; } } // 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); } // Make vertices ReadOnlyCollection <Vector2D> triverts = base.Sector.Sector.Triangles.Vertices; verts = new WorldVertex[triverts.Count]; for (int i = 0; i < triverts.Count; i++) { // Color shading PixelColor c = PixelColor.FromInt(level.color); verts[i].c = c.WithAlpha((byte)General.Clamp(level.alpha, 0, 255)).ToInt(); // Vertex coordinates verts[i].x = triverts[i].x; verts[i].y = triverts[i].y; verts[i].z = level.plane.GetZ(triverts[i]); //(float)s.CeilHeight; // 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) { SwapTriangleVertices(verts); } // Determine render pass if (extrafloor != null) { if (level.alpha < 255) { this.RenderPass = RenderPass.Alpha; } else { this.RenderPass = RenderPass.Mask; } } else { this.RenderPass = RenderPass.Solid; } // Apply vertices base.SetVertices(verts); return(verts.Length > 0); }
public void WriteWorldVertex(WorldVertex v) { _stream.WriteFloat4(v.Position); _stream.WriteFloat2(v.Texcoord); _stream.WriteUByte4N(v.Tangent); }
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); }
//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.ToLowerInvariant(); alpha = info.AlphaByte; if (General.Map.UDMF) { if (Thing.IsFlagSet("translucent")) { renderstyle = "translucent"; alpha = 64; } else if (Thing.IsFlagSet("invisible")) { renderstyle = "none"; alpha = 0; } else if (Thing.Fields.ContainsKey("renderstyle")) { renderstyle = Thing.Fields.GetValue("renderstyle", renderstyle).ToLowerInvariant(); } if ((renderstyle == "add" || renderstyle == "translucent" || renderstyle == "subtract" || renderstyle == "translucentstencil") && Thing.Fields.ContainsKey("alpha")) { alpha = (byte)(General.Clamp(Thing.Fields.GetValue("alpha", info.Alpha), 0.0, 1.0) * 255.0); } else if (renderstyle == "soultrans") { // Lost Soul trasparency is controlled by a CVAR (see https://zdoom.org/wiki/CVARs:Display#transsouls), let's use the default 0.75 here alpha = 192; } else if (renderstyle == "shadow") { alpha = 76; // about 0.3 stencilColor = PixelColor.FromInt(PixelColor.INT_BLACK); } if (renderstyle.EndsWith("stencil")) { stencilColor = PixelColor.FromInt(UniFields.GetInteger(Thing.Fields, "fillcolor", 0)); stencilColor.a = 255; // 0xFF alpha means nothing was read. 0x00 alpha means there was a valid fillcolor. } else if (renderstyle != "shadow") { stencilColor.a = 0; } } else if (General.Map.HEXEN) { if (Thing.IsFlagSet("2048")) { renderstyle = "translucent"; alpha = 64; } else if (Thing.IsFlagSet("4096")) { renderstyle = "none"; alpha = 0; } } // Set appropriate RenderPass switch (renderstyle) { case "translucent": case "subtract": case "soultrans": case "translucentstencil": case "shadow": RenderPass = RenderPass.Alpha; break; case "add": case "addstencil": RenderPass = RenderPass.Additive; break; case "none": RenderPass = RenderPass.Mask; alpha = 0; break; // Many render styles are not supported yet... default: RenderPass = RenderPass.Mask; alpha = 255; break; } } int sectorcolor = new PixelColor(alpha, 255, 255, 255).ToInt(); fogfactor = 0f; //mxd //mxd. Check thing size float thingradius = Thing.Size; // Thing.Size has ThingRadius arg override applied thingheight = Thing.Height; // Thing.Height has ThingHeight arg override applied if (thingradius < 0.1f || thingheight < 0.1f) { thingradius = FIXED_RADIUS; thingheight = FIXED_RADIUS; sizeless = true; } else { sizeless = false; } if (Thing.Sector != null) { SectorData sd = mode.GetSectorData(Thing.Sector); Plane floor = sd.Floor.plane; //mxd if (!info.Bright) { Vector3D thingpos = new Vector3D(Thing.Position.x, Thing.Position.y, Thing.Position.z + sd.Floor.plane.GetZ(Thing.Position)); SectorLevel level = sd.GetLevelAboveOrAt(thingpos); //mxd. Let's use point on floor plane instead of Thing.Sector.FloorHeight; if (nointeraction && level == null && sd.LightLevels.Count > 0) { level = sd.LightLevels[sd.LightLevels.Count - 1]; } //mxd. Use the light level of the highest surface when a thing is above highest sector level. if (level != null) { // TECH: In GZDoom, ceiling glow doesn't affect thing brightness // Use sector brightness for color shading int brightness = level.brightnessbelow; //mxd. Apply lightfloor value // According to Graf, this is incorrect behaviour... // TECH: In (G)ZDoom, this is ignored when ceiling texture is sky or a thing is below a 3D floor // It's probably more involved than this, but for now let's do it only when there are no 3d floors in Thing.Sector... /*if(General.Map.UDMF && sd.LightLevels.Count == 2 && Thing.Sector.CeilTexture != General.Map.Config.SkyFlatName) * { * if(sd.Sector.Fields.GetValue("lightfloorabsolute", false)) * brightness = UniFields.GetInteger(sd.Sector.Fields, "lightfloor"); * else * brightness += UniFields.GetInteger(sd.Sector.Fields, "lightfloor"); * }*/ // Level is glowing if (level.affectedbyglow && level.type == SectorLevelType.Floor) { // Extrafloor glow doesn't affect thing brightness if (level.sector == Thing.Sector) { double planez = level.plane.GetZ(thingpos); // Get glow brightness int glowbrightness = sd.FloorGlow.Brightness / 2; SectorLevel nexthigher = sd.GetLevelAbove(new Vector3D(thingpos, planez)); // Interpolate thing brightness between glow and regular ones if (nexthigher != null) { double higherz = nexthigher.plane.GetZ(thingpos); double delta = General.Clamp(1.0f - (thingpos.z - planez) / (higherz - planez), 0f, 1f); brightness = (int)((glowbrightness + level.sector.Brightness / 2) * delta + nexthigher.sector.Brightness * (1.0f - delta)); } } } // Level below this one is glowing. Only possible for floor glow(?) else if (level.type == SectorLevelType.Glow) { // Interpolate thing brightness between glow and regular ones if (sd.Floor != null && sd.FloorGlow != null) { // Get glow brightness double glowz = level.plane.GetZ(thingpos); double floorz = floor.GetZ(thingpos); double delta = General.Clamp((thingpos.z - floorz) / (glowz - floorz), 0f, 1f); brightness = (int)((sd.FloorGlow.Brightness / 2 + sd.Floor.sector.Brightness / 2) * (1.0f - delta) + sd.Floor.sector.Brightness * delta); } } PixelColor areabrightness = PixelColor.FromInt(mode.CalculateBrightness(brightness)); PixelColor areacolor = PixelColor.Modulate(level.colorbelow, areabrightness); // [ZZ] if sector is using Doom64 lighting, apply thing color here. sectorcolor = PixelColor.Modulate(sd.ColorSprites, areacolor).WithAlpha(alpha).ToInt(); //mxd. Calculate fogfactor fogfactor = VisualGeometry.CalculateFogFactor(level.sector, brightness); } } //TECH: even Bright Thing frames are affected by custom fade... else { Vector3D thingpos = new Vector3D(Thing.Position.x, Thing.Position.y, Thing.Position.z + sd.Floor.plane.GetZ(Thing.Position)); SectorLevel level = sd.GetLevelAboveOrAt(thingpos); if (level != null && level.sector.FogMode > SectorFogMode.CLASSIC) { //mxd. Calculate fogfactor fogfactor = VisualGeometry.CalculateFogFactor(level.sector, level.brightnessbelow); } } } //mxd. Create verts for all sprite angles WorldVertex[][] allverts = new WorldVertex[info.SpriteFrame.Length][]; Vector2D[] alloffsets = new Vector2D[info.SpriteFrame.Length]; base.textures = new ImageData[info.SpriteFrame.Length]; isloaded = true; for (int i = 0; i < sprites.Length; i++) { Vector2D offsets = new Vector2D(); // Check if the texture is loaded ImageData sprite = sprites[i]; if (!sprite.IsImageLoaded && !sprite.LoadFailed) { sprite.LoadImageNow(); } if (sprite.IsImageLoaded) { base.textures[i] = sprite; // Determine sprite size and offset float radius = sprite.ScaledWidth * 0.5f; float height = sprite.ScaledHeight; ISpriteImage spriteimg = sprite as ISpriteImage; if (spriteimg != null) { offsets.x = radius - spriteimg.OffsetX; offsets.y = spriteimg.OffsetY - height; } // Scale by thing type/actor scale // We do this after the offset x/y determination above, because that is entirely in sprite pixels space radius *= info.SpriteScale.Width; height *= info.SpriteScale.Height; offsets.x *= info.SpriteScale.Width; offsets.y *= info.SpriteScale.Height; // Make vertices WorldVertex[] verts = new WorldVertex[6]; //mxd. Sprite mirroring float ul = (info.SpriteFrame[i].Mirror ? 1f : 0f); float ur = (info.SpriteFrame[i].Mirror ? 0f : 1f); if (sizeless) //mxd { float hh = height / 2; verts[0] = new WorldVertex((float)(-radius + offsets.x), 0.0f, (float)(offsets.y - hh), sectorcolor, ul, 1.0f); verts[1] = new WorldVertex((float)(-radius + offsets.x), 0.0f, (float)(hh + offsets.y), sectorcolor, ul, 0.0f); verts[2] = new WorldVertex((float)(+radius + offsets.x), 0.0f, (float)(hh + offsets.y), sectorcolor, ur, 0.0f); verts[3] = verts[0]; verts[4] = verts[2]; verts[5] = new WorldVertex((float)(+radius + offsets.x), 0.0f, (float)(offsets.y - hh), sectorcolor, ur, 1.0f); } else { verts[0] = new WorldVertex((float)(-radius + offsets.x), 0.0f, (float)offsets.y, sectorcolor, ul, 1.0f); verts[1] = new WorldVertex((float)(-radius + offsets.x), 0.0f, (float)(height + offsets.y), sectorcolor, ul, 0.0f); verts[2] = new WorldVertex((float)(+radius + offsets.x), 0.0f, (float)(height + offsets.y), sectorcolor, ur, 0.0f); verts[3] = verts[0]; verts[4] = verts[2]; verts[5] = new WorldVertex((float)(+radius + offsets.x), 0.0f, (float)offsets.y, sectorcolor, ur, 1.0f); } allverts[i] = verts; } else { isloaded = false; base.textures[i] = sprite; // Determine sprite size float radius = Math.Min(thingradius, thingheight / 2f); float height = Math.Min(thingradius * 2f, thingheight); //mxd. Determine sprite offsets offsets.x = radius; offsets.y = height / 2; // Make vertices WorldVertex[] verts = new WorldVertex[6]; verts[0] = new WorldVertex(-radius, 0.0f, 0.0f, sectorcolor, 0.0f, 1.0f); verts[1] = new WorldVertex(-radius, 0.0f, height, sectorcolor, 0.0f, 0.0f); verts[2] = new WorldVertex(+radius, 0.0f, height, sectorcolor, 1.0f, 0.0f); verts[3] = verts[0]; verts[4] = verts[2]; verts[5] = new WorldVertex(+radius, 0.0f, 0.0f, sectorcolor, 1.0f, 1.0f); allverts[i] = verts; } //mxd. Store offsets alloffsets[i] = offsets; } //mxd SetVertices(allverts, alloffsets /*, floor, ceiling*/); // Determine position Vector3D pos = Thing.Position; if (Thing.Type == 9501) { if (Thing.Sector != null) //mxd { // This is a special thing that needs special positioning SectorData sd = mode.GetSectorData(Thing.Sector); pos.z = sd.Ceiling.sector.CeilHeight + Thing.Position.z; } } else if (Thing.Type == 9500) { if (Thing.Sector != null) //mxd { // This is a special thing that needs special positioning SectorData sd = mode.GetSectorData(Thing.Sector); pos.z = sd.Floor.sector.FloorHeight + Thing.Position.z; } } else if (info.AbsoluteZ) { // Absolute Z position pos.z = Thing.Position.z; } else if (info.Hangs) { // Hang from ceiling if (Thing.Sector != null) { SectorData sd = mode.GetSectorData(Thing.Sector); double maxz = sd.Ceiling.plane.GetZ(Thing.Position) - info.Height; pos.z = maxz; if (Thing.Position.z > 0 || nointeraction) { pos.z -= Thing.Position.z; } // Check if below floor if (!nointeraction) { double minz = sd.Floor.plane.GetZ(Thing.Position); if (pos.z < minz) { pos.z = Math.Min(minz, maxz); } } } } else { // Stand on floor if (Thing.Sector != null) { SectorData sd = mode.GetSectorData(Thing.Sector); double minz = sd.Floor.plane.GetZ(Thing.Position); pos.z = minz; if (Thing.Position.z > 0 || nointeraction) { pos.z += Thing.Position.z; } // Check if above ceiling if (!nointeraction) { double maxz = sd.Ceiling.plane.GetZ(Thing.Position) - info.Height; if (pos.z > maxz) { pos.z = Math.Max(minz, maxz); } } } } // Apply settings SetPosition(pos); SetCageColor(Thing.Color); // Keep info for object picking cageradius2 = thingradius * Angle2D.SQRT2; cageradius2 = cageradius2 * cageradius2; pos2d = pos; if (sizeless) //mxd { boxp1 = new Vector3D(pos.x - thingradius, pos.y - thingradius, pos.z - thingradius / 2); boxp2 = new Vector3D(pos.x + thingradius, pos.y + thingradius, pos.z + thingradius / 2); } else { boxp1 = new Vector3D(pos.x - thingradius, pos.y - thingradius, pos.z); boxp2 = new Vector3D(pos.x + thingradius, pos.y + thingradius, pos.z + thingheight); } // Done changed = false; return(true); }
// This builds the geometry. Returns false when no geometry created. public override bool Setup() { WorldVertex[] verts; WorldVertex v; Sector s = base.Sector.Sector; int brightness; // Load floor texture base.Texture = General.Map.Data.GetFlatImage(s.LongCeilTexture); if (base.Texture == null) { brightness = mode.CalculateBrightness(256); base.Texture = General.Map.Data.MissingTexture3D; setuponloadedtexture = s.LongCeilTexture; } else { brightness = mode.CalculateBrightness(s.Brightness); if (!base.Texture.IsImageLoaded) { setuponloadedtexture = s.LongCeilTexture; } } // Make vertices verts = new WorldVertex[s.Triangles.Vertices.Count]; for (int i = 0; i < s.Triangles.Vertices.Count; i++) { // Use sector brightness for color shading verts[i].c = brightness; // Grid aligned texture coordinates if (base.Texture.IsImageLoaded) { verts[i].u = s.Triangles.Vertices[i].x / base.Texture.ScaledWidth; verts[i].v = -s.Triangles.Vertices[i].y / base.Texture.ScaledHeight; } else { verts[i].u = s.Triangles.Vertices[i].x / 64; verts[i].v = -s.Triangles.Vertices[i].y / 64; } // Vertex coordinates verts[i].x = s.Triangles.Vertices[i].x; verts[i].y = s.Triangles.Vertices[i].y; verts[i].z = (float)s.CeilHeight; } // The sector triangulation created clockwise triangles that // are right up for the floor. For the ceiling we must flip // the triangles upside down. // Swap some vertices to flip all triangles for (int i = 0; i < verts.Length; i += 3) { // Swap v = verts[i]; verts[i] = verts[i + 1]; verts[i + 1] = v; } // Apply vertices base.SetVertices(verts); return(verts.Length > 0); }
// This builds the geometry. Returns false when no geometry created. public override bool Setup() { // Left and right vertices for this wall Vector2D vl, vr; if (Sidedef.IsFront) { vl = new Vector2D(Sidedef.Line.Start.Position); vr = new Vector2D(Sidedef.Line.End.Position); } else { vl = new Vector2D(Sidedef.Line.End.Position); vr = new Vector2D(Sidedef.Line.Start.Position); } // Keep top and bottom planes for intersection testing top = Sidedef.Sector.CeilingPlane; bottom = Sidedef.Sector.FloorPlane; // Get ceiling and floor heights float cl = top.GetZ(vl); float cr = top.GetZ(vr); float fl = bottom.GetZ(vl); float fr = bottom.GetZ(vr); // Anything to see? if (cl - fl > 0.01f || cr - fr > 0.01f) { int brightness = MapElement.CalculateBrightness(Sidedef.Shade); // Texture given? Texture = General.Map.Data.GetImageData(Sidedef.TileIndex); if (Texture is UnknownImage) { // Use missing texture //Texture = General.Map.Data.MissingTexture3D; setuponloadedimage = -1; } else if (!Texture.IsImageLoaded) { setuponloadedimage = Sidedef.TileIndex; } WorldVertex[] verts = new WorldVertex[6]; verts[0] = new WorldVertex(vl.x, vl.y, fl, brightness, 0, 0); verts[1] = new WorldVertex(vl.x, vl.y, cl, brightness, 0, 0); verts[2] = new WorldVertex(vr.x, vr.y, cr, brightness, 0, 0); verts[3] = verts[0]; verts[4] = verts[2]; verts[5] = new WorldVertex(vr.x, vr.y, fr, brightness, 0, 0); //mxd. Set UV coords the Build way... int xref = (!Sidedef.ImageFlipX ? 1 : 0); int xrefinv = 1 - xref; int yref = (Sidedef.AlignImageToBottom ? Sidedef.Sector.FloorHeight : Sidedef.Sector.CeilingHeight); float ypancoef = CalculateOffsetV(Sidedef.OffsetY, Texture, !Sidedef.AlignImageToBottom); float scaledtexrepeaty = ((Texture.Height * 128f) / Sidedef.RepeatY); for (int i = 0; i < 6; i++) { float dist = ((i == 2 || i == 4 || i == 5) ? xref : xrefinv); verts[i].u = ((dist * 8.0f * Sidedef.RepeatX) + Sidedef.OffsetX) / Texture.Width; // w->wall.buffer[i].v = (-(float)(yref + (w->wall.buffer[i].y * 16)) / ((tilesiz[curpicnum].y * 2048.0f) / (float)(wal->yrepeat))) + ypancoef; verts[i].v = ((yref + (-verts[i].z)) / scaledtexrepeaty) - ypancoef; if (Sidedef.ImageFlipY) { verts[i].v *= -1; } } // Apply vertices SetVertices(verts); return(true); } // No geometry for invisible wall SetVertices(new WorldVertex[0]); return(false); }
// This sets the vertices for the thing sprite protected void SetVertices(WorldVertex[][] verts, Vector2D[] offsets /*, Plane floor, Plane ceiling*/) { // Copy vertices vertices = new WorldVertex[verts.Length][]; triangles = new int[verts.Length]; //mxd for (int i = 0; i < verts.Length; i++) { vertices[i] = new WorldVertex[verts[i].Length]; verts[i].CopyTo(vertices[i], 0); triangles[i] = vertices[i].Length / 3; } updategeo = true; //mxd. Do some special GZDoom rendering shenanigans... for (int c = 0; c < vertices.Length; c++) { if (triangles[c] < 2) { continue; } Matrix transform, rotation; float centerx, centerz; // ROLLCENTER flag support if (info.RollSprite && info.RollCenter && thing.Roll != 0) { // Rotate around sprite center centerx = offsets[c].x; centerz = vertices[c][1].z * 0.5f - offsets[c].y; } else { // Sprite center is already where it needs to be centerx = 0f; centerz = 0f; } switch (thing.RenderMode) { // Don't do anything case ThingRenderMode.MODEL: break; case ThingRenderMode.VOXEL: break; // Actor becomes a flat sprite which can be tilted with the use of the Pitch actor property. case ThingRenderMode.FLATSPRITE: transform = Matrix.Scaling(thing.ScaleX, thing.ScaleX, thing.ScaleY); // Apply roll? if (thing.Roll != 0) { if (info.RollCenter) { rotation = Matrix.RotationY(-thing.RollRad); transform *= Matrix.Translation(-centerx, -centerx, -centerz) * rotation * Matrix.Translation(centerx, centerx, centerz); } else { // Sprite center is already where it needs to be transform *= Matrix.RotationY(-thing.RollRad); } } // Apply pitch transform *= Matrix.RotationX(thing.PitchRad + Angle2D.PIHALF); // Apply angle transform *= Matrix.RotationZ(thing.Angle); // Apply transform float zoffset = ((thing.Pitch == 0f && thing.Position.z == 0f) ? 0.1f : 0f); // Slight offset to avoid z-fighting... for (int i = 0; i < vertices[c].Length; i++) { Vector4 transformed = Vector3.Transform(new Vector3(vertices[c][i].x, vertices[c][i].y, vertices[c][i].z), transform); vertices[c][i].x = transformed.X; vertices[c][i].y = transformed.Y; vertices[c][i].z = transformed.Z + zoffset; } break; // Similar to FLATSPRITE but is not affected by pitch. case ThingRenderMode.WALLSPRITE: transform = Matrix.Scaling(thing.ScaleX, thing.ScaleX, thing.ScaleY); // Apply roll? if (thing.Roll != 0) { rotation = Matrix.RotationY(-thing.RollRad) * Matrix.RotationZ(thing.Angle); if (info.RollCenter) { transform *= Matrix.Translation(-centerx, -centerx, -centerz) * rotation * Matrix.Translation(centerx, centerx, centerz); } else { transform *= rotation; // Sprite center is already where it needs to be } } else { transform *= Matrix.RotationZ(thing.Angle); } // Apply transform for (int i = 0; i < vertices[c].Length; i++) { Vector4 transformed = Vector3.Transform(new Vector3(vertices[c][i].x, vertices[c][i].y, vertices[c][i].z), transform); vertices[c][i].x = transformed.X; vertices[c][i].y = transformed.Y; vertices[c][i].z = transformed.Z; } break; #region Some old GLOOME FLOOR_SPRITE/CEILING_SPRITE support code /*case Thing.SpriteRenderMode.FLOOR_SPRITE: * Matrix floorrotation = Matrix.RotationZ(info.RollSprite ? Thing.RollRad : 0f) * Matrix.RotationY(Thing.Angle) * Matrix.RotationX(Angle2D.PIHALF); * * m = Matrix.Translation(0f, 0f, -localcenterz) * floorrotation * Matrix.Translation(0f, 0f, localcenterz); * * for(int i = 0; i < vertices[c].Length; i++) * { * Vector4 transformed = Vector3.Transform(new Vector3(vertices[c][i].x, vertices[c][i].y, vertices[c][i].z), m); * vertices[c][i].x = transformed.X; * vertices[c][i].y = transformed.Y; * vertices[c][i].z = transformed.Z; * } * * // TODO: this won't work on things with AbsoluteZ flag * // TODO: +ROLLSPRITE implies +STICKTOPLANE? * if(info.StickToPlane || info.RollSprite) * { * // Calculate vertical offset * float floorz = floor.GetZ(Thing.Position); * float ceilz = ceiling.GetZ(Thing.Position); * * if(!float.IsNaN(floorz) && !float.IsNaN(ceilz)) * { * float voffset; * if(info.Hangs) * { * float thingz = ceilz - Thing.Position.z + Thing.Height; * voffset = 0.01f - floorz - General.Clamp(thingz, 0, ceilz - floorz); * } * else * { * voffset = 0.01f - floorz - General.Clamp(Thing.Position.z, 0, ceilz - floorz); * } * * // Apply it * for(int i = 0; i < vertices[c].Length; i++) * vertices[c][i].z = floor.GetZ(vertices[c][i].x + Thing.Position.x, vertices[c][i].y + Thing.Position.y) + voffset; * } * } * break; * * case Thing.SpriteRenderMode.CEILING_SPRITE: * Matrix ceilrotation = Matrix.RotationZ(info.RollSprite ? Thing.RollRad : 0f) * Matrix.RotationY(Thing.Angle) * Matrix.RotationX(Angle2D.PIHALF); * * m = Matrix.Translation(0f, 0f, -localcenterz) * ceilrotation * Matrix.Translation(0f, 0f, localcenterz); * * for(int i = 0; i < vertices[c].Length; i++) * { * Vector4 transformed = Vector3.Transform(new Vector3(vertices[c][i].x, vertices[c][i].y, vertices[c][i].z), m); * vertices[c][i].x = transformed.X; * vertices[c][i].y = transformed.Y; * vertices[c][i].z = transformed.Z; * } * * // TODO: this won't work on things with AbsoluteZ flag * // TODO: +ROLLSPRITE implies +STICKTOPLANE? * if(info.StickToPlane || info.RollSprite) * { * // Calculate vertical offset * float floorz = floor.GetZ(Thing.Position); * float ceilz = ceiling.GetZ(Thing.Position); * * if(!float.IsNaN(floorz) && !float.IsNaN(ceilz)) * { * float voffset; * if(info.Hangs) * { * float thingz = ceilz - Math.Max(0, Thing.Position.z) - Thing.Height; * voffset = -0.01f - General.Clamp(thingz, 0, ceilz - floorz); * } * else * { * voffset = -0.01f - floorz - General.Clamp(Thing.Position.z, 0, ceilz - floorz); * } * * // Apply it * for(int i = 0; i < vertices[c].Length; i++) * vertices[c][i].z = ceiling.GetZ(vertices[c][i].x + Thing.Position.x, vertices[c][i].y + Thing.Position.y) + voffset; * } * } * break;*/ #endregion case ThingRenderMode.NORMAL: transform = Matrix.Scaling(thing.ScaleX, thing.ScaleX, thing.ScaleY); // Apply roll? if (info.RollSprite && thing.Roll != 0) { rotation = Matrix.RotationY(-thing.RollRad); if (info.RollCenter) { transform *= Matrix.Translation(-centerx, -centerx, -centerz) * rotation * Matrix.Translation(centerx, centerx, centerz); } else { transform *= rotation; // Sprite center is already where it needs to be } } // Apply transform for (int i = 0; i < vertices[c].Length; i++) { Vector4 transformed = Vector3.Transform(new Vector3(vertices[c][i].x, vertices[c][i].y, vertices[c][i].z), transform); vertices[c][i].x = transformed.X; vertices[c][i].y = transformed.Y; vertices[c][i].z = transformed.Z; } break; default: throw new NotImplementedException("Unknown ThingRenderMode"); } } }
// 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() { 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); }
private static MD3LoadResult ReadMD2Model(ref BoundingBoxSizes bbs, Stream s, Device device, int frame, string framename) { long start = s.Position; MD3LoadResult result = new MD3LoadResult(); using (var br = new BinaryReader(s, Encoding.ASCII)) { string magic = ReadString(br, 4); if (magic != "IDP2") //magic number: "IDP2" { result.Errors = "unknown header: expected \"IDP2\", but got \"" + magic + "\""; return(result); } int modelVersion = br.ReadInt32(); if (modelVersion != 8) //MD2 version. Must be equal to 8 { result.Errors = "expected MD3 version 15, but got " + modelVersion; return(result); } int texWidth = br.ReadInt32(); int texHeight = br.ReadInt32(); int framesize = br.ReadInt32(); // Size of one frame in bytes s.Position += 4; //Number of textures int num_verts = br.ReadInt32(); //Number of vertices int num_uv = br.ReadInt32(); //The number of UV coordinates in the model int num_tris = br.ReadInt32(); //Number of triangles s.Position += 4; //Number of OpenGL commands int num_frames = br.ReadInt32(); //Total number of frames // Sanity checks if (frame < 0 || frame >= num_frames) { result.Errors = "frame " + frame + " is outside of model's frame range [0.." + (num_frames - 1) + "]"; return(result); } s.Position += 4; //Offset to skin names (each skin name is an unsigned char[64] and are null terminated) int ofs_uv = br.ReadInt32(); //Offset to s-t texture coordinates int ofs_tris = br.ReadInt32(); //Offset to triangles int ofs_animFrame = br.ReadInt32(); //An offset to the first animation frame List <int> polyIndecesList = new List <int>(); List <int> uvIndecesList = new List <int>(); List <Vector2> uvCoordsList = new List <Vector2>(); List <WorldVertex> vertList = new List <WorldVertex>(); // Polygons s.Position = ofs_tris + start; for (int i = 0; i < num_tris; i++) { polyIndecesList.Add(br.ReadUInt16()); polyIndecesList.Add(br.ReadUInt16()); polyIndecesList.Add(br.ReadUInt16()); uvIndecesList.Add(br.ReadUInt16()); uvIndecesList.Add(br.ReadUInt16()); uvIndecesList.Add(br.ReadUInt16()); } // UV coords s.Position = ofs_uv + start; for (int i = 0; i < num_uv; i++) { uvCoordsList.Add(new Vector2((float)br.ReadInt16() / texWidth, (float)br.ReadInt16() / texHeight)); } // Frames // Find correct frame if (!string.IsNullOrEmpty(framename)) { // Skip frames untill frame name matches bool framefound = false; for (int i = 0; i < num_frames; i++) { s.Position = ofs_animFrame + start + i * framesize; s.Position += 24; // Skip scale and translate string curframename = ReadString(br, 16).ToLowerInvariant(); if (curframename == framename) { // Step back so scale and translate can be read s.Position -= 40; framefound = true; break; } } // No dice? Bail out! if (!framefound) { result.Errors = "unable to find frame \"" + framename + "\"!"; return(result); } } else { // If we have frame number, we can go directly to target frame s.Position = ofs_animFrame + start + frame * framesize; } Vector3 scale = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); Vector3 translate = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); s.Position += 16; // Skip frame name // Prepare to fix rotation angle float angleOfsetCos = (float)Math.Cos(-Angle2D.PIHALF); float angleOfsetSin = (float)Math.Sin(-Angle2D.PIHALF); //verts for (int i = 0; i < num_verts; i++) { WorldVertex v = new WorldVertex(); v.x = (br.ReadByte() * scale.X + translate.X); v.y = (br.ReadByte() * scale.Y + translate.Y); v.z = (br.ReadByte() * scale.Z + translate.Z); // Fix rotation angle float rx = angleOfsetCos * v.x - angleOfsetSin * v.y; float ry = angleOfsetSin * v.x + angleOfsetCos * v.y; v.y = ry; v.x = rx; vertList.Add(v); s.Position += 1; //vertex normal } for (int i = 0; i < polyIndecesList.Count; i++) { WorldVertex v = vertList[polyIndecesList[i]]; //bounding box BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, new WorldVertex(v.y, v.x, v.z)); //uv float tu = uvCoordsList[uvIndecesList[i]].X; float tv = uvCoordsList[uvIndecesList[i]].Y; //uv-coordinates already set? if (v.c == -1 && (v.u != tu || v.v != tv)) { //add a new vertex vertList.Add(new WorldVertex(v.x, v.y, v.z, -1, tu, tv)); polyIndecesList[i] = vertList.Count - 1; } else { v.u = tu; v.v = tv; v.c = -1; //set color to white //return to proper place vertList[polyIndecesList[i]] = v; } } //mesh Mesh mesh = new Mesh(device, polyIndecesList.Count / 3, vertList.Count, MeshFlags.Use32Bit | MeshFlags.IndexBufferManaged | MeshFlags.VertexBufferManaged, vertexElements); using (DataStream stream = mesh.LockVertexBuffer(LockFlags.None)) { stream.WriteRange(vertList.ToArray()); } mesh.UnlockVertexBuffer(); using (DataStream stream = mesh.LockIndexBuffer(LockFlags.None)) { stream.WriteRange(polyIndecesList.ToArray()); } mesh.UnlockIndexBuffer(); mesh.OptimizeInPlace(MeshOptimizeFlags.AttributeSort); //store in result result.Meshes.Add(mesh); result.Skins.Add(""); //no skin support for MD2 } return(result); }
private static string ReadSurface(ref BoundingBoxSizes bbs, ref string skin, BinaryReader br, List <int> polyIndecesList, List <WorldVertex> vertList, int frame) { int vertexOffset = vertList.Count; long start = br.BaseStream.Position; string magic = ReadString(br, 4); if (magic != "IDP3") { return("error while reading surface. Unknown header: expected \"IDP3\", but got \"" + magic + "\""); } string name = ReadString(br, 64); int flags = br.ReadInt32(); int numFrames = br.ReadInt32(); //Number of animation frames. This should match NUM_FRAMES in the MD3 header. int numShaders = br.ReadInt32(); //Number of Shader objects defined in this Surface, with a limit of MD3_MAX_SHADERS. Current value of MD3_MAX_SHADERS is 256. int numVerts = br.ReadInt32(); //Number of Vertex objects defined in this Surface, up to MD3_MAX_VERTS. Current value of MD3_MAX_VERTS is 4096. int numTriangles = br.ReadInt32(); //Number of Triangle objects defined in this Surface, maximum of MD3_MAX_TRIANGLES. Current value of MD3_MAX_TRIANGLES is 8192. int ofsTriangles = br.ReadInt32(); //Relative offset from SURFACE_START where the list of Triangle objects starts. int ofsShaders = br.ReadInt32(); int ofsST = br.ReadInt32(); //Relative offset from SURFACE_START where the list of ST objects (s-t texture coordinates) starts. int ofsNormal = br.ReadInt32(); //Relative offset from SURFACE_START where the list of Vertex objects (X-Y-Z-N vertices) starts. int ofsEnd = br.ReadInt32(); //Relative offset from SURFACE_START to where the Surface object ends. // Sanity check if (frame < 0 || frame >= numFrames) { return("frame " + frame + " is outside of model's frame range [0.." + (numFrames - 1) + "]"); } // Polygons if (start + ofsTriangles != br.BaseStream.Position) { br.BaseStream.Position = start + ofsTriangles; } for (int i = 0; i < numTriangles * 3; i++) { polyIndecesList.Add(vertexOffset + br.ReadInt32()); } // Shaders if (start + ofsShaders != br.BaseStream.Position) { br.BaseStream.Position = start + ofsShaders; } skin = ReadString(br, 64); //we are interested only in the first one // Vertices if (start + ofsST != br.BaseStream.Position) { br.BaseStream.Position = start + ofsST; } for (int i = 0; i < numVerts; i++) { WorldVertex v = new WorldVertex(); v.c = -1; //white v.u = br.ReadSingle(); v.v = br.ReadSingle(); vertList.Add(v); } // Positions and normals long vertoffset = start + ofsNormal + numVerts * 8 * frame; // The length of Vertex struct is 8 bytes if (br.BaseStream.Position != vertoffset) { br.BaseStream.Position = vertoffset; } for (int i = vertexOffset; i < vertexOffset + numVerts; i++) { WorldVertex v = vertList[i]; //read vertex v.y = -(float)br.ReadInt16() / 64; v.x = (float)br.ReadInt16() / 64; v.z = (float)br.ReadInt16() / 64; //bounding box BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, v); var lat = br.ReadByte() * (2 * Math.PI) / 255.0; var lng = br.ReadByte() * (2 * Math.PI) / 255.0; v.nx = (float)(Math.Sin(lng) * Math.Sin(lat)); v.ny = -(float)(Math.Cos(lng) * Math.Sin(lat)); v.nz = (float)(Math.Cos(lat)); vertList[i] = v; } if (start + ofsEnd != br.BaseStream.Position) { br.BaseStream.Position = start + ofsEnd; } return(""); }
// This builds the geometry. Returns false when no geometry created. public override bool Setup() { WorldVertex[] verts; //int brightness = mode.CalculateBrightness(Sidedef.Sector.Brightness); int c1; int c2; if (!Sidedef.Line.IsFlagSet("8388608")) { c1 = Sidedef.Sector.ThingColor.GetColor(); c2 = Sidedef.Sector.ThingColor.GetColor(); } else { c1 = Sidedef.Sector.TopColor.GetColor(); c2 = Sidedef.Sector.LowerColor.GetColor(); } // Calculate size of this wall part float geotop = (float)Math.Min(Sidedef.Sector.CeilHeight, Sidedef.Other.Sector.CeilHeight); float geobottom = (float)Math.Max(Sidedef.Sector.FloorHeight, Sidedef.Other.Sector.FloorHeight); float geoheight = geotop - geobottom; if (geoheight > 0.001f) { // Texture given? if ((Sidedef.MiddleTexture.Length > 0) && (Sidedef.MiddleTexture[0] != '-')) { Vector2D t1 = new Vector2D(); Vector2D t2 = new Vector2D(); float textop, texbottom; float cliptop = 0.0f; float clipbottom = 0.0f; // 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; } } // Get texture scaled size Vector2D tsz = new Vector2D(base.Texture.ScaledWidth, base.Texture.ScaledHeight); // villsa if (General.Map.FormatInterface.InDoom64Mode) { textop = geotop; } else { // Because the middle texture on a double sided line does not repeat vertically, // we first determine the visible portion of the texture if (Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag)) { textop = geobottom + tsz.y; } else { textop = geotop; } // Apply texture offset textop += Sidedef.OffsetY; } // villsa if (General.Map.FormatInterface.InDoom64Mode) { texbottom = geobottom; } else { // Calculate texture portion bottom texbottom = textop - tsz.y; } // Clip texture portion by geometry if (geotop < textop) { cliptop = textop - geotop; textop = geotop; } if (geobottom > texbottom) { clipbottom = geobottom - texbottom; texbottom = geobottom; } // Check if anything is still visible if ((textop - texbottom) > 0.001f) { // Determine texture coordinatess // villsa if (General.Map.FormatInterface.InDoom64Mode) { t1.y = geobottom + Sidedef.OffsetY - geotop; t2.y = geotop + Sidedef.OffsetY - geotop; } else { t1.y = cliptop; t2.y = tsz.y - clipbottom; } t1.x = Sidedef.OffsetX; t2.x = t1.x + Sidedef.Line.Length; // Transform pixel coordinates to texture coordinates t1 /= tsz; t2 /= tsz; // Get world coordinates for geometry Vector2D v1, v2; if (Sidedef.IsFront) { v1 = Sidedef.Line.Start.Position; v2 = Sidedef.Line.End.Position; } else { v1 = Sidedef.Line.End.Position; v2 = Sidedef.Line.Start.Position; } // Make vertices verts = new WorldVertex[6]; verts[0] = new WorldVertex(v1.x, v1.y, texbottom, c2, t1.x, t2.y); verts[1] = new WorldVertex(v1.x, v1.y, textop, c1, t1.x, t1.y); verts[2] = new WorldVertex(v2.x, v2.y, textop, c1, t2.x, t1.y); verts[3] = verts[0]; verts[4] = verts[2]; verts[5] = new WorldVertex(v2.x, v2.y, texbottom, c2, t2.x, t2.y); // Keep properties base.top = textop; base.bottom = texbottom; // Apply vertices base.SetVertices(verts); return(true); } } } // No geometry for invisible wall base.top = geotop; base.bottom = geotop; // bottom same as top so that it has a height of 0 (otherwise it will still be picked up by object picking) verts = new WorldVertex[0]; base.SetVertices(verts); return(false); }
// This builds the thing geometry. Returns false when nothing was created. public virtual bool Setup() { // Must have a width and height! float radius, height; // Find the sector in which the thing resides Thing.DetermineSector(mode.BlockMap); if (sprite != null) { //mxd. Transparency and brightness int brightness; if (Thing.SemiTransparent) { brightness = MapElement.CalculateBrightness(Thing.Shade, (byte)(Thing.Transparent ? 85 : 170)); this.RenderPass = RenderPass.Alpha; } else { brightness = MapElement.CalculateBrightness(Thing.Shade); this.RenderPass = RenderPass.Mask; } // Check if the texture is loaded sprite.LoadImage(); isloaded = sprite.IsImageLoaded; if (isloaded) { base.Texture = sprite; // Determine sprite size and offsets bool flooraligned = Thing.FlatAligned; bool wallaligned = Thing.WallAligned; bool truecentered = Thing.TrueCentered; float xratio = Thing.RepeatX * ((flooraligned && truecentered) ? 0.2f : 0.25f); float yratio = Thing.RepeatY * 0.25f; int xsize = (int)(sprite.Width * xratio); int ysize = (int)(sprite.Height * yratio); int tilexoff = Thing.OffsetX + sprite.OffsetX; int tileyoff = Thing.OffsetY + sprite.OffsetY; int xoff = (int)(tilexoff * xratio); int yoff = (int)(tileyoff * yratio); if (truecentered && !flooraligned) { yoff -= ysize / 2; // Seems that centeryoff shenanigans in polymer_updatesprite.cpp do exactly the same thing... } bool xflip = Thing.FlipX; bool yflip = Thing.FlipY; // Initially set flipu and flipv. bool flipu = (xflip ^ flooraligned); bool flipv = (yflip && !flooraligned); if (flipu) { xoff = -xoff; } if (yflip && (flooraligned || wallaligned)) { yoff = -yoff; } radius = xsize * 0.5f; height = ysize; // Make vertices WorldVertex[] verts = new WorldVertex[6]; verts[0] = new WorldVertex(-radius + xoff, 0.0f, yoff, brightness, 0.0f, 1.0f); verts[1] = new WorldVertex(-radius + xoff, 0.0f, height + yoff, brightness, 0.0f, 0.0f); verts[2] = new WorldVertex(radius + xoff, 0.0f, height + yoff, brightness, 1.0f, 0.0f); verts[3] = verts[0]; verts[4] = verts[2]; verts[5] = new WorldVertex(radius + xoff, 0.0f, yoff, brightness, 1.0f, 1.0f); // Update UVs if needed if (flipu || flipv) { for (int i = 0; i < 6; i++) { if (flipu) { verts[i].u = (verts[i].u - 1.0f) * -1.0f; } if (flipv) { verts[i].v = (verts[i].v - 1.0f) * -1.0f; } } } SetVertices(verts); } else { base.Texture = General.Map.Data.Hourglass3D; // Determine sprite size radius = Math.Min(Texture.Width / 2f, Texture.Height / 2f); height = Math.Min(Texture.Width, Texture.Height); // Make vertices WorldVertex[] verts = new WorldVertex[6]; verts[0] = new WorldVertex(-radius, 0.0f, 0.0f, brightness, 0.0f, 1.0f); verts[1] = new WorldVertex(-radius, 0.0f, height, brightness, 0.0f, 0.0f); verts[2] = new WorldVertex(+radius, 0.0f, height, brightness, 1.0f, 0.0f); verts[3] = verts[0]; verts[4] = verts[2]; verts[5] = new WorldVertex(+radius, 0.0f, 0.0f, brightness, 1.0f, 1.0f); SetVertices(verts); } } else { // Use some default values... radius = MINIMUM_RADIUS; height = MINIMUM_HEIGHT; SetVertices(new WorldVertex[0]); } // Determine position Vector3D pos = Thing.Position; if (Thing.Sector != null) { pos.z = Thing.Sector.FloorPlane.GetZ(Thing.Position.x, Thing.Position.y); } if (Thing.Position.z > 0) { pos.z += Thing.Position.z; } // Check if above ceiling if ((Thing.Sector != null) && ((pos.z + height) > Thing.Sector.CeilingHeight)) { // Put thing against ceiling pos.z = Thing.Sector.CeilingHeight - height; } // Apply settings SetPosition(pos); SetCageSize(Math.Max(radius, MINIMUM_RADIUS), Math.Max(height, MINIMUM_HEIGHT)); SetCageColor(Thing.Color); // Keep info for object picking cageradius2 = radius * Angle2D.SQRT2; cageradius2 = cageradius2 * cageradius2; pos2d = pos; boxp1 = new Vector3D(pos.x - radius, pos.y - radius, pos.z); boxp2 = new Vector3D(pos.x + radius, pos.y + radius, pos.z + height); // 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 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) { // Use sector brightness for color shading byte brightness = (byte)General.Clamp(Thing.Sector.Brightness, 0, 255); sectorcolor = new PixelColor(255, brightness, brightness, brightness); } // 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 (info.AbsoluteZ) { // Absolute Z position pos.z = Thing.Position.z; } else if (info.Hangs) { // Hang from ceiling if (Thing.Sector != null) { pos.z = Thing.Sector.CeilHeight - info.Height; } if (Thing.Position.z > 0) { pos.z -= Thing.Position.z; } // Check if below floor if ((Thing.Sector != null) && (pos.z < Thing.Sector.FloorHeight)) { // Put thing on the floor pos.z = Thing.Sector.FloorHeight; } } else { // Stand on floor if (Thing.Sector != null) { pos.z = Thing.Sector.FloorHeight; } if (Thing.Position.z > 0) { pos.z += Thing.Position.z; } // Check if above ceiling if ((Thing.Sector != null) && ((pos.z + info.Height) > Thing.Sector.CeilHeight)) { // Put thing against ceiling pos.z = Thing.Sector.CeilHeight - 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); }
//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 builds the thing geometry. Returns false when nothing was created. public virtual bool Setup() { int sectorcolor = 0; // 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) { // Use sector brightness for color shading //byte brightness = (byte)General.Clamp(Thing.Sector.Brightness, 0, 255); //sectorcolor = new PixelColor(255, brightness, brightness, brightness); // villsa 9/11/11 (builder64) cameras/triggers are rendered full bright if (Thing.Type == 0 || Thing.Type == 89) { sectorcolor = -1; } else { sectorcolor = Thing.Sector.ThingColor.GetColor(); } } // villsa 9/11/11 (builder64) render camera/trigger icon if (Thing.Type == 0 || Thing.Type == 89) { if (Thing.Type == 89) { base.Texture = General.Map.Data.ThingTrigger; } else { base.Texture = General.Map.Data.ThingCamera; } // 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, 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); SetVertices(verts); } else { // 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; } // Make vertices WorldVertex[] verts = new WorldVertex[6]; verts[0] = new WorldVertex(-radius + offsetx, 0.0f, 0.0f + offsety, sectorcolor, 0.0f, 1.0f); verts[1] = new WorldVertex(-radius + offsetx, 0.0f, height + offsety, sectorcolor, 0.0f, 0.0f); verts[2] = new WorldVertex(+radius + offsetx, 0.0f, height + offsety, sectorcolor, 1.0f, 0.0f); verts[3] = verts[0]; verts[4] = verts[2]; verts[5] = new WorldVertex(+radius + offsetx, 0.0f, 0.0f + offsety, sectorcolor, 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, 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); SetVertices(verts); } } } // Determine position Vector3D pos = Thing.Position; if (info.AbsoluteZ) { // Absolute Z position pos.z = Thing.Position.z; } else if (info.Hangs) { // Hang from ceiling if (Thing.Sector != null) { pos.z = Thing.Sector.CeilHeight - info.Height; } if (Thing.Position.z > 0) { pos.z -= Thing.Position.z; } // Check if below floor if ((Thing.Sector != null) && (pos.z < Thing.Sector.FloorHeight)) { // Put thing on the floor pos.z = Thing.Sector.FloorHeight; } } else { // Stand on floor if (Thing.Sector != null) { pos.z = Thing.Sector.FloorHeight; } if (Thing.Position.z > 0) { pos.z += Thing.Position.z; } // Check if above ceiling if ((Thing.Sector != null) && ((pos.z + info.Height) > Thing.Sector.CeilHeight)) { // Put thing against ceiling pos.z = Thing.Sector.CeilHeight - 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); }
// This updates the visual thing public virtual void Update() { // Do we need to update the geometry buffer? if (updategeo) { //mxd. Trash geometry buffers if (geobuffers != null) { foreach (VertexBuffer geobuffer in geobuffers) { geobuffer.Dispose(); } } // Any vertics? if (vertices.Length > 0) { geobuffers = new VertexBuffer[vertices.Length]; for (int i = 0; i < vertices.Length; i++) { // Make a new buffer geobuffers[i] = new VertexBuffer(General.Map.Graphics.Device, WorldVertex.Stride * vertices[i].Length, Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default); // Fill the buffer DataStream bufferstream = geobuffers[i].Lock(0, WorldVertex.Stride * vertices[i].Length, LockFlags.Discard); bufferstream.WriteRange(vertices[i]); geobuffers[i].Unlock(); bufferstream.Dispose(); } } //mxd. Check if thing is light CheckLightState(); // Done updategeo = false; } //mxd. Need to update thing cage? if (updatecage) { // Trash cage buffer if (cagebuffer != null) { cagebuffer.Dispose(); } cagebuffer = null; // Make a new cage List <WorldVertex> cageverts; if (sizeless) { WorldVertex v0 = new WorldVertex(-thing.Size + position_v3.X, -thing.Size + position_v3.Y, position_v3.Z); WorldVertex v1 = new WorldVertex(thing.Size + position_v3.X, thing.Size + position_v3.Y, position_v3.Z); WorldVertex v2 = new WorldVertex(thing.Size + position_v3.X, -thing.Size + position_v3.Y, position_v3.Z); WorldVertex v3 = new WorldVertex(-thing.Size + position_v3.X, thing.Size + position_v3.Y, position_v3.Z); WorldVertex v4 = new WorldVertex(position_v3.X, position_v3.Y, thing.Size + position_v3.Z); WorldVertex v5 = new WorldVertex(position_v3.X, position_v3.Y, -thing.Size + position_v3.Z); cageverts = new List <WorldVertex>(new[] { v0, v1, v2, v3, v4, v5 }); } else { float top = position_v3.Z + thing.Height; float bottom = position_v3.Z; WorldVertex v0 = new WorldVertex(-thing.Size + position_v3.X, -thing.Size + position_v3.Y, bottom); WorldVertex v1 = new WorldVertex(-thing.Size + position_v3.X, thing.Size + position_v3.Y, bottom); WorldVertex v2 = new WorldVertex(thing.Size + position_v3.X, thing.Size + position_v3.Y, bottom); WorldVertex v3 = new WorldVertex(thing.Size + position_v3.X, -thing.Size + position_v3.Y, bottom); WorldVertex v4 = new WorldVertex(-thing.Size + position_v3.X, -thing.Size + position_v3.Y, top); WorldVertex v5 = new WorldVertex(-thing.Size + position_v3.X, thing.Size + position_v3.Y, top); WorldVertex v6 = new WorldVertex(thing.Size + position_v3.X, thing.Size + position_v3.Y, top); WorldVertex v7 = new WorldVertex(thing.Size + position_v3.X, -thing.Size + position_v3.Y, top); cageverts = new List <WorldVertex>(new[] { v0, v1, v1, v2, v2, v3, v3, v0, v4, v5, v5, v6, v6, v7, v7, v4, v0, v4, v1, v5, v2, v6, v3, v7 }); } // Make new arrow if (Thing.IsDirectional) { Matrix transform = Matrix.Scaling(thing.Size, thing.Size, thing.Size) * (Matrix.RotationY(-Thing.RollRad) * Matrix.RotationX(-Thing.PitchRad) * Matrix.RotationZ(Thing.Angle)) * (sizeless ? position : position * Matrix.Translation(0.0f, 0.0f, thingheight / 2f)); WorldVertex a0 = new WorldVertex(Vector3D.Transform(0.0f, 0.0f, 0.0f, transform)); //start WorldVertex a1 = new WorldVertex(Vector3D.Transform(0.0f, -1.5f, 0.0f, transform)); //end WorldVertex a2 = new WorldVertex(Vector3D.Transform(0.2f, -1.1f, 0.2f, transform)); WorldVertex a3 = new WorldVertex(Vector3D.Transform(-0.2f, -1.1f, 0.2f, transform)); WorldVertex a4 = new WorldVertex(Vector3D.Transform(0.2f, -1.1f, -0.2f, transform)); WorldVertex a5 = new WorldVertex(Vector3D.Transform(-0.2f, -1.1f, -0.2f, transform)); cageverts.AddRange(new[] { a0, a1, a1, a2, a1, a3, a1, a4, a1, a5 }); } // Create buffer WorldVertex[] cv = cageverts.ToArray(); cagelength = cv.Length / 2; cagebuffer = new VertexBuffer(General.Map.Graphics.Device, WorldVertex.Stride * cv.Length, Usage.WriteOnly | Usage.Dynamic, VertexFormat.None, Pool.Default); cagebuffer.Lock(0, WorldVertex.Stride * cv.Length, LockFlags.None).WriteRange(cv); cagebuffer.Unlock(); // Done updatecage = false; } }
// This builds the geometry. Returns false when no geometry created. public override bool Setup() { int brightness = mode.CalculateBrightness(Sidedef.Sector.Brightness); // Calculate size of this wall part float geotop = (float)Sidedef.Other.Sector.FloorHeight; float geobottom = (float)Sidedef.Sector.FloorHeight; float geoheight = geotop - geobottom; if (geoheight > 0.001f) { Vector2D t1 = new Vector2D(); Vector2D t2 = new Vector2D(); // Texture given? if ((Sidedef.LowTexture.Length > 0) && (Sidedef.LowTexture[0] != '-')) { // Load texture base.Texture = General.Map.Data.GetTextureImage(Sidedef.LongLowTexture); if (base.Texture == null) { base.Texture = General.Map.Data.MissingTexture3D; 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 Vector2D tsz = new Vector2D(base.Texture.ScaledWidth, base.Texture.ScaledHeight); // Determine texture coordinates // See http://doom.wikia.com/wiki/Texture_alignment // We just use pixels for coordinates for now if (Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag)) { // When lower unpegged is set, the lower texture is bound to the bottom t1.y = (float)Sidedef.Sector.CeilHeight - geotop; } t2.x = t1.x + Sidedef.Line.Length; t2.y = t1.y + geoheight; // Apply texture offset if (General.Map.Config.ScaledTextureOffsets && !base.Texture.WorldPanning) { t1 += new Vector2D(Sidedef.OffsetX * base.Texture.Scale.x, Sidedef.OffsetY * base.Texture.Scale.y); t2 += new Vector2D(Sidedef.OffsetX * base.Texture.Scale.x, Sidedef.OffsetY * base.Texture.Scale.y); } else { t1 += new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY); t2 += new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY); } // Transform pixel coordinates to texture coordinates t1 /= tsz; t2 /= tsz; // Get world coordinates for geometry Vector2D v1, v2; if (Sidedef.IsFront) { v1 = Sidedef.Line.Start.Position; v2 = Sidedef.Line.End.Position; } else { v1 = Sidedef.Line.End.Position; v2 = Sidedef.Line.Start.Position; } // Make vertices WorldVertex[] verts = new WorldVertex[6]; verts[0] = new WorldVertex(v1.x, v1.y, geobottom, brightness, t1.x, t2.y); verts[1] = new WorldVertex(v1.x, v1.y, geotop, brightness, t1.x, t1.y); verts[2] = new WorldVertex(v2.x, v2.y, geotop, brightness, t2.x, t1.y); verts[3] = verts[0]; verts[4] = verts[2]; verts[5] = new WorldVertex(v2.x, v2.y, geobottom, brightness, t2.x, t2.y); // Keep properties base.top = geotop; base.bottom = geobottom; // Apply vertices base.SetVertices(verts); return(true); } else { // No geometry for invisible wall base.top = geotop; base.bottom = geobottom; WorldVertex[] verts = new WorldVertex[0]; base.SetVertices(verts); return(false); } }