Creating New FieldContainer Classes

Most developers who use OpenSG as a scene-graph library will probable never create their own OpenSG FieldContainer classes. Similar to widget libs (e.g. qt, gtk) people just use instances (the widgets) but never create new classes.

However, you can always extend the type system of OpenSG to integrate new cores (e.g. a fancy LOD switch) or application specific FieldContainers.

FieldContainers are the system's central mechanisms to deal with any kind of thread safe data (see Fields & Field Containers). Therefore, the class declaration must include various extra meta information for the field and FieldContainer type handling.

In most systems (e.g. Inventor), you would probably start writing a new class or node by just 'copy and paste'-ing an existing implementation. However, since OpenSG needs all this extra meta data it is not a simple but very error prone process to create the field container source by hand. Instead, we provide a graphical tool to create and manage the FieldContainer description and implementation.

The basic idea is that you use the 'field container description editor' ($OSGROOT/Tools/fcdEdit) to create an XML file including the description of your FieldContainer fields and interfaces.

The tool is also able to create all necessary C++ source files. The FieldContainer code is split into classes (e.g. for a Foo FieldContainer: FooBase and Foo). This strategy has various advantages:

XML Description (Foo.fcd)

Includes all field and meta descriptions for a single FieldContainer. Can be read and written by the fcdEdit tool. You should only change it by hand when you're sure of what you're doing.

Field Types (FooFields.h)

Include the field and pointer declarations the the FieldContainer to be used in other FieldContainers as reference. You should not change the file by hand.

Base/Meta Type (FooBase.h, FooBase.inl,

FooBase.cpp)

Holds all the meta and field information. Do not change it by hand. Use the fcdEdit tool to create the files anew whenever you change the XML description.

'User Code' implementation (Foo.h,

Foo.inl, Foo.cpp)

Holds the 'user code'. The fcdEdit is able to create a skeleton for your FieldContainer implementation. The code does not include any meta information, therefore, it is not necessary to create it anew whenever you change the interface.

Include new action handlers or whatever you need as functionality.

fcdEdit-numbered.png

fcdEdit

A quick explanation of the different buttons/input fields:

  1. The name of the FieldContainer. Should follow the capitalization rules.

  2. Defines whether the FC is a part of the OpenSG system. This mainly influences the way system headers are included (with or without the OpenSG/ prefix).

  3. Defines whether the FC can be decorated. Currentl only used for osg::CameraDecorator classes, but usable in general. The main effect is to turn all Field access emthods into virtual functions, to allow overriding them, and creating the necessary default Decorator classes.

  4. The name of the parent class of the FC.

  5. Defines if the parent class is a system component.

  6. The name of the library this FC will be included in. If it is only to be used in application programs, just leave it blank and the appropriate code for an application component will be generated.

  7. Define whether Field types for pointers to this type of FC should be created.

  8. Defines whether this FC is abstract or concrete (i.e. whether it can be instantiated or not).

  9. A general description of this FC. It will be included in the actual FC code. As this code cannot be regenerated changing this description after the code has been generated will not make a difference.

  10. A list of the Fields of this FC.

  11. The name of the currently selected Field.

  12. The type of the currently selected Field. There is a list of predefined types that can be selected from, but it is also possible to just write the type of the Field here.

  13. The cardinality of the Field, i.e. if it's a single (osg::SField) or multi element (osg::MField) field.

  14. The access type of the field: private, protected or public. In general Fields should be public, if they are internal they should be protected.

  15. The visibility of the Field mainly influences whether the Field is written to files etc. Fields that have no meaning beyond the current execution of the program should be internal, Fields that hold configuration data should be external.

  16. The header containing the declaration of the Field's type. If left empty a name is guessed, using the name OSG<type>Fields.h. For type names ending in Ptr (the default for FieldContainerPtr types) the Ptr is removed. This works for all system types and for standard user-defined types. For other types, just enter the header file name into this field, excluding the delimiters.

  17. The default value of the Field. Used to initialize the Field, and put verbatim into the constructor definition for the given Field (e.g. FC::FC() : Field(default) { })

  18. The header needed to declare the default value. Useful to access enum or constants from external files (like opengl.h).

  19. A short description of the Field's contents and function. Will be put into the code documentation.

  20. Creates a new Field.

  21. Deletes a Field.

  22. Clones a Field.

  23. Moves the Field up in the list.

  24. Moves the Field down in the list.

  25. Clear the whole system, start from scratch.

  26. Load an .fcd file.

  27. Currently unused.

  28. Save the current settings to the .fcd file.

  29. Write the FieldContainer Base code. This can also be done by calling fcdEdit with the -b command line option and the .fcd file. It is safe to do that, as no user changes should be done to the Base code.

  30. Currently unused.

  31. Currently unused.

  32. Save the current settings to a different .fcd file.

  33. Write the actual FieldContainer code. This can also be done by calling fcdEdit with the -f command line option and the .fcd file. This will not keep any changes made to the FC code, and thus can quickly destroy whatever work has been done to it. To prevent that from happening on accident, it will not overwrite existing files.

  34. Minimal info about the progam.

  35. Leave the program.

Prototype Replacement

If you create a replacement for a system component, e.g. a smarter DistanceLOD node that can handle predictive LOD selection, and want the system to use your version of the DistanceLOD from now on you can do that. Internally all field container instances are created by cloning a prototype instance. You can access the prototype for a given FieldContainer via its class type which you can access using FC::getClassType(). The class type has a setPrototype() method to assign the prototype.

Be careful to only replace the prototype with classes derived from the original class, or the behavior of the system is undefined.

Initialization / Deinitalization

As OpenSG uses object replication for thread-safe data, constructors and destructors are not always the right place for initialisation and deletion anymore. They are called for every aspect, which usually is more than once. For initialisation that should be done only once per object, the onCreate() method can be used. Similarly, onDestroy() is called once, when the object is destroyed.

There is also a difference between the constructors. The copy constructor is called for every aspect of an object, the default constructor is only called once, during the static init phase, to create the initial prototype instance for the class. As code running in the static init phase faces some restrictions (e.g. the order of initializations is undefined, thus any other object might not yet have been initialized) a saver way to do class-global initializations was added. The initMethod() method is called during osgInit(), which is after all static inits are done.

As a summary here's a list of when which method is called:

onCreate() and on Destroy() are also called for the initial prototype creation. Not all prototypes might need the resources a real instance needs, and initial prototype creation is run during static init, where it might not be safe to access other classes. To allow a destinction between prototype creation and the standard running state of the system there's a global variable GlobalSystemState, which will be set to Startup during static init and Running after osgInit() is finished. During osgExit() it will be set to Shutdown.


Generated on 8 Feb 2010 for OpenSG by  doxygen 1.6.1