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 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))
if(string.IsNullOrEmpty(token)) continue;
token = StripTokenQuotes(token).ToLowerInvariant();
if(token != "model") continue;
// Find classname
SkipWhitespace(true);
string classname = ReadToken(ActorStructure.ACTOR_CLASS_SPECIAL_TOKENS);
if(string.IsNullOrEmpty(classname))
{
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();
ReportError("Expected actor class");
return false;
}
// Now find opening brace
if(!NextTokenIs("{")) return false;
if(!string.IsNullOrEmpty(classname) && !entries.ContainsKey(classname))
{
// Now find opening brace
if(!NextTokenIs("{")) return false;
ModeldefStructure mds = new ModeldefStructure();
if(mds.Parse(this, displayclassname) && mds.ModelData != null)
{
entries.Add(classname, mds.ModelData);
}
if(HasError)
{
LogError();
ClearError();
}
// Skip untill current structure end
if(!mds.ParsingFinished) SkipStructure(1);
}
}
else
// Parse the structure
ModeldefStructure mds = new ModeldefStructure();
if(mds.Parse(this))
{
// Fetch Actor info
if(actorsbyclass.ContainsKey(classname))
{
// Unknown structure!
if(token != "{")
{
string token2;
do
{
if(!SkipWhitespace(true)) break;
token2 = ReadToken();
if(string.IsNullOrEmpty(token2)) break;
}
while(token2 != "{");
}
ThingTypeInfo info = General.Map.Data.GetThingInfoEx(actorsbyclass[classname]);
SkipStructure(1);
// 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)
{
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
}
}

View file

@ -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
#endregion
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;
#region ================== Variables
string token;
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;
#endregion
private Dictionary<string, HashSet<FrameStructure>> frames;
//read modeldef structure contents
parsingfinished = false;
#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))
{
token = parser.ReadToken();
if(!string.IsNullOrEmpty(token))
string token = parser.ReadToken();
if(string.IsNullOrEmpty(token)) continue;
switch(token.ToLowerInvariant())
{
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
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;
#endregion
#region ================== PitchOffset
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
#region ================== RollOffset
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;
#endregion
#region ================== InheritActorPitch
case "inheritactorpitch":
inheritactorpitch = true;
break;
#endregion
#region ================== InheritActorRoll
case "inheritactorroll":
inheritactorroll = true;
break;
#endregion
#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;
#endregion
}
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
}
}

View file

@ -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();

View file

@ -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();

View file

@ -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);
}
}
}

View file

@ -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(); }

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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