Overview
This example shows how to use the articulated model agent. The application loads a geometry file, adds the geometry to the scene and articulates parts of the geometry. Of particular interest in this tutorial is the use of agents to load and manage the geometry.
Expected Result
Model Articulation Distributed Result
Example details
VR-Vantage uses agents to create and synchronize scene objects among all display engines in the configuration. The master creates and owns agents which causes scene objects to be created locally and simultainiously sends messages to the distributed systems to manage their own copies of the same scene object. Agents in VR-Vantage take the same name as the objects they manage with the added suffix 'Agent'. Because agents are used to send messages to distributed system (and not receive back replies), they are one-way communication objects. Their APIs expose the write-only interface of the objects they manage.
This application loads a geometry file using a DtArticulatedModelAgent (which manages an underlying DtArticulatedModel) and inserts the model in the scene using a DtSceneObjectAgent (which manages an underlying DtSceneObject). The DtSceneObjectAgent is used to position the object within the scene, and the DtArticulatedModelAgent is used to move the articulated parts of the model. To coordinate articulation of the model, a custom DtDriver is used to manage both the DtSceneObjectAgent and DtArticulatedModelAgent. DtDriver objects registered with the display engine have the benefit of being called when the simulation starts, when the simulation stops, and every simulation time-update at regular intervals.
The custom DtDriver in this application creates a new DtSceneObjectAgent and DtArticulationModelAgent when the simulation starts (in its 'onStart()' function), and destroys the objects when the simulation stops (in its 'onStop()' function). During the simulation time-updates, the custom DtDriver's onTick() function is called, where it updates the articulated parts of the model. Because the agents are considered to have a 'write-only' API, the custom DtDriver stores its own copy of the model-status used for calculating articulation updates. For this application, the only model-status data stored are current rotation values used to transform selected articulated parts.
Derived from a DtDriver is a custom driver used to load and manipulate the model. The custom driver stores the current part orientation in a map for use when calculating the next part-articulation update.
class MyArticulationDriver : public DtDriver
typedef std::map<int, DtTaitBryan> PartOrientationMap;
PartOrientationMap myPartMap;
The constructor for the custom DtDriver takes a display engine reference, with which it registers a geometry-file definition and then initializes the part-orientation map for specific part-ids in the model.
MyArticulationDriver(DtDe& de)
{
std::string sharedDataPath = de.dePathConfiguration().sharedDataPath();
DtModelDefinition md("theModel");
md.setParameter("filename", sharedDataPath + "/ModelData/Vehicles/Tracked/M1Abrams/M1Abrams_FBX.medf");
de.sharedState().addModelDefinition(md);
myPartMap[turretId] = DtTaitBryan(0.0f, 0.0f, 0.0f);
myPartMap[barrelId] = DtTaitBryan(0.0f, 0.0f, 0.0f);
myPartMap[gun1Id] = DtTaitBryan(0.0f, 0.0f, 0.0f);
myPartMap[gun2Id] = DtTaitBryan(0.0f, 0.0f, 0.0f);
}
When the custom DtDriver is notified that the simulation is starting it creates a new DtArticulationModelAgent and set it to the model-definition created at construction time.
virtual bool onStart()
{
myModel = DtArticulatedModelAgent::create(myAgentManager);
myModel->setModelDefinition("theModel");
Then the custom DtDriver creates a DtSceneObjectAgent and adds the model-agent to it.
mySceneObject = DtSceneObjectAgent::create(myAgentManager);
mySceneObject->addModel(myModel, 0);
return true;
}
During the running simulation, the custom DtDriver gets notified at regular intervals when it is time to update itself. It first gets the simulation time, then calculates the delta-time since the last update.
virtual bool onTick()
{
double simTime = myAgentManager.de().simulationTime();
double deltaTime = simTime - mySimTime;
mySimTime = simTime;
The custom DtDriver then uses the simulation times to update the articulated-parts of the model using private functions.
rotatePart(turretId, deltaTime);
rotatePart(gun1Id, deltaTime);
pitchPart(barrelId, simTime);
pitchPart(gun2Id, simTime);
return true;
}
The articulated parts that are rotated have their current rotation values incremented by some factor every simulation tick. The current rotation value is stored with the custom DtDriver instead of being retrieved from the DtArticulationModelAgent because the agent does not export getter functions.
void rotatePart(int partId, double deltaTime)
{
PartOrientationMap::iterator iter(myPartMap.find(partId));
DtTaitBryan& taitBryan(iter->second);
taitBryan.setPsi(taitBryan.psi() + (deltaTime * myRadsPerSecond));
myModel->setPartOrientation(partId, taitBryan, DtVector());
}
Similarly the articulated parts that are pitched (angled up/down) have their current rotation values set every simulation tick. Again, the current rotation value is stored with the custom DtDriver instead of being retrieved from the DtArticulationModelAgent because the agent does not export getter functions.
void pitchPart(int partId, double simTime)
{
PartOrientationMap::iterator iter(myPartMap.find(partId));
DtTaitBryan& taitBryan(iter->second);
taitBryan.setTheta(((sin(simTime) + 1.0f) * 0.5f) * myMaxAngle);
myModel->setPartOrientation(partId, taitBryan, DtVector());
}
Building the Example
VR-Vantage includes pre-built versions of the example application. To build it yourself, follow the instructions at Building VR-Vantage Examples, Applications, and Plug-ins.
Running the Example
This example is an application. You can run it by running ./bin64/exampleModelArticulationDistributed.exe (on Windows) or ./bin64/exampleModelArticulationDistributed (on Linux). For more information about running examples, please see Running Applications and Examples.
Learn More
Example Source Files
exampleModelArticulationDistributed.cxx
#include <string>
#include <cmath>
#include <osg/Math>
#include <matrix/vlTaitBryan.h>
#include <matrix/vlVector.h>
#include <vrvCore/DtArticulatedModelAgent.hpp>
#include <vrvCore/DtSceneObjectAgent.hpp>
#include <functional>
static int turretId(4096);
static int barrelId(4416);
static int gun1Id(6016);
static int gun2Id(5696);
using namespace makVrv;
class MyArticulationDriver :
public DtDriver
{
typedef std::map<int, DtTaitBryan> PartOrientationMap;
public:
MyArticulationDriver(
DtDe& de)
:
DtDriver(de.agentManager(),
"MyArticulationDriver")
, mySceneObject(0)
, myModel(0)
, mySimTime(0.0)
, myPartMap()
, myRadsPerSecond(osg::DegreesToRadians(15.0f))
, myMaxAngle(osg::DegreesToRadians(60.0f))
{
md.setParameter("filename", sharedDataPath + "/ModelData/Vehicles/Tracked/M1Abrams/M1Abrams_FBX.medf");
myPartMap[turretId] = DtTaitBryan(0.0f, 0.0f, 0.0f);
myPartMap[barrelId] = DtTaitBryan(0.0f, 0.0f, 0.0f);
myPartMap[gun1Id] = DtTaitBryan(0.0f, 0.0f, 0.0f);
myPartMap[gun2Id] = DtTaitBryan(0.0f, 0.0f, 0.0f);
}
virtual ~MyArticulationDriver()
{
if (mySceneObject && myModel)
mySceneObject->removeModel(myModel);
delete myModel;
myModel = 0;
delete mySceneObject;
mySceneObject = 0;
}
virtual const std::string& className() const
{
static const std::string name("MyArticulationDriver");
return name;
}
virtual bool onStart()
{
myModel = DtArticulatedModelAgent::create(myAgentManager);
myModel->setModelDefinition("theModel");
mySceneObject = DtSceneObjectAgent::create(myAgentManager);
mySceneObject->setPosition(0, DtVector(0.0f, 25.0f, 0.0f));
mySceneObject->setOrientation(0, DtTaitBryan(
osg::DegreesToRadians(-150.0f),
osg::DegreesToRadians(-30.0f),
osg::DegreesToRadians(-10.0f)));
mySceneObject->addModel(myModel, 0);
mySimTime = myAgentManager.de().simulationTime();
return true;
}
virtual bool onStop()
{
mySceneObject->removeModel(myModel);
delete myModel;
myModel = 0;
delete mySceneObject;
mySceneObject = 0;
return true;
}
virtual bool onTick()
{
double simTime = myAgentManager.de().simulationTime();
double deltaTime = simTime - mySimTime;
mySimTime = simTime;
rotatePart(turretId, deltaTime);
rotatePart(gun1Id, deltaTime);
pitchPart(barrelId, simTime);
pitchPart(gun2Id, simTime);
return true;
}
private:
void pitchPart(int partId, double simTime)
{
PartOrientationMap::iterator iter(myPartMap.find(partId));
if (iter == myPartMap.end())
return;
DtTaitBryan& taitBryan(iter->second);
taitBryan.setTheta(((sin(simTime) + 1.0f) * 0.5f) * myMaxAngle);
myModel->setPartOrientation(partId, taitBryan, DtVector());
}
void rotatePart(int partId, double deltaTime)
{
PartOrientationMap::iterator iter(myPartMap.find(partId));
if (iter == myPartMap.end())
return;
DtTaitBryan& taitBryan(iter->second);
taitBryan.setPsi(taitBryan.psi() + (deltaTime * myRadsPerSecond));
myModel->setPartOrientation(partId, taitBryan, DtVector());
}
private:
DtSceneObjectAgent* mySceneObject;
DtArticulatedModelAgent* myModel;
double mySimTime;
PartOrientationMap myPartMap;
const double myRadsPerSecond;
const double myMaxAngle;
};
{
}
int main(int argc, char** argv)
{
namespace ph = std::placeholders;
MyArticulationDriver* driver =
new MyArticulationDriver(igApp.
de());
return 0;
}
[<< Examples] [Home] [Top of Page]