VR-Forces 5.0.1 Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
7.6.0 - Example of Transforming Nodes Programmatically

Table of Contents

This example shows how VR-Forces uses meta files and related code to animate the flag on the Arleight Burke ship.

7.6.0.1 - Editing the Arleigh Burke Model

The Arleigh Burke model was modified to include specific nodes for the flag at the back of the ship. The flag has three overlapping models for the three wind states (no wind, low wind, and high wind). Figure 1 illustrates the flag models:

arleighburkeflag.png
Figure 1. Flag Model for Arleigh Burke

Figure 2 illustrates the structure of the specific nodes for the flag in Creator.

html vrv_flagnodes.png "Figure 2. Node Structure for the Flags"

The node named 'FlagDir' is a DoF (degree of freedom) node. The FlagDir node rotates the flag to fly in the wind direction. The FlagDir node also has an annotation as seen in Figure 3. This annotation is used in the meta file to find the FlagDir node in the scene graph when Vantage traverses it.

vrv_flagdirnode.png
Figure 3. Annotation for FlagDir Node

The node named sw2 is used to set the flag state in this example. sw2 is a switch node that has 3 states (nodes g323, g323_1, g325). While FlagDir is for orientation, the sw2 node is for making the flag appear upright when the wind is high. The node has an annotation as seen in Figure 4.

vrv_sw2node.png
Figure 4: Flag Switch Node

For matching a node, either the node name or the annotation (or both) can be provided. Any node that matches either criterion will be processed. For the flag state, both the node name and annotation are used for matching criteria.

7.6.0.2 - The Arleigh Burke Meta File

The Arleigh Burke meta file is in the data directory: data\Vehicles\Surface\ddg51_arleigh-burke_navy\1407007162_arleigh-burke_ddg51_arleigh-burke_navy.meta. The meta file for the Arleigh Burke flag is as follows:

{
"NodeFeatures": [
{
"Linkage": {
"Comment": "Traversal will match up this feature based on NodeName match OR AnnotationName match",
"NodeName": "sw2",
"AnnotationName": "@dis switch wind_effect"
},
"FeatureData": [
{
"FeatureName": "ShipFlagSwitch",
"Comment": "Change state based on wind speed. Each state corresponds to a km/h speed",
"WindSpeed": [
5,
10,
15
],
"WindState": [
0,
1,
2
]
}
]
},
{
"Linkage": {
"Comment": "Traversal will match up this feature based on NodeName match OR AnnotationName match",
"NodeName": "",
"AnnotationName": "@dis articulated_part 7872"
},
"FeatureData": [
{
"FeatureName": "ShipFlagDir",
"Comment": "This part rotates to align with the net wind velocity (= wind_dir only) (use CW, CCW for rotation direction) (use RotOffsetDeg to compensate for model rotation)",
"RotOffsetDeg": 0,
"RotDir": "CCW"
}
]
}
]
}
]
}

The NodeName and AnnotationName fields match the node name and comments in the model. This is the link between the asset and the meta file.

FeatureName is the link between the meta file and the code. Each specific FeatureName corresponds to a particular class in the Vantage code.

7.6.0.3 - Coding the Arleigh Burke Flag

For the meta file to influence the runtime visuals, new code must be written. The flag code class hierarchy is shown in Figure 4.

html vrv_arleighburkeflagclasshierarchy.png "Figure 4: The Classes Related to the Flag Orientation and State"

Look at DtShipFlagSwitchTickable in Figure 4. Since the flag on the ship may change state on every frame, DtShipFlagSwitchTickable inherits from DtTickable. DtDynamicFeatureTickable includes common elements for all the terrain flags and ship flags.

7.6.0.4 - The Creator Class

The object needs a creator class for dynamic construction. The following classes are derived from DtMetaObjectCreator:

In Vantage, there is a DtMetaObjectFactory, which produces the objects based on the FeatureName string. The FeatureName is the link between the meta file and the code (via the DtMetaObjectFactory). The creator constructor has many arguments. However, not every meta object needs to use all the arguments.

7.6.0.5 - Parsing the Meta File Information

