Post-mortem

This is the last semester and our game is published to steam Greenlight.

This semester I finished the car crash scene and night 2 scene. I also did some trivial stuff such as climbable ladder and pause screen.

Most of the time I was fixing bugs. Most bugs are text display. We support both keyboard + mouse and gamepad, so we need to show different hints to the players. For example, when playing with keyboard and mouse, we show player “Press F to open the door”. When player is using a gamepad, we need to show “Press X to open the door”. This seems an easy task but actually it could cause a lot of bugs.

Another pain is oculus rift. Even if it works fine in the editor doesn’t mean it can also works well with build version.

Game Engineering 2 Assignment 10

Objective:

1. Implement diffuse lighting.

2. Learn how to debug fragment shader.

Add Normals to Meshes

My Maya plug-in can export normal information of each vertex, so all I need to do is to add this information when I write the binary mesh file and change my D3DVERTEXELEMENT9 struct.

Ambient and Diffuse Lighting Models

In an ambient lighting model, all light beams fall uniformly from all directions onto an object. This leads to:

color = original_color * ambient_intensity * ambient_color

In a diffuse lighting model, the direction of the light is considered but the reflections are independent of the observer’s position.

Diffuse lighting model follows Lambert’s law and this leads to:

color = original_color *  diffuse_intensity * diffuse_color * cos alpha

To use this formula the normal vectors of the object and the direction vector of diffuse light needs to be normalized.

Here is a screenshot of my program:

screenshot10

Use I, K, J, L to move the light.

To modify the ambient light and the diffuse light, change the values in Asserts/demo.scene.

scene10

Use PIX To Debug Fragment Shader

To debug a fragment shader:

1) Select the drawcall you’d like to debug

2) In “Render” tab right-click the pixel and choose “Debug This Pixel”

3) Click the “Debug Pixel (x, y)” link

debug_fragment

Time Estimates

  • Reading – 1 hours
  • Coding – 2 hours
  • Write up – 1 hours
  • Total – 4 hours

Download Executable

Game Engineering 2 Assignment 9

Objective:

1. Create a Maya plug-in that can export geometry in a customized mesh format.

2. Add user-defined events to organize the information that PIX shows.

3. Add a setting file that user can specify settings for my game.

Create a Maya Plug-in

Maya plug-ins can be made with C++ and Python. Here I used C++ because I’m not familiar with Python.

Before I start writing the plug-in, there are some set up need to be done in the Visual Studio:

  •  Create a dynamic libaray (.dll) project and set the Target Extension to .mll
  • C++ -> General -> Additional Include Directories: (Maya Install Path)\include\
  • C++ -> Preprocessor: add NT_PLUGIN; REQUIRE_IOSTREAM;
  • Linker->Additional Library Directories: (Maya Install Path)\lib\
  • Linker->Input->Additional Dependencies: add Foundation.lib; OpenMaya.lib;

Autodesk has a file translator example at here. In my case I only need to implement 3 methods:

  • bool haveWriteMethod() const;
  • MString defaultExtension() const;
  • MStatus writer( const MFileObject& i_file, const MString& i_options, FileAccessMode i_mode );

I had a helper function WriteMeshToFile() called from writer() method. This function uses std::ofstream to write vertex and index information to a file. The only thing needs to be noticed is Maya uses right-hand system and Direct3D9 uses left-hand system and this leads to:

  • For position, normal and tangent: z value in Maya needs to be -z in D3D;
  • For bitangent: x and y in Maya needs to be -x, -y in D3D;
  • For texture coordinate: v in Maya needs to be 1 – v in D3D;
  • For index: index for a triangle A, B, C needs to be A, C, B in D3D

Improved AssetBuilder

I improved my AssetBuilder and it now supports multiple image formats. This is what my AssetsToBuild.lua looks like:

assetlist

Now you can use .bmp, .jpg, .tga, .dds as textures instead of using just .png.

Manage Game Scene

I feel like I need a file to organize my scene so I came up with this scene file:

Scene1

It lists all the entities in my scene (Cameras and GameObjects for now). Each game object has 4 parameters: position, rotation, mesh and material.

pyramid.mesh in my Assets folder is a mesh I exported from Maya.

Here is a screenshot of my program:

sc9

 

Orgnize PIX Information

