External Reference in Model Processor

I’ve been having issues getting a custom content processor to build an .fbx model I have been working with.
The model builds fine with the default model importer, but it fails when using my custom processor at the following lines:

EffectMaterialContent deferredShadingMaterial = new EffectMaterialContent();
deferredShadingMaterial.Effect = new ExternalReference<EffectContent>("Shaders/LightMap.xnb");

At first, I had the path written as "Shaders/LightMap.fx", which is how I would have done it in XNA, but it threw an exception, stating that it was not a valid .xnb file, so I am assuming it must reference the built .xnb file. I have verified that the shader is at the location, and it is built.

The error I get now is the following:

Couldn’t find a default importer for ‘Shaders/LightMap.xnb’!

I’ve looked at the source, and it appears to come from here

public void ResolveImporterAndProcessor(string sourceFilepath, ref string importerName, ref string processorName)
{
    // Resolve the importer name.
    if (string.IsNullOrEmpty(importerName))
        importerName = FindImporterByExtension(Path.GetExtension(sourceFilepath));
    if (string.IsNullOrEmpty(importerName))
        throw new Exception(string.Format("Couldn't find a default importer for '{0}'!", sourceFilepath));

    // Resolve the processor name.
    if (string.IsNullOrEmpty(processorName))
        processorName = FindDefaultProcessor(importerName);
    if (string.IsNullOrEmpty(processorName))
        throw new Exception(string.Format("Couldn't find a default processor for importer '{0}'!", importerName));
}

But I’m having a hard time tracing it backwards.
Any thoughts?

It should work the same as in XNA. (My content processors use the same code for XNA and MonoGame.)

You need to specify the path to the .fx file, not the .xnb file. Though I am not sure why your first example isn’t working.

I always pass the absolute path to the .fx file as the parameter. Perhaps, if you want to specify the relative path, you need to use the second constructor:

public ExternalReference(string filename, ContentIdentity relativeToContent)

thanks for the hints @mgarstenauer

I had a good look again at the source and I think I’m closer. I’m pretty sure you’re right about the absolute paths, although it appears to me that the file does need to end in .xnb, accoding to the code below :

public void WriteExternalReference<T>(ExternalReference<T> reference)
{
...
    // Make sure the filename ends with .xnb
    if (!fileName.EndsWith(".xnb"))
        throw new ArgumentException(string.Format("ExternalReference '{0}' must reference a .xnb file", fileName));
    // Make sure it is in the same root directory
    if (!fileName.StartsWith(rootDirectory, StringComparison.OrdinalIgnoreCase))
        throw new ArgumentException(string.Format("ExternalReference '{0}' must be in the root directory '{1}'", fileName, rootDirectory));
...
}

I’ve set mine to use an absolute path (which kind of sucks, is there no other way?) but unfortunately it did not solve my problem fully.
At the end of my ConvertMaterial function, I am returning the following:

return context.Convert<MaterialContent, MaterialContent>(deferredShadingMaterial, typeof(MaterialProcessor).Name);

this appears to be the code that is actually failing to begin with, the filename was a false positive, althought a separate issue. If anyone has any suggestions on what I’m doing wrong, or what’s the proper way to load shaders as external references to set them as the default shaders in my models, I’m open to advice

Hi. I bumped into the same kind of problem, using an fx file directly within a contentprocessor.
It is a real pain, it used to work like a charm with xna, but now i’m kinda stopped in my project.
Is it better to use
CompiledEffectContent compiledEffect = context.BuildAndLoadAsset<EffectContent, CompiledEffectContent> ?
Then is it convertible to MaterialContent after my own process when calling
return context.Convert<MaterialContent, MaterialContent>(deferredShadingMaterial, typeof(MaterialProcessor).Name);

Working on the exact problem. Also there is a discussion on a similar problem right here: Custom model processor Perhaps, some of us will find the solution

I saw this one too but i had to make a choice between the two :wink:
I made the processor output all of its members into a text file to see what is going on.
OpaqueData. Textures. CompiledEffectContent etc.
All of it seems ok but the name which is empty or null and identity is null.
Calling context.Convert<MaterialContent, MaterialContent> seems to always throw a null reference exception…
I will investigate later when i’m off.

Back after some tests.

With XNA, in my ConvertMaterial method i used this:

EffectMaterialContent deferredShadingMaterial = new EffectMaterialContent();
deferredShadingMaterial.Effect = new ExternalReference(“Fx/GBuffer/RenderGBuffer.fx”);
//deferredShadingMaterial.Effect = extref; //use this if xna way !

