From 580f7d4461638afc73600185f4f1a325ddef6d2e Mon Sep 17 00:00:00 2001 From: MaxED <j.maxed@gmail.com> Date: Wed, 6 Apr 2016 11:44:38 +0000 Subject: [PATCH] Classic modes: further text label rendering optimization. MODELDEF parser: rewrote most of the parser logic. Now it picks actor model(s) based on Frame / FrameName properties. --- .../Core/GZBuilder/GZDoom/ModeldefParser.cs | 147 +-- .../GZBuilder/GZDoom/ModeldefStructure.cs | 882 +++++++++--------- Source/Core/General/Launcher.cs | 8 +- Source/Core/Rendering/Renderer2D.cs | 4 +- Source/Core/Rendering/TextLabel.cs | 58 +- Source/Core/Windows/MainForm.cs | 37 +- .../BuilderModes/ClassicModes/LinedefsMode.cs | 4 +- .../BuilderModes/ClassicModes/SectorsMode.cs | 2 +- .../BuilderModes/ClassicModes/ThingsMode.cs | 5 +- .../BuilderModes/General/LineLengthLabel.cs | 14 +- 10 files changed, 597 insertions(+), 564 deletions(-) diff --git a/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs b/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs index d8b3a21ec..e40b3fb14 100644 --- a/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs +++ b/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs @@ -1,34 +1,50 @@ -using System; +#region ================== Namespaces + +using System; using System.Collections.Generic; using CodeImp.DoomBuilder.Config; using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.ZDoom; using CodeImp.DoomBuilder.GZBuilder.Data; +using SlimDX; + +#endregion namespace CodeImp.DoomBuilder.GZBuilder.GZDoom { internal class ModeldefParser : ZDTextParser { - internal override ScriptType ScriptType { get { return ScriptType.MODELDEF; } } + #region ================== Variables private readonly Dictionary<string, int> actorsbyclass; - internal Dictionary<string, int> ActorsByClass { get { return actorsbyclass; } } - private Dictionary<string, ModelData> entries; //classname, entry + + #endregion + + #region ================== Properties + + internal override ScriptType ScriptType { get { return ScriptType.MODELDEF; } } internal Dictionary<string, ModelData> Entries { get { return entries; } } + #endregion + + #region ================== Constructor + internal ModeldefParser(Dictionary<string, int> actorsbyclass) { this.actorsbyclass = actorsbyclass; - this.entries = new Dictionary<string, ModelData>(StringComparer.Ordinal); + this.entries = new Dictionary<string, ModelData>(StringComparer.OrdinalIgnoreCase); } - //should be called after all decorate actors are parsed + #endregion + + #region ================== Parsing + + // Should be called after all decorate actors are parsed public override bool Parse(TextResourceData data, bool clearerrors) { - entries = new Dictionary<string, ModelData>(StringComparer.Ordinal); - - //mxd. Already parsed? + // Already parsed? if(!base.AddTextResource(data)) { if(clearerrors) ClearError(); @@ -42,72 +58,75 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom while(SkipWhitespace(true)) { string token = ReadToken(); - if(!string.IsNullOrEmpty(token)) - { - token = StripTokenQuotes(token).ToLowerInvariant(); - if(token == "model") //model structure start - { - // Find classname - SkipWhitespace(true); - string displayclassname = StripTokenQuotes(ReadToken(ActorStructure.ACTOR_CLASS_SPECIAL_TOKENS)); - string classname = displayclassname.ToLowerInvariant(); - - if(!string.IsNullOrEmpty(classname) && !entries.ContainsKey(classname)) - { - // Now find opening brace - if(!NextTokenIs("{")) return false; + if(string.IsNullOrEmpty(token)) continue; - ModeldefStructure mds = new ModeldefStructure(); - if(mds.Parse(this, displayclassname) && mds.ModelData != null) - { - entries.Add(classname, mds.ModelData); - } - - if(HasError) - { - LogError(); - ClearError(); - } + token = StripTokenQuotes(token).ToLowerInvariant(); + if(token != "model") continue; - // Skip untill current structure end - if(!mds.ParsingFinished) SkipStructure(1); - } - } - else + // Find classname + SkipWhitespace(true); + string classname = ReadToken(ActorStructure.ACTOR_CLASS_SPECIAL_TOKENS); + if(string.IsNullOrEmpty(classname)) + { + ReportError("Expected actor class"); + return false; + } + + // Now find opening brace + if(!NextTokenIs("{")) return false; + + // Parse the structure + ModeldefStructure mds = new ModeldefStructure(); + if(mds.Parse(this)) + { + // Fetch Actor info + if(actorsbyclass.ContainsKey(classname)) { - // Unknown structure! - if(token != "{") + ThingTypeInfo info = General.Map.Data.GetThingInfoEx(actorsbyclass[classname]); + + // Actor has a valid sprite? + if(info != null && !string.IsNullOrEmpty(info.Sprite) && !info.Sprite.ToLowerInvariant().StartsWith(DataManager.INTERNAL_PREFIX)) { - string token2; - do + string targetsprite = info.Sprite.Substring(0, 5); + if(mds.Frames.ContainsKey(targetsprite)) { - if(!SkipWhitespace(true)) break; - token2 = ReadToken(); - if(string.IsNullOrEmpty(token2)) break; - } - while(token2 != "{"); - } + // Create model data + ModelData md = new ModelData { InheritActorPitch = mds.InheritActorPitch, InheritActorRoll = mds.InheritActorRoll }; + + // Things are complicated in GZDoom... + Matrix moffset = Matrix.Translation(mds.Offset.Y, -mds.Offset.X, mds.Offset.Z); + Matrix mrotation = Matrix.RotationY(-Angle2D.DegToRad(mds.RollOffset)) * Matrix.RotationX(-Angle2D.DegToRad(mds.PitchOffset)) * Matrix.RotationZ(Angle2D.DegToRad(mds.AngleOffset)); + md.SetTransform(mrotation, moffset, mds.Scale); + + // Add models + foreach(var fs in mds.Frames[targetsprite]) + { + // Texture name will be empty when skin path is embedded in the model + string texturename = (!string.IsNullOrEmpty(mds.TextureNames[fs.ModelIndex]) ? mds.TextureNames[fs.ModelIndex].ToLowerInvariant() : string.Empty); + + md.TextureNames.Add(texturename); + md.ModelNames.Add(mds.ModelNames[fs.ModelIndex].ToLowerInvariant()); + md.FrameNames.Add(fs.FrameName); + md.FrameIndices.Add(fs.FrameIndex); + } - SkipStructure(1); + // Add to collection + entries[classname] = md; + } + } } } + + if(HasError) + { + LogError(); + ClearError(); + } } - return entries.Count > 0; + return true; } - // Skips untill current structure end - private void SkipStructure(int scopelevel) - { - do - { - if(!SkipWhitespace(true)) break; - string token = ReadToken(); - if(string.IsNullOrEmpty(token)) break; - if(token == "{") scopelevel++; - if(token == "}") scopelevel--; - } - while(scopelevel > 0); - } + #endregion } } diff --git a/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs b/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs index 388880c43..a791d212c 100644 --- a/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs +++ b/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs @@ -1,490 +1,494 @@ #region ================== Namespaces using System; +using System.Collections.Generic; using System.IO; using System.Globalization; using SlimDX; using CodeImp.DoomBuilder.GZBuilder.Data; -using CodeImp.DoomBuilder.Geometry; #endregion namespace CodeImp.DoomBuilder.GZBuilder.GZDoom { - internal sealed class ModeldefStructure + internal sealed class ModeldefStructure { + #region ================== Constants + private const int MAX_MODELS = 4; //maximum models per modeldef entry, zero-based - private bool parsingfinished; - internal ModelData ModelData; - internal bool ParsingFinished { get { return parsingfinished; } } + #endregion + + #region ================== Structs - internal bool Parse(ModeldefParser parser, string classname) + internal struct FrameStructure { + public string SpriteName; // Stays here for HashSet duplicate checks + public int ModelIndex; + public int FrameIndex; + public string FrameName; + } -#region ================== Vars - - string[] textureNames = new string[MAX_MODELS]; - string[] modelNames = new string[MAX_MODELS]; - string[] frameNames = new string[MAX_MODELS]; - int[] frameIndices = new int[MAX_MODELS]; - bool[] modelsUsed = new bool[MAX_MODELS]; - string path = ""; - Vector3 scale = new Vector3(1, 1, 1); - Vector3 offset = new Vector3(); - float angleOffset = 0; - float pitchOffset = 0; - float rollOffset = 0; - bool inheritactorpitch = false; - bool inheritactorroll = false; - - string token; - -#endregion - - //read modeldef structure contents - parsingfinished = false; - while(!parsingfinished && parser.SkipWhitespace(true)) - { - token = parser.ReadToken(); - if(!string.IsNullOrEmpty(token)) - { - token = parser.StripTokenQuotes(token).ToLowerInvariant(); //ANYTHING can be quoted... - switch(token) - { - -#region ================== Path - - case "path": - parser.SkipWhitespace(true); - path = parser.StripTokenQuotes(parser.ReadToken(false)).Replace("/", "\\"); // Don't skip newline - if(string.IsNullOrEmpty(path)) - { - parser.ReportError("Expected model path"); - return false; - } - break; - -#endregion - -#region ================== Model - - case "model": - parser.SkipWhitespace(true); - - //model index - int index; - token = parser.StripTokenQuotes(parser.ReadToken()); - if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out index)) - { - // Not numeric! - parser.ReportError("Expected model index, but got \"" + token + "\""); - return false; - } - - if(index >= MAX_MODELS) - { - parser.ReportError("GZDoom doesn't allow more than " + MAX_MODELS + " models per MODELDEF entry"); - return false; - } - - parser.SkipWhitespace(true); - - //model path - token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline - if(string.IsNullOrEmpty(token)) - { - parser.ReportError("Expected model name"); - return false; - } - - //check invalid path chars - if(!parser.CheckInvalidPathChars(token)) return false; - - //check extension - string modelext = Path.GetExtension(token); - if(string.IsNullOrEmpty(modelext)) - { - parser.ReportError("Model \"" + token + "\" won't be loaded. Models without extension are not supported by GZDoom"); - return false; - } - - if(modelext != ".md3" && modelext != ".md2") - { - parser.ReportError("Model \"" + token + "\" won't be loaded. Only MD2 and MD3 models are supported"); - return false; - } - - //GZDoom allows models with identical modelIndex, it uses the last one encountered - modelNames[index] = Path.Combine(path, token); - break; - -#endregion - -#region ================== Skin - - case "skin": - parser.SkipWhitespace(true); - - //skin index - int skinIndex; - token = parser.StripTokenQuotes(parser.ReadToken()); - if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out skinIndex)) - { - // Not numeric! - parser.ReportError("Expected skin index, but got \"" + token + "\""); - return false; - } - - if(skinIndex >= MAX_MODELS) - { - parser.ReportError("GZDoom doesn't allow more than " + MAX_MODELS + " skins per MODELDEF entry"); - return false; - } - - parser.SkipWhitespace(true); - - //skin path - token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline - if(string.IsNullOrEmpty(token)) - { - parser.ReportError("Expected skin path"); - return false; - } - - //check invalid path chars - if(!parser.CheckInvalidPathChars(token)) return false; - - //check extension - string texext = Path.GetExtension(token); - if(Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, texext) == -1) - { - parser.ReportError("Image format \"" + texext + "\" is not supported"); - return false; - } - - //GZDoom allows skins with identical modelIndex, it uses the last one encountered - textureNames[skinIndex] = Path.Combine(path, token); - break; - -#endregion - -#region ================== Scale - - case "scale": - parser.SkipWhitespace(true); - token = parser.StripTokenQuotes(parser.ReadToken()); - if(!parser.ReadSignedFloat(token, ref scale.Y)) - { - // Not numeric! - parser.ReportError("Expected Scale X value, but got \"" + token + "\""); - return false; - } - - parser.SkipWhitespace(true); - token = parser.StripTokenQuotes(parser.ReadToken()); - if(!parser.ReadSignedFloat(token, ref scale.X)) - { - // Not numeric! - parser.ReportError("Expected Scale Y value, but got \"" + token + "\""); - return false; - } - - parser.SkipWhitespace(true); - token = parser.StripTokenQuotes(parser.ReadToken()); - if(!parser.ReadSignedFloat(token, ref scale.Z)) - { - // Not numeric! - parser.ReportError("Expected Scale Z value, but got \"" + token + "\""); - return false; - } - break; - -#endregion - -#region ================== Offset - - case "offset": - parser.SkipWhitespace(true); - token = parser.StripTokenQuotes(parser.ReadToken()); - if(!parser.ReadSignedFloat(token, ref offset.X)) - { - // Not numeric! - parser.ReportError("Expected Offset X value, but got \"" + token + "\""); - return false; - } - - parser.SkipWhitespace(true); - token = parser.StripTokenQuotes(parser.ReadToken()); - if(!parser.ReadSignedFloat(token, ref offset.Y)) - { - // Not numeric! - parser.ReportError("Expected Offset Y value, but got \"" + token + "\""); - return false; - } - - parser.SkipWhitespace(true); - token = parser.StripTokenQuotes(parser.ReadToken()); - if(!parser.ReadSignedFloat(token, ref offset.Z)) - { - // Not numeric! - parser.ReportError("Expected Offset Z value, but got \"" + token + "\""); - return false; - } - break; - -#endregion - -#region ================== ZOffset - - case "zoffset": - parser.SkipWhitespace(true); - token = parser.StripTokenQuotes(parser.ReadToken()); - if(!parser.ReadSignedFloat(token, ref offset.Z)) - { - // Not numeric! - parser.ReportError("Expected ZOffset value, but got \"" + token + "\""); - return false; - } - break; - -#endregion - -#region ================== AngleOffset + #endregion - case "angleoffset": - parser.SkipWhitespace(true); - token = parser.StripTokenQuotes(parser.ReadToken()); - if(!parser.ReadSignedFloat(token, ref angleOffset)) - { - // Not numeric! - parser.ReportError("Expected AngleOffset value, but got \"" + token + "\""); - return false; - } - break; + #region ================== Variables -#endregion + private string[] texturenames; + private string[] modelnames; + private string path; + private Vector3 scale; + private Vector3 offset; + private float angleoffset; + private float pitchoffset; + private float rolloffset; + private bool inheritactorpitch; + private bool inheritactorroll; -#region ================== PitchOffset + private Dictionary<string, HashSet<FrameStructure>> frames; - case "pitchoffset": - parser.SkipWhitespace(true); - token = parser.StripTokenQuotes(parser.ReadToken()); - if(!parser.ReadSignedFloat(token, ref pitchOffset)) - { - // Not numeric! - parser.ReportError("Expected PitchOffset value, but got \"" + token + "\""); - return false; - } - break; + #endregion -#endregion + #region ================== Properties -#region ================== RollOffset + public string[] TextureNames { get { return texturenames; } } + public string[] ModelNames { get { return modelnames; } } + public Vector3 Scale { get { return scale; } } + public Vector3 Offset { get { return offset; } } + public float AngleOffset { get { return angleoffset; } } + public float PitchOffset { get { return pitchoffset; } } + public float RollOffset { get { return rolloffset; } } + public bool InheritActorPitch { get { return inheritactorpitch; } } + public bool InheritActorRoll { get { return inheritactorroll; } } - case "rolloffset": - parser.SkipWhitespace(true); - token = parser.StripTokenQuotes(parser.ReadToken()); - if(!parser.ReadSignedFloat(token, ref rollOffset)) - { - // Not numeric! - parser.ReportError("Expected RollOffset value, but got \"" + token + "\""); - return false; - } - break; + public Dictionary<string, HashSet<FrameStructure>> Frames { get { return frames; } } -#endregion - -#region ================== InheritActorPitch + #endregion - case "inheritactorpitch": - inheritactorpitch = true; - break; + #region ================== Constructor -#endregion - -#region ================== InheritActorRoll + internal ModeldefStructure() + { + texturenames = new string[MAX_MODELS]; + modelnames = new string[MAX_MODELS]; + frames = new Dictionary<string, HashSet<FrameStructure>>(StringComparer.OrdinalIgnoreCase); + scale = new Vector3(1.0f, 1.0f, 1.0f); + } - case "inheritactorroll": - inheritactorroll = true; - break; + #endregion -#endregion + #region ================== Parsing -#region ================== Frame / FrameIndex - - case "frameindex": - case "frame": - //parsed all required fields. if got more than one model - find which one(s) should be displayed - if(modelNames.GetLength(0) > 1) - { - string spriteLump = null; - string spriteFrame = null; - - //step back - parser.DataStream.Seek(-token.Length - 1, SeekOrigin.Current); - - //here we check which models are used in first encountered lump and frame - while(parser.SkipWhitespace(true)) - { - token = parser.StripTokenQuotes(parser.ReadToken()).ToLowerInvariant(); - if(token == "frameindex" || token == "frame") - { - bool frameIndex = (token == "frameindex"); - parser.SkipWhitespace(true); - - //should be sprite lump - token = parser.StripTokenQuotes(parser.ReadToken()).ToLowerInvariant(); - if(string.IsNullOrEmpty(spriteLump)) - { - spriteLump = token; - } - else if(spriteLump != token) //got another lump - { - for(int i = 0; i < modelsUsed.Length; i++) - { - if(!modelsUsed[i]) - { - modelNames[i] = null; - textureNames[i] = null; - } - } - break; - } - - parser.SkipWhitespace(true); - - //should be sprite frame - token = parser.StripTokenQuotes(parser.ReadToken()).ToLowerInvariant(); - if(string.IsNullOrEmpty(spriteFrame)) - { - spriteFrame = token; - } - else if(spriteFrame != token) //got another frame - { - for(int i = 0; i < modelsUsed.Length; i++) - { - if(!modelsUsed[i]) - { - modelNames[i] = null; - textureNames[i] = null; - } - } - break; - } - - parser.SkipWhitespace(true); - - //should be model index - token = parser.StripTokenQuotes(parser.ReadToken()); - - int modelIndex; - if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out modelIndex)) - { - // Not numeric! - parser.ReportError("Expected model index, but got \"" + token + "\""); - return false; - } - - if(modelIndex >= MAX_MODELS) - { - parser.ReportError("GZDoom doesn't allow more than " + MAX_MODELS + " models per MODELDEF entry"); - return false; - } - - if(modelNames[modelIndex] == null) - { - parser.ReportError("Model index doesn't correspond to any defined model"); - return false; - } - - modelsUsed[modelIndex] = true; - - parser.SkipWhitespace(true); - - // Should be frame name or index - token = parser.StripTokenQuotes(parser.ReadToken()); - if(frameIndex) - { - int frame = 0; - if(!parser.ReadSignedInt(token, ref frame)) - { - // Not numeric! - parser.ReportError("Expected model frame index, but got \"" + token + "\""); - return false; - } - - // Skip the model if frame index is -1 - if(frame == -1) modelsUsed[modelIndex] = false; - else frameIndices[modelIndex] = frame; - } - else - { - if(string.IsNullOrEmpty(token)) - { - // Missing! - parser.ReportError("Expected model frame name"); - return false; - } - - frameNames[modelIndex] = token.ToLowerInvariant(); - } - } - else - { - //must be "}", step back - parser.DataStream.Seek(-token.Length - 1, SeekOrigin.Current); - break; - } - } - } - - parsingfinished = true; - break; + internal bool Parse(ModeldefParser parser) + { + // Read modeldef structure contents + bool parsingfinished = false; + while(!parsingfinished && parser.SkipWhitespace(true)) + { + string token = parser.ReadToken(); + if(string.IsNullOrEmpty(token)) continue; -#endregion - } + switch(token.ToLowerInvariant()) + { + case "path": + parser.SkipWhitespace(true); + path = parser.StripTokenQuotes(parser.ReadToken(false)).Replace("/", "\\"); // Don't skip newline + if(string.IsNullOrEmpty(path)) + { + parser.ReportError("Expected model path"); + return false; + } + break; + + case "model": + parser.SkipWhitespace(true); + + // Model index + int index; + token = parser.ReadToken(); + if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out index) || index < 0) + { + // Not numeric! + parser.ReportError("Expected model index, but got \"" + token + "\""); + return false; + } + + if(index >= MAX_MODELS) + { + parser.ReportError("GZDoom doesn't allow more than " + MAX_MODELS + " models per MODELDEF entry"); + return false; + } + + parser.SkipWhitespace(true); + + // Model path + token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline + if(string.IsNullOrEmpty(token)) + { + parser.ReportError("Expected model name"); + return false; + } + + // Check invalid path chars + if(!parser.CheckInvalidPathChars(token)) return false; + + // Check extension + string modelext = Path.GetExtension(token); + if(string.IsNullOrEmpty(modelext)) + { + parser.ReportError("Model \"" + token + "\" won't be loaded. Models without extension are not supported by GZDoom"); + return false; + } + + if(modelext != ".md3" && modelext != ".md2") + { + parser.ReportError("Model \"" + token + "\" won't be loaded. Only MD2 and MD3 models are supported"); + return false; + } + + // GZDoom allows models with identical index, it uses the last one encountered + modelnames[index] = Path.Combine(path, token); + break; + + case "skin": + parser.SkipWhitespace(true); + + // Skin index + int skinindex; + token = parser.ReadToken(); + if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out skinindex) || skinindex < 0) + { + // Not numeric! + parser.ReportError("Expected skin index, but got \"" + token + "\""); + return false; + } + + if(skinindex >= MAX_MODELS) + { + parser.ReportError("GZDoom doesn't allow more than " + MAX_MODELS + " skins per MODELDEF entry"); + return false; + } + + parser.SkipWhitespace(true); + + // Skin path + token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline + if(string.IsNullOrEmpty(token)) + { + parser.ReportError("Expected skin path"); + return false; + } + + // Check invalid path chars + if(!parser.CheckInvalidPathChars(token)) return false; + + // Check extension + string texext = Path.GetExtension(token); + if(Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, texext) == -1) + { + parser.ReportError("Image format \"" + texext + "\" is not supported"); + return false; + } + + // GZDoom allows skins with identical index, it uses the last one encountered + texturenames[skinindex] = Path.Combine(path, token); + break; + + case "scale": + parser.SkipWhitespace(true); + token = parser.ReadToken(); + if(!parser.ReadSignedFloat(token, ref scale.Y)) + { + // Not numeric! + parser.ReportError("Expected Scale X value, but got \"" + token + "\""); + return false; + } + + parser.SkipWhitespace(true); + token = parser.ReadToken(); + if(!parser.ReadSignedFloat(token, ref scale.X)) + { + // Not numeric! + parser.ReportError("Expected Scale Y value, but got \"" + token + "\""); + return false; + } + + parser.SkipWhitespace(true); + token = parser.ReadToken(); + if(!parser.ReadSignedFloat(token, ref scale.Z)) + { + // Not numeric! + parser.ReportError("Expected Scale Z value, but got \"" + token + "\""); + return false; + } + break; + + case "offset": + parser.SkipWhitespace(true); + token = parser.ReadToken(); + if(!parser.ReadSignedFloat(token, ref offset.X)) + { + // Not numeric! + parser.ReportError("Expected Offset X value, but got \"" + token + "\""); + return false; + } + + parser.SkipWhitespace(true); + token = parser.ReadToken(); + if(!parser.ReadSignedFloat(token, ref offset.Y)) + { + // Not numeric! + parser.ReportError("Expected Offset Y value, but got \"" + token + "\""); + return false; + } + + parser.SkipWhitespace(true); + token = parser.ReadToken(); + if(!parser.ReadSignedFloat(token, ref offset.Z)) + { + // Not numeric! + parser.ReportError("Expected Offset Z value, but got \"" + token + "\""); + return false; + } + break; + + case "zoffset": + parser.SkipWhitespace(true); + token = parser.ReadToken(); + if(!parser.ReadSignedFloat(token, ref offset.Z)) + { + // Not numeric! + parser.ReportError("Expected ZOffset value, but got \"" + token + "\""); + return false; + } + break; + + case "angleoffset": + parser.SkipWhitespace(true); + token = parser.ReadToken(); + if(!parser.ReadSignedFloat(token, ref angleoffset)) + { + // Not numeric! + parser.ReportError("Expected AngleOffset value, but got \"" + token + "\""); + return false; + } + break; + + case "pitchoffset": + parser.SkipWhitespace(true); + token = parser.ReadToken(); + if(!parser.ReadSignedFloat(token, ref pitchoffset)) + { + // Not numeric! + parser.ReportError("Expected PitchOffset value, but got \"" + token + "\""); + return false; + } + break; + + case "rolloffset": + parser.SkipWhitespace(true); + token = parser.ReadToken(); + if(!parser.ReadSignedFloat(token, ref rolloffset)) + { + // Not numeric! + parser.ReportError("Expected RollOffset value, but got \"" + token + "\""); + return false; + } + break; + + case "inheritactorpitch": inheritactorpitch = true; break; + case "inheritactorroll": inheritactorroll = true; break; + + //FrameIndex <XXXX> <X> <model index> <frame number> + case "frameindex": + // Sprite name + parser.SkipWhitespace(true); + string fispritename = parser.ReadToken(); + if(string.IsNullOrEmpty(fispritename)) + { + parser.ReportError("Expected sprite name"); + return false; + } + if(fispritename.Length != 4) + { + parser.ReportError("Sprite name must be 4 characters long"); + return false; + } + + // Sprite frame + parser.SkipWhitespace(true); + token = parser.ReadToken(); + if(string.IsNullOrEmpty(token)) + { + parser.ReportError("Expected sprite frame"); + return false; + } + if(token.Length != 1) + { + parser.ReportError("Sprite frame must be 1 character long"); + return false; + } + + // Make full name + fispritename += token; + + // Model index + parser.SkipWhitespace(true); + int fimodelindnex; + token = parser.ReadToken(); + if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out fimodelindnex) || fimodelindnex < 0) + { + // Not numeric! + parser.ReportError("Expected model index, but got \"" + token + "\""); + return false; + } + + // Frame number + parser.SkipWhitespace(true); + int fiframeindnex; + token = parser.ReadToken(); + if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out fiframeindnex) || fiframeindnex < 0) + { + // Not numeric! + parser.ReportError("Expected frame index, but got \"" + token + "\""); + return false; + } + + // Add to collection + FrameStructure fifs = new FrameStructure { FrameIndex = fiframeindnex, ModelIndex = fimodelindnex, SpriteName = fispritename }; + if(!frames.ContainsKey(fispritename)) + { + frames.Add(fispritename, new HashSet<FrameStructure>()); + frames[fispritename].Add(fifs); + } + else if(frames[fispritename].Contains(fifs)) + { + parser.LogWarning("Duplicate FrameIndex definition"); + } + else + { + frames[fispritename].Add(fifs); + } + break; + + //Frame <XXXX> <X> <model index> <"frame name"> + case "frame": + // Sprite name + parser.SkipWhitespace(true); + string spritename = parser.ReadToken(); + if(string.IsNullOrEmpty(spritename)) + { + parser.ReportError("Expected sprite name"); + return false; + } + if(spritename.Length != 4) + { + parser.ReportError("Sprite name must be 4 characters long"); + return false; + } + + // Sprite frame + parser.SkipWhitespace(true); + token = parser.ReadToken(); + if(string.IsNullOrEmpty(token)) + { + parser.ReportError("Expected sprite frame"); + return false; + } + if(token.Length != 1) + { + parser.ReportError("Sprite frame must be 1 character long"); + return false; + } + + // Make full name + spritename += token; + + // Model index + parser.SkipWhitespace(true); + int modelindnex; + token = parser.ReadToken(); + if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out modelindnex) || modelindnex < 0) + { + // Not numeric! + parser.ReportError("Expected model index, but got \"" + token + "\""); + return false; + } + + // Frame name + parser.SkipWhitespace(true); + string framename = parser.StripTokenQuotes(parser.ReadToken()); + if(string.IsNullOrEmpty(framename)) + { + parser.ReportError("Expected frame name"); + return false; + } + + // Add to collection + FrameStructure fs = new FrameStructure { FrameName = framename, ModelIndex = modelindnex, SpriteName = spritename }; + if(!frames.ContainsKey(spritename)) + { + frames.Add(spritename, new HashSet<FrameStructure>()); + frames[spritename].Add(fs); + } + else if(frames[spritename].Contains(fs)) + { + parser.LogWarning("Duplicate Frame definition"); + } + else + { + frames[spritename].Add(fs); + } + break; + + case "{": + parser.ReportError("Unexpected scope start"); + return false; + + // Structure ends here + case "}": + parsingfinished = true; + break; } } - // Find closing brace, then quit - while(parser.SkipWhitespace(true)) + // Perform some integrity checks + if(!parsingfinished) { - token = parser.ReadToken(); - if(string.IsNullOrEmpty(token) || token == "}") break; - } - - // Bail out when got errors or no models are used - if(Array.IndexOf(modelsUsed, true) == -1) - { - parser.ReportError("No models are used by \"" + classname + "\""); + parser.ReportError("Unclosed structure scope"); return false; } - - // Classname is set in ModeldefParser - ModelData = new ModelData { InheritActorPitch = inheritactorpitch, InheritActorRoll = inheritactorroll }; - Matrix moffset = Matrix.Translation(offset.Y, -offset.X, offset.Z); // Things are complicated in GZDoom... - Matrix mrotation = Matrix.RotationY(-Angle2D.DegToRad(rollOffset)) * Matrix.RotationX(-Angle2D.DegToRad(pitchOffset)) * Matrix.RotationZ(Angle2D.DegToRad(angleOffset)); - ModelData.SetTransform(mrotation, moffset, scale); - - for(int i = 0; i < modelNames.Length; i++) + + // Any models defined? + bool valid = false; + for(int i = 0; i < modelnames.Length; i++) { - if(!string.IsNullOrEmpty(modelNames[i]) && modelsUsed[i]) + if(!string.IsNullOrEmpty(modelnames[i])) { - ModelData.TextureNames.Add(string.IsNullOrEmpty(textureNames[i]) ? string.Empty : textureNames[i].ToLowerInvariant()); - ModelData.ModelNames.Add(modelNames[i].ToLowerInvariant()); - ModelData.FrameNames.Add(frameNames[i]); - ModelData.FrameIndices.Add(frameIndices[i]); + //INFO: skin may be defined in the model itself, so we don't check it here + valid = true; + break; } } - if(ModelData.ModelNames.Count == 0) + if(!valid) { - parser.ReportError("\"" + classname + "\" has no models"); + parser.ReportError("Structure doesn't define any models"); return false; } + // Check skin-model associations + for(int i = 0; i < texturenames.Length; i++) + { + if(!string.IsNullOrEmpty(texturenames[i]) && string.IsNullOrEmpty(modelnames[i])) + { + parser.ReportError("No model is defined for skin " + i + ":\"" + texturenames[i] + "\""); + return false; + } + } + return true; } + + #endregion } } diff --git a/Source/Core/General/Launcher.cs b/Source/Core/General/Launcher.cs index 2ed4c6cd6..80bc594da 100644 --- a/Source/Core/General/Launcher.cs +++ b/Source/Core/General/Launcher.cs @@ -375,11 +375,11 @@ namespace CodeImp.DoomBuilder General.Map.Graphics.Reset(); General.MainWindow.RedrawDisplay(); } - else if(General.Editing.Mode is VisualMode) + /*else if(General.Editing.Mode is VisualMode) { - //General.MainWindow.StopExclusiveMouseInput(); - //General.MainWindow.StartExclusiveMouseInput(); - } + General.MainWindow.StopExclusiveMouseInput(); + General.MainWindow.StartExclusiveMouseInput(); + }*/ } General.MainWindow.FocusDisplay(); diff --git a/Source/Core/Rendering/Renderer2D.cs b/Source/Core/Rendering/Renderer2D.cs index 4076670c4..ef753b97e 100644 --- a/Source/Core/Rendering/Renderer2D.cs +++ b/Source/Core/Rendering/Renderer2D.cs @@ -1615,7 +1615,7 @@ namespace CodeImp.DoomBuilder.Rendering graphics.Device.SetRenderState(RenderState.FogEnable, false); graphics.Shaders.Display2D.Texture1 = label.Texture; SetWorldTransformation(false); - graphics.Shaders.Display2D.SetSettings(1f, 1f, 0f, 1f, true); + graphics.Shaders.Display2D.SetSettings(1f, 1f, 0f, 1f, false); graphics.Device.SetStreamSource(0, label.VertexBuffer, 0, FlatVertex.Stride); // Draw @@ -1648,7 +1648,7 @@ namespace CodeImp.DoomBuilder.Rendering graphics.Device.SetRenderState(RenderState.TextureFactor, -1); graphics.Device.SetRenderState(RenderState.FogEnable, false); SetWorldTransformation(false); - graphics.Shaders.Display2D.SetSettings(1f, 1f, 0f, 1f, true); + graphics.Shaders.Display2D.SetSettings(1f, 1f, 0f, 1f, false); // Begin drawing graphics.Shaders.Display2D.Begin(); diff --git a/Source/Core/Rendering/TextLabel.cs b/Source/Core/Rendering/TextLabel.cs index 26f35fcd2..7f75619c8 100644 --- a/Source/Core/Rendering/TextLabel.cs +++ b/Source/Core/Rendering/TextLabel.cs @@ -88,7 +88,7 @@ namespace CodeImp.DoomBuilder.Rendering public TextAlignmentX AlignX { get { return alignx; } set { alignx = value; updateneeded = true; } } public TextAlignmentY AlignY { get { return aligny; } set { aligny = value; updateneeded = true; } } public PixelColor Color { get { return color; } set { if(!color.Equals(value)) { color = value; textureupdateneeded = true; } } } - public PixelColor Backcolor { get { return backcolor; } set { if(!backcolor.Equals(value)) { backcolor = value; textureupdateneeded = true; } } } + public PixelColor BackColor { get { return backcolor; } set { if(!backcolor.Equals(value)) { backcolor = value; textureupdateneeded = true; } } } public bool DrawBackground { get { return drawbg; } set { if(drawbg != value) { drawbg = value; textureupdateneeded = true; } } } //mxd internal Texture Texture { get { return texture; } } //mxd internal VertexBuffer VertexBuffer { get { return textbuffer; } } @@ -109,7 +109,7 @@ namespace CodeImp.DoomBuilder.Rendering this.font = General.Settings.TextLabelFont; //mxd this.rect = new RectangleF(0f, 0f, 1f, 1f); this.color = new PixelColor(255, 255, 255, 255); - this.backcolor = new PixelColor(255, 0, 0, 0); + this.backcolor = new PixelColor(128, 0, 0, 0); this.alignx = TextAlignmentX.Center; this.aligny = TextAlignmentY.Top; this.textsize = new SizeF(); @@ -118,10 +118,6 @@ namespace CodeImp.DoomBuilder.Rendering // Register as resource General.Map.Graphics.RegisterResource(this); - - //mxd. Create the buffer - this.textbuffer = new VertexBuffer(General.Map.Graphics.Device, 4 * FlatVertex.Stride, - Usage.Dynamic | Usage.WriteOnly, VertexFormat.None, Pool.Default); // We have no destructor GC.SuppressFinalize(this); @@ -203,7 +199,7 @@ namespace CodeImp.DoomBuilder.Rendering } // Create label image - Bitmap img = CreateLabelImage(text, font, color, backcolor, drawbg); + Bitmap img = CreateLabelImage(text, font, alignx, aligny, color, backcolor, drawbg); textsize = img.Size; // Create texture @@ -234,6 +230,13 @@ namespace CodeImp.DoomBuilder.Rendering case TextAlignmentY.Bottom: beginy = absview.Y + absview.Height - textsize.Height; break; } + //mxd. Create the buffer + if(textbuffer == null || textbuffer.Disposed) + { + textbuffer = new VertexBuffer(General.Map.Graphics.Device, 4 * FlatVertex.Stride, + Usage.Dynamic | Usage.WriteOnly, VertexFormat.None, Pool.Default); + } + //mxd. Lock the buffer using(DataStream stream = textbuffer.Lock(0, 4 * FlatVertex.Stride, LockFlags.Discard | LockFlags.NoSystemLock)) { @@ -258,7 +261,7 @@ namespace CodeImp.DoomBuilder.Rendering } //mxd - private static Bitmap CreateLabelImage(string text, Font font, PixelColor color, PixelColor backcolor, bool drawbg) + private static Bitmap CreateLabelImage(string text, Font font, TextAlignmentX alignx, TextAlignmentY aligny, PixelColor color, PixelColor backcolor, bool drawbg) { PointF textorigin = new PointF(4, 3); RectangleF textrect = new RectangleF(textorigin, General.Interface.MeasureString(text, font)); @@ -266,7 +269,25 @@ namespace CodeImp.DoomBuilder.Rendering textrect.Height = (float)Math.Round(textrect.Height); RectangleF bgrect = new RectangleF(0, 0, textrect.Width + textorigin.X * 2, textrect.Height + textorigin.Y * 2); - Bitmap result = new Bitmap((int)bgrect.Width, (int)bgrect.Height); + // Make PO2 image, for speed and giggles... + RectangleF po2rect = new RectangleF(0, 0, General.NextPowerOf2((int)bgrect.Width), General.NextPowerOf2((int)bgrect.Height)); + + switch(alignx) + { + case TextAlignmentX.Center: bgrect.X = (po2rect.Width - bgrect.Width) / 2; break; + case TextAlignmentX.Right: bgrect.X = po2rect.Width - bgrect.Width; break; + } + + switch(aligny) + { + case TextAlignmentY.Middle: bgrect.Y = (po2rect.Height - bgrect.Height) / 2; break; + case TextAlignmentY.Bottom: bgrect.Y = po2rect.Height - bgrect.Height; break; + } + + textrect.X += bgrect.X; + textrect.Y += bgrect.Y; + + Bitmap result = new Bitmap((int)po2rect.Width, (int)po2rect.Height); using(Graphics g = Graphics.FromImage(result)) { g.SmoothingMode = SmoothingMode.HighQuality; @@ -276,7 +297,7 @@ namespace CodeImp.DoomBuilder.Rendering // Draw text using(StringFormat sf = new StringFormat()) { - sf.FormatFlags = StringFormatFlags.NoWrap; + sf.FormatFlags = StringFormatFlags.FitBlackBox | StringFormatFlags.NoWrap; sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center; @@ -319,21 +340,20 @@ namespace CodeImp.DoomBuilder.Rendering using(SolidBrush brush = new SolidBrush(backcolor.ToColor())) g.DrawString(text, font, brush, textrect, sf); } - // Draw text with outline + // Draw plain text else { - RectangleF pathrect = textrect; - pathrect.Inflate(1, 3); + RectangleF plainbgrect = textrect; + if(text.Length > 1) plainbgrect.Inflate(6, 2); - GraphicsPath p = new GraphicsPath(); - p.AddString(text, font.FontFamily, (int)font.Style, g.DpiY * font.Size / 72f, pathrect, sf); + RectangleF plaintextrect = textrect; + plaintextrect.Inflate(6, 4); - // Draw'n'fill text - using(Pen pen = new Pen(backcolor.ToColor(), 3)) - g.DrawPath(pen, p); + using(SolidBrush brush = new SolidBrush(backcolor.ToColor())) + g.FillRectangle(brush, plainbgrect); using(SolidBrush brush = new SolidBrush(color.ToColor())) - g.FillPath(brush, p); + g.DrawString(text, font, brush, plaintextrect, sf); } } } diff --git a/Source/Core/Windows/MainForm.cs b/Source/Core/Windows/MainForm.cs index 519617db8..2cb68c50d 100644 --- a/Source/Core/Windows/MainForm.cs +++ b/Source/Core/Windows/MainForm.cs @@ -204,16 +204,19 @@ namespace CodeImp.DoomBuilder.Windows // Constructor internal MainForm() { + // Fetch pointer + windowptr = base.Handle; + + //mxd. Graphics + graphics = Graphics.FromHwndInternal(windowptr); + //mxd. Set DPI-aware icon size - using(Graphics g = this.CreateGraphics()) - { - DPIScaler = new SizeF(g.DpiX / 96, g.DpiY / 96); + DPIScaler = new SizeF(graphics.DpiX / 96, graphics.DpiY / 96); - if(DPIScaler.Width != 1.0f || DPIScaler.Height != 1.0f) - { - ScaledIconSize.Width = (int)Math.Round(ScaledIconSize.Width * DPIScaler.Width); - ScaledIconSize.Height = (int)Math.Round(ScaledIconSize.Height * DPIScaler.Height); - } + if(DPIScaler.Width != 1.0f || DPIScaler.Height != 1.0f) + { + ScaledIconSize.Width = (int)Math.Round(ScaledIconSize.Width * DPIScaler.Width); + ScaledIconSize.Height = (int)Math.Round(ScaledIconSize.Height * DPIScaler.Height); } // Setup controls @@ -234,9 +237,6 @@ namespace CodeImp.DoomBuilder.Windows labelcollapsedinfo.Text = ""; display.Dock = DockStyle.Fill; - // Fetch pointer - windowptr = base.Handle; - // Make array for view modes viewmodesbuttons = new ToolStripButton[Renderer2D.NUM_VIEW_MODES]; viewmodesbuttons[(int)ViewMode.Normal] = buttonviewnormal; @@ -290,9 +290,6 @@ namespace CodeImp.DoomBuilder.Windows //mxd. Hints hintsPanel = new HintsPanel(); hintsDocker = new Docker("hints", "Help", hintsPanel); - - //mxd. Graphics - graphics = Graphics.FromHwndInternal(windowptr); } #endregion @@ -2679,14 +2676,14 @@ namespace CodeImp.DoomBuilder.Windows private string GetDisplayFilename(string filename) { // String doesnt fit? - if(GetStringWidth(filename) > MAX_RECENT_FILES_PIXELS) + if(MeasureString(filename, this.Font).Width > MAX_RECENT_FILES_PIXELS) { // Start chopping off characters for(int i = filename.Length - 6; i >= 0; i--) { // Does it fit now? string newname = filename.Substring(0, 3) + "..." + filename.Substring(filename.Length - i, i); - if(GetStringWidth(newname) <= MAX_RECENT_FILES_PIXELS) return newname; + if(MeasureString(newname, this.Font).Width <= MAX_RECENT_FILES_PIXELS) return newname; } // Cant find anything that fits (most unlikely!) @@ -2699,14 +2696,6 @@ namespace CodeImp.DoomBuilder.Windows } } - // This returns the width of a string - private float GetStringWidth(string str) - { - Graphics g = Graphics.FromHwndInternal(this.Handle); - SizeF strsize = g.MeasureString(str, this.Font); - return strsize.Width; - } - // Exit clicked private void itemexit_Click(object sender, EventArgs e) { this.Close(); } diff --git a/Source/Plugins/BuilderModes/ClassicModes/LinedefsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/LinedefsMode.cs index 8a653f9e6..ec077bf02 100644 --- a/Source/Plugins/BuilderModes/ClassicModes/LinedefsMode.cs +++ b/Source/Plugins/BuilderModes/ClassicModes/LinedefsMode.cs @@ -395,7 +395,7 @@ namespace CodeImp.DoomBuilder.BuilderModes l.AlignX = TextAlignmentX.Center; l.AlignY = TextAlignmentY.Middle; l.Color = General.Colors.InfoLine; - l.Backcolor = General.Colors.Background.WithAlpha(255); + l.BackColor = General.Colors.Background.WithAlpha(128); larr[i] = l; } @@ -430,7 +430,7 @@ namespace CodeImp.DoomBuilder.BuilderModes l.AlignX = TextAlignmentX.Center; l.AlignY = TextAlignmentY.Middle; l.Color = (linedef == highlighted ? General.Colors.Selection : General.Colors.Highlight); - l.Backcolor = General.Colors.Background.WithAlpha(255); + l.BackColor = General.Colors.Background.WithAlpha(192); l.Text = (++index).ToString(); labels.Add(linedef, l); } diff --git a/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs index 6ddd12777..fe826fcae 100644 --- a/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs +++ b/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs @@ -160,7 +160,7 @@ namespace CodeImp.DoomBuilder.BuilderModes labelarray[i].AlignX = TextAlignmentX.Center; labelarray[i].AlignY = TextAlignmentY.Middle; labelarray[i].Color = c; - labelarray[i].Backcolor = General.Colors.Background.WithAlpha(255); + labelarray[i].BackColor = General.Colors.Background.WithAlpha(128); } labels.Add(s, labelarray); } diff --git a/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs index 292525314..ee9614437 100644 --- a/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs +++ b/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs @@ -936,7 +936,7 @@ namespace CodeImp.DoomBuilder.BuilderModes l.AlignX = TextAlignmentX.Center; l.AlignY = TextAlignmentY.Middle; l.Color = General.Colors.InfoLine; - l.Backcolor = General.Colors.Background.WithAlpha(255); + l.BackColor = General.Colors.Background.WithAlpha(128); larr[i] = l; } @@ -982,8 +982,7 @@ namespace CodeImp.DoomBuilder.BuilderModes } l.Color = (thing == highlighted ? General.Colors.Selection : General.Colors.Highlight); - l.Backcolor = General.Colors.Background.WithAlpha(255); - l.DrawBackground = true; + l.BackColor = General.Colors.Background.WithAlpha(192); l.Text = (++index).ToString(); labels.Add(thing, l); } diff --git a/Source/Plugins/BuilderModes/General/LineLengthLabel.cs b/Source/Plugins/BuilderModes/General/LineLengthLabel.cs index 575e99c2d..0ae3dd16d 100644 --- a/Source/Plugins/BuilderModes/General/LineLengthLabel.cs +++ b/Source/Plugins/BuilderModes/General/LineLengthLabel.cs @@ -103,12 +103,14 @@ namespace CodeImp.DoomBuilder.BuilderModes // Initialization private void Initialize() { - label = new TextLabel(); - label.AlignX = TextAlignmentX.Center; - label.AlignY = TextAlignmentY.Middle; - label.Color = General.Colors.Highlight; - label.Backcolor = General.Colors.Background; - label.TransformCoords = true; + label = new TextLabel + { + AlignX = TextAlignmentX.Center, + AlignY = TextAlignmentY.Middle, + Color = General.Colors.Highlight, + BackColor = General.Colors.Background.WithAlpha(64), + TransformCoords = true, + }; } // Disposer -- GitLab