Blender, Direct3D, Meshes and You

July 10th, 2009

I’ve spent last couple of days writing a custom mesh exporter for Blender. It all seemed pretty trivial in the beginning (“it’s just conversion of data structures”). It’s easy to iterate over all the faces in the mesh, and then over vertices and then reading their position and normal. Everything seems fine, but there’s a difference in how Blender stores the data, and what format will a Direct3D VertexBuffer accept. Specifically I had two major annoyances.

First problem – flat faces vs smooth faces. If you look at Blender’s Python API Documentation, you will see, that a MFace is just marked as smooth and there’s all to it. If a face is smooth, the normals get interpolated for positions between the vertices. This works the same in Direct3D world. What about flat faces? Well, in Blender a MFace is not marked as smooth and has another attribute – no (short for “normal”). Therefore, if you want to have a flat-shaded face in Direct3D, you have to duplicate the vertices and use their original positions and replace their normals with face normals. This introduces another problem – your exported vertex buffer will have additional vertices in it, which won’t map easily (1-to-1) to the indices Blender has.

Second problem – UV mapping. In Blender you can create a UVLayer. It’s just a set of UV coordinates mapped to vertices in faces. What does Direct3D expect? UV coordinates mapped to vertices… not faces. Blender stores UV coordinates as an attribute of MFace, as list of UV vectors. Notice how a single vertex can belong to multiple faces, and therefore can be assigned a different set of UV coordinates for every face. This means we can’t reuse this vertex in VertexBuffer (or I don’t know a way to do so). Direct3D VertexBuffer will have to hold a copy of this vertex with its original position and normal for every pair of UV coordinates it’s assigned. This again introduces problem of vertex indices not mapping easily (1-to-1) between Blender world and Direct3D world.

The indices problem can be solved by reindexing the newly created vertices and matching them with the faces. Of course that would work, as long as it would be done in a deterministic way (vertices not changing their indices between exports and mesh modifications). And everyone knows indices are needed for the DrawIndexedPrimitive call.

After spending a couple of hours thinking about an elegant solution, I gave up. I’ve decided to go with less elegant, simpler DrawPrimitive calls. So I just iterate over all the faces in the mesh and create vertices as I go – three new vertices per every face. I realize there will be more data to process for the GPU, but I’m just beginning to write my engine and need to make progress. I have more interesting problems to solve at this very moment. After all, I write abstractions for the low-level stuff and can always optimize the mesh later. Using NvTriStrip comes to mind as one of possible ways to achieve that. Or something smart, as I’ll get to know more about Blender and Direct3D.

From C to C# to C++

July 3rd, 2009

It’s amazing, how much computing has evolved in just a dozen years. In 1997 I started learning C, just after I got comfortable with MC680×0 and x86 Assembly. It was natural to think about the data structures in terms of their layout and alignment in memory. It was natural to think about “what will the CPU do if I do that”. It was very natural to think of all these details, to the level of bare silicon. I remember how happy I was when my first inline assembly optimization started working. I remember spending hours with a simple debugger, watching the memory and CPU registers change after every instruction – and I remember enjoying this time a lot. Every little optimization counted, because the resources at hand were very scarce. My PC at that time was a Pentium MMX running at a whopping speed of 75MHz, with 64MB of RAM and S3 ViRGE video card. Can you imagine using one of those today? The code people used to write wasn’t even called “native”. There was no “managed” code yet. The code was just “code”. A couple of months later I started learning C++ and were using the two alternately, whenever I wanted.

Fast forward to 2003, the time of .NET Framework version 1.1. Somehow I have never seriously used Java, therefore C# and .NET Framework was my first “managed” programming environment. I loved C# for its readability and simplicity. There were no pointers and memory allocations that needed to be taken care of. There was no header file mess, everything got neatly compiled. The managed environment took a lot of burden off the programmer, but not without a cost. The programs written in managed code were really resource hungry, which wouldn’t be acceptable six years earlier. The focus was on writing code fast, instead of writing fast code. Of course it had its advantages – I learned quite a lot about real object oriented programming, software architecture, design patterns (well, I learned the names of some tricks I’ve been using, and learned a couple of new ones) and code maintainability. I think I wouldn’t be able to learn that if I were using C++. I worked on semi-enterprise level applications.

I used .NET and C# for six years. Then I decided I needed a change.

Right now I’m re-learning C++, and learning the modern approach to C++ programming (existence of which I wasn’t even aware). The first few weeks were a horror. I couldn’t live without good refactoring tools (Visual Assist X isn’t even half as good as Resharper). I’ve made total mess in my code, because I was used to not having to think about the header files. Class construction worked the opposite way I was used to. Resource leaks in my code were ubiquitous. The C++ syntax caused pain in my fingers. And a couple of weeks later, I still make newbie mistakes I wouldn’t make in C#. And again, I’m enjoying the hell out of my time in front of computer screen, solving problems that would be nonexistent in “managed” code. It’s fun, because again it feels like solving a puzzle. And again, programming till late night is a fun thing to do.

I think everyone should try something new (or old) every now and then. It’s really refreshing.

Besides, all modern games are written in “native” code for a reason…