RSS
 

Archive for the ‘Scripting’ Category

GUI work and more internal structure…

19 Jan

Nothing much to add to the post at Spellcaster Studios… Writing for two blogs is kind of a pain from time to time… Smile

Spent the last couple of nights working on scripting internal structures, streamlining some functions and inventing a way to pass arrays/lists from LuaJIT to C++ and vice-versa…

image

My problem was that the raycasting should return a set of intersections (not just the first one)… The way I found to do this was to create a semi-templated class to handle the array itself, get some functions to access it/destroy it and do the conversion in the raycasting function itself…

The end result is something like this:

ffi = require("ffi")
ffi.cdef[[

	int             LuaIntersectionArray_get_size(LuaIntersectionArray* ar);
	LuaIntersection	LuaIntersectionArray_get(LuaIntersectionArray* ar,
                                                 int index);
	void            LuaIntersectionArray_free(LuaIntersectionArray* ar);
]]

LuaIntersectionArray_mt = {
   __index = {
      type = function(a) return "LuaArray<LuaIntersection>" end,
      log = function(a,log_type,pre_text) std.log(log_type,pre_text..type(a)) end,
      size = function(array) return ffi.C.LuaIntersectionArray_get_size(array) end,
      get = function(array,index) return ffi.C.LuaIntersectionArray_get(array,index) end,
      free = function(array) ffi.C.LuaIntersectionArray_free(array) end,
   },
}
LuaIntersectionArray = ffi.metatype("LuaIntersectionArray",LuaIntersectionArray_mt)


function get_lua_array(c_array)
   local lua_array={}
   for i=0,c_array:size()-1 do
      local obj=c_array:get(i)
      table.insert(lua_array,obj)
   end
   c_array:free()
   return lua_array
end

 

Of course I have to do this for every type of array I want to support on Lua (except the get_lua_array function, that is generic).

On the C/C++ side, I create the LuaIntersectionArray class, with the access methods, using a #define directive that declares all the functions, etc.

Then, on the Lua side, when I want to receive an array of intersections, I can do something like this:

ffi = require("ffi")
ffi.cdef[[

	LuaIntersectionArray*	raycast_static_mesh_array(LuaRay ray);
]]
----------------------------------------------------------------------
-- Shortcut functions
function raycast_static_mesh_array(ray)
   return get_lua_array(ffi.C.raycast_static_mesh_array(ray))
end

 

It’s not that elegant, can lead to memory leaks (although it shouldn’t happen if I keep things contained like this), but it’s pretty simple to use, since the return of raycast_static_mesh_array (which only raycasts static meshes and returns an array) is a simple Lua array/table (same thing there, really).

 

On other notes, check out the animation system on the new Max Payne game, it’s fantastic!

I wish I could spend 4 or 5 months just researching IK and animation to do such good looking stuff! Smile

 

More work…

12 Jan

Haven’t had much time for anything else but work on the SurgePlayer, lately. In case I haven’t mentioned it yet, the SurgePlayer is the “game” counterpart of SurgeEd. It’s to SurgeEd as FlashPlayer is to Adobe Flash.

Basically, SurgePlayer is the runtime that takes content created in SurgeEd (and loads of scripts) and actually implements the game rules, etc.

It has been a challenge developing it because I really want to create an open framework, in terms of usability… I want to be able to use the same executable file to drive all my games from now on (which is a big challenge).

Most of the work lately was trying to integrate the changes brought on by the additional capabilities of LuaJIT, but I finally have the system up and running again, with spawning “creatures” that use the underlying navigation mesh to adjust their “random” position, that gets requested by the helper object that actually spawns them. It’s very cool!

player01

Not impressive looking, I know, but the underlying code is very nice and elegant (for my taste, of course).

The framerate is low because there are about 1200 distinct objects in this scene (the ground is made of very small tiles), and because of the shadows and no primitive clustering (yet), the frame rate takes a dive…