In D3D9 users can group their code with D3DPERF_BeginEvent(0, eventName) and D3DPERF_EndEvent().

I grouped my setting material code and drawing mesh code. Here is a PIX capture showing the events in my program:

PIX_organize

User Settings File

I added a settings file where users can change window width, height and whether the program should run in full screen mode.

The basic idea is treat the setting file as a Lua table and read entities from the table.

Time Estimates

  • Reading – 2 hours
  • Coding – 6 hours
  • Write up – 3 hours
  • Total – 11 hours

Download Executable

Game Engineering 2 Assignment 8

Objective:

1. Create a Texture Builder that can convert images in most common image formats to DDS images.

2. Apply textures to meshes.

Create a Texture Builder

To convert an image to a DDS image, there are 3 things we need to do:

1) Get the information about the image using D3DXGetImageInfoFromFile function.

The reason we do this is we need to know what kind of texture it is. An image could be a texture, a cube texture or a volume texture and each kind of texture has its own create function.

If we dig further we will find that Direct3D support the following image file formats: BMP, JPG, TGA, PNG, DDS, PPM, DIB, HDR, PFM.

2) Create a texture from the image file

To create a texture we called D3DXCreateTextureFromFileEx function.

To create a cube texture we called D3DXCreateCubeTextureFromFileEx function.

To create a volume texture we called D3DXCreateVolumeTextureFromFileEx function.

3) Save the texture to a file

To save the texture as a DDS file, we called D3DXSaveTextureToFile(path, D3DXIFF_DDS, pTexture, NULL).

Texture Coodinates

The texture coordinates in Direct3D 9 are defined like this:

uv

Here is a screenshot of my program:

screenshot_08

Here is a PIX capture showing the texture I used:

PIX_texture

Here is the definition of my plane that using this texture:

floor_08

Time Estimates

  • Reading – 2 hours
  • Coding – 4 hours
  • Write up – 2 hours
  • Total – 8 hours

Download Executable

Game Engineering 2 Assignment 7

Objective:

1. Create a human-readable mesh file.

2. Create a mesh builder to save the mesh file as a binary file.

3. Read the binary mesh file to create mesh in the game.

Mesh File

Here is the mesh file I used to define the floor mesh in my game:

mesh

I stored the data of each vertex in an array so I can iterate all the vertices in a for loop. For each vertex, there is a key  “position” and a key “color” and this is for human to read.

Here is the binary version of the same file after it is processed by my mesh builder:

binary

 

A binary file is much smaller than a text file. In my case, the size of my human-readable mesh file is 436 bytes and the size of binary file is 128 bytes, just 1/4 of the text file. So using binary files can reduce the memory consumption of my program.

Writing a Lua Helper Class 

Now I have 2 types of asset are represented as Lua tables, one is mesh and the other is material. Instead of writing reading Lua table functions for each asset, I decided to write a helper class to deal with Lua tables.

Consider my mesh file, here is how I use my helper class:

LuaHelperUsage

Basically what I want to achieve is I can use a key to get any value I want in the Lua table.

I used this tutorial  Using Lua with C++ as my reference when I code my helper class.

Here is my helper class:

LuaHelper

 

The most important function is PushToStack. What it does is it pushes the table we want to manipulate on the top of the stack, which has a index of -1 in Lua. Here is the code:

PushToStack

Write/Read Binary Files in C++

I used std::ofstream and std::ifstream to manipulate binary files. I used 4 writes and 4 reads to write and read my mesh data.

Here is how I write file:

writemesh

 

And here is how I read file:

readmesh

It is possible to use less read and write commands. For example, if I put vertex_count and index_count into a struct, I can reduce my read/write commands to 3. I didn’t do that because I want to keep my code as simple as possible.

Time Estimates

  • Reading – 4 hours
  • Coding – 6 hours
  • Write up – 2 hours
  • Total – 12 hours

Download Executable

Bitwise Operation in C++

1. If we want to know whether one bit of this number is zero:

// i_index: the index of rightmost bit is 0
bool IsZero(int i_number, unsigned int i_index)
{
return ((i_number & (1 << i_index)) == 0);
}

2. If we want to know the exact value of one bit:

// i_index: the index of rightmost bit is 0
int GetOneBit(int i_number, unsigned int i_index)
{
int temp = i_number;
temp = temp >> i_index;
temp = temp & 1;
return temp;
}

