C++ Script

  1. Basics
  2. EventIn Function Declaration
  3. Node Field Slots Declaration
  4. Sending EventOut Messages
  5. Sending Events to Nodes by Reference
This tutorial explains how to create dynamic link libraries that can be integrated as a script into a VRML file. (sources: cppscript.cpp, cppscript.wrl. To view this example load the cppscript.wrl file with the glutCyberle VRML-Browser from the previous tutorial. To compile the script with the gun compiler for Linux platforms use:
g++ -shared -loaw_misc -I'../../lib/open-activewrl/include' -L'../../lib' cppscript.cpp -o cppscript.so
You have to link liboaw_misc.a to the shared object, because static libraries for the glutCyberle VRML-browser are used. Unfortunately the gnu compiler doesn't give warnings if liboaw_misc.a is missing. The error will occure at runtime and eventIn functions, which use functions from libaow_misc.a can't be evaluated.

Basics

In the url field of the script node you can specify the name of the dll/so file that has to be loaded as a script. Usually for dynamic link libraries the prefix .dll is used on windows platforms and the prefix .so is used on linux platforms. If the prefix .dll or .so is given to the url of the script node it will be replaced by the common prefix of the platform the oaw component is been executed. In this way the same wrl file can be used on different platforms while the different dynamic linked libraries lie in the same folder.

EventIn Function Declaration

Every single eventIn field has to match a exported function within the cpp file.
DLLEXPORT void fractionIn(const SFFloat& f, Params* pParams, const SFTime& t){
..
}
The DLLEXPORT macro is defined on each platform appropriately. Every eventIn function has to have three parameters. First one for the value of the same type in the script declaration. Second one reference to a Params struct and third the one parameter for the time of execution. Through the members of the Params struct field the eventIn function has access to the browser-object and the fields. The struct Params has to be declared as follows:
struct Params{
  SFNode* thisProto;         //Reference to proto to which the script belongs.
  CybSAI::ScriptC* script;   //Reference to the script object interface
  CybSAI::BrowserC* browser; //Reference to the browser object interface
  ScriptFields* pFields;     //Reference to the scripts fields.
  EventOutFlags* pOutFlag;   //Reference to EventOut Flags that have to be set if a eventOut has changed
  EventOuts*  pOut;          //Reference to the EventOut values.
  ScriptData* pScriptData;   //Reference to private script data.
};

Node Field Slots Declaration

The Params struct itself contains references to structs of the field slots and the eventOut slots of the script. There are to references to structs concerning the eventOut slots of the script. First, one struct for the values and one for flags that can be set to indicate the instance of script node which eventOut values has been changed during processing. The members of the ScriptFields, EventOuts and EventOutFlags have to be declard in the same order as they are in the script declaration of the VRML file.
In the example of this tutorial the script is declared like this:
DEF SCRIPT Script{
    eventIn SFFloat fractionIn
    eventOut SFVec3f posOut
    field SFFloat amplitude 0.5
    field SFNode transBall2 USE        T2
    field SFNode transBall3 USE        T3
    field SFNode transBall4 USE        T4
    field SFNode transBall5 USE        T5
    url "cppscript.so"
}
For this script the following structs are needed:
struct EventOuts{
  SFVec3f     *posOut;
};

struct EventOutFlags{
  short posOut;
};

struct ScriptFields{
  SFFloat* amplitude;
  SFNode* ball2Trans;
  SFNode* ball3Trans;
  SFNode* ball4Trans;
  SFNode* ball5Trans;
};
The members of the struct again are references to the slot values. The EventOutFlags are of the type short.

Sending EventOut Messages

The recommended way to send an event out is through the script object interface in the params data struct.
  double p = FIELD(amplitude)*sin(2*PI*f);
  //this is the new and recommended way to send eventouts
  pParams->script->SendEventOutByName("posOut",&SFVec3f(0,0,-p));
The old way to instruct the script to route a value of an eventOut slot was to copy the value to the member of the EventOut data structure and to set the appropriate member of the EventOutFlags data structure to true. Anyhow there occure problems on windows plattforms if the execution component of this script is not compiled and linked with MFC as dynamic link libraries. To be complient on all plattforms it is therefor not recommended to use this way.
  pParams->pOut->posOut->z = -p;
  pParams->pOutFlag->posOut = 1;
Like can be seen by the amplitude value there exists a FIELD macro.

Sending Events to Nodes by Reference

If you got the reference of a node, you can also send events to this node directly. This is shown in the following code segment.
//You can change the values of node by reference like this:
long fieldID = FIELD(ball2Trans).GetFieldEventID("translation");
SFVec3f v1(p,0,p);
FIELD(ball2Trans).SetEvent(fieldID,&v1);
First a id number of the field that you want to access has to be retrieved. With this id number the event can be send to the node through the SetEvent function. This way is more appropriate if you want to change the value of the field several times during simulation. But more comfortable is to us the SetEventByName function.
SFVec3f v2(-p,0,p);
FIELD(ball3Trans).SetEventByName("translation",&v2);