Difficultys FBX blender to monogame.

Im going to give this another shot later.

Does anyone know were i can find the better skinned example now or the skinned example i was pretty sure someone updated it for monogame but i can’t find it now.

Edit:

I managed to get rid of all the extra junk.

Bone: Cube
|_Mesh: Cube
|_MeshParts: 1
|_MeshPart[0]... primitive count:12
|_Effects: 1
 : Microsoft.Xna.Framework.Graphics.EffectParameter

So i just found out that if you use cycles rendering in blender textures won’t export.

Re ReEdit

Ok so i managed to get it to work (not even sure how) but monogame doesn’t read it anyways it errors. The object viewer in windows does though.

If you look very close at the error there is a mispelling of the extension .fbx it says fbm

dunno if this is a hard coded change and there was a typo made or what.

This is the first actual dedicated blender post ive found for converting to games https://www.gamefromscratch.com/post/2014/07/16/Baking-Blender-materials-to-texture-to-make-them-usable-in-a-game-engine.aspx
i got the other info off a forum site.

Here is the instructions do do it but it doesn’t actually embed the file in the fbx most of the time. I had to do something else to get it to work but im honestly not sure what i did.

If i don’t embed it there are other difficultys.
The problem here on the monogame side since you have to load it in yourself

  1. if you misplace the texture or mix them up very bad since you don’t know the name of the textures for the fbx.
    Worse what texture goes with what bone mesh or part when you load it in.
    As well there is no info i can get to pull out the name of the texture that a bone mesh or part wants ? As all the names in the mesh.effects have null for the texture name.
        /// Off the file > drop down menu There is a option... External files  automatically pack into blend.
        /// will this work for the fbx ?  No
        /// 
        /// When exporting to .fbx, you can't export materials, you'll have the surface color, 
        /// but not other settings such as glossiness and reflection
        /// 
        ///  For textures, yes you can, but you have to use blender internal (!!blender render ?)
        ///  (it does not work with cycles), 
        ///    make sure you don't use nodes, 
        ///    apply the textures, 
        ///  then, when in the fbx export settings, 
        ///    go to bottom, 
        ///    set path to copy, 
        ///    then check the box-like icon next to it, 
        ///  it should now embed the textures with the exported fbx file.
        ///  
        /// But it doesn't
        /// You have to do something else which i did by accident and im not even sure what it was.

I don’t have any helpful links, but I would definitely recommend the blender 2.8 beta. The UI is cleaner and the hotkeys are much more intuitive.

I might just do that as im a noob with it i might as well start on the latest version.

Well luckily digging thru the dust of my old files.
I still have a old Copy of the skinned example the skinned content extensions 4.0 example and the ‘real’ Better Skinned Example The one with the model of the girl that spins around but its not updated.

Just updated it from xna 4.0 well at least it builds dunno if it will really work.
Now i just need a updated model, If anyone has the dude one that is updated that would be great too.

Ok im starting to get really frustrated.
So nearly everything i have tried try doing to load a rigged textured fbx will NOT work.

To be honest i could care 2 bits about loading a 15 year old model dude.fbx but since this is the only tutorial i can find i have been trying.
The sad part is even visual studio can load the model in its viewer.

Loading new fbx models without embedded textures works but the resulting model data after being loaded in, doesn’t properly contain the texture information that relates to each bone

From initial tests the bone mesh vertice data was all on the root bone as well im not 100 on that. Either way i can only use one texture as no texture info is passed into the model.

__________________ output_________

Bone: RootNode
|_Meshes: None so no MeshParts either
|_Children(4)...
  Bone: Cube   Parent: RootNode
  |_Mesh: Cube
  |_MeshParts: 1
  |_MeshPart[0]... primitive count:960
  |_Effects: 1
   : Microsoft.Xna.Framework.Graphics.EffectParameter
  |_No Children
  Bone: Lamp   Parent: RootNode
  |_Meshes: None so no MeshParts either
  |_No Children
  Bone: Camera   Parent: RootNode
  |_Meshes: None so no MeshParts either
  |_No Children
  Bone: Armature   Parent: RootNode
  |_Meshes: None so no MeshParts either
  |_Children(1)...
    Bone: rootBone     Parent: Armature
    |_Meshes: None so no MeshParts either
    |_Children(1)...
      Bone: topBone       Parent: rootBone
      |_Meshes: None so no MeshParts either
      |_Children(1)...
        Bone: topBone_end         Parent: topBone
        |_Meshes: None so no MeshParts either
        |_No Children

In reality above the rootBone is the base for the topBone and both should have a separate lists of vertice meshparts as these are weighted and part of the mesh is shared between bones.
Though that might be a blender exporting issue.

So writing my own skin shader bone animator and manipulator here is a no go. The importer simply doesn’t put all the information into the model data properly at all, so the requisite information is missing.