RapidJSON is used to parse the configuration parameters for the object. DtShipFlagSwitchTickable.cxx contains an example of code for parsing in the configuration data. The following code demonstrates the use of RapidJSON to extract information out of a JSON document. VR-Forces uses the RapidJSON library to parse the meta file. The RapidJSON library is well-documented, so it is straightforward to find other examples of parsing more complicated parameters.

const rapidjson::Value& windJsonData)
{
// Get our node and cast it to the correct type for what we expect
myWindSwitchNode = dynamic_cast<osgSim::MultiSwitch*>(n);
{
myDe.DT_LOG_WARN << "DtShipFlagSwitchTickable: node (" << n->getName()
<< ") with type (" << n->className()
<< ") is not convertible to MultiSwitch" << std::endl;
}
// Get the feature name --- WHAT IS theTagFEATURE NAME???!?!
// Get the array of wind speeds
if(!windJsonData.HasMember(theTagWindSpeeds))
{
myDe.DT_LOG_WARN << "Error: Could not find entry (" << theTagWindSpeeds
<< ") in feature (" << myFeatureName << ")" << std::endl;
return;
}
const rapidjson::Value& speedsArray = windJsonData[theTagWindSpeeds];
const int numSpeeds = speedsArray.Size();
// Get the array of switches
if(!windJsonData.HasMember(theTagSwitchIndices))
{
myDe.DT_LOG_WARN << "Error: Could not find entry (" << theTagSwitchIndices
<< ") in feature (" << myFeatureName << ")" << std::endl;
return;
}
const rapidjson::Value& statesArray = windJsonData[theTagSwitchIndices];
const int numStates = statesArray.Size();
if(numSpeeds != numStates)
{
myDe.DT_LOG_WARN << "Error: Number of wind speeds (" << numSpeeds
<< ") does not match the number of wind switch states ("
<< numStates << "). Using minimum." << std::endl;
return;
}
int numWindElements = DtMin(numSpeeds, numStates);
for (int i = 0; i < numWindElements; ++i)
{
myWindSpeeds.push_back(speedsArray[i].GetDouble());
mySwitchIndices.push_back(statesArray[i].GetInt());
}
}

7.6.0.6 - Registering with the DtTickableManager

Since DtShipFlagSwitchTickable is a DtTickable element, it is registered with the DtTickableManager so that its update function is called every frame as follows:

DtShipFlagSwitchTickable* DtShipFlagSwitchTickableCreator::create(...)
{
...
windTickable->configure(node, jsonObject);
// This object is tickable, so add this object to the tickable manager
de.renderer().tickableManager().addTickableObject( windTickable );
return windTickable;
}

If your object is not updated every frame, this step is optional.

7.6.0.7 - Creating the Update Function

The DtShipFlagSwitchTickable::Update function is called every frame, so update tries to return early if parameters have not changed since the last frame. The flag also has a smoother so that the direction does not change drastically frame to frame.

{
{
return;
}
// winDirRad = radiansClockwiseFromNorth
// in radians East from North. This is the direction the wind is
// coming from
float windDirRad = myEnvironment.windDirection();
// In meters / second!
float windSpeedMperS = myEnvironment.windSpeed();
// If there is no sceneObj, use only the
// wind speed (not wind+ship speed)
double windSpeed = windSpeedMperS;
// Get the direction and speed of the current ship
// This should have been set in DtOsgArticulatedModel
{
// Do the simple thing - assume ship speed = 0
SetWindNodeSwitch(windSpeed);
return;
}
// Calculate the wind direction as a vector
DtVector windDirVec = computeWindVec(mySceneObject, windDirRad);
// Get the ship updater
DtDeadReckonUpdater* updater = dynamic_cast<DtDeadReckonUpdater*>(mySceneObject->findUpdater());
DtVector shipVelMperS;
if(updater)
{
// Get the ship velocity (assuming m/s)
shipVelMperS = updater->approxVelocity(tNow);
}
else
{
DtVector currPos;
mySceneObject->getPosition(0, currPos);
DtVector deltaPos = currPos - myLastPos;
DtTime deltaTime = tNow - myLastTime;
if(deltaTime <= 0)
{
return;
}
DtVector velocity;
DtVecScale(deltaPos, 1.0f/deltaTime, velocity);
shipVelMperS = velocity;
myLastPos = currPos;
myLastTime = tNow;
}
// The flag direction is the opposite of the boat velocity
DtVector flagVel;
DtVecNeg(shipVelMperS, flagVel);
// Get the wind direction scaled by the wind speed
DtVector scaledWindVec;
DtVecScale(windDirVec, windSpeedMperS, scaledWindVec);
// Compute combined ship+wind vector
DtVector combinedWindDir = scaledWindVec + flagVel;
// This is the wind speed taking into account the ship and the wind
windSpeed = DtMath::Sqrt(combinedWindDir.magnitudeSquared());
// Smooth if there is enough data or
// else use the raw wind speed
{
}
// Wind speed didn't change, node switch state shouldn't change
if(windSpeed == myLastWindSpeed)
{
return;
}
else
{
SetWindNodeSwitch(windSpeed);
myLastWindSpeed = windSpeed;
}
}

If your object does not need to be udpated, this step is optional.

7.6.0.8 - Register with the DtMetaObjectFactory

In order for the DtShipFlagSwitchTickable object to be available to VR-Forces, it needs to be registered with the DtMetaObjectFactory. This occurs in vrvOsg.cxx file in the init function. However, you can register objects in their own files as well:

// Register the creators with the DtMetaObjectFactory
// the*FeatureName must match "FeatureName" field in the meta file
DtMetaObjectFactory& mof = DtMetaObjectFactory::instance(de);
mof.addCreator(DtShipFlagSwitchTickable::theShipFlagSwitchFeatureName, new DtShipFlagSwitchTickableCreator());

In this code, theShipFlagSwitchFeatureName refers to the string "ShipFlagSwitch", which matched the FeatureName in the meta file. This is how the connection between the code and the meta file is established.

7.6.0.9 - Traversing the Scene Graph

You must attach the code to matching nodes. The matching is done through a traversal, which in DtOsgArticulatedModel and DtOsgTerrainPatch is done in a function called applyMetaFile. applyMetaFile relies on the DtMetaFileVisitor which is used as follows:

DtMetaFileVisitor metaFileVisitor(myDe, myModelMetaFeatures, myModelMetaAttachments, myModelDefinition, elementId, mySceneObjectId, myUniqueId, myIsInstanced);
rootNode->accept(metaFileVisitor);

A function similar to applyMetaFile would be needed in custom classes to associate the meta file objects with the nodes in the scene graph.

7.6.0.10 - Instancing Support

You can incorporate instancing into a meta object. An example of handling instanced data is provided in the update functions. Ships are not instanced, but flags on terrain can be instanced. The instanced path for an update function is as follows:

// update the multiswitch
unsigned int switchIndex = getSwitchIndexForSpeed(windSpeed);
unsigned int switchSet = myWindSwitchNode->getActiveSwitchSet();
myWindSwitchNode->setSingleChildOn(switchSet, switchIndex);
// Instanced path
if(myUseInstancing)
{
// Use myUniqueId from DtOsgArticulatedModel
DtInstanceState* instanceState = myInstanceManager.findInstanceState(myUniqueId);
if(instanceState)
{
unsigned int numChildren = myWindSwitchNode->getNumChildren();
for (unsigned int childIx = 0; childIx < numChildren; ++childIx)
{
bool state = myWindSwitchNode-> getChildValue(myWindSwitchNode->getChild(childIx)
switchIndex);
myInstanceManager.setSwitchState(*instanceState, myNodeTypeIndex, childIx, state);
}
}
}

The instancing path needs specific code that mirrors code in DtOsgArticulatedModel. In this example, the node is set normally and then the state is read back to update the instanced data.

[<< Transforming Nodes Programmatically] [Home] [Top of Page] [VR-Vantage Shader Architecture >>]


Document ID: Generated on Mon Jun 20 00:38:30 EDT 2022 from SVN revision 244029
Copyright © 2005-2021 MAK Technologies. All Rights Reserved (www.mak.com)