Monogame and Blender (Part 2)

In my last post in this series (click here for part 1), I described all the details necessary to get a C# Monogame up and running using objects exported from Blender.  In this post, I’m going to go more in depth and describe how to animate an object and how to handle multiple objects in a scene.

Animation

Let’s make our cube rotate around the X coordinate.  We want it to smoothly rotate like it’s a box on a spit.  First, we’ll fix the Blender coordinate rotation code by stuffing it in a getter at the top of your Game1 class:

private Matrix BlenderRotateFix =< Matrix.CreateRotationX(MathHelper.ToRadians(-90)) * Matrix.CreateRotationY(MathHelper.ToRadians(-90));

Now we can alter the effect.World property in the Draw method to look like this:

effect.World = BlenderRotateFix;

Now let’s setup a 3D vector that will contain the current rotation point used by the animation.  Add this declaration at the top of your Game1 class:

private Vector3 CubeRotation;

Then add this code to your LoadContent method to initialize the vector:

CubeRotation = new Vector3(0, 0, 0);

To increment the X rotation every frame of your animation, you’ll add this code to the Update method:

CubeRotation.X += MathHelper.ToRadians(1);

This code will cause the X rotation to increment by 1 degree every frame of your game.  If you’re paying attention, you might notice that I’m just adding 1 degree for each call to update.  Eventually that number will overflow.  So let’s fix that right now by adding a modulo clamp to the number and prevent it from exceeding 360 degrees (or 2 Pi):

CubeRotation.X += MathHelper.ToRadians(1);
CubeRotation.X = CubeRotation.X % MathHelper.ToRadians(360);

Now we need to actually apply the rotation to the view.  Inside your Draw method, you’ll need to add a few lines of code after the BlenderRotationFix line:

effect.World = BlenderRotateFix;
effect.World *= Matrix.CreateRotationX(CubeRotation.X);
effect.World *= Matrix.CreateRotationY(CubeRotation.Y);
effect.World *= Matrix.CreateRotationZ(CubeRotation.Z);

Now run your code and watch the animation:

Spend some time with the code to learn it.  Make the cube rotate around the Y and Z axis.  Make it rotate faster or slower.  You can use decimal numbers as degrees (like 0.1).  Coordinates are represented in float, so you’ll need to keep in mind that any decimal numbers will need to be specified in float like 0.1f.  Otherwise, C# uses double precision as a default and you’ll get an error.

The code for this demo is located at my GitHub account and you can download it by clicking here.

Lighting

Lighting is a complex subject on it’s own and lighting techniques are intimately tied to the shader.  So I’m just going to show you a couple of simple things you can adjust on the light sources.  So let’s perform an experiment on the light used in our demo. 

First, we can move the light to a different location.  Remove these two lines of code in your Draw method:

effect.EnableDefaultLighting();
effect.PreferPerPixelLighting = true;

Next, add these three lines of code:

effect.LightingEnabled = true; // Turn on the lighting subsystem.

effect.DirectionalLight0.DiffuseColor = new Vector3(0.2f, 0.2f, 0.2f);
effect.DirectionalLight0.Direction = new Vector3(0, -100, -100);

You’ll see an ugly scene consisting of a cube with a very bright light that is up and to the left of the cube:

Let’s change the color of the cube in Blender.  Go back to blender and open your cube (or create a new one).  Then select the material tool:

Cube Material in Blender

Now click on that box below “Diffuse” and you can select any color you would like.  I adjusted my color to be a solid blue color.  Then export to an FBX file and put it into your content pipeline.  Change the name of your object to match the FBX filename and run your program:

Blue Cube

You can use emissive lighting to lighten up the shape.  This technique acts like light is being transmitted from the object, except it doesn’t affect any of the objects around it like a light source.  Add the following light effect to your draw method, right after the previous lines of code that were added earlier:

effect.EmissiveColor = new Vector3(0.4f, 0.4f, 0.4f);

You should see something like this:

Emissive Light Source

The FBX File

I’m going to switch subjects a bit and cover the FBX file in more depth.  First, let’s look at the scene in Blender:

Default Blender Startup Screen