Further there is no example fbx i know were to download that has been verified to actually work in monogame even for testing.

Loading embedded fbx model textures are a problem.
loading old fbx files that (don’t) have embedded textures are a problem.
With the fbx importer or the assimp importer.

Using the skinned effect Or not using it makes no difference…
If i cant load the model texture properly and get at the model bone mesh information.

Im not even sure why there is a skinned effect if we don’t have a skinned importer ?

When i load the dude regularly it looks for the textures in the project folder. Instead of the content folder.
If i put them in the project folder it fails to load them with a different error of course.

When i update dude.fbx using the autodesk 2013 tool either with or without embeding the pipline fails to even generate a xnb in one of two different ways.

The old dude.fbx or the converted version fails to even import in blender.
So i cant fix the old ones there.

I download and poped open nkasts version


It had 80+ errors just loading it up, needless to say at this point i just closed it.

.
.
So trying with a new blender made fbx.

When i tried updateing the skinned model processor or the better skinned to load in all the data properly so i could get at it. I cant get the processor to show up in the pipeline tool. …argg…

When i tried with the better skinned example same problem.

Since i don’t have xna installed (which i doubt i could even do it if i wanted to now which i absolutely don’t want to and shouldn’t have to, As vs2017 refuses to even try) i can’t see what the original project directory or reference structure looks like so it might or might not be something im doing on that side.

But neither example i have has a custom importer dunno if that matters.
As im not a content pipeline guru. Yet im not completely new if its this hard for me i can only imagine a new person trying to load his model.

It might be that im using the portable maybe not i dunno at this point because absolutely nothing seems to work in any sort of consistent manner. If i try to just plug into the regular content pipeline i have other errors. I tried reversing my class library dependancys all sorts of stuff.
Just nothing.

What i can do is load a regular blender fbx with a texture not embeded in it and get it up and running like that as the uv and vertice information on a single mesh with no bones seems ok that’s about it.

Since 99% of everything i find is outdated or just doesn’t work right.

I need some sort of starting point and google is no help here any help is appreciated.

Re:Edit:
Well after going at it all day and looking at source code a mile long that reference tons of other stuff that is out of date or wont work on its own or that wont compile. As well as due to the lack of input on this.

I think its easier to just stop wasting time and just write a parser im going to start with the ascii 6.1 fbx output of blender for now (which even that looks obtuse and overly complicated) until a simpler solution is presented that works.

Totally agree - it’s very hard to get it working - I remember having loads of problems with FBX. Eventually I had it working for basic skinned mesh example which I believe was using all the frames of the bones when ideally it should only be using specific keyframes and interpolating the bone motions - which I believe “BetterSkinned” does via Slerp & Lerp. I found this too (“BetterAnimation”) - maybe similar or better: https://github.com/patrykos91

In original skinned mesh, I had to use notepad++ to open the FBX ascii and remove the animation transforms from the model since it screws up the pipeline extension. I’d use ctrl-F and type in the model’s geometry-name and remove the animation curve stuff from it (Lcl Translation, Lcl Rotation, Lcl Scaling)… after deleting that from the file it worked. Never tried with sub-materials or multiple textures on the model though. I guess starting with a really simple model/animation and expanding from there is the way to go when improving on it. Never used embedded textures and carefully set up relative file paths that would work in original file (kind of a pain).

I like the idea of a new parser. I was going to try ascii fbx parser but it looked like a nightmare so I started on a different kind of simple ascii exporter/converter (using baked morph targets for keyframes of interest). I agree that we need some simple standard way for everyone to get 3D characters up and running in a fairly reliable sort of way.

It most certainly looks to me to be a nightmare so far.
Ill take any help i can get on it.

So far i have basically just figured out the node element data structure of the ascii and have it loading up the text into separated string parts. I have a general grasp on how to parse most of the data vertices indices uv ect… there are special rules for much of it that one would think should be simple but its been complicated.

Though not sure about how to parse the blendweights which is really important.

I haven’t yet figured what to do with most of it after that or out how to link that data or how to decode much of it at all like the transform properties.

I can’t see how the animation bones or bone transformations are intended to link together.

If you have had any success with decoding the keyframes in the ascii or any insights on how that data is structured i would be much appreciative for any information that might help.

Whatever work i do on this if it amounts to anything will be shared and probably at the least posted i just started on this a couple days ago though.

Here are my preliminary notes.

