Squirrel Scripting Language

A place for users of OGRE to discuss ideas and experiences of utilitising OGRE in their games / demos / applications.
User avatar
morez
Kobold
Posts: 36
Joined: Mon Nov 14, 2005 11:40 am
Location: Verona, Italy

Squirrel Scripting Language

Post by morez »

Hi guys,
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 :D )
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
Ok so let's see how to change our header file SquirrelOgre.h

Code: Select all

#ifndef __SquirrelOgre_h_
#define __SquirrelOgre_h_

#include "sqplus.h" // Include the SQPlus header

#include "ExampleApplication.h"

using namespace SqPlus; // SqPlus namespace
Simple and clear.

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
Here we have declared a custom constructor for the Ogre::Vector3 class.

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)
Here we have declared the Actor class and called the macro DECLARE_INSTANCE_TYPE for both Actor and Ogre::Vector3 classes. Note that the Actor class methods parameters are passed by reference in setScale and setPosition methods and I've used const SQChar* instead of std::string in setMaterialName and setMesh.

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);
	}
I've put everything in the createScene method. First of all we create the Virtual Machine. Than we compile and run the script "Test.nut". Test.nut file is in the same directory of the exe file. After that we bind the classes Actor and Vector3. Note how the constructor is bind.
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" );
}
Ok that's all. I hope it could be useful for someone. :wink:
nfz
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 1263
Joined: Wed Sep 24, 2003 4:00 pm
Location: Halifax, Nova Scotia, Canada

Post by nfz »

why not put this in the Ogre Wiki?
User avatar
Chris Jones
Lich
Posts: 1742
Joined: Tue Apr 05, 2005 1:11 pm
Location: Gosport, South England
x 1

Post by Chris Jones »

wiki it! itll get lost in the forums otherwise :(
User avatar
Feral
Halfling
Posts: 80
Joined: Thu May 26, 2005 9:12 pm
Location: NORTHERN California, USA

Post by Feral »

As per request this now lives in the wiki at http://www.ogre3d.org/wiki/index.php/Sq ... g_Language

Thank you morez (=
Hell656
Gnoblar
Posts: 21
Joined: Thu Nov 17, 2005 6:03 pm

Post by Hell656 »

Here Lua is compared to Squirrel.
User avatar
morez
Kobold
Posts: 36
Joined: Mon Nov 14, 2005 11:40 am
Location: Verona, Italy

Post by morez »

Very nice, thanks :)
Tyn
Gremlin
Posts: 195
Joined: Mon Jul 19, 2004 11:40 pm
Location: England
Contact:

Post by Tyn »

Thanks for posting this, I'd not seen Squirrel before. I've been playing with it over the last few days and found it pretty easy to work with. I really like it. If all keeps going this well then I think I'll look at using it with my project. Inheriting classes in Squirrel that are defined in C++ seems something that is going to make any many things possible, really interesting.

A couple of additions which I've really found useful is to add error handling to this, through the Ogre log manager. It's in the demos the provide, but they aren't documented particuarly well so I thought I'd help make Ogre users lives easier. Squirrel allows you to define a custom function in your code to handle print messages, both from the script and from any errors that Squirrel wants to send.

Firstly, you need to add this header to the top of the __SquirrelOgre_h_ header:

Code: Select all

#include <stdarg.h>
This header allows you to do the magic required to make a function behave like printf, that is taking many arguements relating to the prototype you also send ( something like "Days %d: Name: %s" with additional integer and string arguements).

Add this to the same class as the createScene function ( SquirrelOgre I think, looking at the header ).

Code: Select all

		static void printFunc(HSQUIRRELVM v,const SQChar * s,...) 
		{

			std::string tempStr = "";

			static SQChar temp[2048];
			va_list vl;
			va_start(vl,s);
			scvsprintf( temp,s,vl);
			va_end(vl);

			Ogre::LogManager::getSingletonPtr()->getLog("squirrel.log")->logMessage(temp);
		}
Then, in createScene() after the SquirrelVM::Init() line, add this:

Code: Select all

Ogre::LogManager::getSingletonPtr()->createLog("squirrel.log");
sq_setprintfunc(SquirrelVM::GetVMPtr(), SquirrelOgre::printFunc);
This will now rerout any print calls or compiler error messages. This only works for errors upon compiling, you need to redirect the errors that throw exceptions, basically the line RunScript line. If we change this to:

Code: Select all

		try
		{
			SquirrelVM::RunScript(testSquirrel);

		}

		catch(SquirrelError & e)
		{
			sq_getprintfunc(SquirrelVM::GetVMPtr())
				(SquirrelVM::GetVMPtr(),_T("Error: %s, %s\n"),e.desc);
		}
This will now send any exceptions that are thrown during the running of the script to the log file for further examination Using ::print() in the script language will also print through this function to the Ogre log, meaning you can keep track of where you are in a script file or any other information you can give that might be important to debugging.
"A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools."
Douglas Adams

http://www.spacecolonization.net
2080: Space Colonization
User avatar
morez
Kobold
Posts: 36
Joined: Mon Nov 14, 2005 11:40 am
Location: Verona, Italy

Post by morez »

That's very nice and useful :)
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Post by jacmoe »

Only if the above is added to the wiki article.
Otherwise it will cease being nice and useful a lot sooner that you think :wink:
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Post by jacmoe »

In tough competion with Angelscript and GameMonkey Script, Squirrel won!
A lot of it was due to the powerfulnes of sqplus. And the fact that the Ogre Wiki had a nice Squirrel example. :)´

