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.
This commit is contained in:
MaxED 2016-04-06 11:44:38 +00:00
parent ee12da96a1
commit 580f7d4461
10 changed files with 605 additions and 572 deletions

View file

@ -1,34 +1,50 @@
using System; #region ================== Namespaces
using System;
using System.Collections.Generic; using System.Collections.Generic;
using CodeImp.DoomBuilder.Config; using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Data; using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.ZDoom; using CodeImp.DoomBuilder.ZDoom;
using CodeImp.DoomBuilder.GZBuilder.Data; using CodeImp.DoomBuilder.GZBuilder.Data;
using SlimDX;
#endregion
namespace CodeImp.DoomBuilder.GZBuilder.GZDoom namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
{ {
internal class ModeldefParser : ZDTextParser internal class ModeldefParser : ZDTextParser
{ {
internal override ScriptType ScriptType { get { return ScriptType.MODELDEF; } } #region ================== Variables
private readonly Dictionary<string, int> actorsbyclass; private readonly Dictionary<string, int> actorsbyclass;
internal Dictionary<string, int> ActorsByClass { get { return actorsbyclass; } }
private Dictionary<string, ModelData> entries; //classname, entry 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; } } internal Dictionary<string, ModelData> Entries { get { return entries; } }
#endregion
#region ================== Constructor
internal ModeldefParser(Dictionary<string, int> actorsbyclass) internal ModeldefParser(Dictionary<string, int> actorsbyclass)
{ {
this.actorsbyclass = 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) public override bool Parse(TextResourceData data, bool clearerrors)
{ {
entries = new Dictionary<string, ModelData>(StringComparer.Ordinal); // Already parsed?
//mxd. Already parsed?
if(!base.AddTextResource(data)) if(!base.AddTextResource(data))
{ {
if(clearerrors) ClearError(); if(clearerrors) ClearError();
@ -42,25 +58,63 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
while(SkipWhitespace(true)) while(SkipWhitespace(true))
{ {
string token = ReadToken(); string token = ReadToken();
if(!string.IsNullOrEmpty(token)) if(string.IsNullOrEmpty(token)) continue;
{
token = StripTokenQuotes(token).ToLowerInvariant(); token = StripTokenQuotes(token).ToLowerInvariant();
if(token == "model") //model structure start if(token != "model") continue;
{
// Find classname // Find classname
SkipWhitespace(true); SkipWhitespace(true);
string displayclassname = StripTokenQuotes(ReadToken(ActorStructure.ACTOR_CLASS_SPECIAL_TOKENS)); string classname = ReadToken(ActorStructure.ACTOR_CLASS_SPECIAL_TOKENS);
string classname = displayclassname.ToLowerInvariant(); if(string.IsNullOrEmpty(classname))
if(!string.IsNullOrEmpty(classname) && !entries.ContainsKey(classname))
{ {
ReportError("Expected actor class");
return false;
}
// Now find opening brace // Now find opening brace
if(!NextTokenIs("{")) return false; if(!NextTokenIs("{")) return false;
// Parse the structure
ModeldefStructure mds = new ModeldefStructure(); ModeldefStructure mds = new ModeldefStructure();
if(mds.Parse(this, displayclassname) && mds.ModelData != null) if(mds.Parse(this))
{ {
entries.Add(classname, mds.ModelData); // Fetch Actor info
if(actorsbyclass.ContainsKey(classname))
{
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 targetsprite = info.Sprite.Substring(0, 5);
if(mds.Frames.ContainsKey(targetsprite))
{
// 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);
}
// Add to collection
entries[classname] = md;
}
}
}
} }
if(HasError) if(HasError)
@ -68,46 +122,11 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
LogError(); LogError();
ClearError(); ClearError();
} }
// Skip untill current structure end
if(!mds.ParsingFinished) SkipStructure(1);
}
}
else
{
// Unknown structure!
if(token != "{")
{
string token2;
do
{
if(!SkipWhitespace(true)) break;
token2 = ReadToken();
if(string.IsNullOrEmpty(token2)) break;
}
while(token2 != "{");
} }
SkipStructure(1); return true;
}
}
} }
return entries.Count > 0; #endregion
}
// 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);
}
} }
} }

