Using an Extended FOM in VR-Vantage
Simulation developers often extend FOMs to add attributes that are not part of the base FOM. They then want to be able to visualize the new attributes in VR-Vantage. This example shows how to create a VR-Vantage plug-in that listens for the new attributes in an extended FOM. It also shows how to configure VR-Vantage to display the new attribute.
Requirements and Limitations
-
The example uses an extended version of RPR FOM 0.8 that is available as the addAttr example in VR-Link. You must modify and build the example. Therefore, you must have a VR-Link developers license to implement this example.
-
This example only works with HLA 1.3. Adding support for other versions of HLA would not be difficult.
-
The FOM mapper accessory does not check to verify that the correct FED file or RPR FOM version is used.
Modifying the addAttr Example
You must modify the the addAttr example in VR-Link 3.13.2 to work properly with VR-Vantage. The addAttr example sets the position of the published entity to 0,0,0, which VR-Vantage cannot discover. Therefore, you must change the example to set the position to some other value. The changes also set the Marking Text, which will allow the entity to show up in the Object List panel.
To modify addAttr for this example, in addAttr.cxx, add the following lines:
esr->setLocation(DtVector(1,0,0));
esr->setMarkingText("Entity with mass");
after the line:
Rebuild addAttr. Please see the VR-Link documentation for instructions about how to build and run their examples.
The exampleFomMapper Plug-in:
The FOM Mapper example is a plug-in that lets VR-Vantage listen to an extended FOM and visualize its new attributes. The plug-in extends several of VR-Vantage's systems.
The simulation connection (VR-Link) drivers (for DIS and HLA protocols) have two subsystems - a visualizer system and a listener system. The listener system consists of listener objects that listen to network traffic and report on entities and interactions. The visualizer system connects to listeners. It creates and controls distributed objects that display models and effects in the scene. Each object or interaction is visualized using state visualizers, each of which is responsible for visualizing a particular effect or component. For example, one visualizer creates and updates the main model of a tank, while another visualizer creates and updates fire effects based on the tank's flame state.
The addAttr example adds an attribute called Mass to the FOM. For VR-Vantage to visualize this attribute, it must listen for it and have a visualizer to affect the scene. This example replaces the default entity state listener with a modified entity state listener that contains the additional attributes defined by the FOM. This example also adds a new state visualizer class that listens for the new mass attribute and displays the value as a 2D text label that is attached to the object.
This example has two visualizer classes, a protocol-independent visualizer - MassLabelVisualizer, and a protocol-dependent visualizer - VrlinkMassLabelVisualizer. MassLabelVisualizer is necessary for configuring model definitions in the GUI. It can also be extended to work with any simulation protocol. The protocol specific subclass is used only with the appropriate protocol specific driver. For this example, it is extended to work with HLA 1.3.
Because VR-Vantage has many drivers and they may execute in separate threads, it uses a class called an accessory to extend or modify particular drivers. The exampleFomMapper plug-in has an accessory class which will only work with HLA 1.3 drivers and is responsible for registering the new listener and visualizer classes with the driver's HLA 1.3 DtVrlinkConnection object. This allows the connection object to create the newly registered listener rather than the original listener class and also the protocol-dependent visualizer class rather than the protocol-independent version.
- Note
- This example is more complex than some of the other VR-Vantage examples, because it does several things: it is an example of a plug-in; it shows how to write a visualizer and a listener; it shows how to create an accessory to extend a driver. Because it does so many things, we do not walk you through the code for each action. Please see the comments in the source files for details as well as the other examples that demonstrate in detail each of these development tasks.
To build the exampleFomMapper plug-in, follow the instructions in VR-Vantage Developers Guide.
Using the Plug-in
To use the exampleFomMapper plug-in:
- Create a Separate Directory for Application Data
- Start a VR-Vantage Application that Uses the Plug-in and New Application Data
- Create a Model Definition
- Configure Entities
- Create a Connection using the Extended FOM Mapper
- Connect to the Network and run the addAttr Example
Create a Separate Directory for Application Data
To use the exampleFomMapper plug-in, we recommend that you create a separate appData directory so that the changes you make will not affect the default VR-Vantage configuration.
- Note
- The following procedure creates a configuration that is identical to the factory settings. If you want to start with your current configuration and settings, rather than the factory settings, copy the same set of directories from ./appData.
All paths are relative to the VR-Vantage install directory:
- Create a new directory ./exampleFomMapperAppData.
- Copy ./factory/definitions to ./exampleFomMapperAppData.
- Copy ./factory/drivers to ./exampleFomMapperAppData.
- Copy ./factory/gdal_data to ./exampleFomMapperAppData.
- Copy ./factory/geotiff to ./exampleFomMapperAppData.
- Copy ./factory/importConfig to ./exampleFomMapperAppData.
- Copy ./factory/proj_lib to ./exampleFomMapperAppData.
- Copy ./factory/schema to ./exampleFomMapperAppData.
- Copy ./factory/settings to ./exampleFomMapperAppData.
Start a VR-Vantage Application that Uses the Plug-in and New Application Data
The startup command must specify the plug-in and the new appData directory, as follows (using VR-Vantage Stealth):
vrvStealth –appDataDir ../exampleFomMapperAppData –plugin ../examples/plugins/release/exampleFomMapperPlugin.dll
Create a Model Definition
The MassLabelVisualizer uses a DtLabelWidgetAgent which can have a model definition for defining the appearance of the widget. Widgets use model definitions with the schema 'WidgetTheme'.
-
Select the Model Definition Editor page.
-
On the Schemas toolbar, click the Filter icon and select WidgetTheme.
-
Click the Add Model Definition icon. The Add Model Definition dialog box opens.
-
Type the name of the definition, in this example MassInfoWidget. MassInfoWidget is added to the list of widgets.
-
Select MassInfoWidget.
-
For each of the parameters listed in the following table, click the Add Parameter button and select the parameter from the list. Then, enter the value shown in the table.
| backgroundImage | ../data/Overlays/infoBackground.png |
| font | ../data/fonts/mak_mono.ttf |
| fontSize | 11 |
| height | 18 |
| originX | 100 |
| originY | 0 |
| paddingX | 6 |
| paddingY | -1 |
| textAlignment | Left |
| textColor | 0, 0, 0, 255 |
| width | 140 |
Configure Entities
- Select the Entity Definition Editor page.
- Select the Land entity.
- Click on the Add Visual Definitions icon. The Add Visualizers dialog box opens.
- Select the 2D Mass Label visualizer. An entry is added to the list of visualizers.
- Scroll to the bottom of the Visual Definitions list and select 2D Mass Label
- Click on the Add Attribute icon, select modeldefinitionschema, and click OK.
- Change the model Definition Schema to WidgetTheme.
- Click on the Add Attribute icon, select modeldefinition, and click OK.
- Change the modelDefinition to MassInfoWidget.
- Click on the Add Attribute icon, select visualtype, and click OK.
- Change the visual Type to main_model.
- Click on the Add Attribute icon, select parent, and click OK.
- Change the parent to Main Object.
- Click on the Add Attribute icon, select type, and click OK.
- Type should automatically be set to Mass Label Visualizer; if not, change it.
Close the Visual Model Editors dialog box.
Create a Connection using the Extended FOM Mapper
-
Choose Settings -> Connections.
-
Select the HLA 1.3 Connections page.
-
Click the Delete icon (minus sign) until you delete the existing connections. (This will prevent confusion when using this example. The installed connections are still available if you use the default appData directory.)
-
Click the Add Connection icon. A connection called HLA 1.3 Connection #1 is added.
-
Click the Rename icon. The Rename Connection dialog box opens.
-
Rename the connection to FOM Mapper Example Connection.
-
Change the Federation Name to VrlExtend.
-
Change the FED File Name to VrlExtend.fed.
-
Change the FOM Version to 0.8.
Connect to the Network and run the addAttr Example
- If the Simulation Connections dialog box is not still open, choose Settings -> Connections.
- Select the HLA 1.3 Connections page.
- Select the FOM Mapper Example Connection.
- Click Connect.
- Close the Simulation Connections dialog box.
- Run the VR-Link addAttr example program.
The addAttr example should start publishing an entity. VR-Vantage should discover the entity. To quickly see the entity, attach to it by pressing the period (.) key. A tank should be visible. To the right of the tank there should be a label indicating the entity's mass.
- Note
- The entity will be displayed in space, with no terrain. If you want it on a specific terrain, when you rebuild addAttr use a set of coordinates that you know will work with a specific terrain and load that terrain in VR-Vantage. For the purposes of this example, displaying the label that shows the vehicle's mass proves that VR-Vantage works with the extended FOM.
Building the Example
VR-Vantage includes pre-built versions of the example plug-in. To build it yourself, follow the instructions at Building VR-Vantage Examples, Applications, and Plug-ins.
Running the Example
This example is a plug-in. You can run it by running ./bin/exampleFomMapper_stealth.bat (on Windows) or ./bin/exampleFomMapper_stealth.sh (on Linux). For more information about running examples, please see Running Applications and Examples.
Example Source Files
exampleFomMapper.h
#pragma once
#ifdef _WIN32
#ifdef EXAMPLEFOMMAPPER_EXPORTS
#define DT_DLL_EXAMPLEFOMMAPPER __declspec ( dllexport )
#else
#define DT_DLL_EXAMPLEFOMMAPPER __declspec ( dllimport )
#endif
#else
#define DT_DLL_EXAMPLEFOMMAPPER
#endif
#if DtHLA
#define DT_PROTOCOL_NAMESPACE vrvHla13
#define DL_DLL_IGCONVRLINK DT_DLL_VRVHLA13
#endif
configFomMap.h
#pragma once
#if DtHLA
#include <vl/fomMapper.h>
namespace makVrv
{
{
}
}
void encodeMass(
const DtStateRepository& stateRep,
RTI::AttributeHandleValuePairSet* avList,
RTI::AttributeHandle handle);
const RTI::AttributeHandleValuePairSet& avlist,
int pairSetIndex);
const DtStateRepository& stateRep,
const DtStateRepository& asSeenByRemote);
#endif
configFomMap.cxx
#if DtHLA
#if DtHLA
#define DT_PROTOCOL_NAMESPACE vrvHla13
#endif
#include <vl/hlaObj.h>
#include <vl/encFactory.h>
#include <vl/decFactory.h>
#include <vl/intEncFact.h>
#include <vl/intDecFact.h>
#include <vl/hInterFactory.h>
{
mapper->stateEncoderFactory()->addAttributeEncoder(
"BaseEntity", "Mass",
(DtHlaStateEncoder::DtAttributeEncoder)
encodeMass,
true);
mapper->stateDecoderFactory()->addAttributeDecoder(
"BaseEntity", "Mass",
(DtHlaStateDecoder::DtAttributeDecoder)
decodeMass,
true);
mapper->stateEncoderFactory()->addAttributeChecker(
"BaseEntity", "Mass",
(DtHlaStateEncoder::DtAttributeChecker)
needMass,
true);
}
void encodeMass(
const DtStateRepository& stateRep,
RTI::AttributeHandleValuePairSet* avList, RTI::AttributeHandle handle)
{
if(actualRep)
{
DtNetFloat64 netVal(mass);
avList->add(handle, (char*) &netVal, sizeof(DtNetFloat64));
}
}
const RTI::AttributeHandleValuePairSet& avlist, int pairSetIndex)
{
if(actualRep)
{
DtNetFloat64 netVal;
RTI::ULong length = 0;
avlist.getValue(pairSetIndex, (char* )&netVal, length);
actualRep->
setMass((
double) netVal);
}
}
bool needMass(
const DtStateRepository& stateRep,
const DtStateRepository& asSeenByRemote)
{
if(actualRep && actualSeen)
{
return (
bool) (actualRep->
mass() != actualSeen->
mass());
}
else
{
return false;
}
}
#endif
MyEntityStateListener.h
#pragma once
#include <vl/entitySR.h>
{
public:
};
{
public:
virtual makVrv::vrvHla13::DtVrlinkEntityStateListener*
create(DtHlaObject*
object);
};
MyEntityStateListener.cxx
using namespace makVrv;
using namespace makVrv::vrvHla13;
{
}
{
}
{
}
{
}
{
}
{
}
MyEntityStateRepInterceptor.h
#pragma once
#include <vl/hlaObj.h>
#include <vl/entitySR.h>
: public makVrv::vrvHla13::DtEntityStateInterceptor<DtEntityStateRepository>
{
public:
virtual void setMass( float mass );
virtual float mass() const;
public:
virtual DtStateRepository* clone(bool copy = false) const;
protected:
protected:
float myMass;
};
: public makVrv::vrvHla13::DtEntityStateInterceptorCreator
{
public:
};
MyEntityStateRepInterceptor.cxx
using namespace makVrv;
using namespace makVrv::vrvHla13;
, myMass(0.0)
{
}
{
{
if(listener)
{
}
}
}
{
}
{
DtTHROW_NEW(DtCorruptedState,"Can't clone MyEntityStateRepInterceptor");
}
{
}
{
}
{
}
MyEntityStateRep.h
#include <vl/entitySR.h>
{
public:
virtual void setMass(float mass);
virtual float mass() const;
virtual void printData() const;
virtual DtStateRepository* clone(bool copy = false) const;
protected:
float myMass;
};
: public makVrv::vrvHla13::DtEntityStateRepositoryCreator
{
public:
};
MyEntityStateRep.cxx
{
}
{
}
{
printf(
"Mass: %lf\n",
mass());
}
{
if (copy)
{
}
else
{
}
}
{
}
MassLabelVisualizer.h
#pragma once
namespace makVrv
{
class DtWidgetLabelAgent;
class DtProjectedWidgetModelAgent;
}
{
public:
protected:
protected:
};
MassLabelVisualizer.cxx
#include <stdio.h>
using namespace makVrv;
, myProjectedModel(0)
, myLabel(0)
{
}
{
}
{
{
}
{
}
{
}
}
{
{
char buffer[256];
sprintf(buffer,"Mass: %4.4f kg",mass);
DtUnicode txt = DtUnicode::fromAscii(buffer);
}
}
{
{
}
}
{
{
}
}
{
}
{
"MassLabelVisualizerSchema",
DtUnicode::tr("2D Mass Label"));
"MassLabelVisualizer",
DtUnicode::tr("Mass Label Visualizer"),
DtStateVisualizer::EntityVisualizerType,
thisSchema);
return thisTypeInfo;
}
VrlinkMassLabelVisualizer.h
#pragma once
{
public:
protected:
};
VrlinkMassLabelVisualizer.inl
#pragma once
{
if(myCustomListener)
{
}
}
{
{
if(rep)
{
}
else
{
}
}
}
DtFomMapperAccessory.h
#pragma once
namespace makVrv
{
class DtConnection;
{
{
public:
DtFomMapperAccessory();
virtual ~DtFomMapperAccessory();
virtual void install(DtDriver* driver);
virtual void uninstall(DtDriver* baseConn);
virtual bool isCompatible(DtDriver* driver);
protected:
virtual void slot_onSimCreated(DtConnection* connection);
virtual void slot_onSimConnected(DtConnection* connection);
virtual void slot_onSimAboutToBeDisconnected(DtConnection* connection);
};
}
}
DtFomMapperAccessory.inl
#include <vl/exerciseConn.h>
#include <boost/bind.hpp>
#include <iostream>
namespace makVrv
{
{
{
}
DtFomMapperAccessory::~DtFomMapperAccessory()
{
}
void DtFomMapperAccessory::install(DtDriver* driver)
{
DtVrlinkDriver* vld = dynamic_cast<DtVrlinkDriver*>(driver);
if(vld)
{
vld->signal_simulationCreated.connect(
boost::bind(&DtFomMapperAccessory::slot_onSimCreated, this, _1));
}
}
void DtFomMapperAccessory::uninstall(DtDriver* baseConn)
{
DtVrlinkDriver* vld = dynamic_cast<DtVrlinkDriver*>(baseConn);
if(vld)
{
vld->signal_simulationCreated.disconnect(
boost::bind(&DtFomMapperAccessory::slot_onSimCreated, this, _1));
}
}
bool DtFomMapperAccessory::isCompatible(DtDriver* driver)
{
DtVrlinkDriver* dd = dynamic_cast<DtVrlinkDriver*>(driver);
if(dd)
{
return true;
}
return false;
}
void DtFomMapperAccessory::slot_onSimCreated(DtConnection* conn)
{
conn->signal_connected.connect(
boost::bind(&DtFomMapperAccessory::slot_onSimConnected, this, conn));
conn->signal_toBeDisconnected.connect(
boost::bind(&DtFomMapperAccessory::slot_onSimAboutToBeDisconnected, this, conn));
DtVrlinkConnection* vrlCon = dynamic_cast<DtVrlinkConnection*>(conn);
if(vrlCon)
{
DtEntityStateRepositoryCreator::registerInstance(*vrlCon,
conn->registerInstance("DtEntityStateInterceptorCreator",
conn->registerInstance("DtVrlinkEntityStateListenerCreator",
}
}
void DtFomMapperAccessory::slot_onSimConnected(DtConnection* conn)
{
DtVrlinkConnection* vrlCon = dynamic_cast<DtVrlinkConnection*>(conn);
if(vrlCon)
{
}
}
void DtFomMapperAccessory::slot_onSimAboutToBeDisconnected(DtConnection* conn)
{
DtVrlinkConnection* vrlCon = dynamic_cast<DtVrlinkConnection*>(conn);
if(vrlCon)
{
vrlCon->registerInstance("DtFomMapperVisualizer", 0);
}
}
}
}
exampleFomMapperPlugin.h
#pragma once
#define DT_DE_PLUGIN_EXPORT_MACRO DT_DLL_EXAMPLEFOMMAPPER
namespace makVrv
{
class DtDe;
}
exampleFomMapperPlugin.cxx
#include <boost/bind.hpp>
using namespace makVrv;
namespace exampleFomMapperPlugin
{
void postInitializeWork(
DtDe* de)
{
new MassLabelVisualizerCreator);
new VrlinkMassLabelVisualizerCreator);
}
{
{
}
}
}
{
return true;
}