3.3. Reading VRML files

You can create a node using CreateParse constructor to parse the node. Or you can initialize node contents by parsing it using Parse method. However, these both approaches require you to first prepare appropriate TX3DLexer instance and a list of read node names.

There are comfortable routines like ParseVRMLFile that take care of this for you. They create appropriate lexer, and may create also suitable TStream instance to read given file content.

Some details about parsing:

  • Our VRML/X3D lexer is a unified lexer for both VRML 1.0, 2.0 and (classic) X3D. Most of the syntax is identical, minor differences can be handled correctly by a lexer because it always knows VRML/X3D header line of the given file. So it knows what syntax to expect.

  • VRML/X3D version of the original file is saved in TVRMLRootNode.ForceVersion. This will be used later when saving. Parser always returns TVRMLRootNode instance, this keeps some per-file settings like version and X3D profile, components and meta values.

    When saving, you can save any TVRMLNode instance to file. If it is not TVRMLRootNode, or if TVRMLRootNode.HasForceVersion is false, we simply assume it uses the latest X3D version.

    In engine versions <= 2.5.0 we experimented with auto-detecting the suitable VRML/X3D version for nodes inside, but this mechanism was dropped. It was complicated, and was failing anyway for complicated cases (nodes from mixed versions, things with routes, imports, exports etc.). If you want to save a specific VRML/X3D version, it's best to simply wrap it inside TVRMLRootNode and force desired version explicitly. Modern programs should target only X3D anyway, as VRML 1.0 is ancient, and VRML 2.0 is old too (from 1997).

  • While parsing, ForVRMLVersion method mentioned earlier may be used to decide which node classes to create based on VRML/X3D version indicated in the file's header line.

  • To properly handle DEF / USE mechanism we keep a list of known node names while parsing. After a node with DEF clause is parsed we add the node name and it's reference to NodeNameBinding list that is passed through all parse routines. When a USE clause is encountered, we just search this list for appropriate node name.

    Simple VRML rules of DEF / USE behavior make this approach correct. Remember that VRML name scope is not modeled after normal programming languages, where name scope of an identifier is usually limited to the structure (function, class, etc.) where this identifier is declared. In VRML, name scope always spans to the end of whole VRML file (or to the next DEF occurrence with the same name, that overrides previous name). Also, the name scope is always limited to the current file — for example, you cannot use names defined in other VRML files (that you included by Inline nodes, or that include you). (Prototypes and external prototypes in VRML 2.0 are designed to allow reusing VRML code between different VRML files.)

    The simple trick with adding our name to NodeNameBinding after the node is fully parsed prevents creating loops in our graph, in case supplied VRML file is invalid.