Browse Source

Proposal for VrmlParser integration as a copy-paste of the entire library

Bart McLeod 9 years ago
parent
commit
0fb04e15d2

+ 104 - 0
examples/js/libs/VrmlParser/README.md

@@ -0,0 +1,104 @@
+# VRML Parser
+I started this project by building on top of my earlier improvements of the sequential parsing that Ricardo Cabello alias Mr.Doob (The author of ThreeJs) had started. Half way this project I have taken a different approach, by moving to the PEG.js parser, which is based on Expression Grammar. An Expression Grammar offers a modern approach to writing parsers. All you do is define a grammar (The VRML 97 specification rules, basically) and the parser can be generated for you, based on the grammar. You may then use the parser to parse a VRML file and the resulting parsed node tree to create a ThreeJs file or in-memory ThreeJs world, to be rendered in a browser.
+
+The actual PEG.js grammar is in `vrml.pegjs`.
+
+## Installation
+You need to install node, npm and pegjs-require.
+
+## Usage
+The parser is implemented in JavaScript and can be run by node from the command line. An example is added: `vrml-parser.js`. Just run
+`node vrml-parser.js` on the command line to see the experimental output.
+
+To parse a different file, edit `vrml-parser.js` and change the path to the `test.wrl` file that is being loaded, 
+or paste the contents of your VRML file into `test.wrl`.
+
+Originally, `test.wrl` contains the same content as the `house.wrl` from the ThreeJs examples, except for
+an occasional typo I had to fix to make the parser succeed.
+
+### How to use the parser in the browser
+
+*For your convenience, a pre-parsed `vrml.js` has been added to the project. It is ready for you to use in a browser. It exposes the vrmlParser global variable as the parser.*
+
+*Also, you may serve the example.html file locally, for example using the built-in webserver that comes with php, to see a working example of the VrmlParser and the ThreeJsRenderer. Currently, it offers no navigation options, so you
+can't walk through.*
+
+To use the parser in the Browser, you cannot make use of the nodeJs approach, you will have to generate a browser friendly version of the parser. It can be generated using the following command:
+
+```
+pegjs -e vrmlParser vrml.pegjs
+```
+*See also http://pegjs.org/documentation#generating-a-parser-command-line*
+
+This generates the parser as a JavaScript file, that can be loaded in the html page using a <cript> tag like any other JavaScript file.
+
+Note that the options come before the input file name. Running this command will generate the parser in `vrml.js`, which you can then load in the browser. The parser will be available to you as vrmlParser:
+
+```
+var xhrLoader = new THREE.XHRLoader();
+// onLoad, onProgress, onError
+xhrLoader.load("wrl/brinkweg/single_brinkweg.wrl", function (data){
+    var tree = vrmlParser.parse(data);
+    consele.log(tree);
+}, function (){},function(){});
+
+```
+Instead of using the THREE.XHRLoader you could use XMLHttpRequest. For the browser, you do not need a console renderer to get visual textual output, you may just call `console.log(tree);` and you will be able to expand all nodes in the browser console, to verify that your VRML file has been parsed as expected.
+
+### Inline nodes
+The parser currently makes no attempt to load Inline nodes. It only parses them as an Inline node with
+a single property: the url. Depending on the type of renderer you use, you might want to feed the VRML file
+found at the url to the parser in turn, to get its contents as a node tree. If you are not writing a renderer,
+but would still like to see the contents of your whole VRML world, including Inline nodes, you may
+use `php make_single_file.php input.wrl [output-dir]`. For this command you need php. It will output 
+the file `single_input.wrl` in the same directory as `input.wrl` or in the `output-dir` if you specified
+one. Once you've got `single_input.wrl` you may use that as input in the `vrml-parser.js` and run that through
+`node vrml-parser.js` to see the full tree on the console.
+
+
+### Parser errors and reporting issues
+If the parser fails with information about a location in your VRML file, it will report the position
+in the terminal output. This might help you in correcting any errors in your VRML file, if you feel the
+error message is correct. Otherwise, please report an issue on github: https://github.com/bartmcleod/VrmlParser
+
+## Dependencies
+1. NodeJs
+2. npm
+3. pegjs-require
+4. fs
+5. php, if you want to merge multiple VRML files into one.
+
+## Milestones
+1. Working, experimental grammar, based on experience, memory and sample files. It should be able to parse house.wrl, from the ThreeJs examples.
+2. Refactoring the example VRML loader for ThreeJS to use the experimental grammar based PEG.js parser instead of line by line parsing.
+3. Adding support for all parsed nodes, including animation to the VRML loader from the ThreeJS examples.
+4. Refining the parser to support the VRML 97 specification more closely, based on the specification and strict test files.
+5. Automated testing
+
+### Next Milestone
+*Refactoring the example VRML loader for ThreeJS to use the experimental grammar based PEG.js parser instead of line by line parsing.*
+
+In order for this to work, the whole stack we used so far has to work in the browser, if we want to do the parsing of the VRML at the same time as rendering it in ThreeJs. We can simplify the milestone by splitting the process:
+1. Generate a parser script that can be used in a browser (see instruction above).
+2. Load the script in an HTML page
+3. Write a ThreeJs renderer to render the output of the parser to a ThreeJs scene.
+4. Integrate the results with the ThreeJs VrmlLoader
+
+
+## Nice to have
+Now that I have defined a Grammar that does a basic job of parsing VRML in JavaScript, it would be nice to have
+something similar that would add PEG.js grammar support and VRML support to my favorite IDE: PHPStorm, or
+more generally speaking, IntelliJ IDEA.
+
+## How you can help
+If you are interested in the projects, please test as many of your VRML files as possible and post any issues you find with your files, along with the file that yields errors and a description of 
+the issue.
+
+### Coding style
+Although my first commits did not adhere to any specific coding style, I decided to follow best practices
+already validated by the node community. We should adhere to the coding style as described here: http://nodeguide.com/style.html
+
+## License
+You are free to use the Grammar any way you like, with the only restriction that you should mention 
+the original author: Bart McLeod. If you do not remove any @copyright and @author annotations you will
+be fine.

+ 41 - 0
examples/js/libs/VrmlParser/Renderer/Console.js

@@ -0,0 +1,41 @@
+/**
+ * @author Bart McLeod, [email protected]
+ * @since 2016-05-10
+ *
+ * The VrmlParser/Renderer/Console renders the node tree as text output to the console,
+ * so that one can see what it is parsing.
+ */
+
+module.exports = {
+  depth: 0,
+  decoration: '',
+  /**
+   * Render method, that takes the output from the VrmlParser as input and
+   * writes a textual representation of the node tree to the console.
+   *
+   * @param nodeTree
+   */
+  render: function (tree) {
+
+    this.decoration = '';
+    // determine decoration base on depth
+    for (var j = 0; j < this.depth; j++) {
+      this.decoration += '-';
+    }
+
+    for (var a in tree) {
+      if ('string' === typeof a) {
+
+        var value = tree[a];
+        if ('object' === typeof value) {
+          this.depth++;
+          console.log(this.decoration + a);
+          this.render(value);
+          this.depth--;
+        } else {
+          console.log(this.decoration + a + ': ' + tree[a]);
+        }
+      }
+    }
+  }
+};

+ 541 - 0
examples/js/libs/VrmlParser/Renderer/ThreeJsRenderer.js

@@ -0,0 +1,541 @@
+/**
+ * Not written as a NodeJs module yet, because this would require to use browserify
+ * to make it available in the browser, while it is onlly useful in the browser anyway.
+ *
+ * @copyright Bart McLeod 2016, [email protected]
+ * @author Bart McLeod / http://spaceweb.nl/
+ */
+if ( 'undefined' === typeof VrmlParser ) {
+  VrmlParser = {};
+}
+
+if ( 'undefined' === typeof VrmlParser.Renderer ) {
+  VrmlParser.Renderer = {};
+}
+
+VrmlParser.Renderer.ThreeJsRenderer = function () {
+};
+
+VrmlParser.Renderer.ThreeJsRenderer.prototype = {
+  REVISION: 1,
+  constructor: VrmlParser.Renderer.ThreeJsRenderer,
+
+  log: function () {
+    console.log.apply(console, arguments);
+  },
+
+  warn: function () {
+    console.warn.apply(console, arguments);
+  },
+
+  error: function () {
+    console.error.apply(console, arguments);
+  },
+
+  /**
+   * @param Object nodeTree
+   * @param THREE.Scene scene
+   */
+  render: function (nodeTree, scene) {
+
+    console.log('VrmlParser.Renderer.ThreeJsRenderer ' + this.REVISION);
+
+    /**
+     * Colors ar return by the parser as vector{x, y, z}.
+     * We want them as color{r, g, b}.
+     * @param vector
+     */
+    var convertVectorToColor = function (vector) {
+      return {r: vector.x, g: vector.y, b: vector.z};
+    }
+
+    /**
+     * Interpolates colors a and b following their relative distance
+     * expressed by t.
+     *
+     * @param float a
+     * @param float b
+     * @param float t
+     * @returns {Color}
+     */
+    var interpolateColors = function (a, b, t) {
+      a = convertVectorToColor(a);
+      b = convertVectorToColor(b);
+      var deltaR = a.r - b.r;
+      var deltaG = a.g - b.g;
+      var deltaB = a.b - b.b;
+
+      var c = new THREE.Color();
+
+      c.r = a.r - t * deltaR;
+      c.g = a.g - t * deltaG;
+      c.b = a.b - t * deltaB;
+
+      return c;
+
+    };
+
+    /**
+     * Vertically paints the faces interpolating between the
+     * specified colors at the specified angels. This is used for the Background
+     * node, but could be applied to other nodes with multiple faces as well.
+     *
+     * When used with the Background node, default is directionIsDown is true if
+     * interpolating the skyColor down from the Zenith. When interpolationg up from
+     * the Nadir i.e. interpolating the groundColor, the directionIsDown is false.
+     *
+     * The first angle is never specified, it is the Zenith (0 rad). Angles are specified
+     * in radians. The geometry is thought a sphere, but could be anything. The color interpolation
+     * is linear along the Y axis in any case.
+     *
+     * You must specify one more color than you have angles at the beginning of the colors array.
+     * This is the color of the Zenith (the top of the shape).
+     *
+     * @param geometry
+     * @param radius
+     * @param angles
+     * @param colors
+     * @param boolean directionIsDown Whether to work bottom up or top down.
+     */
+    var paintFaces = function (geometry, radius, angles, colors, directionIsDown) {
+      // @todo: while this is all neat and jolly, we really should declare each variable on its own line
+      var f, n, p, vertexIndex, color;
+
+      var direction = directionIsDown ? 1 : -1;
+
+      var faceIndices = ['a', 'b', 'c', 'd'];
+
+      var coord = [], aColor, bColor, t = 1, A = {}, B = {}, applyColor = false, colorIndex;
+
+      for ( var k = 0; k < angles.length; k++ ) {
+
+        var vec = {};
+
+        // push the vector at which the color changes
+        vec.y = direction * ( Math.cos(angles[k]) * radius );
+
+        vec.x = direction * ( Math.sin(angles[k]) * radius );
+
+        coord.push(vec);
+
+      }
+
+      // painting the colors on the faces
+      for ( var i = 0; i < geometry.faces.length; i++ ) {
+
+        f = geometry.faces[i];
+
+        n = ( f instanceof THREE.Face3 ) ? 3 : 4;
+
+        for ( var j = 0; j < n; j++ ) {
+
+          vertexIndex = f[faceIndices[j]];
+
+          p = geometry.vertices[vertexIndex];
+
+          for ( var index = 0; index < colors.length; index++ ) {
+
+            // linear interpolation between aColor and bColor, calculate proportion
+            // A is previous point (angle)
+            if ( index === 0 ) {
+
+              A.x = 0;
+              A.y = directionIsDown ? radius : -1 * radius;
+
+            } else {
+
+              A.x = coord[index - 1].x;
+              A.y = coord[index - 1].y;
+
+            }
+
+            // B is current point (angle)
+            B = coord[index];
+
+            if ( undefined !== B ) {
+
+              // p has to be between the points A and B which we interpolate
+              applyColor = directionIsDown ? p.y <= A.y && p.y > B.y : p.y >= A.y && p.y < B.y;
+
+              if ( applyColor ) {
+
+                bColor = colors[index + 1];
+
+                aColor = colors[index];
+
+                // below is simple linear interpolation
+                t = Math.abs(p.y - A.y) / ( A.y - B.y );
+
+                // to make it faster, you can only calculate this if the y coord changes, the color is the same for points with the same y
+                color = interpolateColors(aColor, bColor, t);
+
+                f.vertexColors[j] = color;
+
+              }
+
+            } else if ( undefined === f.vertexColors[j] ) {
+
+              colorIndex = directionIsDown ? colors.length - 1 : 0;
+              f.vertexColors[j] = convertVectorToColor(colors[colorIndex]);
+
+            }
+
+          }
+
+        }
+
+      }
+
+    };
+
+    /**
+     * Utility to quickly and safely check if a given property is
+     * present and set on a node.
+     *
+     * @param string property
+     * @return boolean
+     */
+    var has = function (property) {
+      // note that this pull the object the 'has' method is assigned to into this functions scope
+      return ('undefined' !== typeof this[property] && null !== this[property]);
+    };
+
+    /**
+     * Convert VRML node representation into a ThreeJS 3D object.
+     *
+     * @param object node VRML node as parsed by the VrmlParser.
+     * @returns {THREE.Object3D}
+     */
+    var parseNode = function (node) {
+      if ( undefined === node.node ) {
+        // not a node, for now, ignore it
+        return false;
+      }
+
+      // for syntactic sugar only:
+      node.has = has;
+
+      // this will be the returned ThreeJS object returned from parseNode, if not overwritten
+      var object = new THREE.Object3D();
+
+      switch ( node.node ) {
+        case 'Group':
+        case 'Transform':
+          if ( node.has('children') ) {
+            // sugar
+            node.children.has = has;
+            // children can be a node or an array
+            if ( node.children.has('node') ) {
+              // children is a node
+              object.add(parseNode(node.children));
+            } else if ( node.children.has('length') ) {
+              // children should be an array
+              for ( var i = 0; i < node.children.length; i++ ) {
+
+                var child = node.children[i];
+                var threeJsObj = parseNode(child);
+                if ( false !== threeJsObj ) {
+                  object.add(threeJsObj);
+                }
+
+              }
+            }
+          }
+
+          if ( node.has('translation') ) {
+
+            var t = node.translation;
+
+            object.position.set(t.x, t.y, t.z);
+
+          }
+
+          if ( node.has('rotation') ) {
+
+            var r = node.rotation;
+
+            object.quaternion.setFromAxisAngle(new THREE.Vector3(r.x, r.y, r.z), r.radians);
+
+          }
+
+          if ( node.has('scale') ) {
+
+            var s = node.scale;
+
+            object.scale.set(s.x, s.y, s.z);
+
+          }
+          break;
+
+        case 'Shape':
+          object = new THREE.Mesh();
+
+          if ( node.has('geometry') ) {
+            object.geometry = parseNode(node.geometry);
+          }
+
+          if ( node.has('appearance') ) {
+            var appearance = node.appearance;
+
+            // sugar
+            appearance.has = has;
+
+            if ( appearance.has('material') ) {
+              var vrmlMaterial = appearance.material;
+
+              // sugar
+              vrmlMaterial.has = has;
+
+              var material = new THREE.MeshPhongMaterial();
+
+              if ( vrmlMaterial.has('diffuseColor') ) {
+
+                var materialColor = convertVectorToColor(vrmlMaterial.diffuseColor);
+
+                material.color.setRGB(materialColor.r, materialColor.g, materialColor.b);
+
+              }
+
+              if ( vrmlMaterial.has('emissiveColor') ) {
+
+                var emissiveColor = convertVectorToColor(vrmlMaterial.emissiveColor);
+
+                material.emissive.setRGB(emissiveColor.r, emissiveColor.g, emissiveColor.b);
+
+              }
+
+              if ( vrmlMaterial.has('specularColor') ) {
+
+                var specularColor = convertVectorToColor(vrmlMaterial.specularColor);
+
+                material.specular.setRGB(specularColor.r, specularColor.g, specularColor.b);
+
+              }
+
+              if ( vrmlMaterial.has('transparency') ) {
+
+                // transparency is opposite of opacity
+                material.opacity = Math.abs(1 - vrmlMaterial.transparency);
+
+                material.transparent = true;
+
+              }
+
+            }
+
+            object.material = material;
+
+            if ( 'ImageTexture' === vrmlMaterial.node ) {
+
+              var textureName = vrmlMaterial.textureName;
+
+              if ( textureName ) {
+
+                object.material.name = textureName[1];
+
+                object.material.map = textureLoader.load(texturePath + textureName[1]);
+
+              }
+
+            }
+
+            if ( 'IndexedFaceSet' === node.geometry.node ) {
+              //if ( false === node.geometry.node.solid ) {
+
+                object.material.side = THREE.DoubleSide;
+
+             // }
+            }
+
+          }
+
+          break;
+
+        case 'Background':
+          object = false;
+
+          var segments = 20;
+
+          // sky (full sphere):
+
+          var radius = 2e4;
+
+          var skyGeometry = new THREE.SphereGeometry(radius, segments, segments);
+          var skyMaterial = new THREE.MeshBasicMaterial({fog: false, side: THREE.BackSide});
+
+          if ( node.skyColor.length > 1 ) {
+
+            paintFaces(skyGeometry, radius, node.skyAngle, node.skyColor, true);
+
+            skyMaterial.vertexColors = THREE.VertexColors;
+
+          } else {
+
+            var color = convertVectorToColor(node.skyColor[0]);
+
+            skyMaterial.color.setRGB(color.r, color.g, color.b);
+
+          }
+
+          scene.add(new THREE.Mesh(skyGeometry, skyMaterial));
+
+          // ground (half sphere):
+
+          if ( node.has('groundColor') ) {
+
+            radius = 1.2e4;
+
+            var groundGeometry = new THREE.SphereGeometry(radius, segments, segments, 0, 2 * Math.PI, 0.5 * Math.PI, 1.5 * Math.PI);
+            var groundMaterial = new THREE.MeshBasicMaterial({
+              fog: false,
+              side: THREE.BackSide,
+              vertexColors: THREE.VertexColors
+            });
+
+            paintFaces(groundGeometry, radius, node.groundAngle, node.groundColor, false);
+
+            scene.add(new THREE.Mesh(groundGeometry, groundMaterial));
+          }
+
+          break;
+
+        case 'Box':
+          var s = node.size;
+          object = new THREE.BoxGeometry(s.x, s.y, s.z);
+          break;
+
+        case 'Cylinder':
+          object = new THREE.CylinderGeometry(node.radius, node.radius, node.height);
+          break;
+
+        case 'Cone':
+          object = new THREE.CylinderGeometry(node.topRadius, node.bottomRadius, node.height);
+          break;
+
+        case 'Sphere':
+          object = new THREE.SphereGeometry(node.radius);
+          break;
+
+        case 'IndexedFaceSet':
+
+          object = new THREE.Geometry();
+
+          var indexes, uvIndexes, uvs;
+
+          var vec;
+
+          if ( node.has('texCoord') ) {
+
+            uvs = node.texCoord.points;
+
+          }
+
+          if ( node.has('coord') ) {
+
+            for ( var k = 0, l = node.coord.point.length; k < l; k++ ) {
+
+              var point = node.coord.point[k];
+
+              vec = new THREE.Vector3(point.x, point.y, point.z);
+
+              object.vertices.push(vec);
+
+            }
+
+          }
+
+          var skip = 0;
+
+          // some shapes only have vertices for use in other shapes
+          if ( node.has('coordIndex') ) {
+
+            // read this: http://math.hws.edu/eck/cs424/notes2013/16_Threejs_Advanced.html
+            for ( var i = 0, j = node.coordIndex.length; i < j; i++ ) {
+
+              indexes = node.coordIndex[i];
+
+              if ( node.has('texCoordIndex') ) {
+                uvIndexes = node.texCoordIndex[i];
+              }
+
+              // vrml support multipoint indexed face sets (more then 3 vertices). You must calculate the composing triangles here
+              skip = 0;
+
+              // Face3 only works with triangles, but IndexedFaceSet allows shapes with more then three vertices, build them of triangles
+              while ( indexes.length >= 3 && skip < ( indexes.length - 2 ) ) {
+
+                var face = new THREE.Face3(
+                  indexes[0],
+                  indexes[skip + (node.ccw ? 1 : 2)],
+                  indexes[skip + (node.ccw ? 2 : 1)],
+                  null // normal, will be added later
+                  // todo: pass in the color, if a color index is present
+                );
+
+                if ( uvs && uvIndexes ) {
+                  object.faceVertexUvs [0].push([
+                    new THREE.Vector2(
+                      uvs[uvIndexes[0]].x,
+                      uvs[uvIndexes[0]].y
+                    ),
+                    new THREE.Vector2(
+                      uvs[uvIndexes[skip + (node.ccw ? 1 : 2)]].x,
+                      uvs[uvIndexes[skip + (node.ccw ? 1 : 2)]].y
+                    ),
+                    new THREE.Vector2(
+                      uvs[uvIndexes[skip + (node.ccw ? 2 : 1)]].x,
+                      uvs[uvIndexes[skip + (node.ccw ? 2 : 1)]].y
+                    )
+                  ]);
+                }
+
+                skip++;
+
+                object.faces.push(face);
+
+              }
+
+            }
+
+          }
+
+          object.computeFaceNormals();
+
+          //object.computeVertexNormals(); // does not show
+
+          object.computeBoundingSphere();
+
+          break;
+      }
+
+      if ( false !== object ) {
+        if ( undefined !== object.userData ) {
+          // keep the original VRML node for reference
+          object.userData.originalVrmlNode = node;
+        }
+
+        if ( node.has('name') ) {
+          object.name = node.name;
+        } else if ( node.has('node') ) {
+          object.name = node.node;
+        }
+        object.castShadow = true;
+        object.receiveShadow = true;
+      }
+      return object;
+    };
+
+    for ( var n = 0; n < nodeTree.length; n++ ) {
+      var childNode = parseNode(nodeTree[n]);
+      if ( false !== childNode ) {
+        scene.add(childNode);
+      }
+    }
+
+    console.log(scene);
+    // @todo: parse nodeTree.nodeDefinitions
+    // @todo: parse nodeTree.routes
+
+  }
+
+};

+ 39 - 0
examples/js/libs/VrmlParser/SimpleIndexedFaceSet.wrl

@@ -0,0 +1,39 @@
+#VRML V2.0 utf8
+
+Transform {
+	# a comment about the transform node
+	children [
+		Shape {
+            appearance Appearance {
+                material Material {
+                    diffuseColor 1 1 0
+                    emissiveColor 0.1 0 0
+                }
+            }
+            geometry IndexedFaceSet {
+                color             NULL
+                coord  Coordinate {
+                    point [
+                        0 1 0,
+                        1 0 0,
+                        0 0 1,
+                    ]
+                }
+                normal            NULL
+                texCoord          NULL
+                ccw               TRUE
+                colorIndex        [ ]
+                colorPerVertex    TRUE
+                convex            TRUE # more commenting here
+                coordIndex  [
+                    0 ,1 , 2, -1,
+                ]
+            }
+        }
+	]
+    rotation 0.4 0.2 0.1 0
+    translation -6 2 0
+    scale 4 4 4 # another comment
+}
+
+#the comment at the vrml root level

+ 184 - 0
examples/js/libs/VrmlParser/example.html

@@ -0,0 +1,184 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>Proof of Concept VRML loading with ThreeJs and VrmlParser</title>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <style>
+        body {
+            font-family: Monospace;
+            background-color: #000;
+            color: #fff;
+            margin: 0px;
+            overflow: hidden;
+        }
+
+        #info {
+            color: #fff;
+            position: absolute;
+            top: 10px;
+            width: 100%;
+            text-align: center;
+            z-index: 100;
+            display: block;
+        }
+
+        #info a, .button {
+            color: #f00;
+            font-weight: bold;
+            text-decoration: underline;
+            cursor: pointer
+        }
+    </style>
+
+</head>
+
+<body>
+<div id="info">
+    <a href="http://threejs.org" target="_blank">three.js / VrmlParser</a> -
+    vrml format loader test using VrmlParser -
+</div>
+
+<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r76/three.js"></script>
+
+<script src="vrml.js"></script>
+<script src="Renderer/ThreeJsRenderer.js"></script>
+
+<script type="x-shader/x-vertex" id="vertexShader">
+
+      			varying vec3 vWorldPosition;
+
+      			void main() {
+
+      				vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
+      				vWorldPosition = worldPosition.xyz;
+
+      				gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
+
+      			}
+
+
+
+</script>
+
+<script type="x-shader/x-fragment" id="fragmentShader">
+
+      			uniform vec3 topColor;
+      			uniform vec3 bottomColor;
+      			uniform float offset;
+      			uniform float exponent;
+
+      			varying vec3 vWorldPosition;
+
+      			void main() {
+
+      				float h = normalize( vWorldPosition + offset ).y;
+      				gl_FragColor = vec4( mix( bottomColor, topColor, max( pow( h, exponent ), 0.0 ) ), 1.0 );
+
+      			}
+
+
+
+</script>
+<script>
+
+    var container, stats;
+
+    var camera, controls, scene, renderer;
+
+    var cross;
+
+    init();
+    animate();
+
+    function init() {
+
+        camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 1e10);
+        camera.position.z = 6;
+
+        scene = new THREE.Scene();
+
+        scene.fog = new THREE.Fog(0xffffff, 1, 5000);
+        scene.fog.color.setHSL(0.6, 0, 1);
+
+        scene.add(camera);
+
+        // light
+
+        var dirLight = new THREE.DirectionalLight(0xffffff);
+        dirLight.position.set(200, 200, 1000).normalize();
+
+        var ambientLight = new THREE.AmbientLight(0x444444);
+        dirLight.position.set(-200, -200, 1000).normalize();
+
+        camera.add(dirLight);
+        scene.add(ambientLight);
+        camera.add(dirLight.target);
+
+        // VRML parser example:
+        var xhrLoader = new THREE.XHRLoader();
+        // onLoad, onProgress, onError
+        xhrLoader.load('example.wrl', function (data) {
+            try {
+                var tree = vrmlParser.parse(data);
+            } catch ( e ) {
+                console.log('Exception with message ' + e.message);
+
+                if ( undefined !== e.location ) {
+                    console.log('Exception at location start: offset: ' + e.location.start.offset + ' line: ' + e.location.start.line + ' column: ' + e.location.start.column);
+                    console.log('Exception at location end: offset: ' + e.location.end.offset + ' line: ' + e.location.end.line + ' column: ' + e.location.end.column);
+                }
+
+                return;
+            }
+            console.log(tree);
+            var renderer = new VrmlParser.Renderer.ThreeJsRenderer();
+            renderer.render(tree, scene);
+
+        }, function () {
+        }, function (error) {
+            var request = error.target;
+            if ( 404 === request.status ) {
+                console.log('VRML Document not found at ' + request.responseURL);
+            }
+            console.log(error);
+        });
+
+        // renderer
+
+        renderer = new THREE.WebGLRenderer({antialias: true});
+        renderer.setClearColor(0x000000, 1);
+        renderer.setSize(window.innerWidth, window.innerHeight);
+
+        container = document.createElement('div');
+        document.body.appendChild(container);
+        container.appendChild(renderer.domElement);
+
+        //
+
+        window.addEventListener('resize', onWindowResize, false);
+
+    }
+
+    function onWindowResize() {
+
+        camera.aspect = window.innerWidth / window.innerHeight;
+        camera.updateProjectionMatrix();
+
+        renderer.setSize(window.innerWidth, window.innerHeight);
+
+
+    }
+
+    function animate() {
+
+        requestAnimationFrame(animate);
+
+        renderer.render(scene, camera);
+
+    }
+
+</script>
+
+</body>
+</html>

+ 5183 - 0
examples/js/libs/VrmlParser/example.wrl