There are three objects on this screen: The cube, a camera and a light source (that little round thing in the upper right).  I have circled each object so you can see what I’m talking about.  When you render your mesh in Monogame, the camera and light sources are never used.  So you can delete those and reduce your file size.

To delete the camera go to the scene properties and click on the camera object:

Scene Properties

You should see the camera turn orange indicating that it has been selected:

Camera, Selected

Now click in the main view area and hit the delete button on your keyboard.  You’ll see a menu pop up and ask if you want to delete the camera.  Click “Delete” and the camera will disappear.

Next, go back to the scene properties and click on the Lamp.   The lamp will become highlighted.  Click in the main view area and press the delete key.  Then click on “Delete” and the lamp will disappear.

Now export your cube and include it in your pipeline.  Now, if you run your program, you can see that nothing changed.

What is in the FBX file?

Export your cube again.  Before you click on the final export button, notice that there is a properties window that allows you to set FBX file parameters:

FBX File Save Properties

Change the Version to FBX 6.1 ASCII and then export your file again.  You cannot use the ascii version of this file in your pipeline, but it can be handy for troubleshooting purposes.

Once you have created the file, open it in a text editor like TextPad++ or Sublime.  There is a lot of sections in this file.  Most of the sections are not used by Monogame.  Search for “cube” and you’ll see a large chunk of text describing your cube.  First, you’ll see that it is a “Mesh” and that is followed by a long list of properties, all set to defaults.  Next is the list of vertices.  This is convenient for troubleshooting translation problem that you might have when you can’t get a shape to position exactly where you want it to.  For the default cube, the vertices line up on a 1×1 cubic grid:

Vertices: 1.000000,1.000000,-1.000000,1.000000,-1.000000,
-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,1.000000,
-1.000000,1.000000,0.999999,1.000000,0.999999,-1.000001,1.000000,
-1.000000,-1.000000,1.000000,-1.000000,1.000000,1.000000

A cube has 8 vertices and there are 3 coordinates for each (X,Y,Z), which means that there are 24 numbers in that list above.  Everything checks out.

Where did the color of the cube get stored?

If you search for “Material”, you’ll come across a section where a material was added to the file:

Material: "Material::Material", "" {
    Version: 102
    ShadingModel: "lambert"
    MultiLayer: 0
    Properties60:  {
        Property: "ShadingModel", "KString", "", "Lambert"
        Property: "MultiLayer", "bool", "",0
        Property: "EmissiveColor", "ColorRGB", "",0.0073,0.0037,0.8000
        Property: "EmissiveFactor", "double", "",0.0000
        Property: "AmbientColor", "ColorRGB", "",1.0000,1.0000,1.0000
        Property: "AmbientFactor", "double", "",1.0000
        Property: "DiffuseColor", "ColorRGB", "",0.0073,0.0037,0.8000
        Property: "DiffuseFactor", "double", "",0.8000
        Property: "Bump", "Vector3D", "",0,0,0
        Property: "TransparentColor", "ColorRGB", "",1,1,1
        Property: "TransparencyFactor", "double", "",0.0000
        Property: "SpecularColor", "ColorRGB", "",1.0000,1.0000,1.0000
        Property: "SpecularFactor", "double", "",0.5000
        Property: "ShininessExponent", "double", "",12.3
        Property: "ReflectionColor", "ColorRGB", "",0,0,0
        Property: "ReflectionFactor", "double", "",1
        Property: "Emissive", "ColorRGB", "",0,0,0
        Property: "Ambient", "ColorRGB", "",1.0,1.0,1.0
        Property: "Diffuse", "ColorRGB", "",0.0,0.0,0.8
        Property: "Specular", "ColorRGB", "",1.0,1.0,1.0
        Property: "Shininess", "double", "",12.3
        Property: "Opacity", "double", "",1.0
        Property: "Reflectivity", "double", "",0
    }
}

You can see that the diffuse color was set to 0.0073,0.0037,0.8000.  That was my blue color.  That was the property that I had set in Blender.  The name of the material is called “Material” which is a bit confusing.  You can name your materials in Blender.  If you go back to the material property screen and click the tiny plus button next to the Material pick list, you’ll see another material appear, named “Material.001”:

Add a Material

