A game programmer, interested in graphics programming.
Posts by Simon Yeung
  1. Implementing Voxel Cone Tracing ( Counting comments... )
  2. Angle based SSAO ( Counting comments... )
  3. Shader Generator ( Counting comments... )
  4. Photon Mapping Part 2 ( Counting comments... )
  5. Photon Mapping Part 1 ( Counting comments... )
  6. Software Rasterizer Part 2 ( Counting comments... )
  7. Software Rasterizer Part 1 ( Counting comments... )
  8. Light Pre Pass Renderer on iPhone ( Counting comments... )
  9. Extracting dominant light from Spherical Harmonics ( Counting comments... )
  10. Inverse Kinematics (2 joints) for foot placement ( Counting comments... )
  11. Writing an iPhone Game Engine (Part 7- Postmortem) ( Counting comments... )
  12. Microfacet BRDF ( Counting comments... )
  13. Spherical Harmonic Lighting ( Counting comments... )
  14. Dual Quaternion ( Counting comments... )
  15. Dual Number ( Counting comments... )
  16. Using PID Controller ( Counting comments... )
  17. Writing an iPhone Game Engine (Part 6- Performance) ( Counting comments... )
  18. Writing an iPhone Game Engine (Part 5- Audio) ( Counting comments... )
  19. Writing an iPhone Game Engine (Part 4- Streaming) ( Counting comments... )
  20. Writing an iPhone Game Engine (Part 3- Scripting) ( Counting comments... )
  21. Writing an iPhone Game Engine (Part 2- Maya Tools) ( Counting comments... )
  22. Writing an iPhone Game Engine (Part 1- Memory management) ( Counting comments... )
  23. Writing an iPhone Game Engine (Part 0 - Introduction) ( Counting comments... )
  24. SSAO using Line Integrals ( Counting comments... )
Technology/ Code /

Adding script support to an engine has many benefits such as writing game play code without recompile the engine source code which may takes a long time. It also can draw a much clear boundary between the game code and the engine code. I chose to use Lua(ver. 5.1.4) as it is easy to embed and its size is small. As I am no expert on Lua, I would like to write about what I have learnt on how to bind Lua and C/C++.

Calling C function from Lua
First, you need to create a lua_State*, where all Lua operations are done within it, by lua_open() (or you may use lua_newState() if you need to hook up your memory allocator to Lua.)

  1. lua_State* luaState= lua_open();

Lua and C can exchange data through a virtual stack. Both Lua and C can push and pop data from or to the stack. Say, we have already register a C function for Lua to use with function signature:

  1. function drawText(str, screenPosX, screenPosY);

then in Lua side, when the following Lua script is executed:

  1. textWidth= drawText("Ready Go~",  240, 160);

3 values will be pushed to the Lua Stack:

We can get the values from the stack in C using an absolute index counting from the bottom of the stack (start from 1) or a relative index to the top of the stack (start from -1).

Then we can retrieve the values in stack inside the C function called by Lua with the following code:

  1. int drawText(lua_State* luaState)
  2. {
  3.     float screenPosY = (float) lua_tonumber(luaState, -1);  // get the value 160
  4.     float screenPosX = (float) lua_tonumber(luaState, -2);   // get the value 240
  5.     const char* str = lua_tostring(luaState, -3);                     // get the value "Ready Go~"
  6.     printf("Text '%s' draw at (%f, %f)\n", str, screenPosX, screenPosY);
  7.     int textWidth= strlen(str);
  8.     lua_pushnumber(luaState, textWidth);                            // return a value to Lua
  9.     return 1;                                                                           // number of values return to Lua
  10. }
when the C function exit, the stack will look like:
After Lua get the return value from the C function, the parameter and the return value of the C function will be popped out of the stack.
But before Lua can execute the drawText(), remember to register it to the lua_State* by:
  1. lua_register(luaState, "drawText", drawText);



Calling Lua functions from C
We can also call Lua functions from C. For example, we have declare a function in Lua script for initializing the engine configurations:

  1. function initEngineConfig(date)
  2. print('initialize engine on ' .. date);
  3. end

As Lua is a typeless language, it also treats functions as variables. So we need to push the Lua variable 'initEngineConfig' into the stack with its arguments with the following C code:

  1. lua_getglobal(luaState, "initEngineConfig");   // get the function to the stack
  2. lua_pushstring(luaState, "18th Aug, 2011");   // push the argument of the function
  3. lua_call(luaState, 1, 0);                          // execute the function

(You may also use lua_pcall() instead of lua_call() to get more debug info  when error occurs)
The above C codes is equivalent to calling a function in Lua:

  1. initEngineConfig("18th Aug, 2011");

We can also use similar technique to execute an object's method in C. For instance, we can call an object's method in Lua:

  1. gameObjectA:update(timeSlice);

We can do this in C too. In Lua, the colon syntax is just a short form for writing the statement:

  1. gameObjectA.update(gameObjectA, timeSlice);
So, we need to push the 'update' function of 'gameObjectA' onto the Lua stack with 2 arguments:
  1. lua_getglobal(luaState, "gameObjectA"); // for getting the 'update' function of 'gameObjectA'
  2. lua_getfield(luaState, -1, "update");     // get the 'update' function of 'gameObjectA'
  3. lua_insert(luaState, -2);                           // swap the order of "gameObjectA" and "update"
  4.                                                                 // so that "gameObjectA" becomes an argument
  5. lua_pushnumber(luaState, 1.0f/30.0f); // push the timeSlice argument on the stack.
  6. lua_call(luaState, 2, 0);                      // execute the functions.

This is basically how Lua interacts with C, but you also need to know how to represent C structure as user data or light user data. You may also need to know LUA_REGISTRY_INDEX for creating a variable in C without worrying conflicts in variable name. After knowing these things you may want to try some binding library to generate the binding. But I hope these little binding methods can help someone who want to bind Lua and C on their own.


Reference:
[1] Lua manual: http://www.lua.org/manual/5.1/manual.html
[2] Lua user data: http://www.lua.org/pil/28.1.html
[3] Lua light user data: http://www.lua.org/pil/28.5.html
[4] LUA_REGISTRY_INDEX: http://www.lua.org/pil/27.3.2.html