SmartBody : Appendix 4: Using the simplified interface to SmartBody

smartbody-dll provides a simplified interface to SmartBody in a dynamically linked library. This can be used when a C-style API is needed to interface with another component. This interface provides access to the state of the SmartBody scene, but does not provide low level access directly via C-functions. Instead, it provides a set of C-functions that call Python functions that can be used to access the internals of SmartBody.

To integrate SmartBody into a game engine using smartbody-dll, you will need to extend the SmartBodyListener class:

#include "smartbody-dll.h"
class MyListener : public SmartBodyListener
{
   virtual void OnCharacterCreate( const std::string& name, const std::string& objectClass );
   virtual void OnCharacterDelete( const std::string& name );
   virtual void OnCharacterChanged( 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 );
}

The OnCharacterCreate() function gets called whenever a SmartBody character is instantiated, where the object class is a string that can be used by a renderer to determine what mesh to use as deformable geometry, or other character-specific configuration that is handled by the renderer. The  OnCharacterDelete() function whenever a character is removed. The OnCharacterChanged() method is called whenever that character's skeleton is changed, such as by adding joints or other channels. The OnViseme() method is called every frame and sends the values of the visemes (for renderers that use blend shapes). The OnChannel() function is called every frame whenever non-joint information is send from SmartBody. The integration should create characters in the game engine in response to those callbacks, for example:

void MyListener::OnCharacterCreate( const std::string& name, const std::string& objectClass )
{
   if (name does not exist)
   {
      // create game engine character here
   }
}

To initialize SmartBody, instance the class Smartbody_dll,set the media path, and the initialize it:

sb = new Smartbody_dll();
sb->SetMediaPath("/path/to/my/data/");
sb->Init("/path/to/Python/", true);

Then call the Update() function by setting the time every simulation frame:

sb->Update(currenTimeInSeconds);

 

After the Update(), the character state can be retrieved and used the update the state of the game engine character as follows:

int num = sb->GetNumberOfCharacters()
for (size_t n = 0; n < num; n++)
{
   SmartBodyCharacter& character = sb->GetCharacter(n);
   int numJoints = character.m_joints.size();
   for (size_t j = 0; j < numJoints; j++)
   {
      SmartbodyJoint& joint = character.m_joints[j];
      // get the position from the joint
      float xpos = joint.x;
      float ypos = joint.y;
      float zpos = joint.z;
      // get the orientation from the joint
      float quatw = joint.rw
      float quatx = joint.rx;
      float quaty = joint.ry
      float quatz = joint.rz;
      // update the game engine character
      //  ...
      // ...
}

 

To access other functionality, any function allowed by the SmartBody Python API can be accessed using the following functions:

bool PythonCommandVoid( const std::string & command );
bool PythonCommandBool( const std::string & command );
int PythonCommandInt( const std::string & command );
float PythonCommandFloat( const std::string & command );
std::string PythonCommandString( const std::string & command );

For example, to run a Python command that has no return value:

PythonCommandVoid("scene.setSceneScale(1.0)");

 

Alternatively, if you want to retrieve data from a Python function, the Bool, Int, Float and String functions can be called, as long as the return value is then assigned to a Python variable called ret. For example:

int val = PythonCommandBool("ret = scene.getNumCharacters()");
std::string val = PythonCommandString("ret = scene.getMotion(\"LHandOnHip_RArm_GestureOffer\").getName()");

 

Note that the Python context persists throughout the simulation, so you can assign a Python variable during one call:

PythonCommandVoid("mycharacter = scene.getCharacter(\"utah\")");

 

and subsequently use it during a later call:

float xpos = PythonCommandFloat("mycharacter.getPosition().getData(0)");

 

At any time, SmartBody commands can be sent by using the ProcessVHMsgs() function, which sends a message to the ActiveMQ server, which is then picked up by SmartBody. Note that since this interface requires the VHMessage interface, it is necessary to have an ActiveMQ server running.The message should be of the following format:

ProcessVHMsgs( "sb", "python command goes here");

where 'command' is the Python command. For details on the Python interface, please consult Using Python with SmartBody and Appendix 2: Python API for SmartBody.

Note that game engines that require a C-style interface (for example, an engine that only uses C#, which can access C-style but not C++-style functions), as opposed to a C++-style interface can use the smartbody-c-dll.h interface instead of smartbody-dll.h, with similarly named functions:

 

C++ FunctionC FunctionComments
OnCharacterCreate
SBM_IsCharacterCreatedEquivalent of callback function. Must be queried every frame.
OnCharacterDelete
SBM_IsCharacterDeletedEquivalent of callback function. Must be queried every frame.
OnCharacterChanged
SBM_IsCharacterChangedEquivalent of callback function. Must be queried every frame.
OnViseme
SBM_IsVisemeSetEquivalent of callback function. Must be queried every frame.
OnChannel
SBM_IsChannelSetEquivalent of callback function. Must be queried every frame.
SetMediaPathSBM_SetMediaPathSets the media path
InitSBM_InitInitializes a SmartBody instance
UpdateSBM_UpdateUpdates the SmartBody simulation step
ProcessVHMsgsSBM_ProcessVHMsgsProcesses any pending Virtual Human messages