DualTextureEffect processor dont load second texture

Hi, I’m trying to load models with double UVs and double textures with DualTextureEffect processor same way I did it in XNA and with same models with no luck. I tried different formats and different export settings in Blender, I tried just add textures in UV windows as I did before and I tried making two textures in material/making two material. Did anyone know how to load models with two texture for DualTextureEffect?
I know I can load all second textures separately and set them, but that will be a real pain in my case.

If you are in a hurry, maybe do a custom model processor, and store the “diffuse0” as “Texture” in the OpacityCollection (or as usual, so don’t touch it), and “diffuse1” as “DualTexturing”.
Doing so will allow to build and load a model with dualtexture. But there may be a better/simpler solution.
If not, someone will explain it as i’ve never used it yet.

1 Like

Thanks for answering! I’m not sure I follow tho. I made new custom processor based on ModelProcessor, but I can’t wrap my head around where to get second texture from.
I tried to override Process, like this:

[ContentProcessor]
    public class ModelDualTextureProcessor : ModelProcessor
    {
        public override ModelContent Process(NodeContent input,
                                             ContentProcessorContext context)
        {
            ModelContent model = base.Process(input, context);
            
            Dictionary<string, object> tagData = new Dictionary<string, object>();
            model.Tag = tagData;

            string test ="";
            foreach (KeyValuePair<string, ExternalReference<TextureContent>> val in model.Meshes[0].MeshParts[0].Material.Textures)
            {
                test += val.Key + " " + val.Value.Name + "  ";
            }
            tagData.Add("TextureNames", test);

            return model;
        }

I wanted to get debug info in model tag. Only thing I get is “Texture”, no other textures so I guess I should override something before that. I tried to understand ModelProcessor + MaterialProcessor sources in Monogame, but I just don’t get it.

When I read the data from a model, I use something like this:

private Dictionary<string, string> TraverseGeometryForTextureDataRetrieval(MeshContent mesh)
{
	Dictionary<string, string> dFoundTextures = new Dictionary<string, string>();

	foreach(GeometryContent geometry in mesh.Geometry)
	{
		MaterialContent mc = geometry.Material;
		if(mc != null)
		{
			if(mc.Textures != null)
			{
				foreach(KeyValuePair<string, ExternalReference<TextureContent>> tex in mc.Textures)
				{
					if(!dFoundTextures.Keys.Contains(tex.Key.ToLower()))
						dFoundTextures.Add(tex.Key.ToLower(), tex.Value.Filename);
					else
						dFoundTextures[tex.Key.ToLower()] = tex.Value.Filename;
				}
			}
		}
	}
	return dFoundTextures;
}

Writing to a textfile what is returned by this method (for each submesh of course) or debugging the pipeline should give you hints about what is stored in the model.

Oh, I see, thanks! So I scaning for textures like this:

void FindEverything(NodeContent node)
        {
            MeshContent mesh = node as MeshContent;

            if (mesh != null)
            {
                foreach (GeometryContent geometry in mesh.Geometry)
                {
                    MaterialContent mc = geometry.Material;
                    if (mc != null)
                    {
                        if (mc.Textures != null)
                        {
                            foreach (KeyValuePair<string, ExternalReference<TextureContent>> tex in mc.Textures)
                            {
                                if (!dFoundTextures.Keys.Contains(tex.Key.ToLower()))
                                    dFoundTextures.Add(tex.Key.ToLower(), tex.Value.Filename);
                                else
                                    dFoundTextures[tex.Key.ToLower()] = tex.Value.Filename;
                            }
                        }
                    }
                }
            }

            // Recursively scan over the children of this node.
            foreach (NodeContent child in node.Children)
            {
                FindEverything(child);
            }
        }

And I’m using it simply like this:

Dictionary<string, string> dFoundTextures = new Dictionary<string, string>();

public override ModelContent Process(NodeContent input,
                                             ContentProcessorContext context)
        {

            FindEverything(input);

            Dictionary<string, object> tagData = new Dictionary<string, object>();
            model.Tag = tagData;

            string test = "";
            foreach (KeyValuePair<string, string> val in dFoundTextures)
            {
                test += val.Key + " " + val.Value + "  ";
            }

            tagData.Add("TextureNames", test);

            return base.Process(input, context);
}

But there is never more than one texture with key “Texture” with a path to original PNG file, no matter what I do in Blender. So my guess I should override something before Process or my models is somehow wrong afterall.
Also I noticed that “Texture” is always last texture from material in Blender.

I’m far from an expert with Blender.
But with this way of traversing geometry and getting all textures, you should see all the textures you attached to the model.
Maybe it needs a special importer for dualtextured models ?

I’m looking at all textures (with your code) even before “base.Process(input, context)” in Model-Processor, so as far as I understand it doesn’t matter what effect I set in the importer. It’s always one texture per MeshPart no matter what I tried or what model in what format I load.

Did you ever try to load model with two textures (like second normal/bump texture for example)? If you did and you had success with it, can you upload any of this models so I can debug it, please?

Right now I’m thinking I need to just give up even tho I really appreciate your help! I guess I will manually redo all models and upload/set second texture manually in monogame, it will really take a while in my case but it seems like the only option.

I did with the Sponza model :slight_smile:
You should give it a try to see if you can display it correctly (the model is not perfect, but it is free)

Maybe not the best way to do it, but it works with my deferred engine:
After this loop, calling base.Process should build the model with the new textures

note:_swLog is just an object writing debug infos to a file.

//Add the keys to the material, so they can be used by the shader
foreach(GeometryContent geometry in mesh.Geometry)
{
	//Prevent crashes when building. It can happen with models found on the web
	if(geometry.Material == null)
	{
		_swLog._w("****geometry.Material is null****", true);
		_swLog.Flush();
		continue;
	}
	if(geometry.Material.Textures == null)
	{
		_swLog._w("****geometry.Material.Textures is null****", true);
		_swLog.Flush();
		continue;
	}
	
	/* Replace _NormalMapKey with DualTexture2 for example: 
	the name must match the shader's texture parameter
	Mine is NormaMap in my shader, so _NormalMapKey is "NormalMap"
	*/
	if(geometry.Material.Textures.ContainsKey(_NormalMapKey))
	{
		ExternalReference<TextureContent> texRef = geometry.Material.Textures[_NormalMapKey];
		geometry.Material.Textures.Remove(_NormalMapKey);
		_swLog._d("NORMAL texRef=" + texRef.Filename, 1);
		_swLog.Flush();
		geometry.Material.Textures.Add("NormalMap", texRef);
	}
	else
	{
		geometry.Material.Textures.Add("NormalMap", new ExternalReference<TextureContent>(normalMapPath));
		_swLog._d("NORMAL MATERIAL SPECIFIED normalMapPath=" + normalMapPath, 1);
		_swLog.Flush();
	}

	//Example 2
	if(geometry.Material.Textures.ContainsKey(_SpecularMapKey))
	{
		ExternalReference<TextureContent> texRef = geometry.Material.Textures[_SpecularMapKey];
		geometry.Material.Textures.Remove(_SpecularMapKey);
		_swLog._d("SPECULAR texRef=" + texRef.Filename, 1);
		_swLog.Flush();
		geometry.Material.Textures.Add("SpecularMap", texRef);
	}
	else
	{
		geometry.Material.Textures.Add("SpecularMap", new ExternalReference<TextureContent>(specularMapPath));
		_swLog._d("SPECULAR MATERIAL SPECIFIED specularMapPath=" + specularMapPath, 1);
		_swLog.Flush();
	}
	
	//... etc
}