View file

@ -1,11 +1,11 @@
#region ================== Namespaces #region ================== Namespaces
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Globalization; using System.Globalization;
using SlimDX; using SlimDX;
using CodeImp.DoomBuilder.GZBuilder.Data; using CodeImp.DoomBuilder.GZBuilder.Data;
using CodeImp.DoomBuilder.Geometry;
#endregion #endregion
@ -13,48 +13,82 @@ 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 const int MAX_MODELS = 4; //maximum models per modeldef entry, zero-based
private bool parsingfinished;
internal ModelData ModelData;
internal bool ParsingFinished { get { return parsingfinished; } }
internal bool Parse(ModeldefParser parser, string classname)
{
#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 #endregion
//read modeldef structure contents #region ================== Structs
parsingfinished = false;
internal struct FrameStructure
{
public string SpriteName; // Stays here for HashSet duplicate checks
public int ModelIndex;
public int FrameIndex;
public string FrameName;
}
#endregion
#region ================== Variables
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;
private Dictionary<string, HashSet<FrameStructure>> frames;
#endregion
#region ================== Properties
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; } }
public Dictionary<string, HashSet<FrameStructure>> Frames { get { return frames; } }
#endregion
#region ================== Constructor
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);
}
#endregion
#region ================== Parsing
internal bool Parse(ModeldefParser parser)
{
// Read modeldef structure contents
bool parsingfinished = false;
while(!parsingfinished && parser.SkipWhitespace(true)) while(!parsingfinished && parser.SkipWhitespace(true))
{ {
token = parser.ReadToken(); string token = parser.ReadToken();
if(!string.IsNullOrEmpty(token)) if(string.IsNullOrEmpty(token)) continue;
{
token = parser.StripTokenQuotes(token).ToLowerInvariant(); //ANYTHING can be quoted...
switch(token)
{
#region ================== Path
switch(token.ToLowerInvariant())
{
case "path": case "path":
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
path = parser.StripTokenQuotes(parser.ReadToken(false)).Replace("/", "\\"); // Don't skip newline path = parser.StripTokenQuotes(parser.ReadToken(false)).Replace("/", "\\"); // Don't skip newline
@ -65,17 +99,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
} }
break; break;
#endregion
#region ================== Model
case "model": case "model":
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
//model index // Model index
int index; int index;
token = parser.StripTokenQuotes(parser.ReadToken()); token = parser.ReadToken();
if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out index)) if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out index) || index < 0)
{ {
// Not numeric! // Not numeric!
parser.ReportError("Expected model index, but got \"" + token + "\""); parser.ReportError("Expected model index, but got \"" + token + "\"");
@ -90,7 +120,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
//model path // Model path
token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline
if(string.IsNullOrEmpty(token)) if(string.IsNullOrEmpty(token))
{ {
@ -98,10 +128,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
return false; return false;
} }
//check invalid path chars // Check invalid path chars
if(!parser.CheckInvalidPathChars(token)) return false; if(!parser.CheckInvalidPathChars(token)) return false;
//check extension // Check extension
string modelext = Path.GetExtension(token); string modelext = Path.GetExtension(token);
if(string.IsNullOrEmpty(modelext)) if(string.IsNullOrEmpty(modelext))
{ {
@ -115,28 +145,24 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
return false; return false;
} }
//GZDoom allows models with identical modelIndex, it uses the last one encountered // GZDoom allows models with identical index, it uses the last one encountered
modelNames[index] = Path.Combine(path, token); modelnames[index] = Path.Combine(path, token);
break; break;
#endregion
#region ================== Skin
case "skin": case "skin":
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
//skin index // Skin index
int skinIndex; int skinindex;
token = parser.StripTokenQuotes(parser.ReadToken()); token = parser.ReadToken();
if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out skinIndex)) if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out skinindex) || skinindex < 0)
{ {
// Not numeric! // Not numeric!
parser.ReportError("Expected skin index, but got \"" + token + "\""); parser.ReportError("Expected skin index, but got \"" + token + "\"");
return false; return false;
} }
if(skinIndex >= MAX_MODELS) if(skinindex >= MAX_MODELS)
{ {
parser.ReportError("GZDoom doesn't allow more than " + MAX_MODELS + " skins per MODELDEF entry"); parser.ReportError("GZDoom doesn't allow more than " + MAX_MODELS + " skins per MODELDEF entry");
return false; return false;
@ -144,7 +170,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
//skin path // Skin path
token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline
if(string.IsNullOrEmpty(token)) if(string.IsNullOrEmpty(token))
{ {
@ -152,10 +178,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
return false; return false;
} }
//check invalid path chars // Check invalid path chars
if(!parser.CheckInvalidPathChars(token)) return false; if(!parser.CheckInvalidPathChars(token)) return false;
//check extension // Check extension
string texext = Path.GetExtension(token); string texext = Path.GetExtension(token);
if(Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, texext) == -1) if(Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, texext) == -1)
{ {
@ -163,17 +189,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
return false; return false;
} }
//GZDoom allows skins with identical modelIndex, it uses the last one encountered // GZDoom allows skins with identical index, it uses the last one encountered
textureNames[skinIndex] = Path.Combine(path, token); texturenames[skinindex] = Path.Combine(path, token);
break; break;
#endregion
#region ================== Scale
case "scale": case "scale":
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken()); token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref scale.Y)) if(!parser.ReadSignedFloat(token, ref scale.Y))
{ {
// Not numeric! // Not numeric!
@ -182,7 +204,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
} }
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken()); token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref scale.X)) if(!parser.ReadSignedFloat(token, ref scale.X))
{ {
// Not numeric! // Not numeric!
@ -191,7 +213,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
} }
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken()); token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref scale.Z)) if(!parser.ReadSignedFloat(token, ref scale.Z))
{ {
// Not numeric! // Not numeric!
@ -200,13 +222,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
} }
break; break;
#endregion
#region ================== Offset
case "offset": case "offset":
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken()); token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref offset.X)) if(!parser.ReadSignedFloat(token, ref offset.X))
{ {
// Not numeric! // Not numeric!
@ -215,7 +233,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
} }
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken()); token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref offset.Y)) if(!parser.ReadSignedFloat(token, ref offset.Y))
{ {
// Not numeric! // Not numeric!
@ -224,7 +242,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
} }
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken()); token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref offset.Z)) if(!parser.ReadSignedFloat(token, ref offset.Z))
{ {
// Not numeric! // Not numeric!
@ -233,13 +251,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
} }
break; break;
#endregion
#region ================== ZOffset
case "zoffset": case "zoffset":
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken()); token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref offset.Z)) if(!parser.ReadSignedFloat(token, ref offset.Z))
{ {
// Not numeric! // Not numeric!
@ -248,14 +262,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
} }
break; break;
#endregion
#region ================== AngleOffset
case "angleoffset": case "angleoffset":
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken()); token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref angleOffset)) if(!parser.ReadSignedFloat(token, ref angleoffset))
{ {
// Not numeric! // Not numeric!
parser.ReportError("Expected AngleOffset value, but got \"" + token + "\""); parser.ReportError("Expected AngleOffset value, but got \"" + token + "\"");
@ -263,14 +273,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
} }
break; break;
#endregion
#region ================== PitchOffset
case "pitchoffset": case "pitchoffset":
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken()); token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref pitchOffset)) if(!parser.ReadSignedFloat(token, ref pitchoffset))
{ {
// Not numeric! // Not numeric!
parser.ReportError("Expected PitchOffset value, but got \"" + token + "\""); parser.ReportError("Expected PitchOffset value, but got \"" + token + "\"");
@ -278,14 +284,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
} }
break; break;
#endregion
#region ================== RollOffset
case "rolloffset": case "rolloffset":
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken()); token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref rollOffset)) if(!parser.ReadSignedFloat(token, ref rolloffset))
{ {
// Not numeric! // Not numeric!
parser.ReportError("Expected RollOffset value, but got \"" + token + "\""); parser.ReportError("Expected RollOffset value, but got \"" + token + "\"");
@ -293,198 +295,200 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
} }
break; break;
#endregion case "inheritactorpitch": inheritactorpitch = true; break;
case "inheritactorroll": inheritactorroll = true; break;
#region ================== InheritActorPitch
case "inheritactorpitch":
inheritactorpitch = true;
break;
#endregion
#region ================== InheritActorRoll
case "inheritactorroll":
inheritactorroll = true;
break;
#endregion
#region ================== Frame / FrameIndex
//FrameIndex <XXXX> <X> <model index> <frame number>
case "frameindex": case "frameindex":
case "frame": // Sprite name
//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); parser.SkipWhitespace(true);
string fispritename = parser.ReadToken();
//should be sprite lump if(string.IsNullOrEmpty(fispritename))
token = parser.StripTokenQuotes(parser.ReadToken()).ToLowerInvariant();
if(string.IsNullOrEmpty(spriteLump))
{ {
spriteLump = token; parser.ReportError("Expected sprite name");
return false;
} }
else if(spriteLump != token) //got another lump if(fispritename.Length != 4)
{ {
for(int i = 0; i < modelsUsed.Length; i++) parser.ReportError("Sprite name must be 4 characters long");
{ return false;
if(!modelsUsed[i])
{
modelNames[i] = null;
textureNames[i] = null;
}
}
break;
} }
// Sprite frame
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
token = parser.ReadToken();
//should be sprite frame if(string.IsNullOrEmpty(token))
token = parser.StripTokenQuotes(parser.ReadToken()).ToLowerInvariant();
if(string.IsNullOrEmpty(spriteFrame))
{ {
spriteFrame = token; parser.ReportError("Expected sprite frame");
return false;
} }
else if(spriteFrame != token) //got another frame if(token.Length != 1)
{ {
for(int i = 0; i < modelsUsed.Length; i++) parser.ReportError("Sprite frame must be 1 character long");
{ return false;
if(!modelsUsed[i])
{
modelNames[i] = null;
textureNames[i] = null;
}
}
break;
} }
// Make full name
fispritename += token;
// Model index
parser.SkipWhitespace(true); parser.SkipWhitespace(true);
int fimodelindnex;
//should be model index token = parser.ReadToken();
token = parser.StripTokenQuotes(parser.ReadToken()); if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out fimodelindnex) || fimodelindnex < 0)
int modelIndex;
if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out modelIndex))
{ {
// Not numeric! // Not numeric!
parser.ReportError("Expected model index, but got \"" + token + "\""); parser.ReportError("Expected model index, but got \"" + token + "\"");
return false; return false;
} }
if(modelIndex >= MAX_MODELS) // Frame number
{
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); parser.SkipWhitespace(true);
int fiframeindnex;
// Should be frame name or index token = parser.ReadToken();
token = parser.StripTokenQuotes(parser.ReadToken()); if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out fiframeindnex) || fiframeindnex < 0)
if(frameIndex)
{
int frame = 0;
if(!parser.ReadSignedInt(token, ref frame))
{ {
// Not numeric! // Not numeric!
parser.ReportError("Expected model frame index, but got \"" + token + "\""); parser.ReportError("Expected frame index, but got \"" + token + "\"");
return false; return false;
} }
// Skip the model if frame index is -1 // Add to collection
if(frame == -1) modelsUsed[modelIndex] = false; FrameStructure fifs = new FrameStructure { FrameIndex = fiframeindnex, ModelIndex = fimodelindnex, SpriteName = fispritename };
else frameIndices[modelIndex] = frame; 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 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)) if(string.IsNullOrEmpty(token))
{ {
// Missing! parser.ReportError("Expected sprite frame");
parser.ReportError("Expected model frame name"); return false;
}
if(token.Length != 1)
{
parser.ReportError("Sprite frame must be 1 character long");
return false; return false;
} }
frameNames[modelIndex] = token.ToLowerInvariant(); // 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 else
{ {
//must be "}", step back frames[spritename].Add(fs);
parser.DataStream.Seek(-token.Length - 1, SeekOrigin.Current); }
break; break;
}
}
}
case "{":
parser.ReportError("Unexpected scope start");
return false;
// Structure ends here
case "}":
parsingfinished = true; parsingfinished = true;
break; break;
#endregion
}
} }
} }
// Find closing brace, then quit // Perform some integrity checks
while(parser.SkipWhitespace(true)) if(!parsingfinished)
{ {
token = parser.ReadToken(); parser.ReportError("Unclosed structure scope");
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 + "\"");
return false; return false;
} }
// Classname is set in ModeldefParser // Any models defined?
ModelData = new ModelData { InheritActorPitch = inheritactorpitch, InheritActorRoll = inheritactorroll }; bool valid = false;
Matrix moffset = Matrix.Translation(offset.Y, -offset.X, offset.Z); // Things are complicated in GZDoom... for(int i = 0; i < modelnames.Length; i++)
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++)
{ {
if(!string.IsNullOrEmpty(modelNames[i]) && modelsUsed[i]) if(!string.IsNullOrEmpty(modelnames[i]))
{ {
ModelData.TextureNames.Add(string.IsNullOrEmpty(textureNames[i]) ? string.Empty : textureNames[i].ToLowerInvariant()); //INFO: skin may be defined in the model itself, so we don't check it here
ModelData.ModelNames.Add(modelNames[i].ToLowerInvariant()); valid = true;
ModelData.FrameNames.Add(frameNames[i]); break;
ModelData.FrameIndices.Add(frameIndices[i]);
} }
} }
if(ModelData.ModelNames.Count == 0) if(!valid)
{ {
parser.ReportError("\"" + classname + "\" has no models"); parser.ReportError("Structure doesn't define any models");
return false; 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; return true;
} }
#endregion
} }
} }

