VR-Forces Development_Version Class Documentation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
3.5 - Creating and Managing Objects

Table of Contents

The Object Manager creates and maintains local objects.

It also instantiates local instances of remotely simulated objects. It monitors the network for state updates from remote objects in an exercise and for VR-Forces-specific state information. When it detects the presence of a new remote object, it creates a new remote DtVrfObject to represent it and adds it to list of objects to be managed.

3.5.1 Object Factories

When VR-Forces reads object specifications from a file, or when it receives them in an interface message, it needs to instantiate those objects in a simulation. To allow you to add new types of objects without having to modify the file-reading code, VR-Forces uses object factories. A factory is a list of creator functions indexed by a key (which can be a string, an integer, or both) that indicates the kind of object to create. The following types of objects have factories that use this basic scheme:

The factories are also used to create simulation objects and control objects that are specified in interface messages from the GUI.

The factories are created by the DtVrfCreator. For more information about how to add new types of objects to VR-Forces using object factories, please see 2.4 - Customizing or Extending the Simulation Engine.

3.5.2 Creating a New Local Object

Locally simulated DtVrfObjects in VR-Forces should be created through the DtVrfObjectManager. They are created by calling one of the Object Manager’s createAndInitVrfObject() functions. These functions create, initialize, and add a new instance of a DtVrfObject to the Object Manager.

There are two overloaded versions of the createAndInitVrfObject() function, for use with different kinds of object geometry. The first form takes a list of vertices as an argument. This is the set of vertices in local coordinates that make up the geometry of the object. This form is suitable for creating control objects, such as routes and waypoints. For example, to create a route named Route 1, consisting of three vertices, do the following:

DtVrfObject * route;
DtList vertices;
// Add some vertices to our route. Expressed in geocentic coordinates.
vertices.add(new DtVector(-2812729.7730, -4332999.1841, 3728433.175));
vertices.add(new DtVector(-2812375.6097, -4332915.4285, 3728795.2170));
vertices.add(new DtVector(-2812101.6319, -4332990.6562, 3728913.6333));
// Route object type.
// Extensions to object type enumerations are in objTypeEnums.h
DtObjectType routeType(1, 17, 0, 0, 2, 0, 0, 0);
route = objectManager->createAndInitVrfObject(
routeType,
true,
"Route 1",
DtString::nullString(),
DtForceOther,
vertices);

The second form of the createAndInitVrfObject() call takes a single position as an argument. This form is suitable for creating simulation objects or tactical graphics with simple geometry such as waypoints. The object’s geometry gets initialized with a single vertex given by the position. For example, to create a waypoint named Waypoint Alpha, do the following:

DtVrfObject * waypoint;
// Waypoint object type.
// Extensions to object type enumerations are in objTypeEnums.h
DtObjectType waypointType(1, 16, 0, 0, 1, 0, 0, 0);
// Initial position of waypoint, in geocentric coordinates.
DtVector initialPosition(-2812375.6097, -4332915.4285, 3728795.2170);
waypoint = objectManager->createAndInitVrfObject(
waypointType,
true,
"Route 1",
DtString::nullString(),
DtForceOther,
initialPosition);

If the new local object has been configured with a local network interface, then the object is published over the network. Any remote VR-Forces application’s Object Manager will detect the existence of the new DtVrfObject and automatically create a remote instance of it.

Note
Do not call the DtVrfObject constructor directly. Do all of the creation of DtVrfObjects through the Object Manager (through its createAndInitVrfObject()) functions. The Object Manager not only instantiates the object, but initializes and adds the object to itself for management.

3.5.3 Removing a Locally Simulated Object from the Simulation

To destroy a simulated object, call one of the DtVrfObjectManager::removeAndDelete() functions. One form of the this function takes a pointer to a DtVrfObject directly; one form takes a DtEntityIdentifier key. In this function, the Object Manager calls queueObjectForRemoval() to place the DtVrfObject on a list of objects to be removed. At the end of the Object Manager’s tick, it iterates through the list of objects to be removed, removes the object from its list of DtVrfObjects, and deletes it. If the DtVrfObject is locally simulated, then it also sends out a DtIfVrfObjectDeleted interface message to notify remote VR-Forces applications that the object should be deleted. Remote VR-Forces applications receive the interface message and delete the object in their Object Managers.

Note
Do not delete a DtVrfObject directly. Use DtVrfObjectManager::removeAndDelete() to ensure that the DtVrfObject is removed at a safe point during the simulation frame.

3.5.4 Getting Notified When Objects are Added or Removed

You can register callbacks with the Object Manager to receive notification for the following events:

The callbacks are registered with the DtVrfObjectManager using the following functions:

"Object about to be added" callbacks are invoked immediately after a new DtVrfObject has been created and initialized, but just before it gets added to the Object Manager’s list. "Object added" callbacks get invoked immediately after a newly created and initialized DtVrfObject is added to the Object Manager’s list. "Object about to be removed" callbacks get invoked just prior to removal of an object from the Object Manager’s list.

Callback functions must have a signature of the following form:

(DtVrfObject * vrfObject, void * usr);

