Some more work done on TEXTURES support

This commit is contained in:
codeimp 2009-02-15 23:58:28 +00:00
parent ab74b041c5
commit 2047756694
9 changed files with 374 additions and 51 deletions

View file

@ -663,6 +663,7 @@
<None Include="Resources\Close.png" />
<Compile Include="Config\AllTexturesSet.cs" />
<Compile Include="Config\FlagTranslation.cs" />
<Compile Include="Data\HighResImage.cs" />
<Compile Include="Data\PK3FileImage.cs" />
<Compile Include="Data\PK3StructuredReader.cs" />
<Compile Include="ZDoom\ActorStructure.cs" />

202
Source/Data/HighResImage.cs Normal file
View file

@ -0,0 +1,202 @@
#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.IO;
using System.IO;
#endregion
namespace CodeImp.DoomBuilder.Data
{
internal sealed unsafe class HighResImage : ImageData
{
#region ================== Variables
private List<TexturePatch> patches;
private float scalex;
private float scaley;
#endregion
#region ================== Constructor / Disposer
// Constructor
public HighResImage(string name, int width, int height, float scalex, float scaley)
{
// Initialize
this.width = width;
this.height = height;
this.scalex = scalex;
this.scaley = scaley;
this.scaledwidth = (float)width * scalex;
this.scaledheight = (float)height * scaley;
this.patches = new List<TexturePatch>();
SetName(name);
// We have no destructor
GC.SuppressFinalize(this);
}
#endregion
#region ================== Methods
// This adds a patch to the texture
public void AddPatch(TexturePatch patch)
{
// Add it
patches.Add(patch);
}
// This loads the image
protected override void LocalLoadImage()
{
IImageReader reader;
MemoryStream mem;
byte[] membytes;
Graphics g = null;
// Checks
if(this.IsImageLoaded) return;
if((width == 0) || (height == 0)) return;
lock(this)
{
// Create texture bitmap
try
{
if(bitmap != null) bitmap.Dispose();
bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
BitmapData bitmapdata = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
PixelColor* pixels = (PixelColor*)bitmapdata.Scan0.ToPointer();
General.ZeroMemory(new IntPtr(pixels), width * height * sizeof(PixelColor));
bitmap.UnlockBits(bitmapdata);
g = Graphics.FromImage(bitmap);
}
catch(Exception e)
{
// Unable to make bitmap
General.WriteLogLine("ERROR: Unable to load texture image '" + this.Name + "'. " + e.GetType().Name + ": " + e.Message);
loadfailed = true;
}
if(!loadfailed)
{
// Go for all patches
foreach(TexturePatch p in patches)
{
// Get the patch data stream
Stream patchdata = General.Map.Data.GetPatchData(p.lumpname);
if(patchdata != null)
{
// Copy patch data to memory
patchdata.Seek(0, SeekOrigin.Begin);
membytes = new byte[(int)patchdata.Length];
patchdata.Read(membytes, 0, (int)patchdata.Length);
mem = new MemoryStream(membytes);
mem.Seek(0, SeekOrigin.Begin);
// Get a reader for the data
reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMPICTURE, General.Map.Data.Palette);
if(reader is UnknownImageReader)
{
// Data is in an unknown format!
General.WriteLogLine("ERROR: Patch lump '" + p.lumpname + "' data format could not be read, while loading texture '" + this.Name + "'!");
loadfailed = true;
}
else
{
// Get the patch
mem.Seek(0, SeekOrigin.Begin);
Bitmap patchbmp = null;
try { patchbmp = reader.ReadAsBitmap(mem); }
catch(InvalidDataException)
{
// Data cannot be read!
General.WriteLogLine("ERROR: Patch lump '" + p.lumpname + "' data format could not be read, while loading texture '" + this.Name + "'!");
loadfailed = true;
}
if(patchbmp != null)
{
// Adjust patch alpha
if(p.alpha < 1.0f)
{
BitmapData bmpdata = null;
try
{
bmpdata = patchbmp.LockBits(new Rectangle(0, 0, patchbmp.Size.Width, patchbmp.Size.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
}
catch(Exception e)
{
General.WriteLogLine("ERROR: Cannot lock image '" + p.lumpname + "' for alpha adjustment. " + e.GetType().Name + ": " + e.Message);
}
if(bmpdata != null)
{
PixelColor* pixels = (PixelColor*)(bmpdata.Scan0.ToPointer());
int numpixels = bmpdata.Width * bmpdata.Height;
for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
{
cp->a = (byte)((((float)cp->a * PixelColor.BYTE_TO_FLOAT) * p.alpha) * 255.0f);
}
patchbmp.UnlockBits(bmpdata);
}
}
// Draw the patch on the texture image
Rectangle tgtrect = new Rectangle(p.x, p.y, patchbmp.Size.Width, patchbmp.Size.Height);
g.DrawImageUnscaledAndClipped(patchbmp, tgtrect);
patchbmp.Dispose();
}
}
// Done
mem.Dispose();
}
else
{
// Missing a patch lump!
General.WriteLogLine("ERROR: Missing patch lump '" + p.lumpname + "' while loading texture '" + this.Name + "'!");
loadfailed = true;
}
}
}
// Dispose bitmap if load failed
if(loadfailed && (bitmap != null))
{
bitmap.Dispose();
bitmap = null;
}
// Pass on to base
base.LocalLoadImage();
}
}
#endregion
}
}

View file

@ -158,7 +158,7 @@ namespace CodeImp.DoomBuilder.Data
// This loads the textures
public override ICollection<ImageData> LoadTextures(PatchNames pnames)
{
List<ImageData> images = new List<ImageData>();
Dictionary<long, ImageData> images = new Dictionary<long, ImageData>();
ICollection<ImageData> collection;
List<ImageData> imgset = new List<ImageData>();
@ -184,19 +184,6 @@ namespace CodeImp.DoomBuilder.Data
// Add images from texture directory
collection = LoadDirectoryImages(TEXTURES_DIR, false, true);
AddImagesToList(images, collection);
// Load TEXTURES lump file
imgset.Clear();
string texturesfile = FindFirstFile("TEXTURES", false);
if((texturesfile != null) && FileExists(texturesfile))
{
MemoryStream filedata = LoadFile(texturesfile);
WADReader.LoadHighresTextures(filedata, texturesfile, ref imgset);
filedata.Dispose();
}
// Add images from TEXTURES lump files
AddImagesToList(images, imgset);
// Load TEXTURE1 lump file
imgset.Clear();
@ -220,7 +207,20 @@ namespace CodeImp.DoomBuilder.Data
// Add images from TEXTURE1 and TEXTURE2 lump files
AddImagesToList(images, imgset);
return images;
// Load TEXTURES lump file
imgset.Clear();
string texturesfile = FindFirstFile("TEXTURES", false);
if((texturesfile != null) && FileExists(texturesfile))
{
MemoryStream filedata = LoadFile(texturesfile);
WADReader.LoadHighresTextures(filedata, texturesfile, ref imgset, images, null);
filedata.Dispose();
}
// Add images from TEXTURES lump file
AddImagesToList(images, imgset);
return new List<ImageData>(images.Values);
}
// This returns the patch names from the PNAMES lump
@ -260,7 +260,7 @@ namespace CodeImp.DoomBuilder.Data
// This loads the textures
public override ICollection<ImageData> LoadFlats()
{
List<ImageData> images = new List<ImageData>();
Dictionary<long, ImageData> images = new Dictionary<long, ImageData>();
ICollection<ImageData> collection;
// Error when suspended
@ -284,8 +284,8 @@ namespace CodeImp.DoomBuilder.Data
// Add images from flats directory
collection = LoadDirectoryImages(FLATS_DIR, true, true);
AddImagesToList(images, collection);
return images;
return new List<ImageData>(images.Values);
}
#endregion
@ -346,24 +346,14 @@ namespace CodeImp.DoomBuilder.Data
}
// This copies images from a collection unless they already exist in the list
private void AddImagesToList(List<ImageData> targetlist, ICollection<ImageData> sourcelist)
private void AddImagesToList(Dictionary<long, ImageData> targetlist, ICollection<ImageData> sourcelist)
{
// Go for all source images
foreach(ImageData src in sourcelist)
{
// Check if exists in target list
bool alreadyexists = false;
foreach(ImageData tgt in targetlist)
{
if(tgt.LongName == src.LongName)
{
alreadyexists = true;
break;
}
}
// Add source image to target list
if(!alreadyexists) targetlist.Add(src);
if(!targetlist.ContainsKey(src.LongName))
targetlist.Add(src.LongName, src);
}
}