I really like the binding syntax, and I am looking forward to using it. :D

@Tyn:
I know you have been messing around with Squirrel/SqPlus - do you mind share some of your code/experiences ? :)

I will probably update the Wiki article as I go. :wink:
Tyn
Gremlin
Posts: 195
Joined: Mon Jul 19, 2004 11:40 pm
Location: England
Contact:

Post by Tyn »

I really like it. I'm using it to create GUI sheets in CEGUI and reading from a SQLite database and code I'd written in C++ pretty much directly translated to Squirrel without any problems. It's great to work with, when you get logging in there it makes life so much easier. Binding classes in C++ is so easy, very quick to work with once you figure out what you are doing. The examples given pretty much cover all the eventualities I've thought of so far.

My only real problem I've come across is when you have created a class that's type is binded to Squirrel in C++ and then attempt to pass it to a Squirrel function, although I'm pretty sure there's a custom way of popping this externally created object to the scripts stack. Hasn't really been that much of a problem.

The wrapping I have done for CEGUI in Squirrel isn't that good, I've done it quickly since I'm more looking at other areas but I think I'll probably look into CEGUI's script plugins and create one for Squirrel so that it's wrapped properly.

I'm going to be releasing the program at some time this month and will be releasing the code at the same time, along with the script files that are used. I'll be sure to let you guys know when the code is available.
"A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools."
Douglas Adams

http://www.spacecolonization.net
2080: Space Colonization
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Post by jacmoe »

Tyn wrote:I really like it. I'm using it to create GUI sheets in CEGUI and reading from a SQLite database and code I'd written in C++ pretty much directly translated to Squirrel without any problems. It's great to work with, when you get logging in there it makes life so much easier. Binding classes in C++ is so easy, very quick to work with once you figure out what you are doing. The examples given pretty much cover all the eventualities I've thought of so far.
Thanks! This is exactly what I wanted to hear! :)

Now I can start digging into it.
Knowing that I am not going to hit a wall.
The binding work is so much more enjoyable than lua::bind or even Angelscript.

I think Ogre likes Squirrels..! :P
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Post by jacmoe »

Tyn wrote:The wrapping I have done for CEGUI in Squirrel isn't that good, I've done it quickly since I'm more looking at other areas but I think I'll probably look into CEGUI's script plugins and create one for Squirrel so that it's wrapped properly.
This would be so very cool if you decide to do that!
It is no fun to fire up Lua just to talk to CEGUI in script, when your app is already Squirrel'd. :)
User avatar
morez
Kobold
Posts: 36
Joined: Mon Nov 14, 2005 11:40 am
Location: Verona, Italy

Post by morez »

In tough competion with Angelscript and GameMonkey Script, Squirrel won!
A lot of it was due to the powerfulnes of sqplus. And the fact that the Ogre Wiki had a nice Squirrel example. Smile´

I really like the binding syntax, and I am looking forward to using it.
Yeah, that's one of the reasons why I started with Squirrel. Beyond this Squirrel is born with OOP in mind. Hope to see your work soon Tyn, good work! :)
Tyn
Gremlin
Posts: 195
Joined: Mon Jul 19, 2004 11:40 pm
Location: England
Contact:

Post by Tyn »

I'll definatly have a poke around the Lua script library and see how it all works and more importantly how much work it would be, but it is something I'd like to use myself.
"A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools."
Douglas Adams

http://www.spacecolonization.net
2080: Space Colonization
Tyn
Gremlin
Posts: 195
Joined: Mon Jul 19, 2004 11:40 pm
Location: England
Contact:

Post by Tyn »

I took a look at CEGUILua and it seems simple enough. Functions to run scripts either directly or through the resource system and a couple for binding/unbinding needed CEGUI classes.

It uses toLua++ to bind all of CEGUI's classes, whereas it looks as though I'll have to write a bunch of wrapping classes over the top of existing CEGUI ones for Squirrel, it doesn't like strings for one thing. So that'd mean getting all functionality would probably take some time. Can't figure out a decent way of using Singletons either so it'll probably be a bunch of functions at the CEGUI "namespace" that wraps them also. It'll probably be a week or so before I can start anyway as I want to finish what I'm doing so I've got some time to think about it.
"A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools."
Douglas Adams

http://www.spacecolonization.net
2080: Space Colonization
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Post by jacmoe »

Maybe this will help you out:
Rincewind wrote:Ok, here's a first attempt to allow the usage of std::string in SQplus.

