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;
+ }
+ }
+ }
+
+ }
+}