3d model viewer for Monogame [ver 0.6]

Download link:
Version 0.6



  • Import a variety of .obj and .fbx files. They must contain texture coordinates. A mesh cannot have different materials for submeshes (yet?).

  • Meshes without normals are supported. Meshes without texcoords/uv are supported, too, but they won’t display textures.

  • Skinned Meshes with up to 4 bones per vertex are supported

  • Animated Skinned Meshes

  • plausible environment lighting, with support for 4 texture inputs - albedo, normal, roughness, metallic (no alpha yet)

  • HBAO solution

  • Parallax Occlusion Mapping

  • Intuitive UI


  • LMB drag the object

  • RMB -rotate around the object

  • Shift+RMB - rotate the environment

  • MW - zoom in/out

  • space - center object

The files MGSkinnedAnimationPipeline.dll and MGSkinnedAnimationAux.dll inside the main directory are DLLs downloaded from the internet, therefore Windows might block them. You can unblock them with right click->properties->unblock. More info here:



Wooooooow… That fish looks beautiful! Really shows off MG’s potential.

By the way, as for the “random crashes”, I may be able to help you out with that (when I get home). I’ve made a list of things which cause the MonoGame to throw an error in regards to how an FBX model is made. It could provide some clues.

the crashes are my fault, these models used to run fine before i implemented the animation too.

The fish you can download here

Great stuff as usual mate!

Hum… As 95% of my models have textures links, I can help you with use cases :wink:

Good news everyone! I simply manipulated the content pipeline dll so it ignores textures! You can of course load the textures manually, so that shouldn’t be a problem at all.


In the gif above you can see the memory go crazy, but i fixed that now

So all *.fbx files should be compatible now, I haven’t encountered an incompatible one now. But if you do please leave feedback!

EDIT: some fix

and most .obj files i’ve tried, work too!


MGCB should properly support linked files. https://github.com/MonoGame/MonoGame/issues/5109
With that you could fix the texture issue here.

the issue lays therein that i can’t make mgcb output the files in the output directory if i use absolute paths, so i have to copy the files to the local directory to work with relative paths.

If the output directory is relative it will create some relative output folders which are somewhere between the viewer and the source file directory.

But I can’t know what references the file has beforehand.


I had the same issue. and solved it with a custom ContentManager.

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Microsoft.Xna.Framework.Content;

namespace TableEditor.Models.AetherProject
    class ProjectContentManager: ContentManager
        Dictionary<string, object> loadedAssets = new Dictionary<string, object>();
        List<IDisposable> disposableAssets = new List<IDisposable>();
        private string _projectLocation = "";

        public ProjectContentManager(IServiceProvider serviceProvider, string rootDirectory): base(serviceProvider, rootDirectory)
        public ProjectContentManager(IServiceProvider serviceProvider): base(serviceProvider)

        protected override Stream OpenStream(string assetName)
            Stream stream;
            if (Path.IsPathRooted(assetName))
                var assetPath = Path.Combine(RootDirectory, assetName);
                stream = File.OpenRead(assetPath + ".xnb");
                stream = base.OpenStream(assetName);
            return stream;
        public override T Load<T>(string assetName)
            if (loadedAssets.ContainsKey(assetName))
                return (T)loadedAssets[assetName];

            string callerLocation   = Path.GetDirectoryName(Assembly.GetCallingAssembly().Location) + "\\";
            //string hostLocation     = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\";
            string currentLocation  = Directory.GetCurrentDirectory();
            string projectLocation = _projectLocation;

            string assetPath = assetName; //default value
            if(File.Exists(Path.Combine(callerLocation, RootDirectory, assetName) + ".xnb"))
                assetPath = Path.Combine(callerLocation, RootDirectory, assetName);
            else if (File.Exists(Path.Combine(currentLocation, RootDirectory, assetName) + ".xnb"))
                assetPath = Path.Combine(currentLocation, RootDirectory, assetName);
            else if (File.Exists(Path.Combine(projectLocation, RootDirectory, assetName) + ".xnb"))
                assetPath = Path.Combine(projectLocation, RootDirectory, assetName);
            T asset = ReadAsset<T>(assetPath, RecordDisposableAsset);
            loadedAssets.Add(assetName, asset);

            return asset;
        public override void Unload()
            foreach (IDisposable disposable in disposableAssets)

        void RecordDisposableAsset(IDisposable disposable)

        public IDisposable UnloadAsset(string assetName)
                return null;
            object asset = loadedAssets[assetName];            

            if (asset is IDisposable)
                if (disposableAssets.Contains((IDisposable)asset))

            return asset as IDisposable;

