From afd710801fffc7dced8818aa47b67ce4ef889f4d Mon Sep 17 00:00:00 2001
From: Marisa Kirisame <OrdinaryMagician@users.noreply.github.com>
Date: Thu, 5 Jul 2018 11:50:03 +0200
Subject: [PATCH] Update .3d support to match coelckers/gzdoom#506 (PR#221 by
 Marisa_Kirisame)

---
 Source/Core/GZBuilder/md3/ModelReader.cs | 311 +++++++++++++----------
 1 file changed, 171 insertions(+), 140 deletions(-)

diff --git a/Source/Core/GZBuilder/md3/ModelReader.cs b/Source/Core/GZBuilder/md3/ModelReader.cs
index 41a8f6851..fbb2cc9c6 100755
--- a/Source/Core/GZBuilder/md3/ModelReader.cs
+++ b/Source/Core/GZBuilder/md3/ModelReader.cs
@@ -29,14 +29,14 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			public List<Mesh> Meshes;
 			public string Errors;
 
-			public MD3LoadResult() 
+			public MD3LoadResult()
 			{
 				Skins = new List<string>();
 				Meshes = new List<Mesh>();
 			}
 		}
 
-		private static readonly VertexElement[] vertexElements = new[] 
+		private static readonly VertexElement[] vertexElements = new[]
 		{
 			new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0),
 			new VertexElement(0, 12, DeclarationType.Color, DeclarationMethod.Default, DeclarationUsage.Color, 0),
@@ -49,20 +49,20 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
 		#region ================== Load
 
-		public static void Load(ModelData mde, List<DataReader> containers, Device device) 
+		public static void Load(ModelData mde, List<DataReader> containers, Device device)
 		{
 			if(mde.IsVoxel) LoadKVX(mde, containers, device);
 			else LoadModel(mde, containers, device);
 		}
 