View file

@ -375,11 +375,11 @@ namespace CodeImp.DoomBuilder
General.Map.Graphics.Reset(); General.Map.Graphics.Reset();
General.MainWindow.RedrawDisplay(); General.MainWindow.RedrawDisplay();
} }
else if(General.Editing.Mode is VisualMode) /*else if(General.Editing.Mode is VisualMode)
{ {
//General.MainWindow.StopExclusiveMouseInput(); General.MainWindow.StopExclusiveMouseInput();
//General.MainWindow.StartExclusiveMouseInput(); General.MainWindow.StartExclusiveMouseInput();
} }*/
} }
General.MainWindow.FocusDisplay(); General.MainWindow.FocusDisplay();

View file

@ -1615,7 +1615,7 @@ namespace CodeImp.DoomBuilder.Rendering
graphics.Device.SetRenderState(RenderState.FogEnable, false); graphics.Device.SetRenderState(RenderState.FogEnable, false);
graphics.Shaders.Display2D.Texture1 = label.Texture; graphics.Shaders.Display2D.Texture1 = label.Texture;
SetWorldTransformation(false); 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); graphics.Device.SetStreamSource(0, label.VertexBuffer, 0, FlatVertex.Stride);
// Draw // Draw
@ -1648,7 +1648,7 @@ namespace CodeImp.DoomBuilder.Rendering
graphics.Device.SetRenderState(RenderState.TextureFactor, -1); graphics.Device.SetRenderState(RenderState.TextureFactor, -1);
graphics.Device.SetRenderState(RenderState.FogEnable, false); graphics.Device.SetRenderState(RenderState.FogEnable, false);
SetWorldTransformation(false); SetWorldTransformation(false);
graphics.Shaders.Display2D.SetSettings(1f, 1f, 0f, 1f, true); graphics.Shaders.Display2D.SetSettings(1f, 1f, 0f, 1f, false);
// Begin drawing // Begin drawing
graphics.Shaders.Display2D.Begin(); graphics.Shaders.Display2D.Begin();

