Building Worlds by Hand Second Edition, More or Less
Nodes, Routes, Scripts, and the Web That Almost Was Includes CD-ROM*

VRML 2.0The Annotated Homage

Pandymic Press
*The CD-ROM is in Chapter 11.
You do not need a drive.
Contents
Table of

Contents

  1. Preface
  2. Part I. Background
  3. 1. A Short History of a Long Dream
  4. 2. Hello, Cyberspace
  5. 3. Nodes, Fields, and the Scene Graph
  6. Part II. Building Things
  7. 4. Shapes
  8. 5. Appearance, Light, and Weather
  9. Part III. Making Things Happen
  10. 6. Things That Move
  11. 7. Being Somewhere
  12. 8. Scripting
  13. 9. Prototypes
  14. Part IV. Odds and Ends
  15. 10. VRML 1.0: The Separator Years
  16. 11. The Companion CD-ROM
  17. Appendixes
  18. A. Node Reference
  19. B. Inside This Browser
  20. C. Further Reading
  21. Colophon
Preface
Preface

Before We Begin

In 1996 you could type about forty lines into a text editor, save the file with a .wrl extension, put it on a web server, and anyone with the right plug-in could walk around inside it. Not look at a picture of it. Walk around inside it. It would be slow, it would be ugly, and it would be absolutely thrilling.

That language was VRML, the Virtual Reality Modeling Language. This book is a tribute to it. It is also a working implementation: every example in these pages runs, live, inside this page, on a small VRML browser written from scratch in modern JavaScript on top of WebGL. No plug-in required. We checked.

You can read the book front to back, or you can skip to Chapter 11, where you will find the Workbench: an editor and viewer for writing your own worlds, saving them in your browser, and downloading them as real .wrl files that would have worked (mostly) in 1997.

Who This Book Is For

People who remember VRML and want to see it run again. People who never heard of it and wonder what all the fuss was about. People who like the idea of describing a 3D world in a text file and are a little tired of build tools. If you can edit HTML, you can write VRML. It was designed that way on purpose.

Conventions Used in This Book

The following typographical conventions are used:

This icon marks a note: a bit of background, a footnote that got ambitious, or a piece of history.

This icon marks a warning. Usually it means the spec says one thing and every browser of the era did another.

This icon marks a tip. Most tips are about the Workbench.

How the Examples Work

Each example has a numbered listing, followed by a gray box that looks like a browser plug-in waiting for permission to run. Click it. The world loads in place, with a small control bar on top. In keeping with tradition, we only let a few plug-ins run at once; open a fifth and the oldest one quietly goes back to sleep. Every listing also has an Open in the Workbench link, which copies it into the editor in Chapter 11 so you can break it.

The viewers support three ways of moving around, chosen from the control bar:

Page Up and Page Down step through a world's viewpoints, and the Snap button saves a screenshot, which is more than most 1996 browsers could manage.

A Note About Running This Locally

The book is a static web page. It needs to be served over HTTP because it uses JavaScript modules, and a couple of examples load files from the worlds/ folder. Any static server will do. From the project folder:

python3 -m http.server 8000

Then visit http://localhost:8000/. You will need a browser with WebGL 2, which by now is nearly all of them.

Acknowledgments

Thanks are due to everyone who argued on the www-vrml mailing list, wrote a browser, wrote a book, or posted a world on a personal home page between a "This page is under construction" banner and a hit counter. Thanks also to the 28.8 kbps modem, without which none of us would have learned patience.

Chapter 1: A Short History of a Long Dream
Chapter 1

A Short History of a Long Dream

The Web is a dull place for a body. Let us give it some rooms.Paraphrase of roughly every VRML talk ever given

In this chapter

The Web was barely a year into its public life when people started wanting to stand inside it. Hypertext was fine. Hypertext with pictures was better. But science fiction had already promised a cyberspace you could walk through, and the people building the Web had read the same novels as everybody else.

Geneva, 1994

The usual version of the story goes like this. In early 1994, Mark Pesce and Tony Parisi built a prototype 3D web browser they called Labyrinth. At the first International Conference on the World Wide Web, held at CERN in Geneva in May 1994, there was a "birds of a feather" session about virtual reality interfaces to the Web. Pesce presented Labyrinth. Tim Berners-Lee and Dave Raggett were in the room. Out of that session came a name, Virtual Reality Markup Language, and a mailing list, www-vrml, hosted at Wired magazine.