-		private static void LoadKVX(ModelData mde, List<DataReader> containers, Device device) 
+		private static void LoadKVX(ModelData mde, List<DataReader> containers, Device device)
 		{
 			mde.Model = new GZModel();
 			string unused = string.Empty;
 			foreach(string name in mde.ModelNames)
 			{
 				//find the model
-				foreach(DataReader dr in containers) 
+				foreach(DataReader dr in containers)
 				{
 					Stream ms = dr.GetVoxelData(name, ref unused);
 					if(ms == null) continue;
@@ -80,32 +80,32 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			mde.SkinNames = null;
 			mde.ModelNames = null;
 
-			if(mde.Model.Meshes == null || mde.Model.Meshes.Count == 0) 
+			if(mde.Model.Meshes == null || mde.Model.Meshes.Count == 0)
 			{
 				mde.Model = null;
 			}
 		}
 
-		private static void LoadModel(ModelData mde, List<DataReader> containers, Device device) 
+		private static void LoadModel(ModelData mde, List<DataReader> containers, Device device)
 		{
 			mde.Model = new GZModel();
 			BoundingBoxSizes bbs = new BoundingBoxSizes();
 			MD3LoadResult result = new MD3LoadResult();
 
 			//load models and textures
-			for(int i = 0; i < mde.ModelNames.Count; i++) 
+			for(int i = 0; i < mde.ModelNames.Count; i++)
 			{
 				// Use model skins?
-				// INFO: Skin MODELDEF property overrides both embedded surface names and ones set using SurfaceSkin MODELDEF property 
+				// INFO: Skin MODELDEF property overrides both embedded surface names and ones set using SurfaceSkin MODELDEF property
 				Dictionary<int, string> skins = null;
 				if(string.IsNullOrEmpty(mde.SkinNames[i]))
 				{
 					skins = (mde.SurfaceSkinNames[i].Count > 0 ? mde.SurfaceSkinNames[i] : new Dictionary<int, string>());
 				}
-			
+
 				// Load mesh
 				MemoryStream ms = LoadFile(containers, mde.ModelNames[i], true);
-				if(ms == null) 
+				if(ms == null)
 				{
 					General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + mde.ModelNames[i] + "\": unable to find file.");
 					continue;
@@ -138,11 +138,11 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
                     continue;
 
                 //got errors?
-                if (!String.IsNullOrEmpty(result.Errors)) 
+                if (!String.IsNullOrEmpty(result.Errors))
 				{
 					General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + mde.ModelNames[i] + "\": " + result.Errors);
-				} 
-				else 
+				}
+				else
 				{
 					//add loaded data to ModeldefEntry
 					mde.Model.Meshes.AddRange(result.Meshes);
@@ -155,12 +155,12 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 					List<string> errors = new List<string>();
 
 					// Texture not defined in MODELDEF?
-					if(skins != null) 
+					if(skins != null)
 					{
-						//try to use model's own skins 
-						for(int m = 0; m < result.Meshes.Count; m++) 
+						//try to use model's own skins
+						for(int m = 0; m < result.Meshes.Count; m++)
 						{
-							if(string.IsNullOrEmpty(result.Skins[m])) 
+							if(string.IsNullOrEmpty(result.Skins[m]))
 							{
 								mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
 								errors.Add("texture not found in MODELDEF or model skin.");
@@ -170,7 +170,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 							string path = result.Skins[m].Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
 							ext = Path.GetExtension(path);
 
-							if(Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, ext) == -1) 
+							if(Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, ext) == -1)
 							{
 								mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
 								errors.Add("image format \"" + ext + "\" is not supported!");
@@ -183,12 +183,12 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
 							Texture t = LoadTexture(containers, path, device);
 
-							if(t == null) 
+							if(t == null)
 							{
 								mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
 								errors.Add("unable to load skin \"" + result.Skins[m] + "\"");
 								continue;
-							} 
+							}
 
 							mde.Model.Textures.Add(t);
 						}
@@ -197,19 +197,19 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 					else
 					{
 						Texture t = LoadTexture(containers, mde.SkinNames[i], device);
-						if(t == null) 
+						if(t == null)
 						{
 							mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
 							errors.Add("unable to load texture \"" + mde.SkinNames[i] + "\"");
-						} 
-						else 
+						}
+						else
 						{
 							mde.Model.Textures.Add(t);
 						}
 					}
 
 					//report errors
-					if(errors.Count > 0) 
+					if(errors.Count > 0)
 					{
 						foreach(string e in errors)
 							General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + mde.ModelNames[i] + "\": " + e);
@@ -221,7 +221,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			mde.SkinNames = null;
 			mde.ModelNames = null;
 
-			if(mde.Model.Meshes == null || mde.Model.Meshes.Count == 0) 
+			if(mde.Model.Meshes == null || mde.Model.Meshes.Count == 0)
 			{
 				mde.Model = null;
 				return;
@@ -234,7 +234,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			bbs.MinY = (int)(bbs.MinY * mde.Scale.Y);
 
 			//calculate model radius
-			mde.Model.Radius = Math.Max(Math.Max(Math.Abs(bbs.MinY), Math.Abs(bbs.MaxY)), Math.Max(Math.Abs(bbs.MinX), Math.Abs(bbs.MaxX))); 
+			mde.Model.Radius = Math.Max(Math.Max(Math.Abs(bbs.MinY), Math.Abs(bbs.MaxY)), Math.Max(Math.Abs(bbs.MinX), Math.Abs(bbs.MaxX)));
 		}
 
         #endregion
@@ -253,11 +253,11 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
         {
             switch (c)
             {
-                case 2:
+                case 0:
                     return PadInt16((n & 0x7ff) << 5) / 128f;
                 case 1:
                     return PadInt16((((int)n >> 11) & 0x7ff) << 5) / 128f;
-                case 0:
+                case 2:
                     return PadInt16((((int)n >> 22) & 0x3ff) << 6) / 128f;
                 default:
                     return 0f;
@@ -269,7 +269,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
             public int[] V;
             public float[] S;
             public float[] T;
-            public int TexNum;
+            public int TexNum, Type;
+            public Vector3D Normal;
         }
 
         internal static MD3LoadResult Read3DModel(ref BoundingBoxSizes bbs, Dictionary<int, string> skins, Stream s, Device device, int frame, string filename, List<DataReader> containers)
@@ -307,10 +308,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
             // read d3d header
             uint d3d_numpolys = br_d.ReadUInt16();
             uint d3d_numverts = br_d.ReadUInt16();
-            stream_d.Position += 16; // bogusrot, bogusframe, bogusnorm[3]
-            uint d3d_fixscale = br_d.ReadUInt32();
-            stream_d.Position += 12; // unused[3]
-            stream_d.Position += 12; // padding[12]
+            stream_d.Position += 44; // bogusrot, bogusframe, bogusnorm[3], fixscale, unused[3], padding[12]
 
             long start_d = stream_d.Position;
 
@@ -327,19 +325,33 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
                 return result;
             }
 
+            // check for deus ex format
+            bool isdeusex = false;
+            if ( (a3d_framesize/d3d_numverts) == 8 ) isdeusex = true;
+
             // read vertices
             WorldVertex[] vertices = new WorldVertex[d3d_numverts];
             for (uint i = 0; i < d3d_numverts; i++)
             {
                 WorldVertex Vert = new WorldVertex();
-                stream_a.Position = start_a + (i + frame * d3d_numverts) * 4;
-                int v_uint = br_a.ReadInt32();
-                //Vert.y = -UnpackUVertex(v_uint, 0);
-                //Vert.z = UnpackUVertex(v_uint, 1);
-                //Vert.x = UnpackUVertex(v_uint, 2);
-                Vert.y = -UnpackUVertex(v_uint, 2);
-                Vert.z = UnpackUVertex(v_uint, 0);
-                Vert.x = -UnpackUVertex(v_uint, 1);
+                if ( isdeusex )
+                {
+                    stream_a.Position = start_a + (i + frame * d3d_numverts) * 8;
+                    int vx = br_a.ReadInt16();
+                    int vy = br_a.ReadInt16();
+                    int vz = br_a.ReadInt16();
+                    Vert.y = -vx;
+                    Vert.z = vz;
+                    Vert.x = -vy;
+                }
+                else
+                {
+                    stream_a.Position = start_a + (i + frame * d3d_numverts) * 4;
+                    int v_uint = br_a.ReadInt32();
+                    Vert.y = -UnpackUVertex(v_uint, 0);
+                    Vert.z = UnpackUVertex(v_uint, 2);
+                    Vert.x = -UnpackUVertex(v_uint, 1);
+                }
                 vertices[i] = Vert;
             }
 
@@ -348,14 +360,15 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
             int[] polyindexlist = new int[d3d_numpolys*3];
             for (uint i = 0; i < d3d_numpolys; i++)
             {
-                // 
+                //
                 stream_d.Position = start_d + 16 * i;
                 polys[i].V = new int[3];
                 polys[i].S = new float[3];
                 polys[i].T = new float[3];
                 for (int j = 0; j < 3; j++)
                     polyindexlist[i*3+j] = polys[i].V[j] = br_d.ReadInt16();
-                stream_d.Position += 2;
+                polys[i].Type = br_d.ReadByte();
+                stream_d.Position += 1; // color
                 for (int j = 0; j < 3; j++)
                 {
                     byte u = br_d.ReadByte();
@@ -366,6 +379,24 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
                 polys[i].TexNum = br_d.ReadByte();
             }
 
+            // calculate poly normals
+            for (uint i = 0; i < d3d_numpolys; i++)
+            {
+                Vector3D[] dir = new Vector3D[2];
+                Vector3D norm;
+                dir[0].x = vertices[polys[i].V[1]].x-vertices[polys[i].V[0]].x;
+                dir[0].y = vertices[polys[i].V[1]].y-vertices[polys[i].V[0]].y;
+                dir[0].z = vertices[polys[i].V[1]].z-vertices[polys[i].V[0]].z;
+                dir[1].x = vertices[polys[i].V[2]].x-vertices[polys[i].V[0]].x;
+                dir[1].y = vertices[polys[i].V[2]].y-vertices[polys[i].V[0]].y;
+                dir[1].z = vertices[polys[i].V[2]].z-vertices[polys[i].V[0]].z;
+                norm.x = dir[0].y * dir[1].z - dir[0].z * dir[1].y;
+                norm.y = dir[0].z * dir[1].x - dir[0].x * dir[1].z;
+                norm.z = dir[0].x * dir[1].y - dir[0].y * dir[1].x;
+                polys[i].Normal = norm.GetNormal();
+            }
+
+            // calculate vertex normals
             for (uint i = 0; i < d3d_numverts; i++)
             {
                 Vector3D nsum = new Vector3D(0, 0, 0);
@@ -373,25 +404,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
                 for (uint j = 0; j < d3d_numpolys; j++)
                 {
                     if ((polys[j].V[0] != i) && (polys[j].V[1] != i) && (polys[j].V[2] != i)) continue;
-                    Vector3D[] vert = new Vector3D[3];
-                    Vector3D[] dir = new Vector3D[2];
-                    Vector3D norm;
-                    // 
-                    for (int k = 0; k < 3; k++)
-                        vert[k] = new Vector3D(vertices[polys[j].V[k]].x, vertices[polys[j].V[k]].y, vertices[polys[j].V[k]].z);
-                    dir[0].x = vert[1].x - vert[0].x;
-                    dir[0].y = vert[1].y - vert[0].y;
-                    dir[0].z = vert[1].z - vert[0].z;
-                    dir[1].x = vert[2].x - vert[0].x;
-                    dir[1].y = vert[2].y - vert[0].y;
-                    dir[1].z = vert[2].z - vert[0].z;
-                    norm.x = dir[0].y * dir[1].z - dir[0].z * dir[1].y;
-                    norm.y = dir[0].z * dir[1].x - dir[0].x * dir[1].z;
-                    norm.z = dir[0].x * dir[1].y - dir[0].y * dir[1].x;
-                    norm = norm.GetNormal();
-                    nsum.x += norm.x;
-                    nsum.y += norm.y;
-                    nsum.z += norm.z;
+                    nsum.x += polys[j].Normal.x;
+                    nsum.y += polys[j].Normal.y;
+                    nsum.z += polys[j].Normal.z;
                     total++;
                 }
                 vertices[i].nx = -nsum.x / total;
@@ -423,11 +438,19 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
                 for (int i = 0; i < polys.Length; i++)
                 {
+                    if ( polys[i].Type&0x08 )
+                        continue;
                     for (int j = 0; j < 3; j++)
                     {
                         WorldVertex vx = vertices[polys[i].V[j]];
                         vx.u = polys[i].S[j];
                         vx.v = polys[i].T[j];
+                        if ( polys[i].Type&0x20 )
+                        {
+                            vx.nx = polys[i].Normal.x;
+                            vx.ny = polys[i].Normal.y;
+                            vx.nz = polys[i].Normal.z;
+                        }
                         out_polys.Add(out_verts.Count);
                         out_verts.Add(vx);
                     }
@@ -445,6 +468,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
                     for (int i = 0; i < polys.Length; i++)
                     {
+                        if ( polys[i].Type&0x08 )
+                            continue;
 
                         if (textureGroupRemap[polys[i].TexNum] != k)
                             continue;
@@ -454,6 +479,12 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
                             WorldVertex vx = vertices[polys[i].V[j]];
                             vx.u = polys[i].S[j];
                             vx.v = polys[i].T[j];
+                            if ( polys[i].Type&0x20 )
+                            {
+                                vx.nx = polys[i].Normal.x;
+                                vx.ny = polys[i].Normal.y;
+                                vx.nz = polys[i].Normal.z;
+                            }
                             out_polys.Add(out_verts.Count);
                             out_verts.Add(vx);
                         }
@@ -471,12 +502,12 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
         #region ================== MD3
 
-        internal static MD3LoadResult ReadMD3Model(ref BoundingBoxSizes bbs, Dictionary<int, string> skins, Stream s, Device device, int frame) 
+        internal static MD3LoadResult ReadMD3Model(ref BoundingBoxSizes bbs, Dictionary<int, string> skins, Stream s, Device device, int frame)
 		{
 			long start = s.Position;
 			MD3LoadResult result = new MD3LoadResult();
 
-			using(var br = new BinaryReader(s, Encoding.ASCII)) 
+			using(var br = new BinaryReader(s, Encoding.ASCII))
 			{
 				string magic = ReadString(br, 4);
 				if(magic != "IDP3")
@@ -507,12 +538,12 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 				Dictionary<string, List<int>> vertexOffsets = new Dictionary<string, List<int>>(StringComparer.Ordinal);
 				bool useskins = false;
 
-				for(int c = 0; c < numSurfaces; c++) 
+				for(int c = 0; c < numSurfaces; c++)
 				{
 					string skin = "";
 					string error = ReadSurface(ref bbs, ref skin, br, polyIndecesList, vertList, frame);
 
-					if(!string.IsNullOrEmpty(error)) 
+					if(!string.IsNullOrEmpty(error))
 					{
 						result.Errors = error;
 						return result;
@@ -533,14 +564,14 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 					if(!string.IsNullOrEmpty(skin))
 					{
 						useskins = true;
-						
-						if(polyIndecesListsPerTexture.ContainsKey(skin)) 
+
+						if(polyIndecesListsPerTexture.ContainsKey(skin))
 						{
 							polyIndecesListsPerTexture[skin].Add(polyIndecesList);
 							vertListsPerTexture[skin].AddRange(vertList.ToArray());
 							vertexOffsets[skin].Add(vertList.Count);
-						} 
-						else 
+						}
+						else
 						{
 							polyIndecesListsPerTexture.Add(skin, new List<List<int>> { polyIndecesList } );
 							vertListsPerTexture.Add(skin, vertList);
@@ -553,27 +584,27 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 					}
 				}
 
-				if(!useskins) 
+				if(!useskins)
 				{
 					//create mesh
 					CreateMesh(device, ref result, vertList, polyIndecesList);
 					result.Skins.Add("");
-				} 
-				else 
+				}
+				else
 				{
 					//create a mesh for each surface texture
-					foreach(KeyValuePair<string, List<List<int>>> group in polyIndecesListsPerTexture) 
+					foreach(KeyValuePair<string, List<List<int>>> group in polyIndecesListsPerTexture)
 					{
 						polyIndecesList = new List<int>();
 						int offset = 0;
-						
+
 						//collect indices, fix vertex offsets
-						for(int i = 0; i < group.Value.Count; i++) 
+						for(int i = 0; i < group.Value.Count; i++)
 						{
-							if(i > 0) 
+							if(i > 0)
 							{
 								//TODO: Damn I need to rewrite all of this stuff from scratch...
-								offset += vertexOffsets[group.Key][i - 1]; 
+								offset += vertexOffsets[group.Key][i - 1];
 								for(int c = 0; c < group.Value[i].Count; c++)
 									group.Value[i][c] += offset;
 							}
@@ -589,11 +620,11 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			return result;
 		}
 
-		private static string ReadSurface(ref BoundingBoxSizes bbs, ref string skin, BinaryReader br, List<int> polyIndecesList, List<WorldVertex> vertList, int frame) 
+		private static string ReadSurface(ref BoundingBoxSizes bbs, ref string skin, BinaryReader br, List<int> polyIndecesList, List<WorldVertex> vertList, int frame)
 		{
 			int vertexOffset = vertList.Count;
 			long start = br.BaseStream.Position;
-			
+
 			string magic = ReadString(br, 4);
 			if(magic != "IDP3") return "error while reading surface. Unknown header: expected \"IDP3\", but got \"" + magic + "\"";
 
@@ -632,7 +663,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			if(start + ofsST != br.BaseStream.Position)
 				br.BaseStream.Position = start + ofsST;
 
-			for(int i = 0; i < numVerts; i++) 
+			for(int i = 0; i < numVerts; i++)
 			{
 				WorldVertex v = new WorldVertex();
 				v.c = -1; //white
@@ -646,7 +677,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			long vertoffset = start + ofsNormal + numVerts * 8 * frame; // The length of Vertex struct is 8 bytes
 			if(br.BaseStream.Position != vertoffset) br.BaseStream.Position = vertoffset;
 
-			for(int i = vertexOffset; i < vertexOffset + numVerts; i++) 
+			for(int i = vertexOffset; i < vertexOffset + numVerts; i++)
 			{
 				WorldVertex v = vertList[i];
 
@@ -677,12 +708,12 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
 		#region ================== MD2
 
-		private static MD3LoadResult ReadMD2Model(ref BoundingBoxSizes bbs, Stream s, Device device, int frame, string framename) 
+		private static MD3LoadResult ReadMD2Model(ref BoundingBoxSizes bbs, Stream s, Device device, int frame, string framename)
 		{
 			long start = s.Position;
 			MD3LoadResult result = new MD3LoadResult();
 
-			using(var br = new BinaryReader(s, Encoding.ASCII)) 
+			using(var br = new BinaryReader(s, Encoding.ASCII))
 			{
 				string magic = ReadString(br, 4);
 				if(magic != "IDP2") //magic number: "IDP2"
@@ -693,7 +724,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
 				int modelVersion = br.ReadInt32();
 				if(modelVersion != 8) //MD2 version. Must be equal to 8
-				{ 
+				{
 					result.Errors = "expected MD3 version 15, but got " + modelVersion;
 					return result;
 				}
@@ -728,7 +759,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 				// Polygons
 				s.Position = ofs_tris + start;
 
-				for(int i = 0; i < num_tris; i++) 
+				for(int i = 0; i < num_tris; i++)
 				{
 					polyIndecesList.Add(br.ReadUInt16());
 					polyIndecesList.Add(br.ReadUInt16());
@@ -742,7 +773,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 				// UV coords
 				s.Position = ofs_uv + start;
 
-				for(int i = 0; i < num_uv; i++) 
+				for(int i = 0; i < num_uv; i++)
 					uvCoordsList.Add(new Vector2((float)br.ReadInt16() / texWidth, (float)br.ReadInt16() / texHeight));
 
 				// Frames
@@ -789,7 +820,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 				float angleOfsetSin = (float)Math.Sin(-Angle2D.PIHALF);
 
 				//verts
-				for(int i = 0; i < num_verts; i++) 
+				for(int i = 0; i < num_verts; i++)
 				{
 					WorldVertex v = new WorldVertex();
 
@@ -807,10 +838,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 					s.Position += 1; //vertex normal
 				}
 
-				for(int i = 0; i < polyIndecesList.Count; i++) 
+				for(int i = 0; i < polyIndecesList.Count; i++)
 				{
 					WorldVertex v = vertList[polyIndecesList[i]];
-					
+
 					//bounding box
 					BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, new WorldVertex(v.y, v.x, v.z));
 
@@ -819,13 +850,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 					float tv = uvCoordsList[uvIndecesList[i]].Y;
 
 					//uv-coordinates already set?
-					if(v.c == -1 && (v.u != tu || v.v != tv)) 
-					{ 
+					if(v.c == -1 && (v.u != tu || v.v != tv))
+					{
 						//add a new vertex
 						vertList.Add(new WorldVertex(v.x, v.y, v.z, -1, tu, tv));
 						polyIndecesList[i] = vertList.Count - 1;
-					} 
-					else 
+					}
+					else
 					{
 						v.u = tu;
 						v.v = tv;
@@ -839,13 +870,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 				//mesh
 				Mesh mesh = new Mesh(device, polyIndecesList.Count / 3, vertList.Count, MeshFlags.Use32Bit | MeshFlags.IndexBufferManaged | MeshFlags.VertexBufferManaged, vertexElements);
 
-				using(DataStream stream = mesh.LockVertexBuffer(LockFlags.None)) 
+				using(DataStream stream = mesh.LockVertexBuffer(LockFlags.None))
 				{
 					stream.WriteRange(vertList.ToArray());
 				}
 				mesh.UnlockVertexBuffer();
 
-				using(DataStream stream = mesh.LockIndexBuffer(LockFlags.None)) 
+				using(DataStream stream = mesh.LockIndexBuffer(LockFlags.None))
 				{
 					stream.WriteRange(polyIndecesList.ToArray());
 				}
@@ -865,7 +896,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
 		#region ================== KVX
 
-		private static void ReadKVX(ModelData mde, Stream stream, Device device) 
+		private static void ReadKVX(ModelData mde, Stream stream, Device device)
 		{
 			PixelColor[] palette = new PixelColor[256];
 			List<WorldVertex> verts = new List<WorldVertex>();
@@ -875,7 +906,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			int facescount = 0;
 			Vector3D pivot;
 
-			using(BinaryReader reader = new BinaryReader(stream, Encoding.ASCII)) 
+			using(BinaryReader reader = new BinaryReader(stream, Encoding.ASCII))
 			{
 				reader.ReadInt32(); //numbytes, we don't use that
 				xsize = reader.ReadInt32();
@@ -891,14 +922,14 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 				int[] xoffset = new int[xsize + 1]; //why is it xsize + 1, not xsize?..
 				short[,] xyoffset = new short[xsize, ysize + 1]; //why is it ysize + 1, not ysize?..
 
-				for(int i = 0; i < xoffset.Length; i++) 
+				for(int i = 0; i < xoffset.Length; i++)
 				{
 					xoffset[i] = reader.ReadInt32();
 				}
 
-				for(int x = 0; x < xsize; x++) 
+				for(int x = 0; x < xsize; x++)
 				{
-					for(int y = 0; y < ysize + 1; y++) 
+					for(int y = 0; y < ysize + 1; y++)
 					{
 						xyoffset[x, y] = reader.ReadInt16();
 					}
@@ -906,9 +937,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
 				//read slabs
 				List<int> offsets = new List<int>(xsize * ysize);
-				for(int x = 0; x < xsize; x++) 
+				for(int x = 0; x < xsize; x++)
 				{
-					for(int y = 0; y < ysize; y++) 
+					for(int y = 0; y < ysize; y++)
 					{
 						offsets.Add(xoffset[x] + xyoffset[x, y] + 28); //for some reason offsets are counted from start of xoffset[]...
 					}
@@ -918,49 +949,49 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 				int slabsEnd = (int)(reader.BaseStream.Length - 768);
 
 				//read palette
-				if(!mde.OverridePalette) 
+				if(!mde.OverridePalette)
 				{
 					reader.BaseStream.Position = slabsEnd;
-					for(int i = 0; i < 256; i++) 
+					for(int i = 0; i < 256; i++)
 					{
 						byte r = (byte)(reader.ReadByte() * 4);
 						byte g = (byte)(reader.ReadByte() * 4);
 						byte b = (byte)(reader.ReadByte() * 4);
 						palette[i] = new PixelColor(255, r, g, b);
 					}
-				} 
-				else 
+				}
+				else
 				{
-					for(int i = 0; i < 256; i++ ) 
+					for(int i = 0; i < 256; i++ )
 					{
 						palette[i] = General.Map.Data.Palette[i];
 					}
 				}
 
-				for(int x = 0; x < xsize; x++) 
+				for(int x = 0; x < xsize; x++)
 				{
-					for(int y = 0; y < ysize; y++) 
+					for(int y = 0; y < ysize; y++)
 					{
 						reader.BaseStream.Position = offsets[counter];
 						int next = (counter < offsets.Count - 1 ? offsets[counter + 1] : slabsEnd);
 
 						//read slab
-						while(reader.BaseStream.Position < next) 
+						while(reader.BaseStream.Position < next)
 						{
 							int ztop = reader.ReadByte();
 							int zleng = reader.ReadByte();
 							if(ztop + zleng > zsize) break;
 							int flags = reader.ReadByte();
 
-							if(zleng > 0) 
+							if(zleng > 0)
 							{
 								List<int> colorIndices = new List<int>(zleng);
-								for(int i = 0; i < zleng; i++) 
+								for(int i = 0; i < zleng; i++)
 								{
 									colorIndices.Add(reader.ReadByte());
 								}
 
-								if((flags & 16) != 0) 
+								if((flags & 16) != 0)
 								{
 									AddFace(verts, indices, verthashes, new Vector3D(x, y, ztop), new Vector3D(x + 1, y, ztop), new Vector3D(x, y + 1, ztop), new Vector3D(x + 1, y + 1, ztop), pivot, colorIndices[0]);
 									facescount += 2;
@@ -968,27 +999,27 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
 								int z = ztop;
 								int cstart = 0;
-								while(z < ztop + zleng) 
+								while(z < ztop + zleng)
 								{
 									int c = 0;
 									while(z + c < ztop + zleng && colorIndices[cstart + c] == colorIndices[cstart]) c++;
 
-									if((flags & 1) != 0) 
+									if((flags & 1) != 0)
 									{
 										AddFace(verts, indices, verthashes, new Vector3D(x, y, z), new Vector3D(x, y + 1, z), new Vector3D(x, y, z + c), new Vector3D(x, y + 1, z + c), pivot, colorIndices[cstart]);
 										facescount += 2;
 									}
-									if((flags & 2) != 0) 
+									if((flags & 2) != 0)
 									{
 										AddFace(verts, indices, verthashes, new Vector3D(x + 1, y + 1, z), new Vector3D(x + 1, y, z), new Vector3D(x + 1, y + 1, z + c), new Vector3D(x + 1, y, z + c), pivot, colorIndices[cstart]);
 										facescount += 2;
 									}
-									if((flags & 4) != 0) 
+									if((flags & 4) != 0)
 									{
 										AddFace(verts, indices, verthashes, new Vector3D(x + 1, y, z), new Vector3D(x, y, z), new Vector3D(x + 1, y, z + c), new Vector3D(x, y, z + c), pivot, colorIndices[cstart]);
 										facescount += 2;
 									}
-									if((flags & 8) != 0) 
+									if((flags & 8) != 0)
 									{
 										AddFace(verts, indices, verthashes, new Vector3D(x, y + 1, z), new Vector3D(x + 1, y + 1, z), new Vector3D(x, y + 1, z + c), new Vector3D(x + 1, y + 1, z + c), pivot, colorIndices[cstart]);
 										facescount += 2;
@@ -999,7 +1030,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 									cstart += c;
 								}
 
-								if((flags & 32) != 0) 
+								if((flags & 32) != 0)
 								{
 									z = ztop + zleng - 1;
 									AddFace(verts, indices, verthashes, new Vector3D(x + 1, y, z + 1), new Vector3D(x, y, z + 1), new Vector3D(x + 1, y + 1, z + 1), new Vector3D(x, y + 1, z + 1), pivot, colorIndices[zleng - 1]);
@@ -1018,7 +1049,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			int maxX = (int)((xsize / 2f + pivot.x) * mde.Scale.X);
 			int minY = (int)((ysize / 2f - pivot.y) * mde.Scale.Y);
 			int maxY = (int)((ysize / 2f + pivot.y) * mde.Scale.Y);
-			
+
 			// Calculate model radius
 			mde.Model.Radius = Math.Max(Math.Max(Math.Abs(minY), Math.Abs(maxY)), Math.Max(Math.Abs(minX), Math.Abs(maxX)));
 
@@ -1036,7 +1067,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			// Create mesh
 			MeshFlags meshflags = MeshFlags.Managed;
 			if(indices.Count > ushort.MaxValue - 1) meshflags |= MeshFlags.Use32Bit;
-			
+
 			Mesh mesh = new Mesh(device, facescount, verts.Count, meshflags, vertexElements);
 
 			DataStream mstream = mesh.VertexBuffer.Lock(0, 0, LockFlags.None);
@@ -1059,13 +1090,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 		}
 
 		// Shameless GZDoom rip-off
-		private static void AddFace(List<WorldVertex> verts, List<int> indices, Dictionary<long, int> hashes, Vector3D v1, Vector3D v2, Vector3D v3, Vector3D v4, Vector3D pivot, int colorIndex) 
+		private static void AddFace(List<WorldVertex> verts, List<int> indices, Dictionary<long, int> hashes, Vector3D v1, Vector3D v2, Vector3D v3, Vector3D v4, Vector3D pivot, int colorIndex)
 		{
 			float pu0 = (colorIndex % 16) / 16f;
 			float pu1 = pu0 + 0.001f;
 			float pv0 = (colorIndex / 16) / 16f;
 			float pv1 = pv0 + 0.001f;
-			
+
 			WorldVertex wv1 = new WorldVertex
 			{
 				x = v1.x - pivot.x,
@@ -1142,18 +1173,18 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			}
 		}
 
-		private unsafe static Bitmap CreateVoxelTexture(PixelColor[] palette) 
+		private unsafe static Bitmap CreateVoxelTexture(PixelColor[] palette)
 		{
 			Bitmap bmp = new Bitmap(16, 16);
 			BitmapData bmpdata = bmp.LockBits(new Rectangle(0, 0, 16, 16), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
 
-			if(bmpdata != null) 
+			if(bmpdata != null)
 			{
 				PixelColor* pixels = (PixelColor*)(bmpdata.Scan0.ToPointer());
 				const int numpixels = 256;
 				int i = 255;
 
-				for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--, i--) 
+				for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--, i--)
 				{
 					cp->r = palette[i].r;
 					cp->g = palette[i].g;
@@ -1165,7 +1196,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
 			//scale bitmap, so colors stay (almost) the same when bilinear filtering is enabled
 			Bitmap scaled = new Bitmap(64, 64);
-			using(Graphics gs = Graphics.FromImage(scaled)) 
+			using(Graphics gs = Graphics.FromImage(scaled))
 			{
 				gs.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
 				gs.DrawImage(bmp, new Rectangle(0, 0, 64, 64), new Rectangle(0, 0, 16, 16), GraphicsUnit.Pixel);
@@ -1179,9 +1210,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
 		#region ================== Utility
 
-		private static MemoryStream LoadFile(List<DataReader> containers, string path, bool isModel) 
+		private static MemoryStream LoadFile(List<DataReader> containers, string path, bool isModel)
 		{
-			foreach(DataReader dr in containers) 
+			foreach(DataReader dr in containers)
 			{
 				if(isModel && dr is WADReader) continue;  //models cannot be stored in WADs
 
@@ -1191,7 +1222,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			return null;
 		}
 
-		private static Texture LoadTexture(List<DataReader> containers, string path, Device device) 
+		private static Texture LoadTexture(List<DataReader> containers, string path, Device device)
 		{
 			if(string.IsNullOrEmpty(path)) return null;
 
@@ -1202,13 +1233,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
 			//create texture
 			if(Path.GetExtension(path) == ".pcx") //pcx format requires special handling...
-			{ 
+			{
 				FileImageReader fir = new FileImageReader();
 				Bitmap bitmap = fir.ReadAsBitmap(ms);
 
 				ms.Close();
 
-				if(bitmap != null) 
+				if(bitmap != null)
 				{
 					BitmapData bmlock = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
 					texture = new Texture(device, bitmap.Width, bitmap.Height, 1, Usage.None, Format.A8R8G8B8, Pool.Managed);
@@ -1219,8 +1250,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 					bitmap.UnlockBits(bmlock);
 					texture.UnlockRectangle(0);
 				}
-			} 
-			else 
+			}
+			else
 			{
 				texture = Texture.FromStream(device, ms);
 
@@ -1253,15 +1284,15 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			result.Meshes.Add(mesh);
 		}
 
-		private static string ReadString(BinaryReader br, int len) 
+		private static string ReadString(BinaryReader br, int len)
 		{
 			string result = string.Empty;
 			int i;
 
-			for(i = 0; i < len; ++i) 
+			for(i = 0; i < len; ++i)
 			{
 				var c = br.ReadChar();
-				if(c == '\0') 
+				if(c == '\0')
 				{
 					++i;
 					break;
@@ -1275,4 +1306,4 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
 		#endregion
 	}
-}
\ No newline at end of file
+}
-- 
GitLab