3. If we want to know the value of several bits:

unsigned int GetBits(unsigned int i_input, unsigned int i_start, unsigned int i_end)
{
assert(i_start <= i_end);
// create mask
unsigned int mask = ((1 << (i_end – i_start + 1)) – 1) << i_start;
// use AND to get bits, then remove all the 0s on the right
unsigned int r = (i_input & mask) >> i_start;

return r;
}

4. Set one bit to one:

void SetBit(int& value, unsigned int i_index)
{
value |= (1 << i_index);
}

5. Set one bit to zero

void ClearBit(int& value, unsigned int i_index)
{
value &= ~(1 << i_index);
}

6. Find first set bit:

unsigned int FindFirstSet(int value)
{
unsigned int pos = 0;
if ((value& 0xFFFF) == 0)
{
value>>= 16;
pos += 16;
}
if ((value& 0xFF) == 0)
{
value>>= 8;
pos += 8;
}
if ((value& 0xF) == 0)
{
value>>= 4;
pos += 4;
}
if ((value& 0x3) == 0)
{
value>>= 2;
pos += 2;
}
if ((value& 0x1) == 0)
pos += 1;
return pos;
}

7. Find how many set bits in a number:

unsigned int SetBitCount(int value)
{
unsigned int count;
for (count = 0; value; count++)
{
value &= (value – 1);
}

return count;
}

Game Engineering 2 Assignment 6

Objective:

1. Learn how to iterate a table in Lua.

2. Learn how to run an application from a C++ program.

3. Learn how to use precompiled shader.

Iterate Table in Lua

The most common way to iterate a table is to use pairs() or ipairs():

for key, value in pairs(table) do
XXX
end

for key, value in ipairs(table) do
XXX
end

The difference between pairs() and ipairs() is pairs() iterates through all the keys in a table and ipairs() only iterates through numeric keys. So if your table is like this:

table = { value1, value2, value3, … }

Then there is no difference between using pairs() and ipairs(). However if your table is like this and each key is a string:

table = {

key1 = value1,

key2 = value2,

key3 = value3,

}

In this case you should use pairs() because your key is not continuous number.

I use Lua table to store all the files that needs to be built by my asset builder.  Here is the Lua file I used:

assetlist

For each kind of asset type, there are 3 elements:

  • builder: indicate which asset builder to use
  • extensions: the extension of the source asset file and the target extension
  • assets: list names of all the asset files, if the asset builder requires additional argument then list the argument here as well

Run Program in C++

Different kind of asset file will be built by different asset builder. In my program, each asset builder is a “.exe” program, so I need to run these “.exe” program in my code.

The way I do is to use a Win32 function named CreateProcess():

BOOL WINAPI CreateProcess(
  _In_opt_     LPCTSTR lpApplicationName,
  _Inout_opt_  LPTSTR lpCommandLine,
  _In_opt_     LPSECURITY_ATTRIBUTES lpProcessAttributes,
  _In_opt_     LPSECURITY_ATTRIBUTES lpThreadAttributes,
  _In_         BOOL bInheritHandles,
  _In_         DWORD dwCreationFlags,
  _In_opt_     LPVOID lpEnvironment,
  _In_opt_     LPCTSTR lpCurrentDirectory,
  _In_         LPSTARTUPINFO lpStartupInfo,
  _Out_        LPPROCESS_INFORMATION lpProcessInformation
);

The 1st parameter of this function is the complete path to the “.exe” file; the 2nd parameter is the argument that passed to that “.exe”.

However, if the path to the “.exe” file contains space, then the correct way to call CreateProcess() function is to pass NULL as the 1st parameter and pass the whole command (path to “.exe” plus arguments) as the 2nd parameter.

Use Precompiled Shader

Previously I compiled my shader when my program loads a shader file (.hlsl file). Now my asset builder compiled all my shader file into binary files and I need to read these binary files whenever my program loads a shader.

The create shader function in Direct3D9 is like this:

HRESULT CreateVertexShader(
  [in]           const DWORD *pFunction,
  [out, retval]  IDirect3DVertexShader9 **ppShader
);

The 1st parameter is a const DWORD*. So now what I need to do is to read all the content of the binary file into a buffer, and then pass this buffer as the 1st parameter.

Problem Encountered

