|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThis is my first article for The Code Project. While C++ has been relegated to hobby status for me, I thought my experience in the following could shed some light for others. That and I hope other people interested in Lua, luabind or Ogre 3d might see that it's not too hard once you get the hang of it. Give it a go and create some cool stuff! So here is my experience from integrating luabind into a very simple Ogre 3d application. I won't go in to detail on the Ogre side of things, but will concentrate on the binding of the Ogre 3d classes to Lua. For more on Ogre 3d itself, refer to its home page, Wiki and very good forum. For luabind documentation, see here. I recommend that you look at this when you see something new in the code below. I'll very briefly touch on compiling luabind and Lua. Finally, for all things Lua, this is the place to go. I'll assume you know a bit about scripting in Lua, as well as have at least done most of the Ogre 3d tutorials. My goal (apart from learn the ins and outs of luabind) was to take one of the Ogre tutorial programs, script the creation of the scene and then duplicate the output of the original program. The challenge was that I have no control over the Ogre 3d API. I could not change the API to suit binding. The CodeOgre 3dYou will need either the Ogre 3d SDK or the sources. Lua and luabindI've included the DLLs, libraries and header files in the ZIP for your convenience. However, you may what to do things differently. You can compile Lua and luabind in several ways. The easiest is to compile everything into one static library. I made a DLL for each, which is included in the project download. However, to compile against Lua 5.1, you have to adjust some things in Lua's configuration. In the file luaconf.h, there is a section on the #define LUA_COMPAT_GETN
luabind also complains about not finding two defines that ARE defined in the Lua headers, but adding them to the header just makes more errors. In the end, I just added to lua_include.hpp: #define LUA_NOREF (-2)
#define LUA_REFNIL (-1)
Make sure you use the same runtime for each if you have them as separate libraries and also as your program. Of course, this applies to all programs. I had The Lua State Manager ClassI created a small class to look after the Lua state. Just something to wrap initialization and closing. It also contains a cool function that checks if a Lua function exists. It's not mine; I think it came from the luabind site. bool LuaStateManager::FuncExist( lua_State* L, const char *name )
{
using namespace luabind;
object g = globals(L);
object func = g[name];
if( func )
{
if( type(func) == LUA_TFUNCTION )
return true;
}
return false;
}
You can use this before luabind has been started. Also, by looking to see if the function BindingWhen I started writing the code to do the binding of the Ogre classes I needed, I picked one I thought was the most useful and jumped in with both feet. This was the
object g = globals(L);
object vector = g["Vector3"];vector["ZERO"] = Vector3::ZERO;
So the solution turned out to be quite simple. We grab the global Lua table, which has everything in it, including the table that represents our binding.cpp
##include "stdafx.h" // Ogre, Lua and luabind headers from here too.
#include "Application.h"
// Prototype this before operator.hpp so it can be found for tostring() operator.
std::ostream& operator<<( std::ostream& stream, const Ogre::Entity ent );
#include "luabind/operator.hpp"
using namespace luabind; using namespace Ogre;
// Some helpful macros for defining constants (sort of) in Lua. Similar to this code:
// object g = globals(L);
// object table = g["class"];
// table["constant"] = class::constant;
#define LUA_CONST_START( class ) { object g = globals(L); object table = g[#class];
#define LUA_CONST( class, name ) table[#name] = class::name
#define LUA_CONST_END }
void bindVector3( lua_State* L )
{
module(L)
[
class_<Vector3>( "Vector3" )
.def_readwrite( "x", &Vector3::x )
.def_readwrite( "y", &Vector3::y )
.def_readwrite( "z", &Vector3::z )
.def(constructor<>())
.def(constructor<Vector3&>())
.def(constructor<Real, Real, Real>())
.def("absDotProduct", &Vector3::absDotProduct)
.def("crossProduct", &Vector3::crossProduct )
.def("directionEquals", &Vector3::directionEquals )
.def("distance", &Vector3::distance )
.def("dotProduct", &Vector3::dotProduct )
.def("getRotationTo", &Vector3::getRotationTo )
.def("isZeroLength", &Vector3::isZeroLength )
.def("length", &Vector3::length )
.def("makeCeil", &Vector3::makeCeil )
.def("makeFloor", &Vector3::makeFloor )
.def("midPoint", &Vector3::midPoint )
.def("normalise", &Vector3::normalise )
.def("nornaliseCopy", &Vector3::normalisedCopy )
.def("perpendicular", &Vector3::perpendicular )
.def("positionCloses", &Vector3::positionCloses )
.def("positionEquals", &Vector3::positionEquals )
//.def("ptr", &Vector3::ptr )
.def("randomDeviant", &Vector3::randomDeviant )
.def("reflect", &Vector3::reflect )
.def("squaredDistance", &Vector3::squaredDistance )
.def("squaredLength", &Vector3::squaredLength )
// Operators
.def( self + other<Vector3>() )
.def( self - other<Vector3>() )
.def( self * other<Vector3>() )
.def( self * Real() )
.def(tostring(self))
];
LUA_CONST_START( Vector3 )
LUA_CONST( Vector3, ZERO);
LUA_CONST( Vector3, UNIT_X);
LUA_CONST( Vector3, UNIT_Y);
LUA_CONST( Vector3, UNIT_Z);
LUA_CONST( Vector3, NEGATIVE_UNIT_X);
LUA_CONST( Vector3, NEGATIVE_UNIT_Y);
LUA_CONST( Vector3, NEGATIVE_UNIT_Z);
LUA_CONST( Vector3, UNIT_SCALE);
LUA_CONST_END;
}
Next it was binding.cpp Continuedvoid bindColourValue( lua_State* L )
{
module(L)
[
class_<ColourValue>("ColourValue")
.def(constructor<>())
.def(constructor<Real, Real, Real, Real>())
.def(constructor<Real, Real, Real>())
.def(tostring(self))
.def_readwrite( "r", &ColourValue::r)
.def_readwrite( "g", &ColourValue::g )
.def_readwrite( "b", &ColourValue::b )
.def_readwrite( "a", &ColourValue::a )
.def( "saturate", &ColourValue::saturate )
// Operators
.def( self + other<ColourValue>() )
.def( self - other<ColourValue>() )
.def( self * other<ColourValue>() )
.def( self * Real() )
.def( self / Real() )
];
LUA_CONST_START( ColourValue )
LUA_CONST( ColourValue, ZERO);
LUA_CONST( ColourValue, Black);
LUA_CONST( ColourValue, White);
LUA_CONST( ColourValue, Red);
LUA_CONST( ColourValue, Green);
LUA_CONST( ColourValue, Blue);
LUA_CONST_END;
}
Now it's time for an entity. Ogre's binding.cpp Continuedstd::ostream& operator<<( std::ostream& stream, const Entity ent )
{
return stream << ent.getName();
}
void bindEntity( lua_State* L ) // And Movable Object for now.
{
module(L)
[
class_<MovableObject>("MovableObject"),
class_<Entity, MovableObject>("Entity")
.def(tostring(self))
.def("setMaterialName", Entity::setMaterialName )
.def("setDisplaySkeleton", Entity::setDisplaySkeleton )
];
}
Scene nodes: I had some “fun” here. Two things: overloaded functions and optional arguments. They confuse luabind somewhat and you, too! You can handle the overloading with a nasty-looking cast. This lets the compiler know exactly which function you want. Optional arguments used with luabind aren't so known. luabind, via template wizardry, selects which function to run by matching prototypes. The entire prototype. So, you either have to use all those arguments that you never usually use or, as I did, write a proxy function. It's a pity, since Lua is very good at optional arguments, too. binding.cpp Continued/// Fake member function for simplifing binding, as the real functions
// have optional aguments, which I don't want to use in the Lua script.
// However luabind does not support optional arguments.
// Think of "obj" as "this"
SceneNode *createChildSceneNode( SceneNode *obj, const String name )
{
return obj->createChildSceneNode( name );
}
void SceneNode_yaw( SceneNode *obj, const Real degrees )
{
return obj->yaw( Degree( degrees ) );
}
void bindSceneNode( lua_State* L )
{
module(L)
[
class_<SceneNode>("SceneNode")
.def("createChildSceneNode", createChildSceneNode )
.def("attachObject", SceneNode::attachObject )
.def("yaw", SceneNode_yaw )
.def("setPosition",
(void( SceneNode::*)(const Vector3&))SceneNode::setPosition )
.def("setPosition",
(void( SceneNode::*)(Real,Real,Real))SceneNode::setPosition )
];
}
Binding applications. Sounds like some legal jargon. I was getting a bit tired by now and a bit slack. These miscellaneous functions in the binding.cpp Continued// Function to give lua access to the one and only applcation object.
Application& getApplication()
{
return application;
}
// TODO: Fix the case in the names of Application's members.
void bindApplication( lua_State* L )
{
module(L)
[
class_<Application>("Application")
.def( "setBackgroundColour", Application::SetBackgroundColour )
.def( "createEntity", Application::CreateEntity )
.def( "getRootNode", Application::GetRootNode )
.def( "getCamera", Application::GetCamera )
];
module(L)
[
def( "getApplication", getApplication )
];
}
Now for some fun with a camera and prototypes. Here I got the hang of writing function casts, so you can see overloaded functions bound. Depending on the arguments you use in Lua, you'll get one or the other. binding.cpp Continuedvoid bindCamera( lua_State* L )
{
module(L)
[
class_<Camera>("Camera")
.def("setPosition",
(void( Camera::*)(const Vector3&))Camera::setPosition )
.def("setPosition",
(void( Camera::*)(Real,Real,Real))Camera::setPosition )
.def("lookAt", (void( Camera::*)(const Vector3&))Camera::lookAt )
.def("lookAt", (void( Camera::*)(Real,Real,Real))Camera::lookAt )
.def("setNearClipDistance", Camera::setNearClipDistance )
.def("setFarClipDistance", Camera::setFarClipDistance )
];
}
Finishing up. binding.cpp Continued// Keep this at the bottom so we don't need prototypes for other bind functions.
void bindLua( lua_State* L )
{
bindVector3( L );
bindColourValue( L );
bindEntity( L );
bindSceneNode( L );
bindApplication( L );
bindCamera( L );
}
So the main things that tripped me up were:
And now for the script! script.lua-- Lua script for test program.
print( 'Hello from LUA!' )
app = getApplication()
app:setBackgroundColour( ColourValue( 0, 0, .25 ) )
camera = app:getCamera()
camera:setPosition( Vector3(0,100,500) )
camera:lookAt( Vector3(0,0,0) )
camera:setNearClipDistance( 5 )
e = app:createEntity( 'Ninja', 'ninja.mesh' )
-- I added this just to see what it did. ;-)
e:setDisplaySkeleton( true )
rootNode = app:getRootNode()
child = rootNode:createChildSceneNode( 'NinjaNode' )
child:attachObject( e )
child:yaw( 180 )
child:setPosition( Vector3.ZERO )
e = app:createEntity( 'floor', 'ground' )
e:setMaterialName( 'Examples/Rockwall' )
Points of Interest / ConclusionThings I learned:
References and Links[1] - Rasterbar luabind documentation [2] - Lua website [3] - Ogre 3d website History
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||