3.2. The sum of VRML 1.0 and 2.0

Our engine handles both VRML 1.0 and VRML 2.0. As we have seen in Chapter 1, Overview of VRML, there are important differences between these VRML versions. The way how I decided to handle both VRML versions is the more difficult, but also more complete approach. Effectively, you have the sum of VRML 1.0 and 2.0 features available.

I decided to avoid trying to create some internal conversions from VRML 1.0 to VRML 2.0, or VRML 2.0 to 1.0, or to some newly invented internal format. I wanted to have a full, flexible, 100% conforming to VRML 1.0 and VRML 2.0 specifications engine. And the fact is that any conversion along the way will likely cause problems — ideologically speaking, that's because there is always something lost, or at least difficult to recover, when a complicated conversion is done.

Practically here are some reasons why a simple conversion between VRML 1.0 and VRML 2.0 is not possible, in any direction:

  1. VRML 2.0 specification authors intentionally wanted to simplify some things that people (both VRML world authors and VRML browser implementors) thought were unnecessarily complicated in VRML 1.0. This causes problems for a potential converter from VRML 1.0 to 2.0, since it will have trouble to express some VRML 1.0 constructs. For example:

    • In VRML 1.0 you can specify multiple materials for a single geometry node. In VRML 2.0 each geometry node uses at most one material. So a potential converter from VRML 1.0 to 2.0 may need to split geometry nodes.

    • In VRML 1.0 you can accumulate texture transformations (Texture2Transform nodes). In VRML 2.0 you can't (you can only place one TextureTransform node in the Appearance.textureTransform field). So a potential converter must accumulate texture transformations on it's own. And this is not trivial in a general case, because you can't directly specify texture transformation matrix in VRML 2.0. Instead you have to express texture transformation in terms of one translation, one rotation and one scaling.

    • In VRML 1.0 you can specify any 4x4 matrix transformation using MatrixTransformation node. This is not possible at all in VRML 2.0. In VRML 2.0 geometry transformation must be specified in terms of translations, rotations and scaling.

    • In VRML 1.0 you can limit which geometry nodes are affected by PointLight or SpotLight by placing light nodes at particular points in the node hierarchy. That's because in VRML 1.0 light nodes work just like other state changing nodes: they affect all subsequent nodes, until blocked by the end of the Separator node.

      In VRML 2.0 this doesn't work. You cannot control what parts of the scene are affected by light nodes by placing light nodes at some particular place in the node hierarchy. Instead, you have to use radius field of light nodes. This means that some VRML 1.0 tricks are simply not possible.

    • OrthographicCamera is not possible to express using VRML 2.0 standard nodes.

    Summary: in certain cases translating VRML 1.0 to 2.0 can be very hard or even impossible. If we want to handle VRML 1.0 perfectly, we can't just write a converter from VRML 1.0 to 2.0 and then define every operation only in terms of VRML 2.0.

  2. On the other hand, VRML 2.0 also includes various things not present in VRML 1.0. This includes many new nodes, that often cannot be expressed at all in VRML 1.0: all sensors, scripts, interpolators, special things like Collision and Billboard.

    Moreover, VRML 2.0 uses SFNode (with possible NULL value) and MFNode, and generally reduces the state that needs to be remembered when processing VRML graph. This means that many existing features have to be expressed differently.

    For example consider specifying normals for IndexedFaceSet. In VRML 2.0 everything that decides about how generated normals are supplied are the normal and normalIndex fields of given IndexedFaceSet node. We take advantage of the SFNode field type, and say that whole Normal node may be just placed within normal field of IndexedFaceSet. So we just keep whole knowledge inside IndexedFaceSet node.

    On the other hand, in VRML 1.0 we have to use the value of last NormalBinding node. This says whether we should use the last Normal node, and how.

    Potential VRML 2.0 to 1.0 converter would have to make a lot of effort to deconstruct VRML 2.0 shape properties back to VRML 1.0 state nodes. This makes conversion difficult to revert (e.g. when we want to write VRML 2.0 content back to file).

That's why I decided to support in my engine the sum of all VRML features. For example, VRML 1.0 nodes can have direct children nodes, so I support it (by Children property of TVRMLNode). VRML 2.0 nodes can have children nodes through SFNode and MFNode fields, so I support it too. I'm not trying hard to combine these two ideas (direct children nodes and children inside MFNode) into one — I just implement and handle them both [8].

In some cases this approach forces me to do more work. For example, for many routines that calculate bounding boxes of geometry nodes, I had to prepare three routines:

  1. Common implementation, as a static procedure inside the X3DNodes unit. This handles actual calculation and as parameters expects already calculated properties of given shape. As a simple example, when calculating bounding box of a cube, we expect to get three parameters describing cube's sizes in X, Y and Z dimension.

  2. VRML 1.0 implementation in VRML 1.0-specific node version that calls the common implementation, after preparing parameters for common implementation. As a simple example, TNodeCube_1 (VRML 1.0 cube) just uses it's FdWidth, FdHeight and FdDepth as appropriate sizes.

  3. And VRML 2.0 implementation in VRML 2.0-specific node version, that also calls the common implementation after preparing it's parameters. As a simple example, TNodeBox (VRML 2.0 cube) accesses three items of it's FdSize field to get the appropriate sizes.

In our simple example above we talked about a cube, and the whole issue with calculating three size values differently for VRML 1.0 and 2.0 was actually trivial. But the point is that for some nodes, like IndexedFaceSet, this is much harder.

For VRML authors this sum approach means that when reading VRML 1.0, many VRML 2.0 constructs (that not conflict with anything in VRML 1.0) are allowed, and the other way around too. That's why you can actually mix VRML 1.0 and 2.0 code in my engine. Consider this strange file:

#VRML V2.0 utf8

Separator {
  DEF VRML2Cube Shape {
    appearance Appearance { material Material { } }
    geometry Sphere { }
  }

  Translation { translation 3 0 0 }

  USE VRML2Cube

  Transform {
    translation 3 0 0
    children [
      USE VRML2Cube

      Translation { translation 3 0 0 }

      USE VRML2Cube
    ]
  }
}

Figure 3.1. Four spheres in mixed VRML 1.0 and 2.0 code

Four spheres in mixed VRML 1.0 and 2.0 code

This file uses VRML 2.0 sphere that is transformed using both VRML 1.0 approach (Translation node that affects all subsequent nodes) and VRML 2.0 approach (Transform node that affects all it's children). And everything works: VRML 1.0 nodes are handled according to VRML 1.0 specification, VRML 2.0 according to VRML 2.0 specification. Transformations, no matter which VRML version was used to specify them, affect all shapes. The file's header line says that it's supposed to be VRML 2.0, and this means that when we have a node name that is possible in both VRML specifications (but must be handled differently in each version), for example Transform node, file header decides which version of this node (TNodeTransform_1 or TNodeTransform_2) is created when parsing this file.

This also means that you have many VRML 2.0 features available in VRML 1.0. VRML 2.0 nodes like Background, Fog and many others, that express features not available at all in standard VRML 1.0, may be freely placed inside VRML 1.0 models when using our engine.

Also including (using WWWInline or Inline nodes) VRML 1.0 files within VRML 2.0 files (and the other way around) is possible. Each VRML file will be parsed taking into account it's own header line, and then included content is actually placed as a children node of including WWWInline or Inline node. So you get VRML graph hierarchy with nodes mixed from both VRML versions.



[8] SmartChildrenXxx properties mentioned in the previous section somewhat combine VRML 1.0 and 2.0 ideas of children nodes, but they are generally not used except in some small pieces of code where they just make the code shorter.