Repair C wrapper.
Fix + Improve C# / .NET wrapper. Add an XNA content processor, this will allow texture tools to be used as part of an XNA project.2.0
parent
3c559666b0
commit
b24e3bbcf2
@ -0,0 +1,482 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Content.Pipeline;
|
||||
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
|
||||
using Microsoft.Xna.Framework.Content.Pipeline.Processors;
|
||||
using Nvidia.TextureTools;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
|
||||
|
||||
namespace NvidiaTextureToolsProcessor
|
||||
{
|
||||
|
||||
[ContentProcessor(DisplayName = "NvidiaTextureProcessor")]
|
||||
public class NvidiaTextureProcessor : ContentProcessor<TextureContent, TextureContent>
|
||||
{
|
||||
// Note: We dont expose all of Texture Tools formats since XNA does not support the custom formats(eg ATI1 / ATI2 etc).
|
||||
// Plus we can use some friendlier/more consistant names
|
||||
public enum TextureOutputFormat
|
||||
{
|
||||
Colour,
|
||||
Normals,
|
||||
|
||||
DXT1,
|
||||
DXT1a, // With 1 bit alpha
|
||||
DXT3,
|
||||
DXT5,
|
||||
DXT5n, // Compressed Normals HILO: R=1, G=y, B=0, A=x
|
||||
}
|
||||
|
||||
bool convertToNormalMap = false;
|
||||
|
||||
bool generateMipmaps = true;
|
||||
int maxMipLevel = -1;
|
||||
|
||||
float inputGamma = 2.2f;
|
||||
float outputGamma = 2.2f;
|
||||
|
||||
TextureOutputFormat textureFormat = TextureOutputFormat.DXT1;
|
||||
Quality quality = Quality.Normal;
|
||||
WrapMode wrapMode = WrapMode.Mirror;
|
||||
MipmapFilter mipMapFilter = MipmapFilter.Box;
|
||||
RoundMode roundMode = RoundMode.None;
|
||||
AlphaMode alphaMode = AlphaMode.None;
|
||||
|
||||
bool enableCuda = true;
|
||||
|
||||
[DisplayName("Convert To Normal Map")]
|
||||
[DefaultValue(false)]
|
||||
[Description("When true the input data is converted from colour/height data into a normal map if the output is a normal map.")]
|
||||
[Category("Input")]
|
||||
public bool ConvertToNormalMap
|
||||
{
|
||||
get { return convertToNormalMap; }
|
||||
set { convertToNormalMap = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Generate Mip Maps")]
|
||||
[DefaultValue(true)]
|
||||
[Description("When set to true the processor will generate mip maps")]
|
||||
[Category("Mip Mapping")]
|
||||
public bool GenerateMipmaps
|
||||
{
|
||||
get { return generateMipmaps; }
|
||||
set { generateMipmaps = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Max Mip Map Level")]
|
||||
[DefaultValue(-1)]
|
||||
[Description("Setting the max mip map level allows partial mip map chains to be generated. -1 generates all levels if mip map generation is enabled.")]
|
||||
[Category("Mip Mapping")]
|
||||
public int MaxMipMapLevel
|
||||
{
|
||||
get { return maxMipLevel; }
|
||||
set { maxMipLevel = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Input Gamma")]
|
||||
[DefaultValue(2.2f)]
|
||||
[Description("The gamma conversion performed before mip map generation, for best results mip maps should be generated in linear colour space.")]
|
||||
[Category("Gamma")]
|
||||
public float InputGamma
|
||||
{
|
||||
get { return inputGamma; }
|
||||
set { inputGamma = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Output Gamma")]
|
||||
[DefaultValue(2.2f)]
|
||||
[Description("The gamma conversion applied during output. In general this should be left at 2.2 for LDR images.")]
|
||||
[Category("Gamma")]
|
||||
public float OutputGamma
|
||||
{
|
||||
get { return outputGamma; }
|
||||
set { outputGamma = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Texture Output Format")]
|
||||
[DefaultValue(TextureOutputFormat.DXT1)]
|
||||
[Description("The format which the processor generates, Color means no compression, DXT1 if useful for textures with no alpha, DXT5 for textures with alpha and DXT5n for normal maps.")]
|
||||
[Category("Output")]
|
||||
public TextureOutputFormat TextureFormat
|
||||
{
|
||||
get { return textureFormat; }
|
||||
set { textureFormat = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Compression Quality")]
|
||||
[DefaultValue(Quality.Normal)]
|
||||
[Description("Specifies the amount of time the processor will spend trying to find the best quality compression. Highest should only be used for testing as it uses a brute force approach.")]
|
||||
[Category("Output")]
|
||||
public Quality Quality
|
||||
{
|
||||
get { return quality; }
|
||||
set { quality = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Wrap Mode")]
|
||||
[DefaultValue(WrapMode.Mirror)]
|
||||
[Description("Specifying the wrap mode used for the texture can sometimes improve the quality of filtering. In general Mirror should give good results.")]
|
||||
[Category("Input")]
|
||||
public WrapMode WrapMode
|
||||
{
|
||||
get { return wrapMode; }
|
||||
set { wrapMode = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Mip Map Filter")]
|
||||
[DefaultValue(MipmapFilter.Box)]
|
||||
[Description("Specifies which filter to use for down sampling mip maps. Box generally gives good results, Triangle will often appear blurry and Kaiser is the slowest but best quality.")]
|
||||
[Category("Mip Mapping")]
|
||||
public MipmapFilter MipMapFilter
|
||||
{
|
||||
get { return mipMapFilter; }
|
||||
set { mipMapFilter = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Texture Size Rounding Mode")]
|
||||
[DefaultValue(RoundMode.None)]
|
||||
[Description("Setting the rounding mode allows the texture to be resized to a power of 2, often needed for less capable hardware.")]
|
||||
[Category("Input")]
|
||||
public RoundMode TextureRoundingMode
|
||||
{
|
||||
get { return roundMode; }
|
||||
set { roundMode = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Alpha Mode")]
|
||||
[DefaultValue(AlphaMode.None)]
|
||||
[Description("Setting the alpha mode allows improved quality when generating mip maps.")]
|
||||
[Category("Input")]
|
||||
public AlphaMode AlphaMode
|
||||
{
|
||||
get { return alphaMode; }
|
||||
set { alphaMode = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Enable Cuda")]
|
||||
[DefaultValue(true)]
|
||||
[Description("When true Cuda will be utilised if available.")]
|
||||
[Category("Compressor")]
|
||||
public bool EnableCuda
|
||||
{
|
||||
get { return enableCuda; }
|
||||
set { enableCuda = value; }
|
||||
}
|
||||
|
||||
public override TextureContent Process(TextureContent input, ContentProcessorContext context)
|
||||
{
|
||||
//System.Diagnostics.Debugger.Launch();
|
||||
|
||||
input.Validate();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
using (CompressorOptionsBundle compressor = new CompressorOptionsBundle())
|
||||
{
|
||||
compressor.InputOptions.ResetTextureLayout();
|
||||
|
||||
TextureType textureType = FindTextureType(input);
|
||||
|
||||
/*
|
||||
* Set options
|
||||
*/
|
||||
|
||||
compressor.InputOptions.SetTextureLayout(textureType, input.Faces[0][0].Width, input.Faces[0][0].Height, 1);
|
||||
compressor.InputOptions.SetFormat(InputFormat.BGRA_8UB);
|
||||
compressor.InputOptions.SetAlphaMode(AlphaMode);
|
||||
compressor.InputOptions.SetMipmapFilter(MipMapFilter);
|
||||
compressor.InputOptions.SetMipmapGeneration(GenerateMipmaps, MaxMipMapLevel);
|
||||
compressor.InputOptions.SetRoundMode(TextureRoundingMode);
|
||||
compressor.InputOptions.SetWrapMode(WrapMode);
|
||||
|
||||
compressor.InputOptions.SetGamma(InputGamma, OutputGamma);
|
||||
compressor.InputOptions.SetNormalizeMipmaps(false);
|
||||
compressor.InputOptions.SetNormalMap(false);
|
||||
compressor.InputOptions.SetConvertToNormalMap(false);
|
||||
|
||||
compressor.CompressionOptions.SetQuality(Quality);
|
||||
|
||||
GeneralOutputHandler outputHandler;
|
||||
|
||||
switch (TextureFormat)
|
||||
{
|
||||
case TextureOutputFormat.Colour:
|
||||
compressor.CompressionOptions.SetFormat(Format.RGBA);
|
||||
outputHandler = new PixelOutputHandler<Color>(textureType);
|
||||
break;
|
||||
|
||||
case TextureOutputFormat.Normals:
|
||||
compressor.CompressionOptions.SetFormat(Format.RGBA);
|
||||
outputHandler = new PixelOutputHandler<Color>(textureType);
|
||||
|
||||
compressor.InputOptions.SetNormalizeMipmaps(true);
|
||||
compressor.InputOptions.SetNormalMap(true);
|
||||
compressor.InputOptions.SetConvertToNormalMap(ConvertToNormalMap);
|
||||
compressor.InputOptions.SetGamma(1.0f, 1.0f);
|
||||
break;
|
||||
|
||||
case TextureOutputFormat.DXT1:
|
||||
compressor.CompressionOptions.SetFormat(Format.DXT1);
|
||||
outputHandler = new Dxt1OutputHandler(textureType);
|
||||
break;
|
||||
|
||||
case TextureOutputFormat.DXT1a:
|
||||
compressor.CompressionOptions.SetFormat(Format.DXT1a);
|
||||
outputHandler = new Dxt1OutputHandler(textureType);
|
||||
break;
|
||||
|
||||
case TextureOutputFormat.DXT3:
|
||||
compressor.CompressionOptions.SetFormat(Format.DXT3);
|
||||
outputHandler = new Dxt3OutputHandler(textureType);
|
||||
break;
|
||||
|
||||
case TextureOutputFormat.DXT5:
|
||||
compressor.CompressionOptions.SetFormat(Format.DXT5);
|
||||
outputHandler = new Dxt5OutputHandler(textureType);
|
||||
break;
|
||||
|
||||
case TextureOutputFormat.DXT5n:
|
||||
//FIXME: We force fastest quality since the normal compression mode is _very_ slow.
|
||||
compressor.CompressionOptions.SetQuality(Quality.Fastest);
|
||||
|
||||
compressor.CompressionOptions.SetFormat(Format.DXT5n);
|
||||
|
||||
compressor.InputOptions.SetNormalizeMipmaps(true);
|
||||
compressor.InputOptions.SetNormalMap(true);
|
||||
compressor.InputOptions.SetConvertToNormalMap(ConvertToNormalMap);
|
||||
compressor.InputOptions.SetGamma(1.0f, 1.0f);
|
||||
|
||||
outputHandler = new Dxt5OutputHandler(textureType);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unknown texture output format: " + TextureFormat);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set input data
|
||||
*/
|
||||
|
||||
//TODO: Use a float format when texture tools support it.
|
||||
input.ConvertBitmapType(typeof(PixelBitmapContent<Color>));
|
||||
|
||||
for (int i = 0; i < input.Faces.Count; i++)
|
||||
{
|
||||
MipmapChain mipChain = input.Faces[i];
|
||||
|
||||
for (int j = 0; j < mipChain.Count; j++)
|
||||
{
|
||||
BitmapContent bitmap = mipChain[j];
|
||||
|
||||
byte[] bitmapData = bitmap.GetPixelData();
|
||||
|
||||
//FIXME: When we move to XNA 4 the layout of Color will change, hence we need to swizzle the input.
|
||||
compressor.InputOptions.SetMipmapData(bitmapData, bitmap.Width, bitmap.Height, 1, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup output
|
||||
*/
|
||||
|
||||
|
||||
compressor.OutputOptions.SetOutputHandler(outputHandler);
|
||||
compressor.OutputOptions.SetOutputHeader(false);
|
||||
|
||||
/*
|
||||
* Go!
|
||||
*/
|
||||
compressor.Compressor.SetEnableCuda(EnableCuda);
|
||||
|
||||
compressor.Compress();
|
||||
/*
|
||||
* Check the output makes sense.
|
||||
*/
|
||||
|
||||
outputHandler.OutputTextureContent.Validate();
|
||||
|
||||
return outputHandler.OutputTextureContent;
|
||||
}
|
||||
}
|
||||
catch (TextureToolsException ttexcept)
|
||||
{
|
||||
throw ConvertException(ttexcept);
|
||||
}
|
||||
}
|
||||
|
||||
private TextureType FindTextureType(TextureContent input)
|
||||
{
|
||||
if (input is Texture2DContent)
|
||||
return TextureType.Texture2D;
|
||||
else if (input is TextureCubeContent)
|
||||
return TextureType.TextureCube;
|
||||
else
|
||||
throw new InvalidContentException("Invalid texture type, cube maps are not supported", input.Identity);
|
||||
}
|
||||
|
||||
|
||||
private Exception ConvertException(TextureToolsException ttexcept)
|
||||
{
|
||||
switch (ttexcept.ErrorCode)
|
||||
{
|
||||
case Error.UnsupportedFeature:
|
||||
return new NotSupportedException("Attempt to use a unsupported feature of NVIDIA Texture Tools",ttexcept);
|
||||
|
||||
case Error.InvalidInput:
|
||||
return new InvalidContentException("Invalid input to NVIDIA texture tools", ttexcept);
|
||||
|
||||
case Error.CudaError:
|
||||
case Error.Unknown:
|
||||
return new InvalidOperationException("NVIDIA Texture Tools returned the following error: " + ttexcept.Message, ttexcept);
|
||||
|
||||
case Error.FileOpen:
|
||||
case Error.FileWrite:
|
||||
return new IOException("NVIDIA Texture Tools returned the following error: " + ttexcept.Message, ttexcept);
|
||||
|
||||
default:
|
||||
return new InvalidOperationException("NVIDIA Texture Tools returned an unknown error: " + ttexcept.Message, ttexcept);
|
||||
}
|
||||
}
|
||||
|
||||
private class Dxt1OutputHandler : GeneralOutputHandler
|
||||
{
|
||||
public Dxt1OutputHandler(TextureType textureType) : base(textureType)
|
||||
{
|
||||
}
|
||||
|
||||
protected override BitmapContent CreateBitmapContent(int width, int height)
|
||||
{
|
||||
return new Dxt1BitmapContent(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
private class Dxt3OutputHandler : GeneralOutputHandler
|
||||
{
|
||||
public Dxt3OutputHandler(TextureType textureType)
|
||||
: base(textureType)
|
||||
{
|
||||
}
|
||||
|
||||
protected override BitmapContent CreateBitmapContent(int width, int height)
|
||||
{
|
||||
return new Dxt3BitmapContent(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
private class Dxt5OutputHandler : GeneralOutputHandler
|
||||
{
|
||||
public Dxt5OutputHandler(TextureType textureType)
|
||||
: base(textureType)
|
||||
{
|
||||
}
|
||||
|
||||
protected override BitmapContent CreateBitmapContent(int width, int height)
|
||||
{
|
||||
return new Dxt5BitmapContent(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
private class PixelOutputHandler<T> : GeneralOutputHandler
|
||||
where T : struct, System.IEquatable<T>
|
||||
{
|
||||
public PixelOutputHandler(TextureType textureType)
|
||||
: base(textureType)
|
||||
{
|
||||
}
|
||||
|
||||
protected override BitmapContent CreateBitmapContent(int width, int height)
|
||||
{
|
||||
return new PixelBitmapContent<T>(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class GeneralOutputHandler : OutputHandlerBase
|
||||
{
|
||||
TextureContent outputTextureContent;
|
||||
|
||||
byte[] tempBitmapData;
|
||||
|
||||
int dataWidth = -1;
|
||||
int dataHeight = -1;
|
||||
int dataSize = -1;
|
||||
|
||||
int faceIndex = -1;
|
||||
int mipIndex = -1;
|
||||
int dataIndex = 0;
|
||||
|
||||
public TextureContent OutputTextureContent
|
||||
{
|
||||
get
|
||||
{
|
||||
CommitLevel();
|
||||
return outputTextureContent;
|
||||
}
|
||||
}
|
||||
|
||||
protected GeneralOutputHandler(TextureType textureType)
|
||||
{
|
||||
switch (textureType)
|
||||
{
|
||||
case TextureType.Texture2D:
|
||||
outputTextureContent = new Texture2DContent();
|
||||
break;
|
||||
|
||||
case TextureType.TextureCube:
|
||||
outputTextureContent = new TextureCubeContent();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unknown texture type: " + textureType);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void BeginImage(int size, int width, int height, int depth, int face, int miplevel)
|
||||
{
|
||||
CommitLevel();
|
||||
|
||||
dataIndex = 0;
|
||||
mipIndex = miplevel;
|
||||
faceIndex = face;
|
||||
|
||||
dataWidth = width;
|
||||
dataHeight = height;
|
||||
dataSize = size;
|
||||
|
||||
tempBitmapData = new byte[size];
|
||||
}
|
||||
|
||||
protected override void WriteData(byte[] dataBuffer, int startIndex, int count)
|
||||
{
|
||||
Array.Copy(dataBuffer, startIndex, tempBitmapData, dataIndex, count);
|
||||
dataIndex += count;
|
||||
}
|
||||
|
||||
protected abstract BitmapContent CreateBitmapContent(int width, int height);
|
||||
|
||||
private void CommitLevel()
|
||||
{
|
||||
if (faceIndex >= 0)
|
||||
{
|
||||
BitmapContent newBitmap = CreateBitmapContent(dataWidth, dataHeight);
|
||||
|
||||
newBitmap.SetPixelData(tempBitmapData);
|
||||
|
||||
outputTextureContent.Faces[faceIndex].Add(newBitmap);
|
||||
|
||||
dataSize = -1;
|
||||
dataWidth = dataHeight = -1;
|
||||
faceIndex = -1;
|
||||
mipIndex = -1;
|
||||
dataIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue