VR-Forces Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
Managing Objects

Table of Contents

Objects and the Object Managers

The DtStateDataManager is responsible for holding the state data that represents every sim object in the system, both local and remote. However, in most cases you will not need to interact with the State Data Manager as there are two object manager classes that provide more conveient access to this data at the level of an individual sim object.

The Sim Object Manager (DtSimObjectManager) maintains read-only access to the current state of all sim objects, both local objects and remote. It is notified by the DtStateDataManager via a callback function when a new sim object is added. The sim objects may be locally added or they may be for objects discovered on the network. For each of these object, a DtSimObject instance is created. Any components that then need to lookup the current state of any simulation object can retrieve a reference its DtSimObject by requesting it from the Sim Object Manager.

The Local Object Manager (DtLocalObjectManager), on the other hand, creates and maintains a list of just the local objects in the simulation. Through the Local Object Manager new sim objects can be created, which hare then added into the DtStateDataManager. Each of these local sim objects is represented by an instance of DtLocalObject. Through the DtLocalObject the current frame data may be read and the next frame data may be updated.

Creating the Object Managers

The Local Object Manager and Sim Object Manager are among the manager objects created by the DtCgf object. The DtCgf object takes responsibility for deleting both of the Object Managers at the end of the simulation. You can create your own derived versions of DtSimObjectManager and DtLocalObjectManager. For a description of the process, please see The VR-Forces Creator.

Creating a New Local Object

Locally simulated objects in VR-Forces are represented by a DtLocalObject instance and are created and maintained by the DtLocalObjectManager. There are two ways to create local objects: through the DtCreateObjectService or directly through the DtLocalObjectManager. Which method you use depends on where the object is created.

Note
Do not call the DtLocalObject constructor directly. Any creation of DtLocalObject instances must be done through the Local Object Manager or Create Object Service. The Local Object Manager not only instantiates the object, but initializes and adds the object to itself for management.

Creating an Object Using the DtCreateObjectService

If you are creating a sim object from within the main tick, such as from sim components like sensors, controllers, and actuators, you need to use the DtCreateObjectService. This service can be accessed through the DtSimulationServices. The creation options are included in the DtVrfLocalObjectCreateInformation class, an instance of which is passed to the service. For instance, the following code could be used in a controller to create a new route.

// Route object type.
// Extensions to object type enumerations are in objectTypeEnums.h
DtObjectType routeType(1, 17, 0, 0, 2, 0, 0, 0);
// Add some vertices to our route. Expressed in geocentic coordinates.
DtList vertices;
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));
// Setup the information to pass to the creation service
info.setObjectType(routeType);
info.setPublishObject(true);
info.setObjectName("Route 1");
info.setForceType(DtForceOther);
info.setVertices(vertices);
DtCreateObjectHandle obj = simulationServices()->createObjectService()->createLocalObject(info);

Creating an Object Using the DtLocalObjectManager

If you are creating a sim object from outside of the main tick, such as if you're embedding DtCgf within your own application, you can create a local object directly through the DtLocalObjectManager. There are multiple overloaded versions of the DtLocalObjectManager::createAndInitVrfObject() function. The recommended version takes a reference to DtVrfLocalObjectCreateInformation, a class that includes the options available for creating a new local object. The following code could be used to create a new waypoint.

// Waypoint object type.
// Extensions to object type enumerations are in objectTypeEnums.h
DtObjectType waypointType(1, 16, 0, 0, 1, 0, 0, 0);
// Initial position of waypoint, in geocentric coordinates.
DtVector position(-2812375.6097, -4332915.4285, 3728795.2170);
// Setup the information to pass to the creation service
info.setObjectType(waypointType);
info.setPublishObject(true);
info.setObjectName("Waypoint 1");
info.setForceType(DtForceOther);
info.setPosition(position);

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 Sim Object Manager will detect the existence of the new sim object and automatically create a remote instance of it.

Removing a Locally Simulated Object from the Simulation

Similar to object creation, there are two ways to destroying and removing a locally simulated object: through the DtCreateObjectService or directly through the DtLocalObjectManager. Which method you use depends on where the object is destroyed. In both cases, the Local Object Manager calls queueObjectForRemoval() to place the DtLocalObject on a list of objects to be removed. At the end of the Local Object Manager’s tick, it iterates through the list of objects to be removed, removes the object from its list of objects, and deletes it. It also sends out a DtIfVrfObjectDeleted interface message to notify remote VR-Forces applications that the object has been deleted.

