Hey there. After hours and hours we managed to hook up a custom effect to a model. Obviously, it turned out to be a few lines of code, but a lot of reading on github.
Here is how it goes. We basically left model processor unchanged and jumped right into material pocessor. But make sure to simply write a dummy modelprocessor even if it doesn’t do anything, since we have to invoke our custom material processor in the convert function (so override that). We precompiled our custom effect file and load it into compiledeffect field of the effectmaterialcontent:
if (input.Textures.ContainsKey(NormalMappingModelProcessor.NormalMapKey))
{
EffectMaterialContent effectMaterial = new EffectMaterialContent();
string directory = Path.GetDirectoryName(input.Identity.SourceFilename);
string effectSrc = Path.Combine(directory, "bin\\Windows\\Normal.xnb");
effectMaterial.CompiledEffect = new ExternalReference<CompiledEffectContent>(effectSrc, input.Identity);
// for some odd reason write methods of contenwriter enforce that the format of the filepath in
//the external reference
// is to be written with forward slash, rather than backward slash; whereas it seems that all
// path operations I perform with standard string methods are inserting backward slash, which
// is why I had to replace slashes below in the filename
effectMaterial.CompiledEffect.Filename = effectSrc.Replace("\\","/");
// Build all textures
foreach (string key in input.Textures.Keys)
{
ExternalReference<TextureContent> builtTexture = BuildTexture(input.Textures[key].Filename, input.Textures[key], context, key);
effectMaterial.Textures.Add(key,builtTexture);
}
// do similar external reference file name slash replacement for textures
string textureName = (Path.Combine(directory, "bin\\Windows\\normalSand_0.xnb")).Replace("\\","/");
effectMaterial.Textures["Bump"].Filename = textureName;
// return effectMaterialContent
return effectMaterial;
}
Ofcourse, you can always compile the effect file during the runtime (see the way it’s done in materialprocessor.cs on Monogame github page).
First observations: we don’t set the effect field of the effectmaterialcontent to a *.fx at all -> don’t get xnb reference problem -> profit :).
Second observation: it will compile nicely, however, that’s where the tricky part comes. You will get an exception saying that there is no respective content reader that can handle effectmaterial content.
Solution to tricky part:
After the processing business is completed, the pipeline will call modelwriter In the respective *.cs file on github you can see that the code eventually gets down to the line output.wirte(material), something like this. We made a conjecture that since there is no specific writer for the effectmaterialcontent it must simply invoke a superclass writer. But, as mentioned earlier, there is no reader assigned to it, which is why an exception will be generated when Content.Load function is called.
So, what do we need? First, we need to write a specific writer, in which we will specify the fields to output in a file, plus which reader should handle this type. It goes as follows:
First, override write function:
protected override void Write(ContentWriter output, EffectMaterialContent value) { output.WriteExternalReference(value.CompiledEffect); Dictionary<string, object> dict = new Dictionary<string, object>(); foreach(KeyValuePair<string,ExternalReference<TextureContent>> item in value.Textures){ dict.Add(item.Key,item.Value); } output.WriteObject<Dictionary<string,object>>(dict); }
Then, associate a reader with the type. In our case we simply used Monogame EffectMaterialReader, which actually reads out everything we need:
public override string GetRuntimeReader(TargetPlatform targetPlatform)
{
var type = typeof(ContentReader);
var readerType = type.Namespace + ".EffectMaterialReader, " + type.Assembly.FullName;
// Console.WriteLine(readerType);
return readerType;
}
And voila. I hope the code is self explanatory, and it will help out people struggling wit the same problem. Finaly, I just realized that even though EffectMaterial (which is a derived class from Effect) is properly assigned to a mesh which has a specific texture key on them (in our case, we assigned special effect for the normal maps), it doesn’t really do anything special at the moment. That is, you will probably have to write a specific Effect class yourself (similar to BasicEffect), to set textures and all the necessary parameters. Furthermore, that means you will have to write your own reader as well.