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.
FromStream
avec un flux de mémoire contenant un bitmap 32 bits (enregistré en png), tout comme votre autre exemple, mais cette méthode n'a pas créé de texture prémultipliée. Prémultiplier explicitement chaque couleur a fait l'affaire, merci.Je pense que la plupart des équipes commettraient les changements xnb (enfin, tous les changements vraiment, xnb inclus) sur leur serveur svn (qui peut être mis en place gratuitement) et permettraient à d'autres (l'artiste, etc.) de mettre à jour leurs propres copies de travail.
En fait, ce serait un bon mécanisme pour l'artiste pour contrôler la version de l'art original (pré-xnb). Il y apporterait des modifications, vous mettrez à jour votre copie de travail, la construirez (en faisant un xnb dans le processus), validera vos modifications, il mettra à jour sa copie de travail de votre travail et tout le monde a toutes les modifications. (Vous avez les dernières illustrations brutes, il a le ou les xnb).
Cela évolue très bien aussi.
la source
J'ai continué à enquêter sur cela et je le publierai au profit de quelqu'un qui a la même question.
Il semble que la bonne réponse à cette question consiste à ignorer ContentPipeline et à 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.
la source
Texture2D.FromStream
en soi ne suffit pas. La raison en est que, depuis la version 4, XNA fonctionne avec des textures alpha prémultipliées, et bien que le pipeline de contenu se charge automatiquement de ce traitement pour vous, ceTexture2D.FromStream
n'est pas le cas, vous rencontrerez probablement des problèmes lors du dessin de sprites avec transparence. Je peux publier une solution de travail si vous le souhaitez.Texture2D.FromStream
ne prend pas en charge le chargement de.BMP
fichiers , contrairement au pipeline de contenu. C'est quelque chose qui vous découragerait probablement si vous utilisiez des.BMP
actifs auparavant, puis que vous passiez àTexture2D.FromStream
. J'ai également une solution de contournement pour cette limitation. Je vais juste aller de l'avant et l'afficher.Découvrez ce projet .
Cela permettra à votre artiste de créer XNB à partir de tout ce que le pipeline de contenu XNA par défaut prend en charge. Le framework XNA redistribuable est toujours nécessaire bien que votre artiste n'ait pas besoin de Visual Studio.
la source