| 1 |
#include <OSGConfig.h> |
|---|
| 2 |
|
|---|
| 3 |
using namespace OSG; |
|---|
| 4 |
|
|---|
| 5 |
/*! \defgroup GrpSystemFieldContainer Field Container |
|---|
| 6 |
\ingroup GrpSystem |
|---|
| 7 |
*/ |
|---|
| 8 |
|
|---|
| 9 |
/*! \defgroup GrpSystemFieldContainerFuncs Field Container Functions |
|---|
| 10 |
\ingroup GrpSystem |
|---|
| 11 |
*/ |
|---|
| 12 |
|
|---|
| 13 |
/*! \defgroup GrpSystemMultithreading Multithreading |
|---|
| 14 |
\ingroup GrpSystem |
|---|
| 15 |
*/ |
|---|
| 16 |
|
|---|
| 17 |
/*! \page PageSystemFieldsNFieldContainers Fields & Field Containers |
|---|
| 18 |
|
|---|
| 19 |
\latexonly Starter:NewChapter \endlatexonly |
|---|
| 20 |
|
|---|
| 21 |
One central goal in OpenSG's design is easy to use thread-safe data. To do |
|---|
| 22 |
that right, you need to replicate the data so that every thread can have its |
|---|
| 23 |
private copy (called aspect) to work on. At some point these different copies |
|---|
| 24 |
will have to be synchronized, and then the parts that actually changed need to |
|---|
| 25 |
be copied from one aspect to another. To do that, the system needs to know |
|---|
| 26 |
what actually changed. As C++ is not reflective, i.e. the classes cannot tell |
|---|
| 27 |
the system which members they have, OpenSG needs to keep track of the changes. |
|---|
| 28 |
That's what Fields and FieldContainers are for. |
|---|
| 29 |
|
|---|
| 30 |
\section PageSystemFCInstance Creating a FieldContainer instance |
|---|
| 31 |
|
|---|
| 32 |
FieldContainer can be created in two ways: By using the FieldContainerFactory |
|---|
| 33 |
or from the class's prototype. You cannot create instances of FieldContainers |
|---|
| 34 |
neither by creating automatic or static variables nor by calling new. You have |
|---|
| 35 |
to use the mentioned two ways. |
|---|
| 36 |
|
|---|
| 37 |
For generic loaders it is useful to create an object by name, and this is what |
|---|
| 38 |
the factory is for. The factory is a singleton, the single instance can be |
|---|
| 39 |
accessed via FieldContainerFactory::the(), which has functions to create |
|---|
| 40 |
arbitrary field containers, with some special versions to directly create |
|---|
| 41 |
different subsets of field containers (Nodes, NodeCores, Attachments). |
|---|
| 42 |
|
|---|
| 43 |
For reasons connected to multi-threading (s. [threadsafety]) specific kinds of |
|---|
| 44 |
pointers have to be used. For every FieldContainer type fc there is a specific |
|---|
| 45 |
pointer type fcPtr. It has all the features of a standard pointer, i.e. it can |
|---|
| 46 |
be dereferenced via -> and it can be downcasted to a derived type by |
|---|
| 47 |
DerivedPtr.dcast( ParentPtr );. |
|---|
| 48 |
|
|---|
| 49 |
Creating a new instance of a specific class is done by calling fcPtr |
|---|
| 50 |
var=fcPtr::create(). |
|---|
| 51 |
|
|---|
| 52 |
\section PageSystemRefCount Reference counting |
|---|
| 53 |
|
|---|
| 54 |
FieldContainers are reference-counted. They are created with a reference count |
|---|
| 55 |
of 0, and the reference count can be manipulated through addRefCP() and |
|---|
| 56 |
subRefCP(). |
|---|
| 57 |
|
|---|
| 58 |
The system increases the reference count only when it stores a reference to an |
|---|
| 59 |
object in the system, e.g. when a node is attached to another node. It does |
|---|
| 60 |
not increase the reference counter for every parameter that is passed around, |
|---|
| 61 |
the pointers mentioned in [fcinstance] are not smart pointers. |
|---|
| 62 |
|
|---|
| 63 |
The reference count is decreased when an object is removed from the system, |
|---|
| 64 |
e.g. when a node is detached from another node, or explicitly using |
|---|
| 65 |
subRefCP(). If the reference count goes to or below 0, the object is removed. |
|---|
| 66 |
Note that objects are created with a reference count of zero, so if a new |
|---|
| 67 |
object (refCnt: 0) is attached to a node (increasing the refCnt to 1) and |
|---|
| 68 |
removed later on (decreasing it to 0), it will be destroyed. Increasing the |
|---|
| 69 |
reference count before removing it is needed to prevent the destruction. |
|---|
| 70 |
|
|---|
| 71 |
\section PageSystemFCManip Manipulation |
|---|
| 72 |
|
|---|
| 73 |
The FieldContainer is the basic unit for multi-thread safety. To synchronize |
|---|
| 74 |
changes between different copies of the data the system needs to know when and |
|---|
| 75 |
what changed. |
|---|
| 76 |
|
|---|
| 77 |
This has to be done explicitly by the program. Thus, before changing a |
|---|
| 78 |
FieldContainer beginEditCP(fcPtr, fieldMask); has to be called. After the |
|---|
| 79 |
changes to the FieldContainer are done this also has to be communicated by |
|---|
| 80 |
calling endEditCP(fcPtr, fieldMask);. Here, fcPtr is the pointer to the |
|---|
| 81 |
FieldContainer being changed, fieldMask is a bit mask describing the fields |
|---|
| 82 |
that are changed. |
|---|
| 83 |
|
|---|
| 84 |
Every FieldContainer defines constants for all its fields that can be used to |
|---|
| 85 |
set up this mask. The naming convention is |
|---|
| 86 |
[FieldContainer]::[FieldName]FieldMask, e.g. Geometry::PositionsFieldMask. |
|---|
| 87 |
These masks can be or-ed together to create the full mask of fields that are |
|---|
| 88 |
changed. |
|---|
| 89 |
|
|---|
| 90 |
To simplify the begin/endEdit sequences and make it easier to not forget closing |
|---|
| 91 |
the edit (which can result in pretty surprising error) there is a helper class |
|---|
| 92 |
osg::CPEditor. |
|---|
| 93 |
|
|---|
| 94 |
The CPEditor is an equivalent to the std::auto_ptr in the sense that as it calls |
|---|
| 95 |
the beginEdit as soon as it is created and calls the endEdit as soon as it goes |
|---|
| 96 |
out of scope. |
|---|
| 97 |
|
|---|
| 98 |
\example Use CPEditor for begin/endEdit: |
|---|
| 99 |
|
|---|
| 100 |
\code |
|---|
| 101 |
|
|---|
| 102 |
GeoPTypesPtr type = GeoPTypesUI8::create(); |
|---|
| 103 |
{ |
|---|
| 104 |
CPEditor te(type, GeoPTypesUI8::GeoPropDataFieldMask); |
|---|
| 105 |
|
|---|
| 106 |
type->addValue(GL_POLYGON ); |
|---|
| 107 |
type->addValue(GL_TRIANGLES); |
|---|
| 108 |
type->addValue(GL_QUADS ); |
|---|
| 109 |
} |
|---|
| 110 |
|
|---|
| 111 |
\endcode |
|---|
| 112 |
|
|---|
| 113 |
\endexample |
|---|
| 114 |
|
|---|
| 115 |
As a further (small) simplification there is a CPEdit macro that creates the |
|---|
| 116 |
CPEditor instance automatically. |
|---|
| 117 |
|
|---|
| 118 |
\example Use CPEdit for begin/endEdit: |
|---|
| 119 |
|
|---|
| 120 |
\code |
|---|
| 121 |
|
|---|
| 122 |
GeoPTypesPtr type = GeoPTypesUI8::create(); |
|---|
| 123 |
{ |
|---|
| 124 |
CPEdit(type, GeoPTypesUI8::GeoPropDataFieldMask); |
|---|
| 125 |
|
|---|
| 126 |
type->addValue(GL_POLYGON ); |
|---|
| 127 |
type->addValue(GL_TRIANGLES); |
|---|
| 128 |
type->addValue(GL_QUADS ); |
|---|
| 129 |
} |
|---|
| 130 |
|
|---|
| 131 |
\endcode |
|---|
| 132 |
|
|---|
| 133 |
\endexample |
|---|
| 134 |
|
|---|
| 135 |
|
|---|
| 136 |
|
|---|
| 137 |
\section PageSystemAttachments FieldContainer attachments |
|---|
| 138 |
|
|---|
| 139 |
OpenSG field containers and nodes do not feature an unused pointer to attach |
|---|
| 140 |
data, usually called user data in other systems. Instead, many field |
|---|
| 141 |
containers feature a map to attach specific kinds of field containers called |
|---|
| 142 |
attachments. The most important ones are Nodes and NodeCores, but many other |
|---|
| 143 |
like Window, Viewport, Camera, etc. are derived from AttachmentContainer and, |
|---|
| 144 |
therefore, can carry attachments. |
|---|
| 145 |
|
|---|
| 146 |
Attachments have to be derived from Attachment (see |
|---|
| 147 |
\ref PageSystemFieldContainerExt for |
|---|
| 148 |
details on how to do that). There are also predefined attachments, right now the |
|---|
| 149 |
only one is NameAttachment, which allows assigning a name to the field |
|---|
| 150 |
containers. |
|---|
| 151 |
|
|---|
| 152 |
Every AttachmentContainer can hold an arbitrary number of attachments. |
|---|
| 153 |
Attachments are divided into separate groups, and there can be only one |
|---|
| 154 |
attachment of every group attached to an AC. Most attachments are a group, but |
|---|
| 155 |
if needed new ones can be used as replacements for their parents. |
|---|
| 156 |
|
|---|
| 157 |
\section PageSystemFCThreadsafety Data separation & Thread safety |
|---|
| 158 |
|
|---|
| 159 |
One of the primary design goals of OpenSG is supporting multi-threaded |
|---|
| 160 |
applications. For asynchronous threads that means that every thread might need |
|---|
| 161 |
its private copy of the data. To combine that with easy usability and |
|---|
| 162 |
efficient access we decided to replicate at the field container level. |
|---|
| 163 |
|
|---|
| 164 |
When a field container is created not only one instance is created but |
|---|
| 165 |
multiple, per default 2. These are called aspects, and every running thread is |
|---|
| 166 |
associated with one of them. Whenever data is changed in a thread, only the |
|---|
| 167 |
aspect that's associated with it is changed, the rest is left as is. |
|---|
| 168 |
|
|---|
| 169 |
*/ |
|---|
| 170 |
|
|---|
| 171 |
#if defined(OSG_DO_DOC) || OSG_DOC_LEVEL > 1 |
|---|
| 172 |
|
|---|
| 173 |
/*! \page PageSystemFieldContainerExt Creating New FieldContainer Classes |
|---|
| 174 |
|
|---|
| 175 |
Most developers who use OpenSG as a scene-graph library will probable never |
|---|
| 176 |
create their own OpenSG FieldContainer classes. Similar to widget libs (e.g. |
|---|
| 177 |
qt, gtk) people just use instances (the widgets) but never create new |
|---|
| 178 |
classes. |
|---|
| 179 |
|
|---|
| 180 |
However, you can always extend the type system of OpenSG to integrate new |
|---|
| 181 |
cores (e.g. a fancy LOD switch) or application specific FieldContainers. |
|---|
| 182 |
|
|---|
| 183 |
FieldContainers are the system's central mechanisms to deal with any kind of |
|---|
| 184 |
thread safe data (see \ref PageSystemFieldsNFieldContainers). Therefore, the |
|---|
| 185 |
class declaration must include various extra meta information for the field |
|---|
| 186 |
and FieldContainer type handling. |
|---|
| 187 |
|
|---|
| 188 |
In most systems (e.g. Inventor), you would probably start writing a new class |
|---|
| 189 |
or node by just 'copy and paste'-ing an existing implementation. However, |
|---|
| 190 |
since OpenSG needs all this extra meta data it is not a simple but very error |
|---|
| 191 |
prone process to create the field container source by hand. Instead, we |
|---|
| 192 |
provide a graphical tool to create and manage the FieldContainer description |
|---|
| 193 |
and implementation. |
|---|
| 194 |
|
|---|
| 195 |
The basic idea is that you use the 'field container description editor' |
|---|
| 196 |
($OSGROOT/Tools/fcdEdit) to create an XML file including the description of |
|---|
| 197 |
your FieldContainer fields and interfaces. |
|---|
| 198 |
|
|---|
| 199 |
The tool is also able to create all necessary C++ source files. The |
|---|
| 200 |
FieldContainer code is split into classes (e.g. for a Foo FieldContainer: |
|---|
| 201 |
FooBase and Foo). This strategy has various advantages: |
|---|
| 202 |
|
|---|
| 203 |
\li Type system changes: If the OpenSG core team decides to change the code |
|---|
| 204 |
interface for the FieldContainer type management we can just recreate the base |
|---|
| 205 |
classes from the XML description. No adaptations 'by hand' are needed. |
|---|
| 206 |
|
|---|
| 207 |
\li Interface changes: If you would like to change the interface of your |
|---|
| 208 |
FieldContainer (e.g. add another field) later on you can just re-edit the XML |
|---|
| 209 |
file in fcdEdit and recreate the base classes. All necessary access methods |
|---|
| 210 |
are created automatically. |
|---|
| 211 |
|
|---|
| 212 |
\section PageSystemFieldContainerExtFCD XML Description (Foo.fcd) |
|---|
| 213 |
|
|---|
| 214 |
Includes all field and meta descriptions for a single FieldContainer. Can be |
|---|
| 215 |
read and written by the fcdEdit tool. You should only change it by hand when |
|---|
| 216 |
you're sure of what you're doing. |
|---|
| 217 |
|
|---|
| 218 |
\section PageSystemFieldContainerExtFieldTypes Field Types (FooFields.h) |
|---|
| 219 |
|
|---|
| 220 |
Include the field and pointer declarations the the FieldContainer to be used |
|---|
| 221 |
in other FieldContainers as reference. You should not change the file by hand. |
|---|
| 222 |
|
|---|
| 223 |
\section PageSystemFieldContainerExtBase Base/Meta Type (FooBase.h, FooBase.inl, |
|---|
| 224 |
FooBase.cpp) |
|---|
| 225 |
|
|---|
| 226 |
Holds all the meta and field information. Do not change it by hand. Use the |
|---|
| 227 |
fcdEdit tool to create the files anew whenever you change the XML |
|---|
| 228 |
description. |
|---|
| 229 |
|
|---|
| 230 |
\section PageSystemFieldContainerExtUser 'User Code' implementation (Foo.h, |
|---|
| 231 |
Foo.inl, Foo.cpp) |
|---|
| 232 |
|
|---|
| 233 |
Holds the 'user code'. The fcdEdit is able to create a skeleton for your |
|---|
| 234 |
FieldContainer implementation. The code does not include any meta information, |
|---|
| 235 |
therefore, it is not necessary to create it anew whenever you change the |
|---|
| 236 |
interface. |
|---|
| 237 |
|
|---|
| 238 |
Include new action handlers or whatever you need as functionality. |
|---|
| 239 |
|
|---|
| 240 |
\image html fcdEdit-numbered.png "fcdEdit" |
|---|
| 241 |
|
|---|
| 242 |
\image latex fcdEdit-numbered.eps "fcdEdit" width=8cm |
|---|
| 243 |
|
|---|
| 244 |
A quick explanation of the different buttons/input fields: |
|---|
| 245 |
|
|---|
| 246 |
<ol> |
|---|
| 247 |
|
|---|
| 248 |
<li>The name of the FieldContainer. Should follow the capitalization rules.</li> |
|---|
| 249 |
|
|---|
| 250 |
<li>Defines whether the FC is a part of the OpenSG system. This mainly |
|---|
| 251 |
influences the way system headers are included (with or without the OpenSG/ |
|---|
| 252 |
prefix). </li> |
|---|
| 253 |
|
|---|
| 254 |
<li>Defines whether the FC can be decorated. Currentl only used for |
|---|
| 255 |
osg::CameraDecorator classes, but usable in general. The main effect is to turn |
|---|
| 256 |
all Field access emthods into virtual functions, to allow overriding them, and |
|---|
| 257 |
creating the necessary default Decorator classes.</li> |
|---|
| 258 |
|
|---|
| 259 |
<li>The name of the parent class of the FC.</li> |
|---|
| 260 |
|
|---|
| 261 |
<li>Defines if the parent class is a system component.</li> |
|---|
| 262 |
|
|---|
| 263 |
<li>The name of the library this FC will be included in. If it is only to be |
|---|
| 264 |
used in application programs, just leave it blank and the appropriate code for |
|---|
| 265 |
an application component will be generated.</li> |
|---|
| 266 |
|
|---|
| 267 |
<li>Define whether Field types for pointers to this type of FC should be |
|---|
| 268 |
created.</li> |
|---|
| 269 |
|
|---|
| 270 |
<li>Defines whether this FC is abstract or concrete (i.e. whether it can be |
|---|
| 271 |
instantiated or not).</li> |
|---|
| 272 |
|
|---|
| 273 |
<li>A general description of this FC. It will be included in the actual FC |
|---|
| 274 |
code. As this code cannot be regenerated changing this description after the |
|---|
| 275 |
code has been generated will not make a difference.</li> |
|---|
| 276 |
|
|---|
| 277 |
<li>A list of the Fields of this FC.</li> |
|---|
| 278 |
|
|---|
| 279 |
<li>The name of the currently selected Field.</li> |
|---|
| 280 |
|
|---|
| 281 |
<li>The type of the currently selected Field. There is a list of predefined |
|---|
| 282 |
types that can be selected from, but it is also possible to just write the type |
|---|
| 283 |
of the Field here.</li> |
|---|
| 284 |
|
|---|
| 285 |
<li>The cardinality of the Field, i.e. if it's a single (osg::SField) or multi |
|---|
| 286 |
element (osg::MField) field.</li> |
|---|
| 287 |
|
|---|
| 288 |
<li>The access type of the field: private, protected or public. In general |
|---|
| 289 |
Fields should be public, if they are internal they should be protected.</li> |
|---|
| 290 |
|
|---|
| 291 |
<li>The visibility of the Field mainly influences whether the Field is written |
|---|
| 292 |
to files etc. Fields that have no meaning beyond the current execution of the |
|---|
| 293 |
program should be internal, Fields that hold configuration data should be |
|---|
| 294 |
external.</li> |
|---|
| 295 |
|
|---|
| 296 |
<li>The header containing the declaration of the Field's type. If left empty a |
|---|
| 297 |
name is guessed, using the name <tt>OSG<type>Fields.h</tt>. For type names |
|---|
| 298 |
ending in \c Ptr (the default for FieldContainerPtr types) the \c Ptr is |
|---|
| 299 |
removed. This works for all system types and for standard user-defined types. |
|---|
| 300 |
For other types, just enter the header file name into this field, excluding the " |
|---|
| 301 |
delimiters. </li> |
|---|
| 302 |
|
|---|
| 303 |
<li>The default value of the Field. Used to initialize the Field, and put |
|---|
| 304 |
verbatim into the constructor definition for the given Field (e.g. <tt>FC::FC() : |
|---|
| 305 |
Field(default) { }</tt>) </li> |
|---|
| 306 |
|
|---|
| 307 |
<li>The header needed to declare the default value. Useful to access enum or |
|---|
| 308 |
constants from external files (like opengl.h).</li> |
|---|
| 309 |
|
|---|
| 310 |
<li>A short description of the Field's contents and function. Will be put into |
|---|
| 311 |
the code documentation.</li> |
|---|
| 312 |
|
|---|
| 313 |
<li>Creates a new Field.</li> |
|---|
| 314 |
|
|---|
| 315 |
<li>Deletes a Field.</li> |
|---|
| 316 |
|
|---|
| 317 |
<li>Clones a Field.</li> |
|---|
| 318 |
|
|---|
| 319 |
<li>Moves the Field up in the list.</li> |
|---|
| 320 |
|
|---|
| 321 |
<li>Moves the Field down in the list.</li> |
|---|
| 322 |
|
|---|
| 323 |
<li>Clear the whole system, start from scratch.</li> |
|---|
| 324 |
|
|---|
| 325 |
<li>Load an .fcd file.</li> |
|---|
| 326 |
|
|---|
| 327 |
<li>Currently unused.</li> |
|---|
| 328 |
|
|---|
| 329 |
<li>Save the current settings to the .fcd file.</li> |
|---|
| 330 |
|
|---|
| 331 |
<li>Write the FieldContainer Base code. This can also be done by calling |
|---|
| 332 |
fcdEdit with the <tt>-b</tt> command line option and the .fcd file. It is safe |
|---|
| 333 |
to do that, as no user changes should be done to the Base code.</li> |
|---|
| 334 |
|
|---|
| 335 |
<li>Currently unused.</li> |
|---|
| 336 |
|
|---|
| 337 |
<li>Currently unused.</li> |
|---|
| 338 |
|
|---|
| 339 |
<li>Save the current settings to a different .fcd file.</li> |
|---|
| 340 |
|
|---|
| 341 |
<li>Write the actual FieldContainer code. This can also be done by calling |
|---|
| 342 |
fcdEdit with the <tt>-f</tt> command line option and the .fcd file. This will |
|---|
| 343 |
not keep any changes made to the FC code, and thus can quickly destroy whatever |
|---|
| 344 |
work has been done to it. To prevent that from happening on accident, it will not |
|---|
| 345 |
overwrite existing files.</li> |
|---|
| 346 |
|
|---|
| 347 |
<li>Minimal info about the progam.</li> |
|---|
| 348 |
|
|---|
| 349 |
<li>Leave the program.</li> |
|---|
| 350 |
|
|---|
| 351 |
</ol> |
|---|
| 352 |
|
|---|
| 353 |
\section PageSystemFieldContainerExtPrototype Prototype Replacement |
|---|
| 354 |
|
|---|
| 355 |
If you create a replacement for a system component, e.g. a smarter DistanceLOD |
|---|
| 356 |
node that can handle predictive LOD selection, and want the system to use your |
|---|
| 357 |
version of the DistanceLOD from now on you can do that. Internally all field |
|---|
| 358 |
container instances are created by cloning a prototype instance. You can |
|---|
| 359 |
access the prototype for a given FieldContainer via its class type which you |
|---|
| 360 |
can access using FC::getClassType(). The class type has a setPrototype() |
|---|
| 361 |
method to assign the prototype. |
|---|
| 362 |
|
|---|
| 363 |
Be careful to only replace the prototype with classes derived from the |
|---|
| 364 |
original class, or the behavior of the system is undefined. |
|---|
| 365 |
|
|---|
| 366 |
\section PageSystemFieldContainerExtInit Initialization / Deinitalization |
|---|
| 367 |
|
|---|
| 368 |
As OpenSG uses object replication for thread-safe data, constructors and |
|---|
| 369 |
destructors are not always the right place for initialisation and deletion |
|---|
| 370 |
anymore. They are called for every aspect, which usually is more than once. |
|---|
| 371 |
For initialisation that should be done only once per object, the onCreate() |
|---|
| 372 |
method can be used. Similarly, onDestroy() is called once, when the object is |
|---|
| 373 |
destroyed. |
|---|
| 374 |
|
|---|
| 375 |
There is also a difference between the constructors. The copy constructor is |
|---|
| 376 |
called for every aspect of an object, the default constructor is only called |
|---|
| 377 |
once, during the static init phase, to create the initial prototype instance |
|---|
| 378 |
for the class. As code running in the static init phase faces some |
|---|
| 379 |
restrictions (e.g. the order of initializations is undefined, thus any other |
|---|
| 380 |
object might not yet have been initialized) a saver way to do class-global |
|---|
| 381 |
initializations was added. The initMethod() method is called during osgInit(), |
|---|
| 382 |
which is after all static inits are done. |
|---|
| 383 |
|
|---|
| 384 |
As a summary here's a list of when which method is called: |
|---|
| 385 |
|
|---|
| 386 |
\li default constructor: once, during static init, to create the initial |
|---|
| 387 |
prototype |
|---|
| 388 |
|
|---|
| 389 |
\li initMethod(): once, during osgInit() |
|---|
| 390 |
|
|---|
| 391 |
\li copy constructor: during object instance creation, once for every aspect |
|---|
| 392 |
|
|---|
| 393 |
\li onCreate(): during object instance creation, once |
|---|
| 394 |
|
|---|
| 395 |
\li onDestroy(): during object instance deletion, once |
|---|
| 396 |
|
|---|
| 397 |
\li destructor: during object instance deletion, once for every aspect |
|---|
| 398 |
|
|---|
| 399 |
onCreate() and on Destroy() are also called for the initial prototype |
|---|
| 400 |
creation. Not all prototypes might need the resources a real instance needs, |
|---|
| 401 |
and initial prototype creation is run during static init, where it might not |
|---|
| 402 |
be safe to access other classes. To allow a destinction between prototype |
|---|
| 403 |
creation and the standard running state of the system there's a global |
|---|
| 404 |
variable GlobalSystemState, which will be set to Startup during static init |
|---|
| 405 |
and Running after osgInit() is finished. During osgExit() it will be set to |
|---|
| 406 |
Shutdown. |
|---|
| 407 |
|
|---|
| 408 |
*/ |
|---|
| 409 |
|
|---|
| 410 |
#endif |
|---|
| 411 |
|
|---|