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:
- In your engine/simulation, create a character. Simultaneously, create a SmartBody character in the SmartBody context with the same name.
- Implement the SBSceneListener interface which handles character creation/deletion and modification of SmartBody characters, and perform the equivalent actions in your engine/simulation.
- Send SmartBody commands to control the character and change the scene every frame as needed.
- 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() {} };
Callback | Event |
---|---|
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)