From a8c23266bb3f9d282c7f4a008b1979eafd67610b Mon Sep 17 00:00:00 2001
From: MaxED <j.maxed@gmail.com>
Date: Mon, 18 Jul 2016 23:41:02 +0000
Subject: [PATCH] Changed: optimized generated voxel mesh size. Pro: they
 occupy ~50% less memory. Con: voxel loading takes ~30% more time. Changed,
 Browse Textures window: several textures with the same name were displayed in
 "All" texture set when overriding WAD textures by Folder/PK3 textures.

---
 Source/Core/Config/AllTexturesSet.cs     |  22 +--
 Source/Core/Data/VoxelImage.cs           |   2 +
 Source/Core/GZBuilder/md3/ModelReader.cs | 167 +++++++++++++++--------
 3 files changed, 122 insertions(+), 69 deletions(-)

diff --git a/Source/Core/Config/AllTexturesSet.cs b/Source/Core/Config/AllTexturesSet.cs
index 1bcb2566c..0fc50612b 100644
--- a/Source/Core/Config/AllTexturesSet.cs
+++ b/Source/Core/Config/AllTexturesSet.cs
@@ -16,6 +16,7 @@
 
 #region ================== Namespaces
 
+using System;
 using System.Collections.Generic;
 using CodeImp.DoomBuilder.Data;
 
@@ -27,22 +28,22 @@ namespace CodeImp.DoomBuilder.Config
 	{
 		#region ================== Constants
 
-		public const string NAME = "All";
+		private const string NAME = "All";
 
 		#endregion
 
 		#region ================== Variables
 
 		// Matching textures and flats
-		private List<ImageData> textures;
-		private List<ImageData> flats;
+		private Dictionary<string, ImageData> textures;
+		private Dictionary<string, ImageData> flats;
 
 		#endregion
 
 		#region ================== Properties
 
-		public ICollection<ImageData> Textures { get { return textures; } }
-		public ICollection<ImageData> Flats { get { return flats; } }
+		public ICollection<ImageData> Textures { get { return textures.Values; } }
+		public ICollection<ImageData> Flats { get { return flats.Values; } }
 
 		#endregion
 
@@ -52,8 +53,8 @@ namespace CodeImp.DoomBuilder.Config
 		public AllTextureSet()
 		{
 			this.name = NAME;
-			this.textures = new List<ImageData>();
-			this.flats = new List<ImageData>();
+			this.textures = new Dictionary<string, ImageData>(StringComparer.Ordinal);
+			this.flats = new Dictionary<string, ImageData>(StringComparer.Ordinal);
 		}
 
 		#endregion
@@ -62,12 +63,15 @@ namespace CodeImp.DoomBuilder.Config
 
 		internal void AddTexture(ImageData image)
 		{
-			textures.Add(image);
+			//mxd. Use short name when adding a texture with "classic" name to override same-named textures 
+			// with textures loaded from directory/pk3 containters
+			textures[image.DisplayName.Length > 8 ? image.Name : image.ShortName] = image;
 		}
 
 		internal void AddFlat(ImageData image)
 		{
-			flats.Add(image);
+			//mxd. Same with flats
+			flats[image.DisplayName.Length > 8 ? image.Name : image.ShortName] = image;
 		}
 
 		#endregion
diff --git a/Source/Core/Data/VoxelImage.cs b/Source/Core/Data/VoxelImage.cs
index 316e2e3c2..af2509f1f 100644
--- a/Source/Core/Data/VoxelImage.cs
+++ b/Source/Core/Data/VoxelImage.cs
@@ -308,6 +308,8 @@ namespace CodeImp.DoomBuilder.Data
 							}
 						}
 					}
