VR-Forces 4.3 Class Documentation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Add Actuator Component

This subclass of the DtAutomotiveActuatorComponent overrides existing movement behavior and moves the entity due north.


You can override init() to perform any initialization of your model. All components have an init() function that gets invoked whenever an entity that uses your actuator is instantiated.

The type() function returns the string by which the actuator is identified in the object parameters database. In myActuator.h, we do the following:

virtual DtString type() const;

In myActuator.cxx, we do the following:

DtString MyActuatorComponent::type() const
{
   return DtAutomotiveActuatorType;
}

Since we are replacing DtAutomotiveActuatorComponent with the new component, we return the type string normally returned by DtAutomotiveActuatorComponent. Every place in the object parameters database that the DtAutomotiveActuator is called for (with the string automotive-actuator), the new actuator will be used instead. If you create a completely new actuator, you need to give it a name, then modify the object parameters database to reference that name. For example, if MyActuatorComponent was a completely new actuator, it might do the following: In the header file, define the string, as follows:

const char NewActuatorType[] = "new-actuator";

In the source file, do the following:

DtString MyActuatorComponent::type() const
{
  return NewActuatorType;
}

In the .ope files for the affected entities, do the following:

(actuators
   (MyNewActuator
      (component-descriptor-type "automotive-component-descriptor")
      (component-type "new-actuator")
   )
...

Adding a New Entity Behavior (Creating an Actuator) Ticking the Actuator The most important function that you need to override in your derived actuator is tick(). This is where the real work of the actuator gets done. The job of tick() is to compute the state of the vehicle for the current frame, based on:

Header file:

/*******************************************************************************
** Copyright (c) 1999 MAK Technologies, Inc.
** All rights reserved.
*******************************************************************************/
/*******************************************************************************
** $RCSfile: myActuator.h,v $ $Revision: 1.11 $ $State: Exp $
*******************************************************************************/

#ifndef MyActuatorComponent_H_
#define MyActuatorComponent_H_

#include "vrfmodel/automotiveActuatorComponent.h"

class MyActuatorComponent : public DtAutomotiveActuatorComponent
{
public:

   //constructor
   MyActuatorComponent(const DtString& name, DtVrfObject* object, 
      DtSimManager* simManager, DtComponentDescriptor* compDescriptor, 
      DtReaderWriterRegistry* parentRegistry = 0);

   //destructor
   virtual ~MyActuatorComponent();

   //Perform any initialization required by the actuator
   virtual bool init();

   //Returns a string from compTypes.h, identifying the type of component
   virtual DtString type() const;

   //Calculate new kinematic state, and copy it to state repository
   virtual void tick();

public:

   //Creator function to be registered with the DtSimComponentFactory --
   //see main.cxx
   static DtSimComponent* creator(const DtString& name, DtVrfObject* object,
      DtSimManager* simManager, DtComponentDescriptor* compDescriptor,
      DtReaderWriterRegistry* parentRegistry);

};

#endif



Implementation:

/*******************************************************************************
** Copyright (c) 2010 MAK Technologies, Inc.
** All rights reserved.
*******************************************************************************/

#include "myActuator.h"
#include "vrfobjcore/simComponentTypes.h"
#include "vrfcore/simManager.h" 
#include "vrfcore/exerciseClock.h"
#include "vrfobjcore/groundVehicleStateRepository.h"
#include "vrfobjcore/vrfObject.h"
#include "vrfobjcore/inputPortGroup.h"
#include "vrfobjcore/updateRepositoryOutputPortGroup.h"
#include "vrfobjcore/constrainedAnalogIOPort.h"
#include "gdb/terrainDatabase.h"

MyActuatorComponent::MyActuatorComponent(
   const DtString& name,
   DtVrfObject* object, 
   DtSimManager* simManager,
   DtComponentDescriptor* compDescriptor, 
   DtReaderWriterRegistry* const parentRegistry) :
   DtAutomotiveActuatorComponent(name, object, simManager, 
      compDescriptor, parentRegistry)
{
}

MyActuatorComponent::~MyActuatorComponent()
{
}

bool MyActuatorComponent::init()
{
   //Here we just call down to the base init, but we can do any necessary
   //initialization of our model here.
   return DtAutomotiveActuatorComponent::init();
}

DtString MyActuatorComponent::type() const
{
   //We're returning the type name normally returned by VR-Forces's
   //standard DtAutomotiveActuatorComponent.  This is used by most automotive
   //entities in their .OPE/ .SYSDEF files.  This will let us swap this
   //actuator for that standard one easily. If you want to make up 
   //your own name, then modify the .ope file for the affected entities
   //to reference the name of your new actuator. 
   //See chapter 6 of the VRForces Backend developer's guide for more information.
   return DtAutomotiveActuatorType;
}

void MyActuatorComponent::tick()
{
   //The job of this function is to calculate the state of the object for the
   //current frame and set that state in the state repository of the entity
   //we belong to.  You can access the previous state of the entity in that
   //same state repository.  The state repository is accessible as
   //entity()->vrfState().  This function returns a
   //DtVrfObjectBaseStateRepository*, which really points to an instance of a
   //subclass.  If this actuator is used to control ground entities, the
   //state repository will really be a DtGroundVehicleStateRepository
   //(although most of its useful functions are inherited from its parent,
   //DtVrfObjectBaseStateRepository - a base class for the state
   //repositories of all kinds of individual entities.)

   //Another responsibility of tick() is to call the dataReceived() method of
   //its port group before returning.  This in turn calls dataReceived()
   //on each port in the group, which is of particular interest to
   //"event" type ports such as DtTriggerPort which check to see if the
   //data on the port is "new". 

   //This sample actuator models a pretty broken vehicle, one that just
   //travels in a straight line, north, with speed modulated by the throttle
   //input.
	
   if ((! mySimManager) || (! entity()->vrfState()) ||
      (! myProcessState) )
   {
      DtWarn("Ticked an unitialized component: %s\n", name().string());
      myAutomotiveControlPortGroup->dataReceived();
      return;
   }
      
   if (! mySimManager->exerciseClock())
   {
      DtWarn("Unitialized sim manager detected by component: %s\n",
         name().string());
      myAutomotiveControlPortGroup->dataReceived();
      return;
   }

   if (dT() == 0.)
   {
      //No simulation needs to happen, since no time has passed.  
      myAutomotiveControlPortGroup->dataReceived();
      return;
   }

   myUpdateRepositoryOutputPortGroup->attemptToActivateAllPorts();

   // If all the ports aren't active no need to continue processing.
   //
   // The base automotive actuator is connected to an "update-repository" actuator that
   // does nothing more than set values in the state repository of the entity.
   // Using that repository allows a different actuator to be created and
   // constructed alongside the default movement actuator, possibly for 
   // different conditions; that alternate actuator can test for these
   // conditions and if they are met, it can activate its ports to the 
   // update-repository actuator and take control of entity movement.
   if (!myUpdateRepositoryOutputPortGroup->allPortsActive())
   {
      return;
   }

   //Grab a pointer to the state repository that we will use to get and set
   //current state.
   DtVrfObjectStateRepository* groundSR = entity()->vrfState();

   //You can use DtGroundVehicleStateRepository's member functions to get the
   //current state of the entity.  
   DtVector lastPosition = groundSR->localPosition();
   DtVector lastVelocity = groundSR->localVelocity();

   //You can get control input values from the input port.  The throttle port 
   //is an element of the DtAutomotiveActuatorComponent and the infrastructure
   //necessary to initialize and link it to the appropriate controller has already
   //been established in the class we've derived from.  Its purpose is to return
   //a throttle value between 0 and 1.  In this simple example, we're just 
   //interpreting throttle as indicating desired speed as a fraction of max 
   //speed (here 10.0).

   double speed = 0;
   if (myThrottlePort)
   {
      speed = myThrottlePort->value() * 10.0;
   }

   //Let's do our simple simulation, moving north in a straight line at a
   //speed proportional to current throttle position.
   
   //Create a vector representing the velocity this tick.
   //Initially in topographic coordiantes (x coordinate points north).
   DtVector newVelocity(speed, 0.0, 0.0);
   
   //Get the rotation matrix for transforming from topographic to local
   //(database) coordinates.
   DtDcm topoToLocalDcm;
   terrainAttachedTo()->coordinateSystem()->topoToLocal(
      lastPosition, topoToLocalDcm);

   //Transform velocity from topographic to local(database) coordinates.
   DtDcmVecMul(topoToLocalDcm, newVelocity, newVelocity);
         
   DtVector displacementThisTick;
   DtVecScale(newVelocity, dT(), displacementThisTick);

   //Compute updated position this tick
   DtVector newPosition = lastPosition + displacementThisTick;

   //Clamp the new position to the terrain. 
   bool dataAvailable = false;
   terrainAttachedTo()->closestIntersection(newPosition, newPosition, false,
      &dataAvailable);
   if (!dataAvailable)
   {
      DtWarn << "Unable to place entity at " << newPosition.string() <<
         std::endl;
   }

   DtDcm newLocalOrientation;
   DtVector newLocalPosition;

   //! Calculate new pitch/roll clamped to terrain
   calculateOrientationAndPosition(newPosition, 
                                    0., 
                                    newLocalOrientation, 
                                    newLocalPosition);

   DtTaitBryan newLocalOrientationTb;
   DtBodyToRef_to_Euler(newLocalOrientation, &newLocalOrientationTb);

   //Now, update the entity's state repository with the new position and
   //velocity.
   myUpdateRepositoryOutputPortGroup->setLocation(newPosition);
   myUpdateRepositoryOutputPortGroup->setVelocity(newVelocity);
   myUpdateRepositoryOutputPortGroup->setOrientation(newLocalOrientationTb);
   
   //Remember to notify ports that we have received the data on them.
   myAutomotiveControlPortGroup->dataReceived();

   // This is optional. This allows another, lower-priority controller
   // to connect to the update-repository actuator, but if there
   // isn't any this actuator will stay connected.
   myUpdateRepositoryOutputPortGroup->allowDeactivation();
}

//This is the creation function we register with the component factory.
DtSimComponent* MyActuatorComponent::creator(const DtString& name,
   DtVrfObject* vrfObject, DtSimManager* simManager, 
   DtComponentDescriptor* compDescriptor,
   DtReaderWriterRegistry* parentRegistry)
{
   return new MyActuatorComponent(name, vrfObject, simManager, compDescriptor,
      parentRegistry);
}


Document ID: Generated on Wed Mar 11 21:20:57 EDT 2015 from SVN revision 150940
Copyright © 2005-2014 VT MÄK. All Rights Reserved (www.mak.com)