¿Cómo crear un shader que use las texturas cargadas automáticamente por el modelo en XNA?

30 06 2007

 

Bueno esta pregunta me surgió al intentar escribir mi propio basicEffect para XNA.

El problema:

Yo quería crear un shader nuevo de iluminación para los modelos, pero este shader me gustaría que pudiera usarlo con objetos texturizados, y lo óptimo siempre es tener un modelo y una sola textura, pero muchas veces te bajas modelos de alguna página, y estos están texturizados por partes, por lo que tienen varias texturas. Entonces en mi shader no podría tener un atributo texture ColorMapTexture, sino que necesitaría tantos como tuviera mi modelo. Pero como trabaja entonces BasicEffect, ya que he hecho bastantes pruebas e incluso en alguno de los GameCompontents de XNACommunity he usado un gorilla el cual usa varias texturas. ¿Cómo han escrito un shader que use las texturas que fueron asignadas desde el 3D studio?

 

Investigando:

Bueno lo primero que se me ocurrió fue mirar el código fuente que se usa para dibujar un modelo en XNA usando BasicEffect:

 

foreach (ModelMesh m in model.Meshes)
{

foreach (BasicEffect effect in m.Effects)

{

effect.EnableDefaultLighting();
effect.World = Matrix.CreateTranslation(0,0,0);

effect.View = camera.View;
effect.Projection = camera.Projection;

}
m.Draw();

}

Y podemos ver como se le pasan al efecto varios parámetros como la matrix de view, projection y world, pero por ningún lado la textura. Entonces si pones un breakpoint en m.Effects te das cuenta de que este tiene almacenado la información internamente con una instancia de BasicEffect por lo que al hacer el casting ya effect tiene la textura de esa parte del modelo, pero tu de ninguna forma puedes acceder a la textura escribiendo m.Effects.Texture. Por lo que pensé que a lo mejor a la hora de cargar el modelo estaría la solución y me fijé en el siguiente código:

model = content.Load<Model>(“modelo3D”);

Y si nos paramos también a debugear un poco vemos que al cargar este modelo dentro de content tenemos un diccionario llamada “loadedAssets” que contiene los effect, texture y todo aquello que cargemos medienate content.load<T>. Y si pones un breakpoint y cargas un modelo que tiene asignado multiples texturas puedes observar como al pasar la linea load, se cargan en el diccionario primero las texturas y finalmente el modelo. Por lo que ahí está toda la información que necesitas, pero como accedes al diccionario loadedAssets, puedes implementar un contentProcessor, pero ¿No habrá una solución más fácil?

En este punto la solución, fue intentar ver como lo hacen los de XNA, por lo que me cogí el reflector y ala a decompilar (si soy un poco bruto, es que soy de pueblo XD). Pero nada el shader BasicEffect viene ya precompilado, por lo que imposible, y al intentar ver lo que hacía el content tampoco encontraba nada.

Solución:

Después de buscar mucho por internet y no encontrar nada, ya que también es complicado buscar este problema en concreto, vamos a mi me ha costado más escribir el título de este post que el post en si XD.

Volví hacia atrás y mirando el código de cómo dibujar con BasicEffect, ahora presté más atencción a lo de que m.Effects contiene internamente la información con una estructura basicEffect y por ello ha hacer el casting a basicEffect obtenemos la textura aunque en m.Effects, no hubiera ninguna forma de hacernos con el puntero a la textura.

Por lo que solución y código genérico que he usado es:

public virtual void ChangeEffectUsedByModel(Model model, Effect replacementEffect)
{

Dictionary<Effect, Effect> effectMapping = new Dictionary<Effect, Effect>();foreach (ModelMesh mesh in model.Meshes)
{

foreach (BasicEffect oldEffect in mesh.Effects)
{

if (!effectMapping.ContainsKey(oldEffect))
{

Effect newEffect = replacementEffect.Clone(replacementEffect.GraphicsDevice);

newEffect.Parameters["Texture"].SetValue(oldEffect.Texture);

newEffect.Parameters["TextureEnabled"].SetValue(

oldEffect.TextureEnabled);

effectMapping.Add(oldEffect, newEffect);

}

}

foreach (ModelMeshPart meshPart in mesh.MeshParts)
{

meshPart.Effect = effectMapping[meshPart.Effect];

}

}

}

