Table of Contents
This and the following chapters will describe how our VRML engine works. We will describe used data structures and algorithms. Together this should give you a good idea of what our engine is capable of, where are it's strengths and weaknesses, and how it's all achieved.
In this document we should not go
into details about some ObjectPascal-specific language constructs
or solutions — this would be too low-level stuff, uninteresting
from a general point of view. If you're an ObjectPascal programmer
and you want to actually use my engine then you may find it helpful
to study
source code (especially example programs in examples
subdirectories) and
units reference while reading this document.
If you only want to read this document, everything that you need
is some basic idea about object-oriented programming.
The base class of our engine is the TVRMLNode
class, not surprisingly representing a VRML node. This is an abstract
class, for all specific VRML node types we have some descendant
of TVRMLNode defined. Naming convention for non-abstract
node classes is like TNodeCoordinate class for VRML
Coordinate node type.
Every VRML node has it's fields available in it's
Fields property. You can also access
individual fields by properties named like FdXxx,
for example FdPoint is a property of
TNodeCoordinate class that represents
point field of Coordinate node.
VRML 1.0 children nodes are accessed by Children
and ChildrenCount properties.
For VRML 2.0 this is not needed, since you access all children nodes
by accessing appropriate SFNode and MFNode
fields. A convenience properties named
SmartChildren and SmartChildrenCount
are defined: for “normal” VRML 2.0 grouping nodes
(this mostly means nodes with MFNode field named
children) the SmartChildrenXxx
properties operate on appropriate MFNode, for other
nodes they operate on VRML 1.0 ChildrenXxx properties.
Because of DEF / USE mechanism
each node may be a children (“children” both in the
VRML 1.0 and 2.0 senses) of more than one node. This means that
we cannot use some trivial destructing strategy. When we destruct
some node's instance, we cannot simply destruct all it's children,
because they are possibly used in other nodes. The simple solution
to this is to keep track in each node about it's parents.
Each node has properties ParentNodes
and ParentNodesCount that track information about
all the nodes that use it in VRML 1.0 style (i.e. on
TVRMLNode.Children list). And properties
ParentFields
and ParentFieldsCount that track information about
all the SFNode and MFNode
fields referencing this node. The children node is automatically destroyed
when it has no parents — which means that
both ParentNodesCount and
ParentFieldsCount are zero.
Effectively, we implemented reference-counting.
And as a bonus, ParentXxx properties are sometimes
helpful when we want to do some “bottom-to-top”
processing of VRML graph (although this should be generally avoided,
“top-to-bottom” processing is much more in the spirit
of the VRML graph).
Classes for VRML nodes specific to particular VRML version
get a suffix _1 or _2 representing
their intended VRML version. For example, we have
TNodeIndexedFaceSet_1 (for VRML 1.0) and
TNodeIndexedFaceSet_2 (for VRML 2.0) classes.
Such nodes always have their ForVRMLVersion method
overridden to indicate in what VRML version they are allowed to be used.
For example, when parser starts reading IndexedFaceSet
node, it creates either TNodeIndexedFaceSet_1 or
TNodeIndexedFaceSet_2, depending on VRML version
indicated in the file header line. Note that this separation between VRML versions
is done only when reading VRML nodes from file. When processing
VRML nodes graph by code you can freely mix VRML nodes from various
VRML versions and everything will work, including writing nodes
back to VRML file (although if you mix VRML versions too carelessly
you may get VRML file that can only be read back by my engine, and not
by other engines that may be limited to only VRML 1.0 or only VRML 2.0).
More on this later in Section 3.2, “The sum of VRML 1.0 and 2.0”.
The result of parsing any VRML file is always a single
TVRMLNode instance representing the root node of the given file.
If the file had more than one root node [7]
then our engine wraps them in an additional Group
node. More precisely, additional instance of TVRMLRootNode
is created. It descends
from TNodeGroup_2 (but is suitable for all VRML/X3D versions).
This way it can always be treated as 100% normal Group
nodes. At the same time, VRML writing code can take special precautions
to not record these “fake” group nodes back to VRML file.
[7] Multiple root nodes are allowed in VRML 2.0 specification. Our engine also allows them for VRML 1.0 because it's an extension often expected by VRML 1.0 creators (humans and programs).