diff --git a/LibBSP/LibBSP.csproj b/LibBSP/LibBSP.csproj index ae0a154..5fedca7 100644 --- a/LibBSP/LibBSP.csproj +++ b/LibBSP/LibBSP.csproj @@ -65,6 +65,7 @@ + @@ -75,6 +76,7 @@ + diff --git a/LibBSP/Source/Structs/BSP/BSP.cs b/LibBSP/Source/Structs/BSP/BSP.cs index b9013a0..4797a09 100644 --- a/LibBSP/Source/Structs/BSP/BSP.cs +++ b/LibBSP/Source/Structs/BSP/BSP.cs @@ -94,7 +94,8 @@ namespace LibBSP { private Lump _edges; private NumList _surfEdges; private Lump _models; - // public byte[] pvs; + private Visibility _visibility; + private Lightmaps _lightmaps; // Quake 2 private Lump _brushes; private Lump _brushSides; @@ -359,6 +360,36 @@ namespace LibBSP { } } + /// + /// A object holding leaf visiblity data for this . + /// + public Visibility visibility { + get { + if (_visibility == null) { + int index = Visibility.GetIndexForLump(version); + if (index >= 0) { + _visibility = new Visibility(reader.ReadLump(this[index]), this, this[index]); + } + } + return _visibility; + } + } + + /// + /// A object holding lightmap data for this . + /// + public Lightmaps lightmaps { + get { + if (_lightmaps == null) { + int index = Lightmaps.GetIndexForLump(version); + if (index >= 0) { + _lightmaps = new Lightmaps(reader.ReadLump(this[index]), this, this[index]); + } + } + return _lightmaps; + } + } + /// /// A of objects in the BSP file representing the Original Faces, if available. /// diff --git a/LibBSP/Source/Structs/BSP/Lumps/Lightmaps.cs b/LibBSP/Source/Structs/BSP/Lumps/Lightmaps.cs new file mode 100644 index 0000000..8137a62 --- /dev/null +++ b/LibBSP/Source/Structs/BSP/Lumps/Lightmaps.cs @@ -0,0 +1,104 @@ +using System; + +namespace LibBSP { +#if UNITY + using Color = UnityEngine.Color32; +#elif GODOT + using Color = Godot.Color; +#else + using Color = System.Drawing.Color; +#endif + + /// + /// Holds the visibility data for a BSP. + /// + public class Lightmaps : ILump { + + /// + /// The this came from. + /// + public BSP Bsp { get; protected set; } + + /// + /// The associated with this . + /// + public LumpInfo LumpInfo { get; protected set; } + + /// + /// Array of bytes used as the data source for visibility info. + /// + public byte[] Data { get; protected set; } + + /// + /// Parses the passed byte array into a object. + /// + /// Array of bytes to parse. + /// The this lump came from. + /// The associated with this lump. + /// was null. + public Lightmaps(byte[] data, BSP bsp, LumpInfo lumpInfo = default(LumpInfo)) { + if (data == null) { + throw new ArgumentNullException(); + } + + Data = data; + Bsp = bsp; + LumpInfo = lumpInfo; + } + + /// + /// Gets the index for this lump in the BSP file for a specific map format. + /// + /// The map type. + /// Index for this lump, or -1 if the format doesn't have this lump. + public static int GetIndexForLump(MapType type) { + switch (type) { + case MapType.CoD: + case MapType.CoD2: + case MapType.CoD4: { + return 1; + } + case MapType.MOHAA: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.FAKK: { + return 2; + } + case MapType.Quake2: + case MapType.Daikatana: + case MapType.SiN: + case MapType.SoF: { + return 7; + } + case MapType.Quake: + case MapType.GoldSrc: + case MapType.Vindictus: + case MapType.TacticalInterventionEncrypted: + case MapType.Source17: + case MapType.Source18: + case MapType.Source19: + case MapType.Source20: + case MapType.Source21: + case MapType.Source22: + case MapType.Source23: + case MapType.Source27: + case MapType.L4D2: + case MapType.DMoMaM: { + return 8; + } + case MapType.Nightfire: { + return 10; + } + case MapType.Quake3: + case MapType.Raven: { + return 14; + } + case MapType.Titanfall: + default: { + return -1; + } + } + } + + } +} diff --git a/LibBSP/Source/Structs/BSP/Lumps/Visibility.cs b/LibBSP/Source/Structs/BSP/Lumps/Visibility.cs new file mode 100644 index 0000000..ac2e529 --- /dev/null +++ b/LibBSP/Source/Structs/BSP/Lumps/Visibility.cs @@ -0,0 +1,303 @@ +using System; + +namespace LibBSP { + + /// + /// Holds the visibility data for a BSP. + /// + public class Visibility : ILump { + + /// + /// The this came from. + /// + public BSP Bsp { get; protected set; } + + /// + /// The associated with this . + /// + public LumpInfo LumpInfo { get; protected set; } + + /// + /// Array of bytes used as the data source for visibility info. + /// + public byte[] Data { get; protected set; } + + /// + /// Gets or sets the number of visibility clusters in this lump. + /// + public int NumClusters { + get { + switch (Bsp.version) { + case MapType.Quake2: + case MapType.SiN: + case MapType.Daikatana: + case MapType.SoF: + case MapType.Vindictus: + case MapType.TacticalInterventionEncrypted: + case MapType.Source17: + case MapType.Source18: + case MapType.Source19: + case MapType.Source20: + case MapType.Source21: + case MapType.Source22: + case MapType.Source23: + case MapType.Source27: + case MapType.L4D2: + case MapType.DMoMaM: + case MapType.Quake3: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: + case MapType.CoD: + case MapType.CoD2: + case MapType.Raven: { + return BitConverter.ToInt32(Data, 0); + } + } + + return -1; + } + set { + switch (Bsp.version) { + case MapType.Quake2: + case MapType.SiN: + case MapType.Daikatana: + case MapType.SoF: + case MapType.Vindictus: + case MapType.TacticalInterventionEncrypted: + case MapType.Source17: + case MapType.Source18: + case MapType.Source19: + case MapType.Source20: + case MapType.Source21: + case MapType.Source22: + case MapType.Source23: + case MapType.Source27: + case MapType.L4D2: + case MapType.DMoMaM: + case MapType.Quake3: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: + case MapType.CoD: + case MapType.CoD2: + case MapType.Raven: { + BitConverter.GetBytes(value).CopyTo(Data, 0); + break; + } + } + } + } + + /// + /// Gets or sets the size in bytes of the visibility data for this map's clusters. + /// + public int ClusterSize { + get { + switch (Bsp.version) { + case MapType.Quake3: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: + case MapType.CoD: + case MapType.CoD2: + case MapType.Raven: { + return BitConverter.ToInt32(Data, 4); + } + } + + return -1; + } + set { + switch (Bsp.version) { + case MapType.Quake3: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: + case MapType.CoD: + case MapType.CoD2: + case MapType.Raven: { + BitConverter.GetBytes(value).CopyTo(Data, 4); + break; + } + } + } + } + + /// + /// Parses the passed byte array into a object. + /// + /// Array of bytes to parse. + /// The this lump came from. + /// The associated with this lump. + /// was null. + public Visibility(byte[] data, BSP bsp, LumpInfo lumpInfo = default(LumpInfo)) { + if (data == null) { + throw new ArgumentNullException(); + } + + Data = data; + Bsp = bsp; + LumpInfo = lumpInfo; + } + + /// + /// Can see the or cluster at index . + /// + /// The . + /// The index of the other or cluster to determine visibility for. + /// Whether can see the or cluster at index . + public bool CanSee(Leaf leaf, int other) { + int offset = GetOffsetForClusterPVS(leaf.Visibility); + if (offset < 0) { + offset = leaf.Visibility; + } + + for (int i = 1; i < leaf.Parent.Bsp.leaves.Count; offset++) { + if (Data[offset] == 0 && Bsp.version != MapType.Nightfire) { + i += 8 * Data[offset + 1]; + if (i > other) { + return false; + } + offset++; + } else { + for (int bit = 1; bit != 0; bit = bit * 2, i++) { + if (other == i) { + return ((Data[offset] & bit) > 0); + } + } + } + } + return false; + } + + /// + /// Gets the offset from the beginning of this lump for the given 's PVS data, if applicable. + /// + /// The cluster to get the offset to the PVS data for. + /// + /// The offset from the beginning of this lump to the given 's PVS data, or + /// -1 if this BSP does not use vis clusters. + /// + public int GetOffsetForClusterPVS(int cluster) { + switch (Bsp.version) { + case MapType.Quake2: + case MapType.Daikatana: + case MapType.SoF: + case MapType.SiN: + case MapType.Source17: + case MapType.Source18: + case MapType.Source19: + case MapType.Source20: + case MapType.Source21: + case MapType.Source22: + case MapType.Source23: + case MapType.Source27: + case MapType.DMoMaM: + case MapType.L4D2: + case MapType.TacticalInterventionEncrypted: + case MapType.Vindictus: { + return BitConverter.ToInt32(Data, 4 + (cluster * 8)); + } + } + + return -1; + } + + /// + /// Gets the offset from the beginning of this lump for the given 's PAS data, if applicable. + /// + /// The cluster to get the offset to the PAS data for. + /// + /// The offset from the beginning of this lump to the given 's PAS data, or + /// -1 if this BSP does not use vis clusters. + /// + public int GetOffsetForClusterPAS(int cluster) { + switch (Bsp.version) { + case MapType.Quake2: + case MapType.Daikatana: + case MapType.SoF: + case MapType.SiN: + case MapType.Source17: + case MapType.Source18: + case MapType.Source19: + case MapType.Source20: + case MapType.Source21: + case MapType.Source22: + case MapType.Source23: + case MapType.Source27: + case MapType.DMoMaM: + case MapType.L4D2: + case MapType.TacticalInterventionEncrypted: + case MapType.Vindictus: { + return BitConverter.ToInt32(Data, 8 + (cluster * 8)); + } + } + + return -1; + } + + /// + /// Gets the index for this lump in the BSP file for a specific map format. + /// + /// The map type. + /// Index for this lump, or -1 if the format doesn't have this lump. + public static int GetIndexForLump(MapType type) { + switch (type) { + case MapType.Quake2: + case MapType.Daikatana: + case MapType.SiN: + case MapType.SoF: { + return 3; + } + case MapType.Quake: + case MapType.GoldSrc: + case MapType.Vindictus: + case MapType.TacticalInterventionEncrypted: + case MapType.Source17: + case MapType.Source18: + case MapType.Source19: + case MapType.Source20: + case MapType.Source21: + case MapType.Source22: + case MapType.Source23: + case MapType.Source27: + case MapType.L4D2: + case MapType.DMoMaM: { + return 4; + } + case MapType.Nightfire: { + return 7; + } + case MapType.FAKK: + case MapType.MOHAA: { + return 15; + } + case MapType.Quake3: + case MapType.Raven: { + return 16; + } + case MapType.STEF2: + case MapType.STEF2Demo: { + return 17; + } + case MapType.CoD: { + return 28; + } + case MapType.CoD2: { + return 36; + } + case MapType.CoD4: + case MapType.Titanfall: + default: { + return -1; + } + } + } + + } +}