he intentado explicarlo sin alargarme demasiado aunque se podrían aclarar muchas cosas, pero espero que os sirva.





XNA vs. ANX

26 06 2007

Hace un par de días, Javi Cantón me dijo que posteara sobre el desarrollo de Mono.Xna, qué tal iban las cosas y tal, porque creo que a algunos de vosotros os interesa. Así que he consultado un poco a fuentes fidedignas y ahora estoy en disposición de hacer un reportaje de cómo va el proyecto.

Para empezar, creo que encontraréis interesante el post de Stuart Carnie en su blog “Aussie Bloke.” La imagen que veis colgada ahi está tomada de un juego tipo Pong muy simple, compilado contra las librerías de Mono.Xna en lugar de las de XNA, en su estado en Abril de este año. El juego en sí mismo, no usaba grandes alardes. Por ejemplo, no usaba el Content Manager para cargar modelos ni utilizaba GameComponents para los objetos del juego. Se trataba de un simple y llano renderizado a pelo de texturas. Pero funcionaba y era jugable. En Abril.

A día de hoy, Mono.Xna ha mejorado un poco en cuanto a la versión de Abril, aunque el trabajo realizado sobre el SVN, todo hay que decirlo, no ha sido demasiado importante. Somos poquitos los que ahora mismo estamos escribiendo código, y la mayoría hemos sufrido, precisamente desde Abril, un bombardeo de trabajos y exámenes en nuestras universidades. Muchos de vosotros lo entenderéis. Sin embargo ahora, en veranito, las cosas vuelven a moverse.

La semana que viene está prevista, aunque pendiente de confirmación, una reunión via Skype entre todos los desarrolladores activos de Mono.Xna. Los temas de la reunión van a ser muy suculentos. Por ejemplo se va a tratar de establecer un sistema de trabajo para descodificar el contenido de los ficheros de contenidos de XNA, tanto los de contenidos gráficos como los creados por XACT y se acordará un camino para el desarrollo de una herramienta XACT libre. La palabra “libre” significa que hay que mirar muy mucho las licencias y patentes, por eso también está abierta la proposición de nombres nuevos para el proyecto, que eviten problemas con la patente “XNA” de Microsoft. El que más está gustando hasta ahora es “ANX,” os podéis hacer una idea de por qué. Si tenéis ideas de cómo llamar al “proyecto anteriormente conocido como Mono.Xna” haced uso intensivo de los comentarios en este post, o meteros en el grupo de desarrollo de Mono.Xna.

Aunque estar atentos al desarrollo de “la tecnología anteriormente conocida como Mono.Xna” es interesante, ¿habéis pensado en meteros directamente en el ajo? En realidad no hace falta ser un crack para empezar a programar en Mono.Xna. Yo ya estoy en ello. Lo único que hay que tener son ganas. Hay una enorme cantidad de funciones matemáticas por programar en las clases matemáticas de XNA, funciones que con un poco de Google, salen solas. Si te parece difícil, puedes ayudar a escribir NUnits contra XNA, para que comprobemos la funcionalidad de Mono.Xna. Y si te sientes con ganas de sudar bits, puedes meterte de lleno con el ContentManager, los GameComponents o la clase Graphics, que tienen tela que cortar. Cualquier ayuda es bienvenida. Y lo mejor de todo es que aprendes mientras desarrollas un proyecto importante.

Y quién sabe, a lo mejor al año que viene te puedes apuntar al “Summer of Code” con un proyecto de XNA.

Si quieres saber más sobre XNA, Mono.Xna y su desarrollo también puedes contactar conmigo en neozack@gmail.com, dejar un bonito comentario en este post, o contactar con nosotros en XNACommunity o en el foro.