The name lasted about as long as it took someone to point out that this was not a markup language. Nothing was being marked up. It became the Virtual Reality Modeling Language, and the acronym survived the change, which is the most important thing an acronym can do.

Borrowing a File Format

The mailing list wanted a specification quickly, and wisely chose not to invent one from nothing. Several existing 3D formats were proposed. The one selected was the ASCII file format of Open Inventor, a 3D toolkit from Silicon Graphics. Inventor files were readable text, they described a hierarchy of objects, and SGI was willing to let the community use the format. Gavin Bell of SGI did much of the work of adapting it, with Parisi and Pesce, and the VRML 1.0 specification was finished in 1995.

VRML 1.0 could describe a static scene: shapes, materials, lights, cameras, and links to other worlds. It could not describe anything that moved. You could fly through a world, but the world would not notice you were there. A clarified revision, VRML 1.0c, followed in early 1996.

Moving Worlds

Everyone agreed that the next version needed behavior: things that move, things you can click, things that react. In 1996 the VRML community issued a call for proposals and got several, from some very large companies. Among them, if memory and the archives serve, were Microsoft's ActiveVRML, Sun's HoloWeb, and Apple's Out of This World. The winner, chosen by a vote of the community, was Moving Worlds, a proposal led by Silicon Graphics with Sony and others.

Moving Worlds threw out the 1.0 state machine and replaced it with self-contained nodes (a Shape owns its appearance and geometry), added events and routes for wiring nodes together, added sensors for time and user input, and added prototypes so authors could define new node types in VRML itself. For anything those could not express, there was the Script node, which could run Java or JavaScript.

The VRML 2.0 specification was released in August 1996, at SIGGRAPH in New Orleans. After some editing and the standards process, it became the international standard ISO/IEC 14772-1:1997, better known as VRML97. For our purposes, VRML 2.0 and VRML97 are the same language, and this book uses the names interchangeably, as did everyone at the time.

The Browser Wars, in 3D

To see a VRML world you needed a VRML browser, which usually meant a plug-in for Netscape Navigator or an ActiveX control for Internet Explorer. There were a lot of them. Some names you may remember:

BrowserFromNotes
WebSpaceSilicon Graphics, later TGSAn early VRML 1.0 browser, built on Inventor itself.
WebFX, then Live3DPaper Software, then NetscapeNetscape bought Paper Software and shipped Live3D with Navigator 3.
Cosmo PlayerSGI / Cosmo SoftwareThe VRML 2.0 browser for many people. Its dashboard is a genre of nostalgia.
WorldViewIntervistaMicrosoft licensed it as the VRML viewer for Internet Explorer.
Community PlaceSonyLeaned into shared, multi-user worlds.
Contactblaxxun interactiveMulti-user worlds, avatars, chat.
CortonaParallelGraphicsKept going long after most of the others had stopped.

They disagreed with each other in small and maddening ways. The same world could look lovely in one browser, render half its textures upside down in another, and crash a third. Authors learned to test everywhere, then to give up and write "Best viewed with" on the page.

What Happened Next

VRML did not take over the Web. The reasons are easy to list in hindsight. Most people had slow modems and no 3D hardware. Plug-ins were a pain to install and a security worry. Building good 3D content was hard, and building it by hand in a text editor was harder. The companies that funded the big browsers changed direction, merged, or ran out of money.

But the ideas did not go anywhere. The Web3D Consortium (as the VRML Consortium renamed itself) carried VRML forward as X3D, which kept the node and field model and added an XML encoding. WebGL arrived in browsers around 2011, and suddenly the plug-in was unnecessary: the browser could draw 3D by itself. That is what this book's engine stands on. In a way, VRML finally got what it needed. It just got it about fifteen years late.

The specification documents themselves are still published by the Web3D Consortium and are well worth reading. This book does not reproduce them; it points at them. See Appendix C.

Chapter 2: Hello, Cyberspace
Chapter 2

Hello, Cyberspace

Our first world will contain one red ball. Do not be disappointed. It is a very good ball.

A VRML file is plain text. It starts with a header line, which tells the browser which version of the language follows, and then it lists nodes. That is the whole recipe. Here is the smallest world worth looking at.

Example 2-1. The obligatory red sphere

Let's go through it line by line, because every later example is built from the same parts.

The header #VRML V2.0 utf8 must be the very first thing in the file, exactly as written. A browser reads it to decide whether it is looking at VRML 2.0 text in UTF-8 encoding or (if it says #VRML V1.0 ascii) the older language. Our browser speaks both.

