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,25 +58,63 @@ 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") //model structure start
{
if(token != "model") continue;
// Find classname
SkipWhitespace(true);
string displayclassname = StripTokenQuotes(ReadToken(ActorStructure.ACTOR_CLASS_SPECIAL_TOKENS));
string classname = displayclassname.ToLowerInvariant();
if(!string.IsNullOrEmpty(classname) && !entries.ContainsKey(classname))
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, 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)
@ -68,46 +122,11 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
LogError();
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;
}
// 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,11 +1,11 @@
#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
@ -13,48 +13,82 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
{
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; } }
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
//read modeldef structure contents
parsingfinished = false;
#region ================== Structs
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))
{
token = parser.ReadToken();
if(!string.IsNullOrEmpty(token))
{
token = parser.StripTokenQuotes(token).ToLowerInvariant(); //ANYTHING can be quoted...
switch(token)
{
#region ================== Path
string token = parser.ReadToken();
if(string.IsNullOrEmpty(token)) continue;
switch(token.ToLowerInvariant())
{
case "path":
parser.SkipWhitespace(true);
path = parser.StripTokenQuotes(parser.ReadToken(false)).Replace("/", "\\"); // Don't skip newline
@ -65,17 +99,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
}
break;
#endregion
#region ================== Model
case "model":
parser.SkipWhitespace(true);
//model index
// Model index
int index;
token = parser.StripTokenQuotes(parser.ReadToken());
if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out 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 + "\"");
@ -90,7 +120,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
parser.SkipWhitespace(true);
//model path
// Model path
token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline
if(string.IsNullOrEmpty(token))
{
@ -98,10 +128,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
return false;
}
//check invalid path chars
// Check invalid path chars
if(!parser.CheckInvalidPathChars(token)) return false;
//check extension
// Check extension
string modelext = Path.GetExtension(token);
if(string.IsNullOrEmpty(modelext))
{
@ -115,28 +145,24 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
return false;
}
//GZDoom allows models with identical modelIndex, it uses the last one encountered
modelNames[index] = Path.Combine(path, token);
// GZDoom allows models with identical index, 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))
// 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)
if(skinindex >= MAX_MODELS)
{
parser.ReportError("GZDoom doesn't allow more than " + MAX_MODELS + " skins per MODELDEF entry");
return false;
@ -144,7 +170,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
parser.SkipWhitespace(true);
//skin path
// Skin path
token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline
if(string.IsNullOrEmpty(token))
{
@ -152,10 +178,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
return false;
}
//check invalid path chars
// Check invalid path chars
if(!parser.CheckInvalidPathChars(token)) return false;
//check extension
// Check extension
string texext = Path.GetExtension(token);
if(Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, texext) == -1)
{
@ -163,17 +189,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
return false;
}
//GZDoom allows skins with identical modelIndex, it uses the last one encountered
textureNames[skinIndex] = Path.Combine(path, token);
// GZDoom allows skins with identical index, 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());
token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref scale.Y))
{
// Not numeric!
@ -182,7 +204,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
}
parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken());
token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref scale.X))
{
// Not numeric!
@ -191,7 +213,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
}
parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken());
token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref scale.Z))
{
// Not numeric!
@ -200,13 +222,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
}
break;
#endregion
#region ================== Offset
case "offset":
parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken());
token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref offset.X))
{
// Not numeric!
@ -215,7 +233,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
}
parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken());
token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref offset.Y))
{
// Not numeric!
@ -224,7 +242,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
}
parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken());
token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref offset.Z))
{
// Not numeric!
@ -233,13 +251,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
}
break;
#endregion
#region ================== ZOffset
case "zoffset":
parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken());
token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref offset.Z))
{
// Not numeric!
@ -248,14 +262,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
}
break;
#endregion
#region ================== AngleOffset
case "angleoffset":
parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken());
if(!parser.ReadSignedFloat(token, ref angleOffset))
token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref angleoffset))
{
// Not numeric!
parser.ReportError("Expected AngleOffset value, but got \"" + token + "\"");
@ -263,14 +273,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
}
break;
#endregion
#region ================== PitchOffset
case "pitchoffset":
parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken());
if(!parser.ReadSignedFloat(token, ref pitchOffset))
token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref pitchoffset))
{
// Not numeric!
parser.ReportError("Expected PitchOffset value, but got \"" + token + "\"");
@ -278,14 +284,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
}
break;
#endregion
#region ================== RollOffset
case "rolloffset":
parser.SkipWhitespace(true);
token = parser.StripTokenQuotes(parser.ReadToken());
if(!parser.ReadSignedFloat(token, ref rollOffset))
token = parser.ReadToken();
if(!parser.ReadSignedFloat(token, ref rolloffset))
{
// Not numeric!
parser.ReportError("Expected RollOffset value, but got \"" + token + "\"");
@ -293,198 +295,200 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
}
break;
#endregion
#region ================== InheritActorPitch
case "inheritactorpitch":
inheritactorpitch = true;
break;
#endregion
#region ================== InheritActorRoll
case "inheritactorroll":
inheritactorroll = true;
break;
#endregion
#region ================== Frame / FrameIndex
case "inheritactorpitch": inheritactorpitch = true; break;
case "inheritactorroll": inheritactorroll = true; break;
//FrameIndex <XXXX> <X> <model index> <frame number>
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");
// Sprite name
parser.SkipWhitespace(true);
//should be sprite lump
token = parser.StripTokenQuotes(parser.ReadToken()).ToLowerInvariant();
if(string.IsNullOrEmpty(spriteLump))
string fispritename = parser.ReadToken();
if(string.IsNullOrEmpty(fispritename))
{
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++)
{
if(!modelsUsed[i])
{
modelNames[i] = null;
textureNames[i] = null;
}
}
break;
parser.ReportError("Sprite name must be 4 characters long");
return false;
}
// Sprite frame
parser.SkipWhitespace(true);
//should be sprite frame
token = parser.StripTokenQuotes(parser.ReadToken()).ToLowerInvariant();
if(string.IsNullOrEmpty(spriteFrame))
token = parser.ReadToken();
if(string.IsNullOrEmpty(token))
{
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++)
{
if(!modelsUsed[i])
{
modelNames[i] = null;
textureNames[i] = null;
}
}
break;
parser.ReportError("Sprite frame must be 1 character long");
return false;
}
// Make full name
fispritename += token;
// Model index
parser.SkipWhitespace(true);
//should be model index
token = parser.StripTokenQuotes(parser.ReadToken());
int modelIndex;
if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out modelIndex))
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;
}
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;
// Frame number
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))
int fiframeindnex;
token = parser.ReadToken();
if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out fiframeindnex) || fiframeindnex < 0)
{
// Not numeric!
parser.ReportError("Expected model frame index, but got \"" + token + "\"");
parser.ReportError("Expected 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;
// 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))
{
// Missing!
parser.ReportError("Expected model frame name");
parser.ReportError("Expected sprite frame");
return false;
}
if(token.Length != 1)
{
parser.ReportError("Sprite frame must be 1 character long");
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
{
//must be "}", step back
parser.DataStream.Seek(-token.Length - 1, SeekOrigin.Current);
frames[spritename].Add(fs);
}
break;
}
}
}
case "{":
parser.ReportError("Unexpected scope start");
return false;
// Structure ends here
case "}":
parsingfinished = true;
break;
#endregion
}
}
}
// 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();
@ -119,10 +119,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,17 +204,20 @@ 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);
}
}
// Setup controls
InitializeComponent();
@ -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