VR-Vantage 1.4.1 API Class Documentation
exampleBulletHole

This example demonstrates how to add a new kind of visualizer to VR-Vantage.

To demonstrate, we'll add the ability to draw bullet detonations as bullet hole decals on the terrain.

To draw the bullet hole decals we can use a distributed polygon model. Since the simulation often doesn't correlate perfectly with the terrain loaded in VR-Vantage, we'll also need a scene object updater to set the position of the model so that it lines up perfectly with the terrain. The visualizer itself will read the simulated detonation's state and construct the decal and updater to visualize it.

In the plugin's initialization function, we can register the new visualizer and:

      // Register a new visualizer. This visualizer will display the new mass
      // attribute as a text label.
      DtStateVisualizerFactory::instance(de).addStateVisualizerCreator(de,
         new DtBulletHoleVisualizerCreator);

      // Register the creator for our enhanced polygon model; this will
      // override the default polygon model implementation provided by
      // the vrvOsg library
      DtAgentFactory::instance(de).registerAgentCreator(
         "DtPolygonModel", new DtMovablePolygonModelClassesCreator);

      // Every visualizer has a visual type, identified by a unique integer
      // key and a unique string name. Visualizers can be controlled by type.
      // In this example, we create a new visual type for the bullet holes
      // and use it to add a toolbar button that toggles them on and off.

      // This integer will be the unique key for the bullet hole visual type.
      // VR-Vantage's default visual types are declared in
      // DtObserverSettingsManager::ObserverSetting. It's important when creating
      // a new visual type to pick an integer id that is unlikely to collide with
      // an existing type.
      const int bulletHoleVisualTypeId = 976;

      // Register a new observer setting item to toggle the bullet hole
      // decals on and off. Registering it here allows it to be added to
      // GUI configurations later on.
      DtQtMenuAssembler::instance(de).registerMenu(new DtObserverSettingItem(
         de, "BulletHoleItem", bulletHoleVisualTypeId));

      // Register the new visual type to control the bullet holes
      DtVisualTypeManager::instance(de).registerVisualType(
         DtVisualTypeManager::VisualType("bullet_holes", bulletHoleVisualTypeId,
         "Bullet Holes", "../examples/exampleBulletHole/BulletHole.png",
         DtVisualTypeManager::ModelSetAll));

      // Add example menu and example toolbar item.
      makVrv::DtVrvApplication* app = makVrv::DtVrvApplication::findFromDe(de);
      if(app)
      {
         app->masterModeMenuConfiguration().addMenuPath(
            DtMenuPath::menu("DtObserverMenu").item("BulletHoleItem"));

         app->masterModeToolbarConfiguration().addToolbarPath(
            DtToolbarPath::toolbar("DtObserverSettingsToolbar").item("BulletHoleItem"));
      }

