Now that we're accustomed with VRML syntax and concepts, let's take a quick look at some notable VRML features that weren't shown yet.
A powerful tool of VRML is the ability to include one
model as a part of another. In VRML 2.0 we do this by
Inline node. It's url
field specifies the URL (possibly relative) of VRML file to load.
Note that our engine doesn't actually support URLs right now
and treats this just as a file name.
The content of referenced VRML file is placed at the position
of given Inline node. This means that you
can apply transformation to inlined content. This also
means that including the same file more than once is sensible
in some situations. But remember the remarks in Section 1.4, “DEF / USE mechanism”:
if you want to include the same file more than once, you should
name the Inline node and then just reuse it.
Such reuse will conserve resources.
url field is actually MFString
and is a sequence of URL values, from the most to least preferred one.
So VRML browser will try to load files from given URLs in order,
until a valid file will be found.
In VRML 1.0 the node is called WWWInline,
and the URL (only one is allowed, it's SFString
field) is specified in the field name.
When using our engine you can mix VRML versions and include VRML 1.0 file from VRML 2.0, or the other way around. Moreover, you can include 3DS and Wavefront OBJ files too.
#VRML V2.0 utf8
DEF MyInline Inline { url "reuse_cone.wrl" }
Transform {
translation 1 0 0
rotation 1 0 0 -0.2
children [
USE MyInline
Transform {
translation 1 0 0
rotation 1 0 0 -0.2
children [
USE MyInline
Transform {
translation 1 0 0
rotation 1 0 0 -0.2
children [
USE MyInline
Transform {
translation 1 0 0
rotation 1 0 0 -0.2
children [
USE MyInline
] } ] } ] } ] }Figure 1.15. Our earlier example of reusing cone inlined a couple of times, each time with a slight translation and rotation