/// <summary>
/// Specification Details for loading.
/// 
///  We denote the data block identifiers as [markers] 
///  So as to avoid confusion between elements or nodes, which have not yet been determined till the text is processed.
///  
///  These so called markers as i have named them ... denote nodes or elements which can contain 
///  Other nodes or element data that is not in a child node but a child element of that node or at the node level.
/// 
/// FBXHeaderExtension:  {   // node  // this is the [markers name] (FBXHeaderExtension) but the [markers type] (node) must be determined when the text is processed.
///      FBXHeaderVersion: 1003  // element (the value here 1003 is the [markerData] or marker type data of the data type element named (FBXHeaderVersion:)
///      FBXVersion: 6100
/// 
/// My method indexing routines ... neseccary as this whole thing is composed in such a odd format ill keep things as defined as i can.
/// 
/// Most of my methods will be using positional looping instead of index and length.
/// Index parameter and variable prefixing will be denoted by  from and to ... from 0 to 10 denotes indexs 0 thru 10
/// start and end will be used to specify indexing before the end location. start 0 end 10 denotes indexs 0 thru 9
/// 
/// 
/// Identifying and catagorizing the text structure seen in the ascii fbx.
/// 
/// Important characters.
/// 
/// { } :  are extremely important for processing the ascii text data be aware :: double colons are used though in variables as part of litteral name identifiers.
/// ; are comment areas in the fbx and are ignored till the next line return which may also then start with a comment.
/// "" are used to identify a string Type variable a element might be empty in this case i believe it is in most cases a empty string type.
/// 
/// Organizational structure.
/// 
///       Properties  are a big part of the fbx data they appear to have a well defined structural order.
///       
///       The data here appears to define A "VariableName", "TheDataType", "a unknown type modifier" , the comma spaced [data] the Types actual data
///       below we can see a new Color(.8f,.8f,.8f, 1f);  though im unclear on the alpha value here.
/// 
///       NodeUnDefined Properties60:  Depth(3) parent Model: children[0] elements [71]  // this node defines the properties60 this appears to act to indicate a processing data handling routine.
///       Property: "Color", "ColorRGB", "",0.8,0.8,0.8
///           Property: "Color", "Color", "A",0.8,0.8,0.8
/// 
/// Here are some ascii text and comments denoting how to look at this structure when procesing it.
/// 
/// Definitions:  // marker name they type of the marker will need to be determined.
/// { // this bracket determines that the marker is a node type not a element thus Definitions: is a marker till it is determined to be a node or element.
///     Version: 100 // element
///     Count: 18  // finding another colon indicates that the last item is a element not a node
///     
///     ObjectType:  // marker name it will be a node.
///     "Model"  // marker this nodes specific identifying data or modifying data.
///     { // this acts as a processing terminator for a marker to be identifyed it determines that the marker is a node type.
///         
///        // This following is a marker element we know this because a ending bracket will be found immediately after.
///        // A colon after it would also indicate it was a element.
///        // note properties are basically a type of element.
///         Count: // the colon is a key char for processing;
///         12  // this is element data.
///         
///       // a ending bracket in this case means that the element and the node must end here.
///       // the bracket defines a previous element ends here, in this case as well it denotes the elements node must return also.
///     }
///     ObjectType: "Geometry"  // this node type specifys marker data i think this is for linking.
///     { // the bracket denotes the ObjectType is a node as well as the end of the marker data search
///        } // the end bracket denotes the return of the node if there had been a element it denotes the end of the element
/// }
/// 
///  Terminating markers of type element is determined by the method.  
/// 
///         Count: // the colon is a marker key char 
///         12  // this is element data.
///         // a ending bracket in this case means that the element and the node must end here.
///     } // the bracket defines a element end in this case as well as denotes the node return
/// 
///
///    Marker and marker name distinctions of importance to note.
///    
///    Elements are just a variable name: followed by data the type is determined unless we code instructions individually to handle them.
///    I will and have started to plan for that already but by default they must be handled in a generic manner.
///    
///    Quotations indicate a string in this case the string may act as a data type such as for culling on as a enum.
///    Or it appears they may be used to link node types together as name identifiers.
/// 
///    Culling: "CullingOff"
///        Version: 101
///        Name: "Model::Camera Switcher" /// (be aware handle :: seperately when processing text). this appears to act as a string identifier for linking not the double colons 
///    CameraIndexName:          // it is possible to have empty strings which a empty spot should indicate as a string i believe.
///                                        // if not this whole processing thing could get very specific vert fast.
///    Vertices: 0.000000,0.000000,-5.895833,0.350694,0.000000,-5.845238, ... ect...
///    // elements may also have a stream of data this is just made to happen with commas. this actually makes them a lot like properties but the data can be variably continuous.
///    // this is somewhat annoying as i would need a list to store this and probably need to handle each type name as if it were a datatype so individually.
///    // however in this case some of these elements will need very specific handling such as the model data.
///     
/// 
/// The most important node is surely the Objects node.
///
///It contains the vertices of our models, the indices, the normals, the UV coordinates, and the materials.It’s structured like this:
///1
///2
///3
///4
///5
///6
///7
///8
///9
///10
///11
///    
///Objects:  { //---- beginning of node Objects
///
///   Model: “model name”, “Mesh” //---- beginning of node of the model
///   { 
///
///       […]
///        Vertices: […]       //---- vertices
///       PolygonVertexIndex: […]  //---- indices
///       LayerElementNormal: { }  //---- node of the normals
///        LayerElementUV: { }  //---- node of the UV coords
///    } //---- end of node of the model
///    Material: “material name”, “” { } //---- node of the material
///    […]
///   } //---- end of node Objects
///
/// 
///   from here basically im looking to decode the data after parsing it to c# data types.
///   following the instructions here and on other sites ... 
///   https://banexdevblog.wordpress.com/2014/06/23/a-quick-tutorial-about-the-fbx-ascii-format/
///   Im still gathering information though. 
///   Much of the degree of success or failure will rely on having all the decoding and linking knowlege i can get on the fbx 6.1 ascii file.
/// 
/// </summary>

