Assignment – 11 – Maya Mesh Exporter and Transparency

The Maya plug-in

This week we added a Maya mesh exporter to our solution. JP gave us a project with most of the supporting code. We had to write the actual export function which writes data to the file. The exporter is intended to export a maya object to our Lua formatted mesh file. It was a trivial task and most of my time was spent getting tabs and the braces to show up nice and correct in the exported mesh.

exporterproject

The MayaMeshExporter project outputs a .mll file which is copied to the maya’s plug-in directory using a custom build step. This project has no dependencies on other projects and no other project depends on it. The plug-in only needs to be built once and is not needed by the game to build. Hence I have also excluded it from the solution build.

After the custom build step, the plug-in is copied to MAYA_PLUG_IN_PATH and shows up in the Maya Plug-in manager.

mayapluginmanager

Once the plug-in is loaded, we can attach the VS debugger to maya’s process and debug the plugin:

exporterdebugging

Transparency

The next step was to make modifications to our graphics code so that we can draw objects with transparency along with opaque objects. This is accomplished via render states. Render states control how the graphics hardware will render the mesh, its vertices, colors etc.

To draw an object with transparency, we want to enable alpha blending and depth testing and disable depth writing. All of these are different render states that can be toggled. When drawing an object with transparency, the shader will use the alpha value to blend the already drawn pixel with the new color value. We want to render these back-to-front so that the objects in the front are properly blended with the objects on the back, similar to the painter’s algorithm. The depth testing is necessary to cull translucent objects in the back if they are occluded by an opaque object in-front of them.

To do all of this, we specify the render states in our effect files. I chose to have another table with the render states as booleans. My effect builder checks for the existence of this table, and then checks for the existence of each render state. If it exists, the value is read and set.

effectwithstates

An efficient way to store the states is to use bits of an integral type. We just need to keep track of which bit corresponds to which render state. This is how I do it:

        typedef uint8_t RenderState;

        enum eRenderStateTypes : RenderState
        {
            ALPHA       =   1 << 0,
            DEPTHTEST   =   1 << 1,
            DEPTHWRITE  =   1 << 2,
            FACECULL    =   1 << 3
        };

        struct EffectInfo
        {
            std::string vertexShaderPath;
            std::string fragmentShaderPath;
            RenderState renderState;
        };

Currently RenderState is of type uint8_t. This means I can store 8 different states, one bit per state. The enum eRenderStateTypes is used to get the integer value for each render state.

RenderState and eRenderStateTypes are used in three places: the effect builder, the effect loader and graphics code that sets the effect. There are multiple benefits of defining the render states this way.

I don’t have to remember which bit corresponds to which render state, the bits can be changed here without anything breaking. I can also easily change the underlying type in one place instead of replacing uint8_ts in a whole bunch of places. All of this drastically reduces chances of bugs and makes it obvious when RenderState is being checked for a specific state.

The render state integer is generated after reading the lua effect file. This is appended to the end of the binary effect file after the paths to the two shaders. Then I can just grab the next byte(sizeof(RenderState)) after reading in the two paths.

The two binary effect files in hex view:effectbinarycomparison

As you can see the transparent effect has the byte: 00001011. This corresponds to the render states:

 		Alpha = true,
		DepthTest = true,
		DepthWrite = false,
		FaceCull = true 

This means everything came out right.

The next step is to read this byte when loading the effect and set the corresponding render states when I call SetEffect.


Here is the new slightly interesting scene:

Controls:

Arrow keys: move box

J,K : rotate the torus

WASD : move camera

Download