Blender importer for Devil May Cry 4: Special Edition – Part I

Recently, I’ve been trying to import some models into Blender. I got those models from Devil May Cry 4: Special Edition(DMC4SE).I’ve never done it before, so it takes me quite a lot of time to figure out how and even more time to fix bunches of problems. There’re many differences in between models from a game and DCC tools, which need special care in order to import the model correctly.That’s why we have this article. I’m here to give you some useful tips on the problems you might also come into when you write your own, especially when you’re trying to import models from a game.

Mesh

DMC4SE uses triangle solely for mesh. So importing a mesh is simply importing a bunch of triangles. For some reasons, there’re 3 ways to access faces in Blender, each has its own pros and cons. You can check it out in Blender’s official document. We’ll use the “bmesh” API, which is well suited for mesh import.

Edit Mode

In Blender, in order to modify a mesh, we have to enter “Edit Mode” first. This is very important. If you find your script not working but no errors are reported, you may want to check if you’re working in the right context. By default, we’re in “OBJECT MODE”, we can toggle “EDIT MODE” by pressing the Tab key. From python script, we can use bpy.ops.object.mode_set(mode='EDIT') to toggle “EDIT MODE”. After you have finished all the modifications, you may have to exit “EDIT MODE” again to see the changes taking effect.

Coordinate System

Before we can start the actual import work, we have to check if the coordinate system is the same between DMC4SE and Blender, and if not, we have to convert all the vertices to the right one while importing. DMC4SE is rendered with Direct3D 10, which uses right-handed coordinate system. However, Blender uses left-haned and treats +Z as the up direction. So, we have to do the conversion.

Vertex Normal

As far as I know, it is impossible in Blender to manually assign or tweak normal for each vertex. Blender will calculate the vertex normal by averaging the normal of the faces which share the same vertex. This behavior becomes very annoying when you are trying to import a mesh from a game.

For example, a full mesh in a 3D modeling software may be splitted into serveral parts when it is exported to a game dedicated format. In that way, vertices on the boundary are splitted up, they still shares the same position, but with different normal. The splitted vertices will recalculate their normal on their own, and will make the shading very ugly. Other times, you may come into some games which use custom normal to achieve special visual appearance, e.g. Guilty Gear. The mesh shades completely different without custom normal.

There’re the “Normal Edit” and “Data transfer” modifer which can “paramatrically” edit the vertex normal, but you can’t really manipulate vertex normal as you wish. So I don’t bother to import the vertex normal. I just ignore it.

Backface Cull

Backface culling is a technique used to reduce the number of triangles need to render. It is used in almost all games and Blender as well. However the backface culling is a global option and once turned on, it works for all the triangles. A lot of things we render have certain volume, and we can’t see the inner side of the object. So, most of the time, we just want to keep it on.

In DMC4SE, Dante and Nero has their coat, and we can view it from both sides. So if we make the coat a surface without thickness, it will appear as not exist when viewed from some angle. But we do not want to just turn off backface culling, we need it to keep the polygon count down. So, developers just duplicate the face in place and flip the normal. This is exactly the solution used in DMC4SE and many many other games. In a game, this may be implementaed as follows:

  1. we have a vertex buffer, which stores vertices v1, v2, v3.
  2. we have a index buffer, which specifies two faces, one clockwise and one counter-clockwise. i.e. v1-v2-v3 and v1-v3-v2.

Because the vertices are exactly the same, we do not need another set of vertices for the “back” face!

When I’m writing the importer, I found out the Blender won’t allow one vertex to belong to multiple faces. This is easy to solve. We have to duplicate not only the faces, but the vertices. That solves the coat problem.

Skeleton

In order to be able to apply skeleton animation to a mesh, we need to import skinning information too. We need to create bones, setup their hierarchy and assign vertices to bones which contribute to its deformation. That’s it, except a few tricky points that takes me a while to figure out.

Edit Mode

After create an armature, you have to enter the “EDIT MODE” in order to create bones. In Blender, an armature has two sets of bones, the edit bones and the pose bones. Edit bones are used in “EDIT MODE” to set up the bone hierarchy. Meanwhile, pose bones are used in “POSE MODE” to create keyframes and animations.By default, we’re in “OBJECT MODE”, we can toggle “EDIT MODE” by pressing the Tab key. From python script, we can use bpy.ops.object.mode_set(mode='EDIT') to toggle “EDIT MODE”

