diff --git a/project/vc8/Nvidia.TextureTools/TextureTools.cs b/project/vc8/Nvidia.TextureTools/TextureTools.cs index 74cdc9b..c4b0da2 100644 --- a/project/vc8/Nvidia.TextureTools/TextureTools.cs +++ b/project/vc8/Nvidia.TextureTools/TextureTools.cs @@ -134,23 +134,45 @@ namespace Nvidia.TextureTools /// public enum Error { + Unknown, InvalidInput, - UserInterruption, - UnsupportedFeature, + + UnsupportedFeature, CudaError, - Unknown, - FileOpen, + + FileOpen, FileWrite, } #endregion - #endregion - #region public class InputOptions - /// + #endregion + + #region Exception Class + + public class TextureToolsException : ApplicationException + { + Error errorCode = Error.Unknown; + + public Error ErrorCode + { + get { return errorCode; } + } + + public TextureToolsException(Error errorCode) : base(Compressor.ErrorString(errorCode)) + { + this.errorCode = errorCode; + } + } + + #endregion + + + #region public class InputOptions + /// /// Input options. /// - public class InputOptions + public class InputOptions : IDisposable { #region Bindings [DllImport("nvtt"), SuppressUnmanagedCodeSecurity] @@ -223,10 +245,7 @@ namespace Nvidia.TextureTools { options = nvttCreateInputOptions(); } - ~InputOptions() - { - nvttDestroyInputOptions(options); - } + public void SetTextureLayout(TextureType type, int w, int h, int d) { @@ -237,6 +256,22 @@ namespace Nvidia.TextureTools nvttResetInputOptionsTextureLayout(options); } + public void SetMipmapData(byte[] data, int width, int height, int depth, int face, int mipmap) + { + //unsafe() would be cleaner, but that would require compiling with the unsafe compiler option.... + GCHandle gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned); + + try + { + SetMipmapData(gcHandle.AddrOfPinnedObject(), width, height, depth, face, mipmap); + } + finally + { + gcHandle.Free(); + } + + } + public void SetMipmapData(IntPtr data, int width, int height, int depth, int face, int mipmap) { nvttSetInputOptionsMipmapData(options, data, width, height, depth, face, mipmap); @@ -326,14 +361,39 @@ namespace Nvidia.TextureTools { nvttSetInputOptionsRoundMode(options, mode); } - } + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (options != IntPtr.Zero) + { + nvttDestroyInputOptions(options); + options = IntPtr.Zero; + } + } + + ~InputOptions() + { + Dispose(false); + } + + #endregion + } #endregion #region public class CompressionOptions /// /// Compression options. /// - public class CompressionOptions + public class CompressionOptions : IDisposable { #region Bindings [DllImport("nvtt"), SuppressUnmanagedCodeSecurity] @@ -356,6 +416,7 @@ namespace Nvidia.TextureTools [DllImport("nvtt"), SuppressUnmanagedCodeSecurity] private extern static void nvttSetCompressionOptionsQuantization(IntPtr compressionOptions, bool colorDithering, bool alphaDithering, bool binaryAlpha, int alphaThreshold); + #endregion internal IntPtr options; @@ -364,10 +425,6 @@ namespace Nvidia.TextureTools { options = nvttCreateCompressionOptions(); } - ~CompressionOptions() - { - nvttDestroyCompressionOptions(options); - } public void SetFormat(Format format) { @@ -403,23 +460,104 @@ namespace Nvidia.TextureTools { nvttSetCompressionOptionsQuantization(options, colorDithering, alphaDithering, binaryAlpha, alphaThreshold); } - } + + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (options != IntPtr.Zero) + { + nvttDestroyCompressionOptions(options); + options = IntPtr.Zero; + } + } + + ~CompressionOptions() + { + Dispose(false); + } + + #endregion + } #endregion #region public class OutputOptions + + public interface IOutputHandler + { + void BeginImage(int size, int width, int height, int depth, int face, int miplevel); + void WriteDataUnsafe(IntPtr data, int size); + } + + /* + * This class provides a nicer interface for the output handler by taking care of the marshalling of the image data. + * However the IOutputHandler interface is still provided to allow the user to do this themselves to avoid the + * additional copying and memory allocations. + */ + public abstract class OutputHandlerBase : IOutputHandler + { + private byte[] tempData; + + protected OutputHandlerBase() + { + } + + protected abstract void BeginImage(int size, int width, int height, int depth, int face, int miplevel); + protected abstract void WriteData(byte[] dataBuffer, int startIndex, int count); + + #region IOutputHandler Members + + void IOutputHandler.BeginImage(int size, int width, int height, int depth, int face, int miplevel) + { + BeginImage(size, width, height, depth, face, miplevel); + } + + void IOutputHandler.WriteDataUnsafe(IntPtr data, int size) + { + if ((tempData == null) || (size > tempData.Length)) + tempData = new byte[size]; + + Marshal.Copy(data, tempData, 0, size); + + // Zero additional buffer elements to to aid reproducability of bugs. + Array.Clear(tempData, size, tempData.Length - size); + + WriteData(tempData, 0, size); + } + + #endregion + } + /// /// Output options. /// - public class OutputOptions + public class OutputOptions : IDisposable { - #region Delegates - public delegate void ErrorHandler(Error error); - private delegate void WriteDataDelegate(IntPtr data, int size); - private delegate void ImageDelegate(int size, int width, int height, int depth, int face, int miplevel); + #region Delegates + + private delegate void InternalErrorHandlerDelegate(Error error); + private delegate void WriteDataDelegate(IntPtr data, int size); + private delegate void ImageDelegate(int size, int width, int height, int depth, int face, int miplevel); + #endregion - #region Bindings - [DllImport("nvtt"), SuppressUnmanagedCodeSecurity] + private Error lastErrorCode = Error.Unknown; + + internal Error LastErrorCode + { + get { return lastErrorCode; } + } + + #region Bindings + [DllImport("nvtt"), SuppressUnmanagedCodeSecurity] private extern static IntPtr nvttCreateOutputOptions(); [DllImport("nvtt"), SuppressUnmanagedCodeSecurity] @@ -429,51 +567,121 @@ namespace Nvidia.TextureTools private extern static void nvttSetOutputOptionsFileName(IntPtr outputOptions, string fileName); [DllImport("nvtt"), SuppressUnmanagedCodeSecurity] - private extern static void nvttSetOutputOptionsErrorHandler(IntPtr outputOptions, ErrorHandler errorHandler); - - private void ErrorCallback(Error error) - { - if (Error != null) Error(error); - } + private extern static void nvttSetOutputOptionsErrorHandler(IntPtr outputOptions, InternalErrorHandlerDelegate errorHandler); [DllImport("nvtt"), SuppressUnmanagedCodeSecurity] private extern static void nvttSetOutputOptionsOutputHeader(IntPtr outputOptions, bool b); - //[DllImport("nvtt"), SuppressUnmanagedCodeSecurity] - //private extern static void nvttSetOutputOptionsOutputHandler(IntPtr outputOptions, WriteDataDelegate writeData, ImageDelegate image); + [DllImport("nvtt"), SuppressUnmanagedCodeSecurity] + private extern static void nvttSetOutputOptionsOutputHandler(IntPtr outputOptions, IntPtr writeData, IntPtr image); #endregion internal IntPtr options; + + //Note: these references are used to prevent garbage collection of the delegates(and hence class) if they are + //only referenced from unmanaged land. + private WriteDataDelegate writeDataDelegate; + private ImageDelegate beginImageDelegate; + + private IOutputHandler currentOutputHandler; public OutputOptions() { options = nvttCreateOutputOptions(); - nvttSetOutputOptionsErrorHandler(options, new ErrorHandler(ErrorCallback)); - } - ~OutputOptions() - { - nvttDestroyOutputOptions(options); + nvttSetOutputOptionsErrorHandler(options, ErrorCallback); } + public void SetFileName(string fileName) { nvttSetOutputOptionsFileName(options, fileName); } - - public event ErrorHandler Error; - + public void SetOutputHeader(bool b) { nvttSetOutputOptionsOutputHeader(options, b); } - // @@ Add OutputHandler interface. - } + public void SetOutputHandler(IOutputHandler outputHandler) + { + if (outputHandler != null) + { + //We need to store a ref in order to prevent garbage collection. + WriteDataDelegate tmpWriteDataDelegate = new WriteDataDelegate(WriteDataCallback); + ImageDelegate tmpBeginImageDelegate = new ImageDelegate(ImageCallback); + + IntPtr ptrWriteData = Marshal.GetFunctionPointerForDelegate(tmpWriteDataDelegate); + IntPtr ptrBeginImage = Marshal.GetFunctionPointerForDelegate(tmpBeginImageDelegate); + + + nvttSetOutputOptionsOutputHandler(options, ptrWriteData, ptrBeginImage); + + writeDataDelegate = tmpWriteDataDelegate; + beginImageDelegate = tmpBeginImageDelegate; + + currentOutputHandler = outputHandler; + } + else + { + nvttSetOutputOptionsOutputHandler(options, IntPtr.Zero, IntPtr.Zero); + + writeDataDelegate = null; + beginImageDelegate = null; + + currentOutputHandler = null; + } + } + + private void ErrorCallback(Error error) + { + lastErrorCode = error; + } + + private void WriteDataCallback(IntPtr data, int size) + { + if (currentOutputHandler != null) currentOutputHandler.WriteDataUnsafe(data, size); + } + + private void ImageCallback(int size, int width, int height, int depth, int face, int miplevel) + { + if (currentOutputHandler != null) currentOutputHandler.BeginImage(size, width, height, depth, face, miplevel); + } + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (options != IntPtr.Zero) + { + nvttDestroyOutputOptions(options); + + options = IntPtr.Zero; + } + + writeDataDelegate = null; + beginImageDelegate = null; + currentOutputHandler = null; + } + + ~OutputOptions() + { + Dispose(false); + } + + #endregion + } #endregion - #region public static class Compressor - public class Compressor + #region public class Compressor + public class Compressor : IDisposable { #region Bindings [DllImport("nvtt"), SuppressUnmanagedCodeSecurity] @@ -490,6 +698,12 @@ namespace Nvidia.TextureTools [DllImport("nvtt"), SuppressUnmanagedCodeSecurity] private static extern IntPtr nvttErrorString(Error error); + + [DllImport("nvtt"), SuppressUnmanagedCodeSecurity] + private extern static uint nvttVersion(); + + [DllImport("nvtt"), SuppressUnmanagedCodeSecurity] + private extern static void nvttEnableCudaCompression(IntPtr compressor, bool enable); #endregion @@ -499,15 +713,14 @@ namespace Nvidia.TextureTools { compressor = nvttCreateCompressor(); } - - ~Compressor() + + public void Compress(InputOptions input, CompressionOptions compression, OutputOptions output) { - nvttDestroyCompressor(compressor); - } - - public bool Compress(InputOptions input, CompressionOptions compression, OutputOptions output) - { - return nvttCompress(compressor, input.options, compression.options, output.options); + if (!nvttCompress(compressor, input.options, compression.options, output.options)) + { + //An error occured, use the last error registered. + throw new TextureToolsException(output.LastErrorCode); + } } public int EstimateSize(InputOptions input, CompressionOptions compression) @@ -520,7 +733,138 @@ namespace Nvidia.TextureTools return Marshal.PtrToStringAnsi(nvttErrorString(error)); } - } + public static uint Version() + { + return nvttVersion(); + } + + public void SetEnableCuda(bool enableCuda) + { + nvttEnableCudaCompression(compressor, enableCuda); + } + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (compressor != IntPtr.Zero) + { + nvttDestroyCompressor(compressor); + compressor = IntPtr.Zero; + } + } + + ~Compressor() + { + Dispose(false); + } + + #endregion + } #endregion + #region public class CompressorOptionsBundle + + /* + * We provide a class which combines all the objects, this simplifies usage such as: + * + * using(CompressorOptionsBundle compressor = new CompressorOptionsBundle()) + * { + * compressor.InputOptions.SetMipmapData(...); + * ... + * } + * + * Making it easy to write exception safe code etc. + */ + public class CompressorOptionsBundle : IDisposable + { + InputOptions inputOptions; + CompressionOptions compressionOptions; + OutputOptions outputOptions; + + Compressor compressor; + + public InputOptions InputOptions + { + get { return inputOptions; } + } + + + public CompressionOptions CompressionOptions + { + get { return compressionOptions; } + } + + public OutputOptions OutputOptions + { + get { return outputOptions; } + } + + public Compressor Compressor + { + get { return compressor; } + } + + public CompressorOptionsBundle() + { + inputOptions = new InputOptions(); + compressionOptions = new CompressionOptions(); + outputOptions = new OutputOptions(); + compressor = new Compressor(); + } + + public void Compress() + { + compressor.Compress(inputOptions, compressionOptions, outputOptions); + } + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (inputOptions != null) + { + inputOptions.Dispose(); + inputOptions = null; + } + + if (compressionOptions != null) + { + compressionOptions.Dispose(); + compressionOptions = null; + } + + if (outputOptions != null) + { + outputOptions.Dispose(); + outputOptions = null; + } + + if (compressor != null) + { + compressor.Dispose(); + compressor = null; + } + } + } + #endregion + + } + + #endregion + } // Nvidia.TextureTools namespace diff --git a/project/vc8/NvidiaTextureToolsProcessor/NvidiaTextureProcessor.cs b/project/vc8/NvidiaTextureToolsProcessor/NvidiaTextureProcessor.cs new file mode 100644 index 0000000..21943a7 --- /dev/null +++ b/project/vc8/NvidiaTextureToolsProcessor/NvidiaTextureProcessor.cs @@ -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 + { + // 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(textureType); + break; + + case TextureOutputFormat.Normals: + compressor.CompressionOptions.SetFormat(Format.RGBA); + outputHandler = new PixelOutputHandler(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)); + + 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 : GeneralOutputHandler + where T : struct, System.IEquatable + { + public PixelOutputHandler(TextureType textureType) + : base(textureType) + { + } + + protected override BitmapContent CreateBitmapContent(int width, int height) + { + return new PixelBitmapContent(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; + } + } + } + } +} \ No newline at end of file diff --git a/project/vc8/NvidiaTextureToolsProcessor/NvidiaTextureToolsProcessor.csproj b/project/vc8/NvidiaTextureToolsProcessor/NvidiaTextureToolsProcessor.csproj new file mode 100644 index 0000000..34b581d --- /dev/null +++ b/project/vc8/NvidiaTextureToolsProcessor/NvidiaTextureToolsProcessor.csproj @@ -0,0 +1,122 @@ + + + {F4446AE2-B5E1-4D17-B33C-275A6770FD50} + Debug + x86 + Library + Properties + NvidiaTextureToolsProcessor + NvidiaTextureToolsProcessor + v3.1 + v3.5 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + x86 + Windows + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + x86 + Windows + + + true + full + false + bin\Xbox 360\Debug + DEBUG;TRACE + prompt + 4 + false + x86 + Xbox 360 + + + pdbonly + true + bin\Xbox 360\Release + TRACE + prompt + 4 + false + x86 + Xbox 360 + + + true + full + false + bin\Zune\Debug + DEBUG;TRACE + prompt + 4 + false + x86 + Zune + + + pdbonly + true + bin\Zune\Release + TRACE + prompt + 4 + false + x86 + Zune + + + + False + True + + + False + True + + + False + true + + + False + + + False + + + 3.5 + + + 3.5 + + + + + TextureTools.cs + + + + + + + + \ No newline at end of file diff --git a/project/vc8/NvidiaTextureToolsProcessor/NvidiaTextureToolsProcessor.sln b/project/vc8/NvidiaTextureToolsProcessor/NvidiaTextureToolsProcessor.sln new file mode 100644 index 0000000..cf34e41 --- /dev/null +++ b/project/vc8/NvidiaTextureToolsProcessor/NvidiaTextureToolsProcessor.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C# Express 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NvidiaTextureToolsProcessor", "NvidiaTextureToolsProcessor.csproj", "{F4446AE2-B5E1-4D17-B33C-275A6770FD50}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Debug|Xbox 360 = Debug|Xbox 360 + Debug|Zune = Debug|Zune + Release|x86 = Release|x86 + Release|Xbox 360 = Release|Xbox 360 + Release|Zune = Release|Zune + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F4446AE2-B5E1-4D17-B33C-275A6770FD50}.Debug|x86.ActiveCfg = Debug|x86 + {F4446AE2-B5E1-4D17-B33C-275A6770FD50}.Debug|x86.Build.0 = Debug|x86 + {F4446AE2-B5E1-4D17-B33C-275A6770FD50}.Debug|Xbox 360.ActiveCfg = Debug|Xbox 360 + {F4446AE2-B5E1-4D17-B33C-275A6770FD50}.Debug|Xbox 360.Build.0 = Debug|Xbox 360 + {F4446AE2-B5E1-4D17-B33C-275A6770FD50}.Debug|Zune.ActiveCfg = Debug|Zune + {F4446AE2-B5E1-4D17-B33C-275A6770FD50}.Debug|Zune.Build.0 = Debug|Zune + {F4446AE2-B5E1-4D17-B33C-275A6770FD50}.Release|x86.ActiveCfg = Release|x86 + {F4446AE2-B5E1-4D17-B33C-275A6770FD50}.Release|x86.Build.0 = Release|x86 + {F4446AE2-B5E1-4D17-B33C-275A6770FD50}.Release|Xbox 360.ActiveCfg = Release|Xbox 360 + {F4446AE2-B5E1-4D17-B33C-275A6770FD50}.Release|Xbox 360.Build.0 = Release|Xbox 360 + {F4446AE2-B5E1-4D17-B33C-275A6770FD50}.Release|Zune.ActiveCfg = Release|Zune + {F4446AE2-B5E1-4D17-B33C-275A6770FD50}.Release|Zune.Build.0 = Release|Zune + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/project/vc8/NvidiaTextureToolsProcessor/Properties/AssemblyInfo.cs b/project/vc8/NvidiaTextureToolsProcessor/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..3650ee3 --- /dev/null +++ b/project/vc8/NvidiaTextureToolsProcessor/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NvidiaTextureToolsProcessor")] +[assembly: AssemblyDescription("An XNA Content Processor for use with GameStudio 3.1")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("NvidiaTextureToolsProcessor")] +[assembly: AssemblyCopyright("Copyright © 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6c0942a6-4e6a-411b-9275-d2676f9720d3")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/nvtt/OutputOptions.cpp b/src/nvtt/OutputOptions.cpp index 7cf8420..300e6b8 100644 --- a/src/nvtt/OutputOptions.cpp +++ b/src/nvtt/OutputOptions.cpp @@ -28,6 +28,7 @@ using namespace nvtt; OutputOptions::OutputOptions() : m(*new OutputOptions::Private()) { + m.cWrapperProxy = NULL; reset(); } @@ -72,7 +73,6 @@ void OutputOptions::setOutputHeader(bool outputHeader) m.outputHeader = outputHeader; } - bool OutputOptions::Private::openFile() const { if (!fileName.isNull()) diff --git a/src/nvtt/OutputOptions.h b/src/nvtt/OutputOptions.h index bddc28f..f67b172 100644 --- a/src/nvtt/OutputOptions.h +++ b/src/nvtt/OutputOptions.h @@ -65,6 +65,8 @@ namespace nvtt ErrorHandler * errorHandler; bool outputHeader; + void * cWrapperProxy; + bool openFile() const; void closeFile() const; }; diff --git a/src/nvtt/nvtt_wrapper.cpp b/src/nvtt/nvtt_wrapper.cpp index 66c16aa..0874d96 100644 --- a/src/nvtt/nvtt_wrapper.cpp +++ b/src/nvtt/nvtt_wrapper.cpp @@ -1,7 +1,49 @@ +#include //For NULL... #include "nvtt.h" #include "nvtt_wrapper.h" +#include "OutputOptions.h" + +namespace nvttCWrap +{ + //Simple class to re-route calls to the C++ interfaces to C functions + struct HandlerProxy : public nvtt::ErrorHandler, public nvtt::OutputHandler + { + public: + + HandlerProxy() + { + errorFunc = NULL; + outputFunc = NULL; + imageFunc = NULL; + } + + virtual void error(nvtt::Error e) + { + if(errorFunc != NULL) + errorFunc((NvttError)e); + } + + virtual void beginImage(int size, int width, int height, int depth, int face, int miplevel) + { + if(imageFunc != NULL) + imageFunc(size, width, height, depth, face, miplevel); + } + + virtual bool writeData(const void * data, int size) + { + if(outputFunc != NULL) + return (outputFunc(data, size) != NVTT_False); + else + return true; //Just say we succeed anyway... despite nothing being done, in case the user only set the beginImage() func. + } + + nvttErrorHandler errorFunc; + nvttOutputHandler outputFunc; + nvttImageHandler imageFunc; + }; +} // InputOptions class. NvttInputOptions * nvttCreateInputOptions() @@ -110,6 +152,7 @@ void nvttSetInputOptionsRoundMode(NvttInputOptions * inputOptions, NvttRoundMode } + // CompressionOptions class. NvttCompressionOptions * nvttCreateCompressionOptions() { @@ -136,11 +179,6 @@ void nvttSetCompressionOptionsColorWeights(NvttCompressionOptions * compressionO compressionOptions->setColorWeights(red, green, blue, alpha); } -/*void nvttEnableCompressionOptionsCudaCompression(NvttCompressionOptions * compressionOptions, NvttBoolean enable) -{ - compressionOptions->enableCudaCompression(enable != NVTT_False); -}*/ - void nvttSetCompressionOptionsPixelFormat(NvttCompressionOptions * compressionOptions, unsigned int bitcount, unsigned int rmask, unsigned int gmask, unsigned int bmask, unsigned int amask) { compressionOptions->setPixelFormat(bitcount, rmask, gmask, bmask, amask); @@ -151,16 +189,23 @@ void nvttSetCompressionOptionsQuantization(NvttCompressionOptions * compressionO compressionOptions->setQuantization(colorDithering != NVTT_False, alphaDithering != NVTT_False, binaryAlpha != NVTT_False, alphaThreshold); } - // OutputOptions class. NvttOutputOptions * nvttCreateOutputOptions() { - return new nvtt::OutputOptions(); + nvtt::OutputOptions * outputOptions = new nvtt::OutputOptions(); + nvttCWrap::HandlerProxy * handlerProxy = new nvttCWrap::HandlerProxy(); + + outputOptions->m.cWrapperProxy = handlerProxy; + + return outputOptions; } void nvttDestroyOutputOptions(NvttOutputOptions * outputOptions) { + nvttCWrap::HandlerProxy * handlerProxy = (nvttCWrap::HandlerProxy *)outputOptions->m.cWrapperProxy; + delete outputOptions; + delete handlerProxy; } void nvttSetOutputOptionsFileName(NvttOutputOptions * outputOptions, const char * fileName) @@ -172,19 +217,45 @@ void nvttSetOutputOptionsOutputHeader(NvttOutputOptions * outputOptions, NvttBoo { outputOptions->setOutputHeader(b != NVTT_False); } -/* + + void nvttSetOutputOptionsErrorHandler(NvttOutputOptions * outputOptions, nvttErrorHandler errorHandler) { - outputOptions->setErrorHandler(errorHandler); + nvttCWrap::HandlerProxy * handlerProxy = (nvttCWrap::HandlerProxy *)outputOptions->m.cWrapperProxy; + + handlerProxy->errorFunc = errorHandler; + + if(errorHandler == NULL) + outputOptions->setErrorHandler(NULL); + else + outputOptions->setErrorHandler(handlerProxy); } void nvttSetOutputOptionsOutputHandler(NvttOutputOptions * outputOptions, nvttOutputHandler outputHandler, nvttImageHandler imageHandler) { + nvttCWrap::HandlerProxy * handlerProxy = (nvttCWrap::HandlerProxy *)outputOptions->m.cWrapperProxy; + + handlerProxy->outputFunc = outputHandler; + handlerProxy->imageFunc = imageHandler; + + if((outputHandler == NULL) && (imageHandler == NULL)) + outputOptions->setOutputHandler(NULL); + else + outputOptions->setOutputHandler(handlerProxy); } -*/ // Compressor class. +NvttCompressor * nvttCreateCompressor() +{ + return new nvtt::Compressor(); +} + +void nvttDestroyCompressor(NvttCompressor * compressor) +{ + delete compressor; +} + NvttBoolean nvttCompress(const NvttCompressor * compressor, const NvttInputOptions * inputOptions, const NvttCompressionOptions * compressionOptions, const NvttOutputOptions * outputOptions) { return (NvttBoolean)compressor->process(*inputOptions, *compressionOptions, *outputOptions); @@ -195,6 +266,11 @@ int nvttEstimateSize(const NvttCompressor * compressor, const NvttInputOptions * return compressor->estimateSize(*inputOptions, *compressionOptions); } +void nvttEnableCudaCompression(NvttCompressor * compressor, NvttBoolean enable) +{ + compressor->enableCudaAcceleration(enable != NVTT_False); +} + // Global functions. const char * nvttErrorString(NvttError e) diff --git a/src/nvtt/nvtt_wrapper.h b/src/nvtt/nvtt_wrapper.h index 100a4a3..30cdec6 100644 --- a/src/nvtt/nvtt_wrapper.h +++ b/src/nvtt/nvtt_wrapper.h @@ -149,11 +149,12 @@ typedef enum typedef enum { + NVTT_Error_Unknown, NVTT_Error_InvalidInput, - NVTT_Error_UserInterruption, + NVTT_Error_UnsupportedFeature, NVTT_Error_CudaError, - NVTT_Error_Unknown, + NVTT_Error_FileOpen, NVTT_Error_FileWrite, } NvttError; @@ -170,9 +171,9 @@ extern "C" { #endif // Callbacks -//typedef void (* nvttErrorHandler)(NvttError e); -//typedef void (* nvttOutputHandler)(const void * data, int size); -//typedef void (* nvttImageHandler)(int size, int width, int height, int depth, int face, int miplevel); +typedef void (* nvttErrorHandler)(NvttError e); +typedef NvttBoolean (* nvttOutputHandler)(const void * data, int size); +typedef void (* nvttImageHandler)(int size, int width, int height, int depth, int face, int miplevel); // InputOptions class. @@ -199,7 +200,6 @@ NVTT_API void nvttSetInputOptionsLinearTransform(NvttInputOptions * inputOptions NVTT_API void nvttSetInputOptionsMaxExtents(NvttInputOptions * inputOptions, int dim); NVTT_API void nvttSetInputOptionsRoundMode(NvttInputOptions * inputOptions, NvttRoundMode mode); - // CompressionOptions class. NVTT_API NvttCompressionOptions * nvttCreateCompressionOptions(); NVTT_API void nvttDestroyCompressionOptions(NvttCompressionOptions * compressionOptions); @@ -210,16 +210,14 @@ NVTT_API void nvttSetCompressionOptionsColorWeights(NvttCompressionOptions * com NVTT_API void nvttSetCompressionOptionsPixelFormat(NvttCompressionOptions * compressionOptions, unsigned int bitcount, unsigned int rmask, unsigned int gmask, unsigned int bmask, unsigned int amask); NVTT_API void nvttSetCompressionOptionsQuantization(NvttCompressionOptions * compressionOptions, NvttBoolean colorDithering, NvttBoolean alphaDithering, NvttBoolean binaryAlpha, int alphaThreshold); - // OutputOptions class. NVTT_API NvttOutputOptions * nvttCreateOutputOptions(); NVTT_API void nvttDestroyOutputOptions(NvttOutputOptions * outputOptions); NVTT_API void nvttSetOutputOptionsFileName(NvttOutputOptions * outputOptions, const char * fileName); NVTT_API void nvttSetOutputOptionsOutputHeader(NvttOutputOptions * outputOptions, NvttBoolean b); -//NVTT_API void nvttSetOutputOptionsErrorHandler(NvttOutputOptions * outputOptions, nvttErrorHandler errorHandler); -//NVTT_API void nvttSetOutputOptionsOutputHandler(NvttOutputOptions * outputOptions, nvttOutputHandler outputHandler, nvttImageHandler imageHandler); - +NVTT_API void nvttSetOutputOptionsErrorHandler(NvttOutputOptions * outputOptions, nvttErrorHandler errorHandler); +NVTT_API void nvttSetOutputOptionsOutputHandler(NvttOutputOptions * outputOptions, nvttOutputHandler outputHandler, nvttImageHandler imageHandler); // Compressor class. NVTT_API NvttCompressor * nvttCreateCompressor(); @@ -228,6 +226,7 @@ NVTT_API void nvttDestroyCompressor(NvttCompressor * compressor); NVTT_API NvttBoolean nvttCompress(const NvttCompressor * compressor, const NvttInputOptions * inputOptions, const NvttCompressionOptions * compressionOptions, const NvttOutputOptions * outputOptions); NVTT_API int nvttEstimateSize(const NvttCompressor * compressor, const NvttInputOptions * inputOptions, const NvttCompressionOptions * compressionOptions); +NVTT_API void nvttEnableCudaCompression(NvttCompressor * compressor, NvttBoolean enable); // Global functions. NVTT_API const char * nvttErrorString(NvttError e);