Anyway, there’s a new post on Spellcaster Studios, showing off the first model built for “Grey”, check it out!

 

LUA JIT

05 Jan

Following the suggestion of one of my readers, I decided to take a deeper look into LUAJIT.

At first glance, LUAJIT is a Just-In-Time compiler for Lua, that uses exactly the same interface as the normal Lua library, but compiles the code instead of interpreting, resulting in very significant speed improvements.

Just that is enough reason to use it, and I intended to do it later in the project…

But what caught my attention was the FFI library that enables me to bind functions and C structures in a much easier fashion…

Under normal circumstances, if I wanted to have a function available to Lua (for example purposes, a sum function that takes two arguments and returns the sum of both), I’d have to:

  1. Write the function itself
  2. Write a bind function (a C function)
  3. Bind that function to the environment

 

On the example:

int sum(int a,int b)
{
   return a+b;
}
 
LuaGlue lua_sum(lua_State* L)
{
   int a=lua_get_number_argument(L,1);
   int b=lua_get_number_argument(L,2);
 
   lua_push_number(L,a+b);
   return 1;
}
 
env->add_function(“sum”,lua_sum);
 

This is of course a simplification, and misses parameter validation, etc…

With Lua JIT, I can just:

  1. Write the function itself
  2. Bind the function directly in LUA

 

Step 1 is almost the same as before:

extern “C” int __declspec(dllexport) sum(int a,int b)
{   
   return a+b;
}
 

The difference is that now I have to say that the function is to be implemented through C call, instead of stdcall (the extern “C” part) and that the function has to be exported (the __declspec(dllexport) part).

Then, for step 2 on LUA, I just have to do:

extern “C” int __declspec(dllexport) sum(int a,int b)
local ffi=require(“ffi”)
ffi.cdef[[
   int sum(int a,int b);
]]
 
local var=ffi.C.sum(10,20)
print(var)
 

Much simpler and safer, I think!

The benefits aren’t just here, you can declare C structs in both sides (C and Lua) and use them without incurring performance penalties, which allied to the metatype tables allow you to actually add something like a Vec3 class or something like that… Check out the part on “Defining Metamethods for a C Type” on the FFI Tutorial. It really shows off what you can do with this…

The only issue I see is that class support is tricky, although you can use classes like this:

C code (assuming a classA):

extern "C" classA __declspec(dllexport) *classA_new() 
{ 
   return new classA; 
}

extern "C" void __declspec(dllexport) classA_set_a(classA* __this,int a) 
{ 
   __this->set_a(a); 
}    

extern "C" int    __declspec(dllexport) classA_get_a(classA* __this) 
{ 
   return __this->get_a(); 
} 

Lua code:

local ffi = require("ffi")ffi.cdef[[
   typedef struct { void *__this; } classA;

   classA*    classA_new();   
   void       classA_set_a(classA* t,int a);   
   int        classA_get_a(classA* t);
]]

local classAlocal classA_mt = 
   {  __index = 
        {   
           set_a = function(t,a) ffi.C.classA_set_a(t,a) end,   
           get_a = function(t) return ffi.C.classA_get_a(t) end,  
        },
   }
classA = ffi.metatype("classA", classA_mt)

function test()   
   local object=ffi.C.classA_new()
   print(object)
   local value=object:get_a()
   std.log(log_debug,string.format("Value=%i",value))
   object:set_a(25)
   std.log(log_debug,string.format("Value=%i",object:get_a()))
end

Very convenient indeed!

Only problem is that I designed the SurgePlayer Lua structure without this in mind, and suddenly I have what seems like a better way of doing things available…

Now I need to rethink the whole system to take advantage of this, but still keep some of the benefits I had on the previous system.

I believe it will be well worth the trouble! Smile

By the way, there’s a new post down at http://www.spellcasterstudios.com, with video! Check it out!

Until next time!