diff --git a/Build/Configurations/Includes/Doom_things.cfg b/Build/Configurations/Includes/Doom_things.cfg
index 601ccbb3312e06ed9a19eefa7c6e5fc463113971..04ead12c6d182252c8c20fee3ed025d9fac7ffb8 100644
--- a/Build/Configurations/Includes/Doom_things.cfg
+++ b/Build/Configurations/Includes/Doom_things.cfg
@@ -367,7 +367,7 @@ powerups
 		title = "Invisibility";
 		sprite = "PINSA0";
 		height = 45;
-		class = "BlurSphere ";
+		class = "BlurSphere";
 	}
 	2025
 	{
diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj
index 68e292f789725b50418cdeac5e5826adfa558da5..72858a3739f25e508fe71071d5614f7946e64321 100644
--- a/Source/Core/Builder.csproj
+++ b/Source/Core/Builder.csproj
@@ -713,10 +713,11 @@
     </Compile>
     <Compile Include="GZBuilder\Data\BoundingBox.cs" />
     <Compile Include="GZBuilder\Data\GZDoomLight.cs" />
-    <Compile Include="GZBuilder\Data\ModelDefEntry.cs" />
+    <Compile Include="GZBuilder\Data\ModeldefEntry.cs" />
     <Compile Include="GZBuilder\Data\ThingBoundingBox.cs" />
+    <Compile Include="GZBuilder\GZDoom\ModeldefParser.cs" />
+    <Compile Include="GZBuilder\GZDoom\ModeldefStructure.cs" />
     <Compile Include="GZBuilder\GZGeneral.cs" />
-    <Compile Include="GZBuilder\IO\ModelDefParser.cs" />
     <Compile Include="GZBuilder\md3\GZModel.cs" />
     <Compile Include="GZBuilder\md3\ModelReader.cs" />
     <Compile Include="IO\DoomColormapReader.cs" />
diff --git a/Source/Core/Builder.sln b/Source/Core/Builder.sln
index 8b5d3259a8534dd34cb83c72226afb26b87891cf..9a59c636000acdd516a53194970da7805119c1a1 100644
--- a/Source/Core/Builder.sln
+++ b/Source/Core/Builder.sln
@@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 10.00
 # Visual C# Express 2008
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Builder", "Builder.csproj", "{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColorPicker", "..\Plugins\ColorPicker\ColorPicker.csproj", "{A4761900-0EA3-4FE4-A919-847FD5080EFC}"
-EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -25,16 +23,6 @@ Global
 		{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}.Release|Mixed Platforms.Build.0 = Release|x86
 		{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}.Release|x86.ActiveCfg = Release|x86
 		{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}.Release|x86.Build.0 = Release|x86
-		{A4761900-0EA3-4FE4-A919-847FD5080EFC}.Debug|Any CPU.ActiveCfg = Debug|x86
-		{A4761900-0EA3-4FE4-A919-847FD5080EFC}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
-		{A4761900-0EA3-4FE4-A919-847FD5080EFC}.Debug|Mixed Platforms.Build.0 = Debug|x86
-		{A4761900-0EA3-4FE4-A919-847FD5080EFC}.Debug|x86.ActiveCfg = Debug|x86
-		{A4761900-0EA3-4FE4-A919-847FD5080EFC}.Debug|x86.Build.0 = Debug|x86
-		{A4761900-0EA3-4FE4-A919-847FD5080EFC}.Release|Any CPU.ActiveCfg = Release|x86
-		{A4761900-0EA3-4FE4-A919-847FD5080EFC}.Release|Mixed Platforms.ActiveCfg = Release|x86
-		{A4761900-0EA3-4FE4-A919-847FD5080EFC}.Release|Mixed Platforms.Build.0 = Release|x86
-		{A4761900-0EA3-4FE4-A919-847FD5080EFC}.Release|x86.ActiveCfg = Release|x86
-		{A4761900-0EA3-4FE4-A919-847FD5080EFC}.Release|x86.Build.0 = Release|x86
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/Source/Core/Data/DataManager.cs b/Source/Core/Data/DataManager.cs
index e393814c982f85f988901d4281236425ef61d3d4..f01c688f1c1cc5240711281c67fac0e82f5497e2 100644
--- a/Source/Core/Data/DataManager.cs
+++ b/Source/Core/Data/DataManager.cs
@@ -33,6 +33,10 @@ using CodeImp.DoomBuilder.Map;
 using CodeImp.DoomBuilder.Windows;
 using CodeImp.DoomBuilder.ZDoom;
 
+using CodeImp.DoomBuilder.GZBuilder.Data;
+using CodeImp.DoomBuilder.GZBuilder.GZDoom;
+using CodeImp.DoomBuilder.GZBuilder.MD3;
+
 #endregion
 
 namespace CodeImp.DoomBuilder.Data
@@ -65,7 +69,10 @@ namespace CodeImp.DoomBuilder.Data
 		private AllTextureSet alltextures;
 
         //mxd Folders
-        private List<string> folders;
+        //private List<string> folders;
+
+        //mxd modeldefs
+        private Dictionary<int, ModeldefEntry> modeldefEntries;
 		
 		// Background loading
 		private Queue<ImageData> imageque;
@@ -107,7 +114,8 @@ namespace CodeImp.DoomBuilder.Data
 		#region ================== Properties
 
         //mxd
-        public List<string> Folders { get { return folders; } }
+        //public List<string> Folders { get { return folders; } }
+        public Dictionary<int, ModeldefEntry> ModeldefEntries { get { return modeldefEntries; } }
 
 		public Playpal Palette { get { return palette; } }
 		public PreviewManager Previews { get { return previews; } }
@@ -247,9 +255,6 @@ namespace CodeImp.DoomBuilder.Data
 			internalsprites = new Dictionary<string, ImageData>();
 			thingcategories = General.Map.Config.GetThingCategories();
 			thingtypes = General.Map.Config.GetThingTypes();
-
-            //mxd
-            folders = new List<string>();
 			
 			// Load texture sets
 			foreach(DefinedTextureSet ts in General.Map.ConfigSettings.TextureSets)
@@ -285,10 +290,6 @@ namespace CodeImp.DoomBuilder.Data
 						// Directory container
 						case DataLocation.RESOURCE_DIRECTORY:
 							c = new DirectoryReader(dl);
-
-                            //mxd
-                            folders.Add(dl.location);
-
 							break;
 
 						// PK3 file container
@@ -322,6 +323,9 @@ namespace CodeImp.DoomBuilder.Data
 			thingcount = LoadDecorateThings();
 			spritecount = LoadThingSprites();
 			LoadInternalSprites();
+
+            //mxd
+            loadModeldefs();
 			
 			// Process colormaps (we just put them in as textures)
 			foreach(KeyValuePair<long, ImageData> t in colormapsonly)
@@ -434,6 +438,13 @@ namespace CodeImp.DoomBuilder.Data
 			foreach(KeyValuePair<long, ImageData> i in flats) i.Value.Dispose();
 			foreach(KeyValuePair<long, ImageData> i in sprites) i.Value.Dispose();
 			palette = null;
+
+            //mxd
+            if (modeldefEntries != null) {
+                foreach (KeyValuePair<int, ModeldefEntry> group in modeldefEntries) {
+                    group.Value.Dispose();
+                }
+            }
 			
 			// Dispose containers
 			foreach(DataReader c in containers) c.Dispose();
@@ -1342,10 +1353,110 @@ namespace CodeImp.DoomBuilder.Data
 		}
 		
 		#endregion
-		
-		#region ================== Tools
 
-		// This finds the first IWAD resource
+        #region ================== Modeldef and models
+
+        public void LoadModels() {
+            General.MainWindow.DisplayStatus(StatusType.Busy, "Loading models...");
+
+            foreach (Thing t in General.Map.Map.Things)
+                LoadModelForThing(t);
+
+            General.MainWindow.RedrawDisplay();
+        }
+
+        public bool LoadModelForThing(Thing t) {
+            if (modeldefEntries.ContainsKey(t.Type)) {
+                if (modeldefEntries[t.Type].Model == null) {
+                    //load model and texture
+                    ModeldefEntry mde = modeldefEntries[t.Type];
+
+                    foreach (DataReader dr in containers) {
+                        currentreader = dr;
+                        if (currentreader.Location.location == mde.Location) {
+                            ModelReader.Parse(ref mde, (PK3StructuredReader)currentreader, General.Map.Graphics.Device);
+                            break;
+                        }
+                    }
+                    currentreader = null;
+
+                    if (mde.Model != null) {
+                        GZBuilder.GZGeneral.LogAndTraceWarning("Loaded model for Thing ¹" + t.Type);
+                        return true;
+                    } else {
+                        modeldefEntries.Remove(t.Type);
+                        GZBuilder.GZGeneral.LogAndTraceWarning("Failed to load model(s) for Thing ¹" + t.Type + ", model(s) location is "+mde.Location+ "\\" + mde.Path +". MODELDEF node removed.");
+                        return false;
+                    }
+                }
+                return true;
+            }
+            return false;
+        }
+
+        //mxd. This parses modeldefs
+        private void loadModeldefs() {
+            General.MainWindow.DisplayStatus(StatusType.Busy, "Parsing model definitions...");
+
+            Dictionary<string, int> Actors = new Dictionary<string, int>();
+            Dictionary<int, ThingTypeInfo> things = General.Map.Config.GetThingTypes();
+
+            //read our new shiny ClassNames for default game things
+            foreach (KeyValuePair<int, ThingTypeInfo> ti in things) {
+                if (ti.Value.ClassName != null)
+                    Actors.Add(ti.Value.ClassName, ti.Key);
+            }
+
+            //and for actors defined in DECORATE
+            ICollection<ActorStructure> ac = decorate.Actors; //General.Map.Data.Decorate.Actors;
+            foreach (ActorStructure actor in ac) {
+                string className = actor.ClassName.ToLower();
+                if (actor.DoomEdNum != -1 && !Actors.ContainsKey(className)) //we don't need actors without DoomEdNum
+                    Actors.Add(className, actor.DoomEdNum);
+            }
+
+            Dictionary<string, ModeldefEntry> modelDefEntriesByName = new Dictionary<string, ModeldefEntry>();
+            ModeldefParser mdeParser = new ModeldefParser();
+
+            foreach (DataReader dr in containers) {
+                currentreader = dr;
+
+                Dictionary<string, Stream> streams = dr.GetModeldefData();
+                foreach (KeyValuePair<string, Stream> group in streams) {
+                    //dbg
+                    GZBuilder.GZGeneral.Trace("Adding mdes from " + currentreader.Location.location);
+
+                    // Parse the data
+                    group.Value.Seek(0, SeekOrigin.Begin);
+                    mdeParser.Parse(group.Value, currentreader.Location.location + "\\" + group.Key);
+                    Dictionary<string, ModeldefEntry> mdes = mdeParser.ModelDefEntries;
+
+                    if (mdes != null) {
+                        foreach (KeyValuePair<string, ModeldefEntry> g in mdes) {
+                            g.Value.Location = currentreader.Location.location;
+                            modelDefEntriesByName.Add(g.Key, g.Value);
+                        }
+                    }
+                }
+            }
+
+            currentreader = null;
+            modeldefEntries = new Dictionary<int, ModeldefEntry>();
+
+            foreach (KeyValuePair<string, ModeldefEntry> e in modelDefEntriesByName) {
+                if (Actors.ContainsKey(e.Key)) {
+                    modeldefEntries[Actors[e.Key]] = modelDefEntriesByName[e.Key];
+                } else {
+                    GZBuilder.GZGeneral.LogAndTraceWarning("Got MODELDEF override for class '" + e.Key + "', but haven't found such class in Decorate");
+                }
+            }
+        }
+
+        #endregion
+
+        #region ================== Tools
+
+        // This finds the first IWAD resource
 		// Returns false when not found
 		internal bool FindFirstIWAD(out DataLocation result)
 		{
diff --git a/Source/Core/Data/DataReader.cs b/Source/Core/Data/DataReader.cs
index 27a5099f689750f9002493139e12a064a63999fc..50da0f4d364d2b2218f9d298d55e47f5a8d53a0f 100644
--- a/Source/Core/Data/DataReader.cs
+++ b/Source/Core/Data/DataReader.cs
@@ -156,6 +156,13 @@ namespace CodeImp.DoomBuilder.Data
 		// When implemented, this returns the decorate lump
 		public virtual List<Stream> GetDecorateData(string pname) { return new List<Stream>(); }
 
+        //mxd. When implemented, this returns the modeldef lump
+        public virtual Dictionary<string, Stream> GetModeldefData() { return new Dictionary<string, Stream>(); }
+
+        //mxd
+        //public Stream GetModel(string path) { return null; }
+        //protected abstract MemoryStream LoadFile(string filename);
+
 		#endregion
 	}
 }