In the GUI, we can create a new visual definition and map bullet detonations to the bullet hole visualizer. Once this is done, the visualizer will be created whenever the simulation reports a bullet detonation interaction. In turn, the visualizer will create its model and updater according to its visual definition:

      DtBulletHoleVisualizer::DtBulletHoleVisualizer(
         DtBaseConnection& simulation, DtStateListener& listener, int modelSet)
      : DtStateVisualizer(simulation, listener, modelSet)
      , myPolygonAgent(0)
      {
      }

      DtBulletHoleVisualizer::~DtBulletHoleVisualizer()
      {
         destroyModelAgents();
      }

      void DtBulletHoleVisualizer::createModelAgents()
      {
         // Only create the bullet hole if the detonation impacted terrain
         if (((DtVrlinkDetonationInteractionStateListener*)&myListener)->detonationResult()
            == DtDetResGroundImpact)
         {
            createBulletHole();
         }
      }

      void DtBulletHoleVisualizer::destroyModelAgents()
      {
         if (mySceneObjectAgent)
         {
            mySimulation.temporaryEffects().takeAgent(mySceneObjectAgent, 60, 0);
            mySceneObjectAgent = 0;
         }

         if (myPolygonAgent)
         {
            mySimulation.temporaryEffects().takeAgent(myPolygonAgent, 60, 0);
            myPolygonAgent = 0;
         }
      }

      void DtBulletHoleVisualizer::setVisualizerDefinition(const DtVisualizerDefinition& visDef)
      {
         const DtVisualizerAttributeFilename* texFile = dynamic_cast<const DtVisualizerAttributeFilename*>(
            visDef.findAttribute("texture"));
         if (texFile)
         {
            myTexture = texFile->value().toAscii();
         }

         const DtVisualizerAttributeFloat* radius = dynamic_cast<const DtVisualizerAttributeFloat*>(
            visDef.findAttribute("radius"));
         if (radius)
         {
            myRadius = radius->value();
         }
         else
         {
            myRadius = 1;
         }

         DtStateVisualizer::setVisualizerDefinition(visDef);
      }

      void DtBulletHoleVisualizer::setModelDefinitionForAgents()
      {

      }

      void DtBulletHoleVisualizer::setParent(DtStateVisualizer*)
      {
         
      }

      void DtBulletHoleVisualizer::createBulletHole() 
      {
         DtStateVisualizer::createModelAgents();

         DtVrlinkDetonationInteractionStateListener* detStateListener = 
            (DtVrlinkDetonationInteractionStateListener*)&myListener;

         // Create a scene object if necessary
         if(!mySceneObjectAgent)
         {
            mySceneObjectAgent = DtSceneObjectAgent::create(mySimulation.sceneInterface(), myDistributeFlag);
            mySceneObjectAgent->setModelSet(myModelSet);
         }

         // Create the bullet hole polygon and add it to the scene object
         // This will create a DtMovablePolygonModelAgent, since the DtPolygonModelAgent's
         // creator was overridden in this plugin's initialization function
         myPolygonAgent = DtPolygonModelAgent::create(mySimulation.sceneInterface(), myDistributeFlag);
         mySceneObjectAgent->addModel(myPolygonAgent, myVisualizerType);
         
         // Set vertices of the polygon
         // The movable polygon uses it's 0th vertex to position the center of the polygon,
         // and vertices 1...n as the actual polygon vertices.
         // The scene object will set vertex 0 for us when the updater gives it a position
         mySceneObjectAgent->setPosition(1, DtVector( myRadius,  myRadius, 0.1));
         mySceneObjectAgent->setPosition(2, DtVector( myRadius, -myRadius, 0.1));
         mySceneObjectAgent->setPosition(3, DtVector(-myRadius, -myRadius, 0.1));
         mySceneObjectAgent->setPosition(4, DtVector(-myRadius,  myRadius, 0.1));

         // Create a bullet hole updater to find the intersection of the bullet and the terrain,
         // and update the scene object's position and orientation accordingly
         DtBulletHoleUpdaterAgent* updater = DtBulletHoleUpdaterAgent::create(mySimulation.sceneInterface(), myDistributeFlag);
         updater->setSceneObject( mySceneObjectAgent );
         updater->setBulletSource(detStateListener->sourceId());
         updater->setBulletEnd(detStateListener->location());

         // Give the polygon a texture
         myPolygonAgent->setNumTextureTiles(1);
         myPolygonAgent->setTexturePath(myTexture);
      }

      const DtStateVisualizer::TypeInfo& DtBulletHoleVisualizer::typeInfo() const
      {
         return theTypeInfo();
      }

      const DtStateVisualizer::TypeInfo& DtBulletHoleVisualizer::theTypeInfo()
      {

         static DtVisualizerSchema* schema = 0;
         if (!schema)
         {
            schema = new DtVisualizerSchema("DtBulletHoleVisualizer",
               DtUnicode::tr("Detonation Bullet Holes"));

            DtVisualizerSchema::Parameter param;
            param.myName = "texture";
            param.myDescription = DtUnicode::tr("Texture file for the bullet hole decals");
            param.myRequired = true; // Force visualizers to specify a texture
            param.myVisualizerAttributeTypeInfoName = DtVisualizerAttributeFilename::theTypeInfo().className;
            schema->addParameter(param);

            param.myName = "radius";
            param.myDescription = DtUnicode::tr("Radius in meters of the bullet hole decals");
            param.myRequired = false; // defaults to 1m if not specified
            param.myVisualizerAttributeTypeInfoName = DtVisualizerAttributeFloat::theTypeInfo().className;
            schema->addParameter(param);
         }

         static DtStateVisualizer::TypeInfo thisTypeInfo(
            "DtBulletHoleVisualizer",
            DtUnicode::tr("Bullet Hole Visualizer"),
            InteractionVisualizerType,
            *schema);

         return thisTypeInfo;
      }