View file

@ -88,7 +88,7 @@ namespace CodeImp.DoomBuilder.Rendering
public TextAlignmentX AlignX { get { return alignx; } set { alignx = value; updateneeded = true; } } public TextAlignmentX AlignX { get { return alignx; } set { alignx = value; updateneeded = true; } }
public TextAlignmentY AlignY { get { return aligny; } set { aligny = 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 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 public bool DrawBackground { get { return drawbg; } set { if(drawbg != value) { drawbg = value; textureupdateneeded = true; } } } //mxd
internal Texture Texture { get { return texture; } } //mxd internal Texture Texture { get { return texture; } } //mxd
internal VertexBuffer VertexBuffer { get { return textbuffer; } } internal VertexBuffer VertexBuffer { get { return textbuffer; } }
@ -109,7 +109,7 @@ namespace CodeImp.DoomBuilder.Rendering
this.font = General.Settings.TextLabelFont; //mxd this.font = General.Settings.TextLabelFont; //mxd
this.rect = new RectangleF(0f, 0f, 1f, 1f); this.rect = new RectangleF(0f, 0f, 1f, 1f);
this.color = new PixelColor(255, 255, 255, 255); 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.alignx = TextAlignmentX.Center;
this.aligny = TextAlignmentY.Top; this.aligny = TextAlignmentY.Top;
this.textsize = new SizeF(); this.textsize = new SizeF();
@ -119,10 +119,6 @@ namespace CodeImp.DoomBuilder.Rendering
// Register as resource // Register as resource
General.Map.Graphics.RegisterResource(this); 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 // We have no destructor
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
@ -203,7 +199,7 @@ namespace CodeImp.DoomBuilder.Rendering
} }
// Create label image // Create label image
Bitmap img = CreateLabelImage(text, font, color, backcolor, drawbg); Bitmap img = CreateLabelImage(text, font, alignx, aligny, color, backcolor, drawbg);
textsize = img.Size; textsize = img.Size;
// Create texture // Create texture
@ -234,6 +230,13 @@ namespace CodeImp.DoomBuilder.Rendering
case TextAlignmentY.Bottom: beginy = absview.Y + absview.Height - textsize.Height; break; 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 //mxd. Lock the buffer
using(DataStream stream = textbuffer.Lock(0, 4 * FlatVertex.Stride, LockFlags.Discard | LockFlags.NoSystemLock)) using(DataStream stream = textbuffer.Lock(0, 4 * FlatVertex.Stride, LockFlags.Discard | LockFlags.NoSystemLock))
{ {
@ -258,7 +261,7 @@ namespace CodeImp.DoomBuilder.Rendering
} }
//mxd //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); PointF textorigin = new PointF(4, 3);
RectangleF textrect = new RectangleF(textorigin, General.Interface.MeasureString(text, font)); 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); textrect.Height = (float)Math.Round(textrect.Height);
RectangleF bgrect = new RectangleF(0, 0, textrect.Width + textorigin.X * 2, textrect.Height + textorigin.Y * 2); 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)) using(Graphics g = Graphics.FromImage(result))
{ {
g.SmoothingMode = SmoothingMode.HighQuality; g.SmoothingMode = SmoothingMode.HighQuality;
@ -276,7 +297,7 @@ namespace CodeImp.DoomBuilder.Rendering
// Draw text // Draw text
using(StringFormat sf = new StringFormat()) using(StringFormat sf = new StringFormat())
{ {
sf.FormatFlags = StringFormatFlags.NoWrap; sf.FormatFlags = StringFormatFlags.FitBlackBox | StringFormatFlags.NoWrap;
sf.Alignment = StringAlignment.Center; sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center;
@ -319,21 +340,20 @@ namespace CodeImp.DoomBuilder.Rendering
using(SolidBrush brush = new SolidBrush(backcolor.ToColor())) using(SolidBrush brush = new SolidBrush(backcolor.ToColor()))
g.DrawString(text, font, brush, textrect, sf); g.DrawString(text, font, brush, textrect, sf);
} }
// Draw text with outline // Draw plain text
else else
{ {
RectangleF pathrect = textrect; RectangleF plainbgrect = textrect;
pathrect.Inflate(1, 3); if(text.Length > 1) plainbgrect.Inflate(6, 2);
GraphicsPath p = new GraphicsPath(); RectangleF plaintextrect = textrect;
p.AddString(text, font.FontFamily, (int)font.Style, g.DpiY * font.Size / 72f, pathrect, sf); plaintextrect.Inflate(6, 4);
// Draw'n'fill text using(SolidBrush brush = new SolidBrush(backcolor.ToColor()))
using(Pen pen = new Pen(backcolor.ToColor(), 3)) g.FillRectangle(brush, plainbgrect);
g.DrawPath(pen, p);
using(SolidBrush brush = new SolidBrush(color.ToColor())) using(SolidBrush brush = new SolidBrush(color.ToColor()))
g.FillPath(brush, p); g.DrawString(text, font, brush, plaintextrect, sf);
} }
} }
} }