diff --git a/Source/Core/Data/DirectoryReader.cs b/Source/Core/Data/DirectoryReader.cs
index ea22d8e520640ddce0c2f0b570cb3308e543336b..e32ae4fb62bf28254aabde42cf1ecf50566ae922 100644
--- a/Source/Core/Data/DirectoryReader.cs
+++ b/Source/Core/Data/DirectoryReader.cs
@@ -268,7 +268,7 @@ namespace CodeImp.DoomBuilder.Data
 		}
 
 		// This returns true if the specified file exists
-		protected override bool FileExists(string filename)
+		public override bool FileExists(string filename)
 		{
 			return files.FileExists(filename);
 		}
@@ -314,7 +314,7 @@ namespace CodeImp.DoomBuilder.Data
 		
 		// This loads an entire file in memory and returns the stream
 		// NOTE: Callers are responsible for disposing the stream!
-		protected override MemoryStream LoadFile(string filename)
+		public override MemoryStream LoadFile(string filename)
 		{
 			return new MemoryStream(File.ReadAllBytes(Path.Combine(location.location, filename)));
 		}
diff --git a/Source/Core/Data/PK3Reader.cs b/Source/Core/Data/PK3Reader.cs
index ec0fc5b43cd802835bec7d94a497a28dae74874a..05319c2b19dd3c6053f36549dc4c7a70ab1a642b 100644
--- a/Source/Core/Data/PK3Reader.cs
+++ b/Source/Core/Data/PK3Reader.cs
@@ -267,7 +267,7 @@ namespace CodeImp.DoomBuilder.Data
 		}
 
 		// This returns true if the specified file exists
-		protected override bool FileExists(string filename)
+		public override bool FileExists(string filename)
 		{
 			return files.FileExists(filename);
 		}
@@ -313,7 +313,7 @@ namespace CodeImp.DoomBuilder.Data
 
 		// This loads an entire file in memory and returns the stream
 		// NOTE: Callers are responsible for disposing the stream!
-		protected override MemoryStream LoadFile(string filename)
+		public override MemoryStream LoadFile(string filename)
 		{
 			MemoryStream filedata = null;
 			byte[] copybuffer = new byte[4096];
@@ -359,7 +359,10 @@ namespace CodeImp.DoomBuilder.Data
 			}
 			else
 			{
-				return filedata;
+				//mxd. rewind before use
+                filedata.Position = 0;
+
+                return filedata;
 			}
 		}
 
diff --git a/Source/Core/Data/PK3StructuredReader.cs b/Source/Core/Data/PK3StructuredReader.cs
index 9b24fabb760d12cf664a3b47f1510713e8f712a6..af3cbcc11f36c84a5bbd7ca1f85593d4d95f79f6 100644
--- a/Source/Core/Data/PK3StructuredReader.cs
+++ b/Source/Core/Data/PK3StructuredReader.cs
@@ -417,10 +417,32 @@ namespace CodeImp.DoomBuilder.Data
 		}
 
 		#endregion
-		
-		#region ================== Methods
-		
-		// This loads the images in this directory
+
+        #region ================== Modeldef
+        
+        //mxd
+        public override Dictionary<string, Stream> GetModeldefData() {
+            Dictionary<string, Stream> streams = new Dictionary<string, Stream>();
+            // Error when suspended
+            if (issuspended) throw new Exception("Data reader is suspended");
+
+            //modedef should be in root folder
+            string[] allFiles = GetAllFiles("", false);
+
+            foreach (string s in allFiles) {
+                if (s.ToLowerInvariant().IndexOf("modeldef") != -1) {
+                    streams.Add(s, LoadFile(s));
+                }
+            }
+
+            return streams;
+        }
+
+        #endregion
+
+        #region ================== Methods
+
+        // This loads the images in this directory
 		private ICollection<ImageData> LoadDirectoryImages(string path, int imagetype, bool includesubdirs)
 		{
 			List<ImageData> images = new List<ImageData>();
@@ -466,7 +488,8 @@ namespace CodeImp.DoomBuilder.Data
 		protected abstract ImageData CreateImage(string name, string filename, int imagetype);
 
 		// This must return true if the specified file exists
-		protected abstract bool FileExists(string filename);
+        //mxd
+		public abstract bool FileExists(string filename);
 
 		// This must return all files in a given directory
 		protected abstract string[] GetAllFiles(string path, bool subfolders);
@@ -488,7 +511,8 @@ namespace CodeImp.DoomBuilder.Data
 		
 		// This must load an entire file in memory and returns the stream
 		// NOTE: Callers are responsible for disposing the stream!
-		protected abstract MemoryStream LoadFile(string filename);
+        //mxd
+		public abstract MemoryStream LoadFile(string filename);
 
 		// This must create a temp file for the speciied file and return the absolute path to the temp file
 		// NOTE: Callers are responsible for removing the temp file when done!
diff --git a/Source/Core/Data/WADReader.cs b/Source/Core/Data/WADReader.cs
index fb8cdd086e290815c7b04e49efbdc8aa495588fb..80d77c219f0afa0206339882acda9a3cc8c0a6a2 100644
--- a/Source/Core/Data/WADReader.cs
+++ b/Source/Core/Data/WADReader.cs
@@ -816,7 +816,6 @@ namespace CodeImp.DoomBuilder.Data
 			
 			return streams;
 		}
-
 		#endregion
 	}
 }
diff --git a/Source/Core/GZBuilder/Data/ModelDefEntry.cs b/Source/Core/GZBuilder/Data/ModelDefEntry.cs
index 74a3524f15a4dde0cbadc5c118c1ec25751ac4da..40e53842df43093ddc99286a61f39028342f890f 100644
--- a/Source/Core/GZBuilder/Data/ModelDefEntry.cs
+++ b/Source/Core/GZBuilder/Data/ModelDefEntry.cs
@@ -5,27 +5,39 @@ using System.Text;
 using SlimDX;
 using SlimDX.Direct3D9;
 