@@ -0,0 +1,5183 @@
+#VRML V2.0 utf8
+
+
+Group {
+children[
+DirectionalLight {
+  ambientIntensity  0
+  color             1 1 0.9
+  direction         0.5 -1.5 -1
+  intensity         1.8
+  on                TRUE
+}
+
+DirectionalLight {
+  ambientIntensity  0
+  color             1 1 0.9
+  direction         -0.5 1 0
+  intensity        1
+  on                TRUE
+}
+
+NavigationInfo {
+
+  avatarSize       [ 0.2, 1.4, 0.5 ]
+
+  headlight        FALSE
+
+
+}
+
+
+
+
+DirectionalLight {
+  ambientIntensity  0
+  color             0.6 0.6 0.8
+  direction         -1 0.5 2
+  intensity         1
+  on                TRUE
+}
+
+Background {
+  groundAngle  [ 1.5 1.6 ]
+  groundColor  [ 0.2 0.6 0.3, 0.4 0.4 0.35, 0.3 0.5 0.6  ]
+  backUrl      []
+  bottomUrl    []
+  frontUrl     []
+  leftUrl      []
+  rightUrl     []
+  topUrl       []
+  skyAngle     [ 1.5 ]
+  skyColor     [ 0.5 0.7 1,	0.7 1 0.9 ]
+
+}
+
+Viewpoint {
+	fieldOfView    1
+	jump           TRUE
+	orientation     0 1 0 -0.5
+	position       -4 2 10
+
+	description    "Entry"
+}
+
+Viewpoint {
+	fieldOfView    1
+	jump           TRUE
+	orientation    0 0 1  0
+	position       3.5 1.7 13
+
+	description    "Vooraanzicht"
+}
+
+Viewpoint {
+	fieldOfView    1
+	jump           TRUE
+	orientation    0 0 1  0
+	position       2.5 1.5 -4.5
+
+	description    "Woonkamer"
+}
+
+Viewpoint {
+	fieldOfView    1
+	jump           TRUE
+	orientation    0 0 1  0
+	position       1.5 1.5 -1.7
+
+	description    "Keuken"
+}
+
+
+
+Viewpoint {
+	fieldOfView    1
+	jump           TRUE
+	orientation    0 1 0  3.1415927
+	position       2.5 2.5 -20
+
+	description    "Achteraanzicht"
+}
+
+Viewpoint {
+	fieldOfView    1
+	jump           TRUE
+	orientation    1 0 0  -1.5707963
+	position       2.5 20 -4.5
+
+	description    "Bovenaanzicht"
+}
+
+
+
+
+
+# beneden/beneden.wrl
+
+
+
+
+DEF vloer Transform {
+	children [
+		Shape {
+appearance Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      1 0.5 0.3
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						0.14	0	-0.14,
+						5.54	0	-0.14,
+						5.54	0	-8.86,
+						0.14	0	-8.86,
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+	0, 1, 2, 3, -1,
+	0, 3, 2, 1, -1,
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             TRUE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+
+DEF drempelvoor Transform {
+children[
+	Shape {
+		appearance 	Appearance {
+			material 	Material {
+	ambientIntensity  0.2
+	diffuseColor      0.4 0.4 0.4
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+					}
+				}
+		geometry 	Box {
+	size	1.23 0.04 0.30
+				}
+
+	}
+ ]
+translation		3.805	0.02 -0.14
+}
+
+DEF drempelachter Transform {
+children[
+	Shape {
+		appearance 	Appearance {
+			material 	Material {
+	ambientIntensity  0.2
+	diffuseColor      0.4 0.4 0.4
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+					}
+				}
+		geometry 	Box {
+	size	1.05 0.04 0.30
+				}
+
+	}
+ ]
+translation		1.725	0.02 -8.86
+}
+
+
+DEF stukmuur1 Transform {
+children[
+	Shape {
+	appearance DEF wandkleur Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      1 0.9 0.7
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+		geometry 	Box {
+	size	0.70 2.70 0.07
+				}
+
+	}
+ ]
+translation		0.63	1.35 -3.395
+}
+
+DEF stukmuur2 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	1.41 2.70 0.07
+				}
+
+	}
+ ]
+translation		2.485	1.35 -3.395
+}
+
+
+DEF stukmuur3 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	0.43 2.70 0.07
+				}
+
+	}
+ ]
+translation		4.205	1.35 -3.395
+}
+
+DEF stukmuur4 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	1.06 2.8 0.07
+				}
+
+	}
+ ]
+translation		4.87	1.4 -4.395
+}
+
+DEF stukmuur5 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	0.07 2.70 2.28
+				}
+
+	}
+ ]
+translation		3.155	1.35 -1.42
+}
+
+DEF stukmuur6 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	0.98 2.8 0.07
+				}
+
+	}
+ ]
+translation		4.91	1.4 -2.325
+}
+
+DEF stukmuur7 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	0.98 2.70 0.07
+				}
+
+	}
+ ]
+translation		4.91	1.35 -1.515
+}
+
+
+DEF stukmuur8 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	0.07 2.70 0.4
+				}
+
+	}
+ ]
+translation		4.455	1.35 -0.48
+}
+
+DEF stukmuur9 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	0.07 2.70 0.74
+				}
+
+	}
+ ]
+translation		4.7	1.35 -1.92
+}
+
+
+
+# binmuur.wrl
+
+
+
+
+Transform {
+	children [
+		Shape {
+appearance Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      1 0.9 0.7
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						0.28	0	-0.28, #0 linkerbenedenhoek voorgevel
+						5.40	0	-0.28,
+						5.40	5.40	-0.28,
+						0.28	5.40	-0.28, #3 linkerbovenhoek voorgevel
+
+						3.19	0	-0.28, #4 voordeur
+						4.42	0	-0.28,
+						4.42	2.30	-0.28,
+						3.19	2.30	-0.28, #7 voordeur
+
+						4.84	1.60	-0.28, #toiletraam-8
+						5.04	1.60	-0.28,
+						5.04	2.30	-0.28,
+						4.84	2.30	-0.28,
+
+						2.60	0.90	-0.28, #keukenraam-12
+						2.60	2.30	-0.28,
+						0.60	2.30	-0.28,
+						0.60	0.90	-0.28,
+
+						0.60	3.60	-0.28, #bovenraam-16
+						1.50	3.60	-0.28,
+						1.50	5.0	-0.28,
+						4.42	5.0	-0.28,
+						4.42	5.40	-0.28,
+						0.60	5.40	-0.28,
+
+						0.28	8.55	-3.15, #linkergevel-22
+						0.28	2.70	-8.72,
+						0.28	0	-8.72,
+
+						5.40	8.55	-3.15, #rechtergevel-25
+						5.40	5.40	-6.30,
+						5.40	5.40	-8.72,
+						5.40	0	-8.72,
+
+						2.25	0	-8.72, #achtergevel-29
+						2.25	0.6	-8.72,
+						4.8	0.6	-8.72,
+						4.8	5.40	-8.72,
+						1.2	0	-8.72,
+						1.2	2.70	-8.72,
+
+
+						3.19	0	-0.14, #diepte van de voordeur-35
+						4.42	0	-0.14,
+						4.42	2.30	-0.14,
+						3.19	2.30	-0.14, #diepte van de voordeur-38
+
+						2.60	0.90	-0.14, # diepte van het keukenraam-39
+						2.60	2.30	-0.14,
+						0.60	2.30	-0.14,
+						0.60	0.90	-0.14,
+
+						4.84	1.60	-0.14, #diepte van het toiletraam-43
+						5.04	1.60	-0.14,
+						5.04	2.30	-0.14,
+						4.84	2.30	-0.14,
+
+						0.60	3.60	-0.14, #diepte van het bovenraam-47
+						1.50	3.60	-0.14,
+						1.50	5.0	-0.14,
+						4.42	5.0	-0.14,
+						4.42	5.40	-0.14,
+						0.60	5.40	-0.14, #52
+
+						2.25	0	-8.86, #diepte van de achtergevel-53
+						2.25	0.6	-8.86,
+						4.8	0.6	-8.86,
+						4.8	5.40	-8.86,
+						1.2	0	-8.86,
+						1.2	2.70	-8.86,	#58
+
+						0.28	5.40	0,	#59 hoekpunt voor dakvorm zolder
+						5.40	5.40	0,
+						0.28	5.40	-6.30, #61
+						0.28	2.70	-9.0,  #62 hoekpunt voor dakvorm 1e verdieping
+
+						0.28	2.7	-0.28, #63 extra punten voor segmentering beneden
+						0.14	2.7	-0.14,
+
+						5.4	2.7	-0.28, #65
+						5.54	2.7	-0.14, #
+
+						0.28	2.7	-8.72, #67
+						0.14	2.7	-8.86, #
+
+						5.4	2.7	-8.72, #69
+						5.54	2.7	-8.86, #
+
+						1.2	2.7	-8.72, #71
+						1.2	2.7	-8.86, #
+
+						4.8	2.7	-8.72, #73
+						4.8	2.7	-8.86, #
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+		0, 4, 12, 15, -1,
+		0, 15, 12, 4, -1,
+
+		0, 15, 14, 63, -1,
+		0, 63, 14, 15, -1,
+
+		14, 13, 65, 63, -1,
+
+		4, 12, 13, 7, -1,
+
+		13, 7, 6, 65, -1,
+
+		6, 11, 10, 65, -1,
+
+		5, 8, 11, 6, -1,
+
+		5, 8, 9, 1, -1,
+
+		1, 9, 10, 65, -1,
+
+		0, 63, 67, 24, -1,
+
+		67, 24, 33, 71, -1,
+
+		28, 29, 30, 31, -1,
+
+		28, 31, 73, 69, -1,
+
+		28, 69, 65, 1, -1,
+
+		63, 65, 66, 64, -1,
+
+		65, 69, 70, 66, -1,
+
+		63, 67, 68, 64, -1,
+
+		69, 73, 74, 70, -1,
+
+		67, 71, 72, 68, -1,
+
+		5, 6, 37, 36, -1,
+
+		6, 7, 38, 37, -1,
+
+		4, 35, 38, 7, -1,
+
+		12, 39, 40, 13, -1,
+
+		13, 40, 41, 14, -1,
+
+		14, 41, 42, 15, -1,
+
+		15, 42, 39, 12, -1,
+
+		8, 43, 44, 9, -1,
+
+
+		9, 44, 45, 10, -1,
+
+
+		10, 45, 46, 11, -1,
+
+
+		11, 46, 43, 8, -1,
+
+
+		29, 53, 54, 30, -1,
+
+
+		30, 54, 55, 31, -1,
+
+
+		31, 55, 74, 73, -1,
+
+
+		33, 57, 72, 71, -1,
+
+
+
+
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             FALSE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+#benodigde tijd tot hier 2 uur(kale buitenkant van het huis )
+
+# /binmuur.wrl
+
+# buitmuur.wrl
+
+
+
+
+Transform {
+	children [
+		Shape {
+appearance DEF steen Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.8 0.4 0.3
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						0	    0	    0, # 0 linkerbenedenhoek voorgevel
+						5.68	0	    0,
+						5.68	5.40	0, # 2
+						0	    5.40	0, # 3 linkerbovenhoek voorgevel
+
+						3.19	0	    0, #voordeur
+						4.42	0	    0,
+						4.42	2.30	0, # 6
+						3.19	2.30	0, # 7 voordeur
+
+						4.84	1.60	0, #toiletraam-8
+						5.04	1.60	0,
+						5.04	2.30	0, # 10
+						4.84	2.30	0,
+
+						2.60	0.90	0, #keukenraam-12
+						2.60	2.30	0, # 13
+						0.60	2.30	0,
+						0.60	0.90	0,
+
+						0.60	3.60	0, #bovenraam-16
+						1.50	3.60	0,
+						1.50	5.0	    0,
+						4.42	5.0	    0,
+						4.42	5.40	0,
+						0.60	5.40	0,
+
+						0	8.555	-3.15, #linkergevel-22
+						0	2.7	-9.0,
+						0	0	-9.0,
+
+						5.68	8.555	-3.15, #rechtergevel-25
+						5.68	5.40	-6.3,
+						5.68	5.40	-9,
+						5.68	0	-9.0,
+
+						2.25	0	-9.0, #achtergevel-29
+						2.25	0.6	-9.0,
+						4.8	0.6	-9.0,
+						4.8	5.40	-9.0,
+						1.2	0	-9.0,
+						1.2	2.7	-9,
+
+
+						3.19	0	-0.14, #diepte van de voordeur-35
+						4.42	0	-0.14,
+						4.42	2.30	-0.14,
+						3.19	2.30	-0.14, #diepte van de voordeur-38
+
+						2.60	0.90	-0.14, # diepte van het keukenraam-39
+						2.60	2.30	-0.14,
+						0.60	2.30	-0.14,
+						0.60	0.90	-0.14,
+
+						4.84	1.60	-0.14, #diepte van het toiletraam-43
+						5.04	1.60	-0.14,
+						5.04	2.30	-0.14,
+						4.84	2.30	-0.14,
+
+						0.60	3.60	-0.14, #diepte van het bovenraam-47
+						1.50	3.60	-0.14,
+						1.50	5.0	-0.14,
+						4.42	5.0	-0.14,
+						4.42	5.40	-0.14,
+						0.60	5.40	-0.14, #52
+
+						2.25	0	-8.86, #diepte van de achtergevel-53
+						2.25	0.6	-8.86,
+						4.8	0.6	-8.86,
+						4.8	5.40	-8.86,
+						1.2	0	-8.86,
+						1.2	2.7	-8.86,	#58
+
+						0	2.7	0, #59 extra punten voor segmentering beneden
+						0.14	2.7	-0.14,
+
+						5.68	2.7	0, #61
+						5.54	2.7	-0.14, #
+
+						0	2.7	-9, #63
+						0.14	2.7	-8.86, #
+
+						5.68	2.7	-9, #65
+						5.54	2.7	-8.86, #
+
+						1.2	2.7	-9, #67
+						1.2	2.7	-8.86, #
+
+						4.8	2.7	-9, #69
+						4.8	2.7	-8.86, #
+
+
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+		0, 4, 12, 15, -1,
+
+		0, 15, 14, 59, -1,
+
+		14, 13, 61, 59,-1,
+
+
+		4, 7, 13, 12, -1,
+
+        13, 11, 61, -1,
+
+		11, 10, 61, -1,
+
+		9, 1, 61, 10, -1,
+
+		5, 1, 9, 8, -1,
+
+		5, 8, 11, 6, -1,
+
+
+		0, 59, 63, 24, -1,
+
+		1, 28, 65, 61, -1,
+
+		28, 29, 30, 31, -1,
+
+		28, 31, 69, 65, -1,
+
+		33, 24, 63, 67, -1,
+
+		5, 6, 37, 36, -1,
+
+		6, 7, 38, 37, -1,
+
+		4, 35, 38, 7, -1,
+
+		12, 13, 40, 39, -1,
+
+		13, 14, 41, 40, -1,
+
+		15, 42, 41, 14, -1,
+
+		12, 39, 42, 15, -1
+
+		8, 9, 44, 43, -1,
+
+		9, 10 , 45, 44, -1,
+
+		10, 11, 46, 45, -1,
+
+		11, 8, 43, 46, -1,
+
+		29, 53, 54, 30, -1,
+
+		30, 54, 55, 31, -1,
+
+		31, 55, 70, 69, -1,
+
+		33, 67, 68, 57, -1,
+
+		59, 61, 62, 60, -1,
+
+		59, 60, 64, 63, -1,
+
+		61, 65, 66, 62, -1,
+
+		65, 69, 70, 66, -1,
+
+		64, 68, 67, 63, -1,
+
+
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             FALSE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+Transform{#schuurtje
+children [
+
+DEF schuurvloer Transform {
+	children [
+		Shape {
+appearance Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      1 0.5 0.3
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						0.1	0	-0.1,
+						2.1	0	-0.1,
+						2.1	0	-3.1,
+						0.1	0	-3.1,
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+	0, 1, 2, 3, -1,
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             FALSE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+
+Transform {
+children [
+Shape {
+appearance USE steen
+geometry Box {	size	2.2	2.6	0.1	}
+}
+]
+translation 1.1 1.3 -0.05
+}
+
+Transform{
+children [
+Shape {
+appearance USE steen
+geometry Box {	size	2.2	2.6	0.1	}
+}
+]
+translation	1.1	1.3	-3.15
+}
+
+Transform{
+children [
+Shape {
+appearance USE steen
+geometry Box {	size	0.1	2.6	1.3	}
+}
+]
+translation		0.05	1.3	-0.75
+}
+
+Transform{
+children [
+Shape {
+appearance USE steen
+geometry Box {	size	0.1	2.6	0.75	}
+}
+]
+translation		0.05	1.3	-2.725
+}
+
+Transform{
+children [
+Shape {
+appearance USE steen
+geometry Box {	size	0.1	2.6	3	}
+}
+]
+translation		2.15	1.3	-1.6
+}
+
+Transform{
+children [
+Shape {
+appearance DEF blauw Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.3 0.4 0.7
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+geometry Box {	size	0.05	0.44	0.85	}
+}
+]
+translation		0.05	2.38	-1.875
+}
+
+Transform{#drempel van schuurdeur
+children [
+
+Transform{#deurpost
+children [
+
+DEF deurpost Shape {
+		appearance 	DEF grijs Appearance {
+			material 	Material {
+	ambientIntensity  0.2
+	diffuseColor      0.4 0.4 0.4
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+					}
+				}
+		geometry 	Box {
+	size	0.05 2.56 0.05
+				}
+	}
+]
+translation 0 1.3 0.45
+}
+
+Transform{#deurpost
+children [
+
+Shape {
+		appearance USE grijs
+		geometry 	Box {
+	size	0.05 0.05 0.85
+				}
+	}
+]
+translation 0 2.135 0
+}
+
+
+Transform{#deurpost
+children [
+
+USE deurpost
+]
+translation 0 1.3 -0.45
+}
+
+Shape {
+		appearance 	USE grijs
+		geometry 	Box {
+	size	0.1 0.04 0.95
+				}
+
+	}
+]
+translation		0.05	0.02	-1.875
+}
+
+
+]
+translation 3.48	0	7.8
+}
+
+# /buitmuur.wrl
+
+
+# trapbeneden.wrl
+
+
+
+
+DEF trapbeneden Transform {
+	children [
+		Shape {
+appearance Appearance {
+	material          Material {
+	ambientIntensity  0.2
+	diffuseColor      1 0.5 0.3
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry DEF trap IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+	-0.2	0	1, #0
+	0	0.2	1,
+	0	0.2	0,
+	-0.2	0	0, #3
+
+	0.28	0.4	1, #4
+	0.6	0.6	1,
+	0.98	0.8	1,
+	0.98	1	0.45, #7
+
+	0.98	1.2	0.25, #8
+	0.98	1.4	0,
+	0.98	1.6	-0.25,
+	0.98	1.8	-0.45, #11
+
+	0.98	2.0	-1, #12
+	0.6	2.2	-1,
+	0.28	2.4	-1,
+	0	2.6	-1, #15
+
+	0	0.6	0, #16
+	0	0.8	0,
+	0	1	0, #18
+	0	1.2	0, #19
+
+	0	1.4	0, #20
+	0	1.6	0,
+	0	1.8	0, #22
+	0	2	0, #23
+
+	0	2.2	0, #24
+	0	2.4	0,
+	0	2.6	0,
+	0	2.8	0,  #27
+
+	-0.2	0.2	1, #28
+	0	0.4	1,
+	0	0.4	0,
+	-0.2	0.2	0, #31
+
+	0.28	0.6	1, #32
+	0.6	0.8	1,
+	0.98	1	1,
+	0.98	1.2	0.45, #35
+
+	0.98	1.4	0.25, #36
+	0.98	1.6	0,
+	0.98	1.8	-0.25,
+	0.98	2	-0.45, #39
+
+	0.98	2.2	-1, #40
+	0.6	2.4	-1,
+	0.28	2.6	-1,
+	0	2.8	-1, #43
+
+	0	0	1,
+	0	0	0, #45
+	0	0.4	0, #46
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+
+	28, 1, 2, 31, -1, #1e_treevlak
+	29, 4, 46, -1, #2e
+	32, 5, 16, -1,
+	33, 6, 17, -1,
+	34, 7, 18, -1,
+	35, 8, 19, -1,
+	36, 9, 20, -1,
+	37, 10, 21, -1,
+	38, 11, 22, -1,
+	39, 12, 23, -1,
+	40, 13, 24, -1,
+	41, 14, 25, -1,
+	42, 15, 26, -1, #13e_treevlak
+
+	0, 28, 31, 3, -1, #voor_en_zijvlakken_van_de_eerste_tree
+	0, 44, 1, 28, -1,
+	3, 31, 2, 45, -1,
+
+	2, 1, 29, 30, -1, #alle_voorvlakken
+	46, 4, 32, 16, -1,
+	16, 5, 33, 17, -1,
+	17, 6, 34, 18, -1,
+	18, 7, 35, 19, -1,
+	19, 8, 36, 20, -1,
+	20, 9, 37, 21, -1,
+	21, 10, 38, 22, -1,
+	22, 11, 39, 23, -1,
+	23, 12, 40, 24, -1,
+	24, 13, 41, 25, -1,
+	25, 14, 42, 26, -1,
+	26, 15, 43, 27, -1,
+
+
+
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             FALSE
+				texCoordIndex     []
+			}
+		}
+	]
+translation 4.42	0	-3.36
+}
+
+
+
+
+
+# /trapbeneden.wrl
+
+# deuren.wrl
+
+
+
+Transform {
+children [
+DEF deurgroep Group {#DEURKEUKENWOONKAMER
+children[
+#dwarslat boven de deur
+DEF dwarslat Transform {
+	children [
+		Shape {
+appearance DEF kozijn Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.95 0.95 0.9
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry Box { size	0.8 0.05 0.05 }
+}
+
+]
+translation	0 2.035 0
+}
+
+
+
+
+DEF ruitje Transform {
+	children [
+		Shape {
+appearance DEF glas Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.6 0.6 0.9
+	emissiveColor     0 0 0
+	shininess         0.8
+	specularColor     1 1 1
+	transparency      0.7
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry Box { size	0.8 0.64 0.01 }
+}
+
+]
+translation	0 2.38 0
+}#ruit boven de deur
+
+#deur
+DEF deur Transform {
+center -0.4 0 0
+	children [
+
+
+
+DEF klikopdeur TouchSensor {}
+  DEF TimeSource TimeSensor { cycleInterval 20.0 } # Run once for 20 sec.
+  # Animeer het openzwaaien van de deur rond de Y as:
+  DEF Deuropen OrientationInterpolator {
+       key      [ 0,      0.025,	0.05,	0.95,	0.975,       1.0 ]
+       keyValue [ 0 1 0 0, 0 1 0 -1, 0 1 0 -2, 0 1 0 -2, 0 1 0 -1, 0 1 0 0 ]
+  }
+
+
+
+# ../boven/deurklink.wrl
+
+
+
+
+
+DEF deurklink Transform {
+	children [
+	    DEF enehelft	Shape {
+            appearance DEF aluminium Appearance {
+                material         Material {
+                ambientIntensity  0.2
+                diffuseColor      0.4 0.4 0.5
+                emissiveColor     0 0 0
+                shininess         0.8
+                specularColor     0.4 0.4 0.5
+                }
+                texture           NULL
+                textureTransform  NULL
+            }
+		    geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {
+					point [
+                        -0.015	0.02	0, #0
+                        0.015		0.02	0,
+                        0.02		-0.02	0,
+                        -0.02		-0.02	0, #3
+
+                        -0.01		-0.015	0.06, #4
+                        0.01		-0.015	0.06,
+                        -0.01		0.015		0.03,
+                        0.01		0.015		0.03,
+
+                        0.005		0.1		0.035, #8
+                        -0.005	0.1		0.035,
+                        -0.005	0.1		0.055,
+                        0.005		0.1		0.055,
+
+
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+
+2, 3, 4, 5, -1,
+2, 5, 4, 3, -1,
+
+4, 5, 11, 10, -1,
+4, 10, 11, 5, -1,
+
+0, 1, 7, 6, -1,
+0, 6, 7, 1, -1,
+
+6, 7, 8, 9, -1,
+6, 9, 8, 7, -1,
+
+8, 9, 10, 11, -1,
+8, 11, 10, 9, -1,
+
+0, 6, 4, 3, -1,
+0, 3, 4, 6, -1,
+
+6, 9, 10, 4, -1,
+6, 4, 10, 9, -1,
+
+1, 2, 5, 7, -1,
+1, 7, 5, 2, -1,
+
+7, 5, 11, 8, -1,
+7, 8, 11, 5, -1,
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             TRUE
+				#texCoordIndex     []
+			}
+		}#enehelft
+
+	Transform {
+		children [
+
+			USE enehelft
+		]
+	rotation 0 1 0 3.1415927
+	translation	0 0 -0.07
+	}
+	Transform {
+
+		children [
+		DEF vlakdeel Shape {
+			appearance USE aluminium
+			geometry Box { size 0.18 0.07 0.01 }
+		}#shape
+		]
+	translation -0.03 0 -0.005
+	}
+
+	Transform {
+
+		children [
+		USE vlakdeel
+		]
+	translation -0.03 0 -0.065
+	}
+
+
+
+	]
+
+translation 0.35 0 0.035
+rotation 0 0 1 1.5707963
+}#deurklink
+
+
+
+# /../boven/deurklink.wrl
+
+DEF deurvorm Shape {
+	appearance DEF deurkleur Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.8 0.8 0.8
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry Box { size	0.8 2 0.05 }
+}
+
+]
+translation	0 1.005 0
+}
+
+
+
+
+
+]
+}#deurgroep
+
+]
+translation 1.38 0 -3.395
+}
+ROUTE klikopdeur.touchTime TO TimeSource.startTime
+ROUTE TimeSource.fraction_changed TO Deuropen.set_fraction
+ROUTE Deuropen.value_changed TO deur.rotation
+
+
+#deur2
+Transform {
+children[ 	USE dwarslat
+		USE ruitje
+
+#deur
+DEF deurkeukengang Transform {
+center -0.4 0 0
+	children [
+
+
+
+	DEF klikopdeur2 TouchSensor {}
+  DEF TimeSource2 TimeSensor { cycleInterval 20.0 } # Run once for 20 sec.
+  # Animeer het openzwaaien van de deur rond de Y as:
+   DEF Deuropen2 OrientationInterpolator {
+       key      [ 0,      0.025,	0.05,	0.95,	0.975,       1.0 ]
+       keyValue [ 0 1 0 0, 0 1 0 -1, 0 1 0 -2, 0 1 0 -2, 0 1 0 -1, 0 1 0 0 ]
+  }
+USE deurklink
+USE deurvorm
+]
+translation	0 1.005 0
+}
+
+]
+translation 3.155 0 -2.96
+rotation 0 1 0 1.5707963
+}
+
+ROUTE klikopdeur2.touchTime TO TimeSource2.startTime
+ROUTE TimeSource2.fraction_changed TO Deuropen2.set_fraction
+ROUTE Deuropen2.value_changed TO deurkeukengang.rotation
+
+
+#deur3
+Transform {
+children[ 	USE dwarslat
+		USE ruitje
+#deur
+DEF deurwoonkamergang Transform {
+center -0.4 0 0
+	children [
+
+
+
+	DEF klikopdeur3 TouchSensor {}
+  DEF TimeSource3 TimeSensor { cycleInterval 20.0 } # Run once for 20 sec.
+  # Animeer het openzwaaien van de deur rond de Y as: deze deur draait tegengesteld aan de andere
+   DEF Deuropen3 OrientationInterpolator {
+       key      [ 0,      0.025,	0.05,	0.95,	0.975,       1.0 ]
+       keyValue [ 0 1 0 0, 0 1 0 1, 0 1 0 2, 0 1 0 2, 0 1 0 1, 0 1 0 0 ]
+  }
+USE deurklink
+USE deurvorm
+]
+translation	0 1.005 0
+}
+
+]
+translation 3.59 0 -3.395
+rotation 0 1 0 0
+}#deur3
+
+ROUTE klikopdeur3.touchTime TO TimeSource3.startTime
+ROUTE TimeSource3.fraction_changed TO Deuropen3.set_fraction
+ROUTE Deuropen3.value_changed TO deurwoonkamergang.rotation
+
+
+#deur4
+Transform {
+children[ DEF dwarslatafw Transform {
+	children [
+		Shape {
+appearance USE kozijn
+			geometry Box { size	0.93 0.05 0.05 }
+}
+
+]
+translation	0 2.035 0
+}
+
+
+
+
+DEF ruitjeafw Transform {
+	children [
+		Shape {
+appearance USE kozijn
+			geometry Box { size	0.93 0.64 0.01 }
+}
+
+]
+translation	0 2.38 0
+}#ruit boven de deur#deur
+DEF deurwoonkamertrapkast Transform {
+center -0.4 0 0
+	children [
+
+
+
+	DEF klikopdeur4 TouchSensor {}
+  DEF TimeSource4 TimeSensor { cycleInterval 20.0 } # Run once for 20 sec.
+  # Animeer het openzwaaien van de deur rond de Y as:
+   DEF Deuropen4 OrientationInterpolator {
+       key      [ 0,      0.025,	0.05,	0.95,	0.975,       1.0 ]
+       keyValue [ 0 1 0 0, 0 1 0 -1, 0 1 0 -2, 0 1 0 -2, 0 1 0 -1, 0 1 0 0 ]
+  }
+USE deurklink
+Shape { #afwijkende deurmaat
+	appearance USE deurkleur
+			geometry Box { size	0.93 2 0.05 }
+}
+]
+translation	0 1.005 0
+}
+
+]
+translation 4.395 0 -3.895
+rotation 0 1 0 -1.5707963
+}#deur4
+
+ROUTE klikopdeur4.touchTime TO TimeSource4.startTime
+ROUTE TimeSource4.fraction_changed TO Deuropen4.set_fraction
+ROUTE Deuropen4.value_changed TO deurwoonkamertrapkast.rotation
+
+
+
+#deur5
+Transform {
+children[ DEF dwarslatafw2 Transform {
+	children [
+		Shape {
+appearance USE kozijn
+			geometry Box { size	0.74 0.05 0.05 }
+}
+
+]
+translation	0 2.035 0
+}
+
+
+
+
+DEF ruitjeafw2 Transform {
+	children [
+		Shape {
+appearance USE kozijn
+			geometry Box { size	0.74 0.64 0.01 }
+}
+
+]
+translation	0 2.38 0
+}#ruit boven de deur#deur
+
+DEF deurgangmeterkast Transform {
+center -0.4 0 0
+	children [
+
+
+
+	DEF klikopdeur5 TouchSensor {}
+  DEF TimeSource5 TimeSensor { cycleInterval 20.0 } # Run once for 20 sec.
+  # Animeer het openzwaaien van de deur rond de Y as:
+   DEF Deuropen5 OrientationInterpolator {
+       key      [ 0,      0.025,	0.05,	0.95,	0.975,       1.0 ]
+       keyValue [ 0 1 0 0, 0 1 0 -0.7, 0 1 0 -1.5, 0 1 0 -1.5, 0 1 0 -0.7, 0 1 0 0 ]
+  }
+DEF deurklinkafw Transform {
+	children [
+	USE enehelft
+	Transform {
+		children [
+
+			USE enehelft
+		]
+	rotation 0 1 0 3.1415927
+	translation	0 0 -0.07
+	}
+	Transform {
+
+		children [
+		DEF vlakdeel Shape {
+			appearance USE aluminium
+			geometry Box { size 0.18 0.07 0.01 }
+		}#shape
+		]
+	translation -0.03 0 -0.005
+	}
+
+	Transform {
+
+		children [
+		USE vlakdeel
+		]
+	translation -0.03 0 -0.065
+	}
+
+
+
+	]
+
+translation 0.32 0 0.035
+rotation 0 0 1 1.5707963
+}#deurklink
+Shape { #afwijkende deurmaat
+	appearance USE deurkleur
+			geometry Box { size	0.74 2 0.05 }
+}
+]
+translation	0 1.005 0
+}
+
+]
+translation 4.455 0 -1.92
+rotation 0 1 0 -1.5707963
+}#deur5
+
+ROUTE klikopdeur5.touchTime TO TimeSource5.startTime
+ROUTE TimeSource5.fraction_changed TO Deuropen5.set_fraction
+ROUTE Deuropen5.value_changed TO deurgangmeterkast.rotation
+
+
+#deur6
+Transform {
+children[ 	USE dwarslat
+		USE ruitje
+#deur
+DEF deurtoiletgang Transform {
+center -0.4 0 0
+	children [
+
+
+
+	DEF klikopdeur6 TouchSensor {}
+  DEF TimeSource6 TimeSensor { cycleInterval 20.0 } # Run once for 20 sec.
+  # Animeer het openzwaaien van de deur rond de Y as:
+   DEF Deuropen6 OrientationInterpolator {
+       key      [ 0,      0.025,	0.05,	0.95,	0.975,       1.0 ]
+       keyValue [ 0 1 0 0, 0 1 0 -1, 0 1 0 -2, 0 1 0 -2, 0 1 0 -1, 0 1 0 0 ]
+  }
+USE deurklink
+USE deurvorm
+]
+translation	0 1.005 0
+}
+
+]
+translation 4.455 0 -1.08
+rotation 0 1 0 -1.5707963
+}#deur6
+
+ROUTE klikopdeur6.touchTime TO TimeSource6.startTime
+ROUTE TimeSource6.fraction_changed TO Deuropen6.set_fraction
+ROUTE Deuropen6.value_changed TO deurtoiletgang.rotation
+
+
+# voordeur.wrl
+
+
+
+
+DEF voordeur Transform {
+center 1.11 0 0
+	children [
+
+		Shape {
+appearance DEF deurkleur Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.8 0.8 0.8
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+
+	0	0	0.025, #0
+	1.11	0	0.025, #
+	1.11	2.19	0.025, #
+	0	2.19	0.025, #3
+
+	0.71	0.49	0.025, #4
+	0.91	0.49	0.025, #
+	0.91	1.99	0.025, #
+	0.71	1.99	0.025, #7
+
+	0	0	-0.025, #8
+	1.11	0	-0.025, #
+	1.11	2.19	-0.025, #
+	0	2.19	-0.025, #11
+
+	0.71	0.49	-0.025, #12
+	0.91	0.49	-0.025, #
+	0.91	1.99	-0.025, #
+	0.71	1.99	-0.025, #15
+	]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+
+	0, 1, 5, 4, -1,
+
+	1, 2, 6, 5, -1,
+
+	6, 2, 3, 7, -1,
+
+	0, 4, 7, 3, -1,
+
+
+	1, 0, 8, 9, -1,
+
+	3, 2, 10, 11, -1,
+
+	0, 3, 11, 8, -1,
+
+	2, 1, 9, 10, -1,
+
+
+	5, 6, 14, 13, -1,
+
+	4, 5, 13, 12, -1,
+
+	6, 7, 15, 14, -1,
+
+	7, 4, 12, 15, -1,
+
+
+	9, 8, 12, 13, -1,
+
+	10, 9, 13, 14, -1,
+
+	11, 10, 14, 15, -1,
+
+	12, 8, 11, 15, -1,
+
+
+
+
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             TRUE
+				texCoordIndex     []
+			}
+		}
+
+DEF klikopvoordeur TouchSensor {}
+  DEF TimeSourcevoor TimeSensor { cycleInterval 20.0 } # Run once for 20 sec.
+  # Animeer het openzwaaien van de deur rond de Y as:
+  DEF VoorDeuropen OrientationInterpolator {
+       key      [ 0,      0.025,	0.05,	0.95,	0.975,       1.0 ]
+       keyValue [ 0 1 0 0, 0 1 0 -0.7, 0 1 0 -1.5, 0 1 0 -1.5, 0 1 0 -0.7, 0 1 0 0 ]
+  }
+
+DEF deurgreep Transform{
+children[
+		Shape {
+	appearance DEF aluminium Appearance {
+		material         Material {
+		ambientIntensity  0.2
+		diffuseColor      0.4 0.4 0.4
+		emissiveColor     0 0 0
+		shininess         0.8
+		specularColor     0.9 0.9 0.9
+		transparency      0
+		}
+		texture           NULL
+		textureTransform  NULL
+		}
+			geometry Box { size	0.2	0.2	0.004	}
+}
+
+]
+translation	0.15	0.9	0.067
+}
+
+
+DEF steunblokje Transform{
+children[
+		Shape {
+	appearance DEF aluminium Appearance {
+		material         Material {
+		ambientIntensity  0.2
+		diffuseColor      0.4 0.4 0.4
+		emissiveColor     0 0 0
+		shininess         0.8
+		specularColor     0.9 0.9 0.9
+		transparency      0
+		}
+		texture           NULL
+		textureTransform  NULL
+		}
+			geometry Box { size	0.14	0.14	0.04	}
+}
+
+]
+translation	0.15	0.9	0.045
+}
+
+DEF ruitjeindeur Transform{
+children[
+		Shape {
+	appearance DEF glas Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.6 0.6 0.9
+	emissiveColor     0 0 0
+	shininess         0.8
+	specularColor     1 1 1
+	transparency      0.7
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry Box { size	0.2	1.5	0.01	}
+}
+
+]
+translation	0.81	1.24	0
+}
+
+	]
+translation 3.25	0.05	-0.14
+}
+
+ROUTE klikopvoordeur.touchTime TO TimeSourcevoor.startTime
+ROUTE TimeSourcevoor.fraction_changed TO VoorDeuropen.set_fraction
+ROUTE VoorDeuropen.value_changed TO voordeur.rotation
+
+# /voordeur.wrl
+
+# achterdeur.wrl
+
+
+
+#bevat ook schuurdeur
+
+
+DEF achterdeur Transform {
+center 0 0 0
+	children [
+
+
+		Shape {
+appearance DEF deurkleur Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.8 0.8 0.8
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry DEF deurInd IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+
+	0.93	0	-0.025, #0
+	0	0	-0.025, #
+	0	2.09	-0.025, #
+	0.93	2.09	-0.025, #3
+
+	0.765	0.6	-0.025, #4
+	0.165	0.6	-0.025, #
+	0.165	1.9	-0.025, #
+	0.765	1.9	-0.025, #7
+
+	0.93	0	0.025, #0
+	0	0	0.025, #
+	0	2.09	0.025, #
+	0.93	2.09	0.025, #3
+
+	0.765	0.6	0.025, #4
+	0.165	0.6	0.025, #
+	0.165	1.9	0.025, #
+	0.765	1.9	0.025, #7
+
+	]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+
+	0, 1, 5, 4, -1,
+
+	1, 2, 6, 5, -1,
+
+	6, 2, 3, 7, -1,
+
+	0, 4, 7, 3, -1,
+
+
+	1, 0, 8, 9, -1,
+
+	3, 2, 10, 11, -1,
+
+	0, 3, 11, 8, -1,
+
+	2, 1, 9, 10, -1,
+
+
+	5, 6, 14, 13, -1,
+
+	4, 5, 13, 12, -1,
+
+	6, 7, 15, 14, -1,
+
+	7, 4, 12, 15, -1,
+
+
+	9, 8, 12, 13, -1,
+
+	10, 9, 13, 14, -1,
+
+	11, 10, 14, 15, -1,
+
+	12, 8, 11, 15, -1,
+
+
+
+
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             TRUE
+				texCoordIndex     []
+			}
+		}
+
+DEF klikopachterdeur TouchSensor {}
+  DEF TimeSourceachter TimeSensor { cycleInterval 20.0 } # Run once for 20 sec.
+  # Animeer het openzwaaien van de deur rond de Y as:
+  DEF achterDeuropen OrientationInterpolator {
+       key      [ 0,      0.025,	0.05,	0.95,	0.975,       1.0 ]
+       keyValue [ 0 1 0 0, 0 1 0 0.7, 0 1 0 1.5, 0 1 0 1.5, 0 1 0 0.7, 0 1 0 0 ]
+  }
+
+
+
+Transform {
+    # ../boven/deurklink.wrl
+
+
+
+
+
+DEF deurklink Transform {
+	children [
+	    DEF enehelft	Shape {
+            appearance DEF aluminium Appearance {
+                material         Material {
+                ambientIntensity  0.2
+                diffuseColor      0.4 0.4 0.5
+                emissiveColor     0 0 0
+                shininess         0.8
+                specularColor     0.4 0.4 0.5
+                }
+                texture           NULL
+                textureTransform  NULL
+            }
+		    geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {
+					point [
+                        -0.015	0.02	0, #0
+                        0.015		0.02	0,
+                        0.02		-0.02	0,
+                        -0.02		-0.02	0, #3
+
+                        -0.01		-0.015	0.06, #4
+                        0.01		-0.015	0.06,
+                        -0.01		0.015		0.03,
+                        0.01		0.015		0.03,
+
+                        0.005		0.1		0.035, #8
+                        -0.005	0.1		0.035,
+                        -0.005	0.1		0.055,
+                        0.005		0.1		0.055,
+
+
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+
+2, 3, 4, 5, -1,
+2, 5, 4, 3, -1,
+
+4, 5, 11, 10, -1,
+4, 10, 11, 5, -1,
+
+0, 1, 7, 6, -1,
+0, 6, 7, 1, -1,
+
+6, 7, 8, 9, -1,
+6, 9, 8, 7, -1,
+
+8, 9, 10, 11, -1,
+8, 11, 10, 9, -1,
+
+0, 6, 4, 3, -1,
+0, 3, 4, 6, -1,
+
+6, 9, 10, 4, -1,
+6, 4, 10, 9, -1,
+
+1, 2, 5, 7, -1,
+1, 7, 5, 2, -1,
+
+7, 5, 11, 8, -1,
+7, 8, 11, 5, -1,
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             TRUE
+				#texCoordIndex     []
+			}
+		}#enehelft
+
+	Transform {
+		children [
+
+			USE enehelft
+		]
+	rotation 0 1 0 3.1415927
+	translation	0 0 -0.07
+	}
+	Transform {
+
+		children [
+		DEF vlakdeel Shape {
+			appearance USE aluminium
+			geometry Box { size 0.18 0.07 0.01 }
+		}#shape
+		]
+	translation -0.03 0 -0.005
+	}
+
+	Transform {
+
+		children [
+		USE vlakdeel
+		]
+	translation -0.03 0 -0.065
+	}
+
+
+
+	]
+
+translation 0.35 0 0.035
+rotation 0 0 1 1.5707963
+}#deurklink
+
+
+
+# /../boven/deurklink.wrl
+    translation 0.50 1 0
+}
+
+
+
+
+DEF ruitindeur Transform{
+children[
+		Shape {
+	appearance DEF glas Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.6 0.6 0.9
+	emissiveColor     0 0 0
+	shininess         0.8
+	specularColor     1 1 1
+	transparency      0.7
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry Box { size	0.6	1.3	0.01	}
+}
+
+]
+translation	0.465	1.25	0
+}
+
+	]
+translation 1.26	0.05	-8.86
+}
+
+ROUTE klikopachterdeur.touchTime TO TimeSourceachter.startTime
+ROUTE TimeSourceachter.fraction_changed TO achterDeuropen.set_fraction
+ROUTE achterDeuropen.value_changed TO achterdeur.rotation
+
+#schuurdeur
+DEF schuurdeur Transform {
+children [
+
+DEF klikopschuurdeur TouchSensor {}
+  DEF TimeSourceschuur TimeSensor { cycleInterval 20.0 } # Run once for 20 sec.
+  # Animeer het openzwaaien van de deur rond de Y as:
+  DEF schuurDeuropen OrientationInterpolator {
+       key      [ 0,      0.025,	0.05,	0.95,	0.975,       1.0 ]
+       keyValue [ 0 1 0 1.5707963, 0 1 0 2.4, 0 1 0 3.4, 0 1 0 3.4, 0 1 0 2.4, 0 1 0 1.5707963 ]
+  }
+
+Transform{
+    children[
+        Shape{
+            appearance 	USE deurkleur
+            geometry	USE deurInd
+        }
+    ]
+    scale 0.9139785	1	1   #maakt de deur op maat
+}
+
+USE ruitindeur
+
+Transform {
+    children USE deurklink
+    translation 0.43 1 0
+}
+
+
+]
+translation 3.53	0.05	6.35
+rotation 0 1 0 1.5707963
+}
+
+ROUTE klikopschuurdeur.touchTime TO TimeSourceschuur.startTime
+ROUTE TimeSourceschuur.fraction_changed TO schuurDeuropen.set_fraction
+ROUTE schuurDeuropen.value_changed TO schuurdeur.rotation
+
+# /achterdeur.wrl
+
+
+
+
+# /deuren.wrl
+
+# ramen.wrl
+
+
+
+
+Transform {
+	children [
+		Shape {
+appearance DEF kozijn Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.95 0.95 0.9
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry Box { size	0.05 1.3 0.05 }
+}
+
+]
+translation	0.625 1.6 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 1.3 0.05 }
+}
+
+]
+translation	2.575 1.6 -0.14
+}
+
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 1.3 0.05 }
+}
+
+]
+translation	1.475 1.6 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 1.3 0.05 }
+}
+
+]
+translation	1.725 1.6 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	2 0.05 0.05 }
+}
+
+]
+translation	1.6 0.925 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	2 0.05 0.05 }
+}
+
+]
+translation	1.6 2.275 -0.14
+}
+
+#einde keukenraam
+
+#begin toiletraam
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 0.7 0.05 }
+}
+
+]
+translation	4.865 1.95 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 0.7 0.05 }
+}
+
+]
+translation	5.015 1.95 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.1 0.05 0.05 }
+}
+
+]
+translation	4.94 1.625 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.1 0.05 0.05 }
+}
+
+]
+translation	4.94 2.275 -0.14
+}
+
+#einde toiletraam
+#voordeur posten
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 2.26 0.05 }
+}
+
+]
+translation	3.215 1.17 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 2.26 0.05 }
+}
+
+]
+translation	4.395 1.17 -0.14
+}
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	1.13 0.05 0.05 }
+}
+
+]
+translation	3.805 2.275 -0.14
+}
+#einde deurposten
+#begin kozijnen achter
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 2.1 0.05 }
+}
+
+]
+translation	3.215 1.65 -8.86
+}
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 2.1 0.05 }
+}
+
+]
+translation	4.775	1.65 -8.86
+}
+
+Transform {
+	children [
+		Shape {
+appearance		DEF blauw Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.3 0.4 0.7
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+
+geometry	Box { size	1.51 0.5 0.05 }
+}
+
+]
+translation	3.995 2.45 -8.86
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	1.51 0.05 0.05 }
+}
+
+]
+translation	3.995 0.625 -8.86
+}#1
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	1.51 0.05 0.05 }
+}
+
+]
+translation	3.995 2.175 -8.86
+}#2
+
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.94 0.05 0.05 }
+}
+
+]
+translation	2.72 0.625 -8.86
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	1.99 0.05 0.05 }
+}
+
+]
+translation	2.195 2.675 -8.86
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	1 0.05 0.05 }
+}
+
+]
+translation	1.725 2.175 -8.86
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 2.61 0.05 }
+}
+
+]
+translation	1.225 1.345 -8.86
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 2.61 0.05 }
+}
+
+]
+translation	2.225 1.345 -8.86
+}
+
+
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 1.5 0.05 }
+}
+
+]
+translation	3.565 1.4 -8.86
+}
+
+#einde ramen achter
+
+
+
+# /ramen.wrl
+
+# ruiten.wrl
+
+
+
+
+#begin vensterglas
+
+Transform {
+	children [
+		Shape {
+appearance DEF glas Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.6 0.6 0.9
+	emissiveColor     0 0 0
+	shininess         0.8
+	specularColor     1 1 1
+	transparency      0.7
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry Box { size	0.94 2 0.01 }
+}
+
+]
+translation	2.72 1.65 -8.86
+}#ruit 5 achter
+
+#ruit1 keuken
+Transform {
+	children [
+		Shape {
+appearance USE glas
+geometry Box { size	0.8 1.3 0.01 }
+}
+
+]
+translation	1.05 1.6 -0.14
+}#ruit 1 keuken
+
+#ruit3 keuken
+Transform {
+	children [
+		Shape {
+appearance USE glas
+			geometry Box { size	0.8 1.3 0.01 }
+}
+
+]
+translation	2.15 1.6 -0.14
+}#ruit 3 keuken
+
+#ruit2 keuken
+Transform {
+	children [
+		Shape {
+appearance USE glas
+			geometry Box { size	0.2 1.3 0.01 }
+}
+
+]
+translation	1.6 1.6 -0.14
+}#ruit 2 keuken
+
+
+#toiletruit
+Transform {
+	children [
+		Shape {
+appearance USE glas
+			geometry Box { size	0.1 0.6 0.01 }
+}
+
+]
+translation	4.94 1.95 -0.14
+}#ruit van toiletraam
+
+#ramen achter
+
+
+
+
+Transform {
+	children [
+		Shape {
+appearance USE glas
+			geometry Box { size	1.16 1.5 0.01 }
+}
+
+]
+translation	4.17 1.4 -8.86
+}#ruit 3 achter
+
+Transform {
+	children [
+		Shape {
+appearance USE glas
+			geometry Box { size	0.3 1.5 0.01 }
+}
+
+]
+translation	3.39 1.4 -8.86
+}#ruit 4 achter
+
+Transform {
+	children [
+		Shape {
+appearance USE glas
+			geometry Box { size	0.95 0.45 0.01 }
+}
+
+]
+translation	1.725 2.425 -8.86
+}#ruit 6 achter
+
+# /ruiten.wrl
+
+
+# /beneden/beneden.wrl
+
+
+DEF boven Transform {
+children[
+
+Transform {
+children[
+
+Transform {
+children[
+	Shape {
+appearance DEF groen Appearance {
+	material         Material {
+	ambientIntensity  1
+	diffuseColor      0 1 0
+	emissiveColor     0 1 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry DEF pijl IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						0 0 0,
+						0.2 0.2 0,
+						0.2 -0.2 0,
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [	0,1,2,-1,
+				]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             FALSE
+				texCoordIndex     []
+			}
+}#shape
+]
+rotation 0 1 0 3.1415927
+}
+
+DEF schuifboven TouchSensor {}
+]
+translation 0.15 2.95 0.3
+}
+
+Transform {
+children[
+
+	Shape {
+appearance DEF rood Appearance {
+	material         Material {
+	ambientIntensity  1
+	diffuseColor      1 0 0
+	emissiveColor     1 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+
+geometry USE pijl
+}
+
+DEF schuifboven2 TouchSensor {}
+]
+
+translation 5.48 2.95 0.3
+}
+
+# boven/boven.wrl
+
+
+
+
+Viewpoint {
+	fieldOfView    1
+	jump           TRUE
+	orientation    1 0 0  0
+	position       2.5 3.75 -2.5
+
+	description    "Ouderslaapkamer"
+}
+
+Viewpoint {
+	fieldOfView    1
+	jump           TRUE
+	orientation    1 0 0  0
+	position       2.5 3.75 -5
+
+	description    "Tweede slaapkamer"
+}
+
+Viewpoint {
+	fieldOfView    1
+	jump           TRUE
+	orientation    1 0 0  0
+	position       4.5 3.75 -7
+
+	description    "Kinderkamer"
+}
+
+Viewpoint {
+	fieldOfView    1
+	jump           TRUE
+	orientation    1 0 0  0
+	position       4.5 3.75 -1.5
+
+	description    "Badkamer"
+}
+
+
+
+#dak van schuur
+
+Transform {
+children [
+Shape {
+appearance  DEF appdak Appearance {
+	 material          Material {
+  	ambientIntensity  0
+  	diffuseColor      0.2 0.2 0.2
+  	emissiveColor     0 0 0
+  	shininess         0
+  	specularColor     0 0 0
+  	transparency      0
+			  }
+
+  texture           ImageTexture {
+  			  url     [""]
+  			  repeatS TRUE
+  			  repeatT TRUE
+ 			  }
+}
+
+geometry Box { size 2.24 0.04 3.24	}
+}
+]
+translation 4.6 2.62 6.2
+}
+
+
+DEF plafond Transform {
+	children [
+		Shape {
+appearance Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      1 1 1
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						0.14	2.70	-0.14,
+						5.54	2.70	-0.14,
+						5.54	2.70	-8.86,
+						0.14	2.70	-8.86,
+
+						5.54	2.70	-2.36, #-4
+						5.54	2.70	-4.36,
+						4.42	2.70	-4.36,
+						4.42	2.70	-2.36,
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+		0, 1, 4, 7, -1,
+		0, 7, 4, 1, -1,
+
+		0, 7, 6, 3, -1,
+		0, 3, 6, 7, -1,
+
+		5, 2, 3, 6, -1,
+		5, 6, 3, 2, -1,
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             TRUE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+
+
+DEF vloerboven Transform {
+	children [
+		Shape {
+appearance Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      1 0.5 0.3
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						0.14	2.80	-0.14,
+						5.54	2.80	-0.14,
+						5.54	2.80	-8.86,
+						0.14	2.80	-8.86,
+
+						5.54	2.80	-2.36, #-4
+						5.54	2.8	-4.36,
+						4.42	2.8	-4.36,
+						4.42	2.8	-2.36,
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+		0, 1, 4, 7, -1,
+		0, 7, 4, 1, -1,
+
+		0, 7, 6, 3, -1,
+		0, 3, 6, 7, -1,
+
+		5, 2, 3, 6, -1,
+		5, 6, 3, 2, -1,
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             TRUE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+
+
+
+DEF stukmuur1 Transform {
+children[
+	Shape {
+	appearance DEF wandkleur Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      1 0.9 0.7
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+		geometry 	Box {
+	size	2.84 2.50 0.07
+				}
+
+	}
+ ]
+translation		1.7	4.05 -4.565
+}
+
+DEF stukmuur2 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	0.07 2.50 0.7
+				}
+
+	}
+ ]
+translation		3.155	4.05 -4.28
+}
+
+
+DEF stukmuur3 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	0.07 2.68 3.57
+				}
+
+	}
+ ]
+translation		3.155	4.05 -7.215
+}
+
+DEF stukmuur4 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	0.07 2.50 2.85
+				}
+
+	}
+ ]
+translation		3.155	4.05 -1.705
+}
+
+DEF stukmuur5 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	0.42 2.50 0.07
+				}
+
+	}
+ ]
+translation		3.4	4.05 -2.325
+}
+
+DEF stukmuur6 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	0.98 2.50 0.07
+				}
+
+	}
+ ]
+translation		4.91	4.05 -2.325
+}
+
+DEF stukmuur7 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	1.34 2.50 0.07
+				}
+
+	}
+ ]
+translation		4.73	4.05 -4.395
+}
+
+
+DEF stukmuur8 Transform {
+children[
+	Shape {
+	appearance USE wandkleur
+		geometry 	Box {
+	size	0.07 2.50 1.2
+				}
+
+	}
+ ]
+translation		4.025	4.05 -4.96
+}
+
+DEF vulstukvoortrap Transform {
+	children [
+		Shape {
+appearance USE wandkleur
+
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						4.42	2.7	-2.36,
+						4.42	2.7	-3.36,
+						4.42	2.8	-3.36,
+						4.42	2.8	-2.36,
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+	0, 1, 2, 3, -1,
+	0, 3, 2, 1, -1,
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             TRUE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+
+
+DEF paalvoortrap Transform {
+children[
+Shape {
+appearance 	USE wandkleur
+geometry	Box { size	0.07	2.50	0.07	}
+}
+]
+translation 4.385	4.05	-3.36
+}
+
+# deuren.wrl
+
+
+
+
+
+#Deuren boven
+
+#deur7
+Transform {
+children[
+
+#dwarslat boven de deur
+DEF dwarslat Transform {
+	children [
+		Shape {
+appearance DEF kozijn Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.95 0.95 0.9
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry Box { size	0.8 0.05 0.05 }
+}
+
+]
+translation	0 2.035 0
+}
+
+
+
+DEF ruitjeboven Transform {
+	children [
+		Shape {
+appearance DEF glas Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.6 0.6 0.9
+	emissiveColor     0 0 0
+	shininess         0.8
+	specularColor     1 1 1
+	transparency      0.7
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry Box { size	0.8 0.44 0.01 }
+}
+
+]
+translation	0 2.28 0
+}#ruit boven de deur
+
+#deur
+DEF deuroudersgang Transform {
+center -0.4 0 0
+	children [
+
+
+
+	DEF klikopdeur7 TouchSensor {}
+  DEF TimeSource7 TimeSensor { cycleInterval 20.0 } # Run once for 20 sec.
+  # Animeer het openzwaaien van de deur rond de Y as:
+   DEF Deuropen7 OrientationInterpolator {
+       key      [ 0,      0.025,	0.05,	0.95,	0.975,       1.0 ]
+       keyValue [ 0 1 0 0, 0 1 0 -1, 0 1 0 -2, 0 1 0 -2, 0 1 0 -1, 0 1 0 0 ]
+  }
+
+
+DEF deurklink # deurklink.wrl
+
+
+
+
+
+DEF deurklink Transform {
+	children [
+	    DEF enehelft	Shape {
+            appearance DEF aluminium Appearance {
+                material         Material {
+                ambientIntensity  0.2
+                diffuseColor      0.4 0.4 0.5
+                emissiveColor     0 0 0
+                shininess         0.8
+                specularColor     0.4 0.4 0.5
+                }
+                texture           NULL
+                textureTransform  NULL
+            }
+		    geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {
+					point [
+                        -0.015	0.02	0, #0
+                        0.015		0.02	0,
+                        0.02		-0.02	0,
+                        -0.02		-0.02	0, #3
+
+                        -0.01		-0.015	0.06, #4
+                        0.01		-0.015	0.06,
+                        -0.01		0.015		0.03,
+                        0.01		0.015		0.03,
+
+                        0.005		0.1		0.035, #8
+                        -0.005	0.1		0.035,
+                        -0.005	0.1		0.055,
+                        0.005		0.1		0.055,
+
+
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+
+2, 3, 4, 5, -1,
+2, 5, 4, 3, -1,
+
+4, 5, 11, 10, -1,
+4, 10, 11, 5, -1,
+
+0, 1, 7, 6, -1,
+0, 6, 7, 1, -1,
+
+6, 7, 8, 9, -1,
+6, 9, 8, 7, -1,
+
+8, 9, 10, 11, -1,
+8, 11, 10, 9, -1,
+
+0, 6, 4, 3, -1,
+0, 3, 4, 6, -1,
+
+6, 9, 10, 4, -1,
+6, 4, 10, 9, -1,
+
+1, 2, 5, 7, -1,
+1, 7, 5, 2, -1,
+
+7, 5, 11, 8, -1,
+7, 8, 11, 5, -1,
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             TRUE
+				#texCoordIndex     []
+			}
+		}#enehelft
+
+	Transform {
+		children [
+
+			USE enehelft
+		]
+	rotation 0 1 0 3.1415927
+	translation	0 0 -0.07
+	}
+	Transform {
+
+		children [
+		DEF vlakdeel Shape {
+			appearance USE aluminium
+			geometry Box { size 0.18 0.07 0.01 }
+		}#shape
+		]
+	translation -0.03 0 -0.005
+	}
+
+	Transform {
+
+		children [
+		USE vlakdeel
+		]
+	translation -0.03 0 -0.065
+	}
+
+
+
+	]
+
+translation 0.35 0 0.035
+rotation 0 0 1 1.5707963
+}#deurklink
+
+
+
+# /deurklink.wrl
+
+
+DEF deurvorm Shape {
+	appearance DEF deurkleur Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.8 0.8 0.8
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry Box { size	0.8 2 0.05 }
+}]
+translation	0 1.005 0
+}
+
+]
+translation 3.155 2.8 -3.53
+rotation 0 1 0 1.5707963
+}
+
+ROUTE klikopdeur7.touchTime TO TimeSource7.startTime
+ROUTE TimeSource7.fraction_changed TO Deuropen7.set_fraction
+ROUTE Deuropen7.value_changed TO deuroudersgang.rotation
+
+#deur8
+Transform {
+children[ 	USE dwarslat
+		USE ruitjeboven
+
+#deur
+DEF deurtweedeslaapkamergang Transform {
+center -0.4 0 0
+	children [
+
+
+
+	DEF klikopdeur8 TouchSensor {}
+  DEF TimeSource8 TimeSensor { cycleInterval 20.0 } # Run once for 20 sec.
+  # Animeer het openzwaaien van de deur rond de Y as:
+   DEF Deuropen8 OrientationInterpolator {
+       key      [ 0,      0.025,	0.05,	0.95,	0.975,       1.0 ]
+       keyValue [ 0 1 0 0, 0 1 0 -1, 0 1 0 -2, 0 1 0 -2, 0 1 0 -1, 0 1 0 0 ]
+  }
+USE deurklink
+USE deurvorm
+]
+translation	0 1.005 0
+}
+
+]
+translation 3.155 2.8 -5.03
+rotation 0 1 0 -1.5707963
+}
+
+ROUTE klikopdeur8.touchTime TO TimeSource8.startTime
+ROUTE TimeSource8.fraction_changed TO Deuropen8.set_fraction
+ROUTE Deuropen8.value_changed TO deurtweedeslaapkamergang.rotation
+
+
+#deur9
+Transform {
+children[ 	USE dwarslat
+		USE ruitjeboven
+
+#deur
+DEF deurkinderkamergang Transform {
+center -0.4 0 0
+	children [
+
+
+
+	DEF klikopdeur9 TouchSensor {}
+  DEF TimeSource9 TimeSensor { cycleInterval 20.0 } # Run once for 20 sec.
+  # Animeer het openzwaaien van de deur rond de Y as:
+   DEF Deuropen9 OrientationInterpolator {
+       key      [ 0,      0.025,	0.05,	0.95,	0.975,       1.0 ]
+       keyValue [ 0 1 0 0, 0 1 0 -1, 0 1 0 -2, 0 1 0 -2, 0 1 0 -1, 0 1 0 0 ]
+  }
+USE deurklink
+USE deurvorm
+]
+translation	0 1.005 0
+}
+
+]
+translation 3.59 2.8 -5.465
+rotation 0 1 0 3.1415927
+}
+
+ROUTE klikopdeur9.touchTime TO TimeSource9.startTime
+ROUTE TimeSource9.fraction_changed TO Deuropen9.set_fraction
+ROUTE Deuropen9.value_changed TO deurkinderkamergang.rotation
+
+
+#deur10
+Transform {
+children[ 	USE dwarslat
+		USE ruitjeboven
+
+#deur
+DEF deurbadkamergang Transform {
+center -0.4 0 0
+	children [
+
+
+
+	DEF klikopdeur10 TouchSensor {}
+  DEF TimeSource10 TimeSensor { cycleInterval 20.0 } # Run once for 20 sec.
+  # Animeer het openzwaaien van de deur rond de Y as:
+   DEF Deuropen10 OrientationInterpolator {
+       key      [ 0,      0.025,	0.05,	0.95,	0.975,       1.0 ]
+       keyValue [ 0 1 0 0, 0 1 0 -1, 0 1 0 -2, 0 1 0 -2, 0 1 0 -1, 0 1 0 0 ]
+  }
+USE deurklink
+USE deurvorm
+]
+translation	0 1.005 0
+}
+
+]
+translation 4.01 2.8 -2.325
+rotation 0 1 0 0
+}
+
+
+
+ROUTE klikopdeur10.touchTime TO TimeSource10.startTime
+ROUTE TimeSource10.fraction_changed TO Deuropen10.set_fraction
+ROUTE Deuropen10.value_changed TO deurbadkamergang.rotation
+
+
+# /deuren.wrl
+
+# trapboven.wrl
+
+
+
+
+
+
+
+
+DEF trapboven Transform {
+	children [
+		Shape {
+appearance Appearance {
+	material          Material {
+	ambientIntensity  0.2
+	diffuseColor      1 0.5 0.3
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry DEF trap2 IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+	-0.2	0	1, #0
+	0	0.2	1,
+	0	0.2	0,
+	-0.2	0	0, #3
+
+	0.28	0.4	1, #4
+	0.6	0.6	1,
+	0.98	0.8	1,
+	0.98	1	0.45, #7
+
+	0.98	1.2	0.25, #8
+	0.98	1.4	0,
+	0.98	1.6	-0.25,
+	0.98	1.8	-0.45, #11
+
+	0.98	2.0	-1, #12
+	0.6	2.2	-1,
+	0.28	2.4	-1,
+	0	2.6	-1, #15
+
+	0	0.6	0, #16
+	0	0.8	0,
+	0	1	0, #18
+	0	1.2	0, #19
+
+	0	1.4	0, #20
+	0	1.6	0,
+	0	1.8	0, #22
+	0	2	0, #23
+
+	0	2.2	0, #24
+	0	2.4	0,
+	0	2.6	0,
+	0	2.8	0,  #27
+
+	-0.2	0.2	1, #28
+	0	0.4	1,
+	0	0.4	0,
+	-0.2	0.2	0, #31
+
+	0.28	0.6	1, #32
+	0.6	0.8	1,
+	0.98	1	1,
+	0.98	1.2	0.45, #35
+
+	0.98	1.4	0.25, #36
+	0.98	1.6	0,
+	0.98	1.8	-0.25,
+	0.98	2	-0.45, #39
+
+	0.98	2.2	-1, #40
+	0.6	2.4	-1,
+	0.28	2.6	-1,
+	0	2.8	-1, #43
+
+	0	0	1,
+	0	0	0, #45
+	0	0.4	0, #46
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+
+	28, 1, 2, 31, -1, #1e_treevlak
+	29, 4, 46, -1, #2e
+	32, 5, 16, -1,
+	33, 6, 17, -1,
+	34, 7, 18, -1,
+	35, 8, 19, -1,
+	36, 9, 20, -1,
+	37, 10, 21, -1,
+	38, 11, 22, -1,
+	39, 12, 23, -1,
+	40, 13, 24, -1,
+	41, 14, 25, -1,
+	42, 15, 26, -1, #13e_treevlak
+
+#	0, 28, 31, 3, -1, #voor_en_zijvlakken_van_de_eerste_tree_komt_boven_niet_voor
+#	0, 44, 1, 28, -1,
+#	3, 31, 2, 45, -1,
+
+	2, 1, 29, 30, -1, #alle_voorvlakken
+	46, 4, 32, 16, -1,
+	16, 5, 33, 17, -1,
+	17, 6, 34, 18, -1,
+	18, 7, 35, 19, -1,
+	19, 8, 36, 20, -1,
+	20, 9, 37, 21, -1,
+	21, 10, 38, 22, -1,
+	22, 11, 39, 23, -1,
+	23, 12, 40, 24, -1,
+	24, 13, 41, 25, -1,
+	25, 14, 42, 26, -1,
+	26, 15, 43, 27, -1,
+
+
+
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             FALSE
+				texCoordIndex     []
+			}
+		}
+	]
+translation 4.42	2.6	-3.36
+}
+
+
+# /trapboven.wrl
+
+# buitmuur.wrl
+
+
+
+
+Transform {
+	children [
+		Shape {
+appearance DEF steen Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.8 0.4 0.3
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						0	0	0, #linkerbenedenhoek voorgevel
+						5.68	0	0,
+						5.68	5.40	0,
+						0	5.40	0, #linkerbovenhoek voorgevel
+
+						3.19	0	0, #voordeur
+						4.42	0	0,
+						4.42	2.30	0,
+						3.19	2.30	0, #voordeur
+
+						4.84	1.60	0, #toiletraam-8
+						5.04	1.60	0,
+						5.04	2.30	0,
+						4.84	2.30	0,
+
+						2.60	0.90	0, #keukenraam-12
+						2.60	2.30	0,
+						0.60	2.30	0,
+						0.60	0.90	0,
+
+						0.60	3.60	0, #bovenraam-16
+						1.50	3.60	0,
+						1.50	5.0	0,
+						4.42	5.0	0,
+						4.42	5.40	0,
+						0.60	5.40	0,
+
+						0	8.555	-3.15, #linkergevel-22
+						0	2.7	-9.0,
+						0	0	-9.0,
+
+						5.68	8.555	-3.15, #rechtergevel-25
+						5.68	5.40	-6.3,
+						5.68	5.40	-9,
+						5.68	0	-9.0,
+
+						2.25	0	-9.0, #achtergevel-29
+						2.25	0.6	-9.0,
+						4.8	0.6	-9.0,
+						4.8	5.40	-9.0,
+						1.2	0	-9.0,
+						1.2	2.7	-9,
+
+
+						3.19	0	-0.14, #diepte van de voordeur-35
+						4.42	0	-0.14,
+						4.42	2.30	-0.14,
+						3.19	2.30	-0.14, #diepte van de voordeur-38
+
+						2.60	0.90	-0.14, # diepte van het keukenraam-39
+						2.60	2.30	-0.14,
+						0.60	2.30	-0.14,
+						0.60	0.90	-0.14,
+
+						4.84	1.60	-0.14, #diepte van het toiletraam-43
+						5.04	1.60	-0.14,
+						5.04	2.30	-0.14,
+						4.84	2.30	-0.14,
+
+						0.60	3.60	-0.14, #diepte van het bovenraam-47
+						1.50	3.60	-0.14,
+						1.50	5.0	-0.14,
+						4.42	5.0	-0.14,
+						4.42	5.40	-0.14,
+						0.60	5.40	-0.14, #52
+
+						2.25	0	-8.86, #diepte van de achtergevel-53
+						2.25	0.6	-8.86,
+						4.8	0.6	-8.86,
+						4.8	5.40	-8.86,
+						1.2	0	-8.86,
+						1.2	2.7	-8.86,	#58
+
+						0	2.7	0, #59 extra punten voor segmentering beneden
+						0.14	2.7	-0.14,
+
+						5.68	2.7	0, #61
+						5.54	2.7	-0.14, #
+
+						0	2.7	-9, #63
+						0.14	2.7	-8.86, #
+
+						5.68	2.7	-9, #65
+						5.54	2.7	-8.86, #
+
+						1.2	2.7	-9, #67
+						1.2	2.7	-8.86, #
+
+						4.8	2.7	-9, #69
+						4.8	2.7	-8.86, #
+
+						0.14	5.4	-0.14, #71
+						5.54	5.4	-0.14, #72
+						5.54	5.4	-8.86, #73
+						0.14	2.7	-9, #74
+						0.14	5.4	-6.3, #75
+						0	5.4	-6.3, #76
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+		16, 21, 3, 59, -1,
+
+		19, 2, 20, -1,
+
+		2, 19, 61, -1,
+
+		65, 27, 2, 61, -1,
+
+		59, 3, 76, 23, -1,
+
+		17, 48, 47, 16, -1,
+
+		18, 49, 48, 17, -1,
+
+		19, 50, 49, 18, -1,
+
+		20, 51, 50, 19, -1,
+
+		16, 47, 52, 21, -1,
+
+		3, 21, 52, 71, -1,
+
+		20, 2, 72, 51, -1,
+
+		2, 27, 73, 72, -1,
+
+		27, 32, 56, 73, -1,
+
+		63, 64, 75, 76, -1,
+
+		3, 76, 75, 71, -1,
+
+		#27, 32, 69, 65, -1,
+		27, 65, 69, 32, -1,
+
+		32, 69, 70, 56, -1,
+
+		59, 17, 16, -1,
+
+		59, 61, 17, -1,
+
+		17, 61 , 18, -1,
+
+		18, 61, 19, -1,
+
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             FALSE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+
+
+# /buitmuur.wrl
+
+# binmuur.wrl
+
+
+
+
+Transform {
+	children [
+		Shape {
+appearance Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      1 0.9 0.7
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						0.28	0	-0.28, #0 linkerbenedenhoek voorgevel
+						5.40	0	-0.28,
+						5.40	5.40	-0.28,
+						0.28	5.40	-0.28, #3 linkerbovenhoek voorgevel
+
+						3.19	0	-0.28, #4 voordeur
+						4.42	0	-0.28,
+						4.42	2.30	-0.28,
+						3.19	2.30	-0.28, #7 voordeur
+
+						4.84	1.60	-0.28, #toiletraam-8
+						5.04	1.60	-0.28,
+						5.04	2.30	-0.28,
+						4.84	2.30	-0.28,
+
+						2.60	0.90	-0.28, #keukenraam-12
+						2.60	2.30	-0.28,
+						0.60	2.30	-0.28,
+						0.60	0.90	-0.28,
+
+						0.60	3.60	-0.28, #bovenraam-16
+						1.50	3.60	-0.28,
+						1.50	5.0	-0.28,
+						4.42	5.0	-0.28,
+						4.42	5.40	-0.28,
+						0.60	5.40	-0.28,
+
+						0.28	8.55	-3.15, #linkergevel-22
+						0.28	2.70	-8.72,
+						0.28	0	-8.72,
+
+						5.40	8.55	-3.15, #rechtergevel-25
+						5.40	5.40	-6.30,
+						5.40	5.40	-8.72,
+						5.40	0	-8.72,
+
+						2.25	0	-8.72, #achtergevel-29
+						2.25	0.6	-8.72,
+						4.8	0.6	-8.72,
+						4.8	5.40	-8.72,
+						1.2	0	-8.72,
+						1.2	2.70	-8.72,
+
+
+						3.19	0	-0.14, #diepte van de voordeur-35
+						4.42	0	-0.14,
+						4.42	2.30	-0.14,
+						3.19	2.30	-0.14, #diepte van de voordeur-38
+
+						2.60	0.90	-0.14, # diepte van het keukenraam-39
+						2.60	2.30	-0.14,
+						0.60	2.30	-0.14,
+						0.60	0.90	-0.14,
+
+						4.84	1.60	-0.14, #diepte van het toiletraam-43
+						5.04	1.60	-0.14,
+						5.04	2.30	-0.14,
+						4.84	2.30	-0.14,
+
+						0.60	3.60	-0.14, #diepte van het bovenraam-47
+						1.50	3.60	-0.14,
+						1.50	5.0	-0.14,
+						4.42	5.0	-0.14,
+						4.42	5.40	-0.14,
+						0.60	5.40	-0.14, #52
+
+						2.25	0	-8.86, #diepte van de achtergevel-53
+						2.25	0.6	-8.86,
+						4.8	0.6	-8.86,
+						4.8	5.40	-8.86,
+						1.2	0	-8.86,
+						1.2	2.70	-8.86,	#58
+
+						0.28	5.40	0,	#59 hoekpunt voor dakvorm zolder
+						5.40	5.40	0,
+						0.28	5.40	-6.30, #61
+						0.28	2.70	-9.0,  #62 hoekpunt voor dakvorm 1e verdieping
+
+						0.28	2.7	-0.28, #63 extra punten voor segmentering boven
+						0.14	2.7	-0.14,
+
+						5.4	2.7	-0.28, #65
+						5.54	2.7	-0.14, #
+
+						0.28	2.7	-8.72, #67
+						0.14	2.7	-8.86, #
+
+						5.4	2.7	-8.72, #69
+						5.54	2.7	-8.86, #
+
+						1.2	2.7	-8.72, #71
+						1.2	2.7	-8.86, #
+
+						4.8	2.7	-8.72, #73
+						4.8	2.7	-8.86, #
+
+						0.14	5.4	-0.14, #75
+						5.54	5.4	-0.14, #76
+						5.54	5.4	-8.86, #77
+						0.14	2.7	-9, #78
+						0.14	5.4	-6.3, #79
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+
+
+
+		16, 21, 3, 63, -1,
+
+		19, 2, 20, -1,
+
+		63, 16, 17, 65, -1,
+
+		17, 18, 19, 65, -1,
+
+		2, 19, 65, -1,
+
+		69, 27, 2, 65, -1,
+
+		63, 3, 61, 62, -1,
+
+		16, 47, 48, 17, -1,
+
+		17, 48, 49, 18, -1,
+
+		18, 49, 50, 19, -1,
+
+		19, 50, 51, 20, -1,
+
+		21, 52, 47, 16, -1,
+
+		73, 74, 56, 32, -1,
+
+		69, 73, 32, 27, -1,
+
+		3, 21, 52, 75 -1,
+
+		20, 2, 76, 51, -1,
+
+		3, 61, 79, 75, -1,
+
+		2, 27, 77, 76, -1,
+
+		62, 61, 79, 78, -1,
+
+		27, 32, 56, 77, -1,
+
+
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             FALSE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+
+
+# /binmuur.wrl
+
+# ramen.wrl
+
+
+
+
+
+
+#begin bovenraam
+
+Transform {
+	children [
+		Shape {
+appearance DEF kozijn Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.95 0.95 0.9
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+
+geometry	Box { size	3.82 0.05 0.05 }
+}
+
+]
+translation	2.51 5.025 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	3.82 0.1 0.05 }
+}
+
+]
+translation	2.51 5.35 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 1.8 0.05 }
+}
+
+]
+translation	0.625 4.5 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 1.8 0.05 }
+}
+
+]
+translation	1.475 4.5 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 0.25 0.05 }
+}
+
+]
+translation	4.395 5.175 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance DEF blauw Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.3 0.4 0.7
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+
+geometry	Box { size	0.9 0.25 0.05 }
+}
+
+]
+translation	3.05 5.175 -0.14
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.9 0.05 0.05 }
+}
+
+]
+translation	1.05 3.625 -0.14
+}
+
+#einde bovenraam
+#begin kozijnen achter
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 2.4 0.05 }
+}
+
+]
+translation	3.215 3.9 -8.86
+}
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 2.4 0.05 }
+}
+
+]
+translation	4.775 3.9 -8.86
+}
+Transform {
+	children [
+		Shape {
+appearance	USE blauw
+geometry	Box { size	1.61 0.3 0.05 }
+}
+
+]
+translation	3.995 5.25 -8.86
+}
+Transform {
+	children [
+		Shape {
+appearance	USE blauw
+geometry	Box { size	1.51 0.8 0.05 }
+}
+
+]
+translation	3.995 3.1 -8.86
+}
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	1.51 0.05 0.05 }
+}
+
+]
+translation	3.995 3.525 -8.86
+}#3
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	1.51 0.05 0.05 }
+}
+
+]
+translation	3.995 5.075 -8.86
+}#4
+
+
+
+
+
+
+
+Transform {
+	children [
+		Shape {
+appearance	USE kozijn
+geometry	Box { size	0.05 1.5 0.05 }
+}
+
+]
+translation	3.565 4.3 -8.86
+}
+
+
+
+#einde ramen achter
+
+
+
+# /ramen.wrl
+
+# ruiten.wrl
+
+
+
+
+
+
+#bovenruiten
+Transform {
+	children [
+		Shape {
+appearance DEF glas Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.6 0.6 0.9
+	emissiveColor     0 0 0
+	shininess         0.8
+	specularColor     1 1 1
+	transparency      0.7
+}	texture           NULL
+	textureTransform  NULL
+}
+
+			geometry Box { size	0.8 1.35 0.01 }
+}
+
+]
+translation	1.05 4.325 -0.14
+}#ruit 1 boven
+
+Transform {
+	children [
+		Shape {
+appearance USE glas
+			geometry Box { size	0.8 0.3 0.01 }
+}
+
+]
+translation	1.05 5.2 -0.14
+}#ruit 2 boven
+
+Transform {
+	children [
+		Shape {
+appearance USE glas
+			geometry Box { size	1.1 0.3 0.01 }
+}
+
+]
+translation	2.05 5.2 -0.14
+}#ruit 3 boven
+
+Transform {
+	children [
+		Shape {
+appearance USE glas
+			geometry Box { size	0.85 0.3 0.01 }
+}
+
+]
+translation	3.935 5.2 -0.14
+}#ruit 4 boven
+
+
+
+#ramen achter
+Transform {
+	children [
+		Shape {
+appearance USE glas
+			geometry Box { size	1.16 1.5 0.01 }
+}
+
+]
+translation	4.17 4.3 -8.86
+}#ruit 1 achter
+
+Transform {
+	children [
+		Shape {
+appearance USE glas
+			geometry Box { size	0.3 1.5 0.01 }
+}
+
+]
+translation	3.39 4.3 -8.86
+}#ruit 2 achter
+
+
+
+# /ruiten.wrl
+
+# /boven/boven.wrl
+
+
+
+DEF zolder Transform {
+children[
+
+Transform {
+children[
+
+Transform{
+children[
+	Shape {
+appearance USE groen
+geometry USE pijl
+}
+]
+rotation 0 1 0 3.1415927
+}
+
+DEF schuifzolder TouchSensor {}
+]
+translation 0.15 5.4 0.3
+}
+
+Transform {
+children[
+
+	Shape {
+appearance USE rood
+geometry USE pijl
+}
+
+DEF schuifzolder2 TouchSensor {}
+]
+translation 5.48 5.4 0.3
+}
+
+DEF dak Transform {
+children [
+# dak/dak.wrl
+
+
+
+
+Transform {
+	children [
+		Shape {
+appearance Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.3 0.4 0.5
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+					0	5.4	0, #dakranden-0
+					5.68	5.4	0,
+					5.68	8.55	-3.15,
+					0	8.55	-3.15,
+					0	2.7	-9,
+					3.12	2.7	-9,
+					3.12	5.4	-6.30,
+					5.68	5.4	-6.30, #7
+
+					1.5	3.7	-8, #dakraam1-8
+					2.3	3.7	-8,
+					2.3	4.7	-7,
+					1.5	4.7	-7,
+
+					4.1	7.3	-1.9,  #dakraam2-12
+					3.5	7.3	-1.9,
+					3.5	7.7	-2.3,
+					4.1	7.7	-2.3, #15
+
+					0	5.44	0.04, #dikte van dakranden-16
+					5.68	5.44	0.04,
+					5.68	8.6065685	-3.15,
+					0	8.6065685	-3.15,
+					0	2.74	-9.04,
+					3.12	2.74	-9.04,
+					3.12	5.44	-6.34,
+					5.68	5.44	-6.34, #23
+
+					1.5	3.74	-8.04, #dikte van het dakraam1-24
+					2.3	3.74	-8.04,
+					2.3	4.74	-7.04,
+					1.5	4.74	-7.04, #27
+
+					4.1	7.34	-1.86,  #dikte van het dakraam2-28
+					3.5	7.34	-1.86,
+					3.5	7.74	-2.26,
+					4.1	7.74	-2.26, #31
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+
+
+	0, 13, 14, 3, -1,
+	0, 3, 14, 13, -1,
+
+	16, 29, 30, 19, -1,
+	16, 19, 30, 29, -1,
+
+	0, 1, 12, 13, -1,
+	0, 13, 12, 1, -1,
+
+	16, 17, 28, 29, -1,
+	16, 29, 28, 17, -1,
+
+	1, 12, 15, 2, -1,
+	1, 2, 15, 12, -1,
+
+	17, 28, 31, 18, -1,
+	17, 18, 31, 28, -1,
+
+	15, 2, 3, 14, -1,
+	15, 14, 3, 2, -1,
+
+	31, 18, 19, 30, -1,
+	31, 30, 19, 18, -1,
+
+	4, 8, 11, 3, -1,
+	4, 3, 11, 8, -1,
+
+	20, 24, 27, 19, -1,
+	20, 19, 27, 24, -1,
+
+	5, 4, 8, 9, -1,
+	5, 9, 8, 4, -1,
+
+	21, 20, 24, 25, -1,
+	21, 25, 24, 20, -1,
+
+	5, 9, 10, 6, -1,
+	5, 6, 10, 9, -1,
+
+	21, 25, 26, 22, -1,
+	21, 22, 26, 25, -1,
+
+	7, 6, 2, -1,
+	7, 2, 6, -1,
+
+	23, 22, 18, -1,
+	23, 18, 22, -1,
+
+	6, 10, 11, 3, 2, -1,
+	6, 2, 3, 11, 10, -1,
+
+	22, 26, 18, -1,
+	22, 18, 26, -1,
+
+	26, 27, 19, -1,
+	26, 19, 27, -1,
+
+	26, 19, 18, -1,
+	26, 18, 19, -1,
+
+	0, 1, 17, 16, -1,
+	0, 16, 17, 1, -1,
+
+	1, 2, 18, 17, -1,
+	1, 17, 18, 2, -1,
+
+	3, 0, 16, 19, -1,
+	3, 19, 16, 0, -1,
+
+	2, 7, 23, 18, -1,
+	2, 18, 23, 7, -1,
+
+	7, 6, 22, 23, -1,
+	7, 23, 22, 6, -1,
+
+	5, 6, 22, 21, -1,
+	5, 21, 22, 6, -1,
+
+	5, 4, 20, 21, -1,
+	5, 21, 20, 4, -1,
+
+	4, 3, 19, 20, -1,
+	4, 20, 19, 3, -1,
+
+	9, 8, 24, 25, -1,
+	9, 25, 24, 8, -1,
+
+	8, 11, 27, 24, -1,
+	8, 24, 27, 11, -1,
+
+	10, 11, 27, 26, -1,
+	10, 26, 27, 11, -1,
+
+	9, 10, 26, 25, -1,
+	9, 25, 26, 10, -1,
+
+	12, 13, 29, 28, -1,
+	12, 28, 29, 13, -1,
+
+	12, 15, 31, 28, -1,
+	12, 28, 31, 15, -1,
+
+	14, 15, 31, 30, -1,
+	14, 30, 31, 15, -1,
+
+	13, 14, 30, 29, -1,
+	13, 29, 30, 14, -1,
+
+
+
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             TRUE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+
+#schoorsteen
+
+Transform {
+	children [
+		Shape {
+appearance Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.1 0.1 0.1
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0.3 0.3 0.3
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+		4.8	8	-2.6,
+		5.3	8	-2.6,
+		5.3	8.4	-3.0,
+		4.8	8.4	-3.0,
+
+		4.9	9.4	-2.65,
+		5.2	9.4	-2.65,
+		5.2	9.4	-2.95,
+		4.9	9.4	-2.95, ]
+	}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+		0, 1, 5, 4, -1,
+		0, 4, 5, 1, -1,
+
+		1, 2, 6, 5, -1,
+		1, 5, 6, 2, -1,
+
+		2, 3, 7, 6, -1,
+		2, 6, 7, 3, -1,
+
+		0, 3, 7, 4, -1,
+		0, 4, 7, 3, -1,
+
+		4, 5, 6, 7, -1,
+		4, 7, 6, 5, -1,
+
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             TRUE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+
+
+
+
+
+# /dak/dak.wrl
+
+Transform {
+children[
+
+
+Transform{
+children[
+
+	Shape {
+appearance USE groen
+geometry USE pijl
+}
+
+]
+rotation 0 1 0 3.1415927
+}
+
+DEF schuifdak TouchSensor {}
+
+]
+translation 0.15 6 0.3
+}
+
+
+Transform {
+children[
+
+	Shape {
+appearance USE rood
+geometry USE pijl
+}
+
+DEF schuifdak2 TouchSensor {}
+]
+translation 5.48 6 0.3
+}
+
+DEF tijd1 TimeSensor {
+	cycleInterval 4
+	enabled       TRUE
+	loop          FALSE
+	startTime     0
+	stopTime      0
+}
+
+DEF plaats1 PositionInterpolator {
+	key           [0, 1]
+	keyValue      [0 0 0 ,6 0 0]
+}
+
+DEF tijd1a TimeSensor {
+	cycleInterval 4
+	enabled       TRUE
+	loop          FALSE
+	startTime     0
+	stopTime      0
+}
+
+DEF plaats1a PositionInterpolator {
+	key           [0, 1]
+	keyValue      [6 0 0 ,0 0 0]
+}
+
+
+
+]
+}#dak
+
+# zolder/zolder.wrl
+
+
+
+Viewpoint {
+	fieldOfView    1
+	jump           TRUE
+	orientation    1 0 0  0
+	position       2.5 7 -3.15
+
+	description    "op zolder"
+}
+
+#kleine platte dakje
+
+Transform {
+children [
+Shape {
+appearance DEF appdak Appearance {
+	 material          Material {
+  	ambientIntensity  0
+  	diffuseColor      0.2 0.2 0.2
+  	emissiveColor     0 0 0
+  	shininess         0
+  	specularColor     0 0 0
+  	transparency      0
+			  }
+
+  texture           ImageTexture {
+  			  url     [""]
+  			  repeatS TRUE
+  			  repeatT TRUE
+ 			  }
+}
+geometry Box { size 2.6 0.04 2.74	}
+}
+]
+translation 4.39 5.40 -7.67
+}
+
+
+DEF zoldervloer Transform {
+	children [
+		Shape {
+appearance Appearance {
+	material          Material {
+	ambientIntensity  0.2
+	diffuseColor      1 0.5 0.3
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						0.14	5.40	0,
+						5.54	5.40	0,
+						5.54	5.40	-6.30,
+						0.14	5.40	-6.30,
+
+						5.54	5.40	-2.36, #-4
+						5.54	5.40	-4.36,
+						4.42	5.40	-4.36,
+						4.42	5.40	-2.36,
+
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+		0, 1, 4, 7, -1,
+		0, 7, 4, 1, -1,
+
+		0, 7, 6, 3, -1,
+		0, 3, 6, 7, -1,
+
+		5, 2, 3, 6, -1,
+		5, 6, 3, 2, -1,
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             TRUE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+DEF plafondboven Transform {
+	children [
+		Shape {
+appearance Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      1 1 1
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						0.14	5.30	-0.14,
+						5.54	5.30	-0.14,
+						5.54	5.30	-8.86,
+						3.19	5.30	-8.86,
+
+						5.54	5.30	-2.36, #-4
+						5.54	5.30	-4.36,
+						4.42	5.30	-4.36,
+						4.42	5.30	-2.36,
+
+						3.19	5.30	-6.40,
+						0.14	5.30	-6.40,
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+		0, 1, 4, 7, -1,
+		0, 7, 4, 1, -1,
+
+		0, 7, 6, 8, 9, -1,
+		0, 9, 8, 6, 7, -1,
+
+		5, 2, 3, 8, 6, -1,
+		5, 6, 8, 3, 2, -1,
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             TRUE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+
+
+DEF vulstukvoortrap Transform {
+	children [
+		Shape {
+appearance DEF wandkleur Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      1 0.9 0.7
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+
+
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						4.42	5.3	-2.36, #0
+						4.42	5.3	-3.36,
+						4.42	5.4	-3.36,
+						4.42	5.4	-2.36, #3
+
+						5.4	5.3	-2.36, #4
+						5.4	5.4	-2.36,
+
+						5.4	5.3	-4.36, #6
+						5.4	5.4	-4.36,
+						4.42	5.3	-4.36, #8
+						4.42	5.4	-4.36,
+
+
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+	0, 1, 2, 3, -1,
+	0, 3, 2, 1, -1,
+	0, 3, 5, 4, -1,
+	9, 8, 6, 7, -1,
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             FALSE
+				texCoordIndex     []
+			}
+		}
+
+]
+}
+
+# ballustrade.wrl
+
+
+
+Transform {
+children[
+
+DEF spijlen Group {
+children [
+Transform{
+children [
+DEF spijl Shape {
+appearance DEF kozijn Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.95 0.95 0.9
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+geometry  Cylinder {
+				bottom  FALSE
+				height  0.8
+				radius  0.015
+				side    TRUE
+				top     FALSE
+			}
+}
+]
+translation -0.025	0.4	0.05
+}
+
+Transform{
+children USE spijl
+translation -0.025	0.4	0.15
+}
+Transform{
+children USE spijl
+translation -0.025	0.4	0.25
+}
+Transform{
+children USE spijl
+translation -0.025	0.4	0.35
+}
+Transform{
+children USE spijl
+translation -0.025	0.4	0.45
+}
+Transform{
+children USE spijl
+translation -0.025	0.4	0.55
+}
+Transform{
+children USE spijl
+translation -0.025	0.4	0.65
+}
+Transform{
+children USE spijl
+translation -0.025	0.4	0.75
+}
+Transform{
+children USE spijl
+translation -0.025	0.4	0.85
+}
+Transform{
+children USE spijl
+translation -0.025	0.4	0.95
+}
+
+]
+}#spijlengroup
+
+Transform{
+translation 0 0 1
+rotation 0 1 0 1.5707963
+children USE spijlen
+}
+
+Transform{
+children [
+Shape {
+appearance USE  kozijn
+geometry  Box { size 0.05	0.03	1.05	}
+}
+]
+translation -0.025	0.815	0.525
+}
+
+Transform{
+children [
+Shape {
+appearance USE  kozijn
+geometry  Box { size 1	0.03	0.05	}
+}
+]
+translation 0.5	0.815	1.025
+}
+
+
+]
+translation	4.42	5.4	-3.36
+}
+
+# /ballustrade.wrl
+
+# binmuur.wrl
+
+
+
+
+Transform {
+	children [
+		Shape {
+appearance Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      1 0.9 0.7
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						0.28	0	-0.28, #0 linkerbenedenhoek voorgevel
+						5.40	0	-0.28,
+						5.40	5.40	-0.28,
+						0.28	5.40	-0.28, #3 linkerbovenhoek voorgevel
+
+						3.19	0	-0.28, #4 voordeur
+						4.42	0	-0.28,
+						4.42	2.30	-0.28,
+						3.19	2.30	-0.28, #7 voordeur
+
+						4.84	1.60	-0.28, #toiletraam-8
+						5.04	1.60	-0.28,
+						5.04	2.30	-0.28,
+						4.84	2.30	-0.28,
+
+						2.60	0.90	-0.28, #keukenraam-12
+						2.60	2.30	-0.28,
+						0.60	2.30	-0.28,
+						0.60	0.90	-0.28,
+
+						0.60	3.60	-0.28, #bovenraam-16
+						1.50	3.60	-0.28,
+						1.50	5.0	-0.28,
+						4.42	5.0	-0.28,
+						4.42	5.40	-0.28,
+						0.60	5.40	-0.28,
+
+						0.28	8.55	-3.15, #linkergevel-22
+						0.28	2.70	-8.72,
+						0.28	0	-8.72,
+
+						5.40	8.55	-3.15, #rechtergevel-25
+						5.40	5.40	-6.30,
+						5.40	5.40	-8.72,
+						5.40	0	-8.72,
+
+						2.25	0	-8.72, #achtergevel-29
+						2.25	0.6	-8.72,
+						4.8	0.6	-8.72,
+						4.8	5.40	-8.72,
+						1.2	0	-8.72,
+						1.2	2.70	-8.72,
+
+
+						3.19	0	-0.14, #diepte van de voordeur-35
+						4.42	0	-0.14,
+						4.42	2.30	-0.14,
+						3.19	2.30	-0.14, #diepte van de voordeur-38
+
+						2.60	0.90	-0.14, # diepte van het keukenraam-39
+						2.60	2.30	-0.14,
+						0.60	2.30	-0.14,
+						0.60	0.90	-0.14,
+
+						4.84	1.60	-0.14, #diepte van het toiletraam-43
+						5.04	1.60	-0.14,
+						5.04	2.30	-0.14,
+						4.84	2.30	-0.14,
+
+						0.60	3.60	-0.14, #diepte van het bovenraam-47
+						1.50	3.60	-0.14,
+						1.50	5.0	-0.14,
+						4.42	5.0	-0.14,
+						4.42	5.40	-0.14,
+						0.60	5.40	-0.14, #52
+
+						2.25	0	-8.86, #diepte van de achtergevel-53
+						2.25	0.6	-8.86,
+						4.8	0.6	-8.86,
+						4.8	5.40	-8.86,
+						1.2	0	-8.86,
+						1.2	2.70	-8.86,	#58
+
+						0.28	5.40	0,	#59 hoekpunt voor dakvorm zolder
+						5.40	5.40	0,
+						0.28	5.40	-6.30, #61
+						0.28	2.70	-9.0,  #62 hoekpunt voor dakvorm 1e verdieping
+
+						0.14	5.40	0,	#63 extra punten voor segmentering
+						5.54	5.40	0,
+						0.14	5.40	-6.30,
+						5.54	5.40	-6.3,  #66
+						5.54	8.55	-3.15, #rechtergevel-67
+						0.14	8.55	-3.15, #linkergevel-68
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+
+		59, 22, 61, -1,
+		60, 25, 26, -1,
+
+		59, 63, 68, 22, -1,
+		68, 22, 61, 65, -1,
+
+		60, 64, 67, 25, -1,
+		25, 67, 66, 26, -1,
+
+
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             FALSE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+
+
+# /binmuur.wrl
+
+# buitmuur.wrl
+
+
+
+
+Transform {
+	children [
+		Shape {
+appearance DEF steen Appearance {
+	material         Material {
+	ambientIntensity  0.2
+	diffuseColor      0.8 0.4 0.3
+	emissiveColor     0 0 0
+	shininess         0.1
+	specularColor     0 0 0
+	transparency      0
+}	texture           NULL
+	textureTransform  NULL
+}
+			geometry IndexedFaceSet {
+				color             NULL
+				coord  Coordinate {	#default NULL
+					point [
+						0	0	0, #linkerbenedenhoek voorgevel
+						5.68	0	0,
+						5.68	5.40	0,
+						0	5.40	0, #linkerbovenhoek voorgevel
+
+						3.19	0	0, #voordeur
+						4.42	0	0,
+						4.42	2.30	0,
+						3.19	2.30	0, #voordeur
+
+						4.84	1.60	0, #toiletraam-8
+						5.04	1.60	0,
+						5.04	2.30	0,
+						4.84	2.30	0,
+
+						2.60	0.90	0, #keukenraam-12
+						2.60	2.30	0,
+						0.60	2.30	0,
+						0.60	0.90	0,
+
+						0.60	3.60	0, #bovenraam-16
+						1.50	3.60	0,
+						1.50	5.0	0,
+						4.42	5.0	0,
+						4.42	5.40	0,
+						0.60	5.40	0,
+
+						0	8.555	-3.15, #linkergevel-22
+						0	2.7	-9.0,
+						0	0	-9.0,
+
+						5.68	8.555	-3.15, #rechtergevel-25
+						5.68	5.40	-6.3,
+						5.68	5.40	-9,
+						5.68	0	-9.0,
+
+						2.25	0	-9.0, #achtergevel-29
+						2.25	0.6	-9.0,
+						4.8	0.6	-9.0,
+						4.8	5.40	-9.0,
+						1.2	0	-9.0,
+						1.2	2.7	-9,
+
+
+						3.19	0	-0.14, #diepte van de voordeur-35
+						4.42	0	-0.14,
+						4.42	2.30	-0.14,
+						3.19	2.30	-0.14, #diepte van de voordeur-38
+
+						2.60	0.90	-0.14, # diepte van het keukenraam-39
+						2.60	2.30	-0.14,
+						0.60	2.30	-0.14,
+						0.60	0.90	-0.14,
+
+						4.84	1.60	-0.14, #diepte van het toiletraam-43
+						5.04	1.60	-0.14,
+						5.04	2.30	-0.14,
+						4.84	2.30	-0.14,
+
+						0.60	3.60	-0.14, #diepte van het bovenraam-47
+						1.50	3.60	-0.14,
+						1.50	5.0	-0.14,
+						4.42	5.0	-0.14,
+						4.42	5.40	-0.14,
+						0.60	5.40	-0.14, #52
+
+						2.25	0	-8.86, #diepte van de achtergevel-53
+						2.25	0.6	-8.86,
+						4.8	0.6	-8.86,
+						4.8	5.40	-8.86,
+						1.2	0	-8.86,
+						1.2	2.7	-8.86,	#58
+
+						0.14	5.40	0,	#59 extra punten voor segmentering
+						5.54	5.40	0,
+						0.14	5.40	-6.30,
+						5.54	5.40	-6.3,  #62
+						5.54	8.55	-3.15, #rechtergevel-63
+						0.14	8.55	-3.15, #linkergevel-64
+						0 	5.4	-6.3,
+					]
+				}
+				normal            NULL
+				texCoord          NULL
+				ccw               TRUE
+				colorIndex        []
+				colorPerVertex    TRUE
+				convex            TRUE
+				coordIndex  [
+	3, 22, 65, -1,
+	2, 26, 25, -1,
+
+	3, 59, 64, 22, -1,
+	61, 65, 22, 64, -1,
+
+	60, 2, 25, 63, -1,
+	63, 25, 26, 62, -1,
+
+
+]     #default []
+				creaseAngle       0
+				normalIndex       []
+				normalPerVertex   TRUE
+				solid             FALSE
+				texCoordIndex     []
+			}
+		}
+	]
+}
+
+
+
+# /buitmuur.wrl
+
+# /zolder/zolder.wrl
+
+DEF tijd2 TimeSensor {
+	cycleInterval 4
+	enabled       TRUE
+	loop          FALSE
+	startTime     0
+	stopTime      0
+}
+
+DEF plaats2 PositionInterpolator {
+	key           [0, 1]
+	keyValue      [0 0 0 ,6 0 0]
+}
+
+DEF tijd2a TimeSensor {
+	cycleInterval 4
+	enabled       TRUE
+	loop          FALSE
+	startTime     0
+	stopTime      0
+}
+
+DEF plaats2a PositionInterpolator {
+	key           [0, 1]
+	keyValue      [6 0 0 ,0 0 0]
+}
+
+
+
+]
+}#zolder
+
+DEF tijd3 TimeSensor {
+	cycleInterval 4
+	enabled       TRUE
+	loop          FALSE
+	startTime     0
+	stopTime      0
+}
+
+DEF plaats3 PositionInterpolator {
+	key           [0, 1]
+	keyValue      [0 0 0 ,6 0 0]
+}
+
+DEF tijd3a TimeSensor {
+	cycleInterval 4
+	enabled       TRUE
+	loop          FALSE
+	startTime     0
+	stopTime      0
+}
+
+DEF plaats3a PositionInterpolator {
+	key           [0, 1]
+	keyValue      [6 0 0 ,0 0 0]
+}
+
+
+]
+}#boven
+
+
+
+]
+}
+
+ROUTE schuifdak.touchTime TO tijd1.startTime
+ROUTE tijd1.fraction_changed TO plaats1.set_fraction
+ROUTE plaats1.value_changed TO dak.translation
+
+ROUTE schuifzolder.touchTime TO tijd2.startTime
+ROUTE tijd2.fraction_changed TO plaats2.set_fraction
+ROUTE plaats2.value_changed TO zolder.translation
+
+ROUTE schuifboven.touchTime TO tijd3.startTime
+ROUTE tijd3.fraction_changed TO plaats3.set_fraction
+ROUTE plaats3.value_changed TO boven.translation
+
+ROUTE schuifdak2.touchTime TO tijd1a.startTime
+ROUTE tijd1a.fraction_changed TO plaats1a.set_fraction
+ROUTE plaats1a.value_changed TO dak.translation
+
+ROUTE schuifzolder2.touchTime TO tijd2a.startTime
+ROUTE tijd2a.fraction_changed TO plaats2a.set_fraction
+ROUTE plaats2a.value_changed TO zolder.translation
+
+ROUTE schuifboven2.touchTime TO tijd3a.startTime
+ROUTE tijd3a.fraction_changed TO plaats3a.set_fraction
+ROUTE plaats3a.value_changed TO boven.translation
+

+ 89 - 0
examples/js/libs/VrmlParser/make_single_file.php

@@ -0,0 +1,89 @@
+<?php
+/**
+ * This builds a single *.wrl (VRML) file from files that previously used
+ * Inline nodes to include seperates files.
+ *
+ * The output file is named after the input file, using a prefix of single_.
+ *
+ * For example house.wrl will become single_house.wrl.
+ *
+ * The Inline nodes specify a url that is relative to the file being read.
+ * Therefore, while descending down the file tree, the relative path must be
+ * respected in order to load the correct wrl content in lieu of the Inline node.
+ *
+ * Usage:
+ * php make_single_file.php filepath [output_path] (convert a file)
+ * php make_single_file.php help (display this help text)
+ *
+ * The filepath is required. This is the path to the root *.wrl file, the file
+ * that includes all other Inline wrl files.
+ *
+ * The output_path is optional. If you omit this argument, the output file will be written
+ * in the same directory where the input file lives.
+ *
+ * @author Bart McLeod ([email protected])
+ * @since 18-10-13 13:04
+ */
+
+if (!isset($argv[1])) {
+    throw new DomainException('The first argument is required and must specify the input *.wrl file.');
+}
+
+$rootFile = $argv[1];
+
+if (!file_exists($rootFile)) {
+    throw new DomainException('The input file does not exist or is not accessible.');
+}
+
+$outputPath = isset($argv[2]) ? $argv[2] : dirname($rootFile);
+
+if (false !== $outputPath && !is_dir($outputPath)) {
+    throw new DomainException('The second argument must be a path to a directory. The specified path is not a directory.');
+}
+
+if (false !== $outputPath && !is_writable($outputPath)) {
+    throw new DomainException('The specified output directory is not writable.');
+}
+
+$outputFile = $outputPath . '/single_' . basename($rootFile);
+
+
+function replace_wrl($file) {
+    $path = dirname($file);
+    $content = file_get_contents($file);
+
+    // disable animations for now, because the VRMLLoader of 3 js will choke on it if there is a rotation in it, thinking it is a rotation inside a transform
+    // $content = str_replace('ROUTE', '#ROUTE', $content);
+    // remove vrml header
+    $content = str_replace('#VRML V2.0 utf8', '', $content);
+
+    // @todo: this code will also include Inline wrl files that are commented out (if the files can be found), it might be hard to keep those out.
+    $content = preg_replace_callback(
+        '/Inline\s?{\s+?url\s+?\["([^"]*)"\]\s+?}/i',
+        function($matches) use ($path) {
+            $filename =  $matches[1];
+//            if ($filename === 'boven/boven.wrl') {
+//                echo PHP_EOL, "Skipped boven", PHP_EOL;
+//                return;
+//            }
+//            if ($filename === 'beneden/beneden.wrl') {
+//                echo PHP_EOL, "Skipped beneden", PHP_EOL;
+//                return;
+//            }
+            $output = replace_wrl($path . DIRECTORY_SEPARATOR . $filename);
+            // mark beginning and end of original include with the filename
+            $output = '# ' . $filename . PHP_EOL . PHP_EOL . $output . PHP_EOL . PHP_EOL . '# /' . $filename;
+            return $output;
+        }
+        ,
+        $content
+    );
+
+    return $content;
+};
+
+
+$content = '#VRML V2.0 utf8' . PHP_EOL . replace_wrl($rootFile);
+file_put_contents($outputFile, $content);
+
+echo 'Conversion of ' . $rootFile . ' succeeded, the file was written to ' . $outputFile, PHP_EOL, 0;

+ 23 - 0
examples/js/libs/VrmlParser/vrml-parser.js

@@ -0,0 +1,23 @@
+/**
+ * @author Bart McLeod [email protected]
+ * @since 2016-03-29
+ */
+
+require('pegjs-require');
+var fs = require('fs');
+var parser = require('./vrml.pegjs');
+var consoleRenderer = require('./Renderer/Console.js');
+var vrmlText = fs.readFileSync('./test.wrl', 'utf8');
+//var vrmlText = fs.readFileSync('./SimpleIndexedFaceSet.wrl', 'utf8');
+
+try {
+  var nodeTree = parser.parse(vrmlText);
+  consoleRenderer.render(nodeTree);
+} catch (e) {
+  console.log('Exception with message ' + e.message);
+
+  if (undefined !== e.location) {
+    console.log('Exception at location start: offset: ' + e.location.start.offset + ' line: ' + e.location.start.line + ' column: ' + e.location.start.column);
+    console.log('Exception at location end: offset: ' + e.location.end.offset + ' line: ' + e.location.end.line + ' column: ' + e.location.end.column);
+  }
+}

+ 2241 - 0
examples/js/libs/VrmlParser/vrml.js

@@ -0,0 +1,2241 @@
+vrmlParser = (function() {
+  "use strict";
+
+  /*
+   * Generated by PEG.js 0.9.0.
+   *
+   * http://pegjs.org/
+   */
+
+  function peg$subclass(child, parent) {
+    function ctor() { this.constructor = child; }
+    ctor.prototype = parent.prototype;
+    child.prototype = new ctor();
+  }
+
+  function peg$SyntaxError(message, expected, found, location) {
+    this.message  = message;
+    this.expected = expected;
+    this.found    = found;
+    this.location = location;
+    this.name     = "SyntaxError";
+
+    if (typeof Error.captureStackTrace === "function") {
+      Error.captureStackTrace(this, peg$SyntaxError);
+    }
+  }
+
+  peg$subclass(peg$SyntaxError, Error);
+
+  function peg$parse(input) {
+    var options = arguments.length > 1 ? arguments[1] : {},
+        parser  = this,
+
+        peg$FAILED = {},
+
+        peg$startRuleFunctions = { vrml: peg$parsevrml },
+        peg$startRuleFunction  = peg$parsevrml,
+
+        peg$c0 = "#VRML V2.0 utf8",
+        peg$c1 = { type: "literal", value: "#VRML V2.0 utf8", description: "\"#VRML V2.0 utf8\"" },
+        peg$c2 = function(vrml) {
+        	    // before returning the root vrml object, enricht it with routes and nodeDefinitions
+        		vrml.nodeDefinitions = nodeDefinitions;
+        		vrml.routes = routes;
+        		return vrml;
+        	},
+        peg$c3 = function(name) { return name; },
+        peg$c4 = function(name, n) {
+                n.name = name;
+                n.isDefinition = true;
+                // store node for later re-use
+                nodeDefinitions[name] = n;
+                //console.log('Registered as ' + name + ' in nodeDefinitions:');
+                //console.log(n);
+                return n;
+            },
+        peg$c5 = function(t, pp) {
+        		var n = {node: t};
+
+        		// node properties are in pp, if pp is not an Inline node, if pp is an inline node, it should be read from the url
+        		for (var i=0; i < pp.length; i++) {
+        			var p = pp[i];
+
+        			// is p a node?
+        			if (undefined !== p.node) {
+        				//console.log(p.node + ' node found');
+
+        				// if the node does not already have children, create children here
+        				if (undefined === n.children) {
+        					n.children = [];
+        				}
+
+        				// @todo for an Inline node, we could use the parser (named 'parser') and fs here, to fetch the inline file and parse it
+        				// on the other hand, it could be left up to the renderers what to do with the inline node.
+        				/*
+        				@see http://pegjs.org/documentation#grammar-syntax-and-semantics
+        				The code inside the predicate can also access the parser object using the parser variable and options passed to the parser using the options variable.
+        				*/
+        				n.children.push(p);
+
+        			} else if (undefined !== p.name) {
+        				// p is a property
+        				n[p.name] = p.value;
+
+        				if (undefined !== p.comment) {
+        					if (undefined === n.comments) { n.comments = {}; }
+        					if (undefined === n.comments[p.name]) { n.comments[p.name] = []; }
+        					n.comments[p.name].push(p.comment);
+        				}
+        			} else if (undefined !== p.src) {
+        			    // p is a route
+        			    // move it to global scope
+        			    routes.push(p);
+        			} else {
+        				// p is a comment
+        				if (undefined === n.nodeComments) {
+                            n.nodeComments = [];
+                        }
+                        n.nodeComments.push(p);
+        			}
+        		}
+
+        		return n;
+        	},
+        peg$c6 = function(name, value, comment) {
+                var p = { name:name, value:value };
+
+                // you could change a color property here by returning r g b instead of x y z
+
+                if (null !== comment) {
+                    p.comment = comment;
+                }
+                return p;
+            },
+        peg$c7 = { type: "other", description: "identifier" },
+        peg$c8 = /^[A-Za-z0-9_]/,
+        peg$c9 = { type: "class", value: "[A-Za-z0-9_]", description: "[A-Za-z0-9_]" },
+        peg$c10 = function(o) { return o.join(''); },
+        peg$c11 = { type: "other", description: "array" },
+        peg$c12 = function(v) { return v; },
+        peg$c13 = function(it) {
+                var a = [];
+                for (var i=0; i < it.length; i++) {
+                    var value = it[i];
+
+                    if (undefined !== value.src) {
+                        // value is a route, add to global routes
+                        routes.push(value);
+                    } else if (undefined !== value.comment) {
+                        // value is a comment
+                        if (undefined === a.comments) {
+                            a.comments = [];
+                        }
+
+                        a.comments.push(value);
+                    } else {
+                        // this is what we are looking for: a value for in our array!
+                        a.push(value);
+                    }
+                }
+
+                return a;
+            },
+        peg$c14 = { type: "other", description: "value" },
+        peg$c15 = "false",
+        peg$c16 = { type: "literal", value: "false", description: "\"false\"" },
+        peg$c17 = "FALSE",
+        peg$c18 = { type: "literal", value: "FALSE", description: "\"FALSE\"" },
+        peg$c19 = function() { return false; },
+        peg$c20 = "null",
+        peg$c21 = { type: "literal", value: "null", description: "\"null\"" },
+        peg$c22 = "NULL",
+        peg$c23 = { type: "literal", value: "NULL", description: "\"NULL\"" },
+        peg$c24 = function() { return null;  },
+        peg$c25 = "true",
+        peg$c26 = { type: "literal", value: "true", description: "\"true\"" },
+        peg$c27 = "TRUE",
+        peg$c28 = { type: "literal", value: "TRUE", description: "\"TRUE\"" },
+        peg$c29 = function() { return true;  },
+        peg$c30 = { type: "other", description: "number" },
+        peg$c31 = function() { return parseFloat(text()); },
+        peg$c32 = ".",
+        peg$c33 = { type: "literal", value: ".", description: "\".\"" },
+        peg$c34 = /^[1-9]/,
+        peg$c35 = { type: "class", value: "[1-9]", description: "[1-9]" },
+        peg$c36 = /^[eE]/,
+        peg$c37 = { type: "class", value: "[eE]", description: "[eE]" },
+        peg$c38 = "-",
+        peg$c39 = { type: "literal", value: "-", description: "\"-\"" },
+        peg$c40 = "+",
+        peg$c41 = { type: "literal", value: "+", description: "\"+\"" },
+        peg$c42 = "0",
+        peg$c43 = { type: "literal", value: "0", description: "\"0\"" },
+        peg$c44 = "#",
+        peg$c45 = { type: "literal", value: "#", description: "\"#\"" },
+        peg$c46 = /^[^\n]/,
+        peg$c47 = { type: "class", value: "[^\\n]", description: "[^\\n]" },
+        peg$c48 = function(text) { return { comment: text.join('').trim() }; },
+        peg$c49 = "ROUTE",
+        peg$c50 = { type: "literal", value: "ROUTE", description: "\"ROUTE\"" },
+        peg$c51 = "TO",
+        peg$c52 = { type: "literal", value: "TO", description: "\"TO\"" },
+        peg$c53 = function(src, target) {
+        	    var route = { source: src, target: target };
+        	    // put it in the global routes collection
+        	    routes.push(route);
+        	    return route;
+        	},
+        peg$c54 = function(name, property) { return { name: name, property: property }; },
+        peg$c55 = "[",
+        peg$c56 = { type: "literal", value: "[", description: "\"[\"" },
+        peg$c57 = "{",
+        peg$c58 = { type: "literal", value: "{", description: "\"{\"" },
+        peg$c59 = "]",
+        peg$c60 = { type: "literal", value: "]", description: "\"]\"" },
+        peg$c61 = "}",
+        peg$c62 = { type: "literal", value: "}", description: "\"}\"" },
+        peg$c63 = ",",
+        peg$c64 = { type: "literal", value: ",", description: "\",\"" },
+        peg$c65 = { type: "other", description: "whitespace" },
+        peg$c66 = /^[ \t\n\r]/,
+        peg$c67 = { type: "class", value: "[ \\t\\n\\r]", description: "[ \\t\\n\\r]" },
+        peg$c68 = function(ws) { return ws.join('');},
+        peg$c69 = " ",
+        peg$c70 = { type: "literal", value: " ", description: "\" \"" },
+        peg$c71 = function(p) { return p; },
+        peg$c72 = function(x, y, z) { return {x:x, y:y, z:z}; },
+        peg$c73 = "DEF",
+        peg$c74 = { type: "literal", value: "DEF", description: "\"DEF\"" },
+        peg$c75 = function() { return true; },
+        peg$c76 = function(name) {
+        	    var obj = nodeDefinitions[name];
+
+        	    if (undefined === obj) {
+        	        console.log(name + ' not found in nodeDefinitions');
+        	        return obj; // undefined obj
+        	    }
+
+        	    if ('function' === typeof obj.clone) {
+        	        return obj.clone();
+        	    }
+
+        	    return obj;
+        	},
+        peg$c77 = "USE",
+        peg$c78 = { type: "literal", value: "USE", description: "\"USE\"" },
+        peg$c79 = "-1",
+        peg$c80 = { type: "literal", value: "-1", description: "\"-1\"" },
+        peg$c81 = function(points) { return points; },
+        peg$c82 = function(i) { if (i==0) { return i; } return i.join(''); },
+        peg$c83 = function(x, y, z, radians) { return {x:x, y:y, z:z, radians:radians}; },
+        peg$c84 = function(uri) { return uri; },
+        peg$c85 = /^[^"]/,
+        peg$c86 = { type: "class", value: "[^\"]", description: "[^\"]" },
+        peg$c87 = "jpg",
+        peg$c88 = { type: "literal", value: "jpg", description: "\"jpg\"" },
+        peg$c89 = "jpeg",
+        peg$c90 = { type: "literal", value: "jpeg", description: "\"jpeg\"" },
+        peg$c91 = "gif",
+        peg$c92 = { type: "literal", value: "gif", description: "\"gif\"" },
+        peg$c93 = "wrl",
+        peg$c94 = { type: "literal", value: "wrl", description: "\"wrl\"" },
+        peg$c95 = function(i, dot, ext) { return i + dot + ext + "BOOOO"; },
+        peg$c96 = function(s) { return '"' + s.join('') + '"'; },
+        peg$c97 = "\"",
+        peg$c98 = { type: "literal", value: "\"", description: "\"\\\"\"" },
+        peg$c99 = /^[0-9]/,
+        peg$c100 = { type: "class", value: "[0-9]", description: "[0-9]" },
+        peg$c101 = /^[0-9a-f]/i,
+        peg$c102 = { type: "class", value: "[0-9a-f]i", description: "[0-9a-f]i" },
+
+        peg$currPos          = 0,
+        peg$savedPos         = 0,
+        peg$posDetailsCache  = [{ line: 1, column: 1, seenCR: false }],
+        peg$maxFailPos       = 0,
+        peg$maxFailExpected  = [],
+        peg$silentFails      = 0,
+
+        peg$result;
+
+    if ("startRule" in options) {
+      if (!(options.startRule in peg$startRuleFunctions)) {
+        throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
+      }
+
+      peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
+    }
+
+    function text() {
+      return input.substring(peg$savedPos, peg$currPos);
+    }
+
+    function location() {
+      return peg$computeLocation(peg$savedPos, peg$currPos);
+    }
+
+    function expected(description) {
+      throw peg$buildException(
+        null,
+        [{ type: "other", description: description }],
+        input.substring(peg$savedPos, peg$currPos),
+        peg$computeLocation(peg$savedPos, peg$currPos)
+      );
+    }
+
+    function error(message) {
+      throw peg$buildException(
+        message,
+        null,
+        input.substring(peg$savedPos, peg$currPos),
+        peg$computeLocation(peg$savedPos, peg$currPos)
+      );
+    }
+
+    function peg$computePosDetails(pos) {
+      var details = peg$posDetailsCache[pos],
+          p, ch;
+
+      if (details) {
+        return details;
+      } else {
+        p = pos - 1;
+        while (!peg$posDetailsCache[p]) {
+          p--;
+        }
+
+        details = peg$posDetailsCache[p];
+        details = {
+          line:   details.line,
+          column: details.column,
+          seenCR: details.seenCR
+        };
+
+        while (p < pos) {
+          ch = input.charAt(p);
+          if (ch === "\n") {
+            if (!details.seenCR) { details.line++; }
+            details.column = 1;
+            details.seenCR = false;
+          } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") {
+            details.line++;
+            details.column = 1;
+            details.seenCR = true;
+          } else {
+            details.column++;
+            details.seenCR = false;
+          }
+
+          p++;
+        }
+
+        peg$posDetailsCache[pos] = details;
+        return details;
+      }
+    }
+
+    function peg$computeLocation(startPos, endPos) {
+      var startPosDetails = peg$computePosDetails(startPos),
+          endPosDetails   = peg$computePosDetails(endPos);
+
+      return {
+        start: {
+          offset: startPos,
+          line:   startPosDetails.line,
+          column: startPosDetails.column
+        },
+        end: {
+          offset: endPos,
+          line:   endPosDetails.line,
+          column: endPosDetails.column
+        }
+      };
+    }
+
+    function peg$fail(expected) {
+      if (peg$currPos < peg$maxFailPos) { return; }
+
+      if (peg$currPos > peg$maxFailPos) {
+        peg$maxFailPos = peg$currPos;
+        peg$maxFailExpected = [];
+      }
+
+      peg$maxFailExpected.push(expected);
+    }
+
+    function peg$buildException(message, expected, found, location) {
+      function cleanupExpected(expected) {
+        var i = 1;
+
+        expected.sort(function(a, b) {
+          if (a.description < b.description) {
+            return -1;
+          } else if (a.description > b.description) {
+            return 1;
+          } else {
+            return 0;
+          }
+        });
+
+        while (i < expected.length) {
+          if (expected[i - 1] === expected[i]) {
+            expected.splice(i, 1);
+          } else {
+            i++;
+          }
+        }
+      }
+
+      function buildMessage(expected, found) {
+        function stringEscape(s) {
+          function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }
+
+          return s
+            .replace(/\\/g,   '\\\\')
+            .replace(/"/g,    '\\"')
+            .replace(/\x08/g, '\\b')
+            .replace(/\t/g,   '\\t')
+            .replace(/\n/g,   '\\n')
+            .replace(/\f/g,   '\\f')
+            .replace(/\r/g,   '\\r')
+            .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
+            .replace(/[\x10-\x1F\x80-\xFF]/g,    function(ch) { return '\\x'  + hex(ch); })
+            .replace(/[\u0100-\u0FFF]/g,         function(ch) { return '\\u0' + hex(ch); })
+            .replace(/[\u1000-\uFFFF]/g,         function(ch) { return '\\u'  + hex(ch); });
+        }
+
+        var expectedDescs = new Array(expected.length),
+            expectedDesc, foundDesc, i;
+
+        for (i = 0; i < expected.length; i++) {
+          expectedDescs[i] = expected[i].description;
+        }
+
+        expectedDesc = expected.length > 1
+          ? expectedDescs.slice(0, -1).join(", ")
+              + " or "
+              + expectedDescs[expected.length - 1]
+          : expectedDescs[0];
+
+        foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input";
+
+        return "Expected " + expectedDesc + " but " + foundDesc + " found.";
+      }
+
+      if (expected !== null) {
+        cleanupExpected(expected);
+      }
+
+      return new peg$SyntaxError(
+        message !== null ? message : buildMessage(expected, found),
+        expected,
+        found,
+        location
+      );
+    }
+
+    function peg$parsevrml() {
+      var s0, s1, s2, s3;
+
+      s0 = peg$currPos;
+      if (input.substr(peg$currPos, 15) === peg$c0) {
+        s1 = peg$c0;
+        peg$currPos += 15;
+      } else {
+        s1 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c1); }
+      }
+      if (s1 !== peg$FAILED) {
+        s2 = [];
+        s3 = peg$parsenodeDefinition();
+        if (s3 === peg$FAILED) {
+          s3 = peg$parsenode();
+          if (s3 === peg$FAILED) {
+            s3 = peg$parsecomment();
+            if (s3 === peg$FAILED) {
+              s3 = peg$parseroute();
+            }
+          }
+        }
+        while (s3 !== peg$FAILED) {
+          s2.push(s3);
+          s3 = peg$parsenodeDefinition();
+          if (s3 === peg$FAILED) {
+            s3 = peg$parsenode();
+            if (s3 === peg$FAILED) {
+              s3 = peg$parsecomment();
+              if (s3 === peg$FAILED) {
+                s3 = peg$parseroute();
+              }
+            }
+          }
+        }
+        if (s2 !== peg$FAILED) {
+          peg$savedPos = s0;
+          s1 = peg$c2(s2);
+          s0 = s1;
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parsenodeDefinition() {
+      var s0, s1, s2, s3, s4, s5, s6;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        s2 = peg$currPos;
+        s3 = peg$parsedef();
+        if (s3 !== peg$FAILED) {
+          s4 = peg$parsews();
+          if (s4 !== peg$FAILED) {
+            s5 = peg$parseidentifier();
+            if (s5 !== peg$FAILED) {
+              s6 = peg$parsews();
+              if (s6 !== peg$FAILED) {
+                peg$savedPos = s2;
+                s3 = peg$c3(s5);
+                s2 = s3;
+              } else {
+                peg$currPos = s2;
+                s2 = peg$FAILED;
+              }
+            } else {
+              peg$currPos = s2;
+              s2 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s2;
+            s2 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s2;
+          s2 = peg$FAILED;
+        }
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsenode();
+          if (s3 !== peg$FAILED) {
+            peg$savedPos = s0;
+            s1 = peg$c4(s2, s3);
+            s0 = s1;
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parsenode() {
+      var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        s2 = peg$parseidentifier();
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s4 = peg$parsebegin_node();
+            if (s4 !== peg$FAILED) {
+              s5 = peg$parsews();
+              if (s5 !== peg$FAILED) {
+                s6 = [];
+                s7 = peg$parsenodeDefinition();
+                if (s7 === peg$FAILED) {
+                  s7 = peg$parseroute();
+                  if (s7 === peg$FAILED) {
+                    s7 = peg$parseproperty();
+                    if (s7 === peg$FAILED) {
+                      s7 = peg$parsenode();
+                      if (s7 === peg$FAILED) {
+                        s7 = peg$parsecomment();
+                      }
+                    }
+                  }
+                }
+                while (s7 !== peg$FAILED) {
+                  s6.push(s7);
+                  s7 = peg$parsenodeDefinition();
+                  if (s7 === peg$FAILED) {
+                    s7 = peg$parseroute();
+                    if (s7 === peg$FAILED) {
+                      s7 = peg$parseproperty();
+                      if (s7 === peg$FAILED) {
+                        s7 = peg$parsenode();
+                        if (s7 === peg$FAILED) {
+                          s7 = peg$parsecomment();
+                        }
+                      }
+                    }
+                  }
+                }
+                if (s6 !== peg$FAILED) {
+                  s7 = peg$parsews();
+                  if (s7 !== peg$FAILED) {
+                    s8 = peg$parseend_node();
+                    if (s8 !== peg$FAILED) {
+                      s9 = peg$parsews();
+                      if (s9 !== peg$FAILED) {
+                        peg$savedPos = s0;
+                        s1 = peg$c5(s2, s6);
+                        s0 = s1;
+                      } else {
+                        peg$currPos = s0;
+                        s0 = peg$FAILED;
+                      }
+                    } else {
+                      peg$currPos = s0;
+                      s0 = peg$FAILED;
+                    }
+                  } else {
+                    peg$currPos = s0;
+                    s0 = peg$FAILED;
+                  }
+                } else {
+                  peg$currPos = s0;
+                  s0 = peg$FAILED;
+                }
+              } else {
+                peg$currPos = s0;
+                s0 = peg$FAILED;
+              }
+            } else {
+              peg$currPos = s0;
+              s0 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parseproperty() {
+      var s0, s1, s2, s3, s4, s5, s6;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        s2 = peg$parseidentifier();
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s4 = peg$parsevalue();
+            if (s4 !== peg$FAILED) {
+              s5 = peg$parsews();
+              if (s5 !== peg$FAILED) {
+                s6 = peg$parsecomment();
+                if (s6 === peg$FAILED) {
+                  s6 = null;
+                }
+                if (s6 !== peg$FAILED) {
+                  peg$savedPos = s0;
+                  s1 = peg$c6(s2, s4, s6);
+                  s0 = s1;
+                } else {
+                  peg$currPos = s0;
+                  s0 = peg$FAILED;
+                }
+              } else {
+                peg$currPos = s0;
+                s0 = peg$FAILED;
+              }
+            } else {
+              peg$currPos = s0;
+              s0 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parseidentifier() {
+      var s0, s1, s2;
+
+      peg$silentFails++;
+      s0 = peg$currPos;
+      s1 = [];
+      if (peg$c8.test(input.charAt(peg$currPos))) {
+        s2 = input.charAt(peg$currPos);
+        peg$currPos++;
+      } else {
+        s2 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c9); }
+      }
+      if (s2 !== peg$FAILED) {
+        while (s2 !== peg$FAILED) {
+          s1.push(s2);
+          if (peg$c8.test(input.charAt(peg$currPos))) {
+            s2 = input.charAt(peg$currPos);
+            peg$currPos++;
+          } else {
+            s2 = peg$FAILED;
+            if (peg$silentFails === 0) { peg$fail(peg$c9); }
+          }
+        }
+      } else {
+        s1 = peg$FAILED;
+      }
+      if (s1 !== peg$FAILED) {
+        peg$savedPos = s0;
+        s1 = peg$c10(s1);
+      }
+      s0 = s1;
+      peg$silentFails--;
+      if (s0 === peg$FAILED) {
+        s1 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c7); }
+      }
+
+      return s0;
+    }
+
+    function peg$parsearray() {
+      var s0, s1, s2, s3, s4, s5, s6;
+
+      peg$silentFails++;
+      s0 = peg$currPos;
+      s1 = peg$parsebegin_array();
+      if (s1 !== peg$FAILED) {
+        s2 = [];
+        s3 = peg$parsecomment();
+        if (s3 === peg$FAILED) {
+          s3 = peg$parseroute();
+          if (s3 === peg$FAILED) {
+            s3 = peg$currPos;
+            s4 = peg$parsevalue();
+            if (s4 !== peg$FAILED) {
+              s5 = peg$parsews();
+              if (s5 !== peg$FAILED) {
+                s6 = peg$parsevalue_separator();
+                if (s6 === peg$FAILED) {
+                  s6 = null;
+                }
+                if (s6 !== peg$FAILED) {
+                  peg$savedPos = s3;
+                  s4 = peg$c12(s4);
+                  s3 = s4;
+                } else {
+                  peg$currPos = s3;
+                  s3 = peg$FAILED;
+                }
+              } else {
+                peg$currPos = s3;
+                s3 = peg$FAILED;
+              }
+            } else {
+              peg$currPos = s3;
+              s3 = peg$FAILED;
+            }
+          }
+        }
+        while (s3 !== peg$FAILED) {
+          s2.push(s3);
+          s3 = peg$parsecomment();
+          if (s3 === peg$FAILED) {
+            s3 = peg$parseroute();
+            if (s3 === peg$FAILED) {
+              s3 = peg$currPos;
+              s4 = peg$parsevalue();
+              if (s4 !== peg$FAILED) {
+                s5 = peg$parsews();
+                if (s5 !== peg$FAILED) {
+                  s6 = peg$parsevalue_separator();
+                  if (s6 === peg$FAILED) {
+                    s6 = null;
+                  }
+                  if (s6 !== peg$FAILED) {
+                    peg$savedPos = s3;
+                    s4 = peg$c12(s4);
+                    s3 = s4;
+                  } else {
+                    peg$currPos = s3;
+                    s3 = peg$FAILED;
+                  }
+                } else {
+                  peg$currPos = s3;
+                  s3 = peg$FAILED;
+                }
+              } else {
+                peg$currPos = s3;
+                s3 = peg$FAILED;
+              }
+            }
+          }
+        }
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parseend_array();
+          if (s3 !== peg$FAILED) {
+            peg$savedPos = s0;
+            s1 = peg$c13(s2);
+            s0 = s1;
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+      peg$silentFails--;
+      if (s0 === peg$FAILED) {
+        s1 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c11); }
+      }
+
+      return s0;
+    }
+
+    function peg$parsevalue() {
+      var s0, s1;
+
+      peg$silentFails++;
+      s0 = peg$parsefalse();
+      if (s0 === peg$FAILED) {
+        s0 = peg$parseface();
+        if (s0 === peg$FAILED) {
+          s0 = peg$parsenull();
+          if (s0 === peg$FAILED) {
+            s0 = peg$parsetrue();
+            if (s0 === peg$FAILED) {
+              s0 = peg$parsenodeDefinition();
+              if (s0 === peg$FAILED) {
+                s0 = peg$parsenode();
+                if (s0 === peg$FAILED) {
+                  s0 = peg$parserotation();
+                  if (s0 === peg$FAILED) {
+                    s0 = peg$parsepoint();
+                    if (s0 === peg$FAILED) {
+                      s0 = peg$parsevector();
+                      if (s0 === peg$FAILED) {
+                        s0 = peg$parseuse_statement();
+                        if (s0 === peg$FAILED) {
+                          s0 = peg$parsearray();
+                          if (s0 === peg$FAILED) {
+                            s0 = peg$parsenumber();
+                            if (s0 === peg$FAILED) {
+                              s0 = peg$parsefloat();
+                              if (s0 === peg$FAILED) {
+                                s0 = peg$parseidentifier();
+                                if (s0 === peg$FAILED) {
+                                  s0 = peg$parseurl();
+                                  if (s0 === peg$FAILED) {
+                                    s0 = peg$parsequoted_string();
+                                  }
+                                }
+                              }
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+      peg$silentFails--;
+      if (s0 === peg$FAILED) {
+        s1 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c14); }
+      }
+
+      return s0;
+    }
+
+    function peg$parsefalse() {
+      var s0, s1;
+
+      if (input.substr(peg$currPos, 5) === peg$c15) {
+        s0 = peg$c15;
+        peg$currPos += 5;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c16); }
+      }
+      if (s0 === peg$FAILED) {
+        s0 = peg$currPos;
+        if (input.substr(peg$currPos, 5) === peg$c17) {
+          s1 = peg$c17;
+          peg$currPos += 5;
+        } else {
+          s1 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c18); }
+        }
+        if (s1 !== peg$FAILED) {
+          peg$savedPos = s0;
+          s1 = peg$c19();
+        }
+        s0 = s1;
+      }
+
+      return s0;
+    }
+
+    function peg$parsenull() {
+      var s0, s1;
+
+      if (input.substr(peg$currPos, 4) === peg$c20) {
+        s0 = peg$c20;
+        peg$currPos += 4;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c21); }
+      }
+      if (s0 === peg$FAILED) {
+        s0 = peg$currPos;
+        if (input.substr(peg$currPos, 4) === peg$c22) {
+          s1 = peg$c22;
+          peg$currPos += 4;
+        } else {
+          s1 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c23); }
+        }
+        if (s1 !== peg$FAILED) {
+          peg$savedPos = s0;
+          s1 = peg$c24();
+        }
+        s0 = s1;
+      }
+
+      return s0;
+    }
+
+    function peg$parsetrue() {
+      var s0, s1;
+
+      if (input.substr(peg$currPos, 4) === peg$c25) {
+        s0 = peg$c25;
+        peg$currPos += 4;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c26); }
+      }
+      if (s0 === peg$FAILED) {
+        s0 = peg$currPos;
+        if (input.substr(peg$currPos, 4) === peg$c27) {
+          s1 = peg$c27;
+          peg$currPos += 4;
+        } else {
+          s1 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c28); }
+        }
+        if (s1 !== peg$FAILED) {
+          peg$savedPos = s0;
+          s1 = peg$c29();
+        }
+        s0 = s1;
+      }
+
+      return s0;
+    }
+
+    function peg$parsenumber() {
+      var s0, s1, s2, s3, s4;
+
+      peg$silentFails++;
+      s0 = peg$currPos;
+      s1 = peg$parseminus();
+      if (s1 === peg$FAILED) {
+        s1 = null;
+      }
+      if (s1 !== peg$FAILED) {
+        s2 = peg$parseint();
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsefrac();
+          if (s3 === peg$FAILED) {
+            s3 = null;
+          }
+          if (s3 !== peg$FAILED) {
+            s4 = peg$parseexp();
+            if (s4 === peg$FAILED) {
+              s4 = null;
+            }
+            if (s4 !== peg$FAILED) {
+              peg$savedPos = s0;
+              s1 = peg$c31();
+              s0 = s1;
+            } else {
+              peg$currPos = s0;
+              s0 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+      peg$silentFails--;
+      if (s0 === peg$FAILED) {
+        s1 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c30); }
+      }
+
+      return s0;
+    }
+
+    function peg$parsedecimal_point() {
+      var s0;
+
+      if (input.charCodeAt(peg$currPos) === 46) {
+        s0 = peg$c32;
+        peg$currPos++;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c33); }
+      }
+
+      return s0;
+    }
+
+    function peg$parsedigit1_9() {
+      var s0;
+
+      if (peg$c34.test(input.charAt(peg$currPos))) {
+        s0 = input.charAt(peg$currPos);
+        peg$currPos++;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c35); }
+      }
+
+      return s0;
+    }
+
+    function peg$parsee() {
+      var s0;
+
+      if (peg$c36.test(input.charAt(peg$currPos))) {
+        s0 = input.charAt(peg$currPos);
+        peg$currPos++;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c37); }
+      }
+
+      return s0;
+    }
+
+    function peg$parseexp() {
+      var s0, s1, s2, s3, s4;
+
+      s0 = peg$currPos;
+      s1 = peg$parsee();
+      if (s1 !== peg$FAILED) {
+        s2 = peg$parseminus();
+        if (s2 === peg$FAILED) {
+          s2 = peg$parseplus();
+        }
+        if (s2 === peg$FAILED) {
+          s2 = null;
+        }
+        if (s2 !== peg$FAILED) {
+          s3 = [];
+          s4 = peg$parseDIGIT();
+          if (s4 !== peg$FAILED) {
+            while (s4 !== peg$FAILED) {
+              s3.push(s4);
+              s4 = peg$parseDIGIT();
+            }
+          } else {
+            s3 = peg$FAILED;
+          }
+          if (s3 !== peg$FAILED) {
+            s1 = [s1, s2, s3];
+            s0 = s1;
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parsefrac() {
+      var s0, s1, s2, s3;
+
+      s0 = peg$currPos;
+      s1 = peg$parsedecimal_point();
+      if (s1 !== peg$FAILED) {
+        s2 = [];
+        s3 = peg$parseDIGIT();
+        if (s3 !== peg$FAILED) {
+          while (s3 !== peg$FAILED) {
+            s2.push(s3);
+            s3 = peg$parseDIGIT();
+          }
+        } else {
+          s2 = peg$FAILED;
+        }
+        if (s2 !== peg$FAILED) {
+          s1 = [s1, s2];
+          s0 = s1;
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parseint() {
+      var s0, s1, s2, s3;
+
+      s0 = peg$parsezero();
+      if (s0 === peg$FAILED) {
+        s0 = peg$currPos;
+        s1 = peg$parsedigit1_9();
+        if (s1 !== peg$FAILED) {
+          s2 = [];
+          s3 = peg$parseDIGIT();
+          while (s3 !== peg$FAILED) {
+            s2.push(s3);
+            s3 = peg$parseDIGIT();
+          }
+          if (s2 !== peg$FAILED) {
+            s1 = [s1, s2];
+            s0 = s1;
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      }
+
+      return s0;
+    }
+
+    function peg$parseminus() {
+      var s0;
+
+      if (input.charCodeAt(peg$currPos) === 45) {
+        s0 = peg$c38;
+        peg$currPos++;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c39); }
+      }
+
+      return s0;
+    }
+
+    function peg$parseplus() {
+      var s0;
+
+      if (input.charCodeAt(peg$currPos) === 43) {
+        s0 = peg$c40;
+        peg$currPos++;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c41); }
+      }
+
+      return s0;
+    }
+
+    function peg$parsezero() {
+      var s0;
+
+      if (input.charCodeAt(peg$currPos) === 48) {
+        s0 = peg$c42;
+        peg$currPos++;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c43); }
+      }
+
+      return s0;
+    }
+
+    function peg$parsecomment() {
+      var s0, s1, s2, s3, s4;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        if (input.charCodeAt(peg$currPos) === 35) {
+          s2 = peg$c44;
+          peg$currPos++;
+        } else {
+          s2 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c45); }
+        }
+        if (s2 !== peg$FAILED) {
+          s3 = [];
+          if (peg$c46.test(input.charAt(peg$currPos))) {
+            s4 = input.charAt(peg$currPos);
+            peg$currPos++;
+          } else {
+            s4 = peg$FAILED;
+            if (peg$silentFails === 0) { peg$fail(peg$c47); }
+          }
+          while (s4 !== peg$FAILED) {
+            s3.push(s4);
+            if (peg$c46.test(input.charAt(peg$currPos))) {
+              s4 = input.charAt(peg$currPos);
+              peg$currPos++;
+            } else {
+              s4 = peg$FAILED;
+              if (peg$silentFails === 0) { peg$fail(peg$c47); }
+            }
+          }
+          if (s3 !== peg$FAILED) {
+            s4 = peg$parsews();
+            if (s4 !== peg$FAILED) {
+              peg$savedPos = s0;
+              s1 = peg$c48(s3);
+              s0 = s1;
+            } else {
+              peg$currPos = s0;
+              s0 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parseroute() {
+      var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        if (input.substr(peg$currPos, 5) === peg$c49) {
+          s2 = peg$c49;
+          peg$currPos += 5;
+        } else {
+          s2 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c50); }
+        }
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s4 = peg$parseroute_part();
+            if (s4 !== peg$FAILED) {
+              s5 = peg$parsews();
+              if (s5 !== peg$FAILED) {
+                if (input.substr(peg$currPos, 2) === peg$c51) {
+                  s6 = peg$c51;
+                  peg$currPos += 2;
+                } else {
+                  s6 = peg$FAILED;
+                  if (peg$silentFails === 0) { peg$fail(peg$c52); }
+                }
+                if (s6 !== peg$FAILED) {
+                  s7 = peg$parsews();
+                  if (s7 !== peg$FAILED) {
+                    s8 = peg$parseroute_part();
+                    if (s8 !== peg$FAILED) {
+                      s9 = peg$parsews();
+                      if (s9 !== peg$FAILED) {
+                        peg$savedPos = s0;
+                        s1 = peg$c53(s4, s8);
+                        s0 = s1;
+                      } else {
+                        peg$currPos = s0;
+                        s0 = peg$FAILED;
+                      }
+                    } else {
+                      peg$currPos = s0;
+                      s0 = peg$FAILED;
+                    }
+                  } else {
+                    peg$currPos = s0;
+                    s0 = peg$FAILED;
+                  }
+                } else {
+                  peg$currPos = s0;
+                  s0 = peg$FAILED;
+                }
+              } else {
+                peg$currPos = s0;
+                s0 = peg$FAILED;
+              }
+            } else {
+              peg$currPos = s0;
+              s0 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parseroute_part() {
+      var s0, s1, s2, s3;
+
+      s0 = peg$currPos;
+      s1 = peg$parseidentifier();
+      if (s1 !== peg$FAILED) {
+        if (input.charCodeAt(peg$currPos) === 46) {
+          s2 = peg$c32;
+          peg$currPos++;
+        } else {
+          s2 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c33); }
+        }
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parseidentifier();
+          if (s3 !== peg$FAILED) {
+            peg$savedPos = s0;
+            s1 = peg$c54(s1, s3);
+            s0 = s1;
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parsebegin_array() {
+      var s0, s1, s2, s3;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        if (input.charCodeAt(peg$currPos) === 91) {
+          s2 = peg$c55;
+          peg$currPos++;
+        } else {
+          s2 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c56); }
+        }
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s1 = [s1, s2, s3];
+            s0 = s1;
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parsebegin_node() {
+      var s0, s1, s2, s3;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        if (input.charCodeAt(peg$currPos) === 123) {
+          s2 = peg$c57;
+          peg$currPos++;
+        } else {
+          s2 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c58); }
+        }
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s1 = [s1, s2, s3];
+            s0 = s1;
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parseend_array() {
+      var s0, s1, s2, s3;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        if (input.charCodeAt(peg$currPos) === 93) {
+          s2 = peg$c59;
+          peg$currPos++;
+        } else {
+          s2 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c60); }
+        }
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s1 = [s1, s2, s3];
+            s0 = s1;
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parseend_node() {
+      var s0, s1, s2, s3;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        if (input.charCodeAt(peg$currPos) === 125) {
+          s2 = peg$c61;
+          peg$currPos++;
+        } else {
+          s2 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c62); }
+        }
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s1 = [s1, s2, s3];
+            s0 = s1;
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parsevalue_separator() {
+      var s0, s1, s2, s3;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        if (input.charCodeAt(peg$currPos) === 44) {
+          s2 = peg$c63;
+          peg$currPos++;
+        } else {
+          s2 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c64); }
+        }
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s1 = [s1, s2, s3];
+            s0 = s1;
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parsews() {
+      var s0, s1, s2;
+
+      peg$silentFails++;
+      s0 = peg$currPos;
+      s1 = [];
+      if (peg$c66.test(input.charAt(peg$currPos))) {
+        s2 = input.charAt(peg$currPos);
+        peg$currPos++;
+      } else {
+        s2 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c67); }
+      }
+      while (s2 !== peg$FAILED) {
+        s1.push(s2);
+        if (peg$c66.test(input.charAt(peg$currPos))) {
+          s2 = input.charAt(peg$currPos);
+          peg$currPos++;
+        } else {
+          s2 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c67); }
+        }
+      }
+      if (s1 !== peg$FAILED) {
+        peg$savedPos = s0;
+        s1 = peg$c68(s1);
+      }
+      s0 = s1;
+      peg$silentFails--;
+      if (s0 === peg$FAILED) {
+        s1 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c65); }
+      }
+
+      return s0;
+    }
+
+    function peg$parsespace() {
+      var s0;
+
+      if (input.charCodeAt(peg$currPos) === 32) {
+        s0 = peg$c69;
+        peg$currPos++;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c70); }
+      }
+
+      return s0;
+    }
+
+    function peg$parsepoint() {
+      var s0, s1, s2, s3, s4;
+
+      s0 = peg$currPos;
+      s1 = peg$parsevector();
+      if (s1 !== peg$FAILED) {
+        if (input.charCodeAt(peg$currPos) === 44) {
+          s2 = peg$c63;
+          peg$currPos++;
+        } else {
+          s2 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c64); }
+        }
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s4 = peg$parsecomment();
+            if (s4 === peg$FAILED) {
+              s4 = null;
+            }
+            if (s4 !== peg$FAILED) {
+              peg$savedPos = s0;
+              s1 = peg$c71(s1);
+              s0 = s1;
+            } else {
+              peg$currPos = s0;
+              s0 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parsevector() {
+      var s0, s1, s2, s3, s4, s5, s6, s7, s8;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        s2 = peg$parsenumber();
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s4 = peg$parsenumber();
+            if (s4 !== peg$FAILED) {
+              s5 = peg$parsews();
+              if (s5 !== peg$FAILED) {
+                s6 = peg$parsenumber();
+                if (s6 !== peg$FAILED) {
+                  s7 = peg$parsews();
+                  if (s7 !== peg$FAILED) {
+                    s8 = peg$parsecomment();
+                    if (s8 === peg$FAILED) {
+                      s8 = null;
+                    }
+                    if (s8 !== peg$FAILED) {
+                      peg$savedPos = s0;
+                      s1 = peg$c72(s2, s4, s6);
+                      s0 = s1;
+                    } else {
+                      peg$currPos = s0;
+                      s0 = peg$FAILED;
+                    }
+                  } else {
+                    peg$currPos = s0;
+                    s0 = peg$FAILED;
+                  }
+                } else {
+                  peg$currPos = s0;
+                  s0 = peg$FAILED;
+                }
+              } else {
+                peg$currPos = s0;
+                s0 = peg$FAILED;
+              }
+            } else {
+              peg$currPos = s0;
+              s0 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parsedef() {
+      var s0, s1;
+
+      s0 = peg$currPos;
+      if (input.substr(peg$currPos, 3) === peg$c73) {
+        s1 = peg$c73;
+        peg$currPos += 3;
+      } else {
+        s1 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c74); }
+      }
+      if (s1 !== peg$FAILED) {
+        peg$savedPos = s0;
+        s1 = peg$c75();
+      }
+      s0 = s1;
+
+      return s0;
+    }
+
+    function peg$parseuse_statement() {
+      var s0, s1, s2, s3, s4;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        s2 = peg$parseuse();
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s4 = peg$parseidentifier();
+            if (s4 !== peg$FAILED) {
+              peg$savedPos = s0;
+              s1 = peg$c76(s4);
+              s0 = s1;
+            } else {
+              peg$currPos = s0;
+              s0 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parseuse() {
+      var s0, s1;
+
+      s0 = peg$currPos;
+      if (input.substr(peg$currPos, 3) === peg$c77) {
+        s1 = peg$c77;
+        peg$currPos += 3;
+      } else {
+        s1 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c78); }
+      }
+      if (s1 !== peg$FAILED) {
+        peg$savedPos = s0;
+        s1 = peg$c75();
+      }
+      s0 = s1;
+
+      return s0;
+    }
+
+    function peg$parseface() {
+      var s0, s1, s2, s3;
+
+      s0 = peg$currPos;
+      s1 = [];
+      s2 = peg$parseindex();
+      if (s2 !== peg$FAILED) {
+        while (s2 !== peg$FAILED) {
+          s1.push(s2);
+          s2 = peg$parseindex();
+        }
+      } else {
+        s1 = peg$FAILED;
+      }
+      if (s1 !== peg$FAILED) {
+        if (input.substr(peg$currPos, 2) === peg$c79) {
+          s2 = peg$c79;
+          peg$currPos += 2;
+        } else {
+          s2 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c80); }
+        }
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            peg$savedPos = s0;
+            s1 = peg$c81(s1);
+            s0 = s1;
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parseindex() {
+      var s0, s1, s2, s3, s4, s5;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        s2 = peg$parseint();
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s4 = peg$parsevalue_separator();
+            if (s4 !== peg$FAILED) {
+              s5 = peg$parsews();
+              if (s5 !== peg$FAILED) {
+                peg$savedPos = s0;
+                s1 = peg$c82(s2);
+                s0 = s1;
+              } else {
+                peg$currPos = s0;
+                s0 = peg$FAILED;
+              }
+            } else {
+              peg$currPos = s0;
+              s0 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parserotation() {
+      var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        s2 = peg$parsenumber();
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s4 = peg$parsenumber();
+            if (s4 !== peg$FAILED) {
+              s5 = peg$parsews();
+              if (s5 !== peg$FAILED) {
+                s6 = peg$parsenumber();
+                if (s6 !== peg$FAILED) {
+                  s7 = peg$parsews();
+                  if (s7 !== peg$FAILED) {
+                    s8 = peg$parsenumber();
+                    if (s8 !== peg$FAILED) {
+                      s9 = peg$parsews();
+                      if (s9 !== peg$FAILED) {
+                        peg$savedPos = s0;
+                        s1 = peg$c83(s2, s4, s6, s8);
+                        s0 = s1;
+                      } else {
+                        peg$currPos = s0;
+                        s0 = peg$FAILED;
+                      }
+                    } else {
+                      peg$currPos = s0;
+                      s0 = peg$FAILED;
+                    }
+                  } else {
+                    peg$currPos = s0;
+                    s0 = peg$FAILED;
+                  }
+                } else {
+                  peg$currPos = s0;
+                  s0 = peg$FAILED;
+                }
+              } else {
+                peg$currPos = s0;
+                s0 = peg$FAILED;
+              }
+            } else {
+              peg$currPos = s0;
+              s0 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parseurl() {
+      var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        s2 = peg$parsebegin_array();
+        if (s2 !== peg$FAILED) {
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            s4 = peg$parsequote();
+            if (s4 !== peg$FAILED) {
+              s5 = peg$parseuri();
+              if (s5 !== peg$FAILED) {
+                s6 = peg$parsequote();
+                if (s6 !== peg$FAILED) {
+                  s7 = peg$parsews();
+                  if (s7 !== peg$FAILED) {
+                    s8 = peg$parseend_array();
+                    if (s8 !== peg$FAILED) {
+                      s9 = peg$parsews();
+                      if (s9 !== peg$FAILED) {
+                        peg$savedPos = s0;
+                        s1 = peg$c84(s5);
+                        s0 = s1;
+                      } else {
+                        peg$currPos = s0;
+                        s0 = peg$FAILED;
+                      }
+                    } else {
+                      peg$currPos = s0;
+                      s0 = peg$FAILED;
+                    }
+                  } else {
+                    peg$currPos = s0;
+                    s0 = peg$FAILED;
+                  }
+                } else {
+                  peg$currPos = s0;
+                  s0 = peg$FAILED;
+                }
+              } else {
+                peg$currPos = s0;
+                s0 = peg$FAILED;
+              }
+            } else {
+              peg$currPos = s0;
+              s0 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parseuri() {
+      var s0, s1, s2, s3;
+
+      s0 = peg$currPos;
+      s1 = [];
+      if (peg$c85.test(input.charAt(peg$currPos))) {
+        s2 = input.charAt(peg$currPos);
+        peg$currPos++;
+      } else {
+        s2 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c86); }
+      }
+      while (s2 !== peg$FAILED) {
+        s1.push(s2);
+        if (peg$c85.test(input.charAt(peg$currPos))) {
+          s2 = input.charAt(peg$currPos);
+          peg$currPos++;
+        } else {
+          s2 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c86); }
+        }
+      }
+      if (s1 !== peg$FAILED) {
+        if (input.charCodeAt(peg$currPos) === 46) {
+          s2 = peg$c32;
+          peg$currPos++;
+        } else {
+          s2 = peg$FAILED;
+          if (peg$silentFails === 0) { peg$fail(peg$c33); }
+        }
+        if (s2 !== peg$FAILED) {
+          if (input.substr(peg$currPos, 3) === peg$c87) {
+            s3 = peg$c87;
+            peg$currPos += 3;
+          } else {
+            s3 = peg$FAILED;
+            if (peg$silentFails === 0) { peg$fail(peg$c88); }
+          }
+          if (s3 === peg$FAILED) {
+            if (input.substr(peg$currPos, 4) === peg$c89) {
+              s3 = peg$c89;
+              peg$currPos += 4;
+            } else {
+              s3 = peg$FAILED;
+              if (peg$silentFails === 0) { peg$fail(peg$c90); }
+            }
+            if (s3 === peg$FAILED) {
+              if (input.substr(peg$currPos, 3) === peg$c91) {
+                s3 = peg$c91;
+                peg$currPos += 3;
+              } else {
+                s3 = peg$FAILED;
+                if (peg$silentFails === 0) { peg$fail(peg$c92); }
+              }
+              if (s3 === peg$FAILED) {
+                if (input.substr(peg$currPos, 3) === peg$c93) {
+                  s3 = peg$c93;
+                  peg$currPos += 3;
+                } else {
+                  s3 = peg$FAILED;
+                  if (peg$silentFails === 0) { peg$fail(peg$c94); }
+                }
+              }
+            }
+          }
+          if (s3 !== peg$FAILED) {
+            peg$savedPos = s0;
+            s1 = peg$c95(s1, s2, s3);
+            s0 = s1;
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parsequoted_string() {
+      var s0, s1, s2, s3, s4, s5;
+
+      s0 = peg$currPos;
+      s1 = peg$parsews();
+      if (s1 !== peg$FAILED) {
+        s2 = peg$parsequote();
+        if (s2 !== peg$FAILED) {
+          s3 = [];
+          if (peg$c85.test(input.charAt(peg$currPos))) {
+            s4 = input.charAt(peg$currPos);
+            peg$currPos++;
+          } else {
+            s4 = peg$FAILED;
+            if (peg$silentFails === 0) { peg$fail(peg$c86); }
+          }
+          while (s4 !== peg$FAILED) {
+            s3.push(s4);
+            if (peg$c85.test(input.charAt(peg$currPos))) {
+              s4 = input.charAt(peg$currPos);
+              peg$currPos++;
+            } else {
+              s4 = peg$FAILED;
+              if (peg$silentFails === 0) { peg$fail(peg$c86); }
+            }
+          }
+          if (s3 !== peg$FAILED) {
+            s4 = peg$parsequote();
+            if (s4 !== peg$FAILED) {
+              s5 = peg$parsews();
+              if (s5 !== peg$FAILED) {
+                peg$savedPos = s0;
+                s1 = peg$c96(s3);
+                s0 = s1;
+              } else {
+                peg$currPos = s0;
+                s0 = peg$FAILED;
+              }
+            } else {
+              peg$currPos = s0;
+              s0 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parsequote() {
+      var s0;
+
+      if (input.charCodeAt(peg$currPos) === 34) {
+        s0 = peg$c97;
+        peg$currPos++;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c98); }
+      }
+
+      return s0;
+    }
+
+    function peg$parsefloat() {
+      var s0, s1, s2;
+
+      s0 = peg$currPos;
+      s1 = peg$parseint();
+      if (s1 === peg$FAILED) {
+        s1 = null;
+      }
+      if (s1 !== peg$FAILED) {
+        s2 = peg$parsefrac();
+        if (s2 !== peg$FAILED) {
+          peg$savedPos = s0;
+          s1 = peg$c31();
+          s0 = s1;
+        } else {
+          peg$currPos = s0;
+          s0 = peg$FAILED;
+        }
+      } else {
+        peg$currPos = s0;
+        s0 = peg$FAILED;
+      }
+
+      return s0;
+    }
+
+    function peg$parseDIGIT() {
+      var s0;
+
+      if (peg$c99.test(input.charAt(peg$currPos))) {
+        s0 = input.charAt(peg$currPos);
+        peg$currPos++;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c100); }
+      }
+
+      return s0;
+    }
+
+    function peg$parseHEXDIG() {
+      var s0;
+
+      if (peg$c101.test(input.charAt(peg$currPos))) {
+        s0 = input.charAt(peg$currPos);
+        peg$currPos++;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$c102); }
+      }
+
+      return s0;
+    }
+
+
+    	var nodeDefinitions = [];
+    	var routes = [];
+
+
+    peg$result = peg$startRuleFunction();
+
+    if (peg$result !== peg$FAILED && peg$currPos === input.length) {
+      return peg$result;
+    } else {
+      if (peg$result !== peg$FAILED && peg$currPos < input.length) {
+        peg$fail({ type: "end", description: "end of input" });
+      }
+
+      throw peg$buildException(
+        null,
+        peg$maxFailExpected,
+        peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,
+        peg$maxFailPos < input.length
+          ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)
+          : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
+      );
+    }
+  }
+
+  return {
+    SyntaxError: peg$SyntaxError,
+    parse:       peg$parse
+  };
+})();

+ 295 - 0
examples/js/libs/VrmlParser/vrml.pegjs

@@ -0,0 +1,295 @@
+/*
+ * VRML Grammar
+ * ============
+ *
+ * VRML Grammar for pegjs, inspired by JSON grammar found here:
+ * https://github.com/pegjs/pegjs/blob/master/examples/json.pegjs
+ *
+ * @author Bart McLeod [email protected]
+ * @since 2016-05-04
+ *
+ * The primary goal of this grammar is to enable you to use a full VRML node tree with modern JavaScript
+ * 3D libraries, such as ThreeJS.If this grammar is used with Pegjs, it will produce a node tree that
+ * you can use with a converter, to produce valid ThreeJS code, or direct output in ThreeJS (or another 3D
+ * JavaScript library of your choice.
+ *
+ * This grammar is currently experimental. Is has been built by trial and error, based on an old
+ * VRML model that I used as a source for the ThreeJs VRML loader example. The ThreeJs example
+ * can be found here, but it currently uses the old line by line parsing method, which has very
+ * limited awareness of the node tree.
+ *
+ * When used with Pegjs (https://github.com/pegjs), it can be used to parse a node tree, so
+ * that full awareness of the node tree will exist. This will allow to do more with VRML in JavaScript,
+ * such as animations.
+ *
+ *
+ */
+{
+	var nodeDefinitions = [];
+	var routes = [];
+}
+
+vrml
+	= '#VRML V2.0 utf8' vrml:(nodeDefinition / node / comment / route)*
+	{
+	    // before returning the root vrml object, enricht it with routes and nodeDefinitions
+		vrml.nodeDefinitions = nodeDefinitions;
+		vrml.routes = routes;
+		return vrml;
+	}
+
+/* ----- Node ------ */
+nodeDefinition
+    = ws name:(def ws name:identifier ws { return name; }) n:node
+    {
+        n.name = name;
+        n.isDefinition = true;
+        // store node for later re-use
+        nodeDefinitions[name] = n;
+        //console.log('Registered as ' + name + ' in nodeDefinitions:');
+        //console.log(n);
+        return n;
+    }
+
+node
+	= ws t:identifier ws begin_node ws pp:( nodeDefinition / route / property / node / comment )* ws end_node ws
+	{
+		var n = {node: t};
+
+		// node properties are in pp, if pp is not an Inline node, if pp is an inline node, it should be read from the url
+		for (var i=0; i < pp.length; i++) {
+			var p = pp[i];
+
+			// is p a node?
+			if (undefined !== p.node) {
+				//console.log(p.node + ' node found');
+
+				// if the node does not already have children, create children here
+				if (undefined === n.children) {
+					n.children = [];
+				}
+
+				// @todo for an Inline node, we could use the parser (named 'parser') and fs here, to fetch the inline file and parse it
+				// on the other hand, it could be left up to the renderers what to do with the inline node.
+				/*
+				@see http://pegjs.org/documentation#grammar-syntax-and-semantics
+				The code inside the predicate can also access the parser object using the parser variable and options passed to the parser using the options variable.
+				*/
+				n.children.push(p);
+
+			} else if (undefined !== p.name) {
+				// p is a property
+				n[p.name] = p.value;
+
+				if (undefined !== p.comment) {
+					if (undefined === n.comments) { n.comments = {}; }
+					if (undefined === n.comments[p.name]) { n.comments[p.name] = []; }
+					n.comments[p.name].push(p.comment);
+				}
+			} else if (undefined !== p.src) {
+			    // p is a route
+			    // move it to global scope
+			    routes.push(p);
+			} else {
+				// p is a comment
+				if (undefined === n.nodeComments) {
+                    n.nodeComments = [];
+                }
+                n.nodeComments.push(p);
+			}
+		}
+
+		return n;
+	}
+
+property
+    = ws name:identifier ws value:value ws comment:comment?
+    {
+        var p = { name:name, value:value };
+
+        // you could change a color property here by returning r g b instead of x y z
+
+        if (null !== comment) {
+            p.comment = comment;
+        }
+        return p;
+    }
+
+identifier "identifier"
+	= o:[A-Za-z0-9_]+ { return o.join(''); }
+
+/* ----- Arrays (The VRML way) ----- */
+
+array "array"
+  = begin_array
+        it:(c:comment / r:route / v:(v:value ws value_separator? { return v; } ) )*
+    end_array
+    {
+        var a = [];
+        for (var i=0; i < it.length; i++) {
+            var value = it[i];
+
+            if (undefined !== value.src) {
+                // value is a route, add to global routes
+                routes.push(value);
+            } else if (undefined !== value.comment) {
+                // value is a comment
+                if (undefined === a.comments) {
+                    a.comments = [];
+                }
+
+                a.comments.push(value);
+            } else {
+                // this is what we are looking for: a value for in our array!
+                a.push(value);
+            }
+        }
+
+        return a;
+    }
+
+
+/* ----- Values ----- */
+
+value "value"
+  = false
+  / face
+  / null
+  / true
+  / nodeDefinition
+  / node
+  / rotation
+  / point
+  / vector
+  / use_statement
+  / array
+  / number
+  / float
+  / identifier
+  / url
+  / quoted_string
+
+
+false = "false" / "FALSE" { return false; }
+null  = "null" / "NULL"  { return null;  }
+true  = "true" / "TRUE"  { return true;  }
+
+
+/* ----- Numbers ----- */
+
+number "number"
+  = minus? int frac? exp? { return parseFloat(text()); }
+
+decimal_point = "."
+digit1_9      = [1-9]
+e             = [eE]
+exp           = e (minus / plus)? DIGIT+
+frac          = decimal_point DIGIT+
+int           = zero / (digit1_9 DIGIT*)
+minus         = "-"
+plus          = "+"
+zero          = "0"
+
+/* ----- VRML Grammar ----- */
+
+comment
+	=  ws "#" text:[^\n]* ws { return { comment: text.join('').trim() }; }
+
+route
+	= ws "ROUTE" ws src:route_part ws "TO"  ws target:route_part ws
+	{
+	    var route = { source: src, target: target };
+	    // put it in the global routes collection
+	    routes.push(route);
+	    return route;
+	}
+
+route_part
+	= name:identifier "." property:identifier
+	{ return { name: name, property: property }; }
+
+begin_array     = ws "[" ws
+begin_node    = ws "{" ws
+end_array       = ws "]" ws
+end_node      = ws "}" ws
+value_separator = ws "," ws
+name_separator  = ws
+
+ws "whitespace"
+	= ws:[ \t\n\r]*
+	{ return ws.join('');}
+
+space
+	= " "
+
+point
+	= p:vector "," ws comment? { return p; }
+
+vector
+	= ws x:number ws y:number ws z:number ws comment?
+	{ return {x:x, y:y, z:z}; }
+
+def
+	= "DEF"
+	{ return true; }
+
+use_statement
+	= ws use ws name:identifier
+	{
+	    var obj = nodeDefinitions[name];
+
+	    if (undefined === obj) {
+	        console.log(name + ' not found in nodeDefinitions');
+	        return obj; // undefined obj
+	    }
+
+	    if ('function' === typeof obj.clone) {
+	        return obj.clone();
+	    }
+
+	    return obj;
+	}
+
+use
+	= "USE"
+	{ return true; }
+
+face
+	= points:index+ "-1" ws
+	{ return points; }
+
+index
+	= ws i:int ws value_separator ws
+	{ if (i==0) { return i; } return i.join(''); }
+
+rotation
+	= ws x:number ws y:number ws z:number ws radians:number ws
+	{ return {x:x, y:y, z:z, radians:radians}; }
+
+url
+	= ws begin_array ws quote uri:uri quote ws end_array ws
+	{ return uri; }
+
+uri
+	= i:[^"]* dot:"." ext:("jpg" / "jpeg" / "gif" / "wrl")
+	{ return i + dot + ext + "BOOOO"; }
+
+quoted_string
+	= ws quote s:[^"]* quote ws
+	{ return '"' + s.join('') + '"'; }
+
+quote
+	= '"'
+
+/* This is a special case, because in VRML it is allowed to write a float as .66 for example, meaning 0.66 */
+float
+	= int ? frac
+	{ return parseFloat(text()); }
+
+/* ----- Core ABNF Rules ----- */
+
+/* See RFC 4234, Appendix B (http://tools.ietf.org/html/rfc4627). */
+DIGIT  = [0-9]
+
+HEXDIG = [0-9a-f]i
+

+ 36 - 997
examples/js/loaders/VRMLLoader.js

@@ -1,1021 +1,60 @@
 /**
 /**
  * @author mrdoob / http://mrdoob.com/
  * @author mrdoob / http://mrdoob.com/
+ * @author Bart McLeod / http://spaceweb.nl/
  */
  */
 
 
-THREE.VRMLLoader = function ( manager ) {
+/**
+ *
+ * @param manager
+ * @param parser VRMLparser based on PEG.js grammar
+ * @param VrmlParser.Renderer.ThreeJs renderer
+ * @constructor
+ */
+THREE.VRMLLoader = function (parser, manager) {
 
 
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+  this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+  this.parser = parser;
 
 
 };
 };
 
 
 THREE.VRMLLoader.prototype = {
 THREE.VRMLLoader.prototype = {
 
 
-	constructor: THREE.VRMLLoader,
-
-	// for IndexedFaceSet support
-	isRecordingPoints: false,
-	isRecordingFaces: false,
-	points: [],
-	indexes : [],
-
-	// for Background support
-	isRecordingAngles: false,
-	isRecordingColors: false,
-	angles: [],
-	colors: [],
-
-	recordingFieldname: null,
-
-	load: function ( url, onLoad, onProgress, onError ) {
-
-		var scope = this;
-
-		var loader = new THREE.XHRLoader( this.manager );
-		loader.load( url, function ( text ) {
-
-			onLoad( scope.parse( text ) );
-
-		}, onProgress, onError );
-
-	},
-
-	setCrossOrigin: function ( value ) {
-
-		this.crossOrigin = value;
-
-	},
-
-	parse: function ( data ) {
-
-		var texturePath = this.texturePath || '';
-
-		var textureLoader = new THREE.TextureLoader( this.manager );
-		textureLoader.setCrossOrigin( this.crossOrigin );
-
-		var parseV1 = function ( lines, scene ) {
-
-			console.warn( 'VRML V1.0 not supported yet' );
-
-		};
-
-		var parseV2 = function ( lines, scene ) {
-
-			var defines = {};
-			var float_pattern = /(\b|\-|\+)([\d\.e]+)/;
-			var float2_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g;
-			var float3_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g;
-
-			/**
-			* Interpolates colors a and b following their relative distance
-			* expressed by t.
-			*
-			* @param float a
-			* @param float b
-			* @param float t
-			* @returns {Color}
-			*/
-			var interpolateColors = function( a, b, t ) {
-
-				var deltaR = a.r - b.r;
-				var deltaG = a.g - b.g;
-				var deltaB = a.b - b.b;
-
-				var c = new THREE.Color();
-
-				c.r = a.r - t * deltaR;
-				c.g = a.g - t * deltaG;
-				c.b = a.b - t * deltaB;
-
-				return c;
-
-			};
-
-			/**
-			 * Vertically paints the faces interpolating between the
-			 * specified colors at the specified angels. This is used for the Background
-			 * node, but could be applied to other nodes with multiple faces as well.
-			 *
-			 * When used with the Background node, default is directionIsDown is true if
-			 * interpolating the skyColor down from the Zenith. When interpolationg up from
-			 * the Nadir i.e. interpolating the groundColor, the directionIsDown is false.
-			 *
-			 * The first angle is never specified, it is the Zenith (0 rad). Angles are specified
-			 * in radians. The geometry is thought a sphere, but could be anything. The color interpolation
-			 * is linear along the Y axis in any case.
-			 *
-			 * You must specify one more color than you have angles at the beginning of the colors array.
-			 * This is the color of the Zenith (the top of the shape).
-			 *
-			 * @param geometry
-			 * @param radius
-			 * @param angles
-			 * @param colors
-			 * @param boolean directionIsDown Whether to work bottom up or top down.
-			 */
-			var paintFaces = function ( geometry, radius, angles, colors, directionIsDown ) {
-
-				var f, n, p, vertexIndex, color;
-
-				var direction = directionIsDown ? 1 : - 1;
-
-				var faceIndices = [ 'a', 'b', 'c', 'd' ];
-
-				var coord = [ ], aColor, bColor, t = 1, A = {}, B = {}, applyColor = false, colorIndex;
-
-				for ( var k = 0; k < angles.length; k ++ ) {
-
-					var vec = { };
-
-					// push the vector at which the color changes
-					vec.y = direction * ( Math.cos( angles[ k ] ) * radius );
-
-					vec.x = direction * ( Math.sin( angles[ k ] ) * radius );
-
-					coord.push( vec );
-
-				}
-
-				// painting the colors on the faces
-				for ( var i = 0; i < geometry.faces.length ; i ++ ) {
-
-					f  = geometry.faces[ i ];
-
-					n = ( f instanceof THREE.Face3 ) ? 3 : 4;
-
-					for ( var j = 0; j < n; j ++ ) {
-
-						vertexIndex = f[ faceIndices[ j ] ];
-
-						p = geometry.vertices[ vertexIndex ];
-
-						for ( var index = 0; index < colors.length; index ++ ) {
-
-							// linear interpolation between aColor and bColor, calculate proportion
-							// A is previous point (angle)
-							if ( index === 0 ) {
-
-								A.x = 0;
-								A.y = directionIsDown ? radius : - 1 * radius;
-
-							} else {
-
-								A.x = coord[ index - 1 ].x;
-								A.y = coord[ index - 1 ].y;
-
-							}
-
-							// B is current point (angle)
-							B = coord[ index ];
-
-							if ( undefined !== B ) {
-
-								// p has to be between the points A and B which we interpolate
-								applyColor = directionIsDown ? p.y <= A.y && p.y > B.y : p.y >= A.y && p.y < B.y;
-
-								if ( applyColor ) {
-
-									bColor = colors[ index + 1 ];
-
-									aColor = colors[ index ];
-
-									// below is simple linear interpolation
-									t = Math.abs( p.y - A.y ) / ( A.y - B.y );
-
-									// to make it faster, you can only calculate this if the y coord changes, the color is the same for points with the same y
-									color = interpolateColors( aColor, bColor, t );
-
-									f.vertexColors[ j ] = color;
-
-								}
-
-							} else if ( undefined === f.vertexColors[ j ] ) {
-
-								colorIndex = directionIsDown ? colors.length - 1 : 0;
-								f.vertexColors[ j ] = colors[ colorIndex ];
-
-							}
-
-						}
-
-					}
-
-				}
-
-			};
-
-			var index = [];
-
-			var parseProperty = function ( node, line ) {
-
-				var parts = [], part, property = {}, fieldName;
-
-				/**
-				 * Expression for matching relevant information, such as a name or value, but not the separators
-				 * @type {RegExp}
-				 */
-				var regex = /[^\s,\[\]]+/g;
-
-				var point, angles, colors;
-
-				while ( null != ( part = regex.exec( line ) ) ) {
-
-					parts.push( part[ 0 ] );
-
-				}
-
-				fieldName = parts[ 0 ];
-
-
-				// trigger several recorders
-				switch ( fieldName ) {
-					case 'skyAngle':
-					case 'groundAngle':
-						this.recordingFieldname = fieldName;
-						this.isRecordingAngles = true;
-						this.angles = [];
-						break;
-					case 'skyColor':
-					case 'groundColor':
-						this.recordingFieldname = fieldName;
-						this.isRecordingColors = true;
-						this.colors = [];
-						break;
-					case 'point':
-						this.recordingFieldname = fieldName;
-						this.isRecordingPoints = true;
-						this.points = [];
-						break;
-					case 'coordIndex':
-					case 'texCoordIndex':
-						this.recordingFieldname = fieldName;
-						this.isRecordingFaces = true;
-						this.indexes = [];
-				}
-
-				if ( this.isRecordingFaces ) {
-
-					// the parts hold the indexes as strings
-					if ( parts.length > 0 ) {
-
-						for ( var ind = 0; ind < parts.length; ind ++ ) {
-
-							// the part should either be positive integer or -1
-							if ( ! /(-?\d+)/.test( parts[ ind ] ) ) {
-
-								continue;
-
-							}
-
-							// end of current face
-							if ( parts[ ind ] === "-1" ) {
-
-								if ( index.length > 0 ) {
-
-									this.indexes.push( index );
-
-								}
-
-								// start new one
-								index = [];
-
-							} else {
-
-								index.push( parseInt( parts[ ind ] ) );
-
-							}
-
-						}
-
-					}
-
-					// end
-					if ( /]/.exec( line ) ) {
-
-						if ( index.length > 0 ) {
-
-							this.indexes.push( index );
-
-						}
-
-						// start new one
-						index = [];
-
-						this.isRecordingFaces = false;
-						node[this.recordingFieldname] = this.indexes;
-
-					}
-
-				} else if ( this.isRecordingPoints ) {
-
-					if ( node.nodeType == 'Coordinate' )
-
-					while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
-
-						point = {
-							x: parseFloat( parts[ 1 ] ),
-							y: parseFloat( parts[ 2 ] ),
-							z: parseFloat( parts[ 3 ] )
-						};
-
-						this.points.push( point );
-
-					}
-
-					if ( node.nodeType == 'TextureCoordinate' )
-
-					while ( null !== ( parts = float2_pattern.exec( line ) ) ) {
-
-						point = {
-							x: parseFloat( parts[ 1 ] ),
-							y: parseFloat( parts[ 2 ] )
-						};
-
-						this.points.push( point );
-
-					}
-
-					// end
-					if ( /]/.exec( line ) ) {
-
-						this.isRecordingPoints = false;
-						node.points = this.points;
-
-					}
-
-				} else if ( this.isRecordingAngles ) {
-
-					// the parts hold the angles as strings
-					if ( parts.length > 0 ) {
-
-						for ( var ind = 0; ind < parts.length; ind ++ ) {
-
-							// the part should be a float
-							if ( ! float_pattern.test( parts[ ind ] ) ) {
-
-								continue;
-
-							}
-
-							this.angles.push( parseFloat( parts[ ind ] ) );
-
-						}
-
-					}
-
-					// end
-					if ( /]/.exec( line ) ) {
-
-						this.isRecordingAngles = false;
-						node[ this.recordingFieldname ] = this.angles;
-
-					}
-
-				} else if ( this.isRecordingColors ) {
-
-					while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
-
-						color = {
-							r: parseFloat( parts[ 1 ] ),
-							g: parseFloat( parts[ 2 ] ),
-							b: parseFloat( parts[ 3 ] )
-						};
-
-						this.colors.push( color );
-
-					}
-
-					// end
-					if ( /]/.exec( line ) ) {
-
-						this.isRecordingColors = false;
-						node[ this.recordingFieldname ] = this.colors;
-
-					}
-
-				} else if ( parts[ parts.length - 1 ] !== 'NULL' && fieldName !== 'children' ) {
-
-					switch ( fieldName ) {
-
-						case 'diffuseColor':
-						case 'emissiveColor':
-						case 'specularColor':
-						case 'color':
-
-							if ( parts.length != 4 ) {
-
-								console.warn( 'Invalid color format detected for ' + fieldName );
-								break;
-
-							}
-
-							property = {
-								r: parseFloat( parts[ 1 ] ),
-								g: parseFloat( parts[ 2 ] ),
-								b: parseFloat( parts[ 3 ] )
-							};
-
-							break;
-
-						case 'translation':
-						case 'scale':
-						case 'size':
-							if ( parts.length != 4 ) {
-
-								console.warn( 'Invalid vector format detected for ' + fieldName );
-								break;
-
-							}
-
-							property = {
-								x: parseFloat( parts[ 1 ] ),
-								y: parseFloat( parts[ 2 ] ),
-								z: parseFloat( parts[ 3 ] )
-							};
-
-							break;
-
-						case 'radius':
-						case 'topRadius':
-						case 'bottomRadius':
-						case 'height':
-						case 'transparency':
-						case 'shininess':
-						case 'ambientIntensity':
-							if ( parts.length != 2 ) {
-
-								console.warn( 'Invalid single float value specification detected for ' + fieldName );
-								break;
-
-							}
-
-							property = parseFloat( parts[ 1 ] );
-
-							break;
-
-						case 'rotation':
-							if ( parts.length != 5 ) {
-
-								console.warn( 'Invalid quaternion format detected for ' + fieldName );
-								break;
-
-							}
-
-							property = {
-								x: parseFloat( parts[ 1 ] ),
-								y: parseFloat( parts[ 2 ] ),
-								z: parseFloat( parts[ 3 ] ),
-								w: parseFloat( parts[ 4 ] )
-							};
-
-							break;
-
-						case 'ccw':
-						case 'solid':
-						case 'colorPerVertex':
-						case 'convex':
-							if ( parts.length != 2 ) {
-
-								console.warn( 'Invalid format detected for ' + fieldName );
-								break;
-
-							}
-
-							property = parts[ 1 ] === 'TRUE' ? true : false;
-
-							break;
-					}
-
-					node[ fieldName ] = property;
-
-				}
-
-				return property;
-
-			};
-
-			var getTree = function ( lines ) {
-
-				var tree = { 'string': 'Scene', children: [] };
-				var current = tree;
-				var matches;
-				var specification;
-
-				for ( var i = 0; i < lines.length; i ++ ) {
-
-					var comment = '';
-
-					var line = lines[ i ];
-
-					// omit whitespace only lines
-					if ( null !== ( result = /^\s+?$/g.exec( line ) ) ) {
-
-						continue;
-
-					}
-
-					line = line.trim();
-
-					// skip empty lines
-					if ( line === '' ) {
-
-						continue;
-
-					}
-
-					if ( /#/.exec( line ) ) {
-
-						var parts = line.split( '#' );
-
-						// discard everything after the #, it is a comment
-						line = parts[ 0 ];
-
-						// well, let's also keep the comment
-						comment = parts[ 1 ];
-
-					}
-
-					if ( matches = /([^\s]*){1}(?:\s+)?{/.exec( line ) ) {
-
-						// first subpattern should match the Node name
-
-						var block = { 'nodeType' : matches[ 1 ], 'string': line, 'parent': current, 'children': [], 'comment' : comment };
-						current.children.push( block );
-						current = block;
-
-						if ( /}/.exec( line ) ) {
-
-							// example: geometry Box { size 1 1 1 } # all on the same line
-							specification = /{(.*)}/.exec( line )[ 1 ];
-
-							// todo: remove once new parsing is complete?
-							block.children.push( specification );
-
-							parseProperty( current, specification );
-
-							current = current.parent;
-
-						}
-
-					} else if ( /}/.exec( line ) ) {
-
-						current = current.parent;
-
-					} else if ( line !== '' ) {
-
-						parseProperty( current, line );
-						// todo: remove once new parsing is complete? we still do not parse geometry and appearance the new way
-						current.children.push( line );
-
-					}
-
-				}
-
-				return tree;
-
-			};
-
-			var parseNode = function ( data, parent ) {
-
-				// console.log( data );
-
-				if ( typeof data === 'string' ) {
-
-					if ( /USE/.exec( data ) ) {
-
-						var defineKey = /USE\s+?([^\s]+)/.exec( data )[ 1 ];
-
-						if ( undefined == defines[ defineKey ] ) {
-
-							console.warn( defineKey + ' is not defined.' );
-
-						} else {
-
-							if ( /appearance/.exec( data ) && defineKey ) {
-
-								parent.material = defines[ defineKey ].clone();
-
-							} else if ( /geometry/.exec( data ) && defineKey ) {
-
-								parent.geometry = defines[ defineKey ].clone();
-
-								// the solid property is not cloned with clone(), is only needed for VRML loading, so we need to transfer it
-								if ( undefined !== defines[ defineKey ].solid && defines[ defineKey ].solid === false ) {
-
-									parent.geometry.solid = false;
-									parent.material.side = THREE.DoubleSide;
-
-								}
-
-							} else if ( defineKey ) {
-
-								var object = defines[ defineKey ].clone();
-								parent.add( object );
-
-							}
-
-						}
-
-					}
-
-					return;
-
-				}
-
-				var object = parent;
-
-				if ( 'Transform' === data.nodeType || 'Group' === data.nodeType ) {
-
-					object = new THREE.Object3D();
-
-					if ( /DEF/.exec( data.string ) ) {
-
-						object.name = /DEF\s+([^\s]+)/.exec( data.string )[ 1 ];
-						defines[ object.name ] = object;
-
-					}
-
-					if ( undefined !== data[ 'translation' ] ) {
-
-						var t = data.translation;
-
-						object.position.set( t.x, t.y, t.z );
-
-					}
-
-					if ( undefined !== data.rotation ) {
-
-						var r = data.rotation;
-
-						object.quaternion.setFromAxisAngle( new THREE.Vector3( r.x, r.y, r.z ), r.w );
-
-					}
-
-					if ( undefined !== data.scale ) {
-
-						var s = data.scale;
-
-						object.scale.set( s.x, s.y, s.z );
-
-					}
-
-					parent.add( object );
-
-				} else if ( 'Shape' === data.nodeType ) {
-
-					object = new THREE.Mesh();
-
-					if ( /DEF/.exec( data.string ) ) {
-
-						object.name = /DEF\s+([^\s]+)/.exec( data.string )[ 1 ];
-
-						defines[ object.name ] = object;
-
-					}
-
-					parent.add( object );
-
-				} else if ( 'Background' === data.nodeType ) {
-
-					var segments = 20;
-
-					// sky (full sphere):
-
-					var radius = 2e4;
-
-					var skyGeometry = new THREE.SphereGeometry( radius, segments, segments );
-					var skyMaterial = new THREE.MeshBasicMaterial( { fog: false, side: THREE.BackSide } );
-
-					if ( data.skyColor.length > 1 ) {
-
-						paintFaces( skyGeometry, radius, data.skyAngle, data.skyColor, true );
-
-						skyMaterial.vertexColors = THREE.VertexColors
-
-					} else {
-
-						var color = data.skyColor[ 0 ];
-						skyMaterial.color.setRGB( color.r, color.b, color.g );
-
-					}
-
-					scene.add( new THREE.Mesh( skyGeometry, skyMaterial ) );
-
-					// ground (half sphere):
-
-					if ( data.groundColor !== undefined ) {
-
-						radius = 1.2e4;
-
-						var groundGeometry = new THREE.SphereGeometry( radius, segments, segments, 0, 2 * Math.PI, 0.5 * Math.PI, 1.5 * Math.PI );
-						var groundMaterial = new THREE.MeshBasicMaterial( { fog: false, side: THREE.BackSide, vertexColors: THREE.VertexColors } );
-
-						paintFaces( groundGeometry, radius, data.groundAngle, data.groundColor, false );
-
-						scene.add( new THREE.Mesh( groundGeometry, groundMaterial ) );
-
-					}
-
-				} else if ( /geometry/.exec( data.string ) ) {
-
-					if ( 'Box' === data.nodeType ) {
-
-						var s = data.size;
-
-						parent.geometry = new THREE.BoxGeometry( s.x, s.y, s.z );
-
-					} else if ( 'Cylinder' === data.nodeType ) {
-
-						parent.geometry = new THREE.CylinderGeometry( data.radius, data.radius, data.height );
-
-					} else if ( 'Cone' === data.nodeType ) {
-
-						parent.geometry = new THREE.CylinderGeometry( data.topRadius, data.bottomRadius, data.height );
-
-					} else if ( 'Sphere' === data.nodeType ) {
-
-						parent.geometry = new THREE.SphereGeometry( data.radius );
-
-					} else if ( 'IndexedFaceSet' === data.nodeType ) {
-
-						var geometry = new THREE.Geometry();
-
-						var indexes, uvIndexes, uvs;
-
-						for ( var i = 0, j = data.children.length; i < j; i ++ ) {
-
-							var child = data.children[ i ];
-
-							var vec;
-
-							if ( 'TextureCoordinate' === child.nodeType ) {
-
-								uvs = child.points;
-
-							}
-
-
-							if ( 'Coordinate' === child.nodeType ) {
-
-								if ( child.points ) {
-
-									for ( var k = 0, l = child.points.length; k < l; k ++ ) {
-
-										var point = child.points[ k ];
-
-										vec = new THREE.Vector3( point.x, point.y, point.z );
-
-										geometry.vertices.push( vec );
-
-									}
-
-								}
-
-								if ( child.string.indexOf ( 'DEF' ) > -1 ) {
-
-									var name = /DEF\s+([^\s]+)/.exec( child.string )[ 1 ];
-
-									defines[ name ] = geometry.vertices;
-
-								}
-
-								if ( child.string.indexOf ( 'USE' ) > -1 ) {
-
-									var defineKey = /USE\s+([^\s]+)/.exec( child.string )[ 1 ];
-
-									geometry.vertices = defines[ defineKey ];
-								}
-
-							}
-
-						}
-
-						var skip = 0;
-
-						// some shapes only have vertices for use in other shapes
-						if ( data.coordIndex ) {
-
-							// read this: http://math.hws.edu/eck/cs424/notes2013/16_Threejs_Advanced.html
-							for ( var i = 0, j = data.coordIndex.length; i < j; i ++ ) {
-
-								indexes = data.coordIndex[ i ]; if ( data.texCoordIndex ) uvIndexes = data.texCoordIndex[ i ];
-
-								// vrml support multipoint indexed face sets (more then 3 vertices). You must calculate the composing triangles here
-								skip = 0;
-
-								// Face3 only works with triangles, but IndexedFaceSet allows shapes with more then three vertices, build them of triangles
-								while ( indexes.length >= 3 && skip < ( indexes.length - 2 ) ) {
-
-									var face = new THREE.Face3(
-										indexes[ 0 ],
-										indexes[ skip + (data.ccw ? 1 : 2) ],
-										indexes[ skip + (data.ccw ? 2 : 1) ],
-										null // normal, will be added later
-										// todo: pass in the color, if a color index is present
-									);
-
-									if ( uvs && uvIndexes ) {
-										geometry.faceVertexUvs [0].push( [
-											new THREE.Vector2 (
-												uvs[ uvIndexes[ 0 ] ].x ,
-												uvs[ uvIndexes[ 0 ] ].y
-											) ,
-											new THREE.Vector2 (
-												uvs[ uvIndexes[ skip + (data.ccw ? 1 : 2) ] ].x ,
-												uvs[ uvIndexes[ skip + (data.ccw ? 1 : 2) ] ].y
-											) ,
-											new THREE.Vector2 (
-												uvs[ uvIndexes[ skip + (data.ccw ? 2 : 1) ] ].x ,
-												uvs[ uvIndexes[ skip + (data.ccw ? 2 : 1) ] ].y
-											)
-										] );
-									}
-
-									skip ++;
-
-									geometry.faces.push( face );
-
-								}
-
-
-							}
-
-						} else {
-
-							// do not add dummy mesh to the scene
-							parent.parent.remove( parent );
-
-						}
-
-						if ( false === data.solid ) {
-
-							parent.material.side = THREE.DoubleSide;
-
-						}
-
-						// we need to store it on the geometry for use with defines
-						geometry.solid = data.solid;
-
-						geometry.computeFaceNormals();
-						//geometry.computeVertexNormals(); // does not show
-						geometry.computeBoundingSphere();
-
-						// see if it's a define
-						if ( /DEF/.exec( data.string ) ) {
-
-							geometry.name = /DEF ([^\s]+)/.exec( data.string )[ 1 ];
-							defines[ geometry.name ] = geometry;
-
-						}
-
-						parent.geometry = geometry;
-
-					}
-
-					return;
-
-				} else if ( /appearance/.exec( data.string ) ) {
-
-					for ( var i = 0; i < data.children.length; i ++ ) {
-
-						var child = data.children[ i ];
-
-						if ( 'Material' === child.nodeType ) {
-
-							var material = new THREE.MeshPhongMaterial();
-
-							if ( undefined !== child.diffuseColor ) {
-
-								var d = child.diffuseColor;
-
-								material.color.setRGB( d.r, d.g, d.b );
-
-							}
-
-							if ( undefined !== child.emissiveColor ) {
-
-								var e = child.emissiveColor;
-
-								material.emissive.setRGB( e.r, e.g, e.b );
-
-							}
-
-							if ( undefined !== child.specularColor ) {
-
-								var s = child.specularColor;
-
-								material.specular.setRGB( s.r, s.g, s.b );
-
-							}
-
-							if ( undefined !== child.transparency ) {
-
-								var t = child.transparency;
-
-								// transparency is opposite of opacity
-								material.opacity = Math.abs( 1 - t );
-
-								material.transparent = true;
-
-							}
-
-							if ( /DEF/.exec( data.string ) ) {
-
-								material.name = /DEF ([^\s]+)/.exec( data.string )[ 1 ];
-
-								defines[ material.name ] = material;
-
-							}
-
-							parent.material = material;
-
-						}
-
-						if ( 'ImageTexture' === child.nodeType ) {
-
-							var textureName = /"([^"]+)"/.exec(child.children[ 0 ]);
-
-							if (textureName) {
-
-								parent.material.name = textureName[ 1 ];
-
-								parent.material.map = textureLoader.load( texturePath + textureName[ 1 ] );
-
-							}
-
-						}
-
-					}
-
-					return;
-
-				}
-
-				for ( var i = 0, l = data.children.length; i < l; i ++ ) {
-
-					var child = data.children[ i ];
-
-					parseNode( data.children[ i ], object );
-
-				}
-
-			};
-
-			parseNode( getTree( lines ), scene );
-
-		};
-
-		var scene = new THREE.Scene();
-
-		var lines = data.split( '\n' );
-
-		// some lines do not have breaks
-		for (var i = lines.length -1; i > -1; i--) {
+  constructor: THREE.VRMLLoader,
 
 
-			// split lines with {..{ or {..[ - some have both
-			if (/{.*[{\[]/.test (lines[i])) {
-				var parts = lines[i].split ('{').join ('{\n').split ('\n');
-				parts.unshift(1);
-				parts.unshift(i);
-				lines.splice.apply(lines, parts);
-			} else
+  parse: function (data) {
+    try {
+      var tree = this.parser.parse(data);
+      console.log(tree);
+      return tree;
+    } catch ( e ) {
+      console.log('Exception with message ' + e.message);
 
 
-			// split lines with ]..}
-			if (/\].*}/.test (lines[i])) {
-				var parts = lines[i].split (']').join (']\n').split ('\n');
-				parts.unshift(1);
-				parts.unshift(i);
-				lines.splice.apply(lines, parts);
-			}
+      if ( undefined !== e.location ) {
+        console.log('Exception at location start: offset: ' + e.location.start.offset + ' line: ' + e.location.start.line + ' column: ' + e.location.start.column);
+        console.log('Exception at location end: offset: ' + e.location.end.offset + ' line: ' + e.location.end.line + ' column: ' + e.location.end.column);
+      }
 
 
-			// split lines with }..}
-			if (/}.*}/.test (lines[i])) {
-				var parts = lines[i].split ('}').join ('}\n').split ('\n');
-				parts.unshift(1);
-				parts.unshift(i);
-				lines.splice.apply(lines, parts);
-			}
+      return;
+    }
+  },
 
 
-			// force the parser to create Coordinate node for empty coords
-			// coord USE something -> coord USE something Coordinate {}
-			if((lines[i].indexOf ('coord') > -1) && (lines[i].indexOf ('[') < 0) && (lines[i].indexOf ('{') < 0)) {
-				lines[i] += ' Coordinate {}';
-			}
-		}
+  load: function (url, onLoad, onProgress, onError) {
 
 
-		var header = lines.shift();
+    var scope = this;
 
 
-		if ( /V1.0/.exec( header ) ) {
+    var loader = new THREE.XHRLoader(this.manager);
+    loader.load(url, function (text) {
 
 
-			parseV1( lines, scene );
+      onLoad(scope.parse(text));
 
 
-		} else if ( /V2.0/.exec( header ) ) {
+    }, onProgress, onError);
 
 
-			parseV2( lines, scene );
+  },
 
 
-		}
+  setCrossOrigin: function (value) {
 
 
-		return scene;
+    this.crossOrigin = value;
 
 
-	}
+  },
 
 
 };
 };

