Custom model processor

Hi, it’s me again with almost the same question.
My custom model processor almost works, but I have got one problem.
This piece of code:

customMaterial.Effect = new ExternalReference<EffectContent>("MainEffect.fx");
customMaterial.CompiledEffect = context.BuildAsset<EffectContent, CompiledEffectContent>(customMaterial.Effect, "EffectProcessor");

gives me an exception: “External reference “MainEffect.fx” must reference a .xnb file.
So the question is, how can I reference the .fx effect as a .xnb file?

Edit:
After a struggle, I finally came to the point where I get another one:
MainEffect.xnb: Bad token CppNet.Token

1 Like

Greetings! Seeing how nobody replied after such a lengthy period of time is rather discouraging.

But perhaps you managed to solve the problem by yourself? I am also writing a custom model processor (doing normal mapping) and I am also getting this exact exception - must reference a .xnb file. My code is identical to yours.

I would greatly appreciate if you could please elucidate what is going on here. Cheers

Hi,

After a while I can say that there is almost no progress.
I think that I could solve this problem using new version of MonoGame. I’ll try again in next few days and tell you if it worked.
Regards :wink:

Thank you so much for your timely response :slight_smile:

Maybe this post can help you guys.

I think I’ll try soon my own custom processor, I’ll post anything I’ve got here.

It didn’t work in this case. The problem is not how to hook up your processor to the Pipeline, but it is during the processing of the model. MonoGame versions prior to 3.5 were clunky when using them with advanced custom shaders assigned to a model. Anyway, I think I know how to solve this issue and I’ll let you know if it works. :slightly_smiling:

1 Like

So regarding that “External reference” exception. After all I simply decided to to set the Effect field to null just like that:

customMaterial.Effect = null;

That actually let me build the model from the monogame content pipeline too, but now I get an exception when I try to load the model in the actual game:
Additional information: Could not find ContentTypeReader Type. Please ensure the name of the Assembly that contains the Type matches the assembly in the full type name

Obviously I miss something fundamental, but it’s hard to keep track of all this. I will keep exploring.

I have compared my processor between xna and monogame.
And the fact that the effect loaded in monogame with the externalreference is of compiledeffect type makes me think the effect should not be null before calling convert method.
It is the only difference i have found between the two processors that makes my content build or not.
So how to load an effect in a processor WITHOUT having to call a compiled one is the right question i think

Hey guys. I think we more or less managed to solve the problem: Mesh effect field

I suppose writing may be a bit hazy at the moment, but when we trim it up and make it look nice, I might as well write a proper guide to it.

I think I’m in the same boat. I can’t get SkinnedModel working at all, but I think it’s because I’m not implementing the processor properly.

When the game runs, this happens:

I’ve never imported a processor before, so it’s entirely possible I’m getting something very basic wrong. The model in this snapshot is just a simple Content.Load(“Models/mod_lizard_skinned”), so maybe I’m loading it in wrong?

These are the settings I’m using with the Content Pipeline:

