/// <summary> /// Draw plane (Must be inside GL_TRIANGLES or GL_LINES for wireframe) /// </summary> /// <param name="gl">OpenGL isntance</param> /// <param name="map">Shared map data</param> /// <param name="ignoreTiled">Do not draw tiled quads in planes</param> /// <param name="wireframe">Draw as wireframe</param> public void Draw(SharpGL.OpenGL gl, Powerslave.Map map, bool ignoreTiled, bool wireframe) { if (this.PolyStart != -1 && this.PolyEnd != -1 && !ignoreTiled) { Vector3D planeNormal = new Vector3D(this.Normal.X / (double)short.MaxValue, this.Normal.Z / (double)short.MaxValue, this.Normal.Y / (double)short.MaxValue); int start = Math.Min(this.PolyStart, this.PolyEnd); int end = Math.Max(this.PolyStart, this.PolyEnd); for (int polygon = start; polygon <= end; polygon++) { map.Quads[polygon].Draw(gl, map, this, planeNormal, wireframe); } } else { List <Powerslave.Vertex> vertices = this.PolyVert.Select(index => map.Vertices[index]).ToList(); foreach (Powerslave.Vertex point in wireframe ? vertices.SelectMany((vertex, index) => new List <Powerslave.Vertex> { vertex, vertices[(index + 1) % vertices.Count] }) : Powerslave.GetTrianglesFromQuad(vertices)) { if (wireframe) { gl.Color(1.0f, 1.0f, 1.0f); } else { Powerslave.SetVertexColor(gl, this.Flags, point.Lightlevel); } gl.Vertex(point.X / 10.0f, point.Z / 10.0f, point.Y / 10.0f); } } }
/// <summary> /// Draw quad (Must be inside GL_TRIANGLES or GL_LINES for wireframe) /// </summary> /// <param name="gl">OpenGL isntance</param> /// <param name="map">Shared map data</param> /// <param name="plane">Plane this quad belongs to</param> /// <param name="planeNormal">Normal vector of the plane</param> /// <param name="wireframe">Draw as wireframe</param> public void Draw(SharpGL.OpenGL gl, Powerslave.Map map, Powerslave.Plane plane, Vector3D planeNormal, bool wireframe) { List <Powerslave.Vertex> quad = this.Indices.Select(index => map.Vertices[index + plane.VertexStart]).ToList(); if (Vector3D.DotProduct(planeNormal, Quad.GetQuadNormal(quad)) < 0.0f) { // Quad is rotated incorrectly, reverse it (might be used as a texture flip) quad.Reverse(); } foreach (Powerslave.Vertex point in wireframe ? quad.SelectMany((vertex, index) => new List <Powerslave.Vertex> { vertex, quad[(index + 1) % quad.Count] }) : Powerslave.GetTrianglesFromQuad(quad)) { if (wireframe) { gl.Color(1.0f, 1.0f, 1.0f); } else { Powerslave.SetVertexColor(gl, plane.Flags, point.Lightlevel); } gl.Vertex(point.X / 10.0f, point.Z / 10.0f, point.Y / 10.0f); } }
/// <summary> /// Read struct from <see cref="FileStream"/> /// </summary> /// <typeparam name="DataType">Type of the struct to read</typeparam> /// <param name="stream">The <see cref="FileStream"/></param> /// <returns>Loaded struct</returns> private static DataType LoadStruct <DataType>(FileStream stream) where DataType : struct { DataType parsed = new DataType(); int size = Marshal.SizeOf(parsed); IntPtr target = Marshal.AllocHGlobal(size); // Read bytes byte[] data = new byte[size]; stream.Read(data, 0, size); Powerslave.FixEndianness <DataType>(data); // Convert bytes to struct Marshal.Copy(data, 0, target, size); parsed = (DataType)Marshal.PtrToStructure(target, typeof(DataType)); Marshal.FreeHGlobal(target); return(parsed); }
/// <summary> /// Load sky texture /// </summary> /// <param name="stream">The <see cref="FileStream"/></param> /// <returns>Sky texture</returns> private static Bitmap LoadSky(FileStream stream) { Powerslave.Skybox skyData = Powerslave.LoadStruct <Powerslave.Skybox>(stream); List <Color> pallet = skyData.Pallet.Select(rgb => Color.FromArgb((byte)(((rgb >> 0) & 31) * 8), (byte)(((rgb >> 5) & 31) * 8), (byte)((rgb >> 10 & 31) * 8))).ToList(); Bitmap sky = new Bitmap((byte.MaxValue + 1) * 2, byte.MaxValue + 1); for (int y = 0; y < sky.Height; y++) { for (int x = 0; x < sky.Width; x++) { sky.SetPixel(x, y, pallet[skyData.Bitmap[(y * sky.Width) + x]]); } } // Bitmaps are flipped over Y axis sky.RotateFlip(RotateFlipType.RotateNoneFlipY); return(sky); }
/// <summary> /// Load map data /// </summary> /// <param name="file">Path to the file</param> /// <returns>Loaded <see cref="Map"/></returns> public static Map Load(string file) { Exception loadingException = null; Map map = new Map { FileName = file }; using (FileStream stream = File.OpenRead(file)) { try { // Load sky texture first map.Sky = Map.LoadSky(stream); // Load map header (skip 1292 bytes of unknown/empty space) stream.Position = 0x2070C; Powerslave.FileHeader header = Powerslave.LoadStruct <Powerslave.FileHeader>(stream); // Load map data map.Sectors = Map.CreateSequence(() => Powerslave.LoadStruct <Powerslave.Sector>(stream), header.SectorCount).ToList(); map.Planes = Map.CreateSequence(() => Powerslave.LoadStruct <Powerslave.Plane>(stream), header.PlaneCount).ToList(); map.Vertices = Map.CreateSequence(() => Powerslave.LoadStruct <Powerslave.Vertex>(stream), header.VertexCount).ToList(); map.Quads = Map.CreateSequence(() => Powerslave.LoadStruct <Powerslave.Quad>(stream), header.QuadCount).ToList(); } catch (Exception ex) { loadingException = ex; if (map.Sky != null) { map.Sky.Dispose(); } } } if (loadingException != null) { throw loadingException; } return(map); }