-using ColladaDotNet.Pipeline.MD3;
+using CodeImp.DoomBuilder.GZBuilder.MD3;
 
 namespace CodeImp.DoomBuilder.GZBuilder.Data
 {
-    public class ModelDefEntry
+    public class ModeldefEntry
     {
-        public string Name;
-        public string Path;
+        public string ClassName;
+        public string Path; //this holds Path parameter of MODELDEF entry
         public List<string> ModelNames;
         public List<string> TextureNames;
+        public string Location; //this holds location of resource, from which modeldef was loaded
 
         public GZModel Model;
 
         public Vector3 Scale;
         public float zOffset;
 
-        public ModelDefEntry() {
-            Scale = new Vector3(1, 1, 1);
-            zOffset = 0;
+        public ModeldefEntry() {
             ModelNames = new List<string>();
             TextureNames = new List<string>();
         }
+
+        public void Dispose() {
+            if (Model != null) {
+                foreach (IndexBuffer ib in Model.Indeces2D)
+                    ib.Dispose();
+
+                foreach (Mesh mesh in Model.Meshes)
+                    mesh.Dispose();
+
+                foreach (Texture t in Model.Textures)
+                    t.Dispose();
+            }
+        }
     }
 }
diff --git a/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs b/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs
new file mode 100644
index 0000000000000000000000000000000000000000..af491586a8d2f6b9585d0f59531aacd4de2b021b
--- /dev/null
+++ b/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+using CodeImp.DoomBuilder.ZDoom;
+using CodeImp.DoomBuilder.GZBuilder.Data;
+using CodeImp.DoomBuilder.GZBuilder.GZDoom;
+
+namespace CodeImp.DoomBuilder.GZBuilder.GZDoom {
+   
+    public class ModeldefParser : ZDTextParser {
+        public static string INVALID_TEXTURE = "**INVALID_TEXTURE**";
+        
+        private Dictionary<string, ModeldefEntry> modelDefEntries; //classname, entry
+        public Dictionary<string, ModeldefEntry> ModelDefEntries { get { return modelDefEntries; } }
+
+        public string Source { get { return sourcename; } }
+
+        //should be called after all decorate actors are parsed 
+        public override bool Parse(Stream stream, string sourcefilename) {
+            base.Parse(stream, sourcefilename);
+            modelDefEntries = new Dictionary<string, ModeldefEntry>();
+
+            // Continue until at the end of the stream
+            while (SkipWhitespace(true)) {
+                string token = ReadToken();
+                if (token != null) {
+                    token = token.ToLowerInvariant();
+
+                    if (token == "model") { //model structure start
+                        //find classname
+                        SkipWhitespace(true);
+                        string className = StripTokenQuotes(ReadToken()).ToLowerInvariant();
+
+                        if (!string.IsNullOrEmpty(className)) {
+                            if (modelDefEntries.ContainsKey(className))
+                                continue; //already got this class; continue to next one
+
+                            //now find opening brace
+                            SkipWhitespace(true);
+                            token = ReadToken();
+                            if (token != "{") {
+                                GZBuilder.GZGeneral.LogAndTraceWarning("Unexpected token found in "+sourcefilename+" at line "+GetCurrentLineNumber()+": expected '{', but got " + token);
+                                continue; //something wrong with modeldef declaration, continue to next one
+                            }
+
+                            ModeldefStructure mds = new ModeldefStructure();
+                            ModeldefEntry mde = mds.Parse(this);
+                            if (mde != null) {
+                                GZBuilder.GZGeneral.Trace("Got mds for class " + className);
+                                mde.ClassName = className;
+                                modelDefEntries.Add(className, mde); 
+                            }
+                        } else {
+                            continue; //no class name found. continue to next structure
+                        }
+
+                    } else {
+                        // Unknown structure!
+                        string token2;
+                        do {
+                            if (!SkipWhitespace(true)) break;
+                            token2 = ReadToken();
+                            if (token2 == null) break;
+                        }
+                        while (token2 != "{");
+                        int scopelevel = 1;
+                        do {
+                            if (!SkipWhitespace(true)) break;
+                            token2 = ReadToken();
+                            if (token2 == null) break;
+                            if (token2 == "{") scopelevel++;
+                            if (token2 == "}") scopelevel--;
+                        }
+                        while (scopelevel > 0);
+                    }
+
+                }
+
+            }
+
+            if (modelDefEntries.Count > 0)
+                return true;
+            return false;
+        }
+    }
+}
diff --git a/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs b/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9c53faaf71e62022222e46ee73f3649784ba5581
--- /dev/null
+++ b/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs
@@ -0,0 +1,230 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+using SlimDX;
+using SlimDX.Direct3D9;
+
+using CodeImp.DoomBuilder.GZBuilder.Data;
+
+namespace CodeImp.DoomBuilder.GZBuilder.GZDoom {
+    public sealed class ModeldefStructure {
+        private string[] supportedTextureExtensions = { ".jpg", ".tga", ".png", ".dds" };
+
+        public ModeldefEntry Parse(ModeldefParser parser) {
+            string[] textureNames = new string[16];
+            string[] modelNames = new string[16];
+            string path = "";
+            Vector3 scale = new Vector3(1, 1, 1);
+            float zOffset = 0;
+            string token;
+            bool gotErrors = false;
+
+            //read modeldef structure contents
+            while (parser.SkipWhitespace(true)) {
+                token = parser.ReadToken();
+
+                if (!string.IsNullOrEmpty(token)) {
+                    token = token.ToLowerInvariant();
+
+                    char a = token[0];
+                    char c = " "[0];
+                    bool b1 = Char.IsWhiteSpace(a);
+                    bool b2 = Char.IsWhiteSpace(c);
+                    bool f;
+                    
+//path
+                    if (token == "path") {
+                        parser.SkipWhitespace(true);
+                        path = parser.StripTokenQuotes(parser.ReadToken()).Replace("/", "\\");
+
+                        if (string.IsNullOrEmpty(path)) {
+                            GZBuilder.GZGeneral.LogAndTraceWarning("Expected path to model, but got '" + token + "' in " + parser.Source + " at line "+parser.GetCurrentLineNumber());
+                            //GZBuilder.GZGeneral.LogAndTraceWarning("Expected path to model, but got '" + token + "'");
+                            gotErrors = true;
+                            break;
+                        }
+//model
+                    } else if (token == "model") {
+                        parser.SkipWhitespace(true);
+
+                        //model index
+                        int modelIndex;
+                        token = parser.ReadToken();
+                        if (!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out modelIndex)) {
+                            // Not numeric!
+                            GZBuilder.GZGeneral.LogAndTraceWarning("Expected model index, but got '" + token + "' in " + parser.Source + " at line " + parser.GetCurrentLineNumber());
+                            gotErrors = true;
+                            break;
+                        }
+
+                        //model path
+                        token = parser.StripTokenQuotes(parser.ReadToken()).ToLowerInvariant();
+                        if (string.IsNullOrEmpty(token)) {
+                            GZBuilder.GZGeneral.LogAndTraceWarning("Expected model name, but got '" + token + "' in " + parser.Source + " at line " + parser.GetCurrentLineNumber());
+                            gotErrors = true;
+                            break;
+                        } else {
+                            //check extension
+                            int dotPos = token.LastIndexOf(".");
+                            string fileExt = token.Substring(token.LastIndexOf("."), token.Length - dotPos);
+                            if (fileExt != ".md3" && fileExt != ".md2") {
+                                GZBuilder.GZGeneral.LogAndTraceWarning("Model '" + token + "' not parsed in " + parser.Source + " at line " + parser.GetCurrentLineNumber() +". Only MD3 and MD2 models are supported.");
+                                gotErrors = true;
+                                break;
+                            }
+
+                            if (modelNames[modelIndex] != null) {
+                                GZBuilder.GZGeneral.LogAndTraceWarning("Error: already got model for index " + modelIndex + " in " + parser.Source + " at line " + parser.GetCurrentLineNumber());
+                                gotErrors = true;
+                                break;
+                            } else {
+                                modelNames[modelIndex] = token;
+                            }
+                        }
+//skin
+                    } else if (token == "skin") {
+                        parser.SkipWhitespace(true);
+
+                        //skin index
+                        int skinIndex;
+                        token = parser.ReadToken();
+                        if (!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out skinIndex)) {
+                            // Not numeric!
+                            GZBuilder.GZGeneral.LogAndTraceWarning("Expected skin index, but got '" + token + "' in " + parser.Source + " at line " + parser.GetCurrentLineNumber());
+                            gotErrors = true;
+                            break;
+                        }
+
+                        //skin path
+                        token = parser.StripTokenQuotes(parser.ReadToken()).ToLowerInvariant();
+                        if (string.IsNullOrEmpty(token)) {
+                            GZBuilder.GZGeneral.LogAndTraceWarning("Expected skin name, but got '" + token + "' in " + parser.Source + " at line " + parser.GetCurrentLineNumber());
+                            gotErrors = true;
+                            break;
+                        } else {
+                            //check extension
+                            int dotPos = token.LastIndexOf(".");
+                            string fileExt = token.Substring(token.LastIndexOf("."), token.Length - dotPos);
+                            if(Array.IndexOf(supportedTextureExtensions, fileExt) == -1)
+                                token = ModeldefParser.INVALID_TEXTURE;
+
+                            if (textureNames[skinIndex] != null) {
+                                GZBuilder.GZGeneral.LogAndTraceWarning("Already got model for index " + skinIndex + " in " + parser.Source + " at line " + parser.GetCurrentLineNumber());
+                                gotErrors = true;
+                                break;
+                            } else {
+                                textureNames[skinIndex] = token;
+                            }
+                        }
+//scale
+                    } else if (token == "scale") {
+                        parser.SkipWhitespace(true);
+
+                        token = parser.ReadToken();
+
+                        int sign = 1;
+                        if (token == "-") {
+                            sign = -1;
+                            token = parser.ReadToken();
+                        }
+
+                        if (!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out scale.X)) {
+                            // Not numeric!
+                            GZBuilder.GZGeneral.LogAndTraceWarning("Expected scale X value, but got '" + token + "' in " + parser.Source + " at line " + parser.GetCurrentLineNumber());
+                            gotErrors = true;
+                            break;
+                        }
+                        scale.X *= sign;
+
+                        parser.SkipWhitespace(true);
+
+                        token = parser.ReadToken();
+
+                        sign = 1;
+                        if (token == "-") {
+                            sign = -1;
+                            token = parser.ReadToken();
+                        }
+
+                        if (!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out scale.Y)) {
+                            // Not numeric!
+                            GZBuilder.GZGeneral.LogAndTraceWarning("Expected scale Y value, but got '" + token + "' in " + parser.Source + " at line " + parser.GetCurrentLineNumber());
+                            gotErrors = true;
+                            break;
+                        }
+                        scale.Y *= sign;
+
+
+                        parser.SkipWhitespace(true);
+
+                        token = parser.ReadToken();
+
+                        sign = 1;
+                        if (token == "-") {
+                            sign = -1;
+                            token = parser.ReadToken();
+                        }
+
+                        if (!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out scale.Z)) {
+                            // Not numeric!
+                            GZBuilder.GZGeneral.LogAndTraceWarning("Expected scale Z value, but got '" + token + "' in " + parser.Source + " at line " + parser.GetCurrentLineNumber());
+                            gotErrors = true;
+                            break;
+                        }
+                        scale.Z *= sign;
+//zoffset
+                    } else if (token == "zoffset") {
+                        parser.SkipWhitespace(true);
+
+                        token = parser.ReadToken();
+
+                        int sign = 1;
+                        if (token == "-") {
+                            sign = -1;
+                            token = parser.ReadToken();
+                        }
+
+                        if (!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out zOffset)) {
+                            // Not numeric!
+                            GZBuilder.GZGeneral.LogAndTraceWarning("Expected ZOffset value, but got '" + token + "' in " + parser.Source + " at line " + parser.GetCurrentLineNumber());
+                            gotErrors = true;
+                            break;
+                        }
+                        zOffset *= sign;
+//frameindex
+                    } else if (token == "frameindex") {
+                        //parsed all required fields
+                        break;
+                    }
+                }
+            }
+
+            //find closing brace, then quit;
+            while (parser.SkipWhitespace(true)) {
+                token = parser.ReadToken();
+                if (token == "}")
+                    break;
+            }
+
+            if (gotErrors)
+                return null;    
+
+            //classname is set in ModeldefParser
+            ModeldefEntry mde = new ModeldefEntry();
+            mde.Path = path;
+            mde.Scale = scale;
+            mde.zOffset = zOffset;
+
+            for (int i = 0; i < textureNames.Length; i++ ) {
+                if (textureNames[i] != null && modelNames[i] != null) {
+                    mde.TextureNames.Add(textureNames[i]);
+                    mde.ModelNames.Add(modelNames[i]);
+                }
+            }
+
+            return mde;
+        }
+    }
+}
diff --git a/Source/Core/GZBuilder/GZGeneral.cs b/Source/Core/GZBuilder/GZGeneral.cs
index 17c5797c912508aa90b907fdae6d81b37272186e..e0b4b8eb84453b03e8e529e7c580e91038220857 100644
--- a/Source/Core/GZBuilder/GZGeneral.cs
+++ b/Source/Core/GZBuilder/GZGeneral.cs
@@ -10,20 +10,14 @@ using CodeImp.DoomBuilder.Actions;
 using CodeImp.DoomBuilder.Windows;
 using CodeImp.DoomBuilder.Config;
 