With Monogame, this way of doing things fails. If I set the full path to the shader i get this:

ExternalReference ‘I:/xMonoGame/____Swift/Engine/Content/Shaders/GBuffer/RenderGBuffer.fx’ must reference a .xnb file.

So i have made it use the xnb file and then i get:

error: Processor ‘MGContentProcessor_SwiftEngine’ had unexpected failure!
System.Exception: Couldn’t find a default importer for ‘I:/xMonoGame/____Swift/MGContentPipelineExt_SwiftEngine/bin/Debug/Content/Shaders/GBuffer/RenderGBuffer.xnb’!
à MonoGame.Framework.Content.Pipeline.Builder.PipelineManager.ResolveImporterAndProcessor(String sourceFilepath, String& importerName, String& processorName)
à MonoGame.Framework.Content.Pipeline.Builder.PipelineManager.BuildContent(String sourceFilepath, String outputFilepath, String importerName, String processorName, OpaqueDataDictionary processorParameters)
à MonoGame.Framework.Content.Pipeline.Builder.PipelineProcessorContext.BuildAsset[TInput,TOutput](ExternalReference1 sourceAsset, String processorName, OpaqueDataDictionary processorParameters, String importerName, String assetName) à Microsoft.Xna.Framework.Content.Pipeline.ContentProcessorContext.BuildAsset[TInput,TOutput](ExternalReference1 sourceAsset, String processorName)
à Microsoft.Xna.Framework.Content.Pipeline.Processors.MaterialProcessor.Process(MaterialContent input, ContentProcessorContext context)
à Microsoft.Xna.Framework.Content.Pipeline.ContentProcessor2.Microsoft.Xna.Framework.Content.Pipeline.IContentProcessor.Process(Object input, ContentProcessorContext context) à MonoGame.Framework.Content.Pipeline.Builder.PipelineProcessorContext.Convert[TInput,TOutput](TInput input, String processorName, OpaqueDataDictionary processorParameters) à MGContentPipelineExt_SwiftEngine.MGContentProcessor_SwiftEngine.ConvertMaterial(MaterialContent material, ContentProcessorContext context) dans i:\xMonoGame\____Swift\MGContentPipelineExt_SwiftEngine\MGContentProcessor_SwiftEngine.cs:ligne 1642 à Microsoft.Xna.Framework.Content.Pipeline.Processors.ModelProcessor.Process(NodeContent input, ContentProcessorContext context) à MGContentPipelineExt_SwiftEngine.MGContentProcessor_SwiftEngine.Process(NodeContent input, ContentProcessorContext context) dans i:\xMonoGame\____Swift\MGContentPipelineExt_SwiftEngine\MGContentProcessor_SwiftEngine.cs:ligne 219 à Microsoft.Xna.Framework.Content.Pipeline.ContentProcessor2.Microsoft.Xna.Framework.Content.Pipeline.IContentProcessor.Process(Object input, ContentProcessorContext context)
à MonoGame.Framework.Content.Pipeline.Builder.PipelineManager.ProcessContent(PipelineBuildEvent pipelineEvent)

If i load the effect with:

ExternalReference<EffectContent> extref = new ExternalReference<EffectContent>("I:/xMonoGame/____Swift/Engine/Content/Shaders/GBuffer/RenderGBuffer.fx");
ExternalReference<CompiledEffectContent> compiledEffect = context.BuildAsset<EffectContent, CompiledEffectContent>(extref, "EffectProcessor");
CompiledEffectContent compiledEffect2 = context.BuildAndLoadAsset<EffectContent, CompiledEffectContent>(
	new ExternalReference<EffectContent>("Shaders/GBuffer/RenderGBuffer.fx"), "EffectProcessor");
EffectMaterialContent deferredShadingMaterial = new EffectMaterialContent();
deferredShadingMaterial.CompiledEffect = compiledEffect; //fails with CompiledEffect2

I still get
ExternalReference ‘I:/xMonoGame/____Swift/Engine/Content/Shaders/GBuffer/RenderGBuffer.fx’ must reference a .xnb file.

So what must i conclude ? Is Monogame sort of broken to assign effects to models during the content pipeline build ?
All projects using some sort of deferred rendering are using a monogame version prior to v 3.5 (or maybe even 3.4)
All others projects i have seen so far use forward rendering i think, and “simple” shaders that don’t need to be assigned in the processor pipe.