Parece que la respuesta correcta a esto es omitir ContentPipeline y usar Texture2D.FromStream para cargar las texturas en tiempo de ejecución. Este método funciona bien en una PC y, aunque habrá un pequeño impacto en el rendimiento, esto es algo que puedo optimizar una vez que esté más cerca de la fecha de lanzamiento. Por ahora, tener la capacidad de modificar dinámicamente el contenido tanto para el editor como para el juego es exactamente lo que necesito. Una vez que el contenido está congelado, puedo optimizarlo volviendo a ContentPipeline.
Como ha elegido esta ruta, debo advertirle que en realidad no es tan simple como usarla solo Texture2D.FromStream
por dos razones:
Problema # 1 - Falta de soporte alfa pre-multiplicado
XNA4 ahora maneja texturas con colores en formato alfa premultiplicado por defecto. Cuando carga una textura a través de la canalización de contenido, ese procesamiento se realiza automáticamente. Desafortunadamente Texture2D.FromStream
, no hace lo mismo, por lo que cualquier textura que requiera cierto grado de transparencia se cargará y se representará incorrectamente. A continuación se muestra una captura de pantalla para ilustrar el problema:
Entonces, para obtener los resultados correctos, debe realizar el procesamiento usted mismo. El método que mostraré usa la GPU para hacer el procesamiento, por lo que es bastante rápido. Se basó en este gran artículo . Por supuesto, también podría indicar SpriteBatch
que se procese en el antiguo modo NonPremultiplyAlpha, pero realmente no recomiendo hacerlo.
Problema 2: formatos no compatibles
La canalización de contenido admite más formatos que Texture2D.FromStream
. En particular, Texture2D.FromStream
solo es compatible con png, jpg y gif. Por otro lado, la canalización de contenido admite bmp, dds, dib, hdr, jpg, pfm, png, ppm y tga. Si intenta cargar un formato usuportado Texture2D.FromStream
, obtendrá unInvalidOperationException
con poca información adicional.
Realmente necesitaba soporte bmp en mi motor, así que para ese caso en particular encontré una solución alternativa que parece funcionar bien. Sin embargo, no conozco ninguno de los otros formatos. El problema con mi método es que debe agregar una referencia al System.Drawing
ensamblaje a su proyecto, ya que utiliza GDI Image.FromStream
que admite más formatos que Texture2D.FromStream
.
Si no le importa admitir bmp, puede eliminar fácilmente esa parte de mi solución y simplemente hacer el procesamiento alfa pre-multiplicado.
Solución - Versión simple (más lenta)
En primer lugar, esta es la solución más simple si no le importa admitir bmps. En este ejemplo, la etapa de procesamiento se realiza completamente en la CPU. Es un poco más lento que la alternativa que mostraré a continuación (comparé ambas soluciones) pero es más fácil de entender:
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 le interesan los bmps, lo que debe hacer es cargar primero la imagen con GDI y luego convertirla a PNG internamente antes de pasarla Texture2D.FromStream
. Aquí está el código que hace eso:
// 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);
}
}
Solución: versión compleja (más rápida)
Finalmente, el enfoque que uso en mis proyectos es usar la GPU para hacer el procesamiento. En este método, debe crear un objetivo de renderizado, configurar algunos estados de mezcla correctamente y dibujar la imagen dos veces con un SpriteBatch. Al final reviso todo RenderTarget2D y clono el contenido en un objeto Texture2D separado porque RenderTarget2D es volátil y no sobrevivirá a cosas como cambiar el tamaño del backbuffer, por lo que es más seguro hacer una copia.
Lo curioso es que incluso con todo esto, en mis pruebas, este enfoque funcionó aproximadamente 3 veces más rápido que el enfoque de la CPU. Por lo tanto, es definitivamente más rápido que revisar cada píxel y calcular el color usted mismo. El código es un poco largo, así que lo coloqué en un pastebin:
http://pastie.org/3651642
Simplemente agregue esa clase a su proyecto y utilícela tan simple como:
TextureLoader textureLoader = new TextureLoader(GraphicsDevice);
Texture2D texture = textureLoader.FromFile("Content/texture.png");
Nota: Solo necesitas crear una TextureLoader
instancia para todo el juego. También estoy usando la solución BMP, pero puede eliminarla si no la necesita y obtener un gran rendimiento, o simplemente dejar el needsBmp
parámetro como falso.