View file

@ -74,7 +74,6 @@ namespace CodeImp.DoomBuilder.Data
// This loads the image
protected override void LocalLoadImage()
{
uint datalength = (uint)(width * height * sizeof(PixelColor));
IImageReader reader;
BitmapData bitmapdata = null;
MemoryStream mem;

View file

@ -25,24 +25,63 @@ using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Rendering;
#endregion
namespace CodeImp.DoomBuilder.Data
{
internal enum TexturePathRenderStyle
{
Copy,
Blend,
Add,
Subtract,
ReverseSubtract,
Modulate,
CopyAlpha
}
internal struct TexturePatch
{
public string lumpname;
public int x;
public int y;
// Constructor
public bool flipx;
public bool flipy;
public int rotate;
public PixelColor blend;
public float alpha;
public TexturePathRenderStyle style;
// Constructor for simple patches
public TexturePatch(string lumpname, int x, int y)
{
// Initialize
this.lumpname = lumpname;
this.x = x;
this.y = y;
this.flipx = false;
this.flipy = false;
this.rotate = 0;
this.blend = new PixelColor(0, 0, 0, 0);
this.alpha = 1.0f;
this.style = TexturePathRenderStyle.Copy;
}
// Constructor for hires patches
public TexturePatch(string lumpname, int x, int y, bool flipx, bool flipy, int rotate, PixelColor blend, float alpha, int style)
{
// Initialize
this.lumpname = lumpname;
this.x = x;
this.y = y;
this.flipx = flipx;
this.flipy = flipy;
this.rotate = rotate;
this.blend = blend;
this.alpha = alpha;
this.style = (TexturePathRenderStyle)style;
}
}
}

View file

@ -26,6 +26,7 @@ using System.Drawing.Imaging;
using System.IO;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.ZDoom;
using CodeImp.DoomBuilder.Rendering;
#endregion
@ -213,6 +214,15 @@ namespace CodeImp.DoomBuilder.Data
LoadTexturesRange(range.start, range.end, ref images, pnames);
}
// Load TEXTURES lump file
lump = file.FindLump("TEXTURES");
if(lump != null)
{
MemoryStream filedata = new MemoryStream(lump.Stream.ReadAllBytes());
WADReader.LoadHighresTextures(filedata, "TEXTURES", ref images, null, null);
filedata.Dispose();
}
// Return result
return images;
}
@ -239,31 +249,18 @@ namespace CodeImp.DoomBuilder.Data
}
// This loads the texture definitions from a TEXTURES lump
public static void LoadHighresTextures(Stream stream, string filename, ref List<ImageData> images)
public static void LoadHighresTextures(Stream stream, string filename, ref List<ImageData> images, Dictionary<long, ImageData> textures, Dictionary<long, ImageData> flats)
{
// Parse the data
TexturesParser parser = new TexturesParser();
parser.Parse(stream, filename);
// Determine default scale
float defaultscale = General.Map.Config.DefaultTextureScale;
// Make the textures
foreach(TextureStructure t in parser.Textures)
{
float scalex, scaley;
if(t.XScale == 0.0f) scalex = defaultscale; else scalex = 1f / t.XScale;
if(t.YScale == 0.0f) scaley = defaultscale; else scaley = 1f / t.YScale;
TextureImage image = new TextureImage(t.Name.ToUpperInvariant(), t.Width, t.Height, scalex, scaley);
// Add patches
foreach(PatchStructure p in t.Patches)
{
image.AddPatch(new TexturePatch(p.Name.ToUpperInvariant(), p.OffsetX, p.OffsetY));
}
// Add the texture
images.Add(image);
ImageData img = t.MakeImage(textures, flats);
images.Add(img);
}
}

