Il semble que la bonne réponse à cela soit d'ignorer ContentPipeline et d'utiliser Texture2D.FromStream pour charger les textures lors de l'exécution. Cette méthode fonctionne bien sur un PC et même s'il y aura une petite baisse de performances, c'est quelque chose que je peux optimiser une fois que je suis plus proche de la date de sortie. Pour l'instant, avoir la possibilité de modifier dynamiquement le contenu de l'éditeur et du jeu est exactement ce dont j'ai besoin. Une fois que le contenu est gelé, je peux l'optimiser en revenant à ContentPipeline.
Puisque vous avez choisi cette voie, je dois vous avertir que ce n'est pas aussi simple que de simplement l'utiliser Texture2D.FromStream
pour deux raisons:
Problème n ° 1 - Manque de support alpha pré-multiplié
XNA4 gère désormais les textures avec des couleurs au format alpha prémultiplié par défaut. Lorsque vous chargez une texture via le pipeline de contenu, ce traitement est effectué automatiquement pour vous. Malheureusement, Texture2D.FromStream
ne fait pas la même chose, donc toutes les textures qui nécessitent un certain degré de transparence seront chargées et rendues incorrectement. Voici une capture d'écran pour illustrer le problème:
Donc, pour obtenir les bons résultats, vous devez effectuer le traitement vous-même. La méthode que je vais montrer utilise le GPU pour faire le traitement, donc c'est assez rapide. Il était basé sur ce grand article . Bien sûr, vous pouvez également demander SpriteBatch
de rendre dans l'ancien mode NonPremultiplyAlpha mais je ne recommande pas vraiment de le faire.
Problème n ° 2 - Formats non pris en charge
Le pipeline de contenu prend en charge plus de formats que Texture2D.FromStream
. En particulier, Texture2D.FromStream
ne prend en charge que les formats png, jpg et gif. D'un autre côté, le pipeline de contenu prend en charge bmp, dds, dib, hdr, jpg, pfm, png, ppm et tga. Si vous essayez de charger un format usuporté, Texture2D.FromStream
vous obtiendrez un InvalidOperationException
avec peu d'informations supplémentaires.
J'avais vraiment besoin du support bmp sur mon moteur, donc pour ce cas particulier, j'ai trouvé une solution de contournement qui semble fonctionner correctement. Je ne connais cependant aucun des autres formats. Le problème avec ma méthode est que vous devez ajouter une référence à l' System.Drawing
assembly à votre projet, car il utilise des GDI Image.FromStream
qui prennent en charge plus de formats que Texture2D.FromStream
.
Si vous ne vous souciez pas de la prise en charge de bmp, vous pouvez facilement supprimer cette partie de ma solution et simplement effectuer le traitement alpha pré-multiplié.
Solution - Version simple (plus lente)
Tout d'abord, voici la solution la plus simple si vous ne vous souciez pas de la prise en charge de bmps. Dans cet exemple, l'étape de traitement se fait entièrement sur le CPU. C'est un peu plus lent que l'alternative que je vais montrer ci-dessous (j'ai testé les deux solutions) mais plus facile à comprendre:
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;
}
Si vous vous souciez des bmps, la chose que vous devez faire est de charger d'abord l'image avec GDI, puis de la convertir en PNG en interne avant de la passer à Texture2D.FromStream
. Voici le code qui fait cela:
// 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);
}
}
Solution - Version complexe (plus rapide)
Enfin, l'approche que j'utilise dans mes projets est d'utiliser le GPU pour faire le traitement à la place. Dans cette méthode, vous devez créer une cible de rendu, configurer correctement certains états de fusion et dessiner l'image deux fois avec un SpriteBatch. À la fin, je passe en revue l'ensemble du RenderTarget2D et clone le contenu dans un objet Texture2D distinct car le RenderTarget2D est volatile et ne survivra pas à des choses comme la modification de la taille du backbuffer, il est donc plus sûr de faire une copie.
Le plus drôle, c'est que même avec tout cela, lors de mes tests, cette approche a fonctionné environ 3 fois plus vite que l'approche CPU. C'est donc définitivement plus rapide que de parcourir chaque pixel et de calculer la couleur vous-même. Le code est un peu long donc je l'ai placé dans une boîte à pâte:
http://pastie.org/3651642
Ajoutez simplement cette classe à votre projet et utilisez-la aussi simplement que:
TextureLoader textureLoader = new TextureLoader(GraphicsDevice);
Texture2D texture = textureLoader.FromFile("Content/texture.png");
Remarque: Vous n'avez besoin de créer qu'une seule TextureLoader
instance pour l'ensemble du jeu. J'utilise également le correctif BMP, mais vous pouvez le supprimer si vous n'en avez pas besoin et gagner un tas de performances, ou tout simplement laisser le needsBmp
paramètre faux.