Shape is a node. A node has a type name, then a pair of braces, and between the braces a list of fields, each a name followed by a value. A Shape has two fields: appearance, which says what the surface looks like, and geometry, which says what the object is. Both of those fields hold other nodes, which is how scenes become trees.

The Sphere has no fields listed, so it gets its defaults: a radius of 1. Every field of every node has a default, and the defaults were chosen so that leaving things out usually does something reasonable. You will come to rely on this.

Units are meters, by convention. Angles are radians, by law. The default camera sits at 0 0 10, ten meters back along the Z axis, looking toward the origin, and the Y axis points up.

Leave out the appearance and the sphere still draws, but it is rendered unlit and pure white. VRML treats "no material" as "don't do lighting at all," which surprises everyone exactly once.

Moving Things Around

Shapes sit at the origin unless something moves them. The Transform node is a grouping node with a position, rotation, and scale. Everything inside its children field is moved along with it. Here is a second world with two shapes and some text.

Example 2-2. Hello, World, with a planet

A few new things showed up. WorldInfo gives the world a title; it has no effect on what you see. Background sets the color of the sky. The text uses emissiveColor, which makes a surface glow as though lit from within, so it reads clearly no matter how the light falls. And the string field holds a list of strings, written inside square brackets, one per line of text.

Try the Walk and Fly buttons. Try Wire, too, which shows you the triangles that the browser actually draws. A sphere is not really a sphere. Nothing is.

Chapter 3: Nodes, Fields, and the Scene Graph
Chapter 3

Nodes, Fields, and the Scene Graph

In this chapter

Everything in VRML is a node, and everything a node knows is in its fields. That sentence is most of the language. This chapter is the rest.

Field Types

Every field has a type, and the type decides how its value is written. Type names start with SF (single field, one value) or MF (multiple field, a list). An MF value is written inside square brackets with values separated by whitespace or commas; if the list has exactly one value, the brackets are optional.

Table 3-1. VRML 2.0 field types
TypeHoldsExample value
SFBoolTRUE or FALSETRUE
SFInt32, MFInt3232-bit integers[ 0 1 2 -1 ]
SFFloat, MFFloatfloating point numbers0.5
SFTime, MFTimeseconds since midnight, January 1, 1970, GMT0
SFString, MFStringUTF-8 text in double quotes[ "one" "two" ]
SFVec2f, MFVec2f2D vectors, often texture coordinates0.5 1
SFVec3f, MFVec3f3D vectors and points0 1.6 10
SFColor, MFColorRGB, each from 0 to 11 0.5 0
SFRotation, MFRotationan axis and an angle in radians0 1 0 1.5708
SFImagewidth, height, components, then pixels2 2 1 0xFF 0 0 0xFF
SFNode, MFNodenodes, or NULLBox { }

Rotations deserve a moment. 0 1 0 1.5708 means "turn around the Y axis by 1.5708 radians," which is 90 degrees, counterclockwise when you look down the axis toward the origin. You will type 1.5708 and 3.1416 so often that your fingers will learn them before your brain does.

Four Kinds of Fields

Fields also come in four kinds, which matter once things start moving in Chapter 6:

Appendix A lists every field of every built-in node, with its kind, type, and default. It is generated from this browser's own internal tables, so if it is wrong there, it is wrong here too, and at least we are consistent.

DEF and USE

Any node can be given a name with DEF. Later in the file, USE puts that same node somewhere else in the tree. Not a copy: the very same node, appearing in two places. Change it once and both places change. The scene graph is therefore not strictly a tree. It is a directed acyclic graph, and if you say that at a party people will move away from you.

Example 3-1. A temple built from one column

Click Wire and count the triangles in the status bar. The browser draws eight columns, but there is only one Cylinder node, one Appearance, and one set of triangles in memory. In 1996 memory was precious and this was a big deal. It still is, a little.

Grouping Nodes

Nodes that hold children are called grouping nodes. You have met Transform. Its plainer sibling Group just holds things. Switch shows one of its children, chosen by number. LOD (level of detail) picks a child depending on how far away you are, so distant things can be drawn with fewer triangles. Billboard turns its children to face you. Anchor makes its children into a hyperlink. Collision was supposed to stop you walking through walls, and in this browser it does not, which makes it the most relaxing node in the language.

Chapter 4: Shapes
Chapter 4

Shapes

Four primitives, a face set, a grid, and a tube. You can build anything from those. People did. Some of it was good.

