Sembra che la risposta giusta a questa sia saltare la ContentPipeline e usare Texture2D.FromStream per caricare le trame in fase di esecuzione. Questo metodo funziona bene su un PC e anche se ci saranno piccole prestazioni, questo è qualcosa che posso ottimizzare una volta più vicino alla data di rilascio. Per ora, avere la possibilità di modificare dinamicamente il contenuto sia per l'editor che per il gioco è esattamente ciò di cui ho bisogno. Una volta congelato il contenuto, posso ottimizzarlo tornando alla ContentPipeline.
Dato che hai scelto questa strada, ti avverto che in realtà non è così semplice come usare solo Texture2D.FromStream
per due motivi:
Problema n. 1 - Mancanza di supporto alfa pre-moltiplicato
XNA4 ora gestisce trame con colori in formato alfa premoltiplicato per impostazione predefinita. Quando si carica una trama attraverso la pipeline di contenuti, tale elaborazione viene eseguita automaticamente. Sfortunatamente Texture2D.FromStream
non fa la stessa cosa, quindi tutte le trame che richiedono un certo grado di trasparenza verranno caricate e renderizzate in modo errato. Di seguito è riportato uno screenshot per illustrare il problema:
Quindi, al fine di ottenere i risultati corretti, è necessario eseguire l'elaborazione da soli. Il metodo che mostrerò usa la GPU per eseguire l'elaborazione, quindi è piuttosto veloce. Era basato su questo fantastico articolo . Ovviamente potresti anche istruire il SpriteBatch
rendering nella vecchia modalità NonPremultiplyAlpha, ma non ti consiglio di farlo.
Problema n. 2 - Formati non supportati
La pipeline di contenuti supporta più formati di Texture2D.FromStream
. In particolare, Texture2D.FromStream
supporta solo png, jpg e gif. D'altra parte, la pipeline di contenuti supporta bmp, dds, dib, hdr, jpg, pfm, png, ppm e tga. Se provi a caricare un formato usuported tramite Texture2D.FromStream
otterrai un InvalidOperationException
po 'di informazioni aggiuntive.
Avevo davvero bisogno del supporto bmp sul mio motore, quindi per quel caso particolare ho trovato una soluzione che sembra funzionare bene. Non conosco nessuno degli altri formati però. Il problema con il mio metodo è che è necessario aggiungere un riferimento System.Drawing
all'assembly al progetto, perché utilizza GDI Image.FromStream
che supporta più formati di Texture2D.FromStream
.
Se non ti interessa supportare bmp, puoi facilmente abbandonare quella parte della mia soluzione e fare semplicemente l'elaborazione alfa pre-moltiplicata.
Soluzione - Versione semplice (più lenta)
Prima di tutto, ecco la soluzione più semplice se non ti interessa supportare bmps. In questo esempio la fase di elaborazione viene eseguita interamente sulla CPU. È un po 'più lento dell'alternativa che mostrerò di seguito (ho fatto un benchmark di entrambe le soluzioni) ma più facile da capire:
public static Texture2D FromStream(GraphicsDevice graphicsDevice, Stream stream)
{
Texture2D texture = Texture2D.FromStream(graphicsDevice, stream);
Color[] data = new Color[texture.Width * texture.Height];
texture.GetData(data);
for (int i = 0; i != data.Length; ++i)
data[i] = Color.FromNonPremultiplied(data[i].ToVector4());
texture.SetData(data);
return texture;
}
Se ti interessa bmps, la cosa che devi fare è caricare prima l'immagine con GDI e poi convertirla in PNG internamente prima di passarla a Texture2D.FromStream
. Ecco il codice che lo fa:
// Load image using GDI because Texture2D.FromStream doesn't support BMP
using (Image image = Image.FromStream(stream))
{
// Now create a MemoryStream which will be passed to Texture2D after converting to PNG internally
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ms.Seek(0, SeekOrigin.Begin);
texture = Texture2D.FromStream(_graphicsDevice, ms);
}
}
Soluzione - Versione complessa (più veloce)
Infine, l'approccio che utilizzo nei miei progetti è quello di utilizzare la GPU per eseguire l'elaborazione. In questo metodo è necessario creare una destinazione di rendering, impostare correttamente alcuni stati di fusione e disegnare due volte l'immagine con uno SpriteBatch. Alla fine vado sull'intero RenderTarget2D e clono il contenuto in un oggetto Texture2D separato perché RenderTarget2D è volatile e non sopravvive a cose come la modifica delle dimensioni del backbuffer, quindi è più sicuro fare una copia.
La cosa divertente è che anche con tutto ciò, nei miei test questo approccio ha funzionato circa 3 volte più veloce dell'approccio CPU. Quindi è decisamente più veloce che andare su ogni pixel e calcolare tu stesso il colore. Il codice è un po 'lungo, quindi l'ho inserito in un pastebin:
http://pastie.org/3651642
Aggiungi quella classe al tuo progetto e usala come:
TextureLoader textureLoader = new TextureLoader(GraphicsDevice);
Texture2D texture = textureLoader.FromFile("Content/texture.png");
Nota: devi solo creare TextureLoader
un'istanza per l'intero gioco. Inoltre sto usando la correzione BMP ma puoi eliminarla se non ti serve e ottenere un sacco di prestazioni, o semplicemente lasciare il needsBmp
parametro come falso.