Added, Classic modes: thing sprites are now angle-dependent.

This commit is contained in:
MaxED 2016-04-08 14:02:08 +00:00
parent a06f44ce19
commit 42607f704f
8 changed files with 444 additions and 191 deletions

View file

@ -18,18 +18,25 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.GZBuilder.Data;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.ZDoom;
using CodeImp.DoomBuilder.Map;
using System.Drawing;
using CodeImp.DoomBuilder.ZDoom;
#endregion
namespace CodeImp.DoomBuilder.Config
{
public struct SpriteFrameInfo //mxd
{
public string Sprite;
public long SpriteLongName;
public bool Mirror;
}
public class ThingTypeInfo : INumberedTitle, IComparable<ThingTypeInfo>
{
#region ================== Constants
@ -50,9 +57,9 @@ namespace CodeImp.DoomBuilder.Config
private readonly int index;
private string title;
private string sprite;
private SpriteFrameInfo[] spriteframe; //mxd. All rotations for given sprite. Currently contains either 1 or 8 frames
private ActorStructure actor;
private string classname; //mxd
private long spritelongname;
private int color;
private float alpha; //mxd
private byte alphabyte; //mxd
@ -88,8 +95,8 @@ namespace CodeImp.DoomBuilder.Config
public int Index { get { return index; } }
public string Title { get { return title; } }
public string Sprite { get { return sprite; } }
public SpriteFrameInfo[] SpriteFrame { get { return spriteframe; } }
public ActorStructure Actor { get { return actor; } }
public long SpriteLongName { get { return spritelongname; } }
public int Color { get { return color; } }
public float Alpha { get { return alpha; } } //mxd
public byte AlphaByte { get { return alphabyte; } } //mxd
@ -147,7 +154,7 @@ namespace CodeImp.DoomBuilder.Config
this.spritescale = new SizeF(1.0f, 1.0f);
this.fixedsize = false;
this.fixedrotation = false; //mxd
this.spritelongname = long.MaxValue;
this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } }; //mxd
this.args = new ArgumentInfo[Linedef.NUM_ARGS];
this.isknown = false;
this.absolutez = false;
@ -199,12 +206,9 @@ namespace CodeImp.DoomBuilder.Config
// Safety
if(this.radius < 4f || this.fixedsize) this.radius = THING_FIXED_SIZE;
if(this.hangs && this.absolutez) this.hangs = false; //mxd
// Make long name for sprite lookup
if(this.sprite.Length <= 8)
this.spritelongname = Lump.MakeLongName(this.sprite);
else
this.spritelongname = long.MaxValue;
//mxd. Create sprite frame
this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } };
// We have no destructor
GC.SuppressFinalize(this);
@ -245,12 +249,9 @@ namespace CodeImp.DoomBuilder.Config
// Safety
if(this.radius < 4f || this.fixedsize) this.radius = THING_FIXED_SIZE;
if(this.hangs && this.absolutez) this.hangs = false; //mxd
// Make long name for sprite lookup
if(this.sprite.Length <= 8)
this.spritelongname = Lump.MakeLongName(this.sprite);
else
this.spritelongname = long.MaxValue;
//mxd. Create sprite frame
this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } };
// We have no destructor
GC.SuppressFinalize(this);
@ -292,6 +293,9 @@ namespace CodeImp.DoomBuilder.Config
// Apply settings from actor
ModifyByDecorateActor(actor);
//mxd. Create sprite frame
this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } };
// We have no destructor
GC.SuppressFinalize(this);
@ -313,6 +317,7 @@ namespace CodeImp.DoomBuilder.Config
// Read properties
this.sprite = cat.Sprite;
this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true), } }; //mxd
this.color = cat.Color;
this.alpha = cat.Alpha; //mxd
this.alphabyte = (byte)(this.alpha * 255); //mxd
@ -334,6 +339,9 @@ namespace CodeImp.DoomBuilder.Config
// Apply settings from actor
ModifyByDecorateActor(actor);
//mxd. Create sprite frame
this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } };
// We have no destructor
GC.SuppressFinalize(this);
}
@ -354,6 +362,8 @@ namespace CodeImp.DoomBuilder.Config
// Copy properties
this.sprite = other.sprite;
this.spriteframe = new SpriteFrameInfo[other.spriteframe.Length]; //mxd
other.spriteframe.CopyTo(this.spriteframe, 0); //mxd
this.color = other.color;
this.alpha = other.alpha; //mxd
this.alphabyte = other.alphabyte; //mxd
@ -443,10 +453,8 @@ namespace CodeImp.DoomBuilder.Config
else if(string.IsNullOrEmpty(sprite))//mxd
sprite = DataManager.INTERNAL_PREFIX + "unknownthing";
if(this.sprite.Length < 9)
this.spritelongname = Lump.MakeLongName(this.sprite);
else
this.spritelongname = long.MaxValue;
//mxd. Create sprite frame
this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } };
// Set sprite scale (mxd. Scale is translated to xscale and yscale in ActorStructure)
if(actor.HasPropertyWithValue("xscale"))
@ -501,6 +509,134 @@ namespace CodeImp.DoomBuilder.Config
if(blocking > THING_BLOCKING_NONE) errorcheck = THING_ERROR_INSIDE_STUCK;
}
//mxd. This tries to find all possible sprite rotations
internal void SetupSpriteFrame()
{
// Empty or internal sprites don't have rotations
if(string.IsNullOrEmpty(sprite) || sprite.StartsWith(DataManager.INTERNAL_PREFIX)) return;
// Skip sprites with strange names
if(sprite.Length != 6 && sprite.Length != 8)
{
General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ". Unsupported sprite name fromat: \"" + sprite + "\"");
return;
}
// Get sprite angle
string anglestr = sprite.Substring(5, 1);
int sourceangle;
if(!int.TryParse(anglestr, NumberStyles.Integer, CultureInfo.InvariantCulture, out sourceangle))
{
General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ". Unable to get sprite angle from sprite \"" + sprite + "\"");
return;
}
if(sourceangle < 0 || sourceangle > 8)
{
General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ", sprite \"" + sprite + "\". Sprite angle must be in [0..8] range");
return;
}
// No rotations?
if(sourceangle == 0) return;
// Gather rotations
string[] frames = new string[8];
bool[] mirror = new bool[8];
int processedcount = 0;
string sourcename = sprite.Substring(0, 4);
IEnumerable<string> spritenames = General.Map.Data.GetSpriteNames(sourcename);
// Process gathered sprites
char sourceframe = sprite[4];
foreach(string s in spritenames)
{
//Check first frame block
char targetframe = s[4];
if(targetframe == sourceframe)
{
// Check angle
int targetangle;
anglestr = s.Substring(5, 1);
if(!int.TryParse(anglestr, NumberStyles.Integer, CultureInfo.InvariantCulture, out targetangle))
{
General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ". Unable to get sprite angle from sprite \"" + s + "\"");
return;
}
if(targetangle < 0 || targetangle > 8)
{
General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ", sprite \"" + s + "\". Sprite angle must be in [0..8] range");
return;
}
// Add to collection
frames[targetangle - 1] = s;
processedcount++;
}
// Check second frame block?
if(s.Length == 6) continue;
targetframe = s[6];
if(targetframe == sourceframe)
{
// Check angle
int targetangle;
anglestr = s.Substring(7, 1);
if(!int.TryParse(anglestr, NumberStyles.Integer, CultureInfo.InvariantCulture, out targetangle))
{
General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ". Unable to get sprite angle from sprite \"" + s + "\"");
return;
}
if(targetangle < 0 || targetangle > 8)
{
General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ", sprite \"" + s + "\". Sprite angle must be in [0..8] range");
return;
}
// Add to collections
frames[targetangle - 1] = s;
mirror[targetangle - 1] = true;
processedcount++;
}
// Gathered all sprites?
if(processedcount == 8) break;
}
// Check collected data
if(processedcount != 8)
{
// Check which angles are missing
List<string> missingangles = new List<string>();
for(int i = 0; i < frames.Length; i++)
{
if(string.IsNullOrEmpty(frames[i]))
missingangles.Add((i + 1).ToString());
}
// Assemble angles to display
string ma = string.Join(", ", missingangles.ToArray());
if(missingangles.Count > 2)
{
int pos = ma.LastIndexOf(",", StringComparison.Ordinal);
if(pos != -1) ma = ma.Remove(pos, 1).Insert(pos, " and");
}
General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ". Sprite rotations " + ma + " for sprite " + sourcename + ", frame " + sourceframe + " are missing");
return;
}
// Create collection
spriteframe = new SpriteFrameInfo[frames.Length];
for(int i = 0; i < frames.Length; i++)
{
spriteframe[i] = new SpriteFrameInfo { Sprite = frames[i], SpriteLongName = Lump.MakeLongName(frames[i]), Mirror = mirror[i] };
}
}
// This is used for sorting
public int CompareTo(ThingTypeInfo other)
{

View file

@ -41,6 +41,7 @@ namespace CodeImp.DoomBuilder
private DebugMessageType filters;
private static long starttime = -1;
private static long storedtime;
private static int counter;
private static DebugConsole me;
@ -144,11 +145,18 @@ namespace CodeImp.DoomBuilder
starttime = SlimDX.Configuration.Timer.ElapsedMilliseconds;
}
public static void PauseTimer()
{
if(starttime == -1) throw new InvalidOperationException("DebugConsole.StartTimer() must be called before DebugConsole.PauseTimer()!");
storedtime += SlimDX.Configuration.Timer.ElapsedMilliseconds - starttime;
}
public static void StopTimer(string message)
{
if(starttime == -1) throw new InvalidOperationException("DebugConsole.StartTimer() must be called before DebugConsole.StopTimer()!");
long duration = SlimDX.Configuration.Timer.ElapsedMilliseconds - starttime;
long duration = SlimDX.Configuration.Timer.ElapsedMilliseconds - starttime + storedtime;
if(message.Contains("%"))
message = message.Replace("%", duration.ToString(CultureInfo.InvariantCulture));
@ -158,6 +166,7 @@ namespace CodeImp.DoomBuilder
WriteLine(DebugMessageType.SPECIAL, message);
starttime = -1;
storedtime = 0;
}
public static void IncrementCounter() { IncrementCounter(1); }