My actual project is more a bunch of messy comments then code when i can actually load in all the text variables to actual c# data types ill post it up.
If there is anyone who would like to help out with decoding the data.

Preliminary text to nodes loading attempt seems to work so far the data isnt parsed yet just separated into a tree and element structure. This is truncated…

Begin Output Display.




RootNode ThisModelFile:  Depth(0) parent ThisModelFile: children[7] elements [2]
ElementUnDefined CreationTime:  Depth(0) |"2019-02-16 07:43:02:000"|
ElementUnDefined Creator:  Depth(0) |"Blender version 2.79 (sub 0)"  D|

  Node FBXHeaderExtension:  Depth(1) parent ThisModelFile: children[2] elements [3]
  ElementUnDefined FBXHeaderVersion:  Depth(1)   |1003|
  Element FBXVersion:  Depth(1)   |6100|
  ElementUnDefined Creator:  Depth(1)   |"FBX SDK/FBX Plugins build 20070228"|

    NodeUnDefined CreationTimeStamp:  Depth(2) parent FBXHeaderExtension: children[0] elements [8]
    Element Version:  Depth(2)     |1000|
    ElementUnDefined Year:  Depth(2)     |2019|
    ElementUnDefined Month:  Depth(2)     |02|
    ElementUnDefined Day:  Depth(2)     |16|
    ElementUnDefined Hour:  Depth(2)     |07|
    ElementUnDefined Minute:  Depth(2)     |43|
    ElementUnDefined Second:  Depth(2)     |02|
    ElementUnDefined Millisecond:  Depth(2)     |0|

    Node OtherFlags:  Depth(2) parent FBXHeaderExtension: children[0] elements [1]
    ElementUnDefined FlagPLE:  Depth(2)     |0|

  Node Definitions:  Depth(1) parent ThisModelFile: children[8] elements [2]
  Element Version:  Depth(1)   |100|
  ElementUnDefined Count:  Depth(1)   |18|

    NodeUnDefined ObjectType:  Depth(2) parent Definitions: children[0] elements [1]
    |"Model"|
    ElementUnDefined Count:  Depth(2)     |12|

    NodeUnDefined ObjectType:  Depth(2) parent Definitions: children[0] elements [1]
    |"Geometry"|
    ElementUnDefined Count:  Depth(2)     |1|

    NodeUnDefined ObjectType:  Depth(2) parent Definitions: children[0] elements [1]
    |"Material"|
    ElementUnDefined Count:  Depth(2)     |1|

    NodeUnDefined ObjectType:  Depth(2) parent Definitions: children[0] elements [1]
    |"Texture"|
    ElementUnDefined Count:  Depth(2)     |1|

    NodeUnDefined ObjectType:  Depth(2) parent Definitions: children[0] elements [1]
    |"Video"|
    ElementUnDefined Count:  Depth(2)     |1|

    NodeUnDefined ObjectType:  Depth(2) parent Definitions: children[0] elements [1]
    |"Deformer"|
    ElementUnDefined Count:  Depth(2)     |3|

    NodeUnDefined ObjectType:  Depth(2) parent Definitions: children[0] elements [1]
    |"Pose"|
    ElementUnDefined Count:  Depth(2)     |1|

    NodeUnDefined ObjectType:  Depth(2) parent Definitions: children[0] elements [1]
    |"GlobalSettings"|
    ElementUnDefined Count:  Depth(2)     |1|

  Node Objects:  Depth(1) parent ThisModelFile: children[20] elements [0]

    Node Model:  Depth(2) parent Objects: children[1] elements [11]
    |"Model::Camera Switcher", "CameraSwitcher"|
    Element Version:  Depth(2)     |232|
    ElementUnDefined MultiLayer:  Depth(2)     |0|
    ElementUnDefined MultiTake:  Depth(2)     |1|
    ElementUnDefined Hidden:  Depth(2)     |"True"|
    ElementUnDefined Shading:  Depth(2)     |W|
    ElementUnDefined Culling:  Depth(2)     |"CullingOff"|
    Element Version:  Depth(2)     |101|
    Element Name:  Depth(2)     |"Model::Camera Switcher"|
    ElementUnDefined CameraId:  Depth(2)     |0|
    ElementUnDefined CameraName:  Depth(2)     |100|
    ElementUnDefined CameraIndexName:  Depth(2)

      NodeUnDefined Properties60:  Depth(3) parent Model: children[0] elements [70]
      ElementUnDefined Property:  Depth(3)       |"QuaternionInterpolate", "bool", "",0|
      ElementUnDefined Property:  Depth(3)       |"Visibility", "Visibility", "A+",1|
      ElementUnDefined Property:  Depth(3)       |"Lcl Translation", "Lcl Translation", "A+",0.000000000000000,0.000000000000000,0.000000000000000|
      ElementUnDefined Property:  Depth(3)       |"Lcl Rotation", "Lcl Rotation", "A+",0.000000000000000,0.000000000000000,0.000000000000000|
      ElementUnDefined Property:  Depth(3)       |"Lcl Scaling", "Lcl Scaling", "A+",1.000000000000000,1.000000000000000,1.000000000000000|
      ElementUnDefined Property:  Depth(3)       |"RotationOffset", "Vector3D", "",0,0,0|
      ElementUnDefined Property:  Depth(3)       |"RotationPivot", "Vector3D", "",0,0,0|
      ElementUnDefined Property:  Depth(3)       |"ScalingOffset", "Vector3D", "",0,0,0|
      ElementUnDefined Property:  Depth(3)       |"ScalingPivot", "Vector3D", "",0,0,0|
      ElementUnDefined Property:  Depth(3)       |"TranslationActive", "bool", "",0|
      ElementUnDefined Property:  Depth(3)       |"TranslationMin", "Vector3D", "",0,0,0|
      ElementUnDefined Property:  Depth(3)       |"TranslationMax", "Vector3D", "",0,0,0|
      ElementUnDefined Property:  Depth(3)       |"TranslationMinX", "bool", "",0|
      ElementUnDefined Property:  Depth(3)       |"TranslationMinY", "bool", "",0|
      ElementUnDefined Property:  Depth(3)       |"TranslationMinZ", "bool", "",0|
      ElementUnDefined Property:  Depth(3)       |"TranslationMaxX", "bool", "",0|
      ElementUnDefined Property:  Depth(3)       |"TranslationMaxY", "bool", "",0|
      ElementUnDefined Property:  Depth(3)       |"TranslationMaxZ", "bool", "",0|
      ElementUnDefined Property:  Depth(3)       |"RotationOrder", "enum", "",0|
      ElementUnDefined Property:  Depth(3)       |"RotationSpaceForLimitOnly", "bool", "",0|
      ElementUnDefined Property:  Depth(3)       |"AxisLen", "double", "",10|
      ElementUnDefined Property:  Depth(3)       |"PreRotation", "Vector3D", "",0,0,0|
      ElementUnDefined Property:  Depth(3)       |"PostRotation", "Vector3D", "",0,0,0|
      ElementUnDefined Property:  Depth(3)       |"RotationActive", "bool", "",0|
      ElementUnDefined Property:  Depth(3)       |"RotationMin", "Vector3D", "",0,0,0|
      ElementUnDefined Property:  Depth(3)       |"RotationMax", "Vector3D", "",0,0,0|
      ElementUnDefined Property:  Depth(3)       |"RotationMinX", "bool", "",0|
      ElementUnDefined Property:  Depth(3)       |"RotationMinY", "bool", "",0|
      ElementUnDefined Property:  Depth(3)       |"RotationMinZ", "bool", "",0|
      ElementUnDefined Property:  Depth(3)       |"RotationMaxX", "bool", "",0|