The following example demonstrates how to register the callbacks described in this section. The application (for example, a simulation back-end) displays diagnostic print statements as objects are added to, and removed from, the Object Manager.

static void addingObjectCb(DtVrfObject * vrfObject, void * usr)
{
DtInfo("About to add new object % \n", vrfObject->markingText());
}
static void addedObjectCb(DtVrfObject * vrfObject, void * usr)
{
DtInfo("Just added new object %s\n", vrfObject->markingText());
}
static void removingObjectCb(DtVrfObject * vrfObject, void * usr)
{
DtInfo("About to delete object %s \n", vrfObject->markingText());
}
main()
{
. . .
objManager->addVrfObjectAboutToBeAddedCallback(addingObjectCb);
objManager->addVrfObjectAddedCallback(addedObjectCb);
objManager->addVrfObjectAboutToBeRemovedCallback(removingObjectCb);
. . .
}
Note
Remember to unregister your callbacks when you no longer need them. Each of the above add*Callback() functions has a corresponding remove*Callback() function you can call to remove it from the Object Manager.

3.5.5 Looking Up Individual Objects

You can look up an individual object in the Object Manager using one of the following keys:

Lookup functions return a pointer to the object if it is found, NULL if it is not found. For example, to look up an object named Waypoint Alpha, do the following:

DtVrfObjectManager* objectManager;
DtVrfObject * object;
. . .
object = objectManager->lookupVrfObjectByName("Waypoint Alpha");

3.5.6 Iterating Through Simulation Objects

The Object Manager provides two different member functions for iterating through the objects in a simulation. An iterator of type DtVrfObjectFilterIterator is returned by the Object Manager’s vrfIterator() member function. The following example shows how to iterate over all the objects in the simulation:

DtVrfObject* pVrfObject = NULL;
DtVrfObjectFilterIterator iterator = pObjectManager->vrfIterator();
for (pVrfObject = iterator.first(); pVrfObject; pVrfObject = iterator.next())
{
. . .
}

3.5.6.1 Including and Excluding Objects

You may want to include or exclude certain types of objects. For example, the following code causes all air vehicles to be excluded.

// Note: -1s are wild cards for DtObjectType::matchPattern().
DtObjectType airObjectFilter(DtObjectTypeIndividual, DtPlatform, DtPlatformDomainAir, -1, -1, -1, -1, -1);
DtVrfObject* pVrfObject = NULL;
DtVrfObjectFilterIterator iterator = pObjectManager->vrfIterator();
for (pVrfObject = iterator.first(); pVrfObject; pVrfObject = iterator.next())
{
if (pVrfObject->objectType().matchPattern(airObjectFilter))
{
continue;
. . .
}

3.5.6.2 Using an Object Predicate Filter

You can include and exclude objects by providing a filter when you request the iterator from the Object Manager. The filter is a DtVrfObjectPredicate. DtVrfObjectPredicates is discussed in detail in "ObjectPredicates, but the following example shows a DtObjectTypeEqualPredicate, which is a kind of DtVrfObjectPredicate being used to accomplish the same thing as in the previous \ref example3-1 "example". In this case, the air vehicles are skipped by the iterator itself. Note the second argument to the constructor of the predicate. It is a negate flag, indicating that the sense of the predicate should be negated, or inverted.

DtObjectType(DtObjectTypeIndividual, DtPlatform, DtPlatformDomainAir, -1, -1, -1, -1, -1)), true);
DtVrfObject* pVrfObject = NULL;
DtVrfObjectFilterIterator iterator = pObjectManager->
vrfIterator(airObjectFilter);
for (pVrfObject = iterator.first(); pVrfObject; pVrfObject = iterator.next())
{
. . .
}

VR-Forces provides a few commonly used predicates, including DtObjectTypeEqualPredicate, DtDestroyedPredicate, and DtFilterFunctionPredicate, which allow you to specify an arbitrary filter function. You can, of course, create your own derived predicates. Please see 3.5.7 Object Predicates for more information.

Note
Early versions of VR-Forces allowed an object type and negate flag to be passed as arguments to the vrfIterator() member function instead of a DtVrfObjectPredicate. This form is still supported for backward compatibility, but use of the DtVrfObjectPredicate is the preferred method.

3.5.6.3 Using Managed Object Lists

While iterators are fairly easy to use, in some cases they may be quite inefficient. For example, if multiple simulation objects had components that needed to iterate through all the non-air domain simulation objects, then each would be comparing every entity’s object type to the filter every simulation frame, even though object types never change, and even though each component ends up with the same set of simulation objects. It would be somewhat more efficient if each component needing all non-air domain simulation objects registered callbacks for simulation object additions and removals, and then built and maintained its own list of non-air simulation objects. When a new simulation object entered the simulation, its object type could be checked once, and it would then be placed on the list or not, based on the filter criteria. While this is an improvement over iterating through all the simulation objects every frame, the use of memory is still inefficient, because every component that needs such a list has to keep (and manage) what is, in effect, the same list.

