From 15a083a6497859a1fab8d5ee9fc1eddbcf93290a Mon Sep 17 00:00:00 2001 From: William Fowler Date: Tue, 5 Oct 2021 00:53:38 -0600 Subject: [PATCH] Add reading of mipmaps for Quake I and GoldSrc engines to Texture. --- LibBSP/Source/Structs/BSP/Lumps/Textures.cs | 24 +++- LibBSP/Source/Structs/BSP/Texture.cs | 130 +++++++++++--------- 2 files changed, 96 insertions(+), 58 deletions(-) diff --git a/LibBSP/Source/Structs/BSP/Lumps/Textures.cs b/LibBSP/Source/Structs/BSP/Lumps/Textures.cs index 023e76b..abb8308 100644 --- a/LibBSP/Source/Structs/BSP/Lumps/Textures.cs +++ b/LibBSP/Source/Structs/BSP/Lumps/Textures.cs @@ -104,10 +104,30 @@ namespace LibBSP { case MapType.GoldSrc: { int numElements = BitConverter.ToInt32(data, 0); structLength = 40; + int currentOffset; + int width; + int height; + int power; + int mipmapOffset; for (int i = 0; i < numElements; ++i) { byte[] myBytes = new byte[structLength]; - Array.Copy(data, BitConverter.ToInt32(data, (i + 1) * 4), myBytes, 0, structLength); - Add(new Texture(myBytes, this)); + byte[][] mipmaps = new byte[Texture.NumMipmaps][]; + currentOffset = BitConverter.ToInt32(data, (i + 1) * 4); + if (currentOffset >= 0) { + Array.Copy(data, currentOffset, myBytes, 0, structLength); + width = BitConverter.ToInt32(myBytes, 16); + height = BitConverter.ToInt32(myBytes, 20); + power = 1; + for (int j = 0; j < mipmaps.Length; ++j) { + mipmapOffset = BitConverter.ToInt32(myBytes, 24 + (4 * j)); + if (mipmapOffset > 0) { + mipmaps[j] = new byte[(width / power) * (height / power)]; + Array.Copy(data, currentOffset + mipmapOffset, mipmaps[j], 0, mipmaps[j].Length); + } + power *= 2; + } + } + Add(new Texture(myBytes, this, mipmaps)); } return; } diff --git a/LibBSP/Source/Structs/BSP/Texture.cs b/LibBSP/Source/Structs/BSP/Texture.cs index d997c69..67fdd54 100644 --- a/LibBSP/Source/Structs/BSP/Texture.cs +++ b/LibBSP/Source/Structs/BSP/Texture.cs @@ -29,6 +29,27 @@ namespace LibBSP { /// public struct Texture : ILumpObject { + /// + /// Number of MipMap levels used by Quake/GoldSrc engines. + /// + public const int NumMipmaps = 4; + /// + /// Index of the full mipmap. + /// + public const int FullMipmap = 0; + /// + /// Index of the half mipmap. + /// + public const int HalfMipmap = 1; + /// + /// Index of the quarter mipmap. + /// + public const int QuarterMipmap = 2; + /// + /// Index of the eighth mipmap. + /// + public const int EighthMipmap = 3; + /// /// The this came from. /// @@ -39,6 +60,19 @@ namespace LibBSP { /// public byte[] Data { get; private set; } + private byte[][] _mipmaps; + /// + /// Mipmap image data for this . + /// + public byte[][] Mipmaps { + get { + if (_mipmaps == null && MipmapFullOffset >= 0) { + _mipmaps = new byte[NumMipmaps][]; + } + return _mipmaps; + } + } + /// /// The to use to interpret . /// @@ -341,26 +375,26 @@ namespace LibBSP { } /// - /// Gets or sets the width of this . + /// Gets or sets the dimensions of this . /// - public uint Width { + public Vector2 Dimensions { get { switch (MapType) { case MapType.Quake: case MapType.GoldSrc: { - return BitConverter.ToUInt32(Data, 16); + return new Vector2(BitConverter.ToUInt32(Data, 16), BitConverter.ToUInt32(Data, 20)); } default: { - return 0; + return new Vector2(0, 0); } } } set { - byte[] bytes = BitConverter.GetBytes(value); switch (MapType) { case MapType.Quake: case MapType.GoldSrc: { - bytes.CopyTo(Data, 16); + BitConverter.GetBytes((int)value.X()).CopyTo(Data, 16); + BitConverter.GetBytes((int)value.Y()).CopyTo(Data, 20); break; } } @@ -368,44 +402,17 @@ namespace LibBSP { } /// - /// Gets or sets the height of this . + /// Gets the offset to the full mipmap of this . /// - public uint Height { + public int MipmapFullOffset { get { switch (MapType) { case MapType.Quake: case MapType.GoldSrc: { - return BitConverter.ToUInt32(Data, 20); + return BitConverter.ToInt32(Data, 24); } default: { - return 0; - } - } - } - set { - byte[] bytes = BitConverter.GetBytes(value); - switch (MapType) { - case MapType.Quake: - case MapType.GoldSrc: { - bytes.CopyTo(Data, 20); - break; - } - } - } - } - - /// - /// Gets the offset to the full version of this . - /// - public uint OffsetFull { - get { - switch (MapType) { - case MapType.Quake: - case MapType.GoldSrc: { - return BitConverter.ToUInt32(Data, 24); - } - default: { - return 0; + return -1; } } } @@ -422,17 +429,17 @@ namespace LibBSP { } /// - /// Gets the offset to the half version of this . + /// Gets the offset to the half mipmap of this . /// - public uint OffsetHalf { + public int MipmapHalfOffset { get { switch (MapType) { case MapType.Quake: case MapType.GoldSrc: { - return BitConverter.ToUInt32(Data, 28); + return BitConverter.ToInt32(Data, 28); } default: { - return 0; + return -1; } } } @@ -449,17 +456,17 @@ namespace LibBSP { } /// - /// Gets the offset to the quarter version of this . + /// Gets the offset to the quarter mipmap of this . /// - public uint OffsetQuarter { + public int MipmapQuarterOffset { get { switch (MapType) { case MapType.Quake: case MapType.GoldSrc: { - return BitConverter.ToUInt32(Data, 32); + return BitConverter.ToInt32(Data, 32); } default: { - return 0; + return -1; } } } @@ -476,17 +483,17 @@ namespace LibBSP { } /// - /// Gets the offset to the eighth version of this . + /// Gets the offset to the eighth mipmap of this . /// - public uint OffsetEighth { + public int MipmapEighthOffset { get { switch (MapType) { case MapType.Quake: case MapType.GoldSrc: { - return BitConverter.ToUInt32(Data, 36); + return BitConverter.ToInt32(Data, 36); } default: { - return 0; + return -1; } } } @@ -604,13 +611,23 @@ namespace LibBSP { /// byte array to parse. /// The this came from. /// was null. - public Texture(byte[] data, ILump parent) { + public Texture(byte[] data, ILump parent) : this(data, parent, null) { } + + /// + /// Creates a new object from a byte array. + /// + /// byte array to parse. + /// The this came from. + /// Data for the mipmap levels. + /// was null. + public Texture(byte[] data, ILump parent, byte[][] mipmaps) { if (data == null) { throw new ArgumentNullException(); } Data = data; Parent = parent; + _mipmaps = mipmaps; } /// @@ -632,6 +649,7 @@ namespace LibBSP { 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); + _mipmaps = source.Mipmaps; return; } else { Data = new byte[GetStructLength(parent.Bsp.version, parent.LumpInfo.version)]; @@ -644,17 +662,17 @@ namespace LibBSP { } } + _mipmaps = source.Mipmaps; Name = source.Name; Mask = source.Mask; Flags = source.Flags; Contents = source.Contents; TextureInfo = source.TextureInfo; - Width = source.Width; - Height = source.Height; - OffsetFull = source.OffsetFull; - OffsetHalf = source.OffsetHalf; - OffsetQuarter = source.OffsetQuarter; - OffsetEighth = source.OffsetEighth; + Dimensions = source.Dimensions; + MipmapFullOffset = source.MipmapFullOffset; + MipmapHalfOffset = source.MipmapHalfOffset; + MipmapQuarterOffset = source.MipmapQuarterOffset; + MipmapEighthOffset = source.MipmapEighthOffset; Value = source.Value; Next = source.Next; Subdivisions = source.Subdivisions;