View file

@ -204,17 +204,20 @@ namespace CodeImp.DoomBuilder.Windows
// Constructor // Constructor
internal MainForm() internal MainForm()
{ {
// Fetch pointer
windowptr = base.Handle;
//mxd. Graphics
graphics = Graphics.FromHwndInternal(windowptr);
//mxd. Set DPI-aware icon size //mxd. Set DPI-aware icon size
using(Graphics g = this.CreateGraphics()) DPIScaler = new SizeF(graphics.DpiX / 96, graphics.DpiY / 96);
{
DPIScaler = new SizeF(g.DpiX / 96, g.DpiY / 96);
if(DPIScaler.Width != 1.0f || DPIScaler.Height != 1.0f) if(DPIScaler.Width != 1.0f || DPIScaler.Height != 1.0f)
{ {
ScaledIconSize.Width = (int)Math.Round(ScaledIconSize.Width * DPIScaler.Width); ScaledIconSize.Width = (int)Math.Round(ScaledIconSize.Width * DPIScaler.Width);
ScaledIconSize.Height = (int)Math.Round(ScaledIconSize.Height * DPIScaler.Height); ScaledIconSize.Height = (int)Math.Round(ScaledIconSize.Height * DPIScaler.Height);
} }
}
// Setup controls // Setup controls
InitializeComponent(); InitializeComponent();
@ -234,9 +237,6 @@ namespace CodeImp.DoomBuilder.Windows
labelcollapsedinfo.Text = ""; labelcollapsedinfo.Text = "";
display.Dock = DockStyle.Fill; display.Dock = DockStyle.Fill;
// Fetch pointer
windowptr = base.Handle;
// Make array for view modes // Make array for view modes
viewmodesbuttons = new ToolStripButton[Renderer2D.NUM_VIEW_MODES]; viewmodesbuttons = new ToolStripButton[Renderer2D.NUM_VIEW_MODES];
viewmodesbuttons[(int)ViewMode.Normal] = buttonviewnormal; viewmodesbuttons[(int)ViewMode.Normal] = buttonviewnormal;
@ -290,9 +290,6 @@ namespace CodeImp.DoomBuilder.Windows
//mxd. Hints //mxd. Hints
hintsPanel = new HintsPanel(); hintsPanel = new HintsPanel();
hintsDocker = new Docker("hints", "Help", hintsPanel); hintsDocker = new Docker("hints", "Help", hintsPanel);
//mxd. Graphics
graphics = Graphics.FromHwndInternal(windowptr);
} }
#endregion #endregion
@ -2679,14 +2676,14 @@ namespace CodeImp.DoomBuilder.Windows
private string GetDisplayFilename(string filename) private string GetDisplayFilename(string filename)
{ {
// String doesnt fit? // String doesnt fit?
if(GetStringWidth(filename) > MAX_RECENT_FILES_PIXELS) if(MeasureString(filename, this.Font).Width > MAX_RECENT_FILES_PIXELS)
{ {
// Start chopping off characters // Start chopping off characters
for(int i = filename.Length - 6; i >= 0; i--) for(int i = filename.Length - 6; i >= 0; i--)
{ {
// Does it fit now? // Does it fit now?
string newname = filename.Substring(0, 3) + "..." + filename.Substring(filename.Length - i, i); 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!) // 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 // Exit clicked
private void itemexit_Click(object sender, EventArgs e) { this.Close(); } private void itemexit_Click(object sender, EventArgs e) { this.Close(); }

View file

@ -395,7 +395,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
l.AlignX = TextAlignmentX.Center; l.AlignX = TextAlignmentX.Center;
l.AlignY = TextAlignmentY.Middle; l.AlignY = TextAlignmentY.Middle;
l.Color = General.Colors.InfoLine; l.Color = General.Colors.InfoLine;
l.Backcolor = General.Colors.Background.WithAlpha(255); l.BackColor = General.Colors.Background.WithAlpha(128);
larr[i] = l; larr[i] = l;
} }
@ -430,7 +430,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
l.AlignX = TextAlignmentX.Center; l.AlignX = TextAlignmentX.Center;
l.AlignY = TextAlignmentY.Middle; l.AlignY = TextAlignmentY.Middle;
l.Color = (linedef == highlighted ? General.Colors.Selection : General.Colors.Highlight); 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(); l.Text = (++index).ToString();
labels.Add(linedef, l); labels.Add(linedef, l);
} }

View file

@ -160,7 +160,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
labelarray[i].AlignX = TextAlignmentX.Center; labelarray[i].AlignX = TextAlignmentX.Center;
labelarray[i].AlignY = TextAlignmentY.Middle; labelarray[i].AlignY = TextAlignmentY.Middle;
labelarray[i].Color = c; labelarray[i].Color = c;
labelarray[i].Backcolor = General.Colors.Background.WithAlpha(255); labelarray[i].BackColor = General.Colors.Background.WithAlpha(128);
} }
labels.Add(s, labelarray); labels.Add(s, labelarray);
} }