All I want is to have armature animation in my 3D game : (

EDIT: I’ve also tried manually compiling with MGCB with no apparent errors:

But even then it still gives me the exact same error.

I also use the Tag property but i have to fill it manually. I don t think/know if the default modelprocessor will fill it for. Currently I would say no as you get this error.

Could you please explain how to fill it manually? I don’t know how to get from the model.Tag to a SkinningData object at all (I don’t really understand what model.Tag is for) :confused:

Model.Tag is of type object if i remember. So you can virtually save anything in this member. Usually it is an array, list or dictionary of values.
You set the desired values in the modelprocessor.
Then at loadtime in the app for ex, in the loadcontent method, after having loaded the model, you read the tag and cast it to the same type as the one in the processor. Then you can use the values in the tag and store it in you model’s class.
There are many examples on the old xna website if it still exists. A skinned example is also available.

1 Like

I think I understand… maybe? But isn’t that what I’m doing with MGCB? I tried using the SkinnedModelPipeline.dll processor which created the .xnb I’m trying to load, but it won’t cast.

Model model = Global.Content.Load<Model>("Models/mod_lizard_test"); // Global.Content is a reference to my content manager.
                                                                    // mod_lizard_test is the .xnb that the MGCB.exe created using SkinnedModelPipeline.dll.

SkinningData skinningData = model.Tag as SkinningData;

if (skinningData == null)
    throw new InvalidOperationException
        ("This model does not contain a SkinningData tag."); // Gets stuck here :( 

Are there more parameters I need to add to the MGCB when I run it?

Or is this what you meant for me to do? Because I CAN cast it as a SkinnedModelPipeline (I think. It won’t let me check if it’s null because “ModelProcessor is defined in an assembly that is not referenced”).

Model model = Global.Content.Load<Model>("Models/mod_lizard_test");
SkinnedModelPipeline.SkinnedModelProcessor test = model.Tag as SkinnedModelPipeline.SkinnedModelProcessor;
SkinningData skinningData = new SkinningData(/* I don't know how to get the required values from 'test' though... */);

SkinningData is ok if it is the same as in the processor. Maybe you need to explicitly cast it with
=(SkinningData) model. Tag

Try with a simple string to see if you can get it in the app. I dont remember if the tag property can contain only simple type values or custom classes too

Nope:

Try with a simple string to see if you can get it in the app. I dont
remember if the tag property can contain only simple type values or
custom classes too

Sorry, but I’m not sure what you mean by that. This is what the Model class (who’s Tag I am casting) looks like:

public sealed class Model

   public sealed class Model
   {
        public Model(GraphicsDevice graphicsDevice, List<ModelBone> bones, List<ModelMesh> meshes);

        public ModelBoneCollection Bones { get; }
        public ModelMeshCollection Meshes { get; }
        public ModelBone Root { get; set; }
        public object Tag { get; set; }

        public void CopyAbsoluteBoneTransformsTo(Matrix[] destinationBoneTransforms);
        public void CopyBoneTransformsFrom(Matrix[] sourceBoneTransforms);
        public void CopyBoneTransformsTo(Matrix[] destinationBoneTransforms);
        public void Draw(Matrix world, Matrix view, Matrix projection);
    }

And this is SkinnedData:

/// <summary>
    /// Combines all the data needed to render and animate a skinned object.
    /// This is typically stored in the Tag property of the Model being animated.
    /// </summary>
    public class SkinningData
    {
        /// <summary>
        /// Constructs a new skinning data object.
        /// </summary>
        public SkinningData(Dictionary<string, AnimationClip> animationClips,
                            List<Matrix> bindPose, List<Matrix> inverseBindPose,
                            List<int> skeletonHierarchy)
        {
            AnimationClips = animationClips;
            BindPose = bindPose;
            InverseBindPose = inverseBindPose;
            SkeletonHierarchy = skeletonHierarchy;
        }


        /// <summary>
        /// Private constructor for use by the XNB deserializer.
        /// </summary>
        private SkinningData()
        {
        }


        /// <summary>
        /// Gets a collection of animation clips. These are stored by name in a
        /// dictionary, so there could for instance be clips for "Walk", "Run",
        /// "JumpReallyHigh", etc.
        /// </summary>
        [ContentSerializer]
        public Dictionary<string, AnimationClip> AnimationClips { get; private set; }


        /// <summary>
        /// Bindpose matrices for each bone in the skeleton,
        /// relative to the parent bone.
        /// </summary>
        [ContentSerializer]
        public List<Matrix> BindPose { get; private set; }


        /// <summary>
        /// Vertex to bonespace transforms for each bone in the skeleton.
        /// </summary>
        [ContentSerializer]
        public List<Matrix> InverseBindPose { get; private set; }


        /// <summary>
        /// For each bone in the skeleton, stores the index of the parent bone.
        /// </summary>
        [ContentSerializer]
        public List<int> SkeletonHierarchy { get; private set; }
    }

Could you please post an example of getting a string? I’m not sure how to do that…

EDIT: According to Visual Studio, the model’s Tag is null?

The post here says to “put it through the SkinnedModelProcessor”, but I’ve done that. It also says to set the Content Processor in the solution explorer to SkinnedModelProcessor, but I can’t find that setting where it’s meant to be. I’ve also had no luck adding the processor to the Content Pipeline app (I added its reference, but it’s not showing up as an option when I select my models).

How do you set the SkinningData in your modelprocessor ?

In your custom processor which should look like this:

[ContentProcessor(DisplayName = “My Skinned Model Processor”)]
public class MySkinnedModelProcessor: ModelProcessor
{

somewhere in your method

public override ModelContent Process(NodeContent input, ContentProcessorContext context)
your should have
model.Tag = tagData;
For you, tagData should be myskinningdata (of type SkinningData). Maybe SkinningData should be in its own *.dll, referenced by both the custom content processor AND the app, sot it is exactly the same (i remember having some issues casting to a class of the same name, but as they were in different projects at the same time it failed)

It is there where you should be able to put “my test string” and then get this in the app.

EDIT2
If I change the parameters around, instead of always compiling, it seems to never compile no matter what:

If they’re reversed (build first, then processor) they always compile as seen below.

EDIT!
So, after doing the stuff I said below, I began to wonder if the MCGB was even acknowledging the parameters I passed it. So I tried the following with a .dll not intended for models:

And the model still worked inside of my game. I think the MCGB is just using the default .fbx importer no matter what I pass it.
EDIT!

Like this?

I re-compiled the project, got the .dll out of the bin/x86/Debug folder, and tried it with the MCGB again (I assumed that the exception being thrown at the very start would just stop the conversion completely). No results; same thing as before:

I tried adding a Console.WriteLine(“Test String.”), but that also gave no results. I also added the new .dll to the MonoGame Pipeline app and it’s not giving it as an option :frowning:

It may be also related to Mesh effect field and the problem of setting an effect at build time with parameters. I’m looking in the pipeline and trying to understand it, but it is a pain to debug

I just created two .dlls that can load and process a skinned model. If anyone is interested I can post them.