Monogame and Blender (Part 1)

One of my favorite gaming graphic library used to be XNA.  Microsoft discontinued the library but was ported to Monogame which is open-source and cross-platform.  If you’re interested in writing 3D games and you don’t know where to start, I’m going to show you how. 

As a hobby, I’ve written 3D games in the past using technologies as old as VESA graphics using fixed-point arithmetic, memory buffers and Borland C in DOS.  I’ve written graphics code using Direct-X (several different versions over the years).  Now I’m using Monogame and Blender.

The first issue with creating 3D games is generating the assets that will be used in your game.  In the past, everything was custom and the developer was responsible for designing and building an editor or using tools such as a text editor to create arrays of points, polygons and solids for consumption of the game.  GIF and JPGs were used for texture mapping.  When I used XNA, I shelled-out $600 and purchased LightWave 3D (version 9.5).  I poured many hours practicing with the interface to build 3D objects for my game.  Fortunately, I had AutoDesk experience from my workplace, so the learning curve didn’t scare me away.  Today, I’m using Blender.  It’s free, it has a lot of power and it generates the files that are compatible with the Monogame content input scheme.

Blender

Let’s start with blender.  First, you’ll need to download and install it.  The site is here.  Installation is straightforward and easy.  Once you’ve installed the program I would recommend you just spend some time playing with the controls.  It will take some time to learn where everything is and how to manipulate shapes, polygons and points to create your game assets.  Your initial screen will look like this:

Blender Startup

The scene at startup consists of one cube, a camera and a light source.  You can “grab” any of the three colored arrows on the cube to push it around the screen and translate the location to any coordinate you desire.  If you look to the right, you’ll see a control panel for selecting the different properties of the cube:

Click on the cube shape to change the translate, scale and rotation of the cube using exact coordinates.  You can see the coordinates here (titled Transform):

Object Transform Properties

As you can see, my cube is centered at the scene origin (0,0,0) with no rotation and a scale factor of 1 (in all three axis).  If you export this cube as an FBX file,  you can use it in Monogame directly.  First, save as a blender file, so you can always go back and edit the original.  The under the File menu, select export and select FBX.  You’ll need to navigate to a directory and click on the “Export FBX” button in the upper right corner of the screen.  I named mine cube.fbx for the sample code.

Monogame

Let’s move over to Visual Studio and start a new game in Monogame.  First, you’ll need to install Monogame, which you can download from here.  Installation is pretty straight-forward.  When you start Visual Studio and select create new project, you’ll see some new project templates to choose from:

Monogame Project Templates

Start with the Monogame Windows Project.  When the solution is created, you’ll see a Game1.cs source file.  Open this file in VS and take a quick look at the methods already created.  Your game logic will follow through these methods like this:

Basic Game Loop

The initialize will create the global game objects.  LoadContent is used for loading all the assets from the content pipeline.  This is where you pre-load all the 3D graphical objects that your game will need.  The update and draw methods form a loop where your game object positions can be updated and then rendered.  You can create more than one draw method to handle different object drawing requirements, but in this sample, there is just one.  Inside your update method, you’ll check the user input.  If the user presses a key or clicks on a button or menu to exit the game, you can exit the update loop here (by calling the Exit method).   Once the game is told to exit, it will call the UnloadContent method to allow you to dispose of any game objects you have declared.

Let’s add our FBX file to the content pipeline.  If you look at the files and virtual folders in the project explorer, you’ll see a folder called “Content”.  This is where all of your graphics assets will be stored.  Copy your FBX file into this folder first and add to your project.  You can right-click on the Content folder in VS and add existing file to add the file.  You’ll have to change the browse window to look for *.* (all files).   You should see the file in the project:

One more step.  You’ll need to double-click on that Content.mgcb file in the Content folder to start up the pipeline tool.  This will open a window that looks like this:

Content Pipeline Tool

Right-click on the Content tree root element and select Add Existing Item.  Then double-click on the cube.fbx file.  Your content pipeline window should look like this:

cube.fbx in the pipeline

Now click on the save button and your content is ready for use.  If you want to test your content, you can click the build menu and build your content pipeline.  This will compile the data in your fbx file and verify that it is not corrupt.  When you’re done close the content pipeline tool window.

Go back to the Game1.cs code and declare a model at the top of your Game1 Class:

public class Game1 : Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    private Model model;