View file

@ -65,6 +65,9 @@ namespace CodeImp.DoomBuilder.Data
public const string INTERNAL_PREFIX = "internal:";
public const int CLASIC_IMAGE_NAME_LENGTH = 8; //mxd
private const int MAX_SKYTEXTURE_SIZE = 2048; //mxd
private long UNKNOWN_THING; //mxd
private long MISSING_THING; //mxd
#endregion
@ -121,7 +124,7 @@ namespace CodeImp.DoomBuilder.Data
private ImageData hourglass3d;
private ImageData crosshair;
private ImageData crosshairbusy;
private Dictionary<string, ImageData> internalsprites;
private Dictionary<string, long> internalspriteslookup; //mxd
private ImageData whitetexture;
private ImageData blacktexture; //mxd
private ImageData thingtexture; //mxd
@ -323,7 +326,6 @@ namespace CodeImp.DoomBuilder.Data
texturesets = new List<MatchingTextureSet>();
usedtextures = new Dictionary<long, bool>(); //mxd
usedflats = new Dictionary<long, bool>(); //mxd
internalsprites = new Dictionary<string, ImageData>(StringComparer.Ordinal);
thingcategories = General.Map.Config.GetThingCategories();
thingtypes = General.Map.Config.GetThingTypes();
@ -586,7 +588,6 @@ namespace CodeImp.DoomBuilder.Data
foreach(KeyValuePair<long, ImageData> i in textures) i.Value.Dispose();
foreach(KeyValuePair<long, ImageData> i in flats) i.Value.Dispose();
foreach(KeyValuePair<long, ImageData> i in sprites) i.Value.Dispose();
foreach(KeyValuePair<string, ImageData> i in internalsprites) i.Value.Dispose(); //mxd
palette = null;
//mxd. Dispose models
@ -615,7 +616,6 @@ namespace CodeImp.DoomBuilder.Data
texturenames = null;
flatnames = null;
imageque = null;
internalsprites = null;
mapinfo = null; //mxd
}
@ -1499,53 +1499,60 @@ namespace CodeImp.DoomBuilder.Data
// Valid sprite name?
if(ti.Sprite.Length == 0 || ti.Sprite.Length > CLASIC_IMAGE_NAME_LENGTH) continue; //mxd
ImageData image = null;
//mxd. Find all sprite angles
ti.SetupSpriteFrame();
// Sprite not in our collection yet?
if(!sprites.ContainsKey(ti.SpriteLongName))
//mxd. Load them all
foreach(SpriteFrameInfo info in ti.SpriteFrame)
{
//mxd. Go for all opened containers
bool spritefound = false;
if(!string.IsNullOrEmpty(ti.Sprite))
ImageData image = null;
// Sprite not in our collection yet?
if(!sprites.ContainsKey(info.SpriteLongName))
{
for(int i = containers.Count - 1; i >= 0; i--)
//mxd. Go for all opened containers
bool spritefound = false;
if(!string.IsNullOrEmpty(info.Sprite))
{
// This contain provides this sprite?
if(containers[i].GetSpriteExists(ti.Sprite))
for(int i = containers.Count - 1; i >= 0; i--)
{
spritefound = true;
break;
// This contain provides this sprite?
if(containers[i].GetSpriteExists(info.Sprite))
{
spritefound = true;
break;
}
}
}
}
if(spritefound)
{
// Make new sprite image
image = new SpriteImage(ti.Sprite);
if(spritefound)
{
// Make new sprite image
image = new SpriteImage(info.Sprite);
// Add to collection
sprites.Add(ti.SpriteLongName, image);
// Add to collection
sprites.Add(info.SpriteLongName, image);
}
else
{
General.ErrorLogger.Add(ErrorType.Error, "Missing sprite lump \"" + info.Sprite + "\". Forgot to include required resources?");
}
}
else
{
General.ErrorLogger.Add(ErrorType.Error, "Missing sprite lump \"" + ti.Sprite + "\". Forgot to include required resources?");
image = sprites[info.SpriteLongName];
}
}
else
{
image = sprites[ti.SpriteLongName];
}
// Add to preview manager
if(image != null) previews.AddImage(image);
// Add to preview manager
if(image != null) previews.AddImage(image);
}
}
// Output info
return sprites.Count;
}
// This returns a specific patch stream
// This returns a specific sprite stream
internal Stream GetSpriteData(string pname, ref string spritelocation)
{
if(!string.IsNullOrEmpty(pname))
@ -1553,13 +1560,13 @@ namespace CodeImp.DoomBuilder.Data
// Go for all opened containers
for(int i = containers.Count - 1; i >= 0; i--)
{
// This contain provides this patch?
// This contain provides this sprite?
Stream spritedata = containers[i].GetSpriteData(pname, ref spritelocation);
if(spritedata != null) return spritedata;
}
}
// No such patch found
// No such sprite found
return null;
}
@ -1574,12 +1581,12 @@ namespace CodeImp.DoomBuilder.Data
// Go for all opened containers
for(int i = containers.Count - 1; i >= 0; i--)
{
// This contain provides this patch?
// This contain provides this sprite?
if(containers[i].GetSpriteExists(pname)) return true;
}
}
// No such patch found
// No such sprite found
return false;
}
@ -1587,39 +1594,54 @@ namespace CodeImp.DoomBuilder.Data
private void LoadInternalSprites()
{
// Add sprite icon files from directory
string name;
string[] files = Directory.GetFiles(General.SpritesPath, "*.png", SearchOption.TopDirectoryOnly);
internalspriteslookup = new Dictionary<string, long>(files.Length + 2); //mxd
foreach(string spritefile in files)
{
ImageData img = new FileImage(Path.GetFileNameWithoutExtension(spritefile).ToLowerInvariant(), spritefile);
img.LoadImage();
img.AllowUnload = false;
internalsprites.Add(img.Name, img);
name = INTERNAL_PREFIX + img.Name;
long hash = Lump.MakeLongName(name, true); //mxd
sprites[hash] = img; //mxd
internalspriteslookup[name] = hash; //mxd
}
// Add some internal resources
if(!internalsprites.ContainsKey("nothing"))
// Add some internal resources.
// mxd. Doesn't seem to be used anywhere
/*name = INTERNAL_PREFIX + "nothing";
if(!internalspriteslookup.ContainsKey(name))
{
ImageData img = new ResourceImage("CodeImp.DoomBuilder.Resources.Nothing.png");
img.LoadImage();
img.AllowUnload = false;
internalsprites.Add("nothing", img);
}
if(!internalsprites.ContainsKey("unknownthing"))
long hash = Lump.MakeLongName(name, true); //mxd
sprites[hash] = img; //mxd
internalspriteslookup[name] = hash; //mxd
}*/
name = INTERNAL_PREFIX + "unknownthing";
UNKNOWN_THING = Lump.MakeLongName(name, true);
if(!internalspriteslookup.ContainsKey(name))
{
ImageData img = new ResourceImage("CodeImp.DoomBuilder.Resources.UnknownThing.png");
img.LoadImage();
img.AllowUnload = false;
internalsprites.Add("unknownthing", img);
sprites[UNKNOWN_THING] = img; //mxd
internalspriteslookup[name] = UNKNOWN_THING; //mxd
}
//mxd
if(!internalsprites.ContainsKey("missingthing"))
name = INTERNAL_PREFIX + "missingthing";
MISSING_THING = Lump.MakeLongName(name, true);
if(!internalspriteslookup.ContainsKey(name))
{
ImageData img = new ResourceImage("CodeImp.DoomBuilder.Resources.MissingThing.png");
img.LoadImage();
img.AllowUnload = false;
internalsprites.Add("missingthing", img);
sprites[MISSING_THING] = img; //mxd
internalspriteslookup[name] = MISSING_THING; //mxd
}
}
@ -1630,11 +1652,11 @@ namespace CodeImp.DoomBuilder.Data
if((name.Length > INTERNAL_PREFIX.Length) && name.ToLowerInvariant().StartsWith(INTERNAL_PREFIX))
{
// Get the internal sprite
string internalname = name.Substring(INTERNAL_PREFIX.Length).ToLowerInvariant();
if(internalsprites.ContainsKey(internalname))
return internalsprites[internalname];
string internalname = name.ToLowerInvariant();
if(internalspriteslookup.ContainsKey(internalname)) //mxd
return sprites[internalspriteslookup[internalname]];
return internalsprites["unknownthing"]; //mxd
return sprites[UNKNOWN_THING]; //mxd
}
else
{
@ -1678,7 +1700,7 @@ namespace CodeImp.DoomBuilder.Data
}
else //mxd
{
ImageData img = string.IsNullOrEmpty(name) ? internalsprites["unknownthing"] : internalsprites["missingthing"];
ImageData img = string.IsNullOrEmpty(name) ? sprites[UNKNOWN_THING] : sprites[MISSING_THING];
// Add to collection
sprites.Add(longname, img);
@ -1689,6 +1711,17 @@ namespace CodeImp.DoomBuilder.Data
}
}
}
//mxd. Returns all sprite names, which start with given string
internal IEnumerable<string> GetSpriteNames(string startswith)
{
HashSet<string> result = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach(DataReader reader in containers)
result.UnionWith(reader.GetSpriteNames(startswith));
return result;
}
#endregion

