In this topic I would like to explain how to set up a very simple application and use Squirrel for scripting (instead of Lua for example )
Squirrel is very nice high level imperative/OO programming language with syntax similar to C/C++ developed by Alberto Demichelis. I'm going to use SQPlus, developed by John Schultz, to bind Squirrel to C++.
This code example has been inspired by the LuaBind example in wiki page.
What do you need?
Note that I'm using Microsoft Visual Studio .NET 2003 (7.1).
You need Ogre (SDK or source) Squirrel and SQPlus of course . SQPlus contains the last stable version of Squirrel. So, download it and compile.
Create the Project
I used the wizard to create a simple Standard Ogre Application. I called it SquirrelOgre.
Let's Include some stuff
Note that this simple example is made in debug mode. So in C++ general (Project properties) add:
- path/to/Squirrel/sqplus
- path/to/Squirrel/include
In Linker general add:
- path/to/Squirrel/lib
In Linker dependencies add:
- squirrelD.lib
- sqstdlibD.lib
- sqplusD.lib
Let's code
This is main.cpp. We do not change this file, I'll do everything in the header file. This is not a clear style code programming lesson
Code: Select all
#include "SquirrelOgre.h"
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char *argv[])
#endif
{
// Create application object
SquirrelOgreApp app;
SET_TERM_HANDLER;
try {
app.go();
} catch( Ogre::Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
std::cerr << "An exception has occured: " <<
e.getFullDescription().c_str() << std::endl;
#endif
}
return 0;
}
#ifdef __cplusplus
}
#endif
Code: Select all
#ifndef __SquirrelOgre_h_
#define __SquirrelOgre_h_
#include "sqplus.h" // Include the SQPlus header
#include "ExampleApplication.h"
using namespace SqPlus; // SqPlus namespace
Code: Select all
// ====================================================================
// Define a custom constructor for Vector 3
// ====================================================================
int constructVector3(int x, int y, int z, HSQUIRRELVM v) {
return PostConstruct<Vector3>(v, new Vector3(x, y, z), ReleaseClassPtr<Vector3>::release);
}
SQ_DECLARE_RELEASE(Vector3) // This is required when using a custom constructor
Code: Select all
// ====================================================================
// Actor class this is a cut & paste from LuaBind wiki tutorial ;)
// ====================================================================
class Actor
{
public:
Actor() {}
Actor( std::string sName, SceneManager *pSceneMgr, Vector3 v3Position, Vector3 v3Scale )
{
mName = sName;
mSceneMgr = pSceneMgr;
std::stringstream ss;
ss << mName << "_ControlNode";
mControlNode = pSceneMgr->getRootSceneNode()->createChildSceneNode( ss.str(), v3Position );
ss.flush();
ss << mName << "_Entity";
mEntity = pSceneMgr->createEntity( ss.str(), "ogrehead.mesh" );
mControlNode->attachObject( mEntity );
mControlNode->setScale( v3Scale );
}
~Actor(){}
void setMaterialName( const SQChar* sMaterialName )
{
mEntity->setMaterialName( sMaterialName );
}
void setScale( Vector3* v3Scale )
{
mControlNode->setScale( *v3Scale );
}
void setPosition( Vector3* v3Position )
{
mControlNode->setPosition( *v3Position );
}
void setMesh( const SQChar* sMeshResourceName )
{
if( mEntity )
{
mControlNode->detachObject( mEntity->getName() );
mSceneMgr->removeEntity( mEntity );
mEntity = 0;
}
mEntity = mSceneMgr->createEntity( mName, sMeshResourceName );
mControlNode->attachObject( mEntity );
}
protected:
std::string mName;
SceneNode *mControlNode;
Entity *mEntity;
SceneManager *mSceneMgr;
};
// Declare both Actor and Vector3 class
DECLARE_INSTANCE_TYPE(Actor)
DECLARE_INSTANCE_TYPE(Vector3)
Code: Select all
virtual void createScene(void)
{
SquirrelVM::Init(); // Start the virtual machine
SquirrelObject testSquirrel = SquirrelVM::CompileScript("Test.nut"); // Compile the script
SquirrelVM::RunScript(testSquirrel); // Run the script
// Bind the Vector3 class
SQClassDef<Vector3>(_T("Vector3")).
staticFunc(constructVector3,_T("constructor")). // here's our custom cunstructor
var(&Vector3::x,_T("x")).
var(&Vector3::y,_T("y")).
var(&Vector3::z,_T("z"));
// Bind the Actor class
SQClassDef<Actor>(_T("Actor")).
func(&Actor::setMaterialName,_T("setMaterialName")).
func(&Actor::setScale,_T("setScale")).
func(&Actor::setPosition,_T("setPosition")).
func(&Actor::setMesh,_T("setMesh"));
// Create an Actor object
Actor *pActor = new Actor( "TestActor", mSceneMgr, Vector3( 0, 0, 0 ), Vector3( 1, 1, 1 ) );
// Call initActor function and pass object by reference
SquirrelFunction<void>(_T("initActor"))(pActor);
// Shutdown the VM when finish
SquirrelVM::Shutdown();
// Delete Actor object
delete pActor;
// Set ambient light
mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));
// Create a light
Light* l = mSceneMgr->createLight("MainLight");
l->setPosition(20,80,50);
}
Finally we create an Actor object and the initActor (define in Test.nut) function is called. Note that pActor is passed by reference.
Finally let's see Test.nut
Code: Select all
// ==========================================================
// Test.nut
// ==========================================================
function initActor( pActor ) {
pActor.setMesh("robot.mesh");
pActor.setScale( Vector3(3, 3, 3) );
pActor.setPosition( Vector3( 0, 0, 0 ) );
pActor.setMaterialName( "Examples/BumpyMetal" );
}