diff --git a/SabreTools.Models/BSP/Clipnode.cs b/SabreTools.Models/BSP/Clipnode.cs new file mode 100644 index 0000000..687ebe4 --- /dev/null +++ b/SabreTools.Models/BSP/Clipnode.cs @@ -0,0 +1,28 @@ +using System.Runtime.InteropServices; + +namespace SabreTools.Models.BSP +{ + /// + /// This lump contains the so-called clipnodes, which build a second + /// BSP tree used only for collision detection. + /// + /// This structure is a reduced form of the BSPNODE struct from the + /// nodes lump. Also the BSP tree built by the clipnodes is simpler + /// than the one described by the BSPNODEs to accelerate collision calculations. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class Clipnode + { + /// + /// Index into planes + /// + public int PlaneIndex; + + /// + /// Negative numbers are contents + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public short[]? ChildrenIndices = new short[2]; + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/ClipnodesLump.cs b/SabreTools.Models/BSP/ClipnodesLump.cs new file mode 100644 index 0000000..13d8dde --- /dev/null +++ b/SabreTools.Models/BSP/ClipnodesLump.cs @@ -0,0 +1,19 @@ +namespace SabreTools.Models.BSP +{ + /// + /// This lump contains the so-called clipnodes, which build a second + /// BSP tree used only for collision detection. + /// + /// This structure is a reduced form of the BSPNODE struct from the + /// nodes lump. Also the BSP tree built by the clipnodes is simpler + /// than the one described by the BSPNODEs to accelerate collision calculations. + /// + /// + public sealed class ClipnodesLump : Lump + { + /// + /// Clipnodes + /// + public Clipnode[]? Clipnodes { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/Constants.cs b/SabreTools.Models/BSP/Constants.cs index b0a4159..a7de4cf 100644 --- a/SabreTools.Models/BSP/Constants.cs +++ b/SabreTools.Models/BSP/Constants.cs @@ -1,25 +1,59 @@ namespace SabreTools.Models.BSP { + /// public static class Constants { + #region Header + /// /// Number of lumps in a BSP /// - public const int HL_BSP_LUMP_COUNT = 15; + public const int HEADER_LUMPS = 15; - /// - /// Index for the entities lump - /// - public const int HL_BSP_LUMP_ENTITIES = 0; + #endregion - /// - /// Index for the texture data lump - /// - public const int HL_BSP_LUMP_TEXTUREDATA = 2; + #region Lump - /// - /// Number of valid mipmap levels - /// - public const int HL_BSP_MIPMAP_COUNT = 4; + public const int MAX_MAP_HULLS = 4; + + public const int MAX_MAP_MODELS = 400; + public const int MAX_MAP_BRUSHES = 4096; + public const int MAX_MAP_ENTITIES = 1024; + public const int MAX_MAP_ENTSTRING = (128 * 1024); + + public const int MAX_MAP_PLANES = 32767; + public const int MAX_MAP_NODES = 32767; + public const int MAX_MAP_CLIPNODES = 32767; + public const int MAX_MAP_LEAFS = 8192; + public const int MAX_MAP_VERTS = 65535; + public const int MAX_MAP_FACES = 65535; + public const int MAX_MAP_MARKSURFACES = 65535; + public const int MAX_MAP_TEXINFO = 8192; + public const int MAX_MAP_EDGES = 256000; + public const int MAX_MAP_SURFEDGES = 512000; + public const int MAX_MAP_TEXTURES = 512; + public const int MAX_MAP_MIPTEX = 0x200000; + public const int MAX_MAP_LIGHTING = 0x200000; + public const int MAX_MAP_VISIBILITY = 0x200000; + + public const int MAX_MAP_PORTALS = 65536; + + #endregion + + #region Entities + + public const int MAX_KEY = 32; + + public const int MAX_VALUE = 1024; + + #endregion + + #region Textures + + public const int MAXTEXTURENAME = 16; + + public const int MIPLEVELS = 4; + + #endregion } } \ No newline at end of file diff --git a/SabreTools.Models/BSP/Edge.cs b/SabreTools.Models/BSP/Edge.cs new file mode 100644 index 0000000..c449d52 --- /dev/null +++ b/SabreTools.Models/BSP/Edge.cs @@ -0,0 +1,19 @@ +using System.Runtime.InteropServices; + +namespace SabreTools.Models.BSP +{ + /// + /// The edges delimit the face and further refer to the vertices of the + /// face. Each edge is pointing to the start and end vertex of the edge. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class Edge + { + /// + /// Indices into vertex array + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public ushort[]? VertexIndices = new ushort[2]; + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/EdgesLump.cs b/SabreTools.Models/BSP/EdgesLump.cs new file mode 100644 index 0000000..8b35925 --- /dev/null +++ b/SabreTools.Models/BSP/EdgesLump.cs @@ -0,0 +1,15 @@ +namespace SabreTools.Models.BSP +{ + /// + /// The edges delimit the face and further refer to the vertices of the + /// face. Each edge is pointing to the start and end vertex of the edge. + /// + /// + public sealed class EdgesLump : Lump + { + /// + /// Edge + /// + public Edge[]? Edges { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/Entity.cs b/SabreTools.Models/BSP/Entity.cs new file mode 100644 index 0000000..fe3631a --- /dev/null +++ b/SabreTools.Models/BSP/Entity.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +namespace SabreTools.Models.BSP +{ + /// + /// The entity lump is basically a pure ASCII text section. + /// It consists of the string representations of all entities, + /// which are copied directly from the input file to the output + /// BSP file by the compiler. + /// + /// Every entity begins and ends with curly brackets. In between + /// there are the attributes of the entity, one in each line, + /// which are pairs of strings enclosed by quotes. The first + /// string is the name of the attribute (the key), the second one + /// its value. The attribute "classname" is mandatory for every + /// entity specifiying its type and therefore, how it is + /// interpreted by the engine. + /// + /// + public sealed class Entity + { + /// + /// Entity attributes + /// + public List>? Attributes { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/EntityLump.cs b/SabreTools.Models/BSP/EntityLump.cs new file mode 100644 index 0000000..8bba1b5 --- /dev/null +++ b/SabreTools.Models/BSP/EntityLump.cs @@ -0,0 +1,25 @@ +namespace SabreTools.Models.BSP +{ + /// + /// The entity lump is basically a pure ASCII text section. + /// It consists of the string representations of all entities, + /// which are copied directly from the input file to the output + /// BSP file by the compiler. + /// + /// Every entity begins and ends with curly brackets. In between + /// there are the attributes of the entity, one in each line, + /// which are pairs of strings enclosed by quotes. The first + /// string is the name of the attribute (the key), the second one + /// its value. The attribute "classname" is mandatory for every + /// entity specifiying its type and therefore, how it is + /// interpreted by the engine. + /// + /// + public sealed class EntityLump : Lump + { + /// + /// Entities + /// + public Entity[]? Entities { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/Enums.cs b/SabreTools.Models/BSP/Enums.cs new file mode 100644 index 0000000..d853e44 --- /dev/null +++ b/SabreTools.Models/BSP/Enums.cs @@ -0,0 +1,160 @@ +using System; + +namespace SabreTools.Models.BSP +{ + /// + public enum Contents : int + { + CONTENTS_EMPTY = -1, + CONTENTS_SOLID = -2, + CONTENTS_WATER = -3, + CONTENTS_SLIME = -4, + CONTENTS_LAVA = -5, + CONTENTS_SKY = -6, + CONTENTS_ORIGIN = -7, + CONTENTS_CLIP = -8, + CONTENTS_CURRENT_0 = -9, + CONTENTS_CURRENT_90 = -10, + CONTENTS_CURRENT_180 = -11, + CONTENTS_CURRENT_270 = -12, + CONTENTS_CURRENT_UP = -13, + CONTENTS_CURRENT_DOWN = -14, + CONTENTS_TRANSLUCENT = -15, + } + + /// + public enum LumpType : int + { + /// + /// The entity lump is basically a pure ASCII text section. + /// It consists of the string representations of all entities, + /// which are copied directly from the input file to the output + /// BSP file by the compiler. + /// + LUMP_ENTITIES = 0, + + /// + /// This lump is a simple array of binary data structures. + /// Each of this structures defines a plane in 3-dimensional + /// space by using the Hesse normal form + /// + LUMP_PLANES = 1, + + /// + /// The texture lump is somehow a bit more complex then the + /// other lumps, because it is possible to save textures + /// directly within the BSP file instead of storing them in + /// external WAD files. + /// + LUMP_TEXTURES = 2, + + /// + /// This lump simply consists of all vertices of the BSP tree. + /// They are stored as a primitve array of triples of floats. + /// + LUMP_VERTICES = 3, + + /// + /// The VIS lump contains data, which is irrelevant to the actual + /// BSP tree, but offers a way to boost up the speed of the + /// renderer significantly. Especially complex maps profit from + /// the use if this data. This lump contains the so-called + /// Potentially Visible Sets (PVS) (also called VIS lists) in the + /// same amout of leaves of the tree, the user can enter (often + /// referred to as VisLeaves). The visiblilty lists are stored as + /// sequences of bitfields, which are run-length encoded. + /// + LUMP_VISIBILITY = 4, + + /// + /// This lump is simple again and contains an array of binary + /// structures, the nodes, which are a major part of the BSP tree. + /// + LUMP_NODES = 5, + + /// + /// The texinfo lump contains informations about how textures are + /// applied to surfaces. The lump itself is an array of binary data + /// structures. + /// + LUMP_TEXINFO = 6, + + /// + /// The face lump contains the surfaces of the scene. + /// + LUMP_FACES = 7, + + /// + /// This is one of the largest lumps in the BSP file. The lightmap + /// lump stores all lightmaps used in the entire map. The lightmaps + /// are arrays of triples of bytes (3 channel color, RGB) and stored + /// continuously. + /// + LUMP_LIGHTING = 8, + + /// + /// This lump contains the so-called clipnodes, which build a second + /// BSP tree used only for collision detection. + /// + LUMP_CLIPNODES = 9, + + /// + /// The leaves lump contains the leaves of the BSP tree. + /// + LUMP_LEAVES = 10, + + /// + /// The marksurfaces lump is a simple array of short integers. + /// + LUMP_MARKSURFACES = 11, + + /// + /// The edges delimit the face and further refer to the vertices of the + /// face. Each edge is pointing to the start and end vertex of the edge. + /// + LUMP_EDGES = 12, + + /// + /// This lump represents pretty much the same mechanism as the marksurfaces. + /// A face can insert its surfedge indexes into this array to get the + /// corresponding edges delimitting the face and further pointing to the + /// vertexes, which are required for rendering. The index can be positive + /// or negative. If the value of the surfedge is positive, the first vertex + /// of the edge is used as vertex for rendering the face, otherwise, the + /// value is multiplied by -1 and the second vertex of the indexed edge is + /// used. + /// + LUMP_SURFEDGES = 13, + + /// + /// A model is kind of a mini BSP tree. Its size is determinded by the + /// bounding box spaned by the first to members of this struct. + /// + LUMP_MODELS = 14, + } + + /// + public enum PlaneType : int + { + // Plane is perpendicular to given axis + PLANE_X = 0, + PLANE_Y = 1, + PLANE_Z = 2, + + // Non-axial plane is snapped to the nearest + PLANE_ANYX = 3, + PLANE_ANYY = 4, + PLANE_ANYZ = 5, + } + + /// + [Flags] + public enum TextureFlag : uint + { + /// + /// Disable lightmaps and subdivision for the surface + /// + /// Used by sky and liquids + DisableLightmaps = 0x01, + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/Face.cs b/SabreTools.Models/BSP/Face.cs new file mode 100644 index 0000000..6f77968 --- /dev/null +++ b/SabreTools.Models/BSP/Face.cs @@ -0,0 +1,66 @@ +using System.Runtime.InteropServices; + +namespace SabreTools.Models.BSP +{ + /// + /// The face lump contains the surfaces of the scene. + /// + /// The first number of this data structure is an index into + /// the planes lump giving a plane which is parallel to this + /// face (meaning they share the same normal). The second + /// value may be seen as a boolean. If nPlaneSide equals 0, + /// then the normal vector of this face equals the one of the + /// parallel plane exactly. Otherwise, the normal of the plane + /// has to be multiplied by -1 to point into the right direction. + /// Afterwards we have an index into the surfedges lump, as + /// well as the count of consecutive surfedges from that position. + /// Furthermore there is an index into the texture info lump, + /// which is used to find the BSPTEXINFO structure needed to + /// calculate the texture coordinates for this face. Afterwards, + /// there are four bytes giving some lighting information (partly + /// used by the renderer to hide sky surfaces). Finally we have + /// an offset in byes giving the beginning of the binary lightmap + /// data of this face in the lighting lump. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class Face + { + /// + /// Plane the face is parallel to + /// + public ushort PlaneIndex; + + /// + /// Set if different normals orientation + /// + public ushort PlaneSideCount; + + /// + /// Index of the first surfedge + /// + public uint FirstEdgeIndex; + + /// + /// Number of consecutive surfedges + /// + public ushort NumberOfEdges; + + /// + /// Index of the texture info structure + /// + public ushort TextureInfoIndex; + + /// + /// Specify lighting styles + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[]? LightingStyles = new byte[4]; + + /// + /// Offsets into the raw lightmap data; if less than zero, + /// then a lightmap was not baked for the given face. + /// + public int LightmapOffsetCount; + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/FacesLump.cs b/SabreTools.Models/BSP/FacesLump.cs new file mode 100644 index 0000000..2c5a00c --- /dev/null +++ b/SabreTools.Models/BSP/FacesLump.cs @@ -0,0 +1,31 @@ +namespace SabreTools.Models.BSP +{ + /// + /// The face lump contains the surfaces of the scene. + /// + /// The first number of this data structure is an index into + /// the planes lump giving a plane which is parallel to this + /// face (meaning they share the same normal). The second + /// value may be seen as a boolean. If nPlaneSide equals 0, + /// then the normal vector of this face equals the one of the + /// parallel plane exactly. Otherwise, the normal of the plane + /// has to be multiplied by -1 to point into the right direction. + /// Afterwards we have an index into the surfedges lump, as + /// well as the count of consecutive surfedges from that position. + /// Furthermore there is an index into the texture info lump, + /// which is used to find the BSPTEXINFO structure needed to + /// calculate the texture coordinates for this face. Afterwards, + /// there are four bytes giving some lighting information (partly + /// used by the renderer to hide sky surfaces). Finally we have + /// an offset in byes giving the beginning of the binary lightmap + /// data of this face in the lighting lump. + /// + /// + public sealed class FacesLump : Lump + { + /// + /// Faces + /// + public Face[]? Faces { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/File.cs b/SabreTools.Models/BSP/File.cs index d448759..2cc23bf 100644 --- a/SabreTools.Models/BSP/File.cs +++ b/SabreTools.Models/BSP/File.cs @@ -4,6 +4,7 @@ namespace SabreTools.Models.BSP /// Half-Life Level /// /// + /// public sealed class File { /// @@ -12,18 +13,9 @@ namespace SabreTools.Models.BSP public Header? Header { get; set; } /// - /// Lumps + /// Lump data /// - public Lump?[]? Lumps { get; set; } - - /// - /// Texture header data - /// - public TextureHeader? TextureHeader { get; set; } - - /// - /// Textures - /// - public Texture?[]? Textures { get; set; } + /// 15 entries + public Lump[]? Lumps { get; set; } = new Lump[Constants.HEADER_LUMPS]; } } \ No newline at end of file diff --git a/SabreTools.Models/BSP/Header.cs b/SabreTools.Models/BSP/Header.cs index ddab40b..a776593 100644 --- a/SabreTools.Models/BSP/Header.cs +++ b/SabreTools.Models/BSP/Header.cs @@ -3,12 +3,20 @@ using System.Runtime.InteropServices; namespace SabreTools.Models.BSP { /// + /// [StructLayout(LayoutKind.Sequential)] public sealed class Header { /// /// Version /// - public uint Version; + /// Must be 30 for a valid HL BSP file + public int Version; + + /// + /// Lumps + /// + /// 15 entries + public LumpTableEntry[]? Lumps { get; set; } } } \ No newline at end of file diff --git a/SabreTools.Models/BSP/Leaf.cs b/SabreTools.Models/BSP/Leaf.cs new file mode 100644 index 0000000..64f9219 --- /dev/null +++ b/SabreTools.Models/BSP/Leaf.cs @@ -0,0 +1,67 @@ +using System.Runtime.InteropServices; + +namespace SabreTools.Models.BSP +{ + /// + /// The leaves lump contains the leaves of the BSP tree. + /// + /// The first entry of this struct is the type of the content + /// of this leaf. It can be one of the predefined values, found + /// in the compiler source codes, and is litte relevant for the + /// actual rendering process. All the more important is the + /// next integer containing the offset into the vis lump. It + /// defines the start of the raw PVS data for this leaf. If this + /// value equals -1, no VIS lists are available for this leaf, + /// usually if the map has been built without the VIS compiler. + /// The next two 16bit integer triples span the bounding box of + /// this leaf. Furthermore, the struct contains an index pointing + /// into the array of marksurfaces loaded from the marksufaces + /// lump as well as the number of consecutive marksurfaces belonging + /// to this leaf. The marksurfaces are looped through during the + /// rendering process and point to the actual faces. The final 4 bytes + /// specify the volume of ambient sounds in Quake, but are unused in + /// GoldSrc. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class Leaf + { + /// + /// Contents enumeration + /// + public Contents Contents; + + /// + /// Offset into the visibility lump + /// + public int VisOffset; + + /// + /// Defines bounding box + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public short[] Mins = new short[3]; + + /// + /// Defines bounding box + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public short[] Maxs = new short[3]; + + /// + /// Index into marksurfaces array + /// + public ushort FirstMarkSurfaceIndex; + + /// + /// Count of marksurfaces array + /// + public ushort MarkSurfacesCount; + + /// + /// Ambient sound levels + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[]? AmbientLevels = new byte[4]; + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/LeavesLump.cs b/SabreTools.Models/BSP/LeavesLump.cs new file mode 100644 index 0000000..65eabf3 --- /dev/null +++ b/SabreTools.Models/BSP/LeavesLump.cs @@ -0,0 +1,31 @@ +namespace SabreTools.Models.BSP +{ + /// + /// The leaves lump contains the leaves of the BSP tree. + /// + /// The first entry of this struct is the type of the content + /// of this leaf. It can be one of the predefined values, found + /// in the compiler source codes, and is litte relevant for the + /// actual rendering process. All the more important is the + /// next integer containing the offset into the vis lump. It + /// defines the start of the raw PVS data for this leaf. If this + /// value equals -1, no VIS lists are available for this leaf, + /// usually if the map has been built without the VIS compiler. + /// The next two 16bit integer triples span the bounding box of + /// this leaf. Furthermore, the struct contains an index pointing + /// into the array of marksurfaces loaded from the marksufaces + /// lump as well as the number of consecutive marksurfaces belonging + /// to this leaf. The marksurfaces are looped through during the + /// rendering process and point to the actual faces. The final 4 bytes + /// specify the volume of ambient sounds in Quake, but are unused in + /// GoldSrc. + /// + /// + public sealed class LeavesLump : Lump + { + /// + /// Leaves + /// + public Leaf[]? Leaves { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/LightmapLump.cs b/SabreTools.Models/BSP/LightmapLump.cs new file mode 100644 index 0000000..12cf9d7 --- /dev/null +++ b/SabreTools.Models/BSP/LightmapLump.cs @@ -0,0 +1,18 @@ +namespace SabreTools.Models.BSP +{ + /// + /// This is one of the largest lumps in the BSP file. The lightmap + /// lump stores all lightmaps used in the entire map. The lightmaps + /// are arrays of triples of bytes (3 channel color, RGB) and stored + /// continuously. + /// + /// + public sealed class LightmapLump : Lump + { + /// + /// Lightmap RGB values + /// + /// Array of 3-byte values + public byte[,]? Lightmap { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/Lump.cs b/SabreTools.Models/BSP/Lump.cs index 253e253..6dd4e8f 100644 --- a/SabreTools.Models/BSP/Lump.cs +++ b/SabreTools.Models/BSP/Lump.cs @@ -1,19 +1,11 @@ -using System.Runtime.InteropServices; - namespace SabreTools.Models.BSP { - /// - [StructLayout(LayoutKind.Sequential)] - public sealed class Lump + /// + /// Represents a common Lump type + /// + /// + public abstract class Lump { - /// - /// Offset - /// - public uint Offset; - - /// - /// Length - /// - public uint Length; + // No shared fields between types } } \ No newline at end of file diff --git a/SabreTools.Models/BSP/LumpTableEntry.cs b/SabreTools.Models/BSP/LumpTableEntry.cs new file mode 100644 index 0000000..875837e --- /dev/null +++ b/SabreTools.Models/BSP/LumpTableEntry.cs @@ -0,0 +1,20 @@ +using System.Runtime.InteropServices; + +namespace SabreTools.Models.BSP +{ + /// + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class LumpTableEntry + { + /// + /// File offset to data + /// + public int Offset; + + /// + /// Length of data + /// + public int Length; + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/MarksurfacesLump.cs b/SabreTools.Models/BSP/MarksurfacesLump.cs new file mode 100644 index 0000000..1fa00d6 --- /dev/null +++ b/SabreTools.Models/BSP/MarksurfacesLump.cs @@ -0,0 +1,19 @@ +namespace SabreTools.Models.BSP +{ + /// + /// The marksurfaces lump is a simple array of short integers. + /// + /// This lump is a simple table for redirecting the marksurfaces + /// indexes in the leafs to the actial face indexes. A leaf inserts + /// it's marksurface indexes into this array and gets the associated + /// faces contained within this leaf. + /// + /// + public sealed class MarksurfacesLump : Lump + { + /// + /// Marksurfaces + /// + public ushort[]? Marksurfaces { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/MipTexture.cs b/SabreTools.Models/BSP/MipTexture.cs new file mode 100644 index 0000000..7c53b1d --- /dev/null +++ b/SabreTools.Models/BSP/MipTexture.cs @@ -0,0 +1,44 @@ +using System.Runtime.InteropServices; + +namespace SabreTools.Models.BSP +{ + /// + /// Each of this structs describes a texture. The name of the + /// texture is a string and may be 16 characters long (including + /// the null-character at the end, char equals a 8bit signed + /// integer). The name of the texture is needed, if the texture + /// has to be found and loaded from an external WAD file. + /// Furthermore, the struct contains the width and height of + /// the texture. The 4 offsets at the end can either be zero, + /// if the texture is stored in an external WAD file, or point + /// to the beginnings of the binary texture data within the + /// texture lump relative to the beginning of it's BSPMIPTEX struct. + /// + /// + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class MipTexture + { + /// + /// Name of texture + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.MAXTEXTURENAME)] + public string? Name; + + /// + /// Extends of the texture + /// + public uint Width; + + /// + /// Extends of the texture + /// + public uint Height; + + /// + /// Offsets to texture mipmaps BSPMIPTEX + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.MIPLEVELS)] + public uint[]? Offsets; + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/Model.cs b/SabreTools.Models/BSP/Model.cs new file mode 100644 index 0000000..1cd65f8 --- /dev/null +++ b/SabreTools.Models/BSP/Model.cs @@ -0,0 +1,65 @@ +using System.Runtime.InteropServices; + +namespace SabreTools.Models.BSP +{ + /// + /// A model is kind of a mini BSP tree. Its size is determinded + /// by the bounding box spaned by the first to members of this + /// struct. The major difference between a model and the BSP + /// tree holding the scene is that the models use a local + /// coordinate system for their vertexes and just state its + /// origin in world coordinates. During rendering the coordinate + /// system is translated to the origin of the model (glTranslate()) + /// and moved back after the models BSP tree has been traversed. + /// Furthermore their are 4 indexes into node arrays. The first + /// one has proofed to index the root node of the mini BSP tree + /// used for rendering. The other three indexes could probably be + /// used for collision detection, meaning they point into the + /// clipnodes, but I am not sure about this. The meaning of the + /// next value is also somehow unclear to me. Finally their are + /// direct indexes into the faces array, not taking the redirecting + /// by the marksurfaces. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class Model + { + /// + /// Defines bounding box + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public float[]? Mins = new float[3]; + + /// + /// Defines bounding box + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public float[]? Maxs = new float[3]; + + /// + /// Coordinates to move the coordinate system + /// + public Vector3D OriginVector; + + /// + /// Index into nodes array + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.MAX_MAP_HULLS)] + public int[]? HeadnodesIndex = new int[Constants.MAX_MAP_HULLS]; + + /// + /// ??? + /// + public int VisLeafsCount; + + /// + /// Index into faces + /// + public int FirstFaceIndex; + + /// + /// Count of faces + /// + public int FacesCount; + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/ModelsLump.cs b/SabreTools.Models/BSP/ModelsLump.cs new file mode 100644 index 0000000..ac4874f --- /dev/null +++ b/SabreTools.Models/BSP/ModelsLump.cs @@ -0,0 +1,29 @@ +namespace SabreTools.Models.BSP +{ + /// + /// A model is kind of a mini BSP tree. Its size is determinded + /// by the bounding box spaned by the first to members of this + /// struct. The major difference between a model and the BSP + /// tree holding the scene is that the models use a local + /// coordinate system for their vertexes and just state its + /// origin in world coordinates. During rendering the coordinate + /// system is translated to the origin of the model (glTranslate()) + /// and moved back after the models BSP tree has been traversed. + /// Furthermore their are 4 indexes into node arrays. The first + /// one has proofed to index the root node of the mini BSP tree + /// used for rendering. The other three indexes could probably be + /// used for collision detection, meaning they point into the + /// clipnodes, but I am not sure about this. The meaning of the + /// next value is also somehow unclear to me. Finally their are + /// direct indexes into the faces array, not taking the redirecting + /// by the marksurfaces. + /// + /// + public sealed class ModelsLump : Lump + { + /// + /// Model + /// + public Model[]? Models { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/Node.cs b/SabreTools.Models/BSP/Node.cs new file mode 100644 index 0000000..9968b1c --- /dev/null +++ b/SabreTools.Models/BSP/Node.cs @@ -0,0 +1,61 @@ +using System.Runtime.InteropServices; + +namespace SabreTools.Models.BSP +{ + /// + /// This lump is simple again and contains an array of binary + /// structures, the nodes, which are a major part of the BSP tree. + /// + /// Every BSPNODE structure represents a node in the BSP tree and + /// every node equals more or less a division step of the BSP algorithm. + /// Therefore, each node has an index (iPlane) referring to a plane + /// in the plane lump which devides the node into its two child nodes. + /// The childnodes are also stored as indexes. Contrary to the plane + /// index, the node index for the child is signed. If the index is + /// larger than 0, the index indicates a child node. If it is equal + /// to or smaller than zero (no valid array index), the bitwise + /// inversed value of the index gives an index into the leaves lump. + /// Additionally two points (nMins, nMaxs) span the bounding box + /// (AABB, axis aligned bounding box) delimitting the space of the node. + /// Finally firstFace indexes into the face lump and spezifies the + /// first of nFaces surfaces contained in this node. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class Node + { + /// + /// Index into Planes lump + /// + public uint PlaneIndex; + + /// + /// If > 0, then indices into Nodes. + /// Otherwise bitwise inverse indices into Leafs + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public ushort[] Children = new ushort[2]; + + /// + /// Defines bounding box + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public ushort[] Mins = new ushort[3]; + + /// + /// Defines bounding box + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public ushort[] Maxs = new ushort[3]; + + /// + /// Index into Faces + /// + public ushort FirstFace; + + /// + /// Count of Faces + /// + public ushort FaceCount; + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/NodesLump.cs b/SabreTools.Models/BSP/NodesLump.cs new file mode 100644 index 0000000..8e3b1b2 --- /dev/null +++ b/SabreTools.Models/BSP/NodesLump.cs @@ -0,0 +1,29 @@ +namespace SabreTools.Models.BSP +{ + /// + /// This lump is simple again and contains an array of binary + /// structures, the nodes, which are a major part of the BSP tree. + /// + /// Every BSPNODE structure represents a node in the BSP tree and + /// every node equals more or less a division step of the BSP algorithm. + /// Therefore, each node has an index (iPlane) referring to a plane + /// in the plane lump which devides the node into its two child nodes. + /// The childnodes are also stored as indexes. Contrary to the plane + /// index, the node index for the child is signed. If the index is + /// larger than 0, the index indicates a child node. If it is equal + /// to or smaller than zero (no valid array index), the bitwise + /// inversed value of the index gives an index into the leaves lump. + /// Additionally two points (nMins, nMaxs) span the bounding box + /// (AABB, axis aligned bounding box) delimitting the space of the node. + /// Finally firstFace indexes into the face lump and spezifies the + /// first of nFaces surfaces contained in this node. + /// + /// + public sealed class NodesLump : Lump + { + /// + /// Nodes + /// + public Node[]? Nodes { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/Plane.cs b/SabreTools.Models/BSP/Plane.cs new file mode 100644 index 0000000..4aa5ac4 --- /dev/null +++ b/SabreTools.Models/BSP/Plane.cs @@ -0,0 +1,39 @@ +using System.Runtime.InteropServices; + +namespace SabreTools.Models.BSP +{ + /// + /// Each of this structures defines a plane in 3-dimensional + /// space by using the Hesse normal form: normal * point - distance = 0 + /// + /// Where vNormal is the normalized normal vector of the plane + /// and fDist is the distance of the plane to the origin of + /// the coord system. Additionally, the structure also saves an + /// integer describing the orientation of the plane in space. + /// If nType equals PLANE_X, then the normal of the plane will + /// be parallel to the x axis, meaning the plane is perpendicular + /// to the x axis. If nType equals PLANE_ANYX, then the plane's + /// normal is nearer to the x axis then to any other axis. + /// This information is used by the renderer to speed up some + /// computations. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class Plane + { + /// + /// The planes normal vector + /// + public Vector3D NormalVector; + + /// + /// Plane equation is: vNormal * X = fDist + /// + public float Distance; + + /// + /// Plane type + /// + public PlaneType PlaneType; + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/PlaneLump.cs b/SabreTools.Models/BSP/PlaneLump.cs new file mode 100644 index 0000000..d4c44bb --- /dev/null +++ b/SabreTools.Models/BSP/PlaneLump.cs @@ -0,0 +1,15 @@ +namespace SabreTools.Models.BSP +{ + /// + /// Each of this structures defines a plane in 3-dimensional + /// space by using the Hesse normal form: normal * point - distance = 0 + /// + /// + public sealed class PlaneLump + { + /// + /// Planes + /// + public Plane[]? Planes { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/SurfedgesLump.cs b/SabreTools.Models/BSP/SurfedgesLump.cs new file mode 100644 index 0000000..3cd1888 --- /dev/null +++ b/SabreTools.Models/BSP/SurfedgesLump.cs @@ -0,0 +1,21 @@ +namespace SabreTools.Models.BSP +{ + /// + /// This lump represents pretty much the same mechanism as the marksurfaces. + /// A face can insert its surfedge indexes into this array to get the + /// corresponding edges delimitting the face and further pointing to the + /// vertexes, which are required for rendering. The index can be positive + /// or negative. If the value of the surfedge is positive, the first vertex + /// of the edge is used as vertex for rendering the face, otherwise, the + /// value is multiplied by -1 and the second vertex of the indexed edge is + /// used. + /// + /// + public sealed class SurfedgesLump : Lump + { + /// + /// Surfedges + /// + public int[]? Surfedges { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/Texinfo.cs b/SabreTools.Models/BSP/Texinfo.cs new file mode 100644 index 0000000..1860400 --- /dev/null +++ b/SabreTools.Models/BSP/Texinfo.cs @@ -0,0 +1,52 @@ +using System.Runtime.InteropServices; + +namespace SabreTools.Models.BSP +{ + /// + /// The texinfo lump contains informations about how textures are + /// applied to surfaces. The lump itself is an array of binary data + /// structures. + /// + /// This struct is mainly responsible for the calculation of the texture + /// coordinates (vS, fSShift, vT, fTShift). This values determine the + /// position of the texture on the surface. The iMiptex integer refers + /// to the textures in the texture lump and would be the index in an + /// array of BSPMITEX structs. Finally, there are 4 Bytes used for flags. + /// Only one flag is used by the vanilla engine, being 0x1 for disabling + /// lightmaps and subdivision for the surface (used by sky and liquids). + /// + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class Texinfo + { + /// + /// S-vector + /// + public Vector3D SVector; + + /// + /// Texture shift in the S direction + /// + public float TextureSShift; + + /// + /// T-vector + /// + public Vector3D TVector; + + /// + /// Texture shift in the T direction + /// + public float TextureTShift; + + /// + /// Index into textures array + /// + public uint MiptexIndex; + + /// + /// Texture flags + /// + public TextureFlag Flags; + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/TexinfoLump.cs b/SabreTools.Models/BSP/TexinfoLump.cs new file mode 100644 index 0000000..0583a33 --- /dev/null +++ b/SabreTools.Models/BSP/TexinfoLump.cs @@ -0,0 +1,24 @@ +namespace SabreTools.Models.BSP +{ + /// + /// The texinfo lump contains informations about how textures are + /// applied to surfaces. The lump itself is an array of binary data + /// structures. + /// + /// This struct is mainly responsible for the calculation of the texture + /// coordinates (vS, fSShift, vT, fTShift). This values determine the + /// position of the texture on the surface. The iMiptex integer refers + /// to the textures in the texture lump and would be the index in an + /// array of BSPMITEX structs. Finally, there are 4 Bytes used for flags. + /// Only one flag is used by the vanilla engine, being 0x1 for disabling + /// lightmaps and subdivision for the surface (used by sky and liquids). + /// + /// + public sealed class TexinfoLump : Lump + { + /// + /// Texinfos + /// + public Texinfo[]? Texinfos { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/Texture.cs b/SabreTools.Models/BSP/Texture.cs deleted file mode 100644 index abb2c89..0000000 --- a/SabreTools.Models/BSP/Texture.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace SabreTools.Models.BSP -{ - /// - public sealed class Texture - { - /// - /// Name - /// - public string? Name { get; set; } - - /// - /// Width - /// - public uint Width { get; set; } - - /// - /// Height - /// - public uint Height { get; set; } - - /// - /// Offsets - /// - public uint[]? Offsets { get; set; } - - /// - /// Texture data - /// - public byte[]? TextureData { get; set; } - - /// - /// Palette size - /// - public uint PaletteSize { get; set; } - - /// - /// Palette data - /// - public byte[]? PaletteData { get; set; } - } -} \ No newline at end of file diff --git a/SabreTools.Models/BSP/TextureHeader.cs b/SabreTools.Models/BSP/TextureHeader.cs index b9ec245..4d82b16 100644 --- a/SabreTools.Models/BSP/TextureHeader.cs +++ b/SabreTools.Models/BSP/TextureHeader.cs @@ -1,17 +1,18 @@ namespace SabreTools.Models.BSP { /// + /// public sealed class TextureHeader { /// - /// Texture count + /// Number of BSPMIPTEX structures /// - public uint TextureCount { get; set; } + public uint MipTextureCount { get; set; } /// /// Offsets /// - /// TextureCount entries - public uint[]? Offsets { get; set; } + /// entries + public int[]? Offsets { get; set; } } } \ No newline at end of file diff --git a/SabreTools.Models/BSP/TextureLump.cs b/SabreTools.Models/BSP/TextureLump.cs new file mode 100644 index 0000000..1d77061 --- /dev/null +++ b/SabreTools.Models/BSP/TextureLump.cs @@ -0,0 +1,16 @@ +namespace SabreTools.Models.BSP +{ + /// + public sealed class TextureLump : Lump + { + /// + /// Texture header data + /// + public TextureHeader? Header { get; set; } + + /// + /// Textures + /// + public MipTexture[]? Textures { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/Vector3D.cs b/SabreTools.Models/BSP/Vector3D.cs new file mode 100644 index 0000000..49cac66 --- /dev/null +++ b/SabreTools.Models/BSP/Vector3D.cs @@ -0,0 +1,17 @@ +using System.Runtime.InteropServices; + +namespace SabreTools.Models.BSP +{ + /// + /// There is a common struct used to represent a point in + /// 3-dimensional space which is used throughout the file + /// spec and the code of the hlbsp project. + /// + [StructLayout(LayoutKind.Sequential)] + public struct Vector3D + { + public float X { get; set; } + public float Y { get; set; } + public float Z { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/VerticesLump.cs b/SabreTools.Models/BSP/VerticesLump.cs new file mode 100644 index 0000000..c89b6c9 --- /dev/null +++ b/SabreTools.Models/BSP/VerticesLump.cs @@ -0,0 +1,15 @@ +namespace SabreTools.Models.BSP +{ + /// + /// This lump simply consists of all vertices of the BSP tree. + /// They are stored as a primitve array of triples of floats. + /// + /// + public sealed class VerticesLump : Lump + { + /// + /// Vertices + /// + public Vector3D[]? Vertices { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/BSP/VisibilityLump.cs b/SabreTools.Models/BSP/VisibilityLump.cs new file mode 100644 index 0000000..b648517 --- /dev/null +++ b/SabreTools.Models/BSP/VisibilityLump.cs @@ -0,0 +1,18 @@ +namespace SabreTools.Models.BSP +{ + /// + /// The VIS lump contains data, which is irrelevant to the actual + /// BSP tree, but offers a way to boost up the speed of the + /// renderer significantly. Especially complex maps profit from + /// the use if this data. This lump contains the so-called + /// Potentially Visible Sets (PVS) (also called VIS lists) in the + /// same amout of leaves of the tree, the user can enter (often + /// referred to as VisLeaves). The visiblilty lists are stored as + /// sequences of bitfields, which are run-length encoded. + /// + /// + public sealed class VisibilityLump + { + public byte[]? Data { get; set; } + } +} \ No newline at end of file