Depending on how successful i am if i can get enough data to load. Then from there i could just put it into a nice model structure and turn it into a xnb or directly spit it out as xml. I could also then take a shot at the binary.

If its just successful enough i could additionally make some modeling tools to go with it to manually rig whatever i cant read in right.

Im really doubtful ill be able to read in all the transforms right and maybe i wont need i to dunno yet. Id be happy if i can get the basics + the blend weights and bones loaded in well enough this model im using here has rigging but no actual animations to look at.

This is probably beyond me but im not stumped yet so.

I’d like to recommend you AssimpNet (actually it’s what mgcb uses). It really solves the problem of parsing and exposes all the FBX data you need to generate your models.

1 Like

Well it doesn’t work right in monogame.

You may have missed the entire first half of this post.

Unless you have a working example to prove it does or you have a link to a tutorial or instructions to make it work. That you have tested yourself. Then im dismissing that suggestion.

If all the guys doing monogame cant make it work for monogame when it was added years ago, why should i think i can get it to work. Most of them have far more knowledge resources and help then i do alone. I tried and tried to load a model with rigged info for days even the textures wouldn’t load right the mesh info isn’t even loading in correctly per bone there is no blend weights shown ect… Im guessing assimp works better for other formats then fbx but monogame only takes fbx even if you use the assimp content importer or ,x which is ancient.