I'm not quite sure about how to handle unicode characters. Also, I'm not quite sure about the lifetime of the c_str() pointer in the Push-function.
My first tests show it works though.

Comments welcome.

Rincewind

Code: Select all

inline bool    Match(TypeWrapper,HSQUIRRELVM v,int idx) {
        return sq_gettype(v,idx) == OT_STRING;
    }
    inline ::String Get(TypeWrapper,HSQUIRRELVM v,int idx)
    { 
        const SQChar * s;
        SQPLUS_CHECK_GET(sq_getstring(v,idx,&s)); 
        return std::string(s); 
    }

    inline void Push(HSQUIRRELVM v,const std::string& value) { sq_pushstring(v,value.c_str(),-1); }
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Post by jacmoe »

Hot tip: Change all squirrel projects to use /MDd and /MD - it will make the error about conflicting libraries go away. :wink:
josericardo_jr
Halfling
Posts: 94
Joined: Mon Aug 01, 2005 3:42 pm

Post by josericardo_jr »

Hi,

How I could configure Squirrel to use Ogre::Vector3 class constructor default? In this example, you make a custom constructor and passed it to squirel. I don't want to do it. I want to use the default constructor.

Thanks a lot.

Ricardo
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Post by jacmoe »

By all means: do it - and see what happens! :)
josericardo_jr
Halfling
Posts: 94
Joined: Mon Aug 01, 2005 3:42 pm

Post by josericardo_jr »

Hi Jacmoe,

I tried but I receive an error when I execute the script tha uses Ogre::Vector. It's like a point error.

Ricardo
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Post by haffax »

You should really learn how to write proper problem descriptions. :?
team-pantheon programmer
creators of Rastullahs Lockenpracht
josericardo_jr
Halfling
Posts: 94
Joined: Mon Aug 01, 2005 3:42 pm

Post by josericardo_jr »

Sorry!

It's my base class:

Code: Select all

class objTeste
{
	Ogre::Vector3 position;
	Ogre::Entity *e;
	Ogre::SceneNode *snode;

public:
	objTeste(){}
	objTeste( Ogre::SceneManager *sm, Ogre::String mesh )
	{
		e = sm -> createEntity( mesh + "_TestMesh", mesh );
		snode = sm -> getRootSceneNode() -> createChildSceneNode( mesh + "_NodeTeste" );
		snode -> attachObject( e );
	}

	void setPosition( float x, float y, float z )
	{
		Ogre::Vector3 vt( x, y, z );
		snode -> setPosition( vt );
	}
};

DECLARE_INSTANCE_TYPE( objTeste )
DECLARE_INSTANCE_TYPE( Ogre::Vector3 )

Here, I setup Squirrel and my object and use setPosition function:

Code: Select all

	SquirrelVM::Init();
	SquirrelObject testSquirrel = SquirrelVM::CompileScript( "Teste.nut" );
	SquirrelVM::RunScript( testSquirrel );

	SqPlus::SQClassDef<Ogre::Vector3>(_T("Ogre::Vector3") );

	SqPlus::SQClassDef<objTeste>(_T("objTeste")).
		func(&objTeste::setPosition, _T( "setPosition" ) );


	mTeste = new objTeste( mSceneMgr, "ellipsoid.mesh" );	

	SqPlus::SquirrelFunction<void>(_T("initObject"))(mTeste);
	SquirrelVM::Shutdown();
And Here is my Teste.nut

Code: Select all

function initObject( mTeste )
{
	mTeste.setPosition( 10, 20, 30 );
}
When my program execute the SquirrelFunction above, I have this error:

Unhandled exception at 0x7969a4e1 in GeomSnake.exe: Microsoft C++ exception: SquirrelError @ 0x0012f510.


Does anyone knows what this means?

Thanks a lot.

Ricardo
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Post by jacmoe »

Try implementing the printFunc as described by Tyn some posts above - then you can catch and learn like this:

Code: Select all

      try 
      { 
         SquirrelVM::RunScript(testSquirrel); 

      } 

      catch(SquirrelError & e) 
      { 
         sq_getprintfunc(SquirrelVM::GetVMPtr()) 
            (SquirrelVM::GetVMPtr(),_T("Error: %s, %s\n"),e.desc); 
      }
You should catch exceptions and handle them - then you won't get unhandled exceptions.
josericardo_jr
Halfling
Posts: 94
Joined: Mon Aug 01, 2005 3:42 pm

Post by josericardo_jr »

Hi Jacmoe,

Thanks for your help. I do it and my erros whas that:

14:49:16:
AN ERROR HAS OCCURED [Incorrect function argument]

14:49:16:
CALLSTACK

14:49:16: *FUNCTION [initObject()] Teste.nut line [3]

14:49:16:
LOCALS

14:49:16: [mTeste] INSTANCE

14:49:16: [this] TABLE

14:49:19: Error: _^‹Ý]‹MU‹ë
Post Reply