View file

@ -936,7 +936,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
l.AlignX = TextAlignmentX.Center; l.AlignX = TextAlignmentX.Center;
l.AlignY = TextAlignmentY.Middle; l.AlignY = TextAlignmentY.Middle;
l.Color = General.Colors.InfoLine; l.Color = General.Colors.InfoLine;
l.Backcolor = General.Colors.Background.WithAlpha(255); l.BackColor = General.Colors.Background.WithAlpha(128);
larr[i] = l; larr[i] = l;
} }
@ -982,8 +982,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
} }
l.Color = (thing == highlighted ? General.Colors.Selection : General.Colors.Highlight); l.Color = (thing == highlighted ? General.Colors.Selection : General.Colors.Highlight);
l.Backcolor = General.Colors.Background.WithAlpha(255); l.BackColor = General.Colors.Background.WithAlpha(192);
l.DrawBackground = true;
l.Text = (++index).ToString(); l.Text = (++index).ToString();
labels.Add(thing, l); labels.Add(thing, l);
} }

View file

@ -103,12 +103,14 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Initialization // Initialization
private void Initialize() private void Initialize()
{ {
label = new TextLabel(); label = new TextLabel
label.AlignX = TextAlignmentX.Center; {
label.AlignY = TextAlignmentY.Middle; AlignX = TextAlignmentX.Center,
label.Color = General.Colors.Highlight; AlignY = TextAlignmentY.Middle,
label.Backcolor = General.Colors.Background; Color = General.Colors.Highlight,
label.TransformCoords = true; BackColor = General.Colors.Background.WithAlpha(64),
TransformCoords = true,
};
} }
// Disposer // Disposer