VRML allows you to specify a texture coordinate transformation. This allows you to translate, scale and rotate visible texture on given shape.
In VRML 1.0, you do this by Texture2Transform
node — this works analogous to Transform,
but transformations are only 2D. Texture transformations in VRML 1.0
accumulate, just like normal transformations. Here's an example:
#VRML V1.0 ascii
Group {
Texture2 { filename "sample_texture.png" }
Cube { }
Transform { translation 3 0 0 }
Separator {
# translate texture
Texture2Transform { translation 0.5 0.5 }
Cube { }
}
Transform { translation 3 0 0 }
Separator {
# rotate texture by Pi/4
Texture2Transform { rotation 0.7853981634 }
Cube { }
}
Transform { translation 3 0 0 }
Separator {
# scale texture
Texture2Transform { scaleFactor 2 2 }
Cube { }
Transform { translation 3 0 0 }
# rotate texture by Pi/4.
# Texture transformation accumulates, so this will
# be both scaled and rotated.
Texture2Transform { rotation 0.7853981634 }
Cube { }
}
}Remember that we transform texture coordinates, so e.g. scale 2x means that the texture appears 2 times smaller.
VRML 2.0 proposes a different approach here:
We have similar TextureTransform node, but we can
use it only as a value for textureTransform field
of Appearance. This also means that there
is no way how texture transformations could accumulate.
Here's a VRML 2.0 file equivalent to previous VRML 1.0 example:
#VRML V2.0 utf8
Shape {
appearance Appearance {
texture DEF SampleTexture
ImageTexture { url "sample_texture.png" }
}
geometry Box { }
}
Transform {
translation 3 0 0
children Shape {
appearance Appearance {
texture USE SampleTexture
# translate texture
textureTransform TextureTransform { translation 0.5 0.5 }
}
geometry Box { }
}
}
Transform {
translation 6 0 0
children Shape {
appearance Appearance {
texture USE SampleTexture
# rotate texture by Pi/4
textureTransform TextureTransform { rotation 0.7853981634 }
}
geometry Box { }
}
}
Transform {
translation 9 0 0
children Shape {
appearance Appearance {
texture USE SampleTexture
# scale texture
textureTransform TextureTransform { scale 2 2 }
}
geometry Box { }
}
}
Transform {
translation 12 0 0
children Shape {
appearance Appearance {
texture USE SampleTexture
# scale and rotate the texture.
# There's no way to accumulate texture transformations,
# so we just do both rotation and scaling by
# TextureTransform node below.
textureTransform TextureTransform {
rotation 0.7853981634
scale 2 2
}
}
geometry Box { }
}
}
You can specify various navigation information using
the NavigationInfo node.
type field describes
preferred navigation type. You can “EXAMINE”
model, “WALK” in the model (with collision detection
and gravity) and “FLY” (collision detection,
but no gravity).
avatarSize
field sets viewer (avatar) sizes. These typically have to be
adjusted for each world to “feel right”.
Although you should note that VRML generally
suggests to treat length 1.0 in your world as “1 meter”.
If you will design your VRML world following this assumption,
then default avatarSize will feel quite adequate,
assuming that you want the viewer to have human size in your world.
Viewer sizes are used for collision detection.
Viewer size together with
visibilityLimit may be also used
to set VRML browsers Z-buffer near and far clipping planes.
This is the case with our engine. By default our engine tries to calculate
sensible values for near and far based on scene bounding box size.
You can also specify moving speed
(speed field), and whether head light is on
(headlight field).
To specify default viewer position and orientation in the
world you use Viewpoint node. In VRML 1.0,
instead of Viewpoint you have
PerspectiveCamera and
OrthogonalCamera (in VRML 2.0 viewpoint
is always perspective). Viewpoint and camera nodes may be generally
specified anywhere in the file. The first viewpoint/camera node
found in the file (but only in the active part of the file —
e.g. not in inactive children of Switch)
will be used as the starting position/orientation.
Note that viewpoint/camera nodes
are also affected by transformation.
Finally, note that my VRML viewer view3dscene has a useful function to print VRML viewpoint/camera nodes ready to be pasted to VRML file, see menu item “Console” -> “Print current camera node”.
Here's an example file. It defines a viewpoint (generated
by view3dscene) and a navigation info
and then includes actual world geometry from other file
(shown in our earlier example
about inlining).
#VRML V2.0 utf8
Viewpoint {
position 11.832 2.897 6.162
orientation -0.463 0.868 0.172 0.810
}
NavigationInfo {
avatarSize [ 0.5, 2 ]
speed 1.0
headlight TRUE
}
Inline { url "inline.wrl" }IndexedFaceSet nodes (and a couple of other
nodes in VRML 2.0 like ElevationGrid) have
some notable features to make their rendering better and
more efficient:
You can use non-convex faces if you set
convex field to FALSE.
It will be VRML browser's responsibility to correctly
triangulate them. By default faces are assumed to be
convex (following the general rule that the default behavior
is the easiest one to handle by VRML browsers).
By default shapes are assumed to be
solid which allows to use backface
culling when rendering them.
If you don't supply pre-generated normal vectors
for your shapes, they will be calculated by the VRML
browser. You can control how they will be calculated
by the creaseAngle field: if the angle
between adjacent faces will be less than specified
creaseAngle, the normal vectors in
appropriate points will be smooth. This allows you
to specify preferred “smoothness” of the
shape. In VRML 2.0 by default creaseAngle is zero
(so all normals are flat; again this follows the rule that
the default behavior is the easiest one for VRML browsers).
See example below.
For VRML 1.0 the creaseAngle,
backface culling and convex faces settings are controlled by
ShapeHints node.
All VRML shapes have some sensible default texture mapping.
This means that you don't
have to specify texture coordinates if you want the texture
mapped. You only have to specify some texture.
For IndexedFaceSet the default
texture mapping adjusts to shape's bounding box (see
VRML specification for details).
Here's an example of the creaseAngle
use. Three times we define the same geometry in IndexedFaceSet
node, each time using different creaseAngle values.
The left tower uses creaseAngle 0, so all
faces are rendered flat. Second tower uses creaseAngle 1
and it looks good — smooth where it should be.
The third tower uses creaseAngle 4,
which just means that normals are smoothed everywhere (this case
is actually optimized inside our engine, so it's calculated
faster) — it looks bad, we can see that normals are
smoothed where they shouldn't be.
#VRML V2.0 utf8
Viewpoint {
position 31.893 -69.771 89.662
orientation 0.999 0.022 -0.012 0.974
}
Switch {
choice DEF TowerCoordinates Coordinate {
point [
4.157832 4.157833 -1.000000,
4.889094 3.266788 -1.000000,
........
]
}
}
Transform {
children Shape {
appearance Appearance { material Material { } }
geometry IndexedFaceSet {
coordIndex [
63 0 31 32 -1,
31 30 33 32 -1,
........
]
coord USE TowerCoordinates
creaseAngle 0
}
}
}
Transform {
translation 30 0 0
children Shape {
appearance Appearance { material Material { } }
geometry IndexedFaceSet {
coordIndex [
63 0 31 32 -1,
31 30 33 32 -1,
........
]
coord USE TowerCoordinates
creaseAngle 1
}
}
}
Transform {
translation 60 0 0
children Shape {
appearance Appearance { material Material { } }
geometry IndexedFaceSet {
coordIndex [
63 0 31 32 -1,
31 30 33 32 -1,
........
]
coord USE TowerCoordinates
creaseAngle 4
}
}
}These constructions define new VRML nodes in terms of already available ones. The idea is basically like macros, but it works on VRML nodes level (not on textual level, even not on VRML tokens level) so it's really safe.
These constructions define syntax of new VRML nodes, without defining their implementation. The implementation can be specified in other VRML file (using normal prototypes mentioned above) or can be deduced by particular VRML browser using some browser-specific means (for example, a browser may just have some non-standard nodes built-in). If a browser doesn't know how to handle given node, it can at least correctly parse the node (and ignore it).
For example, many VRML browsers handle some non-standard VRML nodes. If you use these nodes and you want to make your VRML files at least readable by other VRML browsers, you should declare these non-standard nodes using external prototypes.
Even better, you can provide a list of proposed implementations for each external prototype. They are checked in order, VRML browser should chose the first implementation that it can use. So you can make the 1st item a URN that is recognized only by your VRML browser, and indicanting built-in node implementation. And the 2nd item may point to a URL with another VRML file that at least partially emulates the functionality of this non-standard node, by using normal prototype. This way other VRML browsers will be able to at least partially make use of your node.
Our engine handles prototypes and external prototypes perfectly
(since around September 2007). We have some non-standard VRML extensions
(see
Kambi VRML game engine extensions list),
and they can be declared as external prototypes
with URN like
"urn:vrmlengine.sourceforge.net:node:KambiTriangulation".
So other VRML browsers should be able to at least parse them.
There are some very notable VRML 97 features that I didn't describe in this document, simply because they are not implemented yet. To name just a few:
They define animation by interpolation of appropriate sets of values. My engine also allows to do animations, also by interpolating, so the internal approach is actually the same. However the way to specify the animations for my engine is not by VRML nodes, but instead by providing two or more VRML models with the same structure. My approach has some advantages and some disadvantages when compared to VRML interpolators, the details will be explained in Chapter 6, Animation.
VRML 97 specification includes a great support for extending content with any kind of external language. Detailed description of Java and ECMAScript (JavaScript) bindings is given by the specification, and it's expected that other languages could use similar approaches. X3D specification pushes this even further, by describing external language interface in a way that is “neutral” to actual programming language (which means that it should be applicable to pretty much all existing programming languages).
Scripts can be invoked on various events, and the events can in turn be generated by various nodes. In particular, sensor nodes are special kind of nodes that were designed only to generate events in particular situations.
My engine doesn't support any kind of scripting for now. My initial approach was directed at making special programs (like games ...) that simply use the VRML engine, so any logic was expressed in normal ObjectPascal code that was later compiled.
Of course, it would be great to implement scripting and move as much of this logic as possible to VRML files. For VRML authors, this is also the way to not be tied to any particular VRML engine. Although for really large programs there's no way that whole logic could be moved into a scripting language...
NURBS curves and surfaces. Optional in VRML 97 specification (but obviously terribly useful, so their lack in current implementation deserves mention).