// end source

To place the bullet hole decal, we will make a vector from the shooter to the detonation and intersect it with the terrain. The bullet hole decal will be placed at the location of that intersection and rotated to have the same normal vector. Since intersections require access to the current scene graph, we use an updater to do the calculation:

   void DtBulletHoleUpdater::update( double simTime )
   {
      if (myDirty)
      {
         // find the location of the scene object with id = bullet source id
         DtVector bulletSource;

         DtAgentUpdateResolverInterface* resolver = 
            myDe.agentManager().findUpdater( mySource );

         if( resolver )
         {
            DtSceneObject* sceneObject = resolver->
               castObjectTypeFromUpdater<DtSceneObject>( "DtSceneObject" );  
            if ( sceneObject )
            {
               sceneObject->getPosition(0, bulletSource);
            }
         }
         
         // Intersect the bullet with the terrain
         DtIntersector& intersector = DtIntersectorManager::instance(myDe).modelSetIntersector( mySceneObject->modelSet() );

         // Find the first intersection along a vector starting at the shot origin
         // and continuing 2x trajectory
         DtVector trajectory = myEnd - bulletSource;
         DtVector segmentEnd = trajectory + trajectory + bulletSource;
         DtIntersectorResult isectResult;
         bool hit = intersector.findFirstIntersectWithLineSegment(
            bulletSource.x(), bulletSource.y(), bulletSource.z(),
            segmentEnd.x(), segmentEnd.y(), segmentEnd.z(),
            isectResult, DtIntersector::SupportNodes);

         if (hit)
         {
            mySceneObject->setVisible(true);         
            mySceneObject->setPosition(0, DtVector(isectResult.x, isectResult.y, isectResult.z));

            // make a taitbryan to rotate from (0, 0, 1) up to intersection normal up
            // so that the bullet hole is parallel to the surface that it hit
            DtTaitBryan orientation;
            DtVector localDown;
            DtVecNeg( DtVector(isectResult.normalX, isectResult.normalY, isectResult.normalZ), localDown );

            // convert Euler to Orientation
            DtDcm localOriDcm( orientation );
            DtVector frontVec;

            // Get front vector
            frontVec[0] = localOriDcm[1][0];
            frontVec[1] = localOriDcm[0][0];
            frontVec[2] = -localOriDcm[2][0];

            // current front X down gives us a right vector
            DtVector rightVec = localDown.crossProduct( frontVec );
            DtVecNormalize( rightVec, rightVec );

            // and then new right cross down gives us a new front vector
            frontVec = rightVec.crossProduct( localDown );
            DtVecNormalize( frontVec, frontVec );   

            // Go from vectors back to DCM 
            DtDcm resultDcm;

            register double* resDcmPtr = &resultDcm[0][0];
            *resDcmPtr++ = frontVec[1];
            *resDcmPtr++ = rightVec[1];
            *resDcmPtr++ = localDown[1];
            *resDcmPtr++ = frontVec[0];
            *resDcmPtr++ = rightVec[0];
            *resDcmPtr++ = localDown[0];
            *resDcmPtr++ = -frontVec[2];
            *resDcmPtr++ = -rightVec[2];
            *resDcmPtr = -localDown[2];

            // turn Dcm back to euler
            DtTaitBryan result;
            DtBodyToRef_to_Euler( resultDcm, &result );

            mySceneObject->setOrientation(0, result);
         }
         else
         {
            mySceneObject->setVisible(false);
         }

         myDirty = false;
      }
   } // end source

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/exampleBulletHole_stealth.bat (on Windows) or ./bin/exampleBulletHole_stealth.sh (on Linux). For more information about running examples, please see Running Applications and Examples.



Copyright © 2005-2012 VT MÄK Inc. All Rights Reserved (www.mak.com)