+
+					lumpdata.Dispose();
 				}
 				else
 				{
diff --git a/Source/Core/GZBuilder/md3/ModelReader.cs b/Source/Core/GZBuilder/md3/ModelReader.cs
index fb2c77148..35375de7d 100644
--- a/Source/Core/GZBuilder/md3/ModelReader.cs
+++ b/Source/Core/GZBuilder/md3/ModelReader.cs
@@ -634,7 +634,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 		{
 			PixelColor[] palette = new PixelColor[256];
 			List<WorldVertex> verts = new List<WorldVertex>();
+			List<int> indices = new List<int>();
+			Dictionary<long, int> verthashes = new Dictionary<long, int>();
 			int xsize, ysize, zsize;
+			int facescount = 0;
 			Vector3D pivot;
 
 			using(BinaryReader reader = new BinaryReader(stream, Encoding.ASCII)) 
@@ -724,7 +727,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
 								if((flags & 16) != 0) 
 								{
-									AddFace(verts, 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]);
+									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;
 								}
 
 								int z = ztop;
@@ -736,19 +740,23 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 
 									if((flags & 1) != 0) 
 									{
-										AddFace(verts, 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]);
+										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) 
 									{
-										AddFace(verts, 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]);
+										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) 
 									{
-										AddFace(verts, 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]);
+										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) 
 									{
-										AddFace(verts, 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]);
+										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;
 									}
 
 									if(c == 0) c++;
@@ -759,7 +767,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 								if((flags & 32) != 0) 
 								{
 									z = ztop + zleng - 1;
-									AddFace(verts, 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]);
+									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]);
+									facescount += 2;
 								}
 							}
 						}
@@ -775,10 +784,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			int minY = (int)((ysize / 2f - pivot.y) * mde.Scale.Y);
 			int maxY = (int)((ysize / 2f + pivot.y) * mde.Scale.Y);
 			
-			// calculate model radius
+			// Calculate model radius
 			mde.Model.Radius = Math.Max(Math.Max(Math.Abs(minY), Math.Abs(maxY)), Math.Max(Math.Abs(minX), Math.Abs(maxX)));
 
-			//create texture
+			// Create texture
 			MemoryStream memstream = new MemoryStream((4096 * 4) + 4096);
 			using(Bitmap bmp = CreateVoxelTexture(palette)) bmp.Save(memstream, ImageFormat.Bmp);
 			memstream.Seek(0, SeekOrigin.Begin);
@@ -786,78 +795,116 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
 			Texture texture = Texture.FromStream(device, memstream, (int)memstream.Length, 64, 64, 0, Usage.None, Format.Unknown, Pool.Managed, Filter.Point, Filter.Box, 0);
 			memstream.Dispose();
 
-			//add texture
+			// Add texture
 			mde.Model.Textures.Add(texture);
 
-			//create mesh
-			int[] indices = new int[verts.Count];
-			for(int i = 0; i < verts.Count; i++) 
-			{
-				indices[i] = i;
-			}
-
-			Mesh mesh = new Mesh(device, verts.Count / 3, verts.Count, MeshFlags.Use32Bit | MeshFlags.IndexBufferManaged | MeshFlags.VertexBufferManaged, vertexElements);
+			// 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);
 			mstream.WriteRange(verts.ToArray());
 			mesh.VertexBuffer.Unlock();
 
 			mstream = mesh.IndexBuffer.Lock(0, 0, LockFlags.None);
-			mstream.WriteRange(indices);
+
+			if(indices.Count > ushort.MaxValue - 1)
+				mstream.WriteRange(indices.ToArray());
+			else
+				foreach(int index in indices) mstream.Write((ushort)index);
+
 			mesh.IndexBuffer.Unlock();
 
 			mesh.OptimizeInPlace(MeshOptimizeFlags.AttributeSort);
 
-			//add mesh
+			// Add mesh
 			mde.Model.Meshes.Add(mesh);
 		}
 
 		// Shameless GZDoom rip-off