View file

@ -34,7 +34,7 @@ namespace CodeImp.DoomBuilder.Rendering
{
#region ================== Constants
private const float BYTE_TO_FLOAT = 0.00392156862745098f;
public const float BYTE_TO_FLOAT = 0.00392156862745098f;
#endregion

View file

@ -45,6 +45,7 @@ namespace CodeImp.DoomBuilder.ZDoom
private int offsety;
private bool flipx;
private bool flipy;
private float alpha;
#endregion
@ -55,6 +56,7 @@ namespace CodeImp.DoomBuilder.ZDoom
public int OffsetY { get { return offsety; } }
public bool FlipX { get { return flipx; } }
public bool FlipY { get { return flipy; } }
public float Alpha { get { return alpha; } }
#endregion
@ -66,6 +68,7 @@ namespace CodeImp.DoomBuilder.ZDoom
string tokenstr;
// Initialize
alpha = 1.0f;
// There should be 3 tokens separated by 2 commas now:
// Name, Width, Height
@ -137,6 +140,11 @@ namespace CodeImp.DoomBuilder.ZDoom
{
flipy = true;
}
else if(token == "alpha")
{
if(!ReadTokenFloat(parser, token, out alpha)) return;
alpha = General.Clamp(alpha, 0.0f, 1.0f);
}
else if(token == "}")
{
// Patch scope ends here,
@ -147,5 +155,67 @@ namespace CodeImp.DoomBuilder.ZDoom
}
#endregion
#region ================== Methods
// This reads the next token and sets a floating point value, returns false when failed
private bool ReadTokenFloat(TexturesParser parser, string propertyname, out float value)
{
// Next token is the property value to set
parser.SkipWhitespace(true);
string strvalue = parser.ReadToken();
if(!string.IsNullOrEmpty(strvalue))
{
// Try parsing as value
if(!float.TryParse(strvalue, out value))
{
parser.ReportError("Expected numeric value for property '" + propertyname + "'");
return false;
}
else
{
// Success
return true;
}
}
else
{
// Can't find the property value!
parser.ReportError("Expected a value for property '" + propertyname + "'");
value = 0.0f;
return false;
}
}
// This reads the next token and sets an integral value, returns false when failed
private bool ReadTokenInt(TexturesParser parser, string propertyname, out int value)
{
// Next token is the property value to set
parser.SkipWhitespace(true);
string strvalue = parser.ReadToken();
if(!string.IsNullOrEmpty(strvalue))
{
// Try parsing as value
if(!int.TryParse(strvalue, out value))
{
parser.ReportError("Expected integral value for property '" + propertyname + "'");
return false;
}
else
{
// Success
return true;
}
}
else
{
// Can't find the property value!
parser.ReportError("Expected a value for property '" + propertyname + "'");
value = 0;
return false;
}
}
#endregion
}
}

View file

@ -26,6 +26,7 @@ using CodeImp.DoomBuilder.Data;
using System.IO;
using System.Diagnostics;
using CodeImp.DoomBuilder.Compilers;
using CodeImp.DoomBuilder.Rendering;
#endregion
@ -253,6 +254,30 @@ namespace CodeImp.DoomBuilder.ZDoom
}
}
// This makes a HighResImage texture for this texture
internal HighResImage MakeImage(Dictionary<long, ImageData> textures, Dictionary<long, ImageData> flats)
{
float scalex, scaley;
// Determine default scale
float defaultscale = General.Map.Config.DefaultTextureScale;
// Determine scale for texture
if(xscale == 0.0f) scalex = defaultscale; else scalex = 1f / xscale;
if(yscale == 0.0f) scaley = defaultscale; else scaley = 1f / yscale;
// Make texture
HighResImage tex = new HighResImage(name, width, height, scalex, scaley);
// Add patches
foreach(PatchStructure p in patches)
{
tex.AddPatch(new TexturePatch(p.Name.ToUpperInvariant(), p.OffsetX, p.OffsetY, p.FlipX, p.FlipY, 0, new PixelColor(0, 0, 0, 0), p.Alpha, 0));
}
return tex;
}
#endregion
}
}