diff --git a/LibBSP/Source/Structs/BSP/Model.cs b/LibBSP/Source/Structs/BSP/Model.cs index 48f6c88..53ded66 100644 --- a/LibBSP/Source/Structs/BSP/Model.cs +++ b/LibBSP/Source/Structs/BSP/Model.cs @@ -1,8 +1,19 @@ +#if UNITY_3_4 || UNITY_3_5 || UNITY_4_0 || UNITY_4_0_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5 || UNITY_5_3_OR_NEWER +#define UNITY +#endif + using System; using System.Collections.Generic; using System.Reflection; namespace LibBSP { +#if UNITY + using Vector3 = UnityEngine.Vector3; +#elif GODOT + using Vector3 = Godot.Vector3; +#else + using Vector3 = System.Numerics.Vector3; +#endif /// /// A class containing all data needed for the models lump in any given BSP. @@ -58,6 +69,9 @@ namespace LibBSP { [Index("nodes")] public int HeadNodeIndex { get { switch (MapType) { + case MapType.Nightfire: { + return BitConverter.ToInt32(Data, 24); + } case MapType.Quake: case MapType.Quake2: case MapType.Daikatana: @@ -87,6 +101,10 @@ namespace LibBSP { set { byte[] bytes = BitConverter.GetBytes(value); switch (MapType) { + case MapType.Nightfire: { + bytes.CopyTo(Data, 24); + break; + } case MapType.Quake: case MapType.Quake2: case MapType.Daikatana: @@ -114,6 +132,102 @@ namespace LibBSP { } } + /// + /// Gets or sets the index of the first head ClipNode used by this . + /// + public int HeadClipNode1Index { + get { + switch (MapType) { + case MapType.Nightfire: { + return BitConverter.ToInt32(Data, 28); + } + case MapType.Quake: { + return BitConverter.ToInt32(Data, 40); + } + default: { + return -1; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (MapType) { + case MapType.Nightfire: { + bytes.CopyTo(Data, 28); + break; + } + case MapType.Quake: { + bytes.CopyTo(Data, 40); + break; + } + } + } + } + + /// + /// Gets or sets the index of the second head ClipNode used by this . + /// + public int HeadClipNode2Index { + get { + switch (MapType) { + case MapType.Nightfire: { + return BitConverter.ToInt32(Data, 32); + } + case MapType.Quake: { + return BitConverter.ToInt32(Data, 44); + } + default: { + return -1; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (MapType) { + case MapType.Nightfire: { + bytes.CopyTo(Data, 32); + break; + } + case MapType.Quake: { + bytes.CopyTo(Data, 44); + break; + } + } + } + } + + /// + /// Gets or sets the index of the third head ClipNode used by this . + /// + public int HeadClipNode3Index { + get { + switch (MapType) { + case MapType.Nightfire: { + return BitConverter.ToInt32(Data, 36); + } + case MapType.Quake: { + return BitConverter.ToInt32(Data, 48); + } + default: { + return -1; + } + } + } + set { + byte[] bytes = BitConverter.GetBytes(value); + switch (MapType) { + case MapType.Nightfire: { + bytes.CopyTo(Data, 36); + break; + } + case MapType.Quake: { + bytes.CopyTo(Data, 48); + break; + } + } + } + } + /// /// Enumerates the s used by this . /// @@ -159,6 +273,9 @@ namespace LibBSP { case MapType.Nightfire: { return BitConverter.ToInt32(Data, 44); } + case MapType.Quake: { + return BitConverter.ToInt32(Data, 52); + } default: { return -1; } @@ -171,6 +288,10 @@ namespace LibBSP { bytes.CopyTo(Data, 44); break; } + case MapType.Quake: { + bytes.CopyTo(Data, 52); + break; + } } } } @@ -556,6 +677,212 @@ namespace LibBSP { } } + /// + /// Gets or sets the bounding box minimums for this model. + /// + public Vector3 Minimums { + get { + switch (MapType) { + case MapType.Quake: + case MapType.Quake2: + case MapType.Quake3: + case MapType.SiN: + case MapType.SoF: + case MapType.Raven: + case MapType.Nightfire: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: + case MapType.CoD: + case MapType.CoD2: + case MapType.CoD4: + case MapType.Titanfall: + case MapType.Daikatana: + 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.TacticalInterventionEncrypted: + case MapType.Vindictus: + case MapType.DMoMaM: { + return new Vector3(BitConverter.ToSingle(Data, 0), BitConverter.ToSingle(Data, 4), BitConverter.ToSingle(Data, 8)); + } + default: { + return new Vector3(0, 0, 0); + } + } + } + set { + switch (MapType) { + case MapType.Quake: + case MapType.Quake2: + case MapType.Quake3: + case MapType.SiN: + case MapType.SoF: + case MapType.Raven: + case MapType.Nightfire: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: + case MapType.CoD: + case MapType.CoD2: + case MapType.CoD4: + case MapType.Titanfall: + case MapType.Daikatana: + 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.TacticalInterventionEncrypted: + case MapType.Vindictus: + case MapType.DMoMaM: { + value.GetBytes().CopyTo(Data, 0); + break; + } + } + } + } + + /// + /// Gets or sets the bounding box maximums for this model. + /// + public Vector3 Maximums { + get { + switch (MapType) { + case MapType.Quake: + case MapType.Quake2: + case MapType.Quake3: + case MapType.Nightfire: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: + case MapType.CoD: + case MapType.CoD2: + case MapType.CoD4: + case MapType.Titanfall: + case MapType.Daikatana: + 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.TacticalInterventionEncrypted: + case MapType.Vindictus: + case MapType.DMoMaM: { + return new Vector3(BitConverter.ToSingle(Data, 12), BitConverter.ToSingle(Data, 16), BitConverter.ToSingle(Data, 20)); + } + default: { + return new Vector3(0, 0, 0); + } + } + } + set { + switch (MapType) { + case MapType.Quake: + case MapType.Quake2: + case MapType.Quake3: + case MapType.Nightfire: + case MapType.STEF2: + case MapType.STEF2Demo: + case MapType.MOHAA: + case MapType.FAKK: + case MapType.CoD: + case MapType.CoD2: + case MapType.CoD4: + case MapType.Titanfall: + case MapType.Daikatana: + 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.TacticalInterventionEncrypted: + case MapType.Vindictus: + case MapType.DMoMaM: { + value.GetBytes().CopyTo(Data, 12); + break; + } + } + } + } + + /// + /// Gets or sets the origin for this model. + /// + public Vector3 Origin { + get { + switch (MapType) { + case MapType.Quake2: + case MapType.Titanfall: + case MapType.Daikatana: + 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.SiN: + case MapType.SoF: + case MapType.TacticalInterventionEncrypted: + case MapType.Vindictus: + case MapType.DMoMaM: { + return new Vector3(BitConverter.ToSingle(Data, 24), BitConverter.ToSingle(Data, 28), BitConverter.ToSingle(Data, 32)); + } + default: { + return new Vector3(0, 0, 0); + } + } + } + set { + switch (MapType) { + case MapType.Quake2: + case MapType.Titanfall: + case MapType.Daikatana: + 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.SiN: + case MapType.SoF: + case MapType.TacticalInterventionEncrypted: + case MapType.Vindictus: + case MapType.DMoMaM: { + value.GetBytes().CopyTo(Data, 24); + break; + } + } + } + } + /// /// Creates a new object from a byte array. /// @@ -571,6 +898,54 @@ namespace LibBSP { Parent = parent; } + /// + /// Creates a new by copying the fields in , using + /// to get and + /// to use when creating the new . + /// If the 's 's is different from + /// the one from , it does not matter, because fields are copied by name. + /// + /// The to copy. + /// + /// The to use as the of the new . + /// Use null to use the 's instead. + /// + public Model(Model source, ILump parent = null) { + Parent = parent; + + if (parent != null && parent.Bsp != null) { + if (source.Parent != null && source.Parent.Bsp != null && source.Parent.Bsp.version == parent.Bsp.version && source.LumpVersion == parent.LumpInfo.version) { + Data = new byte[source.Data.Length]; + Array.Copy(source.Data, Data, source.Data.Length); + return; + } else { + Data = new byte[GetStructLength(parent.Bsp.version, parent.LumpInfo.version)]; + } + } else { + if (source.Parent != null && source.Parent.Bsp != null) { + Data = new byte[GetStructLength(source.Parent.Bsp.version, source.Parent.LumpInfo.version)]; + } else { + Data = new byte[GetStructLength(MapType.Undefined, 0)]; + } + } + + HeadNodeIndex = source.HeadNodeIndex; + HeadClipNode1Index = source.HeadClipNode1Index; + HeadClipNode2Index = source.HeadClipNode2Index; + HeadClipNode3Index = source.HeadClipNode3Index; + FirstLeafIndex = source.FirstLeafIndex; + NumLeaves = source.NumLeaves; + FirstBrushIndex = source.FirstBrushIndex; + NumBrushes = source.NumBrushes; + FirstFaceIndex = source.FirstFaceIndex; + NumFaces = source.NumFaces; + FirstPatchIndicesIndex = source.FirstPatchIndicesIndex; + NumPatchIndices = source.NumPatchIndices; + Minimums = source.Minimums; + Maximums = source.Maximums; + Origin = source.Origin; + } + /// /// Factory method to parse a byte array into a . ///