View file

@ -209,6 +209,9 @@ namespace CodeImp.DoomBuilder.Data
// When implemented, this checks if the given sprite lump exists
public abstract bool GetSpriteExists(string pname);
//mxd. When implemented, returns all sprites, which name starts with given string
public abstract HashSet<string> GetSpriteNames(string startswith);
#endregion

View file

@ -377,14 +377,14 @@ namespace CodeImp.DoomBuilder.Data
#region ================== Sprites
// This loads the textures
// This loads the sprites
public override IEnumerable<ImageData> LoadSprites(Dictionary<string, TexturesParser> cachedparsers)
{
Dictionary<long, ImageData> images = new Dictionary<long, ImageData>();
List<ImageData> imgset = new List<ImageData>();
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
Dictionary<long, ImageData> images = new Dictionary<long, ImageData>();
List<ImageData> imgset = new List<ImageData>();
// Load from wad files
// Note the backward order, because the last wad's images have priority
@ -421,7 +421,32 @@ namespace CodeImp.DoomBuilder.Data
return new List<ImageData>(images.Values);
}
//mxd. Returns all sprites, which name starts with given string
public override HashSet<string> GetSpriteNames(string startswith)
{
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
HashSet<string> result = new HashSet<string>();
// Load from wad files
// Note the backward order, because the last wad's images have priority
for(int i = wads.Count - 1; i >= 0; i--)
{
result.UnionWith(wads[i].GetSpriteNames(startswith));
}
// Load from out own files
string[] files = GetAllFilesWhichTitleStartsWith(SPRITES_DIR, startswith, true);
foreach(string file in files)
{
result.Add(Path.GetFileNameWithoutExtension(file).ToUpperInvariant());
}
return result;
}
#endregion
#region ================== Colormaps
@ -429,12 +454,12 @@ namespace CodeImp.DoomBuilder.Data
// This loads the textures
public override ICollection<ImageData> LoadColormaps()
{
Dictionary<long, ImageData> images = new Dictionary<long, ImageData>();
ICollection<ImageData> collection;
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
Dictionary<long, ImageData> images = new Dictionary<long, ImageData>();
ICollection<ImageData> collection;
// Load from wad files
// Note the backward order, because the last wad's images have priority
for(int i = wads.Count - 1; i >= 0; i--)
@ -448,8 +473,7 @@ namespace CodeImp.DoomBuilder.Data
AddImagesToList(images, collection);
// Add images to the container-specific texture set
foreach(ImageData img in images.Values)
textureset.AddFlat(img);
foreach(ImageData img in images.Values) textureset.AddFlat(img);
return new List<ImageData>(images.Values);
}