If I make that material pure red and then export my cube again, the ascii file will contain another material like this:

Material: "Material::Material", "" {
    Version: 102
    ShadingModel: "lambert"
    MultiLayer: 0
    Properties60:  {
        Property: "ShadingModel", "KString", "", "Lambert"
        Property: "MultiLayer", "bool", "",0
        Property: "EmissiveColor", "ColorRGB", "",0.8000,0.8000,0.8000
        Property: "EmissiveFactor", "double", "",0.0000
        Property: "AmbientColor", "ColorRGB", "",1.0000,1.0000,1.0000
        Property: "AmbientFactor", "double", "",1.0000
        Property: "DiffuseColor", "ColorRGB", "",0.8000,0.8000,0.8000
        Property: "DiffuseFactor", "double", "",0.8000
        Property: "Bump", "Vector3D", "",0,0,0
        Property: "TransparentColor", "ColorRGB", "",1,1,1
        Property: "TransparencyFactor", "double", "",0.0000
        Property: "SpecularColor", "ColorRGB", "",1.0000,1.0000,1.0000
        Property: "SpecularFactor", "double", "",0.5000
        Property: "ShininessExponent", "double", "",12.3
        Property: "ReflectionColor", "ColorRGB", "",0,0,0
        Property: "ReflectionFactor", "double", "",1
        Property: "Emissive", "ColorRGB", "",0,0,0
        Property: "Ambient", "ColorRGB", "",1.0,1.0,1.0
        Property: "Diffuse", "ColorRGB", "",0.8,0.8,0.8
        Property: "Specular", "ColorRGB", "",1.0,1.0,1.0
        Property: "Shininess", "double", "",12.3
        Property: "Opacity", "double", "",1.0
        Property: "Reflectivity", "double", "",0
    }
}
Material: "Material::Material_001", "" {
    Version: 102
    ShadingModel: "lambert"
    MultiLayer: 0
    Properties60:  {
        Property: "ShadingModel", "KString", "", "Lambert"
        Property: "MultiLayer", "bool", "",0
        Property: "EmissiveColor", "ColorRGB", "",0.8000,0.0000,0.0000
        Property: "EmissiveFactor", "double", "",0.0000
        Property: "AmbientColor", "ColorRGB", "",1.0000,1.0000,1.0000
        Property: "AmbientFactor", "double", "",1.0000
        Property: "DiffuseColor", "ColorRGB", "",0.8000,0.0000,0.0000
        Property: "DiffuseFactor", "double", "",0.8000
        Property: "Bump", "Vector3D", "",0,0,0
        Property: "TransparentColor", "ColorRGB", "",1,1,1
        Property: "TransparencyFactor", "double", "",0.0000
        Property: "SpecularColor", "ColorRGB", "",1.0000,1.0000,1.0000
        Property: "SpecularFactor", "double", "",0.5000
        Property: "ShininessExponent", "double", "",12.3
        Property: "ReflectionColor", "ColorRGB", "",0,0,0
        Property: "ReflectionFactor", "double", "",1
        Property: "Emissive", "ColorRGB", "",0,0,0
        Property: "Ambient", "ColorRGB", "",1.0,1.0,1.0
        Property: "Diffuse", "ColorRGB", "",0.8,0.0,0.0
        Property: "Specular", "ColorRGB", "",1.0,1.0,1.0
        Property: "Shininess", "double", "",12.3
        Property: "Opacity", "double", "",1.0
        Property: "Reflectivity", "double", "",0
    }
}

The name has been converted from Material.001 to Material_001 and you can see the diffuse color stored is 0.8000,0.0000,0.0000.  That is red.

The FBX file uses the Connections section to determine which objects were assigned this material.  In this case two materials are assigned to the cube:

Connections: {
Connect: "OO", "Model::Cube", "Model::Scene"
Connect: "OO", "Material::Material", "Model::Cube"
Connect: "OO", "Material::Material_001", "Model::Cube"
}

Summary

This blog post covered basic information about animation, light sources and data stored in the FBX file.  I have a lot more information to cover so stay tuned for future posts.

You can download the last sample (DemoMonogame3) from my GitHub account by clicking here.

Leave a Reply