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.
FromStream
una secuencia de memoria que contenía un mapa de bits de 32 bits (guardado en png), al igual que su otro ejemplo, pero este método no creó una textura premultiplicada. Explícitamente premultiplicar cada color hizo el truco, gracias.Creo que la mayoría de los equipos comprometerían los cambios xnb (bueno, todos los cambios realmente, xnb incluidos) en su servidor svn (que se puede configurar de forma gratuita) y permitirían a otros (el artista, etc.) actualizar sus propias copias de trabajo.
De hecho, ese sería un buen mecanismo para que el artista controle la versión del arte original (prexnb). Él enviaría cambios a eso, usted actualizaría su copia de trabajo, la compilaría (convirtiéndola en un xnb en el proceso), confirmaría sus cambios, él actualizaría su copia de trabajo de su trabajo y todos tienen todos los cambios. (Tienes la última obra de arte en bruto, él tiene el xnb (s).
Esto escala muy bien también.
fuente
Seguí investigando esto y lo publicaré en beneficio de alguien que tenga la misma pregunta.
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.
fuente
Texture2D.FromStream
por sí solo no es suficiente. La razón de esto es que, dado que la versión 4 XNA funciona con texturas alfa premultiplicadas, y aunque la canalización de contenido se encarga de este procesamiento automáticamente,Texture2D.FromStream
no lo hará, así que es probable que tenga problemas al dibujar sprites con transparencia. Sin embargo, puedo publicar una solución de trabajo si lo desea.Texture2D.FromStream
no admite la carga de.BMP
archivos mientras que la canalización de contenido sí lo hace. Esto es algo que probablemente lo descartaría si usó algún.BMP
activo anteriormente y luego lo cambióTexture2D.FromStream
. También tengo una solución para esa limitación. Voy a seguir adelante y publicarlo.Mira este proyecto .
Le permitirá a su artista crear XNB de casi cualquier cosa que admita la canalización de contenido XNA predeterminada. El marco XNA redistribuible sigue siendo necesario, aunque su artista no necesita Visual Studio.
fuente