mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-26 13:51:40 +00:00
Added: preview sprites are now generated from voxels. Those are used as previews and when model rendering is disabled.
Fixed: voxel support logic was outdated, which in some cases resulted in voxels not being loaded and in some weird behaviour when trying to change pitch/roll of associated things. Updated sprites lookup logic. Now it correctly handles sprites named like NNNNA0B0. Fixed: in some cases DECORATE parser was unable to correctly detect sprite name & frame(s) block. Updated ZDoom_ACS.cfg. Updated ZDoom_DECORATE.cfg.
This commit is contained in:
parent
3677221efa
commit
ec9c3a71f9
25 changed files with 720 additions and 277 deletions
|
@ -1426,14 +1426,14 @@ enums_strife
|
|||
1 = "Base Key (Front)";
|
||||
2 = "Governor's Key";
|
||||
3 = "Travel Passcard";
|
||||
4 = "Blue ID Badge";
|
||||
4 = "ID Badge";
|
||||
5 = "Prison Key";
|
||||
6 = "Severed Hand";
|
||||
7 = "Power Key 1";
|
||||
8 = "Power Key 2";
|
||||
9 = "Power Key 3";
|
||||
10 = "Gold Key";
|
||||
11 = "Gold ID Badge";
|
||||
11 = "ID Card";
|
||||
12 = "Silver Key";
|
||||
13 = "Oracle Key";
|
||||
14 = "Military ID";
|
||||
|
|
|
@ -310,7 +310,7 @@ keywords
|
|||
PolyWait = "void PolyWait(int polyid)";
|
||||
Print = "void Print(type:expression)\nPrint will print something to the screen.\nPrint will only display for the activator of the script\nFor printing to all players, use PrintBold.";
|
||||
PrintBold = "void PrintBold(type:expression)\nThis is exactly the same as Print, except all players will see the printed text\non the screen instead of just the activator of the script.";
|
||||
QuakeEx = "bool QuakeEx(int tid, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, str sound[, int flags = 0[, float mulwavex = 1.0[, float mulwavey = 1.0[, float mulwavez = 1.0[, int falloff = 0[, int highpoint = 0]]]]]])";
|
||||
QuakeEx = "bool QuakeEx(int tid, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, str sound[, int flags = 0[, float mulwavex = 1.0[, float mulwavey = 1.0[, float mulwavez = 1.0[, int falloff = 0[, int highpoint = 0[, float rollintensity = 0.0[, float rollwave = 0.0]]]]]]]])";
|
||||
Radius_Quake = "Radius_Quake(intensity, duration, damrad, tremrad, tid)";
|
||||
Radius_Quake2 = "void Radius_Quake2(int tid, int intensity, int duration, int damrad, int tremrad, str sound)";
|
||||
Random = "int Random(int min, int max)";
|
||||
|
|
|
@ -128,6 +128,7 @@ keywords
|
|||
A_BulletAttack = "A_BulletAttack";
|
||||
A_MonsterRail = "A_MonsterRail";
|
||||
A_Explode = "A_Explode[(int explosiondamage = 128[, int explosionradius = 128[, int flags = XF_HURTSOURCE[, bool alert = false[, int fulldamageradius = 0[, int nails = 0[, int naildamage = 10[, str pufftype = \"BulletPuff\"]]]]]]])]";
|
||||
A_RadiusDamageSelf = "[(int damage = 128[, float distance = 128.0[, int flags = 0[, str flashtype = \"None\"]]])]\nflags: RDSF flags";
|
||||
A_RadiusThrust = "A_RadiusThrust(int force, int distance[, int flags[, int fullthrustdistance]])";
|
||||
A_Detonate = "A_Detonate";
|
||||
A_ThrowGrenade = "bool A_ThrowGrenade(str spawntype[, float spawnheight[, float throwspeed_horz[, float throwspeed_vert[, bool useammo]]]])";
|
||||
|
@ -176,7 +177,7 @@ keywords
|
|||
A_SpawnDebris = "A_SpawnDebris(str type[, bool translation = false[, float horizontal_vel = 1.0[, float vertical_vel = 1.0]]])";
|
||||
A_SpawnItem = "bool A_SpawnItem(str type, int distance, float zpos, bool useammo, bool translation)";
|
||||
A_SpawnItemEx = "bool A_SpawnItemEx(str type[, float xoffset = 0.0[, float yoffset = 0.0[, float zoffset = 0.0[, float xvelocity = 0.0[, float yvelocity = 0.0[, float zvelocity = 0.0[, float angle = 0.0[, int flags = 0[, int skipchance = 0[, int tid = 0]]]]]]]]]])";
|
||||
A_SpawnParticle = "A_SpawnParticle(color color[, int flags = 0[, int lifetime = 35[, int size = 1[, float angle = 0.0[, float xoff = 0.0[, float yoff = 0.0[, float zoff = 0.0[, float velx = 0.0[, float vely = 0.0[, float velz = 0.0[, float accelx = 0.0[, float accely = 0.0[, float accelz = 0.0[, float startalpha = 1.0[, float fadestep = -1.0]]]]]]]]]]]]]]])";
|
||||
A_SpawnParticle = "A_SpawnParticle(color color[, int flags = 0[, int lifetime = 35[, float size = 1.0[, float angle = 0.0[, float xoff = 0.0[, float yoff = 0.0[, float zoff = 0.0[, float velx = 0.0[, float vely = 0.0[, float velz = 0.0[, float accelx = 0.0[, float accely = 0.0[, float accelz = 0.0[, float startalpha = 1.0[, float fadestep = -1.0[, float sizestep = 0.0]]]]]]]]]]]]]]]])";
|
||||
//State jumps
|
||||
A_CheckBlock = "state A_CheckBlock(str block[, int flags = 0[, int pointer = AAPTR_TARGET[, float xoff = 0.0[, float yoff = 0.0[, float zoff = 0.0[, float angle = 0.0]]]]]])";
|
||||
A_CheckCeiling = "state A_CheckCeiling(str state)\nstate A_CheckCeiling(int offset)";
|
||||
|
@ -307,9 +308,9 @@ keywords
|
|||
A_Light1 = "A_Light1";
|
||||
A_Light2 = "A_Light2";
|
||||
A_LightInverse = "A_LightInverse";
|
||||
A_Overlay = "bool A_Overlay(int layer[, state start[, bool nooverride]])";
|
||||
A_Overlay = "bool A_Overlay(int layer[, state start = \"\"[, bool nooverride = false]])";
|
||||
A_OverlayFlags = "A_OverlayFlags(int layer, int flags, bool set)\nflags: PSPF flags.";
|
||||
A_OverlayOffset = "A_OverlayOffset[(int layer = 0[, float x = 0.0f[, float y = 32.0f[, int flags = 0]]]])]\nflags: WOF flags.";
|
||||
A_OverlayOffset = "A_OverlayOffset[(int layer = 0[, float x = 0.0[, float y = 32.0[, int flags = 0]]]])]\nflags: WOF flags.";
|
||||
A_Recoil = "A_Recoil(float force)";
|
||||
A_ZoomFactor = "A_ZoomFactor[(float zoom = 1.0[, int flags = 0])]\nflags: ZOOM flags.";
|
||||
A_SetCrosshair = "A_SetCrosshair(int number)";
|
||||
|
@ -378,7 +379,7 @@ keywords
|
|||
A_SkelWhoosh = "A_SkelWhoosh";
|
||||
A_StartFire = "A_StartFire";
|
||||
A_FireCrackle = "A_FireCrackle";
|
||||
A_BFGSpray = "A_BFGSpray[(str flashtype = \"BFGExtra\"[, int numrays = 40[, int damagecnt = 15[, float angle = 90.0[, float distance = 1024.0[, float vrange = 32.0[, int explicit_damage = 0]]]]]])]";
|
||||
A_BFGSpray = "A_BFGSpray[(str flashtype = \"BFGExtra\"[, int numrays = 40[, int damagecnt = 15[, float angle = 90.0[, float distance = 1024.0[, float vrange = 32.0[, int explicit_damage = 0[, int flags = 0]]]]]]])]\nflags: BFGR flags.";
|
||||
A_BarrelDestroy = "A_BarrelDestroy";
|
||||
//Miscellaneous functions not listed in the "Action functions" wiki article
|
||||
A_Bang4Cloud = "A_Bang4Cloud";
|
||||
|
@ -1369,4 +1370,9 @@ constants
|
|||
PSPF_ADDBOB;
|
||||
PSPF_POWDOUBLE;
|
||||
PSPF_CVARFAST;
|
||||
//A_BFGSpray flags
|
||||
BFGF_MISSILEORIGIN;
|
||||
BFGF_HURTSOURCE;
|
||||
//A_RadiusDamageSelf flags
|
||||
RDSF_BFGDAMAGE;
|
||||
}
|
||||
|
|
|
@ -705,7 +705,7 @@
|
|||
<Compile Include="Actions\HintsManager.cs" />
|
||||
<Compile Include="Config\AllTexturesSet.cs" />
|
||||
<Compile Include="Config\FlagTranslation.cs" />
|
||||
<Compile Include="Config\RenderModeEnums.cs" />
|
||||
<Compile Include="Rendering\RenderModeEnums.cs" />
|
||||
<Compile Include="Config\PasteOptions.cs" />
|
||||
<Compile Include="Config\ScriptDocumentSettings.cs" />
|
||||
<Compile Include="Config\SectorEffectData.cs" />
|
||||
|
@ -825,6 +825,7 @@
|
|||
<Compile Include="Data\PK3FileImage.cs" />
|
||||
<Compile Include="Data\PK3StructuredReader.cs" />
|
||||
<Compile Include="Data\DynamicBitmapImage.cs" />
|
||||
<Compile Include="Data\VoxelImage.cs" />
|
||||
<Compile Include="Editing\CustomThingsFilter.cs" />
|
||||
<Compile Include="General\CRC.cs" />
|
||||
<Compile Include="General\ErrorItem.cs" />
|
||||
|
|
|
@ -24,6 +24,7 @@ using CodeImp.DoomBuilder.Data;
|
|||
using CodeImp.DoomBuilder.GZBuilder.Data;
|
||||
using CodeImp.DoomBuilder.IO;
|
||||
using CodeImp.DoomBuilder.Map;
|
||||
using CodeImp.DoomBuilder.Rendering;
|
||||
using CodeImp.DoomBuilder.ZDoom;
|
||||
|
||||
#endregion
|
||||
|
@ -459,9 +460,8 @@ namespace CodeImp.DoomBuilder.Config
|
|||
|
||||
// Set sprite
|
||||
StateStructure.FrameInfo info = actor.FindSuitableSprite(); //mxd
|
||||
string suitablesprite = (locksprite ? string.Empty : info.Sprite); //mxd
|
||||
if(!string.IsNullOrEmpty(suitablesprite))
|
||||
sprite = suitablesprite;
|
||||
if(!locksprite && !string.IsNullOrEmpty(info.Sprite)) //mxd. Added locksprite property
|
||||
sprite = info.Sprite;
|
||||
else if(string.IsNullOrEmpty(sprite))//mxd
|
||||
sprite = DataManager.INTERNAL_PREFIX + "unknownthing";
|
||||
|
||||
|
@ -548,42 +548,121 @@ 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()
|
||||
//mxd. This tries to find all possible sprite rotations. Returns true when voxel substitute exists
|
||||
internal bool SetupSpriteFrame(HashSet<string> allspritenames, HashSet<string> allvoxelnames)
|
||||
{
|
||||
// Empty or internal sprites don't have rotations
|
||||
if(string.IsNullOrEmpty(sprite) || sprite.StartsWith(DataManager.INTERNAL_PREFIX)) return;
|
||||
// Empty, invalid or internal sprites don't have rotations
|
||||
// Info: we can have either partial 5-char sprite name from DECORATE parser,
|
||||
// or fully defined 6/8-char sprite name defined in Game configuration or by $Sprite property
|
||||
if(string.IsNullOrEmpty(sprite) || sprite.StartsWith(DataManager.INTERNAL_PREFIX)
|
||||
|| (sprite.Length != 5 && sprite.Length != 6 && sprite.Length != 8)) return false;
|
||||
|
||||
// Skip sprites with strange names
|
||||
if(sprite.Length != 6 && sprite.Length != 8) return;
|
||||
string sourcename = sprite.Substring(0, 4);
|
||||
char sourceframe = sprite[4];
|
||||
|
||||
// First try voxels
|
||||
if(allvoxelnames.Count > 0)
|
||||
{
|
||||
// Find a voxel, which matches sourcename
|
||||
HashSet<string> voxelnames = new HashSet<string>();
|
||||
foreach(string s in allvoxelnames)
|
||||
{
|
||||
if(s.StartsWith(sourcename)) voxelnames.Add(s);
|
||||
}
|
||||
|
||||
// Find a voxel, which matches baseframe
|
||||
// Valid voxel can be either 4-char (POSS), 5-char (POSSA) or 6-char (POSSA0)
|
||||
string newsprite = string.Empty;
|
||||
|
||||
// Check 6-char voxels...
|
||||
foreach(string v in voxelnames)
|
||||
{
|
||||
if(v.Length == 6 && v.StartsWith(sourcename + sourceframe) && WADReader.IsValidSpriteName(v))
|
||||
{
|
||||
newsprite = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check 5-char voxels...
|
||||
if(voxelnames.Contains(sourcename + sourceframe)) newsprite = sourcename + sourceframe;
|
||||
|
||||
// Check 4-char voxels...
|
||||
if(voxelnames.Contains(sourcename)) newsprite = sourcename;
|
||||
|
||||
// Voxel found?
|
||||
if(!string.IsNullOrEmpty(newsprite))
|
||||
{
|
||||
// Assign new sprite
|
||||
sprite = newsprite;
|
||||
|
||||
// Recreate sprite frame
|
||||
spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } };
|
||||
|
||||
// Substitute voxel found
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Then try sprites
|
||||
// Find a sprite, which matches sourcename
|
||||
string sourcesprite = string.Empty;
|
||||
HashSet<string> spritenames = new HashSet<string>();
|
||||
foreach(string s in allspritenames)
|
||||
{
|
||||
if(s.StartsWith(sourcename)) spritenames.Add(s);
|
||||
}
|
||||
|
||||
// Find a sprite, which matches baseframe
|
||||
foreach(string s in spritenames)
|
||||
{
|
||||
if(s[4] == sourceframe || (s.Length == 8 && s[6] == sourceframe))
|
||||
{
|
||||
sourcesprite = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Abort if no sprite was found
|
||||
if(string.IsNullOrEmpty(sourcesprite)) return false;
|
||||
|
||||
// Get sprite angle
|
||||
string anglestr = sprite.Substring(5, 1);
|
||||
string anglestr = sourcesprite.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;
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ". Unable to get sprite angle from sprite \"" + sourcesprite + "\"");
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ", sprite \"" + sourcesprite + "\". Sprite angle must be in [0..8] range");
|
||||
return false;
|
||||
}
|
||||
|
||||
// No rotations?
|
||||
if(sourceangle == 0) return;
|
||||
// No rotations? Then spriteframe is already setup
|
||||
if(sourceangle == 0)
|
||||
{
|
||||
// Sprite name still incomplete?
|
||||
if(sprite.Length < 6)
|
||||
{
|
||||
sprite = sourcesprite;
|
||||
|
||||
// Recreate sprite frame. Mirror the sprite if sourceframe matches the second frame block
|
||||
spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true),
|
||||
Mirror = (sprite.Length == 8 && sprite[6] == sourceframe) } };
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -596,7 +675,7 @@ namespace CodeImp.DoomBuilder.Config
|
|||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sanity checks
|
||||
|
@ -610,7 +689,7 @@ namespace CodeImp.DoomBuilder.Config
|
|||
if(targetangle < 1 || targetangle > 8)
|
||||
{
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ", sprite \"" + s + "\". Expected sprite angle in [1..8] range");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Even more sanity checks
|
||||
|
@ -640,7 +719,7 @@ namespace CodeImp.DoomBuilder.Config
|
|||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sanity checks
|
||||
|
@ -654,7 +733,7 @@ namespace CodeImp.DoomBuilder.Config
|
|||
if(targetangle < 1 || targetangle > 8)
|
||||
{
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ", sprite \"" + s + "\". Expected sprite angle in [1..8] range");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Even more sanity checks
|
||||
|
@ -697,7 +776,7 @@ namespace CodeImp.DoomBuilder.Config
|
|||
}
|
||||
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Error in actor \"" + title + "\":" + index + ". Sprite rotations " + ma + " for sprite " + sourcename + ", frame " + sourceframe + " are missing");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create collection
|
||||
|
@ -706,6 +785,12 @@ namespace CodeImp.DoomBuilder.Config
|
|||
{
|
||||
spriteframe[i] = new SpriteFrameInfo { Sprite = frames[i], SpriteLongName = Lump.MakeLongName(frames[i]), Mirror = mirror[i] };
|
||||
}
|
||||
|
||||
// Update preview sprite
|
||||
sprite = spriteframe[1].Sprite;
|
||||
|
||||
// Done
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is used for sorting
|
||||
|
|
|
@ -1499,6 +1499,22 @@ namespace CodeImp.DoomBuilder.Data
|
|||
// This loads the sprites that we really need for things
|
||||
private int LoadThingSprites()
|
||||
{
|
||||
//mxd. Get all sprite names
|
||||
HashSet<string> spritenames = new HashSet<string>(StringComparer.Ordinal);
|
||||
foreach(DataReader dr in containers)
|
||||
{
|
||||
IEnumerable<string> result = dr.GetSpriteNames();
|
||||
if(result != null) spritenames.UnionWith(result);
|
||||
}
|
||||
|
||||
//mxd. Get names of all voxel models, which can be used "as is" (these do not require corresponding sprite to work)
|
||||
HashSet<string> voxelnames = new HashSet<string>(StringComparer.Ordinal);
|
||||
foreach(DataReader dr in containers)
|
||||
{
|
||||
IEnumerable<string> result = dr.GetVoxelNames();
|
||||
if(result != null) voxelnames.UnionWith(result);
|
||||
}
|
||||
|
||||
// Go for all things
|
||||
foreach(ThingTypeInfo ti in General.Map.Data.ThingTypes)
|
||||
{
|
||||
|
@ -1506,9 +1522,26 @@ namespace CodeImp.DoomBuilder.Data
|
|||
if(ti.Sprite.Length == 0 || ti.Sprite.Length > CLASIC_IMAGE_NAME_LENGTH) continue; //mxd
|
||||
|
||||
//mxd. Find all sprite angles
|
||||
ti.SetupSpriteFrame();
|
||||
bool isvoxel = ti.SetupSpriteFrame(spritenames, voxelnames);
|
||||
|
||||
//mxd. Load them all
|
||||
//mxd. Create voxel sprite?
|
||||
if(isvoxel)
|
||||
{
|
||||
if(!sprites.ContainsKey(Lump.MakeLongName(ti.Sprite)))
|
||||
{
|
||||
// Make new voxel image
|
||||
VoxelImage image = new VoxelImage(ti.Sprite, ti.Sprite);
|
||||
|
||||
// Add to collection
|
||||
sprites.Add(image.LongName, image);
|
||||
|
||||
// Add to preview manager
|
||||
previews.AddImage(image);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//mxd. Load all sprites
|
||||
foreach(SpriteFrameInfo info in ti.SpriteFrame)
|
||||
{
|
||||
ImageData image = null;
|
||||
|
@ -1522,7 +1555,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
{
|
||||
for(int i = containers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// This contain provides this sprite?
|
||||
// This container provides this sprite?
|
||||
if(containers[i].GetSpriteExists(info.Sprite))
|
||||
{
|
||||
spritefound = true;
|
||||
|
@ -1553,6 +1586,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
if(image != null) previews.AddImage(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output info
|
||||
return sprites.Count;
|
||||
|
@ -1566,7 +1600,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
// Go for all opened containers
|
||||
for(int i = containers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// This contain provides this sprite?
|
||||
// This container provides this sprite?
|
||||
Stream spritedata = containers[i].GetSpriteData(pname, ref spritelocation);
|
||||
if(spritedata != null) return spritedata;
|
||||
}
|
||||
|
@ -1719,18 +1753,40 @@ namespace CodeImp.DoomBuilder.Data
|
|||
}
|
||||
|
||||
//mxd. Returns all sprite names, which start with given string
|
||||
internal IEnumerable<string> GetSpriteNames(string startswith)
|
||||
internal IEnumerable<string> GetSpriteNames()
|
||||
{
|
||||
HashSet<string> result = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach(DataReader reader in containers)
|
||||
result.UnionWith(reader.GetSpriteNames(startswith));
|
||||
result.UnionWith(reader.GetSpriteNames());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== mxd. Voxels
|
||||
|
||||
// This returns a specific voxel stream
|
||||
internal Stream GetVoxelData(string pname, ref string voxellocation)
|
||||
{
|
||||
if(!string.IsNullOrEmpty(pname))
|
||||
{
|
||||
// Go for all opened containers
|
||||
for(int i = containers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// This container provides this sprite?
|
||||
Stream spritedata = containers[i].GetVoxelData(pname, ref voxellocation);
|
||||
if(spritedata != null) return spritedata;
|
||||
}
|
||||
}
|
||||
|
||||
// No such voxel found
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Things
|
||||
|
||||
// This loads the things from Decorate
|
||||
|
@ -2210,43 +2266,14 @@ namespace CodeImp.DoomBuilder.Data
|
|||
// Bail out when not supported by current game configuration
|
||||
if(string.IsNullOrEmpty(General.Map.Config.DecorateGames)) return;
|
||||
|
||||
// Get names of all voxel models, which can be used "as is"
|
||||
HashSet<string> voxelnames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach(DataReader dr in containers)
|
||||
{
|
||||
currentreader = dr;
|
||||
|
||||
IEnumerable<string> result = dr.GetVoxelNames();
|
||||
if(result == null) continue;
|
||||
|
||||
foreach(string s in result)
|
||||
{
|
||||
if(!voxelnames.Contains(s)) voxelnames.Add(s);
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, List<int>> sprites = new Dictionary<string, List<int>>(StringComparer.Ordinal);
|
||||
|
||||
// Go for all things
|
||||
Dictionary<string, List<int>> allsprites = new Dictionary<string, List<int>>(StringComparer.Ordinal);
|
||||
foreach(ThingTypeInfo ti in thingtypes.Values)
|
||||
{
|
||||
// Valid sprite name?
|
||||
string sprite;
|
||||
|
||||
if(ti.Sprite.Length == 0 || ti.Sprite.Length > CLASIC_IMAGE_NAME_LENGTH)
|
||||
{
|
||||
if(ti.Actor == null) continue;
|
||||
sprite = ti.Actor.FindSuitableVoxel(voxelnames);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite = ti.Sprite;
|
||||
}
|
||||
|
||||
if(string.IsNullOrEmpty(sprite)) continue;
|
||||
if(!sprites.ContainsKey(sprite)) sprites.Add(sprite, new List<int>());
|
||||
sprites[sprite].Add(ti.Index);
|
||||
if(string.IsNullOrEmpty(ti.Sprite) || ti.Sprite.Length > CLASIC_IMAGE_NAME_LENGTH) continue;
|
||||
if(!allsprites.ContainsKey(ti.Sprite)) allsprites.Add(ti.Sprite, new List<int>());
|
||||
allsprites[ti.Sprite].Add(ti.Index);
|
||||
}
|
||||
|
||||
VoxeldefParser parser = new VoxeldefParser();
|
||||
|
@ -2264,13 +2291,34 @@ namespace CodeImp.DoomBuilder.Data
|
|||
{
|
||||
foreach(KeyValuePair<string, ModelData> entry in parser.Entries)
|
||||
{
|
||||
foreach(KeyValuePair<string, List<int>> sc in sprites)
|
||||
foreach(KeyValuePair<string, List<int>> sc in allsprites)
|
||||
{
|
||||
if(sc.Key.Contains(entry.Key))
|
||||
if(sc.Key.StartsWith(entry.Key, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
foreach(int id in sc.Value)
|
||||
modeldefentries[id] = entry.Value;
|
||||
processed.Add(entry.Key);
|
||||
foreach(int id in sc.Value) modeldefentries[id] = entry.Value;
|
||||
processed.Add(sc.Key);
|
||||
|
||||
// Create preview image if it doesn't exist...
|
||||
ImageData sprite = GetSpriteImage(sc.Key);
|
||||
if(sprite == null)
|
||||
{
|
||||
// Make new voxel image
|
||||
sprite = new VoxelImage(sc.Key, entry.Value.ModelNames[0]);
|
||||
|
||||
// Add to collection
|
||||
sprites.Add(sprite.LongName, sprite);
|
||||
|
||||
// Add to preview manager
|
||||
previews.AddImage(sprite);
|
||||
}
|
||||
|
||||
// Apply VOXELDEF settings to the preview image...
|
||||
VoxelImage vi = sprite as VoxelImage;
|
||||
if(vi != null)
|
||||
{
|
||||
vi.AngleOffset = (int)Math.Round(entry.Value.AngleOffset);
|
||||
vi.OverridePalette = entry.Value.OverridePalette;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2286,19 +2334,18 @@ namespace CodeImp.DoomBuilder.Data
|
|||
currentreader = null;
|
||||
|
||||
// Get voxel models
|
||||
foreach(string voxelname in voxelnames)
|
||||
foreach(KeyValuePair<string, List<int>> sc in allsprites)
|
||||
{
|
||||
if(processed.Contains(voxelname)) continue;
|
||||
foreach(KeyValuePair<string, List<int>> sc in sprites)
|
||||
{
|
||||
if(sc.Key.Contains(voxelname))
|
||||
if(processed.Contains(sc.Key)) continue;
|
||||
|
||||
VoxelImage vi = GetSpriteImage(sc.Key) as VoxelImage;
|
||||
if(vi != null)
|
||||
{
|
||||
// It's a model without a definition, and it corresponds to a sprite we can display, so let's add it
|
||||
ModelData data = new ModelData { IsVoxel = true };
|
||||
data.ModelNames.Add(voxelname);
|
||||
data.ModelNames.Add(vi.VoxelName);
|
||||
|
||||
foreach(int id in sprites[sc.Key]) modeldefentries[id] = data;
|
||||
}
|
||||
foreach(int id in sc.Value) modeldefentries[id] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,8 +86,6 @@ namespace CodeImp.DoomBuilder.Data
|
|||
{
|
||||
#region ================== Constants
|
||||
|
||||
protected const string SPRITE_NAME_PATTERN = "(?i)\\A[a-z0-9]{4}([a-z][0-9]{0,2})$"; //mxd
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Variables
|
||||
|
@ -215,7 +213,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
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);
|
||||
public abstract HashSet<string> GetSpriteNames();
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -258,10 +256,10 @@ namespace CodeImp.DoomBuilder.Data
|
|||
public abstract IEnumerable<TextResourceData> GetCvarInfoData();
|
||||
|
||||
//mxd. When implemented, this returns the list of voxel model names
|
||||
public abstract IEnumerable<string> GetVoxelNames();
|
||||
public abstract HashSet<string> GetVoxelNames();
|
||||
|
||||
//mxd. When implemented, this returns the voxel lump
|
||||
public abstract Stream GetVoxelData(string name);
|
||||
public abstract Stream GetVoxelData(string name, ref string voxellocation);
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
|
@ -343,7 +343,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
#region ================== Voxels (mxd)
|
||||
|
||||
//mxd. This finds and returns a voxel stream
|
||||
public override Stream GetVoxelData(string name)
|
||||
public override Stream GetVoxelData(string name, ref string voxellocation)
|
||||
{
|
||||
// Error when suspended
|
||||
if(issuspended) throw new Exception("Data reader is suspended");
|
||||
|
@ -351,7 +351,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
// Find in any of the wad files
|
||||
for(int i = wads.Count - 1; i >= 0; i--)
|
||||
{
|
||||
Stream voxel = wads[i].GetVoxelData(name);
|
||||
Stream voxel = wads[i].GetVoxelData(name, ref voxellocation);
|
||||
if(voxel != null) return voxel;
|
||||
}
|
||||
|
||||
|
@ -362,6 +362,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
string filename = FindFirstFile(path, Path.GetFileName(name), true);
|
||||
if((filename != null) && FileExists(filename))
|
||||
{
|
||||
voxellocation = location.GetDisplayName();
|
||||
return LoadFile(filename);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -359,7 +359,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
#region ================== Voxels (mxd)
|
||||
|
||||
//mxd. This finds and returns a voxel stream or null if no voxel was found
|
||||
public override Stream GetVoxelData(string name)
|
||||
public override Stream GetVoxelData(string name, ref string voxellocation)
|
||||
{
|
||||
// Error when suspended
|
||||
if(issuspended) throw new Exception("Data reader is suspended");
|
||||
|
@ -367,7 +367,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
// Find in any of the wad files
|
||||
for(int i = wads.Count - 1; i >= 0; i--)
|
||||
{
|
||||
Stream voxel = wads[i].GetVoxelData(name);
|
||||
Stream voxel = wads[i].GetVoxelData(name, ref voxellocation);
|
||||
if(voxel != null) return voxel;
|
||||
}
|
||||
|
||||
|
@ -377,6 +377,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
string filename = FindFirstFile(VOXELS_DIR, pfilename, true);
|
||||
if((filename != null) && FileExists(filename))
|
||||
{
|
||||
voxellocation = location.GetDisplayName();
|
||||
return LoadFile(filename);
|
||||
}
|
||||
|
||||
|
|
|
@ -422,8 +422,8 @@ 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)
|
||||
//mxd. This returns all sprite names
|
||||
public override HashSet<string> GetSpriteNames()
|
||||
{
|
||||
// Error when suspended
|
||||
if(issuspended) throw new Exception("Data reader is suspended");
|
||||
|
@ -434,11 +434,11 @@ namespace CodeImp.DoomBuilder.Data
|
|||
// 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));
|
||||
result.UnionWith(wads[i].GetSpriteNames());
|
||||
}
|
||||
|
||||
// Load from out own files
|
||||
string[] files = GetAllFilesWhichTitleStartsWith(SPRITES_DIR, startswith, true);
|
||||
string[] files = GetAllFiles(SPRITES_DIR, true);
|
||||
foreach(string file in files)
|
||||
{
|
||||
// Some users tend to place all manner of graphics into the "Sprites" folder...
|
||||
|
@ -553,22 +553,29 @@ namespace CodeImp.DoomBuilder.Data
|
|||
#region ================== VOXELDEF (mxd)
|
||||
|
||||
//mxd. This returns the list of voxels, which can be used without VOXELDEF definition
|
||||
public override IEnumerable<string> GetVoxelNames()
|
||||
public override HashSet<string> GetVoxelNames()
|
||||
{
|
||||
// Error when suspended
|
||||
if(issuspended) throw new Exception("Data reader is suspended");
|
||||
|
||||
string[] files = GetAllFiles("voxels", false);
|
||||
List<string> voxels = new List<string>();
|
||||
Regex spritename = new Regex(SPRITE_NAME_PATTERN);
|
||||
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].GetVoxelNames());
|
||||
}
|
||||
|
||||
// Load from out own files
|
||||
string[] files = GetAllFiles("voxels", false);
|
||||
foreach(string t in files)
|
||||
{
|
||||
string s = Path.GetFileNameWithoutExtension(t).ToUpperInvariant();
|
||||
if(spritename.IsMatch(s)) voxels.Add(s);
|
||||
if(WADReader.IsValidVoxelName(s)) result.Add(s);
|
||||
}
|
||||
|
||||
return voxels.ToArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
//mxd
|
||||
|
|
|
@ -17,16 +17,22 @@
|
|||
#region ================== Namespaces
|
||||
|
||||
using System;
|
||||
using CodeImp.DoomBuilder.IO;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using CodeImp.DoomBuilder.IO;
|
||||
using CodeImp.DoomBuilder.Windows;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CodeImp.DoomBuilder.Data
|
||||
{
|
||||
public sealed class SpriteImage : ImageData
|
||||
public interface ISpriteImage //mxd
|
||||
{
|
||||
int OffsetX { get; }
|
||||
int OffsetY { get; }
|
||||
}
|
||||
|
||||
public sealed class SpriteImage : ImageData, ISpriteImage
|
||||
{
|
||||
#region ================== Variables
|
||||
|
||||
|
|
325
Source/Core/Data/VoxelImage.cs
Normal file
325
Source/Core/Data/VoxelImage.cs
Normal file
|
@ -0,0 +1,325 @@
|
|||
#region ================== Namespaces
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using CodeImp.DoomBuilder.Rendering;
|
||||
using CodeImp.DoomBuilder.Windows;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CodeImp.DoomBuilder.Data
|
||||
{
|
||||
public sealed class VoxelImage : ImageData, ISpriteImage
|
||||
{
|
||||
#region ================== Variables
|
||||
|
||||
private int offsetx;
|
||||
private int offsety;
|
||||
private readonly string voxelname;
|
||||
private bool overridepalette;
|
||||
private int angleoffset;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Properties
|
||||
|
||||
public int OffsetX { get { return offsetx; } }
|
||||
public int OffsetY { get { return offsety; } }
|
||||
public string VoxelName { get { return voxelname; } }
|
||||
public bool OverridePalette { get { return overridepalette; } internal set { overridepalette = value; } }
|
||||
public int AngleOffset { get { return angleoffset; } internal set { angleoffset = value; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Constructor / Disposer
|
||||
|
||||
// Constructor
|
||||
internal VoxelImage(string name, string voxelname)
|
||||
{
|
||||
// Initialize
|
||||
SetName(name);
|
||||
this.voxelname = voxelname;
|
||||
|
||||
// We have no destructor
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Methods
|
||||
|
||||
override public void LoadImage()
|
||||
{
|
||||
// Do the loading
|
||||
LocalLoadImage();
|
||||
|
||||
// Notify the main thread about the change to redraw display
|
||||
IntPtr strptr = Marshal.StringToCoTaskMemAuto(this.Name);
|
||||
General.SendMessage(General.MainWindow.Handle, (int)MainForm.ThreadMessages.SpriteDataLoaded, strptr.ToInt32(), 0);
|
||||
}
|
||||
|
||||
// This loads the image
|
||||
protected unsafe override void LocalLoadImage()
|
||||
{
|
||||
// Leave when already loaded
|
||||
if(this.IsImageLoaded) return;
|
||||
|
||||
lock(this)
|
||||
{
|
||||
// Get the lump data stream
|
||||
string voxellocation = string.Empty; //mxd
|
||||
Stream lumpdata = General.Map.Data.GetVoxelData(voxelname, ref voxellocation);
|
||||
if(lumpdata != null)
|
||||
{
|
||||
// Copy lump data to memory
|
||||
lumpdata.Seek(0, SeekOrigin.Begin);
|
||||
byte[] membytes = new byte[(int)lumpdata.Length];
|
||||
lumpdata.Read(membytes, 0, (int)lumpdata.Length);
|
||||
|
||||
using(MemoryStream mem = new MemoryStream(membytes))
|
||||
{
|
||||
mem.Seek(0, SeekOrigin.Begin);
|
||||
PixelColor[] palette = new PixelColor[256];
|
||||
|
||||
// Create front projection image from the KVX
|
||||
using(BinaryReader reader = new BinaryReader(mem, Encoding.ASCII))
|
||||
{
|
||||
reader.ReadInt32(); //numbytes, we don't use that
|
||||
int xsize = reader.ReadInt32();
|
||||
int ysize = reader.ReadInt32();
|
||||
int zsize = reader.ReadInt32();
|
||||
|
||||
// Sanity check
|
||||
if(xsize == 0 || ysize == 0 || zsize == 0)
|
||||
{
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Cannot create sprite image for voxel \"" + Path.Combine(voxellocation, voxelname)
|
||||
+ "\" for voxel drawing: voxel has invalid size (width: " + xsize + ", height: " + zsize + ", depth: " + ysize);
|
||||
loadfailed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int pivotx = (int)Math.Round(reader.ReadInt32() / 256f);
|
||||
int pivoty = (int)Math.Round(reader.ReadInt32() / 256f);
|
||||
int pivotz = (int)Math.Round(reader.ReadInt32() / 256f);
|
||||
|
||||
// Read offsets
|
||||
int[] xoffset = new int[xsize + 1]; // why is it xsize + 1, not xsize?..
|
||||
short[,] xyoffset = new short[xsize, ysize + 1]; // why is it ysize + 1, not ysize?..
|
||||
|
||||
for(int i = 0; i < xoffset.Length; i++)
|
||||
{
|
||||
xoffset[i] = reader.ReadInt32();
|
||||
}
|
||||
|
||||
for(int x = 0; x < xsize; x++)
|
||||
{
|
||||
for(int y = 0; y < ysize + 1; y++)
|
||||
{
|
||||
xyoffset[x, y] = reader.ReadInt16();
|
||||
}
|
||||
}
|
||||
|
||||
// Read slabs
|
||||
List<int> offsets = new List<int>(xsize * ysize);
|
||||
for(int x = 0; x < xsize; x++)
|
||||
{
|
||||
for(int y = 0; y < ysize; y++)
|
||||
{
|
||||
offsets.Add(xoffset[x] + xyoffset[x, y] + 28); // for some reason offsets are counted from start of xoffset[]...
|
||||
}
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
int slabsend = (int)(reader.BaseStream.Length - 768);
|
||||
|
||||
// Read palette
|
||||
if(!overridepalette)
|
||||
{
|
||||
reader.BaseStream.Position = slabsend;
|
||||
for(int i = 0; i < 256; i++)
|
||||
{
|
||||
byte r = (byte)(reader.ReadByte() * 4);
|
||||
byte g = (byte)(reader.ReadByte() * 4);
|
||||
byte b = (byte)(reader.ReadByte() * 4);
|
||||
palette[i] = new PixelColor(255, r, g, b);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 256; i++) palette[i] = General.Map.Data.Palette[i];
|
||||
}
|
||||
|
||||
// Populate projection pixels array
|
||||
int imgwidth, imgheight, imgoffsetx;
|
||||
bool checkalpha = false;
|
||||
|
||||
// Convert angleoffsets to the nearest cardinal direction...
|
||||
angleoffset = General.ClampAngle((angleoffset + 45) / 90 * 90);
|
||||
|
||||
switch(angleoffset)
|
||||
{
|
||||
case 0:
|
||||
imgwidth = xsize;
|
||||
imgheight = zsize;
|
||||
imgoffsetx = pivotx;
|
||||
break;
|
||||
|
||||
case 90:
|
||||
imgwidth = ysize;
|
||||
imgheight = zsize;
|
||||
imgoffsetx = imgwidth - pivoty;
|
||||
checkalpha = true;
|
||||
break;
|
||||
|
||||
case 180:
|
||||
imgwidth = xsize;
|
||||
imgheight = zsize;
|
||||
imgoffsetx = imgwidth - pivotx;
|
||||
checkalpha = true;
|
||||
break;
|
||||
|
||||
case 270:
|
||||
imgwidth = ysize;
|
||||
imgheight = zsize;
|
||||
imgoffsetx = pivoty;
|
||||
break;
|
||||
|
||||
default: throw new InvalidDataException("Invalid AngleOffset");
|
||||
}
|
||||
|
||||
int numpixels = imgwidth * imgheight;
|
||||
PixelColor[] pixelsarr = new PixelColor[numpixels];
|
||||
|
||||
// Read pixel colors
|
||||
for(int x = 0; x < xsize; x++)
|
||||
{
|
||||
for(int y = 0; y < ysize; y++)
|
||||
{
|
||||
reader.BaseStream.Position = offsets[counter];
|
||||
int next = (counter < offsets.Count - 1 ? offsets[counter + 1] : slabsend);
|
||||
|
||||
// Read first color from the slab
|
||||
while(reader.BaseStream.Position < next)
|
||||
{
|
||||
int ztop = reader.ReadByte();
|
||||
int zleng = reader.ReadByte();
|
||||
if(ztop + zleng > zsize) break;
|
||||
byte flags = reader.ReadByte();
|
||||
|
||||
if(zleng > 0)
|
||||
{
|
||||
// Skip slab if no flags are given (otherwise some garbage pixels may be drawn)
|
||||
if(flags == 0)
|
||||
{
|
||||
reader.BaseStream.Position += zleng;
|
||||
continue;
|
||||
}
|
||||
|
||||
List<int> colorindices = new List<int>(zleng);
|
||||
for(int i = 0; i < zleng; i++)
|
||||
{
|
||||
colorindices.Add(reader.ReadByte());
|
||||
}
|
||||
|
||||
int z = ztop;
|
||||
int cstart = 0;
|
||||
while(z < ztop + zleng)
|
||||
{
|
||||
// Get pixel position
|
||||
int pixelpos;
|
||||
switch(angleoffset)
|
||||
{
|
||||
case 0: pixelpos = x + z * xsize; break;
|
||||
case 90: pixelpos = y + z * ysize; break;
|
||||
case 180: pixelpos = xsize - x - 1 + z * xsize; break;
|
||||
case 270: pixelpos = ysize - y - 1 + z * ysize; break;
|
||||
default: throw new InvalidDataException("Invalid AngleOffset");
|
||||
}
|
||||
|
||||
// Add to projection pixels array
|
||||
if((checkalpha && pixelsarr[pixelpos].a == 0) || !checkalpha)
|
||||
pixelsarr[pixelpos] = palette[colorindices[cstart]];
|
||||
|
||||
// Increment counters
|
||||
cstart++;
|
||||
z++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw to bitmap
|
||||
if(bitmap != null) bitmap.Dispose();
|
||||
bitmap = new Bitmap(imgwidth, imgheight, PixelFormat.Format32bppArgb);
|
||||
BitmapData bmpdata = null;
|
||||
|
||||
try
|
||||
{
|
||||
bmpdata = bitmap.LockBits(new Rectangle(0, 0, imgwidth, imgheight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Cannot lock image for drawing voxel \""
|
||||
+ Path.Combine(voxellocation, voxelname) + "\". " + e.GetType().Name + ": " + e.Message);
|
||||
bitmap = null;
|
||||
}
|
||||
|
||||
if(bmpdata != null)
|
||||
{
|
||||
// Apply pixels to image
|
||||
PixelColor* pixels = (PixelColor*)bmpdata.Scan0.ToPointer();
|
||||
int i = 0;
|
||||
|
||||
for(PixelColor* cp = pixels; cp < pixels + numpixels; cp++, i++)
|
||||
{
|
||||
if(pixelsarr[i].a == 255)
|
||||
{
|
||||
cp->r = pixelsarr[i].r;
|
||||
cp->g = pixelsarr[i].g;
|
||||
cp->b = pixelsarr[i].b;
|
||||
cp->a = 255;
|
||||
}
|
||||
}
|
||||
|
||||
bitmap.UnlockBits(bmpdata);
|
||||
}
|
||||
|
||||
if(bitmap != null)
|
||||
{
|
||||
// Get width and height from image
|
||||
width = bitmap.Size.Width;
|
||||
height = bitmap.Size.Height;
|
||||
scale.x = 1.0f;
|
||||
scale.y = 1.0f;
|
||||
offsetx = imgoffsetx;
|
||||
offsety = pivotz;
|
||||
}
|
||||
else
|
||||
{
|
||||
loadfailed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Missing voxel lump!
|
||||
General.ErrorLogger.Add(ErrorType.Error, "Missing voxel lump \"" + voxelname + "\". Forgot to include required resources?");
|
||||
}
|
||||
|
||||
// Pass on to base
|
||||
base.LocalLoadImage();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -42,6 +42,9 @@ namespace CodeImp.DoomBuilder.Data
|
|||
private static readonly Regex sprite6 = new Regex(@"(\S{4}[A-Za-z\[\]\\]{1}[0-8]{1})");
|
||||
private static readonly Regex sprite8 = new Regex(@"(\S{4}[A-Za-z\[\]\\]{1}[0-8]{1}[A-Za-z\[\]\\]{1}[0-8]{1})");
|
||||
|
||||
//mxd. Voxel recognition.
|
||||
private static readonly Regex voxel = new Regex(@"^\S{4}(([A-Za-z][0-9]){0,2}|[A-Za-z]{0,1})$");
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Structures
|
||||
|
@ -919,23 +922,17 @@ namespace CodeImp.DoomBuilder.Data
|
|||
return false;
|
||||
}
|
||||
|
||||
//mxd. Returns all sprites, which name starts with given string
|
||||
public override HashSet<string> GetSpriteNames(string startswith)
|
||||
//mxd. This returns all sprite names in the WAD
|
||||
public override HashSet<string> GetSpriteNames()
|
||||
{
|
||||
// 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) && IsValidSpriteName(file.Lumps[i].Name))
|
||||
result.Add(file.Lumps[i].Name);
|
||||
}
|
||||
if(IsValidSpriteName(file.Lumps[i].Name)) result.Add(file.Lumps[i].Name);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -952,25 +949,23 @@ namespace CodeImp.DoomBuilder.Data
|
|||
#region ================== Voxels (mxd)
|
||||
|
||||
//mxd. This returns the list of voxels, which can be used without VOXELDEF definition
|
||||
public override IEnumerable<string> GetVoxelNames()
|
||||
public override HashSet<string> GetVoxelNames()
|
||||
{
|
||||
// Error when suspended
|
||||
if(issuspended) throw new Exception("Data reader is suspended");
|
||||
|
||||
List<string> voxels = new List<string>();
|
||||
Regex spriteName = new Regex(SPRITE_NAME_PATTERN);
|
||||
|
||||
HashSet<string> result = new HashSet<string>();
|
||||
foreach(LumpRange range in voxelranges)
|
||||
{
|
||||
if(range.start == range.end) continue;
|
||||
|
||||
for(int i = range.start + 1; i < range.end; i++)
|
||||
{
|
||||
if(spriteName.IsMatch(file.Lumps[i].Name)) voxels.Add(file.Lumps[i].Name);
|
||||
if(IsValidVoxelName(file.Lumps[i].Name)) result.Add(file.Lumps[i].Name);
|
||||
}
|
||||
}
|
||||
|
||||
return voxels.ToArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
//mxd
|
||||
|
@ -981,7 +976,7 @@ namespace CodeImp.DoomBuilder.Data
|
|||
}
|
||||
|
||||
//mxd. This finds and returns a voxel stream or null if no voxel was found
|
||||
public override Stream GetVoxelData(string name)
|
||||
public override Stream GetVoxelData(string name, ref string voxellocation)
|
||||
{
|
||||
// Error when suspended
|
||||
if(issuspended) throw new Exception("Data reader is suspended");
|
||||
|
@ -990,12 +985,22 @@ namespace CodeImp.DoomBuilder.Data
|
|||
{
|
||||
if(range.start == range.end) continue;
|
||||
Lump lump = file.FindLump(name, range.start, range.end);
|
||||
if(lump != null) return lump.Stream;
|
||||
if(lump != null)
|
||||
{
|
||||
voxellocation = location.GetDisplayName();
|
||||
return lump.Stream;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//mxd
|
||||
internal static bool IsValidVoxelName(string name)
|
||||
{
|
||||
return (name.Length > 3 && name.Length < 7) && voxel.IsMatch(name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Decorate, Gldefs, Mapinfo, etc...
|
||||
|
|
|
@ -38,7 +38,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data
|
|||
|
||||
internal Vector3 Scale { get { return scale; } }
|
||||
internal Matrix Transform { get { return (General.Settings.GZStretchView ? transformstretched : transform); } }
|
||||
internal bool OverridePalette; //used for voxel models only
|
||||
internal bool OverridePalette; // Used for voxel models only
|
||||
internal float AngleOffset; // Used for voxel models only
|
||||
internal bool InheritActorPitch;
|
||||
internal bool InheritActorRoll;
|
||||
|
||||
|
|
|
@ -58,12 +58,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
|||
private static void LoadKVX(ModelData mde, List<DataReader> containers, Device device)
|
||||
{
|
||||
mde.Model = new GZModel();
|
||||
string unused = string.Empty;
|
||||
foreach(string name in mde.ModelNames)
|
||||
{
|
||||
//find the model
|
||||
foreach(DataReader dr in containers)
|
||||
{
|
||||
Stream ms = dr.GetVoxelData(name);
|
||||
Stream ms = dr.GetVoxelData(name, ref unused);
|
||||
if(ms == null) continue;
|
||||
|
||||
//load kvx
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace CodeImp.DoomBuilder.Map
|
|||
#region ================== Constants
|
||||
|
||||
public const int NUM_ARGS = 5;
|
||||
public static HashSet<ThingRenderMode> AlignableRenderModes = new HashSet<ThingRenderMode>
|
||||
public static readonly HashSet<ThingRenderMode> AlignableRenderModes = new HashSet<ThingRenderMode>
|
||||
{
|
||||
ThingRenderMode.FLATSPRITE, ThingRenderMode.WALLSPRITE, ThingRenderMode.MODEL
|
||||
};
|
||||
|
@ -546,14 +546,9 @@ namespace CodeImp.DoomBuilder.Map
|
|||
// Check if the thing has model override
|
||||
if(General.Map.Data.ModeldefEntries.ContainsKey(type))
|
||||
{
|
||||
if(General.Map.Data.ModeldefEntries[type].LoadState == ModelLoadState.None)
|
||||
{
|
||||
if(General.Map.Data.ProcessModel(type)) rendermode = ThingRenderMode.MODEL;
|
||||
}
|
||||
else
|
||||
{
|
||||
rendermode = ThingRenderMode.MODEL;
|
||||
}
|
||||
ModelData md = General.Map.Data.ModeldefEntries[type];
|
||||
if((md.LoadState == ModelLoadState.None && General.Map.Data.ProcessModel(type)) || md.LoadState != ModelLoadState.None)
|
||||
rendermode = (General.Map.Data.ModeldefEntries[type].IsVoxel ? ThingRenderMode.VOXEL : ThingRenderMode.MODEL);
|
||||
}
|
||||
|
||||
// Update radian versions of pitch and roll
|
||||
|
@ -575,6 +570,11 @@ namespace CodeImp.DoomBuilder.Map
|
|||
pitchrad = 0;
|
||||
break;
|
||||
|
||||
case ThingRenderMode.VOXEL:
|
||||
rollrad = 0;
|
||||
pitchrad = 0;
|
||||
break;
|
||||
|
||||
default: throw new NotImplementedException("Unknown ThingRenderMode");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
namespace CodeImp.DoomBuilder.Config
|
||||
namespace CodeImp.DoomBuilder.Rendering
|
||||
{
|
||||
public enum ModelRenderMode
|
||||
{
|
||||
|
@ -19,6 +19,7 @@
|
|||
{
|
||||
NORMAL,
|
||||
MODEL,
|
||||
VOXEL,
|
||||
WALLSPRITE,
|
||||
FLATSPRITE,
|
||||
}
|
|
@ -1175,7 +1175,7 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
if(!fixedcolor && t.Highlighted) continue;
|
||||
|
||||
// Collect models
|
||||
if(t.RenderMode == ThingRenderMode.MODEL)
|
||||
if(t.RenderMode == ThingRenderMode.MODEL || t.RenderMode == ThingRenderMode.VOXEL)
|
||||
{
|
||||
if(!modelsByType.ContainsKey(t.Type)) modelsByType.Add(t.Type, new List<Thing>());
|
||||
modelsByType[t.Type].Add(t);
|
||||
|
@ -1233,7 +1233,8 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
foreach(KeyValuePair<int, List<Thing>> group in thingsByType)
|
||||
{
|
||||
// Skip when all things of this type will be rendered as models
|
||||
if(group.Value[0].RenderMode == ThingRenderMode.MODEL && (General.Settings.GZDrawModelsMode == ModelRenderMode.ALL)) continue;
|
||||
if((group.Value[0].RenderMode == ThingRenderMode.MODEL || group.Value[0].RenderMode == ThingRenderMode.VOXEL)
|
||||
&& (General.Settings.GZDrawModelsMode == ModelRenderMode.ALL)) continue;
|
||||
|
||||
// Find thing information
|
||||
ThingTypeInfo info = General.Map.Data.GetThingInfo(group.Key);
|
||||
|
@ -1285,7 +1286,8 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
|
||||
foreach(Thing t in framegroup.Value)
|
||||
{
|
||||
if(t.RenderMode == ThingRenderMode.MODEL && ((General.Settings.GZDrawModelsMode == ModelRenderMode.SELECTION && t.Selected) || (General.Settings.GZDrawModelsMode == ModelRenderMode.ACTIVE_THINGS_FILTER && alpha == 1.0f)))
|
||||
if((t.RenderMode == ThingRenderMode.MODEL || t.RenderMode == ThingRenderMode.VOXEL)
|
||||
&& ((General.Settings.GZDrawModelsMode == ModelRenderMode.SELECTION && t.Selected) || (General.Settings.GZDrawModelsMode == ModelRenderMode.ACTIVE_THINGS_FILTER && alpha == 1.0f)))
|
||||
continue;
|
||||
|
||||
bool forcespriterendering;
|
||||
|
|
|
@ -1238,8 +1238,17 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
//mxd
|
||||
private Matrix CreateThingPositionMatrix(VisualThing t)
|
||||
{
|
||||
// Use normal ThingRenderMode when model rendering is disabled for this thing
|
||||
ThingRenderMode rendermode = t.Thing.RenderMode;
|
||||
if((t.Thing.RenderMode == ThingRenderMode.MODEL || t.Thing.RenderMode == ThingRenderMode.VOXEL) &&
|
||||
(General.Settings.GZDrawModelsMode == ModelRenderMode.NONE ||
|
||||
(General.Settings.GZDrawModelsMode == ModelRenderMode.SELECTION && !t.Selected)))
|
||||
{
|
||||
rendermode = ThingRenderMode.NORMAL;
|
||||
}
|
||||
|
||||
// Create the matrix for positioning
|
||||
switch(t.Thing.RenderMode)
|
||||
switch(rendermode)
|
||||
{
|
||||
case ThingRenderMode.NORMAL:
|
||||
if(t.Info.XYBillboard) // Apply billboarding?
|
||||
|
@ -1266,6 +1275,7 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
|
||||
case ThingRenderMode.WALLSPRITE:
|
||||
case ThingRenderMode.MODEL:
|
||||
case ThingRenderMode.VOXEL:
|
||||
return Matrix.Scaling(t.Thing.ScaleX, t.Thing.ScaleX, t.Thing.ScaleY) * t.Position;
|
||||
|
||||
default: throw new NotImplementedException("Unknown ThingRenderMode");
|
||||
|
@ -1722,7 +1732,7 @@ namespace CodeImp.DoomBuilder.Rendering
|
|||
}
|
||||
|
||||
//mxd. Gather models
|
||||
if(t.Thing.RenderMode == ThingRenderMode.MODEL &&
|
||||
if((t.Thing.RenderMode == ThingRenderMode.MODEL || t.Thing.RenderMode == ThingRenderMode.VOXEL) &&
|
||||
(General.Settings.GZDrawModelsMode == ModelRenderMode.ALL ||
|
||||
General.Settings.GZDrawModelsMode == ModelRenderMode.ACTIVE_THINGS_FILTER ||
|
||||
(General.Settings.GZDrawModelsMode == ModelRenderMode.SELECTION && t.Selected)))
|
||||
|
|
|
@ -292,6 +292,7 @@ namespace CodeImp.DoomBuilder.VisualModes
|
|||
{
|
||||
// Don't do anything
|
||||
case ThingRenderMode.MODEL: break;
|
||||
case ThingRenderMode.VOXEL: break;
|
||||
|
||||
// Actor becomes a flat sprite which can be tilted with the use of the Pitch actor property.
|
||||
case ThingRenderMode.FLATSPRITE:
|
||||
|
@ -407,7 +408,7 @@ namespace CodeImp.DoomBuilder.VisualModes
|
|||
}
|
||||
break;*/
|
||||
|
||||
default:
|
||||
case ThingRenderMode.NORMAL:
|
||||
if(info.RollSprite)
|
||||
{
|
||||
transform = Matrix.Translation(0f, 0f, -localcenterz) * Matrix.RotationY(-thing.RollRad) * Matrix.Translation(0f, 0f, localcenterz);
|
||||
|
@ -420,6 +421,8 @@ namespace CodeImp.DoomBuilder.VisualModes
|
|||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default: throw new NotImplementedException("Unknown ThingRenderMode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
{
|
||||
#region ================== Constants
|
||||
|
||||
private readonly string[] SPRITE_POSTFIXES = new[] {"2C8", "2D8", "2A8", "2B8", "1C1", "1D1", "1A1", "1B1", "A2", "A1", "A0", "2", "1", "0" };
|
||||
//private readonly string[] SPRITE_POSTFIXES = new[] {"2C8", "2D8", "2A8", "2B8", "1C1", "1D1", "1A1", "1B1", "A2", "A1", "A0", "2", "1", "0" };
|
||||
internal const string ACTOR_CLASS_SPECIAL_TOKENS = ":{}\n;,"; //mxd
|
||||
|
||||
#endregion
|
||||
|
@ -678,7 +678,7 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
//mxd. Valid when internal or exists
|
||||
if(sprite.StartsWith(DataManager.INTERNAL_PREFIX, StringComparison.OrdinalIgnoreCase) || General.Map.Data.GetSpriteExists(sprite))
|
||||
{
|
||||
result.Sprite = sprite; //mxd
|
||||
result.Sprite = sprite;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -733,90 +733,10 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
}
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(result.Sprite))
|
||||
{
|
||||
// The sprite name is not actually complete, we still have to append
|
||||
// the direction characters to it. Find an existing sprite with direction.
|
||||
foreach(string postfix in SPRITE_POSTFIXES)
|
||||
{
|
||||
if(General.Map.Data.GetSpriteExists(result.Sprite + postfix))
|
||||
{
|
||||
result.Sprite += postfix;
|
||||
//mxd. We've found something. Or not...
|
||||
//Info: actual sprites are resolved in ThingTypeInfo.SetupSpriteFrame()
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No sprite found
|
||||
return result;
|
||||
}
|
||||
|
||||
//mxd.
|
||||
///TODO: rewrite this
|
||||
public string FindSuitableVoxel(HashSet<string> voxels)
|
||||
{
|
||||
string result = string.Empty;
|
||||
|
||||
// Try the idle state
|
||||
if(HasState("idle"))
|
||||
{
|
||||
StateStructure s = GetState("idle");
|
||||
StateStructure.FrameInfo info = s.GetSprite(0);
|
||||
if(!string.IsNullOrEmpty(info.Sprite)) result = info.Sprite;
|
||||
}
|
||||
|
||||
// Try the see state
|
||||
if(string.IsNullOrEmpty(result) && HasState("see"))
|
||||
{
|
||||
StateStructure s = GetState("see");
|
||||
StateStructure.FrameInfo info = s.GetSprite(0);
|
||||
if(!string.IsNullOrEmpty(info.Sprite)) result = info.Sprite;
|
||||
}
|
||||
|
||||
// Try the inactive state
|
||||
if(string.IsNullOrEmpty(result) && HasState("inactive"))
|
||||
{
|
||||
StateStructure s = GetState("inactive");
|
||||
StateStructure.FrameInfo info = s.GetSprite(0);
|
||||
if(!string.IsNullOrEmpty(info.Sprite)) result = info.Sprite;
|
||||
}
|
||||
|
||||
// Try the spawn state
|
||||
if(string.IsNullOrEmpty(result) && HasState("spawn"))
|
||||
{
|
||||
StateStructure s = GetState("spawn");
|
||||
StateStructure.FrameInfo info = s.GetSprite(0);
|
||||
if(!string.IsNullOrEmpty(info.Sprite)) result = info.Sprite;
|
||||
}
|
||||
|
||||
// Still no sprite found? then just pick the first we can find
|
||||
if(string.IsNullOrEmpty(result))
|
||||
{
|
||||
Dictionary<string, StateStructure> list = GetAllStates();
|
||||
foreach(StateStructure s in list.Values)
|
||||
{
|
||||
StateStructure.FrameInfo info = s.GetSprite(0);
|
||||
if(!string.IsNullOrEmpty(info.Sprite))
|
||||
{
|
||||
result = info.Sprite;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(result))
|
||||
{
|
||||
if(voxels.Contains(result)) return result;
|
||||
|
||||
// The sprite name may be incomplete. Find an existing sprite with direction.
|
||||
foreach(string postfix in SPRITE_POSTFIXES)
|
||||
if(voxels.Contains(result + postfix)) return result + postfix;
|
||||
}
|
||||
|
||||
|
||||
// No voxel found
|
||||
return "";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -66,8 +66,7 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
while(parser.SkipWhitespace(true))
|
||||
{
|
||||
// Read first token
|
||||
string token = parser.ReadToken();
|
||||
token = token.ToLowerInvariant();
|
||||
string token = parser.ReadToken().ToLowerInvariant();
|
||||
|
||||
// One of the flow control statements?
|
||||
if((token == "loop") || (token == "stop") || (token == "wait") || (token == "fail"))
|
||||
|
@ -146,10 +145,17 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
// No first sprite yet?
|
||||
FrameInfo info = new FrameInfo(); //mxd
|
||||
if(spriteframes.Length > 0)
|
||||
{
|
||||
//mxd. I'm not even 50% sure the parser handles all bizzare cases without shifting sprite name / frame blocks,
|
||||
// so let's log it as a warning, not an error...
|
||||
if(token.Length != 4)
|
||||
{
|
||||
parser.LogWarning("Invalid sprite name \"" + token.ToUpperInvariant() + "\". Sprite names must be exactly 4 characters long");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make the sprite name
|
||||
string spritename = token + spriteframes[0];
|
||||
spritename = spritename.ToUpperInvariant();
|
||||
string spritename = (token + spriteframes[0]).ToUpperInvariant();
|
||||
|
||||
// Ignore some odd ZDoom things
|
||||
if(!spritename.StartsWith("TNT1") && !spritename.StartsWith("----") && !spritename.Contains("#"))
|
||||
|
@ -158,14 +164,13 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
sprites.Add(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Continue until the end of the line
|
||||
parser.SkipWhitespace(false);
|
||||
string t = parser.ReadToken();
|
||||
while(!string.IsNullOrEmpty(t) && t != "\n")
|
||||
{
|
||||
parser.SkipWhitespace(false);
|
||||
t = parser.ReadToken().ToLowerInvariant();
|
||||
|
||||
//mxd. Bright keyword support...
|
||||
if(t == "bright")
|
||||
{
|
||||
|
@ -206,9 +211,23 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
// Break out of this loop
|
||||
break;
|
||||
}
|
||||
|
||||
//mxd. Function params start (those can span multiple lines)
|
||||
else if(t == "(")
|
||||
{
|
||||
int bracelevel = 1;
|
||||
while(!string.IsNullOrEmpty(token) && bracelevel > 0)
|
||||
{
|
||||
parser.SkipWhitespace(true);
|
||||
token = parser.ReadToken();
|
||||
switch(token)
|
||||
{
|
||||
case "(": bracelevel++; break;
|
||||
case ")": bracelevel--; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//mxd. Because stuff like this is also valid: "Actor Oneliner { States { Spawn: WOOT A 1 A_FadeOut(0.1) Loop }}"
|
||||
if(t == "}")
|
||||
else if(t == "}")
|
||||
{
|
||||
// Rewind so that this scope end can be read again
|
||||
parser.DataStream.Seek(-1, SeekOrigin.Current);
|
||||
|
@ -216,6 +235,10 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
// Done here
|
||||
return;
|
||||
}
|
||||
|
||||
// Read next token
|
||||
parser.SkipWhitespace(false);
|
||||
t = parser.ReadToken().ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,13 +61,12 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
return false;
|
||||
}
|
||||
|
||||
modelName = StripQuotes(token).ToLowerInvariant();
|
||||
modelName = StripQuotes(token).ToUpperInvariant();
|
||||
}
|
||||
else if(token == "{") //read the settings
|
||||
{
|
||||
ModelData mde = new ModelData { IsVoxel = true };
|
||||
float scale = 1.0f;
|
||||
float angleoffset = 0;
|
||||
|
||||
while(SkipWhitespace(true))
|
||||
{
|
||||
|
@ -79,7 +78,7 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
if(!string.IsNullOrEmpty(modelName) && spriteNames.Count > 0)
|
||||
{
|
||||
mde.ModelNames.Add(modelName);
|
||||
mde.SetTransform(Matrix.RotationZ(Angle2D.DegToRad(angleoffset)), Matrix.Identity, new Vector3(scale));
|
||||
mde.SetTransform(Matrix.RotationZ(Angle2D.DegToRad(mde.AngleOffset)), Matrix.Identity, new Vector3(scale));
|
||||
|
||||
foreach(string s in spriteNames)
|
||||
{
|
||||
|
@ -104,7 +103,7 @@ namespace CodeImp.DoomBuilder.ZDoom
|
|||
if(!NextTokenIs("=")) return false;
|
||||
|
||||
token = ReadToken();
|
||||
if(!ReadSignedFloat(token, ref angleoffset))
|
||||
if(!ReadSignedFloat(token, ref mde.AngleOffset))
|
||||
{
|
||||
// Not numeric!
|
||||
ReportError("Expected AngleOffset value, but got \"" + token + "\"");
|
||||
|
|
|
@ -635,15 +635,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
if(options.FitWidth)
|
||||
{
|
||||
float scalex, offsetx;
|
||||
float linelength = (float)Math.Round(Sidedef.Line.Length); // Let's use ZDoom-compatible line length here
|
||||
|
||||
if(options.FitAcrossSurfaces)
|
||||
{
|
||||
scalex = Texture.ScaledWidth / (Sidedef.Line.Length * (options.GlobalBounds.Width / Sidedef.Line.Length)) * options.HorizontalRepeat;
|
||||
scalex = Texture.ScaledWidth / (linelength * (options.GlobalBounds.Width / linelength)) * options.HorizontalRepeat;
|
||||
offsetx = (float)Math.Round((options.Bounds.X * scalex - Sidedef.OffsetX - options.ControlSideOffsetX) % Texture.Width, General.Map.FormatInterface.VertexDecimals);
|
||||
}
|
||||
else
|
||||
{
|
||||
scalex = Texture.ScaledWidth / Sidedef.Line.Length * options.HorizontalRepeat;
|
||||
scalex = Texture.ScaledWidth / linelength * options.HorizontalRepeat;
|
||||
offsetx = -Sidedef.OffsetX - options.ControlSideOffsetX;
|
||||
}
|
||||
|
||||
|
|
|
@ -293,7 +293,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
// Determine sprite size and offset
|
||||
float radius = sprite.ScaledWidth * 0.5f;
|
||||
float height = sprite.ScaledHeight;
|
||||
SpriteImage spriteimg = sprite as SpriteImage;
|
||||
ISpriteImage spriteimg = sprite as ISpriteImage;
|
||||
if(spriteimg != null)
|
||||
{
|
||||
offsetx = radius - spriteimg.OffsetX;
|
||||
|
|
Loading…
Reference in a new issue