I'm trying to create a custom Monogame content pipeline extension that packs a bunch of individual sprites into a single sprite. I need help figuring out how to write and read the texture in the pipeline.
My writer outputs a Texture2DContent
(wrapped in SpriteSheetContent
model object) that contains a single BitmapContent
. My reader then attempts to create a Texture2D
from the serialized data, but encounters an exception when setting the data. I cannot make heads or tails of the exception since it's so cryptic.
Where am I going wrong?
Models
public class SpriteSheetContent
{
public Texture2DContent Texture { get; } = new Texture2DContent();
}
public class SpriteSheet
{
public Texture2D Texture;
}
Processor
public override SpriteSheetContent Process( SpriteSheetDeclaration declaration, ContentProcessorContext context )
{
var spriteSheetContent = new SpriteSheetContent();
BitmapContent sprite = <omitted code that creates a single bitmap content>;
spriteSheetContent.Texture.Mipmaps.Add( sprite );
return spriteSheetContent;
}
Writer
[ContentTypeWriter]
public class SpriteBoxWriter : ContentTypeWriter<SpriteSheetContent>
{
protected override void Write( ContentWriter writer, SpriteSheetContent spriteSheetContent )
{
BitmapContent bitmapContent = spriteSheetContent.Texture.Faces[0][0];
bitmapContent.TryGetFormat( out SurfaceFormat format );
writer.Write( (int)format );
writer.Write( bitmapContent.Width );
writer.Write( bitmapContent.Height );
byte[] pixelData = bitmapContent.GetPixelData();
writer.Write( pixelData.Length );
writer.Write( pixelData );
}
public override string GetRuntimeType( TargetPlatform targetPlatform )
{
return typeof( SpriteSheet ).AssemblyQualifiedName;
}
public override string GetRuntimeReader( TargetPlatform targetPlatform )
{
return "Monogame.SpriteBox.SpriteSheetReader, Monogame.SpriteBox";
}
}
Reader
public class SpriteSheetReader : ContentTypeReader<SpriteSheet>
{
protected override SpriteSheet Read( ContentReader reader, SpriteSheet existingInstance )
{
SpriteSheet spriteSheet = existingInstance == null ? new SpriteSheet() : existingInstance;
var format = (SurfaceFormat)reader.ReadInt32();
int width = reader.ReadInt32();
int height = reader.ReadInt32();
var graphicsDeviceManager = (GraphicsDeviceManager)reader.ContentManager.ServiceProvider.GetService( typeof( IGraphicsDeviceManager ) );
spriteSheet.Texture = new Texture2D( graphicsDeviceManager.GraphicsDevice, width, height, mipmap: false, format: format );
int pixelDataLength = reader.ReadInt32();
byte[] pixelData = reader.ReadBytes( pixelDataLength );
spriteSheet.Texture.SetData( pixelData ); // this line results in the exception
return spriteSheet;
}
}
Exception
SharpDX.SharpDXException
HResult=0x80070057
Message=HRESULT: [0x80070057], Module: [General], ApiCode: [E_INVALIDARG/Invalid Arguments], Message: The parameter is incorrect.
Source=SharpDX
StackTrace:
at SharpDX.Result.CheckError()
at SharpDX.Direct3D11.Device.CreateTexture2D(Texture2DDescription& descRef, DataBox[] initialDataRef, Texture2D texture2DOut)
at Microsoft.Xna.Framework.Graphics.Texture2D.CreateTexture()
at Microsoft.Xna.Framework.Graphics.Texture2D.PlatformSetData[T](Int32 level, T[] data, Int32 startIndex, Int32 elementCount)
at Microsoft.Xna.Framework.Graphics.Texture2D.SetData[T](T[] data)
at Monogame.SpriteBox.SpriteSheetReader.ReadTexture(ContentReader reader, SpriteSheet spriteSheet) in D:\Projects\Game Development\Monogame.SpriteBox\SpriteBox\SpriteSheetReader.cs:line 45
at Monogame.SpriteBox.SpriteSheetReader.Read(ContentReader reader, SpriteSheet existingInstance) in D:\Projects\Game Development\Monogame.SpriteBox\SpriteBox\SpriteSheetReader.cs:line 14
at Microsoft.Xna.Framework.Content.ContentTypeReader`1.Read(ContentReader input, Object existingInstance)
at Microsoft.Xna.Framework.Content.ContentReader.InnerReadObject[T](T existingInstance)
at Microsoft.Xna.Framework.Content.ContentReader.ReadObject[T]()
at Microsoft.Xna.Framework.Content.ContentReader.ReadAsset[T]()
at Microsoft.Xna.Framework.Content.ContentManager.ReadAsset[T](String assetName, Action`1 recordDisposableObject)
at Microsoft.Xna.Framework.Content.ContentManager.Load[T](String assetName)
at Game1.Game1.LoadContent() in D:\Projects\Game Development\Monogame.SpriteBox\Game1\Game1.cs:line 48
at Microsoft.Xna.Framework.Game.Initialize()
at Game1.Game1.Initialize() in D:\Projects\Game Development\Monogame.SpriteBox\Game1\Game1.cs:line 35
at Microsoft.Xna.Framework.Game.DoInitialize()
at Microsoft.Xna.Framework.Game.Run(GameRunBehavior runBehavior)
at Microsoft.Xna.Framework.Game.Run()
at Game1.Program.Main() in D:\Projects\Game Development\Monogame.SpriteBox\Game1\Program.cs:line 18
Other approach I've tried already
I thought simply doing writer.WriteObject<Texture2DContent>( spriteSheetContent.Texture )
in the writer, and then doing spriteSheet.Texture = reader.ReadObject<Texture2D>()
in the reader would work. However, this results in the same type of exception.
I even came across Monogame's Texture2DWriter and Texture2DReader, but using their examples leads to the same exception.