So if i have to choose to waste time divining from the ether how to get someone elses broken code to work (in their 10,000 line 40 class file project) with dependencies that then don’t work as well.
Or
load a terribly formatted obtuse ascii file what is the difference?

Well in the later case if i succeed i don’t have to ask others for instructions on how to make their code work… when it wont and i have even less clue how long that would take or if it was even possible.

Unless you have first hand detailed knowledge of how to make it work that you are going to share?
I can’t see going right back to were i was just at when i had exhausted all the ideas i had.

1 Like

I support this great men. I also waste more and more time in order to find some manuals, tutorials and all is insuccessfully. Conclussion is that monogame does not applied to 3d games

I understand your frustration, a couple years back when i was actively working with 3d models and monogame, i’ve had similar problems.
Back than the solution involved switching to an older Blender version (v 2.68) because in v 2.69 they switched their FBX exporter and it wasn’t working with XNA/MonoGame.
I’ve also wrote a custom pipeline processor for the models, so i could load them directly into my model structure. But as i said its been ~2 years so I’ll need to verify the whole process (Blender (v2.8 hopefuly) -> fbx -> pipeline -> game) to see if it still works, than i can write a guide/tutorial to help you (if noone does it sooner). Just gimme a few days and i’ll get back to you.

Ditto on understanding your frustration. I just use a tweak of the Facebook FBX -> GLTF exporter and dump out a custom binary to read instead of even bothering with MG’s pipeline for models. XNA/MG’s mesh rendering is pretty much bogus anyways.

Blender still requires special hacks even to handle its’ poor FBX2013 support.

if (fromBlender) // sigh
{
    target.data[duplicate->second].Position = Vec3(-pos[0], pos[2], pos[1]);
    target.data[duplicate->second].Normal = Vec3(-nor[0], nor[2], nor[1]);
}
else
{
    target.data[duplicate->second].Position = Vec3(pos[0], pos[1], pos[2]);
    target.data[duplicate->second].Normal = Vec3(nor[0], nor[1], nor[2]);
}

In their infinite wisdom, the Blender team omit specifying the coordinate system in their FBX output and then don’t conform with the natural FBX coordinate-system.

I did put in effort into writing a proper FBXSDK based asset-pipeline tool, but everything you need to do such for the pipeline is pretty much private so I just said “screw this psychotic over-engineered nonsense”.

If only I can help with FBX animation, but I surrender using FBX for animated mesh file long time ago since XNA time. Just like what your planning to do, I wrote my loader/parser for mesh file type I supported.

Basically I’m just using Monogame as a renderer after porting my engine in XNA, I don’t use Model class and I don’t use content Pipeline to cook the Model mesh in short I have my own MeshContent and ModelEntity class so I’m not limited to MG Model.Draw() and what should be embedded ot not to model mesh.

But I think MG team and XNA team did a great job for creating Pipeline Tools, for other developers to extend it, we know that even in XNA we have to write our own processor/write for model animation.

Good luck dude ^_^y

I wrote a minimal AssimpNET loader and data printer for you:

the output is something like this (for dude.fbx)

https://pastebin.com/5YzRUKfn

Assimp is just a parser. It loads model files and gives you the data. What you do with the data is what makes the difference :slight_smile: It won’t help you deciding what to do with the data, but it will parse the FBX file in two lines and then you can spend your time doing what you want with that data.

I admit the documentation is scarce to non-existant, but being a wrapper, the documentation of Assimp will also work for AssimpNET. And to be honest, it’s quite intuitive. Once you have the scene loaded, browsing the object will bring you to most of the data you need.

As an extra bonus, it will load lots of kinds of 3d models, not only FBX. If I’m not wrong, version 4 loads glTF , which at least for me is giving me significatively less headaches than FBX, but as usual, YMMV

I started using it 5 years ago and never looked back.

2 Likes

@KaKCat I liked your initial post because that’s the way to do it IMHO, using assimpnet, your not only bound to FBX but on all mesh type it supported.