View file

@ -879,7 +879,29 @@ namespace CodeImp.DoomBuilder.Data
return false;
}
//mxd. Returns all sprites, which name starts with given string
public override HashSet<string> GetSpriteNames(string startswith)
{
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
HashSet<string> result = new HashSet<string>();
if(startswith.Length > 8) return result;
startswith = startswith.ToUpperInvariant();
foreach(LumpRange range in spriteranges)
{
for(int i = range.start; i < range.end + 1; i++)
{
if(file.Lumps[i].Name.StartsWith(startswith))
result.Add(file.Lumps[i].Name);
}
}
return result;
}
#endregion
#region ================== Voxels (mxd)

View file

@ -1083,25 +1083,28 @@ namespace CodeImp.DoomBuilder.Rendering
}
//mxd
private static void CreateThingSpriteVerts(Vector2D screenpos, float width, float height, ref FlatVertex[] verts, int offset, int color)
private static void CreateThingSpriteVerts(Vector2D screenpos, float width, float height, ref FlatVertex[] verts, int offset, int color, bool mirror)
{
float ul = (mirror ? 1f : 0f);
float ur = (mirror ? 0f : 1f);
// Setup fixed rect for circle
verts[offset].x = screenpos.x - width;
verts[offset].y = screenpos.y - height;
verts[offset].c = color;
verts[offset].u = 0;
verts[offset].u = ul;
verts[offset].v = 0;
offset++;
verts[offset].x = screenpos.x + width;
verts[offset].y = screenpos.y - height;
verts[offset].c = color;
verts[offset].u = 1;
verts[offset].u = ur;
verts[offset].v = 0;
offset++;
verts[offset].x = screenpos.x - width;
verts[offset].y = screenpos.y + height;
verts[offset].c = color;
verts[offset].u = 0;
verts[offset].u = ul;
verts[offset].v = 1;
offset++;
verts[offset] = verts[offset - 2];
@ -1111,7 +1114,7 @@ namespace CodeImp.DoomBuilder.Rendering
verts[offset].x = screenpos.x + width;
verts[offset].y = screenpos.y + height;
verts[offset].c = color;
verts[offset].u = 1;
verts[offset].u = ur;
verts[offset].v = 1;
}
@ -1232,112 +1235,135 @@ namespace CodeImp.DoomBuilder.Rendering
// Find sprite texture
if(info.Sprite.Length == 0) continue;
ImageData sprite = General.Map.Data.GetSpriteImage(info.Sprite);
if(sprite == null) continue;
if(!sprite.IsImageLoaded)
// Sort by sprite angle...
Dictionary<int, List<Thing>> thingsbyangle = new Dictionary<int, List<Thing>>(group.Value.Count);
if(info.SpriteFrame.Length == 8)
{
sprite.SetUsedInMap(true);
continue;
}
if(sprite.Texture == null) sprite.CreateTexture();
graphics.Shaders.Things2D.Texture1 = sprite.Texture;
graphics.Shaders.Things2D.ApplySettings();
// Determine next lock size
locksize = (group.Value.Count > THING_BUFFER_SIZE) ? THING_BUFFER_SIZE : group.Value.Count;
verts = new FlatVertex[THING_BUFFER_SIZE * 6];
// Go for all things
buffercount = 0;
totalcount = 0;
foreach(Thing t in group.Value)
{
if(t.IsModel && ((General.Settings.GZDrawModelsMode == ModelRenderMode.SELECTION && t.Selected) || (General.Settings.GZDrawModelsMode == ModelRenderMode.ACTIVE_THINGS_FILTER && alpha == 1.0f))) continue;
bool forcespriterendering;
float spritewidth, spriteheight, spritescale;
// Determine sizes
if(t.FixedSize && scale > 1.0f)
foreach(Thing t in group.Value)
{
spritescale = 1.0f;
forcespriterendering = true; // Always render sprite when thing size is affected by FixedSize setting
}
else if(General.Settings.FixedThingsScale && t.Size * scale > FIXED_THING_SIZE)
{
spritescale = FIXED_THING_SIZE / t.Size;
forcespriterendering = true; // Always render sprite when thing size is affected by FixedThingsScale setting
}
else
{
spritescale = scale;
forcespriterendering = false;
}
// Choose which sprite angle to show
int spriteangle = General.ClampAngle(-t.AngleDoom + 270) / 45; // Convert to [0..7] range
// Calculate scaled sprite size
if(sprite.Width > sprite.Height)
{
spritewidth = (t.Size - THING_SPRITE_SHRINK) * spritescale;
spriteheight = spritewidth * ((float)sprite.Height / sprite.Width);
}
else if(sprite.Width < sprite.Height)
{
spriteheight = (t.Size - THING_SPRITE_SHRINK) * spritescale;
spritewidth = spriteheight * ((float)sprite.Width / sprite.Height);
}
else
{
spritewidth = (t.Size - THING_SPRITE_SHRINK) * spritescale;
spriteheight = spritewidth;
}
float spritesize = Math.Max(spritewidth, spriteheight);
if(!forcespriterendering && spritesize < MINIMUM_SPRITE_RADIUS)
{
// Hackish way to tell arrow rendering code to draw bigger arrow...
Vector3D v = thingsByPosition[t];
v.z = -1;
thingsByPosition[t] = v;
// Don't render tiny little sprites
continue;
}
CreateThingSpriteVerts(thingsByPosition[t], spritewidth, spriteheight, ref verts, buffercount * 6, (t.Selected ? selectionColor : 0xFFFFFF));
buffercount++;
totalcount++;
// Buffer filled?
if(buffercount == locksize)
{
// Write to buffer
stream = thingsvertices.Lock(0, locksize * 6 * FlatVertex.Stride, LockFlags.Discard);
stream.WriteRange(verts, 0, buffercount * 6);
thingsvertices.Unlock();
stream.Dispose();
// Draw!
graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, buffercount * 2);
buffercount = 0;
// Determine next lock size
locksize = ((group.Value.Count - totalcount) > THING_BUFFER_SIZE) ? THING_BUFFER_SIZE : (group.Value.Count - totalcount);
// Add to collection
if(!thingsbyangle.ContainsKey(spriteangle)) thingsbyangle.Add(spriteangle, new List<Thing>());
thingsbyangle[spriteangle].Add(t);
}
}
else
{
thingsbyangle[0] = group.Value;
}
// Write to buffer
stream = thingsvertices.Lock(0, locksize * 6 * FlatVertex.Stride, LockFlags.Discard);
if(buffercount > 0) stream.WriteRange(verts, 0, buffercount * 6);
thingsvertices.Unlock();
stream.Dispose();
foreach(KeyValuePair<int, List<Thing>> framegroup in thingsbyangle)
{
SpriteFrameInfo sfi = info.SpriteFrame[framegroup.Key];
ImageData sprite = General.Map.Data.GetSpriteImage(sfi.Sprite);
if(sprite == null) continue;
if(!sprite.IsImageLoaded)
{
sprite.SetUsedInMap(true);
continue;
}
if(sprite.Texture == null) sprite.CreateTexture();
// Draw what's still remaining
if(buffercount > 0)
graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, buffercount * 2);
graphics.Shaders.Things2D.Texture1 = sprite.Texture;
graphics.Shaders.Things2D.ApplySettings();
// Determine next lock size
locksize = (framegroup.Value.Count > THING_BUFFER_SIZE) ? THING_BUFFER_SIZE : framegroup.Value.Count;
verts = new FlatVertex[THING_BUFFER_SIZE * 6];
// Go for all things
buffercount = 0;
totalcount = 0;
foreach(Thing t in framegroup.Value)
{
if(t.IsModel && ((General.Settings.GZDrawModelsMode == ModelRenderMode.SELECTION && t.Selected) || (General.Settings.GZDrawModelsMode == ModelRenderMode.ACTIVE_THINGS_FILTER && alpha == 1.0f)))
continue;
bool forcespriterendering;
float spritewidth, spriteheight, spritescale;
// Determine sizes
if(t.FixedSize && scale > 1.0f)
{
spritescale = 1.0f;
forcespriterendering = true; // Always render sprite when thing size is affected by FixedSize setting
}
else if(General.Settings.FixedThingsScale && t.Size * scale > FIXED_THING_SIZE)
{
spritescale = FIXED_THING_SIZE / t.Size;
forcespriterendering = true; // Always render sprite when thing size is affected by FixedThingsScale setting
}
else
{
spritescale = scale;
forcespriterendering = false;
}
// Calculate scaled sprite size
if(sprite.Width > sprite.Height)
{
spritewidth = (t.Size - THING_SPRITE_SHRINK) * spritescale;
spriteheight = spritewidth * ((float)sprite.Height / sprite.Width);
}
else if(sprite.Width < sprite.Height)
{
spriteheight = (t.Size - THING_SPRITE_SHRINK) * spritescale;
spritewidth = spriteheight * ((float)sprite.Width / sprite.Height);
}
else
{
spritewidth = (t.Size - THING_SPRITE_SHRINK) * spritescale;
spriteheight = spritewidth;
}
float spritesize = Math.Max(spritewidth, spriteheight);
if(!forcespriterendering && spritesize < MINIMUM_SPRITE_RADIUS)
{
// Hackish way to tell arrow rendering code to draw bigger arrow...
Vector3D v = thingsByPosition[t];
v.z = -1;
thingsByPosition[t] = v;
// Don't render tiny little sprites
continue;
}
CreateThingSpriteVerts(thingsByPosition[t], spritewidth, spriteheight, ref verts, buffercount * 6, (t.Selected ? selectionColor : 0xFFFFFF), sfi.Mirror);
buffercount++;
totalcount++;
// Buffer filled?
if(buffercount == locksize)
{
// Write to buffer
stream = thingsvertices.Lock(0, locksize * 6 * FlatVertex.Stride, LockFlags.Discard);
stream.WriteRange(verts, 0, buffercount * 6);
thingsvertices.Unlock();
stream.Dispose();
// Draw!
graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, buffercount * 2);
buffercount = 0;
// Determine next lock size
locksize = ((framegroup.Value.Count - totalcount) > THING_BUFFER_SIZE) ? THING_BUFFER_SIZE : (framegroup.Value.Count - totalcount);
}
}
// Write to buffer
stream = thingsvertices.Lock(0, locksize * 6 * FlatVertex.Stride, LockFlags.Discard);
if(buffercount > 0) stream.WriteRange(verts, 0, buffercount * 6);
thingsvertices.Unlock();
stream.Dispose();
// Draw what's still remaining
if(buffercount > 0) graphics.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, buffercount * 2);
}
}
// Done

View file

@ -144,10 +144,10 @@ namespace CodeImp.DoomBuilder.ZDoom
TextureStructure tx = new TextureStructure(this, "sprite", virtualpath);
if(this.HasError) return false;
// if a limit for the sprite name length is set make sure that it's not exceeded
if(tx.Name.Length > DataManager.CLASIC_IMAGE_NAME_LENGTH)
//mxd. Sprite name length must be either 6 or 8 chars
if(tx.Name.Length != 6 && tx.Name.Length != 8)
{
ReportError("Sprite name \"" + tx.Name + "\" too long. Sprite names must have a length of " + DataManager.CLASIC_IMAGE_NAME_LENGTH + " characters or less");
ReportError("Sprite name \"" + tx.Name + "\" is incorrect. Sprite names must have a length of 6 or 8 characters");
return false;
}