Whenever a connection is made between fields, inputs, or outputs and the types aren't the same, Inventor tries to insert an engine to convert from the source type to the destination type. Inventor maintains a table that lists the class of engine that converts between a given pair of types.
You can define new converters and add them to the table, either to support additional conversions between built-in field types, or to support conversions to or from new fields you have created (see Chapter 3). For convenience, a single class of engine can support several different types of conversions (for example, a single engine may be able to convert from a rational number field to a float, a short, or a long). When Inventor creates an instance of the field converter, it tells it the source and destination field types.
Creating a field converter is similar to creating other types of engines, with a few additional steps. The following checklist summarizes things you need to do when you create a new field converter.
Select a name for the new field converter class and determine what class it is derived from.
Define and name each input and output for the engine, as for a standard engine.
Define the constructor, destructor, and evaluate() method, as for a standard engine.
Declare two required virtual methods:
virtual SoField *getInput(SoType type);
virtual SoEngineOutput *getOutput(SoType type);
These methods are called by Inventor to obtain the input and output it will connect to and from for a particular conversion.
Implement the initClass() method. For each conversion that the engine supports, after the SO_ENGINE_INIT_CLASS() macro, call
SoDB::addConverter(typeIdOfSourceField, typeIdOfDestinationField, YourEngine::getClassTypeId());
If the converter has multiple inputs or outputs, its evaluate() method can check input.isConnected() and output.getNumConnections() to find out which conversion needs to be done. Or, the getInput() and getOutput() methods can save their parameters in instance variables and the evaluate() method can check them.
The getInput() and getOutput() methods check the passed type and return the appropriate input or output. It is guaranteed that Inventor will never call these methods except for pairs of types registered with SoDB::addConverter() .
Example 6.11, “ ConvertSFShortToSFFloat.h ” shows the header file for a new field converter, ConvertSFShortToSFFloat. Example 6.12, “ ConvertSFShortToSFFloat.cxx ” shows the source code for this class.
Example 6.11. ConvertSFShortToSFFloat.h
#include <Inventor/engines/SoFieldConverter.h> #include <Inventor/fields/SoSFShort.h> #include <Inventor/fields/SoSFFloat.h> class ConvertSFShortToSFFloat : public SoFieldConverter { public: SO_ENGINE_HEADER(ConvertSFShortToSFFloat); // Input: SoSFShort input; // Output: SoEngineOutput output; // (SoSFFloat) // Initialization static void initClass(); static void exitClass(); // Constructor ConvertSFShortToSFFloat(); private: // Destructor virtual ~ConvertSFShortToSFFloat(); // Evaluation method virtual void evaluate(); // These must be defined for a field converter. They return // the input and output connections of the given types. In // our case, we have only one input and one output, so we // know that those will be the given types. virtual SoField * getInput(SoType type); virtual SoEngineOutput * getOutput(SoType type); };
Example 6.12. ConvertSFShortToSFFloat.cxx
#include <Inventor/SoDB.h> #include "ConvertSFShortToSFFloat.h" SO_ENGINE_SOURCE(ConvertSFShortToSFFloat); // Initializes the ConvertSFShortToSFFloat class. void ConvertSFShortToSFFloat::initClass() { SO_ENGINE_INIT_CLASS(ConvertSFShortToSFFloat, SoFieldConverter, "FieldConverter"); // Register this converter's type with the Inventor database // to convert from a field (or engine output) of type // SoSFShort to a field of type SoSFFloat. // We only call this once, since this engine offers only one // type conversion. SoDB::addConverter(SoSFShort::getClassTypeId(), SoSFFloat::getClassTypeId(), getClassTypeId()); } void ConvertSFShortToSFFloat::exitClass() { SoDB::removeConverter(SoSFShort::getClassTypeId(),SoSFFloat::getClassTypeId()); SO_ENGINE_EXIT_CLASS(ConvertSFShortToSFFloat); } // Constructor ConvertSFShortToSFFloat::ConvertSFShortToSFFloat() { // Do standard constructor tasks SO_ENGINE_CONSTRUCTOR(ConvertSFShortToSFFloat); // Define input field and its default value SO_ENGINE_ADD_INPUT(input, (0)); // Define the output, specifying its type SO_ENGINE_ADD_OUTPUT(output, SoSFFloat); } // Destructor. Does nothing. ConvertSFShortToSFFloat::~ConvertSFShortToSFFloat() { } // This is the evaluation routine. void ConvertSFShortToSFFloat::evaluate() { // Get the input value as a short, convert it to a float, and // output it float value = (float) input.getValue(); SO_ENGINE_OUTPUT(output, SoSFFloat, setValue(value)); } // This returns the input field for the given type. Since we // have only one input field, we don't have to check the type. SoField * ConvertSFShortToSFFloat::getInput(SoType) { return &input; } // This does the same for the output. SoEngineOutput * ConvertSFShortToSFFloat::getOutput(SoType) { return &output; }