Tutorial 9: Blueprints and OPAL XML Files

 

 

Note: See a list of example OPAL XML files at the end of this page.

 

This tutorial covers Blueprints and OPAL XML files, two features that are useful when dealing with complex physical objects containing several different components (e.g. vehicles, ragdolls, game levels, etc.)  Blueprints are collections of data objects that describe Solids, Joints, Motors, and Sensors.  They can be setup by manually creating data objects and adding them to the Blueprint, by copying data from existing Solids, Joints, etc., or by loading them from OPAL XML files.  Objects that refer to others (e.g. a Joint refers to two Solids) can only refer to objects within the same Blueprint.  Instantiating a Blueprint returns a BlueprintInstance object that contains pointers to all the instantiated objects within the Blueprint; this will be described towards the end of the tutorial.

 

Creating Blueprints Manually

The first example here shows how to create a Blueprint totally from scratch and instantiate it:

 

 

opal::Simulator* sim = opal::createSimulator();

 

// Create a SolidData object.

opal::SolidData solid1Data;

solid1Data.name = “object1”;

opal::BoxShapeData boxData;

solid1Data.addShape(boxData);

 

// Create another SolidData object.

opal::SolidData solid2Data;

solid2Data.name = “object2”;

solid2Data.translate(0.0, 1.5, 0.0);

opal::CapsuleShapeData capsuleData;

solid2Data.addShape(capsuleData);

 

// Create a JointData object.

opal::JointData jointData;

jointData.setType(opal::HINGE_JOINT);

jointData.solid0BlueprintRefName = solid0;

jointData.solid1BlueprintRefName = solid1;

jointData.anchor = opal::Point3r(0.0, 0.75, 0.0);

jointData.axis[0].direction = opal::Vec3r(1.0, 0.0, 0.0);

 

// Setup a Blueprint.

opal::Blueprint bp;

bp.addSolid(&solid1Data);

bp.addSolid(&solid2Data);

bp.addJoint(jointData);

bp.finalize();

 

// Instantiate the Blueprint.

opal::BlueprintInstance instance;

sim->instantiateBlueprint(instance, bp);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Instantiating the Blueprint will create instances of all the objects contained in the Blueprint.  The call to “finalize” is necessary to let the Blueprint setup internal indices used for references to objects within the Blueprint.  The Blueprint cannot be modified after calling “finalize.”

 

Note the use of object names here.  All references to objects within a Blueprint must use string names.  If the JointData object here contained invalid strings for the Solid names, the Joint could not be instantiated later.  All string names for a certain type of object must be unique within a given Blueprint.  The Blueprint ignores pointer references; for example, the JointData object contains two Solid pointers, but the Blueprint ignores these and only uses the “solid0BlueprintRefName” and “solid0BlueprintRefName” strings.

 

The second example shows how to create a Blueprint by cloning existing objects:

 

 

opal::Simulator* sim = opal::createSimulator();

opal::Solid* solid1 = sim->createSolid();

opal::Solid* solid2 = sim->createSolid();

opal::Solid* joint = sim->createJoint();

 

...

 

// Get a SolidData object from an existing Solid.  It is assumed that the

// Solid’s name has been set via “Solid::setName”.

opal::SolidData solid1Data = solid1->getData();

 

// Get a SolidData object from another existing Solid.  It is assumed that

// the Solid’s name has been set via “Solid::setName”.

opal::SolidData solid2Data = solid2->getData();

 

// Get a JointData object from an existing Joint.

opal::JointData jointData = joint->getData();

 

// Setup a Blueprint.

opal::Blueprint bp;

bp.addSolid(&solid1Data);

bp.addSolid(&solid2Data);

bp.addJoint(jointData);

bp.finalize();

 

// Instantiate the Blueprint.

opal::BlueprintInstance instance1, instance2;

sim->instantiateBlueprint(instance1, bp);

opal::Matrix44r offset;

offset.translate(5, 0, 0);

sim->instantiateBlueprint(instance2, bp, offset);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Besides the use of existing data objects, this example contained a notable difference from the previous one: it instantiated the Blueprint twice, each time at a different location.  The “instantiateBlueprint” function takes an offset matrix (and a real value for scaling, not shown here) that can affect everything in the Blueprint instance at once.

 

 

Loading Blueprints from OPAL XML Files

OPAL XML files can be used to describe all parameters for Solids, Joints, Motors, and Sensors.  Each file can contain a single Blueprint.  Rather than describe the complete specification for these XML files, this tutorial contains an example that shows every possible XML element and attribute in the OPAL XML files (see the “example.xml” file in the section below).  Most XML elements are optional; the only required elements are objects’ “types” and string references to other objects (e.g. a Joint’s references to two Solids).

 

Here we load a Blueprint from a file and instantiate it:

 

 

opal::Simulator* sim = opal::createSimulator();

opal::Blueprint sailboatBP;

 

// Load the Blueprint from a file.

opal::loadFile(sailboatBP, “sailboat.xml”);

 

// Instantiate the Blueprint.

opal::BlueprintInstance instance;

sim->instantiateBlueprint(instance, sailboatBP);

 

 

 

 

 

 

 

 

 

 

 

 

If a Blueprint loaded from a file is to be instantiated multiple times, it is wise to keep the Blueprint around until you are done with it to keep from loading the file multiple times.

 

Getting Access to Instantiated Object Pointers

Once a Blueprint is instantiated, you may want to get pointers to all the individual objects that were just created.  There are two ways to do this: getting a pointer by name and looping over the pointers.  The following examples demonstrate these methods.

 

Here we will load a Blueprint from a file, instantiate it, and get pointers to specific objects by name (note that the objects must have been given names in order to find them after instantiation):

 

 

opal::Simulator* sim = opal::createSimulator();

opal::Blueprint sailboatBP;

 

// Load the Blueprint from a file.

opal::loadFile(sailboatBP, “sailboat.xml”);

 

// Instantiate the Blueprint.

opal::BlueprintInstance instance;

sim->instantiateBlueprint(instance, sailboatBP);

 

opal::Solid* hullSolid = instance.getSolid(“hull”);

opal::Solid* mastSolid = instance.getSolid(“mast”);

opal::ThrusterMotor* engine = NULL;

engine = (opal::ThrusterMotor*)instance.getMotor(“main engine”);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

The next example loops through every Solid in the instance, creating and storing an application-specific object for each:

 

 

opal::Simulator* sim = opal::createSimulator();

opal::Blueprint sailboatBP;

 

// Load the Blueprint from a file.

opal::loadFile(sailboatBP, “sailboat.xml”);

 

// Instantiate the Blueprint.

opal::BlueprintInstance instance;

sim->instantiateBlueprint(instance, sailboatBP);

 

for (int i=0; i<instance.getNumSolids(); ++i)

{

    opal::Solid* solid = instance.getSolid(i);

    PhysicalPart* newPart = new PhysicalPart();

    newPart->setOPALSolid(solid);

    AppSceneManager::addPart(newPart);

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

If a Blueprint loaded from a file is to be instantiated multiple times, it is wise to keep the Blueprint around until you are done with it to keep from loading the file multiple times.

 

 

Example OPAL XML Files

  • example.xml: Contains examples of every object type and parameter.  This is to be used as a reference that shows all the available elements and attributes.  It is a valid file that OPAL can load, but it isn’t very useful to instantiate.

 

  • ragdoll.xml: Contains a ragdoll Blueprint of medium complexity.

 

OPAL is Copyright © 2004-2005 Alan Fischer, Andres Reinot, and Tyler Streeter