Coordinate System

Usually in a game, a matrix will be provided for each bone to represent the local transformation relative the the bones parent. We can extract location, rotation and scale components from the matrix and set up and edit bones accordingly. But we can’t just use the matrix to transform an edit bone, because the matrix is in the “Bone Space”, while we need the “Aramature Space” matrix.

To get the “Aramture Space” matrix, we just need to multiply the local matrix by the parents’ matrix recursively util we reach the root of the armature. Aslo, we need to be aware that in Blender, the matrix is stored in column major order.

Matrix to Blender Edit Bone

After an edit bone has been created, we need to set its head, tail and roll properly. Unfortunately, we only have a matrix here. DCC tools and games represent a bone in different ways. In a game usually, a single matrix is used, because that’s enough to transform a vertex correctly, the player don’t need to see and manipulate a bone, so nothing else is needed. In a DCC tool like Blender however, a bone should be displayed in a proper way for artist to work on, having a certain length and direction is certainly much more convenient. These two representations are basically equivalent.

So, why can’t we just set that matrix? In fact, we could, in older versions of Blender. There’s a “matrix” attribute there, but in newer versions, it becomes readonly. Even if we can set that matrix, we won’t reproduce the same armature as the original model. Because, the bone tail, or the length is lost when an armature is exported to an engine dedicated format. In fact, as long as the transformation matrix will remain the same no matter how long the bone is, as long as the head-tail vector points in the same direction. So, I guess we can only choose a proper bone length and settle with that.

The head-tail part is trival to calculate, we can use the matrix M to transform (0, 0, 0, 1) and (0, 0, length, 1), and we get the head-tail location in Armature space.
The Roll part takes me more time to figure out. We can calculate it from the rotation matrix. But first, let’s see how we can build a rotation matrix from a bone, it’s much eaiser to understand.

A rotation matrix can be break into two parts:
1. Rotate over the shortest arc so that the +z axis is aligned with the head-tail vector, let the matrix be bMatrix;
2. Rotate around the z axis by Roll, let the matrix be rMatrix.

Following is a code list with comment describing this process.

def vec_roll_to_mat3(vec, roll):
    target = Vector((0,1,0))
    nor = vec.normalized()
    axis = target.cross(nor) # calcuate the rotation axis
        # Rotate to align the up axis
    if axis.dot(axis) > 0.000001:
        axis.normalize()
        theta = target.angle(nor)
        bMatrix = Matrix.Rotation(theta, 3, axis)
    else:
        updown = 1 if target.dot(nor) > 0 else -1
        bMatrix = Matrix.Scale(updown, 3)
            # Rotate by roll
    rMatrix = Matrix.Rotation(roll, 3, nor)
    mat = rMatrix * bMatrix
    return mat

Once we known how to construct a rotation matrix from vec/roll, it is trival to calculate vec/roll from a rotation matrix:
1. get the up axis from mat.col[1], this is the “vec” part
2. construct the “bMatrix” as vec_roll_to_mat3(up, 0)
3. calculate the rMatrix = bMatrix.inverted() * rMatrix
4. rMatrix is a 3×3 matrix, but it contains only rotation around z axis. So we can the the angle, i.e the roll as atan2(rMatrix[0][2], rMatrix[2][2])

Assign Vertices to Bones

In Blender, we need to create a vertex group for each bone which represents all the vertices that will be affected by the bone. All the vertices that will be affected should be assigned to the group with proper weight. This is usually done by weight painting, but can also be set by code. Sometimes in a game, bone weight is stored as an unsigned integer, which should be converted to a floating point number in [0.0, 1.0].
Then an armature modifier should be added to the meshes.
In Blender, we need to create a vertex group for each bone which represents all the vertices that will be affected by the bone. All the vertices that will be affected should be assigned to the group with proper weight. This is usually done by weight painting, but can also be set by code. Sometimes in a game, bone weight is stored as an unsigned integer, which should be converted to a floating point number in [0.0, 1.0].
Then an armature modifier should be added to the meshes.