When I tried to call CreateProcess() function I got an error said that windows cannot find the path. The reason is the path contains space and I passed the path as 1st parameter.

Time Estimates

  • Reading – 2 hours
  • Coding – 5 hours
  • Write up – 2 hours
  • Total – 9 hours

Download Executable

Game Engineering 2 Assignment 5

Objective:

1. Learn the transformation between different coordinate spaces.

2. Learn how to debug shader program.

Multiple Coordinate Spaces

The first thing to notice is there are 2 types of Cartesian coordinate systems: left-handed and right-handed.

lh_rh

DirectX uses left-handed and OpenGL uses right-handed. What does this mean is it determines which face is the front face. Normally graphic card will not draw back faces because you will not see them. In a left-handed system a clockwise order of vertices defines a front face.

When we want to draw an object on the screen, each vertex of that object needs to go through 3 processes:

1) Model Space (Local Space) to World Space transformation

2) World Space to View Space (Camera Space) transformation

3) View Space to Projection Space (Screen Space) transformation

World Transform

Usually we store the world position, rotation and scale of an object into respective 3d vector, but graphic card does coordinate transformation through matrices, so what we need to do is to create a matrix from our position, rotation and scale vectors.

The basic idea is to form a matrix from position, rotation and scale respectively, and then multiply these matrices together to form the final matrix.

In D3DX there is a function called D3DXMatrixTransformation that can help us do this job.

View Transform

This transformation is actually the inverse of the camera-to-world transformation.

In D3DX there is a function called D3DXMatrixLookAtLH can help us do this job.

Projection Transform

This transformation has nothing to do with the camera itself. What matters is the field of view, aspect ratio, near and far view-plane of the camera, or what we can say is the lens of the camera.

In D3DX there is a function called D3DXMatrixPerspectiveFovLH can help us do this job.

 

My Program

Here is a screenshot of my program:

app

Control of the Camera:

W, S, A, D: move the camera in XY plane.

Q, E: yaw the camera

Z, C: pitch the camera

Control of the Cube:

Up, Down, Left, Right: move the cube in XZ plane

 

Debug Shader Program

In DirecX 11 we can debug shader code in Visual Studio, but to debug a DrectX 9 shader program we need to use PIX.

Here is a screenshot of debugging shader program in PIX:

PIX

You can run your shader program step by step just like you debug your C++ code in visual studio.

 

Problem Encountered

In this assignment I encountered a problem that my debug build can work properly but my release build just show nothing. It turned out that I put my ID3DXConstantTable::SetMatrix in an assert() function so in release build this function is never called. Unfortunately Visual Studio is smart enough that my entire render function is skipped in the release build. The lesson I learned is never put a function in assert(), instead I should use a variable to store the return value and put that variable in the assert();

 

Time Estimates

  • Reading – 4 hours
  • Coding – 12 hours
  • Write up – 2 hours
  • Total – 18 hours

Download Executable

Game Engineering 2 Assignment 4

Objective:

1. Learn how to use constant table in Direct3D 9.

2. Change material file to include shader constant.

3. Learn how to call executable program from Lua script.

Direct3D Constant Table

A shader program can have global variables just like a normal C/C++ program. These variables are contained in the constant table and can be accessed from C++ program with the ID3DXConstantTable interface.

There are 2 ways to get the ID3DXConstantTable interface of a shader:

1. Using D3DXCompileShaderFromFile

HRESULT D3DXCompileShaderFromFile(
  _In_   LPCTSTR pSrcFile,
  _In_   const D3DXMACRO *pDefines,
  _In_   LPD3DXINCLUDE pInclude,
  _In_   LPCSTR pFunctionName,
  _In_   LPCSTR pProfile,
  _In_   DWORD Flags,
  _Out_  LPD3DXBUFFER *ppShader,
  _Out_  LPD3DXBUFFER *ppErrorMsgs,
  _Out_  LPD3DXCONSTANTTABLE *ppConstantTable
);

We use this function to compile a shader and the last parameter of this function is a pointer to a ID3DXConstantTable pointer.

2. Using D3DXGetShaderConstantTable

HRESULT D3DXGetShaderConstantTable(
  _In_   const DWORD *pFunction,
  _Out_  LPD3DXCONSTANTTABLE * ppConstantTable
);

