From 42607f704fe010723502030d08cf0bfcd6fdaad4 Mon Sep 17 00:00:00 2001 From: MaxED Date: Fri, 8 Apr 2016 14:02:08 +0000 Subject: [PATCH] Added, Classic modes: thing sprites are now angle-dependent. --- Source/Core/Config/ThingTypeInfo.cs | 180 +++++++++++++++--- Source/Core/Controls/DebugConsole.cs | 11 +- Source/Core/Data/DataManager.cs | 133 +++++++++----- Source/Core/Data/DataReader.cs | 3 + Source/Core/Data/PK3StructuredReader.cs | 44 ++++- Source/Core/Data/WADReader.cs | 24 ++- Source/Core/Rendering/Renderer2D.cs | 234 +++++++++++++----------- Source/Core/ZDoom/TexturesParser.cs | 6 +- 8 files changed, 444 insertions(+), 191 deletions(-) diff --git a/Source/Core/Config/ThingTypeInfo.cs b/Source/Core/Config/ThingTypeInfo.cs index e068768e..926bd6822 100644 --- a/Source/Core/Config/ThingTypeInfo.cs +++ b/Source/Core/Config/ThingTypeInfo.cs @@ -18,18 +18,25 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Globalization; +using CodeImp.DoomBuilder.Data; using CodeImp.DoomBuilder.GZBuilder.Data; using CodeImp.DoomBuilder.IO; -using CodeImp.DoomBuilder.Data; -using CodeImp.DoomBuilder.ZDoom; using CodeImp.DoomBuilder.Map; -using System.Drawing; +using CodeImp.DoomBuilder.ZDoom; #endregion namespace CodeImp.DoomBuilder.Config { + public struct SpriteFrameInfo //mxd + { + public string Sprite; + public long SpriteLongName; + public bool Mirror; + } + public class ThingTypeInfo : INumberedTitle, IComparable { #region ================== Constants @@ -50,9 +57,9 @@ namespace CodeImp.DoomBuilder.Config private readonly int index; private string title; private string sprite; + private SpriteFrameInfo[] spriteframe; //mxd. All rotations for given sprite. Currently contains either 1 or 8 frames private ActorStructure actor; private string classname; //mxd - private long spritelongname; private int color; private float alpha; //mxd private byte alphabyte; //mxd @@ -88,8 +95,8 @@ namespace CodeImp.DoomBuilder.Config public int Index { get { return index; } } public string Title { get { return title; } } public string Sprite { get { return sprite; } } + public SpriteFrameInfo[] SpriteFrame { get { return spriteframe; } } public ActorStructure Actor { get { return actor; } } - public long SpriteLongName { get { return spritelongname; } } public int Color { get { return color; } } public float Alpha { get { return alpha; } } //mxd public byte AlphaByte { get { return alphabyte; } } //mxd @@ -147,7 +154,7 @@ namespace CodeImp.DoomBuilder.Config this.spritescale = new SizeF(1.0f, 1.0f); this.fixedsize = false; this.fixedrotation = false; //mxd - this.spritelongname = long.MaxValue; + this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } }; //mxd this.args = new ArgumentInfo[Linedef.NUM_ARGS]; this.isknown = false; this.absolutez = false; @@ -199,12 +206,9 @@ namespace CodeImp.DoomBuilder.Config // Safety if(this.radius < 4f || this.fixedsize) this.radius = THING_FIXED_SIZE; if(this.hangs && this.absolutez) this.hangs = false; //mxd - - // Make long name for sprite lookup - if(this.sprite.Length <= 8) - this.spritelongname = Lump.MakeLongName(this.sprite); - else - this.spritelongname = long.MaxValue; + + //mxd. Create sprite frame + this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } }; // We have no destructor GC.SuppressFinalize(this); @@ -245,12 +249,9 @@ namespace CodeImp.DoomBuilder.Config // Safety if(this.radius < 4f || this.fixedsize) this.radius = THING_FIXED_SIZE; if(this.hangs && this.absolutez) this.hangs = false; //mxd - - // Make long name for sprite lookup - if(this.sprite.Length <= 8) - this.spritelongname = Lump.MakeLongName(this.sprite); - else - this.spritelongname = long.MaxValue; + + //mxd. Create sprite frame + this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } }; // We have no destructor GC.SuppressFinalize(this); @@ -292,6 +293,9 @@ namespace CodeImp.DoomBuilder.Config // Apply settings from actor ModifyByDecorateActor(actor); + + //mxd. Create sprite frame + this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } }; // We have no destructor GC.SuppressFinalize(this); @@ -313,6 +317,7 @@ namespace CodeImp.DoomBuilder.Config // Read properties this.sprite = cat.Sprite; + this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true), } }; //mxd this.color = cat.Color; this.alpha = cat.Alpha; //mxd this.alphabyte = (byte)(this.alpha * 255); //mxd @@ -334,6 +339,9 @@ namespace CodeImp.DoomBuilder.Config // Apply settings from actor ModifyByDecorateActor(actor); + //mxd. Create sprite frame + this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } }; + // We have no destructor GC.SuppressFinalize(this); } @@ -354,6 +362,8 @@ namespace CodeImp.DoomBuilder.Config // Copy properties this.sprite = other.sprite; + this.spriteframe = new SpriteFrameInfo[other.spriteframe.Length]; //mxd + other.spriteframe.CopyTo(this.spriteframe, 0); //mxd this.color = other.color; this.alpha = other.alpha; //mxd this.alphabyte = other.alphabyte; //mxd @@ -443,10 +453,8 @@ namespace CodeImp.DoomBuilder.Config else if(string.IsNullOrEmpty(sprite))//mxd sprite = DataManager.INTERNAL_PREFIX + "unknownthing"; - if(this.sprite.Length < 9) - this.spritelongname = Lump.MakeLongName(this.sprite); - else - this.spritelongname = long.MaxValue; + //mxd. Create sprite frame + this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } }; // Set sprite scale (mxd. Scale is translated to xscale and yscale in ActorStructure) if(actor.HasPropertyWithValue("xscale")) @@ -501,6 +509,134 @@ namespace CodeImp.DoomBuilder.Config if(blocking > THING_BLOCKING_NONE) errorcheck = THING_ERROR_INSIDE_STUCK; } + //mxd. This tries to find all possible sprite rotations + internal void SetupSpriteFrame() + { + // Empty or internal sprites don't have rotations + if(string.IsNullOrEmpty(sprite) || sprite.StartsWith(DataManager.INTERNAL_PREFIX)) return; + + // Skip sprites with strange names + if(sprite.Length != 6 && sprite.Length != 8) + { + General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ". Unsupported sprite name fromat: \"" + sprite + "\""); + return; + } + + // Get sprite angle + string anglestr = sprite.Substring(5, 1); + int sourceangle; + if(!int.TryParse(anglestr, NumberStyles.Integer, CultureInfo.InvariantCulture, out sourceangle)) + { + General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ". Unable to get sprite angle from sprite \"" + sprite + "\""); + return; + } + + if(sourceangle < 0 || sourceangle > 8) + { + General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ", sprite \"" + sprite + "\". Sprite angle must be in [0..8] range"); + return; + } + + // No rotations? + if(sourceangle == 0) return; + + // Gather rotations + string[] frames = new string[8]; + bool[] mirror = new bool[8]; + int processedcount = 0; + string sourcename = sprite.Substring(0, 4); + IEnumerable spritenames = General.Map.Data.GetSpriteNames(sourcename); + + // Process gathered sprites + char sourceframe = sprite[4]; + foreach(string s in spritenames) + { + //Check first frame block + char targetframe = s[4]; + if(targetframe == sourceframe) + { + // Check angle + int targetangle; + anglestr = s.Substring(5, 1); + if(!int.TryParse(anglestr, NumberStyles.Integer, CultureInfo.InvariantCulture, out targetangle)) + { + General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ". Unable to get sprite angle from sprite \"" + s + "\""); + return; + } + + if(targetangle < 0 || targetangle > 8) + { + General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ", sprite \"" + s + "\". Sprite angle must be in [0..8] range"); + return; + } + + // Add to collection + frames[targetangle - 1] = s; + processedcount++; + } + + // Check second frame block? + if(s.Length == 6) continue; + + targetframe = s[6]; + if(targetframe == sourceframe) + { + // Check angle + int targetangle; + anglestr = s.Substring(7, 1); + if(!int.TryParse(anglestr, NumberStyles.Integer, CultureInfo.InvariantCulture, out targetangle)) + { + General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ". Unable to get sprite angle from sprite \"" + s + "\""); + return; + } + + if(targetangle < 0 || targetangle > 8) + { + General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ", sprite \"" + s + "\". Sprite angle must be in [0..8] range"); + return; + } + + // Add to collections + frames[targetangle - 1] = s; + mirror[targetangle - 1] = true; + processedcount++; + } + + // Gathered all sprites? + if(processedcount == 8) break; + } + + // Check collected data + if(processedcount != 8) + { + // Check which angles are missing + List missingangles = new List(); + for(int i = 0; i < frames.Length; i++) + { + if(string.IsNullOrEmpty(frames[i])) + missingangles.Add((i + 1).ToString()); + } + + // Assemble angles to display + string ma = string.Join(", ", missingangles.ToArray()); + if(missingangles.Count > 2) + { + int pos = ma.LastIndexOf(",", StringComparison.Ordinal); + if(pos != -1) ma = ma.Remove(pos, 1).Insert(pos, " and"); + } + + General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ". Sprite rotations " + ma + " for sprite " + sourcename + ", frame " + sourceframe + " are missing"); + return; + } + + // Create collection + spriteframe = new SpriteFrameInfo[frames.Length]; + for(int i = 0; i < frames.Length; i++) + { + spriteframe[i] = new SpriteFrameInfo { Sprite = frames[i], SpriteLongName = Lump.MakeLongName(frames[i]), Mirror = mirror[i] }; + } + } + // This is used for sorting public int CompareTo(ThingTypeInfo other) { diff --git a/Source/Core/Controls/DebugConsole.cs b/Source/Core/Controls/DebugConsole.cs index 2ad24761..6ef52a65 100644 --- a/Source/Core/Controls/DebugConsole.cs +++ b/Source/Core/Controls/DebugConsole.cs @@ -41,6 +41,7 @@ namespace CodeImp.DoomBuilder private DebugMessageType filters; private static long starttime = -1; + private static long storedtime; private static int counter; private static DebugConsole me; @@ -144,11 +145,18 @@ namespace CodeImp.DoomBuilder starttime = SlimDX.Configuration.Timer.ElapsedMilliseconds; } + public static void PauseTimer() + { + if(starttime == -1) throw new InvalidOperationException("DebugConsole.StartTimer() must be called before DebugConsole.PauseTimer()!"); + + storedtime += SlimDX.Configuration.Timer.ElapsedMilliseconds - starttime; + } + public static void StopTimer(string message) { if(starttime == -1) throw new InvalidOperationException("DebugConsole.StartTimer() must be called before DebugConsole.StopTimer()!"); - long duration = SlimDX.Configuration.Timer.ElapsedMilliseconds - starttime; + long duration = SlimDX.Configuration.Timer.ElapsedMilliseconds - starttime + storedtime; if(message.Contains("%")) message = message.Replace("%", duration.ToString(CultureInfo.InvariantCulture)); @@ -158,6 +166,7 @@ namespace CodeImp.DoomBuilder WriteLine(DebugMessageType.SPECIAL, message); starttime = -1; + storedtime = 0; } public static void IncrementCounter() { IncrementCounter(1); } diff --git a/Source/Core/Data/DataManager.cs b/Source/Core/Data/DataManager.cs index e82ffcea..7bbaacc8 100644 --- a/Source/Core/Data/DataManager.cs +++ b/Source/Core/Data/DataManager.cs @@ -65,6 +65,9 @@ namespace CodeImp.DoomBuilder.Data public const string INTERNAL_PREFIX = "internal:"; public const int CLASIC_IMAGE_NAME_LENGTH = 8; //mxd private const int MAX_SKYTEXTURE_SIZE = 2048; //mxd + + private long UNKNOWN_THING; //mxd + private long MISSING_THING; //mxd #endregion @@ -121,7 +124,7 @@ namespace CodeImp.DoomBuilder.Data private ImageData hourglass3d; private ImageData crosshair; private ImageData crosshairbusy; - private Dictionary internalsprites; + private Dictionary internalspriteslookup; //mxd private ImageData whitetexture; private ImageData blacktexture; //mxd private ImageData thingtexture; //mxd @@ -323,7 +326,6 @@ namespace CodeImp.DoomBuilder.Data texturesets = new List(); usedtextures = new Dictionary(); //mxd usedflats = new Dictionary(); //mxd - internalsprites = new Dictionary(StringComparer.Ordinal); thingcategories = General.Map.Config.GetThingCategories(); thingtypes = General.Map.Config.GetThingTypes(); @@ -586,7 +588,6 @@ namespace CodeImp.DoomBuilder.Data foreach(KeyValuePair i in textures) i.Value.Dispose(); foreach(KeyValuePair i in flats) i.Value.Dispose(); foreach(KeyValuePair i in sprites) i.Value.Dispose(); - foreach(KeyValuePair i in internalsprites) i.Value.Dispose(); //mxd palette = null; //mxd. Dispose models @@ -615,7 +616,6 @@ namespace CodeImp.DoomBuilder.Data texturenames = null; flatnames = null; imageque = null; - internalsprites = null; mapinfo = null; //mxd } @@ -1499,53 +1499,60 @@ namespace CodeImp.DoomBuilder.Data // Valid sprite name? if(ti.Sprite.Length == 0 || ti.Sprite.Length > CLASIC_IMAGE_NAME_LENGTH) continue; //mxd - ImageData image = null; + //mxd. Find all sprite angles + ti.SetupSpriteFrame(); - // Sprite not in our collection yet? - if(!sprites.ContainsKey(ti.SpriteLongName)) + //mxd. Load them all + foreach(SpriteFrameInfo info in ti.SpriteFrame) { - //mxd. Go for all opened containers - bool spritefound = false; - if(!string.IsNullOrEmpty(ti.Sprite)) + ImageData image = null; + + // Sprite not in our collection yet? + if(!sprites.ContainsKey(info.SpriteLongName)) { - for(int i = containers.Count - 1; i >= 0; i--) + //mxd. Go for all opened containers + bool spritefound = false; + if(!string.IsNullOrEmpty(info.Sprite)) { - // This contain provides this sprite? - if(containers[i].GetSpriteExists(ti.Sprite)) + for(int i = containers.Count - 1; i >= 0; i--) { - spritefound = true; - break; + // This contain provides this sprite? + if(containers[i].GetSpriteExists(info.Sprite)) + { + spritefound = true; + break; + } } } - } - if(spritefound) - { - // Make new sprite image - image = new SpriteImage(ti.Sprite); + if(spritefound) + { + // Make new sprite image + image = new SpriteImage(info.Sprite); - // Add to collection - sprites.Add(ti.SpriteLongName, image); + // Add to collection + sprites.Add(info.SpriteLongName, image); + } + else + { + General.ErrorLogger.Add(ErrorType.Error, "Missing sprite lump \"" + info.Sprite + "\". Forgot to include required resources?"); + } } else { - General.ErrorLogger.Add(ErrorType.Error, "Missing sprite lump \"" + ti.Sprite + "\". Forgot to include required resources?"); + image = sprites[info.SpriteLongName]; } - } - else - { - image = sprites[ti.SpriteLongName]; - } - // Add to preview manager - if(image != null) previews.AddImage(image); + // Add to preview manager + if(image != null) previews.AddImage(image); + } } // Output info return sprites.Count; } - // This returns a specific patch stream + // This returns a specific sprite stream internal Stream GetSpriteData(string pname, ref string spritelocation) { if(!string.IsNullOrEmpty(pname)) @@ -1553,13 +1560,13 @@ namespace CodeImp.DoomBuilder.Data // Go for all opened containers for(int i = containers.Count - 1; i >= 0; i--) { - // This contain provides this patch? + // This contain provides this sprite? Stream spritedata = containers[i].GetSpriteData(pname, ref spritelocation); if(spritedata != null) return spritedata; } } - // No such patch found + // No such sprite found return null; } @@ -1574,12 +1581,12 @@ namespace CodeImp.DoomBuilder.Data // Go for all opened containers for(int i = containers.Count - 1; i >= 0; i--) { - // This contain provides this patch? + // This contain provides this sprite? if(containers[i].GetSpriteExists(pname)) return true; } } - // No such patch found + // No such sprite found return false; } @@ -1587,39 +1594,54 @@ namespace CodeImp.DoomBuilder.Data private void LoadInternalSprites() { // Add sprite icon files from directory + string name; string[] files = Directory.GetFiles(General.SpritesPath, "*.png", SearchOption.TopDirectoryOnly); + internalspriteslookup = new Dictionary(files.Length + 2); //mxd foreach(string spritefile in files) { ImageData img = new FileImage(Path.GetFileNameWithoutExtension(spritefile).ToLowerInvariant(), spritefile); img.LoadImage(); img.AllowUnload = false; - internalsprites.Add(img.Name, img); + name = INTERNAL_PREFIX + img.Name; + long hash = Lump.MakeLongName(name, true); //mxd + sprites[hash] = img; //mxd + internalspriteslookup[name] = hash; //mxd } - // Add some internal resources - if(!internalsprites.ContainsKey("nothing")) + // Add some internal resources. + // mxd. Doesn't seem to be used anywhere + /*name = INTERNAL_PREFIX + "nothing"; + if(!internalspriteslookup.ContainsKey(name)) { ImageData img = new ResourceImage("CodeImp.DoomBuilder.Resources.Nothing.png"); img.LoadImage(); img.AllowUnload = false; - internalsprites.Add("nothing", img); - } - - if(!internalsprites.ContainsKey("unknownthing")) + long hash = Lump.MakeLongName(name, true); //mxd + sprites[hash] = img; //mxd + internalspriteslookup[name] = hash; //mxd + }*/ + + name = INTERNAL_PREFIX + "unknownthing"; + UNKNOWN_THING = Lump.MakeLongName(name, true); + if(!internalspriteslookup.ContainsKey(name)) { ImageData img = new ResourceImage("CodeImp.DoomBuilder.Resources.UnknownThing.png"); img.LoadImage(); img.AllowUnload = false; - internalsprites.Add("unknownthing", img); + sprites[UNKNOWN_THING] = img; //mxd + internalspriteslookup[name] = UNKNOWN_THING; //mxd } //mxd - if(!internalsprites.ContainsKey("missingthing")) + name = INTERNAL_PREFIX + "missingthing"; + MISSING_THING = Lump.MakeLongName(name, true); + if(!internalspriteslookup.ContainsKey(name)) { ImageData img = new ResourceImage("CodeImp.DoomBuilder.Resources.MissingThing.png"); img.LoadImage(); img.AllowUnload = false; - internalsprites.Add("missingthing", img); + sprites[MISSING_THING] = img; //mxd + internalspriteslookup[name] = MISSING_THING; //mxd } } @@ -1630,11 +1652,11 @@ namespace CodeImp.DoomBuilder.Data if((name.Length > INTERNAL_PREFIX.Length) && name.ToLowerInvariant().StartsWith(INTERNAL_PREFIX)) { // Get the internal sprite - string internalname = name.Substring(INTERNAL_PREFIX.Length).ToLowerInvariant(); - if(internalsprites.ContainsKey(internalname)) - return internalsprites[internalname]; + string internalname = name.ToLowerInvariant(); + if(internalspriteslookup.ContainsKey(internalname)) //mxd + return sprites[internalspriteslookup[internalname]]; - return internalsprites["unknownthing"]; //mxd + return sprites[UNKNOWN_THING]; //mxd } else { @@ -1678,7 +1700,7 @@ namespace CodeImp.DoomBuilder.Data } else //mxd { - ImageData img = string.IsNullOrEmpty(name) ? internalsprites["unknownthing"] : internalsprites["missingthing"]; + ImageData img = string.IsNullOrEmpty(name) ? sprites[UNKNOWN_THING] : sprites[MISSING_THING]; // Add to collection sprites.Add(longname, img); @@ -1689,6 +1711,17 @@ namespace CodeImp.DoomBuilder.Data } } } + + //mxd. Returns all sprite names, which start with given string + internal IEnumerable GetSpriteNames(string startswith) + { + HashSet result = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach(DataReader reader in containers) + result.UnionWith(reader.GetSpriteNames(startswith)); + + return result; + } #endregion diff --git a/Source/Core/Data/DataReader.cs b/Source/Core/Data/DataReader.cs index 59b2469c..bf9e74de 100644 --- a/Source/Core/Data/DataReader.cs +++ b/Source/Core/Data/DataReader.cs @@ -209,6 +209,9 @@ namespace CodeImp.DoomBuilder.Data // When implemented, this checks if the given sprite lump exists public abstract bool GetSpriteExists(string pname); + + //mxd. When implemented, returns all sprites, which name starts with given string + public abstract HashSet GetSpriteNames(string startswith); #endregion diff --git a/Source/Core/Data/PK3StructuredReader.cs b/Source/Core/Data/PK3StructuredReader.cs index d2e553e5..e70381f6 100644 --- a/Source/Core/Data/PK3StructuredReader.cs +++ b/Source/Core/Data/PK3StructuredReader.cs @@ -377,14 +377,14 @@ namespace CodeImp.DoomBuilder.Data #region ================== Sprites - // This loads the textures + // This loads the sprites public override IEnumerable LoadSprites(Dictionary cachedparsers) { - Dictionary images = new Dictionary(); - List imgset = new List(); - // Error when suspended if(issuspended) throw new Exception("Data reader is suspended"); + + Dictionary images = new Dictionary(); + List imgset = new List(); // Load from wad files // Note the backward order, because the last wad's images have priority @@ -421,7 +421,32 @@ namespace CodeImp.DoomBuilder.Data return new List(images.Values); } - + + //mxd. Returns all sprites, which name starts with given string + public override HashSet GetSpriteNames(string startswith) + { + // Error when suspended + if(issuspended) throw new Exception("Data reader is suspended"); + + HashSet result = new HashSet(); + + // Load from wad files + // Note the backward order, because the last wad's images have priority + for(int i = wads.Count - 1; i >= 0; i--) + { + result.UnionWith(wads[i].GetSpriteNames(startswith)); + } + + // Load from out own files + string[] files = GetAllFilesWhichTitleStartsWith(SPRITES_DIR, startswith, true); + foreach(string file in files) + { + result.Add(Path.GetFileNameWithoutExtension(file).ToUpperInvariant()); + } + + return result; + } + #endregion #region ================== Colormaps @@ -429,12 +454,12 @@ namespace CodeImp.DoomBuilder.Data // This loads the textures public override ICollection LoadColormaps() { - Dictionary images = new Dictionary(); - ICollection collection; - // Error when suspended if(issuspended) throw new Exception("Data reader is suspended"); + Dictionary images = new Dictionary(); + ICollection collection; + // Load from wad files // Note the backward order, because the last wad's images have priority for(int i = wads.Count - 1; i >= 0; i--) @@ -448,8 +473,7 @@ namespace CodeImp.DoomBuilder.Data AddImagesToList(images, collection); // Add images to the container-specific texture set - foreach(ImageData img in images.Values) - textureset.AddFlat(img); + foreach(ImageData img in images.Values) textureset.AddFlat(img); return new List(images.Values); } diff --git a/Source/Core/Data/WADReader.cs b/Source/Core/Data/WADReader.cs index a79b52ee..06437efd 100644 --- a/Source/Core/Data/WADReader.cs +++ b/Source/Core/Data/WADReader.cs @@ -879,7 +879,29 @@ namespace CodeImp.DoomBuilder.Data return false; } - + + //mxd. Returns all sprites, which name starts with given string + public override HashSet GetSpriteNames(string startswith) + { + // Error when suspended + if(issuspended) throw new Exception("Data reader is suspended"); + + HashSet result = new HashSet(); + if(startswith.Length > 8) return result; + + startswith = startswith.ToUpperInvariant(); + foreach(LumpRange range in spriteranges) + { + for(int i = range.start; i < range.end + 1; i++) + { + if(file.Lumps[i].Name.StartsWith(startswith)) + result.Add(file.Lumps[i].Name); + } + } + + return result; + } + #endregion #region ================== Voxels (mxd) diff --git a/Source/Core/Rendering/Renderer2D.cs b/Source/Core/Rendering/Renderer2D.cs index ef753b97..c3c770a6 100644 --- a/Source/Core/Rendering/Renderer2D.cs +++ b/Source/Core/Rendering/Renderer2D.cs @@ -1083,25 +1083,28 @@ namespace CodeImp.DoomBuilder.Rendering } //mxd - private static void CreateThingSpriteVerts(Vector2D screenpos, float width, float height, ref FlatVertex[] verts, int offset, int color) + private static void CreateThingSpriteVerts(Vector2D screenpos, float width, float height, ref FlatVertex[] verts, int offset, int color, bool mirror) { + float ul = (mirror ? 1f : 0f); + float ur = (mirror ? 0f : 1f); + // Setup fixed rect for circle verts[offset].x = screenpos.x - width; verts[offset].y = screenpos.y - height; verts[offset].c = color; - verts[offset].u = 0; + verts[offset].u = ul; verts[offset].v = 0; offset++; verts[offset].x = screenpos.x + width; verts[offset].y = screenpos.y - height; verts[offset].c = color; - verts[offset].u = 1; + verts[offset].u = ur; verts[offset].v = 0; offset++; verts[offset].x = screenpos.x - width; verts[offset].y = screenpos.y + height; verts[offset].c = color; - verts[offset].u = 0; + verts[offset].u = ul; verts[offset].v = 1; offset++; verts[offset] = verts[offset - 2]; @@ -1111,7 +1114,7 @@ namespace CodeImp.DoomBuilder.Rendering verts[offset].x = screenpos.x + width; verts[offset].y = screenpos.y + height; verts[offset].c = color; - verts[offset].u = 1; + verts[offset].u = ur; verts[offset].v = 1; } @@ -1232,112 +1235,135 @@ namespace CodeImp.DoomBuilder.Rendering // Find sprite texture if(info.Sprite.Length == 0) continue; - ImageData sprite = General.Map.Data.GetSpriteImage(info.Sprite); - if(sprite == null) continue; - if(!sprite.IsImageLoaded) + // Sort by sprite angle... + Dictionary> thingsbyangle = new Dictionary>(group.Value.Count); + if(info.SpriteFrame.Length == 8) { - sprite.SetUsedInMap(true); - continue; - } - if(sprite.Texture == null) sprite.CreateTexture(); - - graphics.Shaders.Things2D.Texture1 = sprite.Texture; - graphics.Shaders.Things2D.ApplySettings(); - - // Determine next lock size - locksize = (group.Value.Count > THING_BUFFER_SIZE) ? THING_BUFFER_SIZE : group.Value.Count; - verts = new FlatVertex[THING_BUFFER_SIZE * 6]; - - // Go for all things - buffercount = 0; - totalcount = 0; - - foreach(Thing t in group.Value) - { - if(t.IsModel && ((General.Settings.GZDrawModelsMode == ModelRenderMode.SELECTION && t.Selected) || (General.Settings.GZDrawModelsMode == ModelRenderMode.ACTIVE_THINGS_FILTER && alpha == 1.0f))) continue; - - bool forcespriterendering; - float spritewidth, spriteheight, spritescale; - - // Determine sizes - if(t.FixedSize && scale > 1.0f) + foreach(Thing t in group.Value) { - spritescale = 1.0f; - forcespriterendering = true; // Always render sprite when thing size is affected by FixedSize setting - } - else if(General.Settings.FixedThingsScale && t.Size * scale > FIXED_THING_SIZE) - { - spritescale = FIXED_THING_SIZE / t.Size; - forcespriterendering = true; // Always render sprite when thing size is affected by FixedThingsScale setting - } - else - { - spritescale = scale; - forcespriterendering = false; - } + // Choose which sprite angle to show + int spriteangle = General.ClampAngle(-t.AngleDoom + 270) / 45; // Convert to [0..7] range - // Calculate scaled sprite size - if(sprite.Width > sprite.Height) - { - spritewidth = (t.Size - THING_SPRITE_SHRINK) * spritescale; - spriteheight = spritewidth * ((float)sprite.Height / sprite.Width); - } - else if(sprite.Width < sprite.Height) - { - spriteheight = (t.Size - THING_SPRITE_SHRINK) * spritescale; - spritewidth = spriteheight * ((float)sprite.Width / sprite.Height); - } - else - { - spritewidth = (t.Size - THING_SPRITE_SHRINK) * spritescale; - spriteheight = spritewidth; - } - - float spritesize = Math.Max(spritewidth, spriteheight); - - if(!forcespriterendering && spritesize < MINIMUM_SPRITE_RADIUS) - { - // Hackish way to tell arrow rendering code to draw bigger arrow... - Vector3D v = thingsByPosition[t]; - v.z = -1; - thingsByPosition[t] = v; - - // Don't render tiny little sprites - continue; - } - - CreateThingSpriteVerts(thingsByPosition[t], spritewidth, spriteheight, ref verts, buffercount * 6, (t.Selected ? selectionColor : 0xFFFFFF)); - buffercount++; - totalcount++; - - // Buffer filled? - if(buffercount == locksize) - { - // Write to buffer - stream = thingsvertices.Lock(0, locksize * 6 * FlatVertex.Stride, LockFlags.Discard); - stream.WriteRange(verts, 0, buffercount * 6); - thingsvertices.Unlock(); - stream.Dispose(); - - // Draw! - graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, buffercount * 2); - - buffercount = 0; - - // Determine next lock size - locksize = ((group.Value.Count - totalcount) > THING_BUFFER_SIZE) ? THING_BUFFER_SIZE : (group.Value.Count - totalcount); + // Add to collection + if(!thingsbyangle.ContainsKey(spriteangle)) thingsbyangle.Add(spriteangle, new List()); + thingsbyangle[spriteangle].Add(t); } } + else + { + thingsbyangle[0] = group.Value; + } - // Write to buffer - stream = thingsvertices.Lock(0, locksize * 6 * FlatVertex.Stride, LockFlags.Discard); - if(buffercount > 0) stream.WriteRange(verts, 0, buffercount * 6); - thingsvertices.Unlock(); - stream.Dispose(); + foreach(KeyValuePair> framegroup in thingsbyangle) + { + SpriteFrameInfo sfi = info.SpriteFrame[framegroup.Key]; + ImageData sprite = General.Map.Data.GetSpriteImage(sfi.Sprite); + if(sprite == null) continue; + if(!sprite.IsImageLoaded) + { + sprite.SetUsedInMap(true); + continue; + } + if(sprite.Texture == null) sprite.CreateTexture(); - // Draw what's still remaining - if(buffercount > 0) - graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, buffercount * 2); + graphics.Shaders.Things2D.Texture1 = sprite.Texture; + graphics.Shaders.Things2D.ApplySettings(); + + // Determine next lock size + locksize = (framegroup.Value.Count > THING_BUFFER_SIZE) ? THING_BUFFER_SIZE : framegroup.Value.Count; + verts = new FlatVertex[THING_BUFFER_SIZE * 6]; + + // Go for all things + buffercount = 0; + totalcount = 0; + + foreach(Thing t in framegroup.Value) + { + if(t.IsModel && ((General.Settings.GZDrawModelsMode == ModelRenderMode.SELECTION && t.Selected) || (General.Settings.GZDrawModelsMode == ModelRenderMode.ACTIVE_THINGS_FILTER && alpha == 1.0f))) + continue; + + bool forcespriterendering; + float spritewidth, spriteheight, spritescale; + + // Determine sizes + if(t.FixedSize && scale > 1.0f) + { + spritescale = 1.0f; + forcespriterendering = true; // Always render sprite when thing size is affected by FixedSize setting + } + else if(General.Settings.FixedThingsScale && t.Size * scale > FIXED_THING_SIZE) + { + spritescale = FIXED_THING_SIZE / t.Size; + forcespriterendering = true; // Always render sprite when thing size is affected by FixedThingsScale setting + } + else + { + spritescale = scale; + forcespriterendering = false; + } + + // Calculate scaled sprite size + if(sprite.Width > sprite.Height) + { + spritewidth = (t.Size - THING_SPRITE_SHRINK) * spritescale; + spriteheight = spritewidth * ((float)sprite.Height / sprite.Width); + } + else if(sprite.Width < sprite.Height) + { + spriteheight = (t.Size - THING_SPRITE_SHRINK) * spritescale; + spritewidth = spriteheight * ((float)sprite.Width / sprite.Height); + } + else + { + spritewidth = (t.Size - THING_SPRITE_SHRINK) * spritescale; + spriteheight = spritewidth; + } + + float spritesize = Math.Max(spritewidth, spriteheight); + + if(!forcespriterendering && spritesize < MINIMUM_SPRITE_RADIUS) + { + // Hackish way to tell arrow rendering code to draw bigger arrow... + Vector3D v = thingsByPosition[t]; + v.z = -1; + thingsByPosition[t] = v; + + // Don't render tiny little sprites + continue; + } + + CreateThingSpriteVerts(thingsByPosition[t], spritewidth, spriteheight, ref verts, buffercount * 6, (t.Selected ? selectionColor : 0xFFFFFF), sfi.Mirror); + buffercount++; + totalcount++; + + // Buffer filled? + if(buffercount == locksize) + { + // Write to buffer + stream = thingsvertices.Lock(0, locksize * 6 * FlatVertex.Stride, LockFlags.Discard); + stream.WriteRange(verts, 0, buffercount * 6); + thingsvertices.Unlock(); + stream.Dispose(); + + // Draw! + graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, buffercount * 2); + + buffercount = 0; + + // Determine next lock size + locksize = ((framegroup.Value.Count - totalcount) > THING_BUFFER_SIZE) ? THING_BUFFER_SIZE : (framegroup.Value.Count - totalcount); + } + } + + // Write to buffer + stream = thingsvertices.Lock(0, locksize * 6 * FlatVertex.Stride, LockFlags.Discard); + if(buffercount > 0) stream.WriteRange(verts, 0, buffercount * 6); + thingsvertices.Unlock(); + stream.Dispose(); + + // Draw what's still remaining + if(buffercount > 0) graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, buffercount * 2); + } } // Done diff --git a/Source/Core/ZDoom/TexturesParser.cs b/Source/Core/ZDoom/TexturesParser.cs index d01c295f..a8ce9a88 100644 --- a/Source/Core/ZDoom/TexturesParser.cs +++ b/Source/Core/ZDoom/TexturesParser.cs @@ -144,10 +144,10 @@ namespace CodeImp.DoomBuilder.ZDoom TextureStructure tx = new TextureStructure(this, "sprite", virtualpath); if(this.HasError) return false; - // if a limit for the sprite name length is set make sure that it's not exceeded - if(tx.Name.Length > DataManager.CLASIC_IMAGE_NAME_LENGTH) + //mxd. Sprite name length must be either 6 or 8 chars + if(tx.Name.Length != 6 && tx.Name.Length != 8) { - ReportError("Sprite name \"" + tx.Name + "\" too long. Sprite names must have a length of " + DataManager.CLASIC_IMAGE_NAME_LENGTH + " characters or less"); + ReportError("Sprite name \"" + tx.Name + "\" is incorrect. Sprite names must have a length of 6 or 8 characters"); return false; }