Note
Do not delete a DtLocalObject directly. Use one of the methods shown below to ensure that the DtLocalObject is removed at a safe point during the simulation frame.

Removing an Object Using the DtCreateObjectService

If you are removing a sim object from within the main tick, such as from sim components like sensors, controllers, and actuators, you need to use the DtCreateObjectService. This service can be accessed through the DtSimulationServices.

simulationServices()->createObjectService()->removeAndDelete(objUuid, true);

Removing an Object Using the DtLocalObjectManager

If you are creating a sim object from outside of the main tick, such as if you're embedding DtCgf within your own application, you can create a local object directly through the DtLocalObjectManager.

cgf->localObjectManager()->removeAndDelete(objUuid, true);

How the Local Object Manager Creates an Object

The Local Object Manager needs to create an instance of DtVrfObject for each simulated object. It does so as follows:

  1. The Local Object Manager calls DtLocalObject::create() to create an instance of a new DtLocalObject.
  2. The Local Object Manager calls the create() member function of the newly created object. This creates all of the managers, interfaces, and subcomponents needed by the object. Using the DtObjectType as the key, it looks up the object parameter database entry for the object. The appropriate subcomponent list is chosen. (For details, please see Creating an Object's Subcomponents.) Each of the subcomponents is created through a factory method, given the corresponding type string specified in the subcomponent list.
  3. The Local Object Manager calls the init() member function of the newly created object. This invokes the init() functions on each of its subcomponents. Upon completion of this call, the object is fully initialized and ready to be ticked.
  4. The Local Object Manager adds the new object to its list of objects to be managed.

The Local Object Manager ticks all of its objects in parallel using the DtVrfCallbackQueue instance returned by the DtSimulationServices parallelQueue() method.

Note
For a description of how you can add object types, please see Creating a New Local Object and "CreatinganEntity.

Creating an Object's Subcomponents

When the Local Object Manager creates an instance of a DtLocalObject, it needs to configure it with different subcomponents. The Local Object Manager knows which subcomponents to use because the object parameter database entry for each object specifies two lists of subcomponents, one for local objects; the other for remote objects. The strings in the subcomponent list correspond to the C++ objects that make up a sim object (through a has-a relationship).

For example, if the net-interface component lists the string individual-local-entity-net-interface-with-properties, the Local Object Manager knows that it has to create a DtSimIndividualLocalEntityWithPropertiesNetInterface for this object. The figure illustrates this process using the subcomponent list for a ground vehicle.

The following list matches the subcomponent strings to their base class. To find the subclasses for the various subcomponents, please see the class documentation for the base class and navigate to the subclass that you are interested in. Each subclass has a type() member function that returns the type string for the component. The type is registered with the appropriate factory and is what gets specified in the object parameter database.

Using the Sim Object Manager

Getting Notified When Objects are Added or Removed

You can register callbacks with the Sim Object Manager to receive notification for when sim objects are added or when sim objects are removed.

The callbacks are registered with the DtSimObjectManager by connecting to the signals signal_simObjectAdded and signal_simObjectDeleted. For example:

boost::signals2::scoped_connection simObjectAddedConnection = simulationServices()->simObjectManager()->signal_simObjectAdded.connect(
boost::bind(&MyClass::processSimObjectAdded, this, _1));
boost::signals2::scoped_connection simObjectDeletedConnection = simulationServices()->simObjectManager()->signal_simObjectDeleted.connect(
boost::bind(&MyClass::processSimObjectDeleted, this, _1));

Both signals supply a DtSimObjectReference to the sim object being added or removed. "Object added" callbacks get invoked immediately after a newly created and initialized DtSimObject is added to the Sim Object Manager’s list. "Object deleted" callbacks get invoked just after the removal of a sim object from the Sim Object Manager’s list.

Note
Remember to disconnect your callbacks from these signals when you no longer need them. For example:
simObjectAddedConnection.disconnect();

Looking Up Individual Objects

You can look up an individual object in the Sim Object Manager using either the object's DtUUID or DtEntityIdentifier. The lookup functions return a DtSimObjectReference to the object if it is found. The isValid() member of the DtSimObjectReference instance will return false if the object is not found.

For example, to look up an object named Waypoint Alpha, do the following:

DtSimObjectReference object = simulationServices()->simObjectManager()->lookup(objUuid);
if (object.isValid())
{
\\ Object was successfully found
}

Iterating Through Simulation Objects

The Sim Object Manager provides a function that returns a std::map containing a DtSimObjectReference for every sim object in the simulation. The following example shows how to iterate over all the objects in the simulation:

DtSimObjectManager::SimObjects objs = simulationServices()->simObjectManager()->simObjects();
DtSimObjectManager::SimObjects::const_iterator iter = objs.begin();
for (; iter != objs.end(); ++iter)
{
. . .
}

Getting a List of Sim Objects of an Entity Type

You may want to find the list of sim objects that matches a specific entity type. Tthe following returns a list of all air vehicles.

// Note: -1 indicates wild card for DtObjectType::matchPattern().
DtEntityType airEntityType(DtPlatform, DtPlatformDomainAir, -1, -1, -1, -1, -1);
std::vector<DtSimObjectReference> airEntities = simulationServices()->simObjectManager()->objectsOfType(airEntityType);
std::vector<DtSimObjectReference>::const_iterator iter = airEntities.begin();
for (; iter != airEntities.end(); ++iter)
{
. . .
}

Managed Object Lists

While it is fairly easy to iterate over all of the sim objects, 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 Managed Object List Manager, DtManagedObjectListManager, exists to provide such lists. When requested, a 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. When requesting a Managed Object List you can supply it with a predicate. 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. Managed Object List predicates are subclasses of DtVrfObjectPredicate. See Object Predicates for more details.

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 DtManagedObjectListConstPtr returned by getList() in a member variable called myList.

// access (and create, if needed, the air vehicle-free list)
{
DtObjectTypeEqualPredicate airObjectFilter(
DtObjectType(DtObjectTypeIndividual, DtPlatform,
DtPlatformDomainAir, -1, -1, -1, -1, -1)), true);
myList = simulationServices()->objectListManager()->getList(airObjFilter, true);
}
// use the air vehicle-free list
DtExampleComponent::tick()
{
for (iter = myList.begin(); iter != myList.end(); ++iter)
{
simObjRef = *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 Managed Object List 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 = simulationServices()->objectListManager()->getList(destroyedPredicate, true, 1.0);
. . .

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 DtSimObject 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
}

Receiving Notification When a Simulation Object Changes

You can use a DtPredicateCallbackManager 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 has the form:

void* userData);