VRML 2.0 has ten geometry nodes. Four of them are primitives that need almost no explanation: Box, Sphere, Cone, and Cylinder. Each is centered on the origin and sized by a field or two.

Example 4-1. The four primitives, with name tags

The cylinder has its bottom turned off. Orbit underneath it and you can see inside. You can see the inside faces too, as it happens, because this browser lights both sides of an open surface. Many browsers of the time didn't, and you would see straight through the back wall, which made everything look like a stage set.

IndexedFaceSet

For everything that isn't a primitive, there is IndexedFaceSet. You give it a list of points in a Coordinate node, then describe each face as a list of indexes into that list, ending each face with -1. Faces should go counterclockwise when you look at the side you want people to see.

Example 4-2. Three face sets: flat colors, smooth colors, and a concave polygon

Three fields in that listing earn a mention. colorPerVertex FALSE says the Color list has one color per face rather than per point. convex FALSE warns the browser that a polygon has dents, so it must cut it into triangles carefully. (Remove that line and watch the star break. It's educational.) And solid FALSE says the shape has no inside, so both sides should be drawn.

When you don't supply normals, the browser makes them up. By default every face is flat. The creaseAngle field changes that: where two faces meet at an angle smaller than the crease angle, the browser smooths across the join. Set it to about 0.5 radians for "smooth curves, sharp corners," which is what you wanted anyway.

ElevationGrid

Terrain is a grid of heights, and VRML has a node for exactly that. You give it a number of columns and rows, the spacing between them, and a height for every point, row by row.

Example 4-3. An island, with a shallow sea

In 1996, people mostly generated those numbers with a little program, often in Perl, and pasted them in. So did we, although it wasn't Perl. Times change.

Extrusion

Extrusion is the clever one. It takes a 2D outline (the crossSection) and sweeps it along a 3D path (the spine). At each point of the spine the outline can be scaled and twisted. Tubes, vases, torus rings, springs, picture frames, and anything turned on a lathe all fall out of it.

Example 4-4. A ring, a vase, and a spring, all from Extrusion

The spec has a precise recipe for how the cross section is oriented at each spine point, and it involves cross products of neighboring spine segments. When the spine is perfectly straight those cross products are zero and the recipe has to fall back on something else. Browsers of the era did not all agree on what. If your extrusion comes out twisted, add a tiny wiggle to the spine and see if it stops.

Lines, Points, and Words

That leaves three more. IndexedLineSet is like a face set but draws lines instead of filling faces; lines are never lit, so they take their color from a Color node or from the material's emissiveColor. PointSet draws dots. Text, which you have already used, draws flat text in the XY plane, with a FontStyle choosing among "SERIF", "SANS", and "TYPEWRITER". Those are the only three fonts. Choose wisely.

Chapter 5: Appearance, Light, and Weather
Chapter 5

Appearance, Light, and Weather

A Material has six fields, and between them they describe how a surface responds to light. diffuseColor is the color you think of as "the color." specularColor and shininess control highlights. emissiveColor is light the surface gives off on its own. ambientIntensity says how much of the scene's ambient light the surface picks up. transparency goes from 0, solid, to 1, gone.

Example 5-1. Five materials and three lights

The NavigationInfo node at the top turns off the headlight, a light every browser straps to your forehead by default so you can see where you are going. Turning it off lets the scene's own lights do the work. Use the Headlight button in the control bar to switch yours back on and see how flat everything becomes.

The three light types behave as you would guess, with one surprise. PointLight and SpotLight light everything within their radius, anywhere in the world. A DirectionalLight, though, only lights the other children of the group it is in. Put one inside a Transform and it lights that transform's contents and nothing else. This is useful, and nobody expects it.

Textures

A texture is an image wrapped onto a shape. ImageTexture loads a JPEG or PNG (or, in the old days, a GIF) from a URL. PixelTexture lets you type the pixels directly into the file, as hexadecimal numbers, which is exactly as fun as it sounds. TextureTransform scales, rotates, and slides a texture across the surface.

Example 5-2. Typed pixels, a repeating check, and some bricks

Look closely at the middle cylinder. Its texture is grayscale, and a grayscale texture multiplies the material's diffuse color instead of replacing it, so the gray checks come out red. Full-color textures replace the diffuse color entirely. The spec defines this per number of image components, and it is one of those rules that seems arbitrary until the day it saves you from making a second texture file.

The pixels of an SFImage start at the bottom left and go up, row by row. Every image editor you have ever used starts at the top. The smiley in the listing above is drawn upside down so that it appears right side up, and if that sentence makes your head hurt, welcome to texture coordinates.

Sky and Fog

Background paints the sky as bands of color at angles measured from straight up, and the ground as bands measured from straight down. Fog blends distant things into a color, with visibilityRange giving the distance at which they vanish entirely. In 1997 fog was a performance trick: if nobody can see past thirty meters, nobody notices you didn't build anything past thirty meters. It also looks rather lovely.

Example 5-3. A walk in the foggy woods

That world starts in Walk mode, because the NavigationInfo says so. Click in the view and use the arrow keys. Hold Shift to hurry.

Chapter 6: Things That Move
Chapter 6

Things That Move

VRML 1.0 gave us a place. VRML 2.0 gave us a clock.

In this chapter

Animation in VRML 2.0 is plumbing. Some nodes produce events, which are values stamped with a time. A ROUTE statement connects one node's eventOut to another node's eventIn, and from then on, every event that comes out of the first goes into the second. You build behavior by connecting pipes.

ROUTE FromNode.eventOut TO ToNode.eventIn

Both nodes must have names, given with DEF, and the two fields must have the same type. That's the only rule. A route from an SFFloat to an SFFloat is fine. A route from an SFFloat to an SFColor is a load error, and this browser will tell you which line it's on.

The Clock and the Translator

Nearly every animation is built from two parts. A TimeSensor is a clock. While it runs, it sends out a fraction_changed event that climbs from 0 to 1 over each cycleInterval seconds, then starts again if loop is TRUE. An interpolator takes that fraction and turns it into something useful: a position, a rotation, a color, a number. You give the interpolator a list of key fractions and a matching list of keyValue values, and it blends between them.

Example 6-1. A toy box: one clock, four interpolators

Notice that the spin uses four keys to get one full turn instead of two. An OrientationInterpolator always takes the shortest way between two rotations, and the shortest way from 0 to 6.283 radians is to not move at all. Spread the turn over several keys, each less than half a revolution apart, and it behaves.

Touching Things

A sensor watches for something and reports it as events. Pointing device sensors watch your mouse, and they work on all the geometry that shares their parent group. Put a TouchSensor next to a shape in a Group, and clicking the shape fires the sensor. The touchTime event it sends is an SFTime, and a TimeSensor's startTime is also an SFTime. Connect them and you have a button.

Example 6-2. A door with a doorbell

The cursor changes to a pointing hand over the red button. That's the browser telling you there is a sensor there. Press the button while the door is still moving and nothing happens, because a running TimeSensor ignores new start times. This is in the spec, and it is the reason every VRML author has at some point clicked a button nine times in a row while muttering.

Dragging Things

Three more sensors turn mouse drags into motion. PlaneSensor slides things on a flat plane. SphereSensor spins things like a trackball. CylinderSensor turns things around a vertical axis, like a knob. Each sends its result as a value you can route straight into a Transform.

Example 6-3. A slider, a trackball, and a dial

A classic mistake is to put the sensor inside the Transform it moves. Then the sensor's own coordinate system moves as you drag, and the object runs away from the mouse like a cat. Keep the sensor one level up, as a sibling of the thing it moves.

Chapter 7: Being Somewhere
Chapter 7

Being Somewhere

A world is not a model you look at. It's a place you visit. The difference is mostly viewpoints.

A few nodes don't draw anything at all; they describe how a visitor experiences the world. Viewpoint marks a spot to stand, with a name for the menu. NavigationInfo says whether to walk, fly, or examine, how fast, and whether to turn on the headlight. Background and Fog you have met. These four are bindable nodes: there can be many in a world, but only one of each kind is active at a time, and sending TRUE to its set_bind eventIn makes it the active one.

Anchor is the Web part of the World Wide Web. It's a group whose children are a hyperlink. Clicking them follows the url, which can point to another world, to an HTML page, or (with a # in front) to a named Viewpoint in the current world. ProximitySensor watches an invisible box and reports when you walk into it, where you are, and which way you are facing.

Example 7-1. A garden with a lamp that notices you

Walk up the path toward the lamp. When you step inside the sensor's box, its isActive event goes TRUE, and that is routed straight into the light's on field. Walk away and it goes out. No scripting, just one route. The sign is an Anchor inside a Billboard, so it always faces you; hover over it and the status bar shows its description, and clicking it takes you up in the air. The viewpoint menu in the control bar lists the other places to stand, and the browser glides between them, which is a courtesy Cosmo Player taught us all to expect.

A ProximitySensor with a size of zero, the default, is disabled. Its box has to have some volume to notice anyone. If your sensor never fires, check the size first.

Chapter 8: Scripting
Chapter 8

Scripting

Routes can take you a long way. Then one day you need an "if."

In this chapter

The Script node is a node whose behavior you write yourself. You declare its interface in the file, the same way the spec declares the fields of built-in nodes: a list of eventIn, eventOut, and field declarations. Then you give it a program in the url field. When an event arrives at one of its eventIns, the browser calls the function with the same name.

Which Language?

The VRML97 spec defines two scripting languages in its annexes: Java and ECMAScript, the standardized name for JavaScript. In practice there was a third. SGI's Cosmo Player supported VRMLScript, a cut-down JavaScript dialect designed to be small enough to embed in a plug-in. You told the browser which one you meant with a prefix on the source code:

url "javascript: function touched(value, time) { ... }"url "vrmlscript: function touched(value, time) { ... }"

This browser accepts both prefixes, plus ecmascript:, and runs them all as ordinary JavaScript. It does not run Java. If you point a Script at a .class file it will politely decline, which is probably for the best for everyone.

The Rules

Example 8-1. A box that counts

The script has a field called count that keeps its value between calls, which is how a script remembers things. Notice also that the whole program sits inside a VRML string, between double quotes, which is why the JavaScript uses single quotes. Escaping quotes inside quotes inside a 3D file was a rite of passage.

Building the World from Code

A script can also make new nodes. Browser.createVrmlFromString() parses VRML text and hands back the nodes, which you can send to a group's addChildren eventIn. To send events to a node directly, rather than through a route, the script needs an SFNode field pointing at it, and its directOutput field set to TRUE.

Example 8-2. A spiral staircase, built at load time

Scripts Every Frame

Route a looping TimeSensor's time eventOut into a script and the script runs on every frame. That is enough to do your own animation, simulation, or anything else that needs arithmetic. Here a script computes ripples on a pond and sends four hundred new heights to an ElevationGrid, many times a second.

Example 8-3. A pond, rippling under scripted control

That one says vrmlscript: at the top, for old times' sake.

One More: A Clock

Since SFTime counts seconds from 1970, the same way JavaScript's Date counts milliseconds, a script can tell the real time of day. This makes it the only VRML clock in this book that is right more than twice a day.

Example 8-4. A working wall clock
Table 8-1. The Browser object, as implemented here
MethodWhat it does
getName(), getVersion()Identify the browser. Ours answers truthfully.
getCurrentTime()The current time, in VRML seconds.
getCurrentFrameRate(), getCurrentSpeed()Frames per second; navigation speed.
getWorldURL()The URL or file name of the current world.
createVrmlFromString(s)Parse VRML text into an MFNode.
createVrmlFromURL(url, node, event)Fetch and parse, then send the nodes to node.event.
addRoute(a, out, b, in), deleteRoute(...)Wire and unwire routes while running.
loadURL(urls, params)Go somewhere else, like clicking an Anchor.
replaceWorld(nodes)Swap out the whole scene.
setDescription(s)Write a message on the status bar.
print(...)Write to the console. Not in the spec, but every browser had it.

Scripts run on the same thread as everything else, just like they did in 1997. A script containing while (true) { } will freeze this tab, then your patience, then possibly your browser. Save your work first.

Chapter 9: Prototypes
Chapter 9

Prototypes

If you write the same twelve lines three times, VRML would like a word.

The PROTO statement defines a new node type, written in VRML. You give it a name, an interface (the fields, eventIns, and eventOuts it should have), and a body made of ordinary nodes. Inside the body, the keyword IS connects a field of some inner node to a field of the interface. From then on you can use the new type exactly like a built-in one.

Example 9-1. Trees and blinking beacons, as new node types

Only the first node in a prototype's body is drawn. The rest (here, a TimeSensor and a Script) are alive and running but invisible, which makes the first node the prototype's "face" and the others its "brains." Each instance gets its own private copy of everything, including its own clock, which is why the three beacons blink at their own speeds.

Borrowing from Other Files

EXTERNPROTO declares a prototype that lives in another file. You repeat the interface (without default values) and give a URL, with the prototype's name after a #. Inline is simpler still: it pulls in a whole other world and drops it into yours. Between them, VRML authors built shared libraries of parts years before anyone said "package manager."

Example 9-2. A world assembled from other files

The Workbench (Chapter 11) saves worlds into your browser's storage under names like castle.wrl. Inline, Anchor, and EXTERNPROTO can all refer to those names directly, so you can build a library of parts without a web server. Write url "castle.wrl" and the browser checks My Worlds before it looks anywhere else.

Chapter 10: VRML 1.0: The Separator Years
Chapter 10

VRML 1.0: The Separator Years

In VRML 1.0, a Material node is less a property and more a mood that settles over everything after it.

The first version of the language looks familiar at a glance: nodes, braces, fields. But it works in a completely different way. A VRML 1.0 file is a list of instructions for a machine that walks the tree from top to bottom, carrying a traversal state: the current transformation, the current material, the current coordinates, and so on. A Material node does not belong to any shape. It changes the current material, and every shape drawn after it uses the new one, until something changes it again.

The Separator node is what keeps this sane. It saves the state on the way in and restores it on the way out, so whatever happens inside a Separator stays inside it. A typical 1.0 file is Separators all the way down.

Example 10-1. A VRML 1.0 world, as it might have looked in 1995

Under the hood, this browser handles 1.0 by walking the tree exactly as described, with a stack of states, and writing out equivalent VRML 2.0 nodes as it goes. The cameras become Viewpoints, each Material and shape becomes a Shape with its own Appearance, and the state machine evaporates. VRML 2.0's designers did essentially the same thing to the language itself.

The Accumulating Staircase

Here is something 1.0 can do that 2.0 cannot. A Group, unlike a Separator, does not save and restore the state. So if a Group contains a Translation, every time you USE that Group, the translation adds to the one before. The same node, used twelve times, lands in twelve different places.

Example 10-2. One step, used twelve times, climbs

This was considered a feature, then a bug, then a historical curiosity, which is the usual life cycle. VRML 2.0 made every node self-contained, and the staircase trick went the way of the dinosaurs.

Table 10-1. VRML 1.0 nodes and their 2.0 descendants
VRML 1.0VRML 2.0
SeparatorGroup or Transform (every 2.0 group is a separator)
Cube, AsciiTextBox, Text
Coordinate3, TextureCoordinate2Coordinate, TextureCoordinate, now inside the geometry
Material + MaterialBindingMaterial inside Appearance, plus a Color node
Translation, Rotation, Scale, MatrixTransformfields of Transform (no free matrices any more)
PerspectiveCameraViewpoint
ShapeHintsccw, solid, convex, creaseAngle fields on the geometry
WWWAnchor, WWWInlineAnchor, Inline
Texture2ImageTexture, PixelTexture
Chapter 11: The Companion CD-ROM
Chapter 11

The Companion CD-ROM

System requirements: a web browser from this decade. 8 MB of RAM recommended. Sound card optional.

Every computer book of the period came with a disc in an envelope glued inside the back cover. This one comes with the VRML Workbench, which you'll find below, already installed. On the left is an editor; on the right, a viewer; underneath the viewer, a console where scripts print and errors complain.

The Frontispiece

The world that loads into the Workbench the first time you visit is also the frontispiece of this book. It uses most of what we've covered: a PixelTexture floor, a gradient Background, a twisting Extrusion, an orbiting sphere, color interpolation, a Billboard, and three viewpoints. Here it is in its natural habitat.

Example 11-1. The frontispiece

Things to Try

  1. Change the cycleInterval of the Clock and press Ctrl+Enter.
  2. Delete one of the ROUTE lines and see which part stops moving.
  3. Add a TouchSensor next to the star and make the moon stop when you click it. (Hint: route touchTime to the clock's set_stopTime.)
  4. Save your world to My Worlds as moon.wrl, start a new file, and write Inline { url "moon.wrl" }.
  5. Make a mistake on purpose. Misspell a node name. The error message tells you the line and column, which is more than most of us got in 1996.
Appendix A: Node Reference
Appendix A

Node Reference

This appendix lists the interface of every VRML 2.0 node this browser knows about: each field's kind, type, name, and default value, in the same notation the specification uses. It is generated when the page loads, from the very tables the parser uses, so it cannot drift out of date. For what each field means, the specification remains the authority; see Appendix C.

Nodes marked partial are parsed and accepted but not everything about them works. The note under each says what's missing.

Appendix B: Inside This Browser
Appendix B

Inside This Browser

The engine behind this book is a little over four thousand lines of plain JavaScript modules, with no dependencies and no build step. You can read all of it in the js/vrml folder. Here is the tour.

The parser (parser.js)

A tokenizer splits the file into words, strings, and brackets, treating commas as whitespace (as the spec says) and remembering line and column numbers for error messages. A recursive-descent parser then reads statements. It knows the type of every field from the node tables, which it needs, because VRML values carry no type markers: 1 0 0 could be a color, a vector, or three separate floats, and only the field knows. The built-in node table in nodes.js is written in the spec's own interface syntax and parsed at startup by the same parser, which is a nice test that the parser works.

PROTO bodies are stored as token lists and parsed fresh for each instance, with IS wiring the inner nodes to the instance's interface. VRML 1.0 uses the same tokenizer and parser with a different node table, then goes through the converter in v1.js.

The runtime (world.js, behaviors.js)

Routes are stored in a map keyed by source node and field. Sending an event to a node stores the value (for exposedFields), passes it to that node type's behavior (an interpolator computes its output, a TimeSensor starts or stops), and then follows every route out of the node. Each route remembers the timestamp of the last event it carried and refuses a second one with the same stamp, which is the spec's loop-breaking rule in one line of code. Bindable nodes live on stacks, one per type.

Scripts (script.js)

Script source is compiled with the Function constructor and run inside a with block whose scope object is a JavaScript Proxy. Reading a name that matches a script field returns that field, converted to an SFVec3f, SFColor, or whatever its type is; assigning to it queues an event. The with statement is forbidden in modern strict-mode code, and there is something pleasing about using it to emulate 1997 scoping rules. After a function returns, any field objects it modified in place are noticed and sent as well.

Geometry (geometry.js)

Every geometry node becomes a flat list of triangles with positions, normals, colors, and texture coordinates. ElevationGrid and Extrusion generate face-set data and go through the same code as IndexedFaceSet, including crease-angle smoothing and ear-clipping for non-convex polygons. Text is drawn into a canvas and mapped onto a rectangle. Results are cached and rebuilt only when a field changes.

Rendering (renderer.js)

Each frame walks the scene graph, collecting shapes with their transforms, the lights in scope for each, and the pointing-device sensors above each. The shader is WebGL 2 and computes the VRML97 lighting equation per pixel: emissive, plus ambient, diffuse, and specular for each light, with attenuation and spotlight falloff, then linear or exponential fog. Real browsers of the period computed lighting per vertex, which is why big flat floors never showed a spotlight. We cheat in your favor.

Picking and navigation (viewer.js)

Mouse clicks become rays, tested against every triangle on the CPU after a bounding-box check. A hit tells us which sensors to wake. Navigation keeps the camera as an offset from the bound Viewpoint, so animated viewpoints carry you along, and jumps between viewpoints are blended over a fraction of a second.

What's missing

Appendix C: Further Reading
Appendix C

Further Reading

Specifications

Books of the Period

These are real books. Some are out of print, and all of them are easier to find second-hand than you might think.

Colophon
Colophon

About the Cover

The animal on the cover of VRML 2.0: The Annotated Homage is a chambered nautilus, Nautilus pompilius, shown in cross section. The nautilus lives in the deep slopes of coral reefs in the Indo-Pacific and has been doing so, in one form or another, for a very long time. Its shell is divided into a spiral of chambers. As the animal grows it builds a new, larger chamber, moves into it, and seals off the old one behind a wall. A tube called the siphuncle runs back through all the old chambers, letting the nautilus adjust the gas and liquid inside them to rise and sink.

We chose it because it seemed right for a book about a language that kept building a bigger room and moving into it: Inventor, then VRML 1.0, then 2.0, then VRML97, then X3D, with a thread running back through every one of them.

The cover engraving was not drawn by hand. It is generated by about a hundred lines of JavaScript every time the page loads, from a logarithmic spiral and some patience.

Typography

The body text is set in whatever Palatino your computer has, falling back to Georgia; headings in a Garamond, if one can be found. Code is set in Courier, because of course it is. The control bars on the viewers are set in the system font of a popular 1995 operating system, or the closest thing your machine can manage.

About This Edition

This is an unofficial, non-commercial, educational tribute. It is not affiliated with, sponsored by, or endorsed by ISO, the IEC, the Web3D Consortium, or any company or person mentioned in it. VRML is an international standard; the specification text belongs to its publishers and is not reproduced here. Product and company names mentioned in the history chapter are trademarks of their respective owners and appear only to tell the story accurately. "Pandymic Press" is not a real publisher; it is a website with ideas above its station.

The source code for this book and its VRML browser is available under the MIT License. See the LICENSE file that accompanies it.


Back to the cover