The Object Manager provides the getList() member function to request such a list. The list is automatically built and maintained if it does not already exist at the time of the request. These lists are called Managed Object Lists, and once requested, can be iterated using a DtManagedObjectList::const_iterator. You can use the same predicates used for the filtered iterators discussed in 3.5.6.2 Using an Object Predicate Filter, to specify a Managed Object List. When a Managed Object List is requested, the Object Manager checks to see if there is already a list with the same predicate being maintained. If there is, a shared pointer to this list is returned; otherwise, the list is created and a shared pointer to the new list is returned. Managed Object Lists are typically requested during initialization of a component and cached for use later in the component’s tick. The memory associated with a given Managed Object List is automatically cleaned up when the last reference to the list is destroyed.

The following example shows a component requesting a list of objects that excludes simulation objects in the air domain, maintaining a reference to that list, and iterating over it in its tick() function. It keeps the DtManagedObjectListPtr returned by getList() in a member variable called myList.

// access (and create, if needed, the air vehicle-free list)
{
DtObjectTypeEqualPredicate airObjectFilter(
DtPlatformDomainAir, -1, -1, -1, -1, -1)), true);
myList = mySimManager->vrfObjectManager()-> getList(airObjFilter,
true);
}
// use the air vehicle-free list
DtExampleComponent::tick()
{
DtVrfObject* pVrfObject;
for (iter = myList.begin(); iter != myList.end(); ++iter)
{
pVrfObject = *iter;
. . .
}
}

You can create filtered lists in which the object attributes being tested by the predicate change, such as building and maintaining a list of all the destroyed objects. The Object Manager’s getList() member function has two optional arguments - the first is a flag that you can use to tell the Object Manager that the list should be re-evaluated periodically. By default, this is done every simulation frame. The third optional argument specifies a period, in seconds, for the re-evaluation.

The following example requests a list of all destroyed simulation objects, re-evaluated once a second:

// The negate constructor argument could be set to true to get a list of
// healthy objects
DtDestroyedPredicate destroyedPredicate();
myList = mySimManager->vrfObjectManager()->getList(destroyedPredicate, true, 1.0);
. . .

3.5.7 Object Predicates

Object predicates are used to determine if an object meets some condition or set of conditions. The DtVrfObjectPredicate is an abstract class that defines an interface for object predicates. It consists primarily of an eval() function that takes a DtVrfObject* argument and returns a bool. Constructors normally take any data needed (such as a value to compare an object attribute to) and a bool negate flag which inverts the sense of the test (and defaults to false). VR-Forces provides object predicates for:

To create your own object predicate, you need to override the virtual method:

bool eval(DtVrfObject* object)const

which performs the desired test, and:

int predicateEqual(const DtVrfObjectPredicate& orig const)

The predicateEqual() member function is used to determine if an object predicate is the same as one already specified for an existing list. Given a predicate of type MyPredicate that contained member data myData, an example implementation of predicateEqual() might look like the following:

int MyPredicate::predicateEqual(const DtVrfObjectPredicate& orig) const
{
// Use dynamic_cast to first determine if it is the right kind of
// predicate
const MyPredicate * castOrig = dynamic_cast<const MyPredicate *>
(&orig);
if (!castOrig)
{
return 0;
}
// Compare any predicate-specific data
if (myData != castOrig->myData)
{
return 0;
}
// Return the result of the base class comparison, since everything
// else matches
}

3.5.8 Notifying an Application When a Simulation Object Changes

You can use DtVrfObjectPredicate with DtVrfObject to register a callback function to be called when the predicate becomes true. For example, a UAV component might register a callback to be notified if an object destroyed predicate becomes true for the simulation object representing the UAV’s controller station. The callback to be registered is defined in vrfObjPredCbInfo.h and has the form:

DtVrfObjectPredicateEvaluator* predicateEval, void* userData);

For example, in the following code snippet, a callback named objectIsDestroyedCallback, to be called if an object pointed to by pVrfObject is destroyed, is registered with a DtVrfObject.

DtDestroyedPredicate destroyedPredicate;
pVrfObject->addVrfObjectPredicateCallback(objectIsDestroyedCallback, destroyedPredicate, 1.5, this);

The third argument, 1.5, indicates that every 1.5 seconds, the object should re-evaluate if it is destroyed. The final argument is a void* userData argument that will be supplied to the callback function when it is invoked.

The DtVrfObjectPredicateEvaluator is created by the DtVrfObject to manage the predicate evaluation. A pointer to it is provided to make it easy to change that predicate, if desired, when the callback is invoked (using the DtVrfObjectPredicateEvaluator::predicate() and DtVrfObjectPredicateEvaluator::setPredicate() member functions), or modify the interval at which the predicate is evaluated (using VrfObjectPredicateEvaluator::setTestInterval()).

3.5.9 Important Coding Recommendations

Note the following recommendations:

[<< The Network Interface] [Home] [Top of Page] [Object Operations and Blocking Terrain Calls >>]


Document ID: Generated on Mon Jul 4 01:00:18 EDT 2016 from SVN revision 166489
Copyright © 2005-2015 VT MÄK. All Rights Reserved (www.mak.com)