After we compiled a shader, we can call D3DXGetShaderConstantTable to get its constant table. The 1st parameter of this function can be reinterpret_cast<DWORD*>(compiledShader->GetBufferPointer()).

To access a constant, we first need to get its handle. This can be done by calling ID3DXConstantTable::GetConstantByName. To set the value of the constant, we use ID3DXConstantTable::SetXXX, here XXX can be Bool, Float, Int, Matrix or Vector. The only difference is ID3DXConstantTable::SetDefaults, it will set the constant to its default value which is declared in the shader file.

New material format

In this assignment I modified the material file so it now looks like this:

material

 

Constants are stored in a table called “constants”, each constant contains a key, which is the name of the constant and its value, which might be another table. This way my constant table and shader table has the same architecture and I can read them using similar code.

This is the result of applying this material:

run1

This is the result after I change the g_colorModifier to { 0.5, 0.5, 0.0 }

run2

You can use 4 arrow keys (Up, Down, Left, Right) to move the rectangle.

Execute program from Lua

To run a .exe program from a Lua script is very simple. All you need to do is this line:

local result, terminationType, exitCode = os.execute( commandLine )

The command line is the path to your .exe plus the arguments you want to pass to the .exe.

Problem Encountered

When I read the Lua table, I found if the table contains key and value pairs, then when I iterate through the table ,the order that each item is read is not fixed. In my material file I have a table called “shaders” which has 2 key and value pairs. At first I thought the 1st item that I read must be the path of vertex shader, but I get errors when compiling the vertex shader. However, this error seems appear randomly. After some debug I found sometimes the 1st item I got was the path of fragment shader. It turns out that if your table contains keys, then Lua calculates the order by using the hash value of the key and every time Lua may get different hash values.

Time Estimates

  • Reading – 4 hours
  • Coding – 4 hours
  • Write up – 2 hours
  • Total – 10 hours

Download Executable

Game Engineering 2 Assignment 3

Objective:

1. Learn how to use index buffer.

2. Learn how to create your own asset file format.

Direct3D Index Buffer

The only things graphic cards can draw on the screen are points, lines and triangles. This means when we want to draw a rectangle we are actually drawing 2 triangles.

rect

 

To draw 1st triangle, we need vertex V1, V2 and V3; to draw the 2nd triangle we need vertex V2, V3 and V4. In this case vertex V2 and V3 are used twice, if we can find a way to reuse these 2 vertices we can reduce the size of our vertex buffer. The trick is to use index buffer. In the index buffer we store the indices of vertices, which are integer offsets into the vertex buffer.

To use index buffer, we first need to create index buffer using IDirect3DDevice9::CreateIndexBuffer. Then we lock the buffer to fill it with data and unlock it  to let the graphic card use it, just like the way we use a vertex buffer. When we draw our rectangle, we use IDiect3DDevice9::SetIndices to set which index buffer to use. Then we draw with IDirect3DDevice9::DrawIndexedPrimitive.

app_screenshot

 

In my program the bottom-left vertex has an index of 0, the upper-left vertex has an index of 1, the bottom-right vertex has an index of 2 and the upper-right vertex has an index of 3.

 

Pix

 

In PIX we can see the order the vertices I used to draw the 1st triangle is 0, 1, 2 and the 2nd triangle used 2, 1, 3.

Create material asset

In this assignment we need to define a new asset to represent a material using Lua. Here is my material file:

material

Basically it is a Lua table with 2 entries: each one has a key which defines the shader type and a value which is the path of that shader file.

Load this file is just like load any Lua scripts, except this file only return a single value which is the table.

loadtable

Here is the code I load the entry of my table. I first push the key (“vertexShader” or “fragmentShader”) onto the stack. Then I get the value using lua_gettable. Because I pushed the key, so now the key has an index of -1 and the table has an index of -2. Now I can use lua_tostring to get the value.

Problem Encountered

When I debug my program my program reports that it cannot find the material file. The reason is by default when we press F5 in Visual Studio, it runs what $(TargetPath) points to and using $(ProjectDir) as the working directory. However in my program my .exe file and final asset files are not at $(ProjectDir). To solve this problem I need to config the Debugging page as follows:

debug

Time Estimates

  • Reading – 6 hours
  • Coding – 3 hours
  • Write up – 2 hours
  • Total – 11 hours

Download Executable