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.