Additionally, here’s also my old code that I abandoned for finding the key frames when I’m playing with AssimpNet from this code you have Joints and the animation per key frames.

            #region ANIMATION
            //!!
            //!

            // We only support skeletal animation which store information on animation[0]
            if ( pAssimpModel.HasAnimations &&
                 pAssimpModel.Animations.Count > 0 &&
                 pAssimpModel.Animations[0].NodeAnimationChannelCount > 0)
            {
                
                
                var m_AnimInfo    = pAssimpModel.Animations[0];
                int m_JointsCount = m_AnimInfo.NodeAnimationChannelCount;
                int m_FrameCount  = (int)m_AnimInfo.DurationInTicks;
                
                Vector3 m_LastJointFramePos = Vector3.Zero;
                Vector3 m_LastJointFrameRot = Vector3.Zero;
                Vector3 m_LastJointFrameScl = Vector3.Zero;
                bool    m_FoundTime         = false;
                
                pMeshVerts.__MeshJoints = new MeshJoint[m_JointsCount];
                

                // JOINTS
                for (int j = 0; j < m_JointsCount; j++)
                {

                    var m_Joint = m_AnimInfo.NodeAnimationChannels[j];

                    // JOINT INFO
                    pMeshVerts.__MeshJoints[j] = new MeshJoint(m_FrameCount);                   
                    pMeshVerts.__MeshJoints[j].__Name = m_Joint.NodeName;
                    
                    // JOINT : FRAMES
                    for (int f = 0; f < m_FrameCount; f++)
                    {

                        pMeshVerts.__MeshJoints[j].__JointFrames[f] = new MeshJointFrame();

                        //--> JOINT FRAME POSITION
                        //
                        m_FoundTime = false;
                        //
                        if (m_Joint.HasPositionKeys)
                        {
                            for (int t = 0; t < m_Joint.PositionKeyCount; t++)
                            {
                                if (m_Joint.PositionKeys[t].Time == f)
                                {
                                    m_FoundTime = true;
                                    //
                                    pMeshVerts.__MeshJoints[j].__JointFrames[f].__JointPos =
                                    new Vector3(m_Joint.PositionKeys[t].Value.X,
                                                 m_Joint.PositionKeys[t].Value.Y,
                                                 m_Joint.PositionKeys[t].Value.Z);
                                    //
                                    m_LastJointFrameRot = pMeshVerts.__MeshJoints[j].__JointFrames[f].__JointPos;
                                    //
                                    break;
                                }
                            }
                        }
                        //
                        if (!m_FoundTime)
                        {
                            pMeshVerts.__MeshJoints[j].__JointFrames[f].__JointPos = m_LastJointFramePos;
                        }


                        //--> FRAME JOINT ROTATION
                        //
                        m_FoundTime = false;
                        //
                        if (m_Joint.HasRotationKeys)
                        {
                            for (int t = 0; t < m_Joint.RotationKeyCount; t++)
                            {
                                if (m_Joint.RotationKeys[t].Time == f)
                                {
                                    m_FoundTime = true;
                                    //
                                    pMeshVerts.__MeshJoints[j].__JointFrames[f].__JointRot =
                                    new Vector3(m_Joint.RotationKeys[t].Value.X,
                                                 m_Joint.RotationKeys[t].Value.Y,
                                                 m_Joint.RotationKeys[t].Value.Z);
                                    //                                        
                                    m_LastJointFrameRot = pMeshVerts.__MeshJoints[j].__JointFrames[f].__JointRot;
                                    //
                                    break;
                                }
                            }
                        }
                        //
                        if (!m_FoundTime)
                        {
                            pMeshVerts.__MeshJoints[j].__JointFrames[f].__JointRot = m_LastJointFrameRot;
                        }


                        //--> FRAME JOINT ROTATION
                        //
                        m_FoundTime = false;
                        //
                        if (m_Joint.HasScalingKeys)
                        {
                            for (int t = 0; t < m_Joint.ScalingKeyCount; t++)
                            {
                                if (m_Joint.ScalingKeys[t].Time == f)
                                {
                                    m_FoundTime = true;
                                    //
                                    pMeshVerts.__MeshJoints[j].__JointFrames[f].__JointScale =
                                    new Vector3(m_Joint.ScalingKeys[t].Value.X,
                                                 m_Joint.ScalingKeys[t].Value.Y,
                                                 m_Joint.ScalingKeys[t].Value.Z);
                                    //                                        
                                    m_LastJointFrameScl = pMeshVerts.__MeshJoints[j].__JointFrames[f].__JointScale;
                                    //
                                    break;
                                }
                            }
                        }
                        //
                        if (!m_FoundTime)
                        {
                            pMeshVerts.__MeshJoints[j].__JointFrames[f].__JointScale = m_LastJointFrameScl;
                        }


                    }//Frame

                }//Joint


            }

            //!
            //!!
            #endregion ANIMATION