@nkast the content loader is fine, I think, it’s mgcb.exe that’s the problem

And i can’t just stream in the files since I need to generate tangent frames for the models, so I need mgcb

Can you give an example?
mgcb output the texture.xnb on a different directory than model.xnb?

Or is it just that the xnb is generated on a different directory than the running app?

I’ve written about it here typical/working command line inputs for MGCB?

What i want:

read a file from an absolute path and output the processed files in a output folder.

What happens:

If the absolute path of the source is on a different partition the generated .xnb file will be in the source folder
If the absolute path of the source is on the same partition the generated .xnb file will be in some new folder which, and now comes the great part, will generate a new folder somewhere above the working directory with a relative path from the last path that working and source directory share. I thought i was going crazy.

My program executes from here:
C:\Users\Me\Documents\Visual Studio 2015\Projects\HelperSuite\HelperSuite\bin\Windows\x86\Debug

if I process the file
C:\Users\Me\Documents\Visual Studio 2015\Projects\DeferredCascadedShadowMaps\GameThumbnail.png

The resulting processed file will be created here
C:\Users\Me\Documents\Visual Studio 2015\Projects\HelperSuite\HelperSuite\bin\DeferredCascadedShadowMaps\GameThumbnail.xnb

if it’s any help, for mgcb I set the Process.StartInfo.WorkingDirectory = SourcePath.

From there, I always have the output rooted to the source ( OutputDir = SourcePath\Content\ )
The ProjectContentManager will load absolute paths or search for the .xnb in the CurrentDirectory and _projectLocation (=SourcePath?).

The callerLocation is for Modules that load their own content.

I always have the .mgcb file in the SourcePath, so maybe StartInfo.WorkingDirectory has to point to the .mgcb.

Actually, my OutputDir is @SourcePath\…\Content\

1 Like

Whoaaaaaaaaaaaa! That’s one good lookin’ gif!

Buuut… there’s something I should mention. That animation I made, I deliberately made the head bone scale up and down quite extremely to test scaling. Would your program support that?

well you seem to be right then, the animation pipeline does not support scaling correctly.

That said it is possible that something went wrong during the export, but I would think the problem lays with the better skinned sample

Yeah, I’ve looked into it and I think the latter is the case. I have modified my copy of BetterSkinned to at least process scaling in the pipeline correctly, but either I did something wrong at that step, or at the actual rendering step, because, while the scaling does show up, it’s heavily distorted.

If you’re planning on modifying your BetterSkinned, be sure to watch out for any instance where it sets scaling to a default of 1. There are many instances where that 1 multiplier is hard coded in. Come to think of it, it’s entirely possible I just missed one of those instances on my end when I modified it.

interestingly I found that some animations look completely distorted when I import them into blender and export them right away, without changing anything. Strange

Even worse: I exported an animated mesh from blender and imported it again and suddenly it had more bones and the animation wasn’t the same? Wth

This process can be repeated infinitely. Not good.

Is ur IBL cubemap prebaked?

When I load a model, it says it has been converted, but nothing happens.
So I tried with the fish your pointed upper, and same thing, mgcb does its job, but the sphere is still there. Have I missed something ?
Whenever I close the app, windows says it has ceased functioning.
If I launch visual as the debugger, it says:

An unhandled exception of type ‘System.TypeInitializationException’ occurred in MonoGame.Framework.dll

Additional information: an exception has been thrown by the type initiator of ‘Microsoft.Xna.Framework.Audio.SoundEffect’.


Oh, that sucks. I uploaded a new version which produces a log file inside the .exe folder (log.txt). Maybe that can help.

(version 0.4)

I’ve also added support for objects that were previously not handled well.
This one didn’t work before and it had distorted materials when it finally did. Now it’s correct.

and this model didn’t work either, because it has no normals. So I generate the normals inside the pixel shader for that case (not the same uboat as the one mentioned earlier)

Here is a bonus with the stormtrooper with metal armor (metallic set to 1, no metallic texture) just because i think it looks great