Next, you’ll need to load yoru model in the LoadContent method:

    protected override void LoadContent()
    {
        // Create a new SpriteBatch, which can be used to ...
        spriteBatch = new SpriteBatch(GraphicsDevice);

        model = Content.Load<Model>("cube");
    }

Now we need to draw the cube.  Add this code to your Draw method to perform a simple mesh draw:

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        foreach (var mesh in model.Meshes)
        {
            foreach (var effect1 in mesh.Effects)
            {
                var effect = (BasicEffect) effect1;
                effect.EnableDefaultLighting();
                effect.PreferPerPixelLighting = true;
                var cameraPosition = new Vector3(0, 0, 20);
                var cameraLookAtVector = Vector3.Zero;
                var cameraUpVector = Vector3.UnitY;
                effect.View = Matrix.CreateLookAt(cameraPosition, cameraLookAtVector, cameraUpVector);
                float aspectRatio = graphics.PreferredBackBufferWidth / (float)graphics.PreferredBackBufferHeight;
                float fieldOfView = MathHelper.PiOver4;
                float nearClipPlane = 1;
                float farClipPlane = 200;
                effect.Projection = Matrix.CreatePerspectiveFieldOfView(fieldOfView, aspectRatio, nearClipPlane, farClipPlane);
            }

            mesh.Draw();
        }

        base.Draw(gameTime);
    }

If you run the program now, you’ll see something like this:

Cube, Viewed Edge On

I know, it’s not very exciting.  There’s more work ahead.  First, let’s fix the mouse hide problem.  If you move your mouse over the content region of that window (the blue area), you’ll notice that the mouse disappears.  It’s a bit distracting when working on a project, so we’ll make the mouse visible for this demo by setting the IsMouseVisible flag to true inside the Initialize method:

    protected override void Initialize()
    {
        this.IsMouseVisible = true;
        base.Initialize();
    }

Next, let’s rotate the cube to the left and down a bit, just so it looks like a cube.  To do that, I’m just going to modify the effect.World property like this:

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        foreach (var mesh in model.Meshes)
        {
            foreach (var effect1 in mesh.Effects)
            {
                var effect = (BasicEffect) effect1;
                effect.EnableDefaultLighting();
                effect.PreferPerPixelLighting = true;

                effect.World = Matrix.CreateRotationX(MathHelper.ToRadians(45)) * Matrix.CreateRotationY(MathHelper.ToRadians(45));

                var cameraPosition = new Vector3(0, 0, 20);
                var cameraLookAtVector = Vector3.Zero;
                var cameraUpVector = Vector3.UnitY;
                effect.View = Matrix.CreateLookAt(cameraPosition, cameraLookAtVector, cameraUpVector);
                float aspectRatio = graphics.PreferredBackBufferWidth / (float)graphics.PreferredBackBufferHeight;
                float fieldOfView = MathHelper.PiOver4;
                float nearClipPlane = 1;
                float farClipPlane = 200;
                effect.Projection = Matrix.CreatePerspectiveFieldOfView(fieldOfView, aspectRatio, nearClipPlane, farClipPlane);
            }

            mesh.Draw();
        }

        base.Draw(gameTime);
    }

If you examine the code above very carefully, you’ll notice that the camera is setup such that the camreaUpVector is equal to the Y vector.  So now the X axis is from left to right and the Y axis is from top to bottom.  The camera is also positioned to be at a location that is straight back on the Z coordinate by 20 (see cameraPosition property).  The cube is sitting on coordinate (0,0,0) and the camera is pointing at (0,0,0).  One of the most frustrating aspects of getting started in 3D graphics is getting the camera to point at your scene.  I would recommend getting familiar with these camera settings to make it easier to figure out what is going on when your objects don’t show up in the window.  The object could be behind your camera, to the left, top, right or bottom of your camera veiw.  It could also be off in the distance and too small to be seen.  Now that you have rotated the cube by the X and Y axis, you should see something like this:

Rotated Cube

Adjust your camera position to zoom in a bit.  Set the Z coordinate to 10 instead of 20.  Here’s what You’ll see:

Cube, Zoomed in

Let’s do a translate.  If you want to keep your rotation, then you’ll need to add your translation to the previous world rotations.  You can add this line of code right after the effect.World code in the previous sample code:

effect.World += Matrix.CreateTranslation(10, 0, 0);

This will keep your rotation and move the cube to the right by 20:

Translated Cube

Let’s do a little scaling.  Set your translate back to (0,0,0).  Now add a scaling factor of 2x to your cube width (or X coordinate).  Add this line after your translation from above:

effect.World += Matrix.CreateScale(2, 0, 0);

Your cube should now look like this:

Cube 2x Width

Some Blender Details

Go back to Blender and start a new file.  Delete the default cube and create a cone object (you’ll have to select the “Create” tab on the left menu pane).  Click on the Object edit on the right pane and set the X,Y and Z coordinates to (0,0,0):

Set X,Y,Z to (0,0,0)

Notice how the cone points up.  If you look at the lower left corner of the edit screen you’ll see an origin marker.  The marker shows that the up coordinate is the Z coordinate:

Object Origin

The blue arrow is the Z coordinate, the green arrow is the Y coordinate and the red arrow is the X coordinate.

Now follow the steps earlier to export an FBX file and include it into your Monogame pipeline.   Change your demo program to use cone instead of cube.  Finally, change your translate, rotate and scale for the world property to all zeros.  Now you’ll see this:

Cone, From the Top

What happened?  The problem is that our Monogame sample code has a camera that is pointing at the Z axis.  To fix this, we can change the camera position to match the object origin of Blender, or we can rotate the object in Monogame and work with our X,Y,Z coordinates.  Let’s rotate the object to match the way it looks in Blender and pretend like Blender is setup the same as our Monogame.  When you create a game, you’ll have to remember to rotate all of your objects by -90 degrees to start with.  We’ll need to rotate the object by 90 degrees along the X coordinate (remove your scaling and translating code for now).   You should see this:

Cone, Rotated -90 Degrees

There’s still the question of which way the X and Y coordinates are pointing.  A cone doesn’t tell us that.  So we’re going to have to get creative.

Go back to Blender and delete the cone (or create a new file and delete the default cube).  Then add a monkey (yes, there is a shape called monkey and it’s a monkey’s head).  Then export this as an FBX file and import into the content pipeline and change your C# code to use monkey.  Now leave your -90 rotation and see what is displayed:

Monkey Object

In Blender the object looks like this:

Monkey in Blender

Which is rotated 90 degrees to the left (or -90 degrees on the Y axis).  We want the red arrow to represent our Z axis in Monogame.  So let’s rotate the head to the left to match Blender.

Monkey in Monogame Rotated to Match Blender

That should do it.  You’re probably wondering why I’m being so precise about this.  I’m pointing this out so that you’ll know to account for this dependency when you’re designing your scene.  I spent some serious time trying to figure out why my scenes did not look correct after creating something more complex in Blender.  If you know about this issue, you can troubleshoot and fix it quickly.

More Blender Details

There’s one more thing that you have to understand when you create objects in blender and use them in Monogame.  The translate, scaling and rotation set on objects in Blender are not used by the rendering method in Monogame.  There is an easy way to fix this problem.  First, let’s translate our cube in blender and see what it does in Monogame.  Go to Blender, use a cube shape and then set the Y to 10:

Cube Translated

Now export to FBX and include in your content pipeline and change your monkey to the new cube_translated object.  Your cube will look like this:

Translated Cube

The cube does not display at the translated coordinates.  This can become a problem if you build a complex object inside Blender using multiple cubes, cones, spheres, etc.  Each shape will end up rendering inside each other.  The scaling will remain at 1 and the rotation and translation will remain at zero.  To give each shape a relative position, you’ll need to change the x,y,z coordinates of the vertexes of the shape itself.  That is easy in Blender by just switching to the shape edit tool.

Go back to Blender and set your cube to (0,0,0).  Now switch to Edit mode:

Edit Mode

Now, move the cube by dragging it with the green arrow:

Now export the cube and import into your pipeline (I named mine cube_dragged).

Cube Dragged

Keep in mind that the object above has it’s vertex coordinates coded at a translated location.  That means that a rotation will occur at the zero coordinate, which is to the left of the cube.  If you were to animate the above cube rotating along the Y axis, it will orbit the Y axis like a planet orbiting an invisible sun instead of spinning like a top.

Summary

This blog post was very long and detailed.  I hope this is enough information to get you started with 3D in Blender and Monogame.  I will be continuing this series with more posts on how to animate shapes, create complex shapes in Blender and how to perform more complex 3D tasks like texture mapping.  The program featured in this blog post is nothing more than a toy program for demonstration purposes.  I’ll do a deep-dive into how you can refactor your code to make it easier to render and animate different objects independent of each other.

As always, you can go to my GitHub account and download the code by clicking here.

1 thought on “Monogame and Blender (Part 1)

Leave a Reply