-		private static void AddFace(List<WorldVertex> verts, 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.0001f;
+			float pu1 = pu0 + 0.001f;
 			float pv0 = (colorIndex / 16) / 16f;
-			float pv1 = pv0 + 0.0001f;
+			float pv1 = pv0 + 0.001f;
 			
-			WorldVertex wv1 = new WorldVertex();
-			wv1.x = v1.x - pivot.x;
-			wv1.y = -v1.y + pivot.y;
-			wv1.z = -v1.z + pivot.z;
-			wv1.c = -1;
-			wv1.u = pu0;
-			wv1.v = pv0;
-			verts.Add(wv1);
-
-			WorldVertex wv2 = new WorldVertex();
-			wv2.x = v2.x - pivot.x;
-			wv2.y = -v2.y + pivot.y;
-			wv2.z = -v2.z + pivot.z;
-			wv2.c = -1;
-			wv2.u = pu1;
-			wv2.v = pv1;
-			verts.Add(wv2);
-
-			WorldVertex wv4 = new WorldVertex();
-			wv4.x = v4.x - pivot.x;
-			wv4.y = -v4.y + pivot.y;
-			wv4.z = -v4.z + pivot.z;
-			wv4.c = -1;
-			wv4.u = pu0;
-			wv4.v = pv0;
-			verts.Add(wv4);
-
-			WorldVertex wv3 = new WorldVertex();
-			wv3.x = v3.x - pivot.x;
-			wv3.y = -v3.y + pivot.y;
-			wv3.z = -v3.z + pivot.z;
-			wv3.c = -1;
-			wv3.u = pu1;
-			wv3.v = pv1;
-			verts.Add(wv3);
-
-			verts.Add(wv1);
-			verts.Add(wv4);
+			WorldVertex wv1 = new WorldVertex
+			{
+				x = v1.x - pivot.x,
+				y = -v1.y + pivot.y,
+				z = -v1.z + pivot.z,
+				c = -1,
+				u = pu0,
+				v = pv0
+			};
+			int i1 = AddVertex(wv1, verts, indices, hashes);
+
+			WorldVertex wv2 = new WorldVertex
+			{
+				x = v2.x - pivot.x,
+				y = -v2.y + pivot.y,
+				z = -v2.z + pivot.z,
+				c = -1,
+				u = pu1,
+				v = pv1
+			};
+			AddVertex(wv2, verts, indices, hashes);
+
+			WorldVertex wv4 = new WorldVertex
+			{
+				x = v4.x - pivot.x,
+				y = -v4.y + pivot.y,
+				z = -v4.z + pivot.z,
+				c = -1,
+				u = pu0,
+				v = pv0
+			};
+			int i4 = AddVertex(wv4, verts, indices, hashes);
+
+			WorldVertex wv3 = new WorldVertex
+			{
+				x = v3.x - pivot.x,
+				y = -v3.y + pivot.y,
+				z = -v3.z + pivot.z,
+				c = -1,
+				u = pu1,
+				v = pv1
+			};
+			AddVertex(wv3, verts, indices, hashes);
+
+			indices.Add(i1);
+			indices.Add(i4);
+		}
+
+		// Returns index of added vert
+		private static int AddVertex(WorldVertex v, List<WorldVertex> verts, List<int> indices, Dictionary<long, int> hashes)
+		{
+			long hash;
+			unchecked // Overflow is fine, just wrap
+			{
+				hash = 2166136261;
+				hash = (hash * 16777619) ^ v.x.GetHashCode();
+				hash = (hash * 16777619) ^ v.y.GetHashCode();
+				hash = (hash * 16777619) ^ v.z.GetHashCode();
+				hash = (hash * 16777619) ^ v.u.GetHashCode();
+				hash = (hash * 16777619) ^ v.v.GetHashCode();
+			}
+
+			if(hashes.ContainsKey(hash))
+			{
+				indices.Add(hashes[hash]);
+				return hashes[hash];
+			}
+			else
+			{
+				verts.Add(v);
+				hashes.Add(hash, verts.Count - 1);
+				indices.Add(verts.Count - 1);
+				return verts.Count - 1;
+			}
 		}
 
 		private unsafe static Bitmap CreateVoxelTexture(PixelColor[] palette) 
-- 
GitLab