All subclasses of DtSimComponent maintain a DtPredicateCallbackManager and have convienience functions for adding and removing callbacks from it. For example, in the following code snippet, a sim component registers a callback named objectIsDestroyedCallback to be called if an object referenced by simObjRef is destroyed.

DtDestroyedPredicate destroyedPredicate;
addSimObjectPredicateCallback(simObjref, 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.

Spatial Organization of Objects

The DtPhysicalWorld maintains a DtSpatialVrfObjectManager, which it uses to spatially organize specified DtSimObjects. A filter function predicate determines which DtSimObject instances are spatially sorted, to let you easily change the sorting in a derived class. Additionally, the creation, initialization, and use of the DtSpatialVrfObjectManager class is all performed in the appropriate functions, allowing for user modification.

A simple algorithm is used to determine the spatial organization configuration (number of subdivisions in each dimension). Also, all DtSimObject instances are stored in the Spatial Manager, including routes, waypoints, and so on.

To access the spatially sorted sim objects, the DtPhysicalWorld::getVrfObjects() functions are provided to get all sim objects that may intersect the specified primitive (currently only DtChord or DtExtent). The resulting set contains all objects that may intersect the chord or extent. Not all are guaranteed to do so, but the set will not be missing any that actually do. If the exact intersection is required, as for line-of-sight testing, then further refinement is required.


Document ID: Generated on Thu Oct 23 22:29:17 EDT 2025 from SVN revision 280951
Copyright © 2005-2024 MAK Technologies. All Rights Reserved (www.mak.com)