SmartBody : Integrating SmartBody With a Game Engine

SmartBody can be integrated with any game engine or simulation that uses a C++ or C interface, or by those that utilize a Python interface. Additionally, SmartBody can connect to a game engine using the BoneBus interface, which is a network-based API, which would require communication over TCP/IP and UDP.

Suggested Integration Techniques

The main C++ API allows you to have access to most of SmartBody's internal structures, and are generally located in the /sb/ directory. There is also a simplified interface that can be used with C or C++ (located in smartbody-dll/include). In addition, there is a Python API which is nearly identical to the C++ API (generated from Boost-Python) which can be used to control the entire simulation, to set up the scene, or to run snippets of code during runtime.

The following pages describe the C++, C, Python and BoneBus interfaces.

One strategy for integrating SmartBody into a game or simulation is to run chave SmartBody indicate when the character's are created and to response to those events. Then, the run the SmartBody ento create a proxy character in your own engine or simulation based on the function of the SmartBody engine and to have that character's state be controlled by a corresponding SmartBody character:

    1. In your engine/simulation, create a character. Simultaneously, create a SmartBody character in the SmartBody context with the same name.
    2. Implement the SBSceneListener interface which handles character creation/deletion and modification of SmartBody characters, and perform the equivalent actions in your engine/simulation.
    3. Send SmartBody commands to control the character and change the scene every frame as needed.
    4. Query SmartBody every frame to obtain the character state, and change the engine/simulation character's state to match it

The following is a snippet from the SBSceneListener C++ interface:

class SBSceneListener
{
public:
    SBSceneListener() {}
    virtual ~SBSceneListener() {}
    virtual void OnCharacterCreate( const std::string & name, const std::string & objectClass ) {}
    virtual void OnCharacterDelete( const std::string & name ) {}
    virtual void OnCharacterUpdate( const std::string & name ) {}
    virtual void OnPawnCreate( const std::string & name ) {}
    virtual void OnPawnDelete( const std::string & name ) {}
    
    virtual void OnViseme( const std::string & name, const std::string & visemeName, const float weight, const float blendTime ) {}
    virtual void OnChannel( const std::string & name, const std::string & channelName, const float value) {}
    virtual void OnLogMessage( const std::string & message) {}
    virtual void OnEvent( const std::string & eventName, const std::string & eventParameters ) {}
    virtual void OnObjectCreate(SmartBody::SBObject* object) {}
    virtual void OnObjectDelete(SmartBody::SBObject* object) {}
    
    virtual void OnSimulationStart() {}
    virtual void OnSimulationEnd() {}
    virtual void OnSimulationUpdate() {}
};
CallbackEvent
OnCharacterCreate
Character is created
OnCharacterDelete
Character is deleted
OnCharacterUpdate
Character's skeleton, joints or other channels have changed
OnPawnCreate
Pawn is created
OnPawnDelete
Pawn is deleted
OnViseme
Face shape is activated with a particular value
OnChannel
Channel is producing a particular value
OnLogMessage
Message from SmartBody
OnEvent
An event is triggered from SmartBody
OnObjectCreate
Object is created in SmartBody
OnObjectDelete
Object is deleted in SmartBody
OnSimulationStart
Simulation starts
OnSimulationEnd
Simulation ends
OnSimulationUpdate
Simulation steps forward in time

 

 

 

 

Thus, an example would be to create a subclass of the listener:

class MyListener : public SmartBody::SBSceneListener
{
	
	MyListener ();
    ~MyListener ();

	// triggered when a SmartBody character is created
    void OnCharacterCreate( const std::string & name, const std::string & objectClass );

	// triggered when a SmartBody character is deleted
    void OnCharacterDelete( const std::string & name );

	// triggered when a SmartBody character changes the skeleton (add/remove joints, channels)
    void OnCharacterUpdate( const std::string & name );

	// triggered when an object (character or pawn) is created
    void OnPawnCreate( const std::string & name );

	// triggered when an object (character or pawn) is deleted
    void OnPawnDelete( const std::string & name );

	// triggered when a SmartBody event occurs
    void OnEvent( const std::string & eventName, const std::string & eventParameters );
};

...
...

// create an instance of your own listener
MyListener* myListener = new MyListener();
// get the SmartBody scene
SmartBody::SBScene* scene = SmartBody::SBScene::getScene();
// add the listener to the scene
scene->addSceneListener(myListener);
// the callbacks will now be made automatically by SmartBody as those events occur

To run the SmartBody engine, then update the simulation with the current time, then read back the state of each character

// for each simulation step where currentTime = time in seconds
scene->update(currentTime);

// get all the characters
const std::vector<std::string>& characterNames = scene->getCharacterNames();
for (size_t c = 0; c < characterNames.size(); c++)
{
	SmartBody::SBCharacter* character = scene->getCharacter(characterNames[c]);
	// get the skeleton
	SmartBody::SBSkeleton* sbSkel = character->getSkeleton();
    int numJoints = sbSkel->getNumJoints();
	// for each joint, get the position/orientation
    for (int j = 0; j < numJoints; j++)
    {
    	SmartBody::SBJoint* joint = sbSkel->getJoint(j);
		SrQuat orientation = joint->quat()->value();
		SrVec position = joint->getPosition();
		// copy the joint information onto the equivalent game engine character
		...
		...
	}
}

A character or object can be created in SmartBody by either using the C++ interface, as in the following example:

// creates a character with a skeleton that has a single joint
SmartBody::SBCharacter* character = scene->createCharacter("thename", "thetype");
// creates a skeleton from an existing asset
SmartBody::SBSkeleton* skeleton = scene->createSkeleton("mycharacter.dae");
// attaches the skeleton to the character
character->setSkeleton(skeleton);
// creates a standard set of controllers such as animation, gazing, head movements, etc.
character->createStandardControllers();

or by sending Python commands to the engine through C++ by loading the contents of a file, or send the commands directly:

// runs all Python commands from a file
scene->runScript("myscript.py");
// runs a Python command directly
scene->run("scene.createCharacter(\"mycharacter\", \"mytype\")");

A character's skeleton can also be constructed procedurally and dynamically (as opposed loading the asset first, then defining the skeleton from that asset) as follows:

SmartBody::SBSkeleton* dynamicSkeleton = scene->addSkeletonDefinition("myskeleton");
SmartBody::SBJoint* baseJoint = skeleton->createJoint("base", NULL);
SmartBody::SBJoint* joint1 = skeleton->createJoint("child", baseJoint);
SmartBody::SBJoint* joint2 = skeleton->createJoint("grandchild", joint1);

...
// then later, create the character and a new instance of this skeleton
SmartBody::SBSkeleton* skeleton = scene->createSkeleton("myskeleton");
character->setSkeleton(skeleton);

 

 

For pure-Python interfaces, in general, there are Python functions that correspond directly to the C++ functions of the same name.

For example, a Python subclass of the SBSceneListener can be made like this:

class MyListener(CharacterListener):

    def OnCharacterCreate(self, name, type):

        print "Character created..."

        

    def OnPawnCreate(self, name):

        print "Pawn created..."



mylistener = MyListener()

scene.addSceneListener(mylistener)