SaveToStream
method of TVRMLNode
class allows you to save node contents (including children nodes) to any stream.
Just like for reading, there are also more comfortable routines for writing
called SaveToVRMLFile
.
When writing we also keep track of all node names defined
to make use of DEF / USE mechanism. If we want to write a named node,
we first check NodeNameBinding
list whether the same name
with the same node was already written to file. If yes, then we
can place a USE
statement, otherwise we have
to actually write the node's contents and add given node to
NodeNameBinding
list.
The advantages of above NodeNameBinding
approach is that it always works correctly.
Even for node graphs created by code
(as opposed to node graphs read earlier from VRML file).
If node graph was obtained by reading VRML file, then the
DEF / USE statements will be correctly written back to the file, so there will
not be any unnecessary size bloat. But note that in some cases
if you created your node graph by code then some node contents
may be output more than once in the file:
First of all, that's because we can use DEF / USE mechanism only for nodes that are named. For unnamed nodes, we will have to write them in expanded form every time. Even if they were nicely shared in node graph.
Second of all, VRML name scope is weak and if you use the same node name twice, then you may force our writing algorithm to write node in expanded form more than once (because you “overridden” node name between the first
DEF
clause and the potential place for correspondingUSE
clause).
So if you process VRML nodes graph by code and you want to maximize the chances that DEF / USE mechanism will be used while writing as much as it can, then you should always name your nodes, and name them uniquely.
It's not hard to design a general approach that will always
automatically make your names unique. VRML 97 annotated
specification suggests adding to the node name an _
(underscore)
character followed by some integer for this purpose. For example,
in our engine you can enumerate all nodes (use EnumerateNode
method), and for each node that is used more than once (you can
check it easily: such node will have ParentNodesCount + ParentFieldsCount > 1
)
you can append '_' + PtrUInt(Pointer(Node))
to the
node name. The only problem with this approach (and the reason why
it's not done automatically) is that you will have to strip
these suffixes later, if you will read this file back
(assuming that you want to get the same node names).
This can be easily done (just remove everything following the last underscore
in the names of multiply instantiated nodes).
But then if you load the created VRML file into some other
VRML browser, you may see these internal suffixes anyway.
That's why my decision was that by default such behavior is not done.
So the generated VRML file will always have exactly the same node
names as you specified.
As was mentioned a couple of times earlier, we do everything to get the VRML scene graph in memory in exactly the same form as was recorded in VRML file, and when writing the resulting VRML file also directly corresponds (including DEF / USE mechanism and node names) to VRML graph in memory.
Actually, there are two exceptions:
Inline
nodes load their referenced content as their childrenWhen reading VRML file with multiple root nodes, we wrap them in additional
Group
node
... but we work around these two exceptions when writing VRML files. This means that reading the scene graph from file and then writing it back produces the file with the exact same VRML content. But whitespaces (including comments) are removed, when writing we reformat everything to look nice. So you can simply read and then write back VRML file to get a simple VRML pretty-printer.