File diff suppressed because it is too large
+ 176 - 177
examples/models/vrml/house.wrl


+ 6 - 5
examples/webgl_loader_vrml.html

@@ -37,6 +37,8 @@
 		<script src="js/controls/OrbitControls.js"></script>
 		<script src="js/controls/OrbitControls.js"></script>
 
 
 		<script src="js/loaders/VRMLLoader.js"></script>
 		<script src="js/loaders/VRMLLoader.js"></script>
+		<script src="js/libs/VrmlParser/vrml.js"></script>
+		<script src="js/libs/VrmlParser/Renderer/ThreeJsRenderer.js"></script>
 
 
 		<script src="js/Detector.js"></script>
 		<script src="js/Detector.js"></script>
 		<script src="js/libs/stats.min.js"></script>
 		<script src="js/libs/stats.min.js"></script>
@@ -78,11 +80,10 @@
 				camera.add( dirLight );
 				camera.add( dirLight );
 				camera.add( dirLight.target );
 				camera.add( dirLight.target );
 
 
-				var loader = new THREE.VRMLLoader();
-				loader.load( 'models/vrml/house.wrl', function ( object ) {
-
-					scene.add( object );
-
+				var loader = new THREE.VRMLLoader(vrmlParser);
+				loader.load( 'models/vrml/house.wrl', function ( tree ) {
+					var vrmlParserThreeJsRenderer = new VrmlParser.Renderer.ThreeJsRenderer();
+					vrmlParserThreeJsRenderer.render(tree, scene);
 				} );
 				} );
 
 
 				// renderer
 				// renderer

Some files were not shown because too many files changed in this diff