If you go on using assimpNet. here a FULLY TESTED extraction of vertices, works on most mesh type ^_^Y tested and rendered correctly, at least on my engine : - D

           #region MESH
            //!!
            //!

            if (pAssimpModel.HasMeshes)
            {

                pMeshVerts.__VerticesPNT = new List<Z3DC.VertexPNT[]>();


                foreach (var m_Mesh in pAssimpModel.Meshes)
                {

                    var m_VerticesPNT = new Z3DC.VertexPNT[m_Mesh.Faces.Count * 3];
                    //
                    int v = 0;
                    int p = 0;

                    for (int f = 0; f < m_Mesh.Faces.Count; f++)
                    {
                        for (int i = 0; i < m_Mesh.Faces[f].Indices.Count; i++)
                        {
                            if (v >= m_VerticesPNT.Length) break;

                            p = m_Mesh.Faces[f].Indices[i];

                            //*>> Position
                            m_VerticesPNT[v].Position = new Vector3(m_Mesh.Vertices[p].X, m_Mesh.Vertices[p].Y, m_Mesh.Vertices[p].Z);

                            //*>> Normal
                            if (m_Mesh.HasNormals)
                                m_VerticesPNT[v].Normal = new Vector3(m_Mesh.Normals[p].X, m_Mesh.Normals[p].Y, m_Mesh.Normals[p].Z);
                            else
                                m_VerticesPNT[v].Normal = Vector3.Zero;

                            //*>> TexttureUV
                            if (m_Mesh.HasTextureCoords(0))
                            {
                                m_VerticesPNT[v].TextureUV.X = m_Mesh.TextureCoordinateChannels[0][p].X;
                                m_VerticesPNT[v].TextureUV.Y = -m_Mesh.TextureCoordinateChannels[0][p].Y;
                            }
                            else
                            {
                                m_VerticesPNT[v].TextureUV = Vector2.Zero;
                            }

                            v++;
                        }
                    }

                    pMeshVerts.__VerticesPNT.Add(m_VerticesPNT);
                }
            }

            //!
            //!!
            #endregion MESH

If you want the AssimpNet that can be build without irritation : -D I can post it here maybe, because I had the hard time building it finding the correct cecil dependencies. Hopefully you can create a new text mesh format from assimpScene that we can use and parse, Orge and Irrlich engine have created their own mesh file format ^_^y you can call it MGX file mesh format : - D

Good luck ^_^y

I’m also having difficulty with fbx files, but I have no idea of their origin, as I’m porting. KakCat’s data dumper has identified for me that one particular model has no meshes (whereas if I use the official ASSIMP Viewer it thinks it has two, and some other viewers see it as one).
It’s useful information, in that it gives me somewhere to look, but it shows that parsing with ASSIMP and AssimpNET isn’t yielding the same results. Worrisome.

This is the blender file im using atm with the fbx ascii in there as well which loads and displays animation in visual studio 2017. https://drive.google.com/open?id=1cmLQ9LODGSjSCXKCfY7gjC9OP_bpqn2k

Ah thanks kitcat i somehow missed that post, i tried that example got it up and running straight away it works great.

I wish i had seen that earlier.

I can see almost all the data so far.
Ill have to take a look at your keyframes example as im not sure how to put that into a model class.
Im also not sure were the blend weights are yet in the ascii file they are in the subdeformers,

Correct me if im wrong there isn’t anything that is like a vertex group that has bones and weight index lists for nodes. It would seem that would be the natural way to do it but i don’t see that in the scene data.
It just looks like the blend weights are attached to the bones and have the vertice indices listed.
So do you have to build the mesh parts on your own if you want to do the weights on the shader ?
I mean i can do it its not a big deal i just figured it would be in there somewere and i was missing it.

First of all, sorry if my answer is not accurate. The main work on my exporter was done 4 or 5 years ago, and my memory is not what it used to be. I’m too old for this fbx crap ^^

Yes, you’re right. The reason the weight info of bones are attached to meshes (instead of weight info attached to nodes) is that a bone can be tied to several meshes at the same time.

If you have only one mesh and several bones, the node-weight structure would be ok.

However if you have several meshes and several bones (i.e. dude.fbx) you probably couldn’t prewsent all the data without duplicating data (or would be harder to access to) For dude.fbx you’ll have to generate skinning data for several meshes (head, body, …) , you’ll probably see there the reason they chose this layout , specially if you optimize nodes (i.e. the foot nodes are not needed if you’re only rendering and animating the head)

As a side note, what I did with my exporter (in case it’s useful to you, maybe you’re already doing that) is creating my own data structures (that matches the ones needed in the game) fill them with assimp data, and then write them to disk.
At first I tried to directly dump the data as it came from assimp and it was too chaotic. Assimp needs some amount of preprocessing to get the data as you wish (specially with skinning and animation channels).