Now we're approaching the fundamental idea of VRML: some
nodes can be placed as a children of other nodes. We already
saw some examples of this idea in VRML 2.0 examples
above: we placed various nodes inside geometry
field of Shape node. VRML 1.0 has a little different
way of specifying children nodes (inherited from Inventor format)
than VRML 2.0 and X3D — we will see both methods.
In VRML 1.0, you just place children nodes inside the parent node. Like this:
#VRML V1.0 ascii
Group {
Sphere { }
Cube { width 1.5 height 1.5 depth 1.5 }
}Group is the simplest grouping node.
It has no fields, and it's only purpose is just to treat a couple of nodes
as one node.
Note that in VRML 1.0 it's required that a whole VRML file consists of exactly one root node, so we actually had to use some grouping node here. For example the following file is invalid according to VRML 1.0 specification:
#VRML V1.0 ascii
Sphere { }
Cube { width 1.5 height 1.5 depth 1.5 }
Nevertheless the above example is handled by many VRML engines, including our engine described in this document.
In VRML 2.0, you don't place children nodes directly
inside the parent node. Instead you place children nodes inside
fields of type SFNode (this contains
zero (NULL) or one node) or
MFNode (this contains any number (possibly zero)
of nodes). For example, in VRML 2.0 Group
node has an MFNode field children,
so the example file in VRML 2.0 equivalent to previous example looks like
this:
#VRML V2.0 utf8
Group {
children [
Shape { geometry Sphere { } }
Shape { geometry Box { size 1.5 1.5 1.5 } }
]
}Syntax of MFNode is just like for other multiple-valued
fields: a sequence of values, inside brackets ([
and ]).
Example above also shows a couple of other differences between VRML 1.0 and 2.0:
In VRML 2.0 we have to wrap Sphere and
Box nodes inside a Shape node.
Node Cube from VRML 1.0 was renamed
to Box in VRML 2.0.
Size of the box in VRML 2.0 is specified
using size field of type SFVec3f,
while in VRML 1.0 we had three fields (width,
height, depth) of type
SFFloat.
While we're talking about VRML versions differences, note also that
in VRML 2.0 a file can have any number of root nodes. So actually
we didn't have to use Group node in our example,
and the following would be correct VRML 2.0 file too:
#VRML V2.0 utf8
Shape { geometry Sphere { } }
Shape { geometry Box { size 1.5 1.5 1.5 } }
To be honest, we have to point one more VRML difference: as was mentioned before, in VRML 2.0 shapes are unlit by default. So our VRML 2.0 examples above look like this:
To make them lit, we must assign a material
for them. In VRML 2.0 you do this by placing a Material
node inside material field of Appearance
node. Then you place Appearance node inside
appearance field of
appropriate Shape node. Result looks like this:
#VRML V2.0 utf8
Group {
children [
Shape {
appearance Appearance { material Material { } }
geometry Sphere { }
}
Shape {
appearance Appearance { material Material { } }
geometry Box { size 1.5 1.5 1.5 }
}
]
}
We didn't specify any Material node's fields,
so the default properties will be used. Default VRML 2.0 material properties
are the same as for VRML 1.0: light gray diffuse color and a slight
ambient color.
As you can see, VRML 2.0 description gets significantly more verbose than VRML 1.0, but it has many advantages:
The way how children nodes are specified
in VRML 2.0 requires you to always write an SFNode
or MFNode field name
(as opposed to VRML 1.0 where you just write the children nodes).
But the advantages are obvious: in VRML 2.0 you can explicitly
assign different meaning to different children nodes
by placing them within different fields. In VRML 1.0 all the
children nodes had to be treated in the same manner —
the only thing that differentiated children nodes was their
position within the parent.
As mentioned earlier, the default behavior of various VRML 2.0 parts is the one that is the easiest to render. That's why the default behavior is to render unlit, and you have to explicitly specify material to get lit objects.
This is a good thing, since it makes VRML authors more conscious about using features, and hopefully it will force them to create VRML worlds that are easier to render. In the case of rendering unlit objects, this is often perfectly acceptable (or even desired) solution if the object has a detailed texture applied.
Placing the Material
node inside the SFNode field of
Appearance, and then placing
the Appearance node inside
the SFNode field of Shape
may seem like
a “bondage-and-discipline language”, but it allows
various future enhancements of the language without breaking
compatibility. For example you could invent a node that allows
to specify materials using a different properties (like
by describing it's BRDF function, useful for rendering realistic images)
and then just allow this node as a value for the material
field.
Scenario described above actually happened.
First versions of VRML 97 specification didn't include
geospatial coordinates support, including a node
GeoCoordinate.
A node IndexedFaceSet has a field
coord used to specify a set of points for
geometry, and initially you could place a
Coordinate node there.
When specification of geospatial coordinates support was formulated
(and added to VRML 97 specification as optional for VRML browsers),
all that had to be changed was to say that now you can place
GeoCoordinate everywhere where earlier
you could use only Coordinate.
The Shape
node in VRML 2.0 contains almost whole information needed to render
given shape. This means that it's easier to create a VRML rendering engine.
We will contrast this with VRML 1.0 approach that requires
a lot of state information in Section 1.5, “VRML 1.0 state”.
Let's take a look at another grouping node:
VRML 2.0 Transform
node. This node specifies a transformation (a mix
of a translation, a rotation and a scale) for all it's children nodes.
The default field values are such that no transformation actually
takes place, because by default we translate by (0, 0, 0) vector,
rotate by zero angle and scale by 1.0 factor. This means that
the Transform node with all fields left as default
is actually equivalent to a Group node.
Example of a simple translation:
#VRML V2.0 utf8
Shape {
appearance Appearance { material Material { } }
geometry Box { }
}
Transform {
translation 5 0 0
children Shape {
appearance Appearance { material Material { } }
geometry Sphere { }
}
}Note that a child of a Transform node
may be another Transform node. All transformations
are accumulated. For example these two files are equivalent:
#VRML V2.0 utf8
Shape {
appearance Appearance { material Material { } }
geometry Box { }
}
Transform {
translation 5 0 0
children [
Shape {
appearance Appearance { material Material { } }
geometry Sphere { }
}
Transform {
translation 5 0 0
scale 1 3 1
children Shape {
appearance Appearance { material Material { } }
geometry Sphere { }
}
}
]
}#VRML V2.0 utf8
Shape {
appearance Appearance { material Material { } }
geometry Box { }
}
Transform {
translation 5 0 0
children Shape {
appearance Appearance { material Material { } }
geometry Sphere { }
}
}
Transform {
translation 10 0 0
scale 1 3 1
children Shape {
appearance Appearance { material Material { } }
geometry Sphere { }
}
}A Switch node
allows you to choose only one (or none) from
children nodes to be in the active (i.e. visible, participating
in collision detection etc.) part of the scene.
This is useful for various scripts and it's also useful
for hiding nodes referenced later — we will see
an example of this in Section 1.4, “DEF / USE mechanism”.
A Separator and a
TransformSeparator nodes in VRML 1.0.
We will see what they do in Section 1.5, “VRML 1.0 state”.
A LOD node
(the name is an acronym for level of detail)
specifies a different versions of the same object.
The intention is that all children nodes represent the same object,
but with different level of detail: first node is the most
detailed one (and difficult to render, check for collisions etc.),
second one is less detailed, and so on, until the last node
has the least details (it can even be empty, which can be
expressed by a Group node with
no children).
VRML browser should choose the appropriate children to render
based on the distance between the viewer and designated
center point.
Unfortunately, note that LOD is not implemented in our engine yet — for now we always use the most detailed version. As far as VRML specification is concerned, this is acceptable (although non-optimal) behavior.