-using CodeImp.DoomBuilder.GZBuilder.IO;
 using CodeImp.DoomBuilder.GZBuilder.Data;
 using CodeImp.DoomBuilder.GZBuilder.Controls;
 
-using ColladaDotNet.Pipeline.MD3;
-
 namespace CodeImp.DoomBuilder.GZBuilder
 {
     //mxd. should get rid of this class one day...
     public class GZGeneral
     {
-        private static Dictionary<int, ModelDefEntry> modelDefEntries; //doomEdNum, entry
-        public static Dictionary<int, ModelDefEntry> ModelDefEntries { get { return modelDefEntries; } }
-
         //gzdoom light types
         private static int[] gzLights = { /* normal lights */ 9800, 9801, 9802, 9803, 9804, /* additive lights */ 9810, 9811, 9812, 9813, 9814, /* negative lights */ 9820, 9821, 9822, 9823, 9824, /* vavoom lights */ 1502, 1503};
         public static int[] GZ_LIGHTS { get { return gzLights; } }
@@ -34,6 +28,8 @@ namespace CodeImp.DoomBuilder.GZBuilder
 
         public static bool UDMF;
 
+        //public static float[] FogTable; // light to fog conversion table for black fog
+
         //version
         public const float Version = 1.06f;
 
@@ -48,6 +44,26 @@ namespace CodeImp.DoomBuilder.GZBuilder
             General.Actions.BindMethods(typeof(GZGeneral));
             General.MainWindow.UpdateGZDoomPannel();
 
+            //create fog table
+            /*FogTable = new float[256];
+            byte gl_distfog = 255;
+
+            for (int i = 0; i < 256; i++) {
+                if (i < 164) {
+                    FogTable[i] = (gl_distfog >> 1) + (gl_distfog) * (164 - i) / 164;
+                } else if (i < 230) {
+                    FogTable[i] = (gl_distfog >> 1) - (gl_distfog >> 1) * (i - 164) / (230 - 164);
+                } else FogTable[i] = 0;
+
+                //if (i < 128) {
+                    //distfogtable[1][i] = 6.f + (gl_distfog >> 1) + (gl_distfog) * (128 - i) / 48;
+                //} else if (i < 216) {
+                    //distfogtable[1][i] = (216.f - i) / ((216.f - 128.f)) * gl_distfog / 10;
+                //} else distfogtable[1][i] = 0;
+            }*/
+
+            //float[] ft = FogTable;
+
             //create console
 #if DEBUG
             ConsoleDocker cd = new ConsoleDocker();
@@ -58,105 +74,25 @@ namespace CodeImp.DoomBuilder.GZBuilder
         }
 
         public static void OnMapOpenEnd() {
-            loadModelDefs();
-            loadModels();
             UDMF = (General.Map.Config.FormatInterface == "UniversalMapSetIO");
             General.MainWindow.UpdateGZDoomPannel();
         }
 
         public static void OnReloadResources() {
-            loadModelDefs();
-            loadModels();
-
 #if DEBUG
             ((ConsoleDocker)console.Control).Clear();
 #endif
         }
 
-        public static bool LoadModelForThing(Thing t) {
-            if (modelDefEntries.ContainsKey(t.Type)) {
-                string msg = "GZBuilder: got model override for Thing â„–" + t.Type;
-                General.ErrorLogger.Add(ErrorType.Warning, msg);
-                General.WriteLogLine(msg);
-
-                if (modelDefEntries[t.Type].Model == null) {
-                    //load model and texture
-                    ModelDefEntry mde = modelDefEntries[t.Type];
-                    mde.Model = ModelReader.Parse(mde, General.Map.Graphics.Device);
-
-                    if (mde.Model != null) {
-                        //General.Map.IsChanged = true; 
-                        //General.MainWindow.RedrawDisplay(); //update display
-                        msg = "GZBuilder: loaded model for Thing â„–" + t.Type;
-                        General.ErrorLogger.Add(ErrorType.Warning, msg);
-                        General.WriteLogLine(msg);
-                        return true;
-                    } else {
-                        modelDefEntries.Remove(t.Type);
-                        msg = "GZBuilder: failed to load model(s) for Thing â„–" + t.Type + ". ModelDef node removed.";
-                        General.ErrorLogger.Add(ErrorType.Warning, msg);
-                        General.WriteLogLine(msg);
-                        return false;
-                    }
-                }
-                return true;
-            }
-            return false;
-        }
-
-//functions
-        private static void loadModelDefs() {
-            General.MainWindow.DisplayStatus(StatusType.Busy, "Parsing model definitions...");
-
-            Dictionary<string, int> Actors = new Dictionary<string,int>();
-            Dictionary<int, ThingTypeInfo> things = General.Map.Config.GetThingTypes();
-
-            //read our new shiny ClassNames for default game things
-            foreach (KeyValuePair<int, ThingTypeInfo> ti in things) {
-                if (ti.Value.ClassName != null)
-                    Actors.Add(ti.Value.ClassName, ti.Key);
-            }
-
-            //and for actors defined in DECORATE
-            ICollection<ActorStructure> ac = General.Map.Data.Decorate.Actors;
-            foreach (ActorStructure actor in ac) {
-                string className = actor.ClassName.ToLower();
-                if (actor.DoomEdNum != -1 && !Actors.ContainsKey(className)) //we don't need actors without DoomEdNum
-                    Actors.Add(className, actor.DoomEdNum);
-            }
-
-            Dictionary<string, ModelDefEntry> modelDefEntriesByName = new Dictionary<string, ModelDefEntry>();
-
-            foreach (string folder in General.Map.Data.Folders)
-                ModelDefParser.ParseFolder(modelDefEntriesByName, folder);
-
-            modelDefEntries = new Dictionary<int, ModelDefEntry>();
-
-            foreach (KeyValuePair<string, ModelDefEntry> e in modelDefEntriesByName) {
-                if (Actors.ContainsKey(e.Value.Name)) {
-                    modelDefEntries[Actors[e.Value.Name]] = modelDefEntriesByName[e.Value.Name];
-                } else {
-                    string msg = "GZBuilder: ModelDefEntry wasn't found in Decorate: '" + e.Value.Name + "'";
-                    General.ErrorLogger.Add(ErrorType.Warning, msg);
-                    General.WriteLogLine(msg);
-                }
-            }
-        }
-
-        //load models for things which are already in the map
-        private static void loadModels() {
-            General.MainWindow.DisplayStatus(StatusType.Busy, "Loading models...");
-            string msg = "GZBuilder: loading models...";
-            General.ErrorLogger.Add(ErrorType.Warning, msg);
-            General.WriteLogLine(msg);
-            
-            foreach(Thing t in General.Map.Map.Things)
-                LoadModelForThing(t);
-
-            General.MainWindow.RedrawDisplay();
+//debug
+        public static void LogAndTraceWarning(string message) {
+            General.ErrorLogger.Add(ErrorType.Warning, message);
+            General.WriteLogLine(message);
+#if DEBUG
+            Trace(message);
+#endif
         }
 
-//debug
         public static void Trace(string message) {
 #if DEBUG
             ((ConsoleDocker)console.Control).Trace(message);
diff --git a/Source/Core/GZBuilder/IO/ModelDefParser.cs b/Source/Core/GZBuilder/IO/ModelDefParser.cs
deleted file mode 100644
index 4e799e542761dde70530dfb1167e9fce6bcdb89d..0000000000000000000000000000000000000000
--- a/Source/Core/GZBuilder/IO/ModelDefParser.cs
+++ /dev/null
@@ -1,197 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Globalization;
-using CodeImp.DoomBuilder.GZBuilder.Data;
-
-namespace CodeImp.DoomBuilder.GZBuilder.IO
-{
-    class ModelDefParser
-    {
-        public static string INVALID_TEXTURE = "**INVALID_TEXTURE**";
-        private static string[] SUPPORTED_TEXTURE_EXTENSIONS = { ".jpg", ".tga", ".png", ".dds", ".bmp" };
-        
-        public static void ParseFolder(Dictionary<string, ModelDefEntry> modelDefEntriesByName, string path) {
-            string[] files = Directory.GetFiles(path);
-
-            foreach (string fileName in files) {
-                if (fileName.ToLower().IndexOf("modeldef") != -1)
-                    Parse(modelDefEntriesByName, path, fileName);
-            }
-
-            logAndTrace("ModelDefParser: parsed " + modelDefEntriesByName.Count + " definitions;");
-        }
-
-        public static void Parse(Dictionary<string, ModelDefEntry> modelDefEntriesByName, string path, string fileName) {
-            logAndTrace("ModelDefParser: Parsing '" + fileName + "'");
-
-            if (File.Exists(fileName)) {
-                StreamReader s = File.OpenText(fileName);
-                string contents = s.ReadToEnd();
-                s.Close();
-
-                contents = StripComments(contents).ToLower();
-
-                int startIndex = 0;
-                int mdlIndex = 0;
-
-                while ((mdlIndex = contents.IndexOf("model", startIndex)) != -1) {
-                    startIndex = contents.IndexOf("}", mdlIndex);
-                    parseModelDef(modelDefEntriesByName, path, contents.Substring(mdlIndex, startIndex - mdlIndex));
-                }
-
-            } else {
-                logAndTrace("ModelDefParser: File '" + fileName + "' doesn't exist!");
-            }
-        }
-
-        private static void parseModelDef(Dictionary<string, ModelDefEntry> modelDefEntriesByName, string path, string modelDef) {
-            string[] modelNames = new string[16];
-            string[] textureNames = new string[16];
-            
-            string[] lines = modelDef.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
-            char[] space = new char[] { ' ' };
-            string[] parts = lines[0].Split(space, StringSplitOptions.RemoveEmptyEntries);
-            string name = parts[1].Trim().Replace("{", "");
-
-            if (modelDefEntriesByName.ContainsKey(name)) {
-                General.WriteLogLine("Already have ModelDef for '" + name + "'");
-                return;
-            }
-            char[] splitter = new char[] { ' ', '"' };
-            ModelDefEntry mde = new ModelDefEntry();
-            mde.Name = name;
-
-            for (int i = 1; i < lines.Length; i++) {
-                parts = lines[i].Split(space, StringSplitOptions.RemoveEmptyEntries);
-
-                if (parts.Length > 0) {
-                    string s = parts[0].Trim();
-
-                    //LOTS of boilerplate! Yay!!!
-                    if (s.IndexOf("frameindex") != -1) {
-                        if (mde.Name != String.Empty && mde.Path != String.Empty) {
-                            for (int c = 0; c < modelNames.Length; c++) {
-                                if (modelNames[c] != null && textureNames[c] != null) {
-                                    mde.ModelNames.Add(modelNames[c]);
-                                    mde.TextureNames.Add(textureNames[c]);
-                                }
-                            }
-
-                            if (mde.ModelNames.Count > 0 && mde.TextureNames.Count > 0)
-                                modelDefEntriesByName[mde.Name] = mde;
-                            else
-                                logAndTrace("Error while parsing ModelDef. Not all required fileds are present." + Environment.NewLine + "Parsed data: [" + Environment.NewLine + ModelDefEntry_ToString(mde) + "]" + Environment.NewLine);
-                            return; //we don't want to parse all frames
-  
-                        } 
-                        logAndTrace("Error while parsing ModelDef. Not all required fileds are present." + Environment.NewLine + "Parsed data: [" + Environment.NewLine + ModelDefEntry_ToString(mde) + "]" + Environment.NewLine);
-                        return;
-
-                    } else if (s.IndexOf("model") != -1) {
-                        if (parts.Length != 3) {
-                            logAndTrace("Incorrect syntax in 'model' token for class '" + name + "': expected 3 entries, but got " + parts.Length + "!");
-                            return;
-                        }
-
-                        int fileIndex = int.Parse(parts[1].Trim(splitter), NumberStyles.Integer);
-                        string fileName = parts[2].Trim(splitter);
-                        string fileExt = Path.GetExtension(fileName);
-
-                        if (fileExt == ".md3" || fileExt == ".md2") {
-                            if (modelNames[fileIndex] != null) {
-                                logAndTrace("Incorrect syntax in 'model' token for class '" + name + "': already got model with index " + fileIndex + "!");
-                                return;
-                            }
-                            modelNames[fileIndex] = fileName;
-
-                        } else {
-                            logAndTrace("Model '" + fileName + "' not parsed. Only MD3 and MD2 models are supported.");
-                            return;
-                        }
-
-                    } else if (s.IndexOf("path") != -1) {
-                        if (parts.Length != 2) {
-                            logAndTrace("Incorrect syntax in 'path' token for class '" + name + "': expected 2 entries, but got " + parts.Length + "!");
-                            return;
-                        }
-
-                        mde.Path = path + "\\" + parts[1].Trim(splitter).Replace("/", "\\");
-
-                    } else if (s.IndexOf("skin") != -1) {
-                        if (parts.Length != 3) {
-                            logAndTrace("Incorrect syntax in 'skin' token for class '" + name + "': expected 3 entries, but got " + parts.Length + "!");
-                            return;
-                        }
-
-                        int index = int.Parse(parts[1].Trim(splitter), NumberStyles.Integer);
-                        if (textureNames[index] != null) {
-                            logAndTrace("Incorrect syntax in 'skin' token for class '" + name + "': already got skin with index " + index + "!");
-                            return;
-                        }
-
-                        string textureName = parts[2].Trim(splitter);
-                        string fileExt = Path.GetExtension(textureName);
-                        textureNames[index] = Array.IndexOf(SUPPORTED_TEXTURE_EXTENSIONS, fileExt) == -1 ? INVALID_TEXTURE : textureName;
-
-                    } else if (s.IndexOf("scale") != -1) {
-                        if (parts.Length != 4) {
-                            logAndTrace("Incorrect syntax in 'scale' token for class '" + name + "': expected 4 entries, but got " + parts.Length + "!");
-                            return;
-                        }
-
-                        mde.Scale.X = float.Parse(parts[1], NumberStyles.Float);
-                        mde.Scale.Y = float.Parse(parts[2], NumberStyles.Float);
-                        mde.Scale.Z = float.Parse(parts[3], NumberStyles.Float);
-
-                    } else if (s.IndexOf("zoffset") != -1) {
-                        if (parts.Length != 2) {
-                            logAndTrace("Incorrect syntax in 'zoffset' token for class '" + name + "': expected 2 entries, but got " + parts.Length + "!");
-                            return;
-                        }
-
-                        mde.zOffset = float.Parse(parts[1], NumberStyles.Float);
-                    }
-                }
-            }
-        }
-
-        private static string ModelDefEntry_ToString(ModelDefEntry mde) {
-            string[] models = new string[mde.ModelNames.Count];
-            mde.ModelNames.CopyTo(models);
-
-            string[] textures = new string[mde.TextureNames.Count];
-            mde.TextureNames.CopyTo(textures);
-
-            return "Name: " + mde.Name + Environment.NewLine
-                   + "Path: " + mde.Path + Environment.NewLine
-                   + "Models: " + String.Join(", ", models) + Environment.NewLine
-                   + "Skins: " + String.Join(", ", textures) + Environment.NewLine
-                   + "Scale: " + mde.Scale + Environment.NewLine
-                   + "zOffset: " + mde.zOffset + Environment.NewLine;
-        }
-
-        private static string StripComments(string contents) {
-            int start, end;
-
-            //comments
-            while ((start = contents.IndexOf("//")) != -1) {
-                end = contents.IndexOf(Environment.NewLine, start);
-                contents = contents.Remove(start, end - start);
-            }
-
-            //block comments
-            while ((start = contents.IndexOf("/*")) != -1) {
-                end = contents.IndexOf("*/");
-                contents = contents.Remove(start, end - start + 2);
-            }
-
-            return contents;
-        }
-
-        private static void logAndTrace(string message) {
-            General.ErrorLogger.Add(ErrorType.Warning, message);
-            General.WriteLogLine(message);
-        }
-    }
-}
\ No newline at end of file
diff --git a/Source/Core/GZBuilder/md3/GZModel.cs b/Source/Core/GZBuilder/md3/GZModel.cs
index a9193119b1d436d61ff1b82c735167be4b838597..1442cd08e9310f01b68301313ead4af8051a158b 100644
--- a/Source/Core/GZBuilder/md3/GZModel.cs
+++ b/Source/Core/GZBuilder/md3/GZModel.cs
@@ -4,7 +4,7 @@ using SlimDX.Direct3D9;
 using CodeImp.DoomBuilder.Geometry;
 
 
-namespace ColladaDotNet.Pipeline.MD3
+namespace CodeImp.DoomBuilder.GZBuilder.MD3
 {
     public class GZModel {
         public List<Mesh> Meshes;
diff --git a/Source/Core/GZBuilder/md3/ModelReader.cs b/Source/Core/GZBuilder/md3/ModelReader.cs
index 02851523f602d90a5c9c7650e8af76525459be1f..e1889794ed1b3944e0be65cc58bae724dacdfc0f 100644
--- a/Source/Core/GZBuilder/md3/ModelReader.cs
+++ b/Source/Core/GZBuilder/md3/ModelReader.cs
@@ -5,83 +5,90 @@ using System.Text;
 using System.Collections.Generic;
 
 using CodeImp.DoomBuilder;
+using CodeImp.DoomBuilder.Data;
 using CodeImp.DoomBuilder.Rendering;
 using CodeImp.DoomBuilder.GZBuilder.Data;
-using CodeImp.DoomBuilder.GZBuilder.IO;
+using CodeImp.DoomBuilder.GZBuilder.GZDoom;
 
 using SlimDX;
 using SlimDX.Direct3D9;
 
 //mxd. Original version taken from here: http://colladadotnet.codeplex.com/SourceControl/changeset/view/40680
-namespace ColladaDotNet.Pipeline.MD3 {
-    public class ModelReader {
-        public static GZModel Parse(ModelDefEntry mde, Device D3DDevice) {
-            string[] modelPaths = new string[mde.ModelNames.Count];
-            string[] texturePaths = new string[mde.TextureNames.Count];
-
-            mde.ModelNames.CopyTo(modelPaths);
-            mde.TextureNames.CopyTo(texturePaths);
-
-            if (modelPaths.Length != texturePaths.Length || texturePaths.Length == 0 || modelPaths.Length == 0) {
-                General.ErrorLogger.Add(ErrorType.Warning, "MD3Reader: wrong parse params! (modelPaths=" + modelPaths.ToString() + "; texturePaths=" + texturePaths.ToString() + ")");
-                return null;
-            }
+namespace CodeImp.DoomBuilder.GZBuilder.MD3
+{
+    internal class ModelReader
+    {
+        public static void Parse(ref ModeldefEntry mde, PK3StructuredReader reader, Device D3DDevice) {
+            string[] modelNames = new string[mde.ModelNames.Count];
+            string[] textureNames = new string[mde.TextureNames.Count];
+
+            mde.ModelNames.CopyTo(modelNames);
+            mde.TextureNames.CopyTo(textureNames);
+
+            //should never happen
+            /*if (modelNames.Length != textureNames.Length || textureNames.Length == 0 || modelNames.Length == 0) {
+                General.ErrorLogger.Add(ErrorType.Warning, "MD3Reader: wrong parse params! (modelPaths=" + modelNames.ToString() + "; texturePaths=" + textureNames.ToString() + ")");
+                return;
+            }*/
 
-            GZModel model = new GZModel();
-            model.NUM_MESHES = (byte)modelPaths.Length;
+            mde.Model = new GZModel();
+            mde.Model.NUM_MESHES = (byte)modelNames.Length;
 
             BoundingBoxSizes bbs = new BoundingBoxSizes();
 
-            for (int i = 0; i < modelPaths.Length; i++) {
-                string modelPath = mde.Path + "\\" + modelPaths[i];
-                if (File.Exists(modelPath)) {
+            for (int i = 0; i < modelNames.Length; i++) {
+                string modelPath = Path.Combine(mde.Path, modelNames[i]);
+
+                if (reader.FileExists(modelPath)) {
+                    MemoryStream stream = reader.LoadFile(modelPath);
                     General.WriteLogLine("MD3Reader: loading '" + modelPath + "'");
+
                     //mesh
-                    string ext = modelPaths[i].Substring(modelPaths[i].Length - 4);
-                    bool loaded = false;
-                    if (ext == ".md3") {
-                        loaded = ReadMD3Model(ref bbs, mde, model, modelPath, D3DDevice);
-                    } else if (ext == ".md2") {
-                        loaded = ReadMD2Model(ref bbs, mde, model, modelPath, D3DDevice);
-                    }
+                    string ext = modelNames[i].Substring(modelNames[i].Length - 4);
+                    string error = "";
+                    if (ext == ".md3")
+                        error = ReadMD3Model(ref bbs, ref mde, stream, D3DDevice);
+                    else if (ext == ".md2")
+                        error = ReadMD2Model(ref bbs, ref mde, stream, D3DDevice);
 
                     //texture
-                    if (loaded) {
-                        string texturePath = mde.Path + "\\" + texturePaths[i];
-                        if (texturePaths[i] != ModelDefParser.INVALID_TEXTURE && File.Exists(texturePath)) {
-                            model.Textures.Add(Texture.FromFile(D3DDevice, texturePath));
+                    if (string.IsNullOrEmpty(error)) {
+                        string texturePath = Path.Combine(mde.Path, textureNames[i]);
+                        
+                        if (textureNames[i] != ModeldefParser.INVALID_TEXTURE && reader.FileExists(texturePath)) {
+                            mde.Model.Textures.Add(Texture.FromStream(D3DDevice, reader.LoadFile(texturePath)));
                         } else {
-                            model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
-                            if (texturePaths[i] != ModelDefParser.INVALID_TEXTURE)
-                                General.ErrorLogger.Add(ErrorType.Warning, "MD3Reader: unable to load texture '" + texturePath + "' - no such file");
+                            mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
+                            if (textureNames[i] != ModeldefParser.INVALID_TEXTURE)
+                                GZBuilder.GZGeneral.LogAndTraceWarning("MD3Reader: unable to load texture '" + texturePath + "' - no such file");
                         }
                     } else {
-                        model.NUM_MESHES--;
+                        GZBuilder.GZGeneral.LogAndTraceWarning("MD3Reader: error while loading " + modelPath + ": " + error);
+                        mde.Model.NUM_MESHES--;
                     }
+                    stream.Dispose();
+
                 } else {
-                    General.ErrorLogger.Add(ErrorType.Warning, "MD3Reader: unable to load model '" + mde.Path + "\\" + modelPaths[i] + "' - no such file");
-                    model.NUM_MESHES--;
+                    GZBuilder.GZGeneral.LogAndTraceWarning("MD3Reader: unable to load model '" + modelPath + "' - no such file");
+                    mde.Model.NUM_MESHES--;
                 }
             }
 
-            if (model.NUM_MESHES <= 0)
-                return null;
-
-            model.BoundingBox = BoundingBoxTools.CalculateBoundingBox(bbs);
+            if (mde.Model.NUM_MESHES <= 0) {
+                mde.Model = null;
+                return;
+            }
 
-            return model;
+            mde.Model.BoundingBox = BoundingBoxTools.CalculateBoundingBox(bbs);
         }
 
-        private static bool ReadMD3Model(ref BoundingBoxSizes bbs, ModelDefEntry mde, GZModel model, string modelPath, Device D3DDevice) {
-            FileStream s = new FileStream(modelPath, FileMode.Open);
+        private static string ReadMD3Model(ref BoundingBoxSizes bbs, ref ModeldefEntry mde, MemoryStream s, Device D3DDevice) {
             long start = s.Position;
 
             using (var br = new BinaryReader(s, Encoding.ASCII)) {
                 string magic = ReadString(br, 4);
-                if (magic != "IDP3") {
-                    General.ErrorLogger.Add(ErrorType.Warning, "MD3Reader: Error while loading '" + modelPath + "': Magic should be 'IDP3', not '" + magic + "'");
-                    return false;
-                }
+                if (magic != "IDP3")
+                    return "magic should be 'IDP3', not '" + magic + "'";
 
                 s.Position += 80;
                 int numSurfaces = br.ReadInt32();
@@ -94,8 +101,12 @@ namespace ColladaDotNet.Pipeline.MD3 {
                 List<short> polyIndecesList = new List<short>();
                 List<WorldVertex> vertList = new List<WorldVertex>();
 
-                for (int c = 0; c < numSurfaces; ++c)
-                    ReadSurface(ref bbs, br, polyIndecesList, vertList, mde);
+                string error = "";
+                for (int c = 0; c < numSurfaces; ++c) {
+                    error = ReadSurface(ref bbs, br, polyIndecesList, vertList, mde);
+                    if (!string.IsNullOrEmpty(error))
+                        return error;
+                }
 
                 //indeces for rendering current mesh in 2d
                 short[] indeces2d_arr = CreateLineListIndeces(polyIndecesList);
@@ -112,7 +123,7 @@ namespace ColladaDotNet.Pipeline.MD3 {
                 mesh.IndexBuffer.Unlock();
 
                 mesh.OptimizeInPlace(MeshOptimizeFlags.AttributeSort);
-                model.Meshes.Add(mesh);
+                mde.Model.Meshes.Add(mesh);
 
                 //2d data
                 IndexBuffer indeces2d = new IndexBuffer(D3DDevice, 2 * indeces2d_arr.Length, Usage.WriteOnly, Pool.Managed, true);
@@ -120,19 +131,17 @@ namespace ColladaDotNet.Pipeline.MD3 {
                 stream.WriteRange(indeces2d_arr);
                 indeces2d.Unlock();
 
-                model.Indeces2D.Add(indeces2d);
-                model.NumIndeces2D.Add((short)polyIndecesList.Count);
+                mde.Model.Indeces2D.Add(indeces2d);
+                mde.Model.NumIndeces2D.Add((short)polyIndecesList.Count);
             }
-            return true;
+            return "";
         }
 
-        private static void ReadSurface(ref BoundingBoxSizes bbs, BinaryReader br, List<short> polyIndecesList, List<WorldVertex> vertList, ModelDefEntry mde) {
+        private static string ReadSurface(ref BoundingBoxSizes bbs, BinaryReader br, List<short> polyIndecesList, List<WorldVertex> vertList, ModeldefEntry mde) {
             var start = br.BaseStream.Position;
             string magic = ReadString(br, 4);
-            if (magic != "IDP3") {
-                General.ErrorLogger.Add(ErrorType.Warning, "MD3Reader: Error while reading surface: Magic should be 'IDP3', not '" + magic + "'");
-                return;
-            }
+            if (magic != "IDP3")
+                return "error while reading surface: Magic should be 'IDP3', not '" + magic + "'";
 
             br.BaseStream.Position += 76;
             int numVerts = br.ReadInt32(); //Number of Vertex objects defined in this Surface, up to MD3_MAX_VERTS. Current value of MD3_MAX_VERTS is 4096.
@@ -170,9 +179,7 @@ namespace ColladaDotNet.Pipeline.MD3 {
 
             for (int i = 0; i < numVerts; i++) {
                 WorldVertex v = vertList[i];
-                //short[] coords = new short[] { br.ReadInt16(), br.ReadInt16(), br.ReadInt16() };
 
-                //v.Position = new Vector3((float)coords[1] / 64, -(float)coords[0] / 64, (float)coords[2] / 64);
                 v.y = -(float)br.ReadInt16() / 64 * mde.Scale.X;
                 v.x = (float)br.ReadInt16() / 64 * mde.Scale.Y;
                 v.z = (float)br.ReadInt16() / 64 * mde.Scale.Z + mde.zOffset;
@@ -192,23 +199,20 @@ namespace ColladaDotNet.Pipeline.MD3 {
 
             if (start + ofsEnd != br.BaseStream.Position)
                 br.BaseStream.Position = start + ofsEnd;
+            return "";
         }
 
-        private static bool ReadMD2Model(ref BoundingBoxSizes bbs, ModelDefEntry mde, GZModel model, string modelPath, Device D3DDevice) {
-            FileStream s = new FileStream(modelPath, FileMode.Open);
+        private static string ReadMD2Model(ref BoundingBoxSizes bbs, ref ModeldefEntry mde, MemoryStream s, Device D3DDevice) {
             long start = s.Position;
 
             using (var br = new BinaryReader(s, Encoding.ASCII)) {
                 string magic = ReadString(br, 4);
-                if (magic != "IDP2") { //magic number: "IDP2"
-                    General.ErrorLogger.Add(ErrorType.Warning, "MD3Reader: Error while loading '" + modelPath + "': Magic should be 'IDP2', not '" + magic + "'");
-                    return false;
-                }
+                if (magic != "IDP2")  //magic number: "IDP2"
+                    return "magic should be 'IDP2', not '" + magic + "'";
+
                 int modelVersion = br.ReadInt32();
-                if (modelVersion != 8) { //MD2 version. Must be equal to 8
-                    General.ErrorLogger.Add(ErrorType.Warning, "MD3Reader: Error while loading '" + modelPath + "': MD2 version must be 8 but is " + modelVersion);
-                    return false;
-                }
+                if (modelVersion != 8)  //MD2 version. Must be equal to 8
+                    return "MD2 version must be 8 but is " + modelVersion;
 
                 int texWidth = br.ReadInt32();
                 int texHeight = br.ReadInt32();
@@ -219,10 +223,8 @@ namespace ColladaDotNet.Pipeline.MD3 {
                 int num_tris = br.ReadInt32(); //Number of triangles
                 s.Position += 4; //Number of OpenGL commands
 
-                if (br.ReadInt32() == 0) { //Total number of frames
-                    General.ErrorLogger.Add(ErrorType.Warning, "MD3Reader: Error while loading '" + modelPath + "': Model has 0 frames");
-                    return false;
-                }
+                if (br.ReadInt32() == 0)  //Total number of frames
+                    return "model has 0 frames";
 
                 s.Position += 4; //Offset to skin names (each skin name is an unsigned char[64] and are null terminated)
                 int ofs_uv = br.ReadInt32();//Offset to s-t texture coordinates
@@ -284,7 +286,6 @@ namespace ColladaDotNet.Pipeline.MD3 {
                     WorldVertex v = vertList[polyIndecesList[i]];
                     
                     //bounding box
-                    //BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, v);
                     BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, new WorldVertex(v.y, v.x, v.z));
 
                     //uv
@@ -309,7 +310,7 @@ namespace ColladaDotNet.Pipeline.MD3 {
                 mesh.IndexBuffer.Unlock();
 
                 mesh.OptimizeInPlace(MeshOptimizeFlags.AttributeSort);
-                model.Meshes.Add(mesh);
+                mde.Model.Meshes.Add(mesh);
 
                 //2d data
                 IndexBuffer indeces2d = new IndexBuffer(D3DDevice, 2 * indeces2d_arr.Length, Usage.WriteOnly, Pool.Managed, true);
@@ -317,12 +318,11 @@ namespace ColladaDotNet.Pipeline.MD3 {
                 stream.WriteRange(indeces2d_arr);
                 indeces2d.Unlock();
 
-                model.Indeces2D.Add(indeces2d);
-                model.NumIndeces2D.Add((short)polyIndecesList.Count);
-                model.Angle = -90.0f * (float)Math.PI / 180.0f;
-
-                return true;
+                mde.Model.Indeces2D.Add(indeces2d);
+                mde.Model.NumIndeces2D.Add((short)polyIndecesList.Count);
+                mde.Model.Angle = -90.0f * (float)Math.PI / 180.0f;
             }
+            return "";
         }
 
         //this creates list of vertex indeces for rendering using LineList method
@@ -344,41 +344,6 @@ namespace ColladaDotNet.Pipeline.MD3 {
             return indeces2d_arr;
         }
 
-        //this creates array of vectors resembling bounding box
-        /*private static Vector3[] CalculateBoundingBox(BoundingBoxSizes bbs) {
-            //center
-            Vector3 v0 = new Vector3(bbs.MinX + (bbs.MaxX - bbs.MinX) / 2, bbs.MinY + (bbs.MaxY - bbs.MinY) / 2, bbs.MinZ + (bbs.MaxZ - bbs.MinZ) / 2);
-
-            //corners
-            Vector3 v1 = new Vector3(bbs.MinX, bbs.MinY, bbs.MinZ);
-            Vector3 v2 = new Vector3(bbs.MaxX, bbs.MinY, bbs.MinZ);
-            Vector3 v3 = new Vector3(bbs.MinX, bbs.MaxY, bbs.MinZ);
-            Vector3 v4 = new Vector3(bbs.MaxX, bbs.MaxY, bbs.MinZ);
-            Vector3 v5 = new Vector3(bbs.MinX, bbs.MinY, bbs.MaxZ);
-            Vector3 v6 = new Vector3(bbs.MaxX, bbs.MinY, bbs.MaxZ);
-            Vector3 v7 = new Vector3(bbs.MinX, bbs.MaxY, bbs.MaxZ);
-            Vector3 v8 = new Vector3(bbs.MaxX, bbs.MaxY, bbs.MaxZ);
-
-            return new Vector3[] { v0, v1, v2, v3, v4, v5, v6, v7, v8 };
-        }
-
-        private static void UpdateBoundingBoxSizes(ref BoundingBoxSizes bbs, WorldVertex v) {
-            if (v.x < bbs.MinX)
-                bbs.MinX = (short)v.x;
-            else if (v.x > bbs.MaxX)
-                bbs.MaxX = (short)v.x;
-
-            if (v.z < bbs.MinZ)
-                bbs.MinZ = (short)v.z;
-            else if (v.z > bbs.MaxZ)
-                bbs.MaxZ = (short)v.z;
-
-            if (v.y < bbs.MinY)
-                bbs.MinY = (short)v.y;
-            else if (v.y > bbs.MaxY)
-                bbs.MaxY = (short)v.y;
-        }*/
-
         private static string ReadString(BinaryReader br, int len) {
             var NAME = string.Empty;
             int i = 0;
diff --git a/Source/Core/General/MapManager.cs b/Source/Core/General/MapManager.cs
index e9b1e0c2ec61ea0579c56045498ade67c9369641..4ccf015f70e7d519776166ebb638dcb07cec2499 100644
--- a/Source/Core/General/MapManager.cs
+++ b/Source/Core/General/MapManager.cs
@@ -286,6 +286,9 @@ namespace CodeImp.DoomBuilder
 			map.UpdateConfiguration();
 			map.Update();
 			thingsfilter.Update();
+
+            //mxd. load models
+            data.LoadModels();
 			
 			// Bind any methods
 			General.Actions.BindMethods(this);
@@ -409,6 +412,9 @@ namespace CodeImp.DoomBuilder
 			map.SnapAllToAccuracy();
 			map.Update();
 			thingsfilter.Update();
+
+            //mxd. load models
+            data.LoadModels();
 			
 			// Bind any methods
 			General.Actions.BindMethods(this);
diff --git a/Source/Core/IO/WAD.cs b/Source/Core/IO/WAD.cs
index 1103c2992785019b83764f5f5f5bf2cd8c34225d..87431dbe7f61cb0ad015511859473d13c410bb33 100644
--- a/Source/Core/IO/WAD.cs
+++ b/Source/Core/IO/WAD.cs
@@ -365,7 +365,7 @@ namespace CodeImp.DoomBuilder.IO
 		// This finds a lump by name, returns -1 when not found
 		public int FindLumpIndex(string name, int start, int end)
 		{
-			byte[] fixedname;
+			//byte[] fixedname;
 			long longname = Lump.MakeLongName(name);
 			
 			// Fix end when it exceeds length
@@ -375,7 +375,7 @@ namespace CodeImp.DoomBuilder.IO
 			name = name.ToUpperInvariant();
 
 			// Make fixed name
-			fixedname = Lump.MakeFixedName(name, ENCODING);
+			//fixedname = Lump.MakeFixedName(name, ENCODING);
 
 			// Loop through the lumps
 			for(int i = start; i <= end; i++)
diff --git a/Source/Core/Rendering/Renderer2D.cs b/Source/Core/Rendering/Renderer2D.cs
index ffd4def12f5e8252b83053243ef2c6e46570282d..6ab1085b1a96839c4af51e80baf52ccac61f1999 100644
--- a/Source/Core/Rendering/Renderer2D.cs
+++ b/Source/Core/Rendering/Renderer2D.cs
@@ -37,7 +37,7 @@ using CodeImp.DoomBuilder.Editing;
 
 //mxd
 using CodeImp.DoomBuilder.GZBuilder.Data;
-using ColladaDotNet.Pipeline.MD3;
+using CodeImp.DoomBuilder.GZBuilder.MD3;
 
 #endregion
 
@@ -943,7 +943,7 @@ namespace CodeImp.DoomBuilder.Rendering
 			{
                 //mxd. Collect things with models for rendering
                 if (General.Settings.GZDrawModels && (!General.Settings.GZDrawSelectedModelsOnly || t.Selected)) {
-                    Dictionary<int, ModelDefEntry> mde = GZBuilder.GZGeneral.ModelDefEntries;
+                    Dictionary<int, ModeldefEntry> mde = General.Map.Data.ModeldefEntries;
                     if (mde != null && mde.ContainsKey(t.Type)) {
                         thingsWithModel[screenpos] = t;
                     }
@@ -1119,12 +1119,12 @@ namespace CodeImp.DoomBuilder.Rendering
 
                     graphics.Shaders.Things2D.BeginPass(1);
                     foreach(KeyValuePair<Vector2D, Thing> group in thingsWithModel){
-                        ModelDefEntry mde = GZBuilder.GZGeneral.ModelDefEntries[group.Value.Type];
+                        ModeldefEntry mde = General.Map.Data.ModeldefEntries[group.Value.Type];
 
                         if (mde.Model != null)
                             RenderModel(mde.Model, group.Key, group.Value.Angle + mde.Model.Angle, group.Value.Selected);
                         else
-                            group.Value.IsModel = GZBuilder.GZGeneral.LoadModelForThing(group.Value);
+                            group.Value.IsModel = General.Map.Data.LoadModelForThing(group.Value);
                     }
                     graphics.Shaders.Things2D.EndPass();
                 }
diff --git a/Source/Core/Rendering/Renderer3D.cs b/Source/Core/Rendering/Renderer3D.cs
index c24cc870c3035c91f7be74339f23f9ad5fe10b26..c3b147837982b0bcc36b89c59cc4ff083c120867 100644
--- a/Source/Core/Rendering/Renderer3D.cs
+++ b/Source/Core/Rendering/Renderer3D.cs
@@ -34,7 +34,7 @@ using CodeImp.DoomBuilder.VisualModes;
 using CodeImp.DoomBuilder.Map;
 
 //mxd
-using ColladaDotNet.Pipeline.MD3;
+using CodeImp.DoomBuilder.GZBuilder.MD3;
 using CodeImp.DoomBuilder.GZBuilder.Data;
 
 #endregion
@@ -81,7 +81,7 @@ namespace CodeImp.DoomBuilder.Rendering
         private List<VisualThing> thingsWithLight;
         private int[] lightOffsets;
         private Dictionary<Texture, List<VisualGeometry>> litGeometry;
-        private Dictionary<ModelDefEntry, List<VisualThing>> thingsWithModel;
+        private Dictionary<ModeldefEntry, List<VisualThing>> thingsWithModel;
         //dbg
         //int geoSkipped = 0;
         //int totalGeo = 0;
@@ -499,7 +499,7 @@ namespace CodeImp.DoomBuilder.Rendering
 			things = new Dictionary<ImageData, List<VisualThing>>[RENDER_PASSES];
 			thingsbydistance = new BinaryHeap<VisualThing>();
             //mxd 
-            thingsWithModel = new Dictionary<ModelDefEntry, List<VisualThing>>();
+            thingsWithModel = new Dictionary<ModeldefEntry, List<VisualThing>>();
             litGeometry = new Dictionary<Texture, List<VisualGeometry>>();
 
 			for(int i = 0; i < RENDER_PASSES; i++)
@@ -1003,15 +1003,14 @@ namespace CodeImp.DoomBuilder.Rendering
 
         //mxd. render models
         private void RenderModels() {
-            int shaderpass = 4;
-            if (fullbrightness) shaderpass++;
+            int shaderpass = fullbrightness ? 1 : 4;
             int currentshaderpass = shaderpass;
             int highshaderpass = shaderpass + 2;
 
             // Begin rendering with this shader
             graphics.Shaders.World3D.BeginPass(currentshaderpass);
 
-            foreach (KeyValuePair<ModelDefEntry, List<VisualThing>> group in thingsWithModel) {
+            foreach (KeyValuePair<ModeldefEntry, List<VisualThing>> group in thingsWithModel) {
                 foreach (VisualThing t in group.Value) {
                     Color4 vertexColor = new Color4(t.VertexColor);
                     vertexColor.Alpha = 1.0f;
@@ -1177,7 +1176,7 @@ namespace CodeImp.DoomBuilder.Rendering
                 }
             //mxd. gather models
             } else if (General.Settings.GZDrawModels && (!General.Settings.GZDrawSelectedModelsOnly || t.Selected) && t.Thing.IsModel) {
-                ModelDefEntry mde = GZBuilder.GZGeneral.ModelDefEntries[t.Thing.Type];
+                ModeldefEntry mde = General.Map.Data.ModeldefEntries[t.Thing.Type];
 
                 if (!isThingOnScreen(t.BoundingBox))
                     return;
diff --git a/Source/Core/VisualModes/VisualMode.cs b/Source/Core/VisualModes/VisualMode.cs
index 196f71bd12a78965e5aedaa5895b94ead1133c7e..1b072a2994d039189bfaa1e628b01150dc2f266c 100644
--- a/Source/Core/VisualModes/VisualMode.cs
+++ b/Source/Core/VisualModes/VisualMode.cs
@@ -792,7 +792,7 @@ namespace CodeImp.DoomBuilder.VisualModes
             if (refreshSelection || selectedVisualThings == null) {
                 selectedVisualThings = new List<VisualThing>();
                 foreach (KeyValuePair<Thing, VisualThing> group in allthings) {
-                    if (group.Value.Selected)
+                    if (group.Value != null && group.Value.Selected)
                         selectedVisualThings.Add(group.Value);
                 }
             }
diff --git a/Source/Core/VisualModes/VisualThing.cs b/Source/Core/VisualModes/VisualThing.cs
index 6115635f1f251846a81f3bce618fe5387cdf48ee..8b9c5efc41acc33b22a769f95642a8748617ae7c 100644
--- a/Source/Core/VisualModes/VisualThing.cs
+++ b/Source/Core/VisualModes/VisualThing.cs
@@ -93,9 +93,6 @@ namespace CodeImp.DoomBuilder.VisualModes
         private Vector3 position_v3;
         private float lightDelta; //used in light animation
         private Vector3[] boundingBox;
-
-        //mxd. model
-        private bool checkedIfModel;
 		
 		#endregion
 		
@@ -202,7 +199,7 @@ namespace CodeImp.DoomBuilder.VisualModes
 		#endregion
 		
 		#region ================== Methods
-		
+	
 		// This sets the distance from the camera
 		internal void CalculateCameraDistance(Vector2D campos)
 		{
@@ -222,7 +219,7 @@ namespace CodeImp.DoomBuilder.VisualModes
 			if(geobuffer != null) geobuffer.Dispose();
 			geobuffer = null;
 			updategeo = true;
-            checkedIfModel = false;
+            //checkedIfModel = false;
 		}
 		
 		// This is called resets when the device is reset
@@ -295,19 +292,15 @@ namespace CodeImp.DoomBuilder.VisualModes
             // Do we need to update the geometry buffer?
             if (updategeo)
 			{
-                //mxd
-                if (!checkedIfModel) {
-                    //check if thing is model
-                    if (GZBuilder.GZGeneral.ModelDefEntries.ContainsKey(thing.Type)) {
-                        ModelDefEntry mde = GZBuilder.GZGeneral.ModelDefEntries[thing.Type];
-                        if (mde.Model == null)
-                            thing.IsModel = GZBuilder.GZGeneral.LoadModelForThing(thing);
-                        else
-                            thing.IsModel = true;
-                    }
-                    if (thing.IsModel)
-                        updateBoundingBoxForModel();
-                    checkedIfModel = true;
+                //mxd. check if thing is model
+                if (General.Map.Data.ModeldefEntries.ContainsKey(thing.Type)) {
+                    ModeldefEntry mde = General.Map.Data.ModeldefEntries[thing.Type];
+                    if (mde.Model == null)
+                        thing.IsModel = General.Map.Data.LoadModelForThing(thing);
+                    else
+                        thing.IsModel = true;
+                } else {
+                    thing.IsModel = false;
                 }
 
                 // Trash geometry buffer
@@ -474,7 +467,7 @@ namespace CodeImp.DoomBuilder.VisualModes
 
         //mxd. update bounding box from model bounding box
         private void updateBoundingBoxForModel() {
-            ModelDefEntry mde = GZBuilder.GZGeneral.ModelDefEntries[thing.Type];
+            ModeldefEntry mde = General.Map.Data.ModeldefEntries[thing.Type];
             int len = mde.Model.BoundingBox.Length;
             boundingBox = new Vector3[len];
             for (int i = 0; i < len; i++) {
diff --git a/Source/Core/ZDoom/DecorateParser.cs b/Source/Core/ZDoom/DecorateParser.cs
index 10d57de79a3b43c4e3e0cf327ce62b2a556ecd8e..d5de3024767237f42f909460766a1fe984fe4a78 100644
--- a/Source/Core/ZDoom/DecorateParser.cs
+++ b/Source/Core/ZDoom/DecorateParser.cs
@@ -75,7 +75,7 @@ namespace CodeImp.DoomBuilder.ZDoom
 		public DecorateParser()
 		{
 			// Syntax
-			whitespace = "\n \t\r";
+            whitespace = "\n \t\r\u00A0"; //mxd. non-breaking space is also space :)
 			specialtokens = ":{}+-\n;,";
 			
 			// Initialize
diff --git a/Source/Core/ZDoom/TexturesParser.cs b/Source/Core/ZDoom/TexturesParser.cs
index fe28a4fb8c826950e9383d7153292909c42be271..cc42d913cc8b06fab01c01d560c83b262b4fd41f 100644
--- a/Source/Core/ZDoom/TexturesParser.cs
+++ b/Source/Core/ZDoom/TexturesParser.cs
@@ -63,7 +63,7 @@ namespace CodeImp.DoomBuilder.ZDoom
 		public TexturesParser()
 		{
 			// Syntax
-			whitespace = "\n \t\r";
+            whitespace = "\n \t\r\u00A0"; //mxd. non-breaking space is also space :)
 			specialtokens = ",{}\n";
 
 			// Initialize
diff --git a/Source/Core/ZDoom/ZDTextParser.cs b/Source/Core/ZDoom/ZDTextParser.cs
index 1f360795429f046a7bb066a3c3f89d49cec35076..14924fa3f14b665be35452db2aaa80716038b4cf 100644
--- a/Source/Core/ZDoom/ZDTextParser.cs
+++ b/Source/Core/ZDoom/ZDTextParser.cs
@@ -40,7 +40,7 @@ namespace CodeImp.DoomBuilder.ZDoom
 		#region ================== Variables
 		
 		// Parsing
-		protected string whitespace = "\n \t\r";
+        protected string whitespace = "\n \t\r\u00A0"; //mxd. non-breaking space is also space :)
 		protected string specialtokens = ":{}+-\n;";
 		
 		// Input data stream
@@ -297,7 +297,7 @@ namespace CodeImp.DoomBuilder.ZDoom
 		// This reports an error
 		protected internal void ReportError(string message)
 		{
-			long position = datastream.Position;
+			/*long position = datastream.Position;
 			long readpos = 0;
 			int linenumber = 1;
 			
@@ -313,13 +313,35 @@ namespace CodeImp.DoomBuilder.ZDoom
 			}
 			
 			// Return to original position
-			datastream.Seek(position, SeekOrigin.Begin);
+			datastream.Seek(position, SeekOrigin.Begin);*/
 			
 			// Set error information
 			errordesc = message;
-			errorline = linenumber;
+            errorline = GetCurrentLineNumber();
 			errorsource = sourcename;
 		}
+
+        //mxd 
+        protected internal int GetCurrentLineNumber() {
+            long position = datastream.Position;
+            long readpos = 0;
+            int linenumber = 1;
+
+            // Find the line on which we found this error
+            datastream.Seek(0, SeekOrigin.Begin);
+            StreamReader textreader = new StreamReader(datastream, Encoding.ASCII);
+            while (readpos < position) {
+                string line = textreader.ReadLine();
+                if (line == null) break;
+                readpos += line.Length + 2;
+                linenumber++;
+            }
+
+            // Return to original position
+            datastream.Seek(position, SeekOrigin.Begin);
+
+            return linenumber;
+        }
 		
 		#endregion
 	}
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs
index af33cf6365ea111c4457fdb6e49978ba4351ec2d..f458da09b4d159a748fb9b74ec7c89c109d94d4b 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs
@@ -34,6 +34,8 @@ using CodeImp.DoomBuilder.VisualModes;
 using CodeImp.DoomBuilder.Config;
 using CodeImp.DoomBuilder.Data;
 
+using CodeImp.DoomBuilder.GZBuilder.Data;
+
 #endregion
 
 namespace CodeImp.DoomBuilder.BuilderModes
@@ -200,6 +202,18 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					pos.z = Thing.Sector.CeilHeight - info.Height;
 				}
 			}
+
+            //mxd. check model state
+            if (General.Map.Data.ModeldefEntries.ContainsKey(Thing.Type)) {
+                ModeldefEntry mde = General.Map.Data.ModeldefEntries[Thing.Type];
+                if (mde.Model == null) {
+                    Thing.IsModel = General.Map.Data.LoadModelForThing(Thing);
+                } else {
+                    Thing.IsModel = true;
+                }
+            } else {
+                Thing.IsModel = false;
+            }
 			
 			// Apply settings
 			SetPosition(pos);
diff --git a/Source/Plugins/GZDoomEditing/GZDoomEditing.csproj b/Source/Plugins/GZDoomEditing/GZDoomEditing.csproj
index 546a50af97a1ed44144736348797c20d9f8e710c..67ac96bafe7aa46a9246830284db017c8d76c7c8 100644
--- a/Source/Plugins/GZDoomEditing/GZDoomEditing.csproj
+++ b/Source/Plugins/GZDoomEditing/GZDoomEditing.csproj
@@ -95,6 +95,7 @@
     <ProjectReference Include="..\..\Core\Builder.csproj">
       <Project>{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}</Project>
       <Name>Builder</Name>
+      <Private>False</Private>
     </ProjectReference>
   </ItemGroup>
   <ItemGroup>
diff --git a/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualThing.cs b/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualThing.cs
index 57ca33146f028b9d9e923eaf02224db0356e00e7..ccca9a4a8d337b6a60e5dd8b550acb1137fcd279 100644
--- a/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualThing.cs
+++ b/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualThing.cs
@@ -34,6 +34,8 @@ using CodeImp.DoomBuilder.VisualModes;
 using CodeImp.DoomBuilder.Config;
 using CodeImp.DoomBuilder.Data;
 
+using CodeImp.DoomBuilder.GZBuilder.Data;
+
 #endregion
 
 namespace CodeImp.DoomBuilder.GZDoomEditing
@@ -236,6 +238,18 @@ namespace CodeImp.DoomBuilder.GZDoomEditing
 					pos.z = sd.Ceiling.plane.GetZ(Thing.Position) - info.Height;
 				}
 			}
+
+            //mxd. check model state
+            if (General.Map.Data.ModeldefEntries.ContainsKey(Thing.Type)) {
+                ModeldefEntry mde = General.Map.Data.ModeldefEntries[Thing.Type];
+                if (mde.Model == null) {
+                    Thing.IsModel = General.Map.Data.LoadModelForThing(Thing);
+                } else {
+                    Thing.IsModel = true;
+                }
+            } else {
+                Thing.IsModel = false;
+            }
 			
 			// Apply settings
 			SetPosition(pos);