Usando XNA ContentPipeline para exportar un archivo en una máquina sin XNA GS completo

9

Mi juego usa Content Pipeline para cargar el spriteSheet en tiempo de ejecución. El artista del juego me envía la hoja de sprites modificada y yo hago una compilación en mi máquina y le envío un proyecto actualizado. Así que estoy buscando una manera de generar los archivos xnb en su máquina (esta es la salida de la tubería de contenido) sin que tenga que instalar el estudio completo de XNA Game.

1) No quiero que mi artista instale VS + Xna (sé que hay una versión gratuita de VS pero esto no escalará una vez que agreguemos más personas al equipo). 2) No estoy interesado en ejecutar este editor / herramienta en Xbox, por lo que funciona una solución única de Windows. 3) Conozco las opciones de MSBuild pero requieren XNA completo

Investigué el blog de Shawn y encontré la opción de usar Msbuild Sample o una nueva opción en XNA 4.0 que parecía prometedora aquí, pero parece que tiene la misma restricción: es necesario instalar XNA GS completo porque ContentPipeline no es parte de la lista roja de XNA.

Entonces, ¿alguien ha encontrado una solución para esto?

krolth
fuente

Respuestas:

11

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.FromStreampor 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:

ingrese la descripción de la imagen aquí

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 SpriteBatchque 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.FromStreamsolo 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.Drawingensamblaje a su proyecto, ya que utiliza GDI Image.FromStreamque 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 TextureLoaderinstancia 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 needsBmpparámetro como falso.

David Gouveia
fuente
¡Wow esto es genial! Me ayudará mucho :) Muchas gracias David, lo aprecio.
krolth
+1 En realidad, estaba usando FromStreamuna 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.
Groo
3

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.

Steve H
fuente
¿Entonces estás diciendo que no crees que se pueda hacer? Su sugerencia es agregar control de versiones a XNB y sprites, ya lo estamos haciendo. Pero no me gusta porque me convierto en el cuello de botella para ellos. Ya escribí una herramienta para editar las animaciones y pueden probarlas en el juego. Pero si realizan cambios en la hoja de sprites, deben esperar hasta que la construya antes de que puedan verla. Como puedes imaginar, si cometen un error, deben volver a hacerlo.
Krolth
@krolth ¿Es un gran problema que sus artistas obtengan VS Express y XNA como parte de su configuración para trabajar en un proyecto? Creo que en este punto la compensación de tener que escribir una guía y ayudar a las personas a superarla superará con creces la productividad que está perdiendo ahora, ya que los artistas no pueden ver su trabajo en el motor. Para agilizar el proceso, deles un archivo .BAT en el que puedan hacer doble clic para volver a compilar todo sin tener que abrir el IDE. Y si solo están ejecutando OS X, bueno, mierda dura. Bienvenido a game dev. Pueden comprometer sus sprites y esperar los próximos XNB comprometidos.
michael.bartnett
No es un gran problema, solo un dolor. Pero supongo que tendrá que hacer. ¡Gracias a todos por sus respuestas / comentarios!
krolth
1

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.

krolth
fuente
¿Has probado esto correctamente? Desde mi experiencia Texture2D.FromStreampor 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.FromStreamno 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.
David Gouveia
Además, Texture2D.FromStreamno admite la carga de .BMParchivos mientras que la canalización de contenido sí lo hace. Esto es algo que probablemente lo descartaría si usó algún .BMPactivo anteriormente y luego lo cambió Texture2D.FromStream. También tengo una solución para esa limitación. Voy a seguir adelante y publicarlo.
David Gouveia
0

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.

Trueno clásico
fuente
Gracias por la anotación. Al mirar el código, parece una modificación de la muestra de Microsoft a la que hice referencia. Depende de tener instalado el XNA Game Studio completo (ver ContentBuilder.cs). ¿Por qué crees que no?
krolth
No creo que no sea así. Si desea utilizar la canalización de contenido, debe tener el estudio completo del juego. Sin embargo, el proyecto evita que sus artistas tengan que usar Visual Studio. Las únicas otras alternativas son reescribir la canalización de contenido.
ClassicThunder