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).