Browse Source

Merge branch 'dev' into SpecularGlossiness

Takahiro 8 years ago
parent
commit
c451782dc0
100 changed files with 8916 additions and 8300 deletions
  1. 1 1
      .gitignore
  2. 5 1
      build/three.js
  3. 355 354
      build/three.min.js
  4. 5 1
      build/three.module.js
  5. 354 352
      docs/api/animation/AnimationAction.html
  6. 133 133
      docs/api/animation/AnimationClip.html
  7. 97 97
      docs/api/animation/AnimationMixer.html
  8. 59 59
      docs/api/animation/AnimationObjectGroup.html
  9. 54 54
      docs/api/animation/AnimationUtils.html
  10. 234 234
      docs/api/animation/KeyframeTrack.html
  11. 88 88
      docs/api/animation/PropertyBinding.html
  12. 68 68
      docs/api/animation/PropertyMixer.html
  13. 75 75
      docs/api/animation/tracks/BooleanKeyframeTrack.html
  14. 59 59
      docs/api/animation/tracks/ColorKeyframeTrack.html
  15. 59 59
      docs/api/animation/tracks/NumberKeyframeTrack.html
  16. 53 53
      docs/api/animation/tracks/QuaternionKeyframeTrack.html
  17. 58 58
      docs/api/animation/tracks/StringKeyframeTrack.html
  18. 44 44
      docs/api/animation/tracks/VectorKeyframeTrack.html
  19. 7 1
      docs/api/core/Object3D.html
  20. 21 20
      docs/api/extras/core/Curve.html
  21. 3 1
      docs/api/geometries/TubeBufferGeometry.html
  22. 3 1
      docs/api/geometries/TubeGeometry.html
  23. 231 231
      docs/api/loaders/ObjectLoader.html
  24. 6 0
      docs/api/materials/Material.html
  25. 37 37
      docs/api/math/Color.html
  26. 12 0
      docs/api/math/Matrix3.html
  27. 7 5
      docs/api/math/Matrix4.html
  28. 205 0
      docs/index.css
  29. 326 322
      docs/index.html
  30. 373 391
      docs/list.js
  31. 119 119
      docs/manual/introduction/Animation-system.html
  32. 1 1
      docs/manual/introduction/Code-style-guide.html
  33. 8 4
      docs/page.css
  34. 29 3
      docs/page.js
  35. 3 1
      docs/scenes/js/geometry.js
  36. 1 2
      editor/js/Editor.js
  37. 3 22
      editor/js/Script.js
  38. 0 85
      editor/js/Sidebar.Animation.js
  39. 2 1
      editor/js/Sidebar.Material.js
  40. 21 1
      editor/js/Sidebar.Scene.js
  41. 1 1
      editor/js/Viewport.js
  42. 1 13
      editor/js/commands/SetScriptValueCommand.js
  43. 5 2
      examples/files.js
  44. 37 3
      examples/js/CurveExtras.js
  45. 0 73
      examples/js/MorphAnimation.js
  46. 43 21
      examples/js/ParametricGeometries.js
  47. 59 59
      examples/js/RollerCoaster.js
  48. 28 1
      examples/js/WaterShader.js
  49. 4 2
      examples/js/controls/DragControls.js
  50. 0 24
      examples/js/controls/VRControls.js
  51. 2 1
      examples/js/curves/NURBSCurve.js
  52. 5 0
      examples/js/exporters/OBJExporter.js
  53. 5 2
      examples/js/loaders/ColladaLoader.js
  54. 51 40
      examples/js/loaders/DRACOLoader.js
  55. 1114 1965
      examples/js/loaders/FBXLoader2.js
  56. 10 1
      examples/js/loaders/GLTF2Loader.js
  57. 9 0
      examples/js/loaders/GLTFLoader.js
  58. 1 2
      examples/js/loaders/OBJLoader.js
  59. 1002 0
      examples/js/loaders/OBJLoader2.js
  60. 135 103
      examples/js/loaders/VTKLoader.js
  61. 1170 0
      examples/js/loaders/WWOBJLoader2.js
  62. 0 1132
      examples/js/loaders/deprecated/SceneLoader.js
  63. 0 0
      examples/js/loaders/draco_decoder.js
  64. 18 16
      examples/js/vr/WebVR.js
  65. BIN
      examples/models/gltf/BoomBox/glTF-pbrSpecularGlossiness/BoomBox_metallicRoughness.png
  66. BIN
      examples/models/gltf/BoomBox/glTF/BoomBox_metallicRoughness.png
  67. 3 2
      examples/models/skinned/marine/marine_anims_core.json
  68. 0 769
      examples/scenes/test_scene.js
  69. 100 114
      examples/webgl_geometry_extrude_splines.html
  70. 74 51
      examples/webgl_gpu_particle_system.html
  71. 2 2
      examples/webgl_lights_spotlight.html
  72. 47 93
      examples/webgl_loader_collada.html
  73. 78 49
      examples/webgl_loader_collada_skinning.html
  74. 45 1
      examples/webgl_loader_gltf2.html
  75. 37 84
      examples/webgl_loader_json_blender.html
  76. 343 0
      examples/webgl_loader_obj2.html
  77. 491 0
      examples/webgl_loader_obj2_ww.html
  78. 409 0
      examples/webgl_loader_obj2_ww_parallels.html
  79. 0 374
      examples/webgl_loader_scene.html
  80. 1 1
      examples/webgl_marchingcubes.html
  81. 12 27
      examples/webgl_materials.html
  82. 69 32
      examples/webgl_materials_variations_physical.html
  83. 70 36
      examples/webgl_materials_variations_standard.html
  84. 45 62
      examples/webgl_morphtargets.html
  85. 40 13
      examples/webgl_shaders_ocean.html
  86. 5 12
      examples/webvr_cubes.html
  87. 5 2
      examples/webvr_daydream.html
  88. 5 3
      examples/webvr_panorama.html
  89. 58 43
      examples/webvr_rollercoaster.html
  90. 6 4
      examples/webvr_sandbox.html
  91. 5 3
      examples/webvr_video.html
  92. 3 3
      examples/webvr_vive.html
  93. 3 3
      examples/webvr_vive_camerarig.html
  94. 3 3
      examples/webvr_vive_dragging.html
  95. 3 3
      examples/webvr_vive_paint.html
  96. 3 3
      examples/webvr_vive_sculpt.html
  97. 17 0
      src/Three.Legacy.js
  98. 13 3
      src/animation/AnimationAction.js
  99. 1 5
      src/animation/AnimationMixer.js
  100. 1 1
      src/core/BufferGeometry.js

+ 1 - 1
.gitignore

@@ -5,4 +5,4 @@ node_modules
 .idea/
 .vscode/
 npm-debug.log
-
+.jshintrc

File diff suppressed because it is too large
+ 5 - 1
build/three.js


File diff suppressed because it is too large
+ 355 - 354
build/three.min.js


File diff suppressed because it is too large
+ 5 - 1
build/three.module.js


+ 354 - 352
docs/api/animation/AnimationAction.html

@@ -8,356 +8,358 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-    <h1>[name]</h1>
-
-    <div class="desc">
-        AnimationActions schedule the performance of the animations which are stored in
-        [page:AnimationClip AnimationClips].<br /><br />
-        
-        Note: Most of AnimationAction's methods can be chained.<br /><br />
-        
-        For an overview of the different elements of the three.js animation system see the
-        "Animation System" article in the "Next Steps" section of the manual.
-    </div>
-    
-
-    <h2>Constructor</h2>
-    
-
-    <h3>[name]( [page:AnimationMixer mixer], [page:AnimationClip clip], [page:Object3D localRoot] )</h3>
-    <div>       
-        [page:AnimationMixer mixer] - the *AnimationMixer* that is controlled by this action.<br />
-        [page:AnimationClip clip] - the *AnimationClip* that holds the animation data for this action.<br />
-        [page:Object3D localRoot] - the root object on which this action is performed.<br /><br />
-        
-        Note: Instead of calling this constructor directly you should instantiate an AnimationAction with 
-        [page:AnimationMixer.clipAction] since this method provides caching for better performance.
-    </div>   
-    
-   
-    <h2>Properties</h2>
-       
-        
-    <h3>[property:Boolean clampWhenFinished]</h3>
-    <div>
-        If *clampWhenFinished* is set to true the animation will automatically be [page:.paused paused] 
-        on its last frame.<br /><br />
-         
-        If *clampWhenFinished* is set to false, [page:.enabled enabled] will automatically be switched 
-        to false when the last loop of the action has finished, so that this action has no further 
-        impact.<br /><br />
-        
-        Default is false.<br /><br />
-        
-        Note: *clampWhenFinished* has no impact if the action is interrupted (it has only an effect if 
-        its last loop has really finished).
-    </div>    
-
-    <h3>[property:Boolean enabled]</h3>
-    <div>
-        Setting *enabled* to *false* disables this action, so that it has no impact. Default is *true*.<br /><br />
-        
-        When the action is re-enabled, the animation continues from its current [page:.time time] 
-        (setting *enabled* to *false* doesn't reset the action).<br /><br />
-
-        Note: Setting *enabled* to *true* doesn’t automatically restart the animation. Setting *enabled* 
-        to *true* will only restart the animation immediately if the following condition is fulfilled: 
-        [page:.paused paused] is *false*, this action has not been deactivated in the meantime (by 
-        executing a [page:.stop stop] or [page:.reset reset] command), and neither [page:.weight weight] 
-        nor [page:.timeScale timeScale] is 0.
-    </div>
-    
-    <h3>[property:Number loop]</h3>
-    <div>
-        The looping mode (can be changed with [page:.setLoop setLoop]). Default is 
-        [page:Animation THREE.LoopRepeat] (with an infinite number of [page:.repetitions repetitions])<br /><br />
-        
-        Must be one of these constants:<br /><br /> 
-        [page:Animation THREE.LoopOnce] - playing the clip once,<br /> 
-        [page:Animation THREE.LoopRepeat] - playing the clip with the choosen number of *repetitions*, 
-        each time jumping from the end of the clip directly to its beginning,<br /> 
-        [page:Animation THREE.LoopPingPong] - playing the clip with the choosen number of *repetitions*, 
-        alternately playing forward and backward.
-    </div>
-    
-    <h3>[property:Boolean paused]</h3>
-    <div>
-        Setting *paused* to *true* pauses the execution of the action by setting the effective time scale
-        to 0. Default is *false*.<br /><br />
-    </div>    
-    
-    <h3>[property:Number repetitions]</h3>
-    <div>
-        The number of repetitions of the performed [page:AnimationClip] over the course of this action.
-        Can be set via [page:.setLoop setLoop]. Default is *Infinity*.<br /><br />
-        Setting this number has no effect, if the [page:.loop loop mode] is set to 
-        [page:Animation THREE.LoopOnce].<br /><br />
-
-        Note: The first run is not taken into account. For example: if *repetition* is set to 2, the 
-        total number of executed rounds will be 3.
-    </div>
-
-    <h3>[property:Number time]</h3>
-    <div>
-        The local time of this action (in seconds, starting with 0).<br /><br />
-        
-        The value gets clamped or wrapped to 0...clip.duration (according to the loop state). It can be 
-        scaled relativly to the global mixer time by changing [page:.timeScale timeScale] (using 
-        [page:.setEffectiveTimeScale setEffectiveTimeScale] or [page:.setDuration setDuration]).<br />
-    </div>
-
-    <h3>[property:Number timeScale]</h3>
-    <div>  
-        Scaling factor for the [page:.time time]. A value of 0 causes the animation to pause. Negative 
-        values cause the animation to play backwards. Default is 1.<br /><br />
-        Properties/methods concerning *timeScale* (respectively *time*) are:
-        [page:.getEffectiveTimeScale getEffectiveTimeScale], 
-        [page:.halt halt], 
-        [page:.paused paused], 
-        [page:.setDuration setDuration], 
-        [page:.setEffectiveTimeScale setEffectiveTimeScale], 
-        [page:.stopWarping stopWarping],
-        [page:.syncWith syncWith], 
-        [page:.warp warp]. 
-    </div>    
-
-    <h3>[property:Number weight]</h3>
-    <div>
-        The degree of influence of this action (in the interval [0, 1]). Values between 0 (no impact) 
-        and 1 (full impact) can be used to blend between several actions. Default is 1. <br /><br />
-        Properties/methods concerning  *weight* are:
-        [page:.crossFadeFrom crossFadeFrom], 
-        [page:.crossFadeTo crossFadeTo], 
-        [page:.enabled enabled], 
-        [page:.fadeIn fadeIn], 
-        [page:.fadeOut fadeOut], 
-        [page:.getEffectiveWeight getEffectiveWeight], 
-        [page:.setEffectiveWeight setEffectiveWeight], 
-        [page:.stopFading stopFading].
-    </div>
-
-    <h3>[property:Boolean zeroSlopeAtEnd]</h3>
-    <div>
-        Enables smooth interpolation without separate clips for start, loop and end. Default is *true*.
-    </div>
-    
-    <h3>[property:Boolean zeroSlopeAtStart]</h3>
-    <div>
-        Enables smooth interpolation without separate clips for start, loop and end. Default is *true*.
-    </div>
-    
-
-    <h2>Methods</h2> 
-       
-
-    <h3>[method:AnimationAction crossFadeFrom]( [page:AnimationAction fadeOutAction], [page:Number durationInSeconds], [page:Boolean warpBoolean] )</h3>
-    <div>
-        Causes this action to [page:.fadeIn fade in], fading out another action simultaneously, within 
-        the passed time interval. This method can be chained.<br /><br />
-        
-        If warpBoolean is true, additional [page:.warp warping] (gradually changes of the time scales) 
-        will be applied.<br /><br />
-        
-        Note: Like with *fadeIn*/*fadeOut*, the fading starts/ends with a weight of 1.
-        
-    </div>
-
-    <h3>[method:AnimationAction crossFadeTo]( [page:AnimationAction fadeInAction], [page:Number durationInSeconds], [page:Boolean warpBoolean] )</h3>
-    <div>
-        Causes this action to [page:.fadeOut fade out], fading in another action simultaneously, within 
-        the passed time interval. This method can be chained.<br /><br />
-        If warpBoolean is true, additional [page:.warp warping] (gradually changes of the time scales) 
-        will be applied.<br /><br />
-        
-        Note: Like with *fadeIn*/*fadeOut*, the fading starts/ends with a weight of 1.
-    </div>    
-
-    <h3>[method:AnimationAction fadeIn]( [page:Number durationInSeconds] )</h3>
-    <div>
-        Increases the [page:.weight weight] of this action gradually from 0 to 1, within the passed time 
-        interval. This method can be chained.
-    </div>
-
-    <h3>[method:AnimationAction fadeOut]( [page:Number durationInSeconds] )</h3>
-    <div>
-        Decreases the [page:.weight weight] of this action gradually from 1 to 0, within the passed time 
-        interval. This method can be chained.
-    </div>
-    
-    <h3>[method:Number getEffectiveTimeScale]()</h3>
-    <div>
-        Returns the effective time scale (considering the current states of warping and 
-        [page:.paused paused]).
-    </div>
-
-    <h3>[method:number getEffectiveWeight]()</h3>
-    <div>
-        Returns the effective weight (considering the current states of fading and 
-        [page:.enabled enabled]).
-    </div>
-
-    <h3>[method:AnimationClip getClip]()</h3>
-    <div>
-        Returns the clip which holds the animation data for this action.
-    </div>
-
-    <h3>[method:AnimationMixer getMixer]()</h3>
-    <div>
-        Returns the mixer which is responsible for playing this action.
-    </div>
-
-    <h3>[method:Object3D getRoot]()</h3>
-    <div>
-        Returns the root object on which this action is performed.
-    </div>    
-    
-    <h3>[method:AnimationAction halt]( [page:Number durationInSeconds] )</h3>
-    <div>
-        Decelerates this animation's speed to 0 by decreasing [page:.timeScale timeScale] gradually 
-        (starting from its current value), within the passed time interval. This method can be chained.
-    </div>    
-
-    <h3>[method:Boolean isRunning]()</h3>
-    <div>
-        Returns true if this action is activated in the mixer and currently playing (as opposed to 
-        [page:.isScheduled isScheduled]).<br /><br />
-         
-        In addition to being activated in the mixer the following conditions must be fulfilled: 
-        [page:.paused paused] is equal to false, [page:.enabled enabled] is equal to true, 
-        [page:.timeScale timeScale] is different from 0, and there is no scheduling for a delayed start 
-        ([page:.startAt startAt]).
-    </div>
-
-    <h3>[method:Boolean isScheduled]()</h3>
-    <div>
-        Returns true, if this action is activated in the mixer.<br /><br />
-        Note: This doesn’t necessarily mean that the animation is actually running (compare the additional 
-        conditions for [page:.isRunning isRunning]).
-    </div>
-
-    <h3>[method:AnimationAction play]()</h3>
-    <div>
-        Tells the mixer to activate the action. This method can be chained.<br /><br />
-        
-        Note: Activating this action doesn’t necessarily mean that the animation starts immediately: 
-        If the action had already finished before (by reaching the end of its last loop), or if a time 
-        for a delayed start has been set (via [page:.startAt startAt]), a [page:.reset reset] must be 
-        executed first. Some other settings ([page:.paused paused]=true, [page:.enabled enabled]=false, 
-        [page:.weight weight]=0, [page:.timeScale timeScale]=0) can prevent the animation from playing, 
-        too.
-    </div>
-
-    <h3>[method:AnimationAction reset]()</h3>
-    <div>
-        Resets the action. This method can be chained.<br /><br />
-        
-        This method sets [page:.paused paused] to false, [page:.enabled enabled] to true, 
-        [page:.time time] to 0, interrupts any scheduled fading and warping, and removes the internal 
-        loop count and scheduling for delayed starting.<br /><br />
-
-        Note: .*reset* is always called by [page:.stop stop], but .*reset* doesn’t call .*stop* itself.
-        This means: If you want both, resetting and stopping, don’t call .*reset*; call .*stop* instead.
-    </div>
-
-    <h3>[method:AnimationAction setDuration]( [page:Number durationInSeconds] )</h3>
-    <div>
-        Sets the duration for a single loop of this action (by adjusting [page:.timeScale timeScale] 
-        and stopping any scheduled warping). This method can be chained.
-    </div>
-
-    <h3>[method:AnimationAction setEffectiveTimeScale]( [page:Number timeScale] )</h3>
-    <div>
-        Sets the [page:.timeScale timeScale] and stops any scheduled warping. This method can be chained.<br /><br />
-        
-        If [page:.paused paused] is false, the effective time scale (an internal property) will also be set 
-        to this value; otherwise the effective time scale (directly affecting the animation at 
-        this moment) will be set to 0.<br /><br />
-        
-        Note: .*paused* will not be switched to *true* automatically, if .*timeScale* is set to 0 by 
-        this method.
-    </div>    
-
-    <h3>[method:AnimationAction setEffectiveWeight]( [page:Number weight] )</h3>
-    <div>
-        Sets the [page:.weight weight] and stops any scheduled fading. This method can be chained.<br /><br />
- 
-        If [page:.enabled enabled] is true, the effective weight (an internal property) will also be set 
-        to this value; otherwise the effective weight (directly affecting the animation at this moment) 
-        will be set to 0.<br /><br />
-
-        Note: .*enabled* will not be switched to *false* automatically, if .*weight* is set to 0 by 
-        this method.
-    </div>
-    
-    <h3>[method:AnimationAction setLoop]( [page:Number loopMode], [page:Number repetitions] )</h3>
-    <div>
-        Sets the [page:.loop loop mode] and the number of [page:.repetitions repetitions]. This method 
-        can be chained.
-    </div>  
-
-    <h3>[method:AnimationAction startAt]( [page:Number startTimeInSeconds] )</h3>
-    <div>
-        Defines the time for a delayed start (usually passed as [page:AnimationMixer.time] + 
-        deltaTimeInSeconds). This method can be chained.<br /><br />
-        
-        Note: The animation will only start at the given time, if .*startAt* is chained with 
-        [page:.play play], or if the action has already been activated in the mixer (by a previous 
-        call of .*play*, without stopping or resetting it in the meantime).
-    </div>
-
-    <h3>[method:AnimationAction stop]()</h3>
-    <div>
-        Tells the mixer to deactivate this action. This method can be chained.<br /><br />
-        
-        The action will be immediately stopped and completely [page:.reset reset].<br /><br />
-        
-        Note: You can stop all active actions on the same mixer in one go via 
-        [page:AnimationMixer.stopAllAction mixer.stopAllAction].
-    </div>
-    
-    <h3>[method:AnimationAction stopFading]()</h3>
-    <div>
-        Stops any scheduled [page:.fadeIn fading] which is applied to this action. This method can be 
-        chained.
-    </div>
-
-    <h3>[method:AnimationAction stopWarping]()</h3>
-    <div>
-        Stops any scheduled [page:.warp warping] which is applied to this action. This method can be 
-        chained.
-    </div>
-
-    <h3>[method:AnimationAction syncWith]( [page:AnimationAction otherAction] )</h3>
-    <div>
-        Synchronizes this action with the passed other action. This method can be chained.<br /><br />
-        
-        Synchronizing is done by setting this action’s [page:.time time] and [page:.timeScale timeScale] values 
-        to the corresponding values of the other action (stopping any scheduled warping).<br /><br />
-
-        Note: Future changes of the other action's *time* and *timeScale* will not be detected.
-    </div>
-
-    <h3>[method:AnimationAction warp]( [page:Number startTimeScale], [page:Number endTimeScale], [page:Number durationInSeconds] )</h3>
-    <div>
-        Changes the playback speed, within the passed time interval, by modifying 
-        [page:.timeScale timeScale] gradually from *startTimeScale* to *endTimeScale*. This method can 
-        be chained.
-    </div>
-
-
-    <h2>Events</h2>
-    
-    
-    <div class="desc">
-        There are two events indicating when a single loop of the action respectively the entire action has finished. You can react to them with:
-    </div>
-    <code>
-mixer.addEventListener( 'loop', function( e ) { …} ); // properties of e: type, action and loopDelta        
-mixer.addEventListener( 'finished', function( e ) { …} ); // properties of e: type, action and direction
-    </code>    
-
-    <h2>Source</h2>
-
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-  </body>
+		<h1>[name]</h1>
+
+		<div class="desc">
+			AnimationActions schedule the performance of the animations which are stored in
+			[page:AnimationClip AnimationClips].<br /><br />
+
+			Note: Most of AnimationAction's methods can be chained.<br /><br />
+
+			For an overview of the different elements of the three.js animation system see the
+			"Animation System" article in the "Next Steps" section of the manual.
+		</div>
+
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]( [page:AnimationMixer mixer], [page:AnimationClip clip], [page:Object3D localRoot] )</h3>
+		<div>
+			[page:AnimationMixer mixer] - the *AnimationMixer* that is controlled by this action.<br />
+			[page:AnimationClip clip] - the *AnimationClip* that holds the animation data for this action.<br />
+			[page:Object3D localRoot] - the root object on which this action is performed.<br /><br />
+
+			Note: Instead of calling this constructor directly you should instantiate an AnimationAction with
+			[page:AnimationMixer.clipAction] since this method provides caching for better performance.
+		</div>
+
+
+		<h2>Properties</h2>
+
+
+		<h3>[property:Boolean clampWhenFinished]</h3>
+		<div>
+			If *clampWhenFinished* is set to true the animation will automatically be [page:.paused paused]
+			on its last frame.<br /><br />
+
+			If *clampWhenFinished* is set to false, [page:.enabled enabled] will automatically be switched
+			to false when the last loop of the action has finished, so that this action has no further
+			impact.<br /><br />
+
+			Default is false.<br /><br />
+
+			Note: *clampWhenFinished* has no impact if the action is interrupted (it has only an effect if
+			its last loop has really finished).
+		</div>
+
+		<h3>[property:Boolean enabled]</h3>
+		<div>
+			Setting *enabled* to *false* disables this action, so that it has no impact. Default is *true*.<br /><br />
+
+			When the action is re-enabled, the animation continues from its current [page:.time time]
+			(setting *enabled* to *false* doesn't reset the action).<br /><br />
+
+			Note: Setting *enabled* to *true* doesn’t automatically restart the animation. Setting *enabled*
+			to *true* will only restart the animation immediately if the following condition is fulfilled:
+			[page:.paused paused] is *false*, this action has not been deactivated in the meantime (by
+			executing a [page:.stop stop] or [page:.reset reset] command), and neither [page:.weight weight]
+			nor [page:.timeScale timeScale] is 0.
+		</div>
+
+		<h3>[property:Number loop]</h3>
+		<div>
+			The looping mode (can be changed with [page:.setLoop setLoop]). Default is
+			[page:Animation THREE.LoopRepeat] (with an infinite number of [page:.repetitions repetitions])<br /><br />
+
+			Must be one of these constants:<br /><br />
+			[page:Animation THREE.LoopOnce] - playing the clip once,<br />
+			[page:Animation THREE.LoopRepeat] - playing the clip with the choosen number of *repetitions*,
+			each time jumping from the end of the clip directly to its beginning,<br />
+			[page:Animation THREE.LoopPingPong] - playing the clip with the choosen number of *repetitions*,
+			alternately playing forward and backward.
+		</div>
+
+		<h3>[property:Boolean paused]</h3>
+		<div>
+			Setting *paused* to *true* pauses the execution of the action by setting the effective time scale
+			to 0. Default is *false*.<br /><br />
+		</div>
+
+		<h3>[property:Number repetitions]</h3>
+		<div>
+			The number of repetitions of the performed [page:AnimationClip] over the course of this action.
+			Can be set via [page:.setLoop setLoop]. Default is *Infinity*.<br /><br />
+			Setting this number has no effect, if the [page:.loop loop mode] is set to
+			[page:Animation THREE.LoopOnce].<br /><br />
+
+			Note: The first run is not taken into account. For example: if *repetition* is set to 2, the
+			total number of executed rounds will be 3.
+		</div>
+
+		<h3>[property:Number time]</h3>
+		<div>
+			The local time of this action (in seconds, starting with 0).<br /><br />
+
+			The value gets clamped or wrapped to 0...clip.duration (according to the loop state). It can be
+			scaled relativly to the global mixer time by changing [page:.timeScale timeScale] (using
+			[page:.setEffectiveTimeScale setEffectiveTimeScale] or [page:.setDuration setDuration]).<br />
+		</div>
+
+		<h3>[property:Number timeScale]</h3>
+		<div>
+			Scaling factor for the [page:.time time]. A value of 0 causes the animation to pause. Negative
+			values cause the animation to play backwards. Default is 1.<br /><br />
+			Properties/methods concerning *timeScale* (respectively *time*) are:
+			[page:.getEffectiveTimeScale getEffectiveTimeScale],
+			[page:.halt halt],
+			[page:.paused paused],
+			[page:.setDuration setDuration],
+			[page:.setEffectiveTimeScale setEffectiveTimeScale],
+			[page:.stopWarping stopWarping],
+			[page:.syncWith syncWith],
+			[page:.warp warp].
+		</div>
+
+		<h3>[property:Number weight]</h3>
+		<div>
+			The degree of influence of this action (in the interval [0, 1]). Values between 0 (no impact)
+			and 1 (full impact) can be used to blend between several actions. Default is 1. <br /><br />
+			Properties/methods concerning  *weight* are:
+			[page:.crossFadeFrom crossFadeFrom],
+			[page:.crossFadeTo crossFadeTo],
+			[page:.enabled enabled],
+			[page:.fadeIn fadeIn],
+			[page:.fadeOut fadeOut],
+			[page:.getEffectiveWeight getEffectiveWeight],
+			[page:.setEffectiveWeight setEffectiveWeight],
+			[page:.stopFading stopFading].
+		</div>
+
+		<h3>[property:Boolean zeroSlopeAtEnd]</h3>
+		<div>
+			Enables smooth interpolation without separate clips for start, loop and end. Default is *true*.
+		</div>
+
+		<h3>[property:Boolean zeroSlopeAtStart]</h3>
+		<div>
+			Enables smooth interpolation without separate clips for start, loop and end. Default is *true*.
+		</div>
+
+
+		<h2>Methods</h2>
+
+
+		<h3>[method:AnimationAction crossFadeFrom]( [page:AnimationAction fadeOutAction], [page:Number durationInSeconds], [page:Boolean warpBoolean] )</h3>
+		<div>
+			Causes this action to [page:.fadeIn fade in], fading out another action simultaneously, within
+			the passed time interval. This method can be chained.<br /><br />
+
+			If warpBoolean is true, additional [page:.warp warping] (gradually changes of the time scales)
+			will be applied.<br /><br />
+
+			Note: Like with *fadeIn*/*fadeOut*, the fading starts/ends with a weight of 1.
+
+		</div>
+
+		<h3>[method:AnimationAction crossFadeTo]( [page:AnimationAction fadeInAction], [page:Number durationInSeconds], [page:Boolean warpBoolean] )</h3>
+		<div>
+			Causes this action to [page:.fadeOut fade out], fading in another action simultaneously, within
+			the passed time interval. This method can be chained.<br /><br />
+			If warpBoolean is true, additional [page:.warp warping] (gradually changes of the time scales)
+			will be applied.<br /><br />
+
+			Note: Like with *fadeIn*/*fadeOut*, the fading starts/ends with a weight of 1.
+		</div>
+
+		<h3>[method:AnimationAction fadeIn]( [page:Number durationInSeconds] )</h3>
+		<div>
+			Increases the [page:.weight weight] of this action gradually from 0 to 1, within the passed time
+			interval. This method can be chained.
+		</div>
+
+		<h3>[method:AnimationAction fadeOut]( [page:Number durationInSeconds] )</h3>
+		<div>
+			Decreases the [page:.weight weight] of this action gradually from 1 to 0, within the passed time
+			interval. This method can be chained.
+		</div>
+
+		<h3>[method:Number getEffectiveTimeScale]()</h3>
+		<div>
+			Returns the effective time scale (considering the current states of warping and
+			[page:.paused paused]).
+		</div>
+
+		<h3>[method:number getEffectiveWeight]()</h3>
+		<div>
+			Returns the effective weight (considering the current states of fading and
+			[page:.enabled enabled]).
+		</div>
+
+		<h3>[method:AnimationClip getClip]()</h3>
+		<div>
+			Returns the clip which holds the animation data for this action.
+		</div>
+
+		<h3>[method:AnimationMixer getMixer]()</h3>
+		<div>
+			Returns the mixer which is responsible for playing this action.
+		</div>
+
+		<h3>[method:Object3D getRoot]()</h3>
+		<div>
+			Returns the root object on which this action is performed.
+		</div>
+
+		<h3>[method:AnimationAction halt]( [page:Number durationInSeconds] )</h3>
+		<div>
+			Decelerates this animation's speed to 0 by decreasing [page:.timeScale timeScale] gradually
+			(starting from its current value), within the passed time interval. This method can be chained.
+		</div>
+
+		<h3>[method:Boolean isRunning]()</h3>
+		<div>
+			Returns true if the action’s [page:.time time] is currently running.<br /><br />
+
+			In addition to being activated in the mixer (see [page:.isScheduled isScheduled]) the following conditions must be fulfilled:
+			[page:.paused paused] is equal to false, [page:.enabled enabled] is equal to true,
+			[page:.timeScale timeScale] is different from 0, and there is no scheduling for a delayed start
+			([page:.startAt startAt]).<br /><br />
+
+			Note: *isRunning* being true doesn’t necessarily mean that the animation can actually be seen.
+			This is only the case, if [page:.weight weight] is additionally set to a non-zero value.
+		</div>
+
+		<h3>[method:Boolean isScheduled]()</h3>
+		<div>
+			Returns true, if this action is activated in the mixer.<br /><br />
+			Note: This doesn’t necessarily mean that the animation is actually running (compare the additional
+			conditions for [page:.isRunning isRunning]).
+		</div>
+
+		<h3>[method:AnimationAction play]()</h3>
+		<div>
+			Tells the mixer to activate the action. This method can be chained.<br /><br />
+
+			Note: Activating this action doesn’t necessarily mean that the animation starts immediately:
+			If the action had already finished before (by reaching the end of its last loop), or if a time
+			for a delayed start has been set (via [page:.startAt startAt]), a [page:.reset reset] must be
+			executed first. Some other settings ([page:.paused paused]=true, [page:.enabled enabled]=false,
+			[page:.weight weight]=0, [page:.timeScale timeScale]=0) can prevent the animation from playing,
+			too.
+		</div>
+
+		<h3>[method:AnimationAction reset]()</h3>
+		<div>
+			Resets the action. This method can be chained.<br /><br />
+
+			This method sets [page:.paused paused] to false, [page:.enabled enabled] to true,
+			[page:.time time] to 0, interrupts any scheduled fading and warping, and removes the internal
+			loop count and scheduling for delayed starting.<br /><br />
+
+			Note: .*reset* is always called by [page:.stop stop], but .*reset* doesn’t call .*stop* itself.
+			This means: If you want both, resetting and stopping, don’t call .*reset*; call .*stop* instead.
+		</div>
+
+		<h3>[method:AnimationAction setDuration]( [page:Number durationInSeconds] )</h3>
+		<div>
+			Sets the duration for a single loop of this action (by adjusting [page:.timeScale timeScale]
+			and stopping any scheduled warping). This method can be chained.
+		</div>
+
+		<h3>[method:AnimationAction setEffectiveTimeScale]( [page:Number timeScale] )</h3>
+		<div>
+			Sets the [page:.timeScale timeScale] and stops any scheduled warping. This method can be chained.<br /><br />
+
+			If [page:.paused paused] is false, the effective time scale (an internal property) will also be set
+			to this value; otherwise the effective time scale (directly affecting the animation at
+			this moment) will be set to 0.<br /><br />
+
+			Note: .*paused* will not be switched to *true* automatically, if .*timeScale* is set to 0 by
+			this method.
+		</div>
+
+		<h3>[method:AnimationAction setEffectiveWeight]( [page:Number weight] )</h3>
+		<div>
+			Sets the [page:.weight weight] and stops any scheduled fading. This method can be chained.<br /><br />
+
+			If [page:.enabled enabled] is true, the effective weight (an internal property) will also be set
+			to this value; otherwise the effective weight (directly affecting the animation at this moment)
+			will be set to 0.<br /><br />
+
+			Note: .*enabled* will not be switched to *false* automatically, if .*weight* is set to 0 by
+			this method.
+		</div>
+
+		<h3>[method:AnimationAction setLoop]( [page:Number loopMode], [page:Number repetitions] )</h3>
+		<div>
+			Sets the [page:.loop loop mode] and the number of [page:.repetitions repetitions]. This method
+			can be chained.
+		</div>
+
+		<h3>[method:AnimationAction startAt]( [page:Number startTimeInSeconds] )</h3>
+		<div>
+			Defines the time for a delayed start (usually passed as [page:AnimationMixer.time] +
+			deltaTimeInSeconds). This method can be chained.<br /><br />
+
+			Note: The animation will only start at the given time, if .*startAt* is chained with
+			[page:.play play], or if the action has already been activated in the mixer (by a previous
+			call of .*play*, without stopping or resetting it in the meantime).
+		</div>
+
+		<h3>[method:AnimationAction stop]()</h3>
+		<div>
+			Tells the mixer to deactivate this action. This method can be chained.<br /><br />
+
+			The action will be immediately stopped and completely [page:.reset reset].<br /><br />
+
+			Note: You can stop all active actions on the same mixer in one go via 
+			[page:AnimationMixer.stopAllAction mixer.stopAllAction].
+		</div>
+
+		<h3>[method:AnimationAction stopFading]()</h3>
+		<div>
+			Stops any scheduled [page:.fadeIn fading] which is applied to this action. This method can be
+			chained.
+		</div>
+
+		<h3>[method:AnimationAction stopWarping]()</h3>
+		<div>
+			Stops any scheduled [page:.warp warping] which is applied to this action. This method can be
+			chained.
+		</div>
+
+		<h3>[method:AnimationAction syncWith]( [page:AnimationAction otherAction] )</h3>
+		<div>
+			Synchronizes this action with the passed other action. This method can be chained.<br /><br />
+
+			Synchronizing is done by setting this action’s [page:.time time] and [page:.timeScale timeScale] values
+			to the corresponding values of the other action (stopping any scheduled warping).<br /><br />
+
+			Note: Future changes of the other action's *time* and *timeScale* will not be detected.
+		</div>
+
+		<h3>[method:AnimationAction warp]( [page:Number startTimeScale], [page:Number endTimeScale], [page:Number durationInSeconds] )</h3>
+		<div>
+			Changes the playback speed, within the passed time interval, by modifying 
+			[page:.timeScale timeScale] gradually from *startTimeScale* to *endTimeScale*. This method can
+			be chained.
+		</div>
+
+
+		<h2>Events</h2>
+
+
+		<div class="desc">
+			There are two events indicating when a single loop of the action respectively the entire action has finished. You can react to them with:
+		</div>
+		<code>
+		mixer.addEventListener( 'loop', function( e ) { …} ); // properties of e: type, action and loopDelta
+		mixer.addEventListener( 'finished', function( e ) { …} ); // properties of e: type, action and direction
+		</code>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
 </html>

+ 133 - 133
docs/api/animation/AnimationClip.html

@@ -1,136 +1,136 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <base href="../../" />
-    <script src="list.js"></script>
-    <script src="page.js"></script>
-    <link type="text/css" rel="stylesheet" href="page.css" />
-  </head>
-  <body>
-    <h1>[name]</h1>
-
-    <div class="desc">
-    An AnimationClip is a reusable set of keyframe tracks which represent an animation.<br /><br />
-    
-    For an overview of the different elements of the three.js animation system see the
-    "Animation System" article in the "Next Steps" section of the manual.
-    </div>
-    
-
-    <h2>Constructor</h2>
-    
-    
-    <h3>[name]( [page:String name], [page:Number duration], [page:Array tracks] )</h3>
-    <div>
-        [page:String name] - a name for this clip.<br />
-        [page:Number duration] - the duration of this clip (in seconds). If a negative value is passed,
-        the duration will be calculated from the passed *tracks* array.<br />
-        [page:Array tracks] - an array of [page:KeyframeTrack KeyframeTracks].<br /><br />
-    
-        Note: Instead of instantiating an AnimationClip directly with the constructor, you can use one 
-        of its static methods to create AnimationClips: from JSON ([page:.parse parse]), from morph 
-        target sequences ([page:.CreateFromMorphTargetSequence CreateFromMorphTargetSequence],
-        [page:.CreateClipsFromMorphTargetSequences CreateClipsFromMorphTargetSequences]) or from 
-        animation hierarchies ([page:.parseAnimation parseAnimation]) - if your model doesn't already 
-        hold AnimationClips in its geometry's animations array.
-    </div>
-    
-    
-    <h2>Properties</h2>
-    
-
-    <h3>[property:Number duration]</h3>
-    <div>
-        The duration of this clip (in seconds). This can be calculated from the [page:.tracks tracks] 
-        array via [page:.resetDuration resetDuration].
-    </div>     
-
-    <h3>[property:String name]</h3>
-    <div>
-        A name for this clip. A certain clip can be searched via [page:.findByName findByName].
-    </div>    
-
-    <h3>[property:Array tracks]</h3>
-    <div>
-        An array containing a [page:KeyframeTrack] for each property that are animated by this clip.
-    </div> 
-
-    <h3>[property:String uuid]</h3>
-    <div>
-        The [link:http://en.wikipedia.org/wiki/Universally_unique_identifier UUID] of this clip instance.
-        It gets automatically assigned and shouldn't be edited.
-    </div>
-       
-
-    <h2>Methods</h2>
-
-
-    <h3>[method:AnimationClip optimize]()</h3>
-    <div>
-        Optimizes each track by removing equivalent sequential keys (which are common in morph target 
-        sequences).
-    </div>
-
-    <h3>[method:null resetDuration]()</h3>
-    <div>
-        Sets the [page:.duration duration] of the clip to the duration of its longest 
-        [page:KeyframeTrack].
-    </div>
-
-    <h3>[method:AnimationClip trim]()</h3>
-    <div>
-        Trims all tracks to the clip's duration.
-    </div>
-
-
-    <h2>Static Methods</h2>
-
-
-    <h3>[method:Array CreateClipsFromMorphTargetSequences]( [page:String name], [page:Array morphTargetSequence], [page:Number fps], [page:Boolean noLoop] )</h3>
-    <div>
-        Returns an array of new AnimationClips created from the [page:Geometry.morphTargets morph 
-        target sequences] of a geometry, trying to sort morph target names into animation-group-based 
-        patterns like "Walk_001, Walk_002, Run_001, Run_002 ..."<br /><br />
-        
-        This method is called by the [page:JSONLoader] internally, and it uses 
-        [page:.CreateFromMorphTargetSequence CreateFromMorphTargetSequence].
-    </div>
-
-    <h3>[method:AnimationClip CreateFromMorphTargetSequence]( [page:String name], [page:Array morphTargetSequence], [page:Number fps], [page:Boolean noLoop] )</h3>
-    <div>
-        Returns a new AnimationClip from the passed [page:Geometry.morphTargets morph targets array] 
-        of a geometry, taking a name and the number of frames per second.<br /><br />
-
-        Note: The fps parameter is required, but the animation speed can be overridden in an 
-        *AnimationAction* via [page:AnimationAction.setDuration animationAction.setDuration].
-    </div>
-    
-    <h3>[method:AnimationClip findByName]( [page:Object objectOrClipArray], [page:String name] )</h3>
-    <div>
-        Searches for an AnimationClip by name, taking as its first parameter either an array of 
-        AnimationClips, or a mesh or geometry that contains an array named "animations".  
-    </div>
-
-    <h3>[method:AnimationClip parse]( [page:Object json] )</h3>
-    <div>
-        Parses a JSON representation of a clip and returns an AnimationClip.
-    </div>
-    
-    <h3>[method:AnimationClip parseAnimation]( [page:Object animation], [page:Array bones] )</h3>
-    <div>
-        Parses the animation.hierarchy format and returns an AnimationClip.
-    </div>    
-
-    <h3>[method:Object toJSON]( [page:AnimationClip clip] )</h3>
-    <div>
-        Takes an AnimationClip and returns a JSON object.
-    </div>
-    
-
-    <h2>Source</h2>
-
-
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-  </body>
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<div class="desc">
+			An AnimationClip is a reusable set of keyframe tracks which represent an animation.<br /><br />
+
+			For an overview of the different elements of the three.js animation system see the
+			"Animation System" article in the "Next Steps" section of the manual.
+		</div>
+
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]( [page:String name], [page:Number duration], [page:Array tracks] )</h3>
+		<div>
+			[page:String name] - a name for this clip.<br />
+			[page:Number duration] - the duration of this clip (in seconds). If a negative value is passed,
+			the duration will be calculated from the passed *tracks* array.<br />
+			[page:Array tracks] - an array of [page:KeyframeTrack KeyframeTracks].<br /><br />
+
+			Note: Instead of instantiating an AnimationClip directly with the constructor, you can use one
+			of its static methods to create AnimationClips: from JSON ([page:.parse parse]), from morph
+			target sequences ([page:.CreateFromMorphTargetSequence CreateFromMorphTargetSequence],
+			[page:.CreateClipsFromMorphTargetSequences CreateClipsFromMorphTargetSequences]) or from
+			animation hierarchies ([page:.parseAnimation parseAnimation]) - if your model doesn't already
+			hold AnimationClips in its geometry's animations array.
+		</div>
+
+
+		<h2>Properties</h2>
+
+
+		<h3>[property:Number duration]</h3>
+		<div>
+			The duration of this clip (in seconds). This can be calculated from the [page:.tracks tracks]
+			array via [page:.resetDuration resetDuration].
+		</div>
+
+		<h3>[property:String name]</h3>
+		<div>
+			A name for this clip. A certain clip can be searched via [page:.findByName findByName].
+		</div>
+
+		<h3>[property:Array tracks]</h3>
+		<div>
+			An array containing a [page:KeyframeTrack] for each property that are animated by this clip.
+		</div>
+
+		<h3>[property:String uuid]</h3>
+		<div>
+			The [link:http://en.wikipedia.org/wiki/Universally_unique_identifier UUID] of this clip instance.
+			It gets automatically assigned and shouldn't be edited.
+		</div>
+
+
+		<h2>Methods</h2>
+
+
+		<h3>[method:AnimationClip optimize]()</h3>
+		<div>
+			Optimizes each track by removing equivalent sequential keys (which are common in morph target
+			sequences).
+		</div>
+
+		<h3>[method:null resetDuration]()</h3>
+		<div>
+			Sets the [page:.duration duration] of the clip to the duration of its longest
+			[page:KeyframeTrack].
+		</div>
+
+		<h3>[method:AnimationClip trim]()</h3>
+		<div>
+			Trims all tracks to the clip's duration.
+		</div>
+
+
+		<h2>Static Methods</h2>
+
+
+		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [page:String name], [page:Array morphTargetSequence], [page:Number fps], [page:Boolean noLoop] )</h3>
+		<div>
+			Returns an array of new AnimationClips created from the [page:Geometry.morphTargets morph
+			target sequences] of a geometry, trying to sort morph target names into animation-group-based
+			patterns like "Walk_001, Walk_002, Run_001, Run_002 ..."<br /><br />
+
+			This method is called by the [page:JSONLoader] internally, and it uses
+			[page:.CreateFromMorphTargetSequence CreateFromMorphTargetSequence].
+		</div>
+
+		<h3>[method:AnimationClip CreateFromMorphTargetSequence]( [page:String name], [page:Array morphTargetSequence], [page:Number fps], [page:Boolean noLoop] )</h3>
+		<div>
+			Returns a new AnimationClip from the passed [page:Geometry.morphTargets morph targets array]
+			of a geometry, taking a name and the number of frames per second.<br /><br />
+
+			Note: The fps parameter is required, but the animation speed can be overridden in an 
+			*AnimationAction* via [page:AnimationAction.setDuration animationAction.setDuration].
+		</div>
+
+		<h3>[method:AnimationClip findByName]( [page:Object objectOrClipArray], [page:String name] )</h3>
+		<div>
+			Searches for an AnimationClip by name, taking as its first parameter either an array of
+			AnimationClips, or a mesh or geometry that contains an array named "animations".
+		</div>
+
+		<h3>[method:AnimationClip parse]( [page:Object json] )</h3>
+		<div>
+			Parses a JSON representation of a clip and returns an AnimationClip.
+		</div>
+
+		<h3>[method:AnimationClip parseAnimation]( [page:Object animation], [page:Array bones] )</h3>
+		<div>
+			Parses the animation.hierarchy format and returns an AnimationClip.
+		</div>
+
+		<h3>[method:Object toJSON]( [page:AnimationClip clip] )</h3>
+		<div>
+			Takes an AnimationClip and returns a JSON object.
+		</div>
+
+
+		<h2>Source</h2>
+
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
 </html>

+ 97 - 97
docs/api/animation/AnimationMixer.html

@@ -10,102 +10,102 @@
 	<body>
 		<h1>[name]</h1>
 
-	<div class="desc">
-		The AnimationMixer is a player for animations on a particular object in the scene. When 
-        multiple objects in the scene are animated independently, one AnimationMixer may be used for 
-        each object.<br /><br />
-        		
-        For an overview of the different elements of the three.js animation system see the
-        "Animation System" article in the "Next Steps" section of the manual.
-    </div>
-
-
-    <h2>Constructor</h2>
-    
-
-    <h3>[name]( [page:Object3D rootObject] )</h3>
-    <div>
-        [page:Object3D rootObject] - the object whose animations shall be played by this mixer.<br />
-    </div>
-    
-   
-    <h2>Properties</h2>
-
-
-    <h3>[property:Number time]</h3>
-    <div>
-        The global mixer time (in seconds; starting with 0 on the mixer's creation).
-    </div>
-      
-    <h3>[property:Number timeScale]</h3>
-    <div>
-        A scaling factor for the global [page:.time mixer time].<br /><br />
-        
-        Note: Setting the mixer's timeScale to 0 and later back to 1 is a possibility to pause/unpause 
-        all actions that are controlled by this mixer.
-    </div>
-    
-
-    <h2>Methods</h2>
-
-
-    <h3>[method:AnimationAction clipAction]([page:AnimationClip clip], [page:Object3D optionalRoot])</h3>
-    <div>
-        Returns an [page:AnimationAction] for the passed clip, optionally using a root object different 
-        from the mixer's default root. The first parameter can be either an [page:AnimationClip] object 
-        or the name of an AnimationClip.<br /><br />
-        
-        If an action fitting these parameters doesn't yet exist, it will be created by this method.<br /><br />
- 
-        Note: Calling this method several times with the same parameters returns always the same clip 
-        instance.
-    </div>
-
-    <h3>[method:AnimationAction existingAction]([page:AnimationClip clip], [page:Object3D optionalRoot])</h3>
-    <div>
-        Returns an existing [page:AnimationAction] for the passed clip, optionally using a root object
-        different from the mixer's default root.<br /><br />
-        
-        The first parameter can be either an [page:AnimationClip] object or the name of an AnimationClip.
-    </div>
-
-    <h3>[method:AnimationMixer stopAllAction]()</h3>
-    <div>
-        Deactivates all previously scheduled actions on this mixer.
-    </div>
-
-    <h3>[method:AnimationMixer update]([page:Number deltaTimeInSeconds]) </h3>
-    <div>
-        Advances the global mixer time and updates the animation.<br /><br />
-         
-        This is usually done in the render loop, passing [page:Clock.getDelta clock.getDelta] scaled by the mixer's [page:.timeScale timeScale]).
-    </div>
-
-    <h3>[method:Object3D getRoot]()</h3>
-    <div>
-        Returns this mixer's root object.
-    </div>
-
-    <h3>[method:null uncacheClip]([page:AnimationClip clip])</h3>
-
-    <div>
-        Deallocates all memory resources for a clip.
-    </div>
-
-    <h3>[method:null uncacheRoot]([page:Object3D root]) </h3>
-    <div>
-        Deallocates all memory resources for a root object.
-    </div>
-
-    <h3>[method:null uncacheAction]([page:AnimationClip clip], [page:Object3D optionalRoot])</h3>
-    <div>
-        Deallocates all memory resources for an action.
-    </div>
-
-
-    <h2>Source</h2>
-
-
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+		<div class="desc">
+			The AnimationMixer is a player for animations on a particular object in the scene. When
+			multiple objects in the scene are animated independently, one AnimationMixer may be used for
+			each object.<br /><br />
+
+			For an overview of the different elements of the three.js animation system see the
+			"Animation System" article in the "Next Steps" section of the manual.
+		</div>
+
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]( [page:Object3D rootObject] )</h3>
+		<div>
+			[page:Object3D rootObject] - the object whose animations shall be played by this mixer.<br />
+		</div>
+
+
+		<h2>Properties</h2>
+
+
+		<h3>[property:Number time]</h3>
+		<div>
+			The global mixer time (in seconds; starting with 0 on the mixer's creation).
+		</div>
+
+		<h3>[property:Number timeScale]</h3>
+		<div>
+			A scaling factor for the global [page:.time mixer time].<br /><br />
+
+			Note: Setting the mixer's timeScale to 0 and later back to 1 is a possibility to pause/unpause
+			all actions that are controlled by this mixer.
+		</div>
+
+
+		<h2>Methods</h2>
+
+
+		<h3>[method:AnimationAction clipAction]([page:AnimationClip clip], [page:Object3D optionalRoot])</h3>
+		<div>
+			Returns an [page:AnimationAction] for the passed clip, optionally using a root object different
+			from the mixer's default root. The first parameter can be either an [page:AnimationClip] object
+			or the name of an AnimationClip.<br /><br />
+
+			If an action fitting these parameters doesn't yet exist, it will be created by this method.<br /><br />
+
+			Note: Calling this method several times with the same parameters returns always the same clip 
+			instance.
+		</div>
+
+		<h3>[method:AnimationAction existingAction]([page:AnimationClip clip], [page:Object3D optionalRoot])</h3>
+		<div>
+			Returns an existing [page:AnimationAction] for the passed clip, optionally using a root object
+			different from the mixer's default root.<br /><br />
+
+			The first parameter can be either an [page:AnimationClip] object or the name of an AnimationClip.
+		</div>
+
+		<h3>[method:AnimationMixer stopAllAction]()</h3>
+		<div>
+			Deactivates all previously scheduled actions on this mixer.
+		</div>
+
+		<h3>[method:AnimationMixer update]([page:Number deltaTimeInSeconds]) </h3>
+		<div>
+			Advances the global mixer time and updates the animation.<br /><br />
+
+			This is usually done in the render loop, passing [page:Clock.getDelta clock.getDelta] scaled by the mixer's [page:.timeScale timeScale]).
+		</div>
+
+		<h3>[method:Object3D getRoot]()</h3>
+		<div>
+			Returns this mixer's root object.
+		</div>
+
+		<h3>[method:null uncacheClip]([page:AnimationClip clip])</h3>
+
+		<div>
+			Deallocates all memory resources for a clip.
+		</div>
+
+		<h3>[method:null uncacheRoot]([page:Object3D root]) </h3>
+		<div>
+			Deallocates all memory resources for a root object.
+		</div>
+
+		<h3>[method:null uncacheAction]([page:AnimationClip clip], [page:Object3D optionalRoot])</h3>
+		<div>
+			Deallocates all memory resources for an action.
+		</div>
+
+
+		<h2>Source</h2>
+
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
 	</body>
 </html>

+ 59 - 59
docs/api/animation/AnimationObjectGroup.html

@@ -1,85 +1,85 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <base href="../../" />
-    <script src="list.js"></script>
-    <script src="page.js"></script>
-    <link type="text/css" rel="stylesheet" href="page.css" />
-  </head>
-  <body>
-    <h1>[name]</h1>
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
 
-    <div class="desc">A group of objects that receives a shared animation state.<br /><br />
-        		
-        For an overview of the different elements of the three.js animation system see the
-        "Animation System" article in the "Next Steps" section of the manual.
-    </div>
+		<div class="desc">A group of objects that receives a shared animation state.<br /><br />
 
-    <h2>Usage:</h2>
+			For an overview of the different elements of the three.js animation system see the
+			"Animation System" article in the "Next Steps" section of the manual.
+		</div>
 
-    <div class="desc">
-        Add objects you would otherwise pass as 'root' to the	constructor or the [page:AnimationMixer.clipAction clipAction]
-        method of [page:AnimationMixer AnimationMixer] and instead pass this object as 'root'.<br /><br />
+		<h2>Usage:</h2>
 
-        Note that objects of this class appear as one object to the mixer,
-        so cache control of the individual objects must be done	on the group.
-    </div>
+		<div class="desc">
+			Add objects you would otherwise pass as 'root' to the constructor or the [page:AnimationMixer.clipAction clipAction]
+			method of [page:AnimationMixer AnimationMixer] and instead pass this object as 'root'.<br /><br />
 
+			Note that objects of this class appear as one object to the mixer,
+			so cache control of the individual objects must be done	on the group.
+		</div>
 
-    <h2>Limitations</h2>
-    <div class="desc">
-        The animated properties must be compatible among all objects in the group.<br /><br />
 
-        A single property can either be controlled through a target group or directly, but not both.
-    </div>
+		<h2>Limitations</h2>
+		<div class="desc">
+			The animated properties must be compatible among all objects in the group.<br /><br />
 
+			A single property can either be controlled through a target group or directly, but not both.
+		</div>
 
-    <h2>Constructor</h2>
 
+		<h2>Constructor</h2>
 
-    <h3>[name]( [page:object obj1], [page:object obj2], [page:object obj3], ... )</h3>
-      [page:object obj] - an abitrary number of meshes that share the same animation state.<br />
 
-   
-    <h2>Properties</h2>
-    
+		<h3>[name]( [page:object obj1], [page:object obj2], [page:object obj3], ... )</h3>
+		[page:object obj] - an abitrary number of meshes that share the same animation state.<br />
 
-    <h3>[property:object stats]</h3>
-    <div>
-        An object that contains some informations of this *AnimationObjectGroup* (total number, number 
-        in use, number of bindings per object)
-    </div>    
-    
-    <h3>[property:String uuid]</h3>
-    <div>
-        The [link:http://en.wikipedia.org/wiki/Universally_unique_identifier UUID] of this 
-        *AnimationObjectGroup*. It gets automatically assigned and shouldn't be edited.
-    </div>
 
+		<h2>Properties</h2>
 
-    <h2>Methods</h2>
 
+		<h3>[property:object stats]</h3>
+		<div>
+			An object that contains some informations of this *AnimationObjectGroup* (total number, number
+			in use, number of bindings per object)
+		</div>
 
-    <h3>[method:null add]( [page:object obj1], [page:object obj2], [page:object obj3], ... )</h3>
-    <div>
-        Adds an arbitrary number of objects to this *AnimationObjectGroup*.
-    </div>
+		<h3>[property:String uuid]</h3>
+		<div>
+			The [link:http://en.wikipedia.org/wiki/Universally_unique_identifier UUID] of this
+			*AnimationObjectGroup*. It gets automatically assigned and shouldn't be edited.
+		</div>
 
-    <h3>[method:null remove]( [page:object obj1], [page:object obj2], [page:object obj3], ... )</h3>
-    <div>
-        Removes an arbitrary number of objects from this *AnimationObjectGroup*.
-    </div>
 
-    <h3>[method:null uncache]( [page:object obj1], [page:object obj2], [page:object obj3], ... )</h3>
-    <div>
-        Deallocates all memory resources for the passed objects of this *AnimationObjectGroup*.
-    </div>
+		<h2>Methods</h2>
 
 
-    <h2>Source</h2>
+		<h3>[method:null add]( [page:object obj1], [page:object obj2], [page:object obj3], ... )</h3>
+		<div>
+			Adds an arbitrary number of objects to this *AnimationObjectGroup*.
+		</div>
 
+		<h3>[method:null remove]( [page:object obj1], [page:object obj2], [page:object obj3], ... )</h3>
+		<div>
+			Removes an arbitrary number of objects from this *AnimationObjectGroup*.
+		</div>
 
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-  </body>
+		<h3>[method:null uncache]( [page:object obj1], [page:object obj2], [page:object obj3], ... )</h3>
+		<div>
+			Deallocates all memory resources for the passed objects of this *AnimationObjectGroup*.
+		</div>
+
+
+		<h2>Source</h2>
+
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
 </html>

+ 54 - 54
docs/api/animation/AnimationUtils.html

@@ -1,57 +1,57 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <base href="../../" />
-    <script src="list.js"></script>
-    <script src="page.js"></script>
-    <link type="text/css" rel="stylesheet" href="page.css" />
-  </head>
-  <body>
-    <h1>[name]</h1>
-
-    <div class="desc">
-    An object with various functions to assist with animations, used internally.
-    </div>
-    
-
-    <h2>Methods</h2>
-    
-
-    <h3>[method:Array arraySlice]( array, from, to )</h3>
-    <div>
-    This is the same as  Array.prototype.slice, but also works on typed arrays.
-    </div>
-
-    <h3>[method:Array convertArray]( array, type, forceClone )</h3>
-    <div>
-    Converts an array to a specific type.
-    </div>
-
-    <h3>[method:Array flattenJSON]( jsonKeys, times, values, valuePropertyName  )</h3>
-    <div>
-    Used for parsing AOS keyframe formats.
-    </div>
-
-    <h3>[method:Array getKeyframeOrder]( times )</h3>
-    <div>
-    Returns an array by which times and values can be sorted.
-    </div>
-
-    <h3>[method:Boolean isTypedArray]( object )</h3>
-    <div>
-    Returns *true* if the object is a typed array.
-    </div>
-
-    <h3>[method:Array sortedArray]( values, stride, order )</h3>
-    <div>
-    Sorts the array previously returned by [page:AnimationUtils.getKeyframeOrder getKeyframeOrder].
-    </div>
-    
-
-    <h2>Source</h2>
-
-
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-  </body>
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<div class="desc">
+		An object with various functions to assist with animations, used internally.
+		</div>
+
+
+		<h2>Methods</h2>
+
+
+		<h3>[method:Array arraySlice]( array, from, to )</h3>
+		<div>
+		This is the same as  Array.prototype.slice, but also works on typed arrays.
+		</div>
+
+		<h3>[method:Array convertArray]( array, type, forceClone )</h3>
+		<div>
+		Converts an array to a specific type.
+		</div>
+
+		<h3>[method:Array flattenJSON]( jsonKeys, times, values, valuePropertyName  )</h3>
+		<div>
+		Used for parsing AOS keyframe formats.
+		</div>
+
+		<h3>[method:Array getKeyframeOrder]( times )</h3>
+		<div>
+		Returns an array by which times and values can be sorted.
+		</div>
+
+		<h3>[method:Boolean isTypedArray]( object )</h3>
+		<div>
+		Returns *true* if the object is a typed array.
+		</div>
+
+		<h3>[method:Array sortedArray]( values, stride, order )</h3>
+		<div>
+		Sorts the array previously returned by [page:AnimationUtils.getKeyframeOrder getKeyframeOrder].
+		</div>
+
+
+		<h2>Source</h2>
+
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
 </html>

+ 234 - 234
docs/api/animation/KeyframeTrack.html

@@ -1,237 +1,237 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <base href="../../" />
-    <script src="list.js"></script>
-    <script src="page.js"></script>
-    <link type="text/css" rel="stylesheet" href="page.css" />
-  </head>
-  <body>
-
-    <h1>[name]</h1>
-
-    <div class="desc">
-        A KeyframeTrack is a timed sequence of [link:https://en.wikipedia.org/wiki/Key_frame keyframes], 
-        which are composed of lists of times and related values, and which are used to animate a 
-        specific property of an object.<br /><br />
-
-        For an overview of the different elements of the three.js animation system see the
-        "Animation System" article in the "Next Steps" section of the manual.<br /><br />
-
-        In contrast to the animation hierarchy of the 
-        [link:https://github.com/mrdoob/three.js/wiki/JSON-Model-format-3 JSON model format] a 
-        *KeyframeTrack* doesn't store its single keyframes as objects in a "keys" array (holding the 
-        times and the values for each frame together in one place).<br /><br />
-         
-        Instead of this there are always two arrays in a *KeyframeTrack*: the [page:.times times] array 
-        stores the time values for all keyframes of this track in sequential order, and the 
-        [page:.values values] array contains the corresponding changing values of the animated property.<br /><br />
-         
-        A single value, belonging to a certain point of time, can not only be a simple number, but (for 
-        example) a vector (if a position is animated) or a quaternion (if a rotation is animated). For 
-        this reason the values array (which is a flat array, too) might be three or four times as long as the 
-        times array.<br /><br />
-
-        Corresponding to the different possible types of animated values there are several subclasses of 
-        *KeyframeTrack*, inheriting the most properties and methods:
-        
-        <ul> 
-            <li>[page:BooleanKeyframeTrack]</li> 
-            <li>[page:ColorKeyframeTrack]</li>
-            <li>[page:NumberKeyframeTrack]</li>
-            <li>[page:QuaternionKeyframeTrack]</li>
-            <li>[page:StringKeyframeTrack]</li> 
-            <li>[page:VectorKeyframeTrack]</li>
-        </ul>
-        Some examples of how to manually create [page:AnimationClip AnimationClips] with different sorts 
-        of KeyframeTracks can be found in the [link:https://threejs.org/examples/js/AnimationClipCreator.js] 
-        file.<br /><br />
-
-        Since explicit values are only specified for the discrete points of time stored in the times array, 
-        all values in between have to be interpolated.<br /><br />
-        
-        The track's name is important for the connection of this track with a specific property of the 
-        animated node (done by [page:PropertyBinding]).
-    
-
-    </div>
-
-
-    <h2>Constructor</h2>
-
-
-    <h3>[name]( [page:String name], [page:Array times], [page:Array values], [page:Constant interpolation] )</h3>
-    <div>
-      [page:String name] - the identifier for the *KeyframeTrack*.<br />
-      [page:Array times] - an array of keyframe times, converted internally to a 
-      [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array Float32Array].<br />
-      [page:Array values] - an array with the values related to the times array, converted internally to a 
-      [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array Float32Array].<br />
-      [page:Constant interpolation] - the type of interpolation to use. See 
-      [page:Animation Animation Constants] for possible values. Default is [page:Animation InterpolateLinear].
-    </div>
-    
-
-    <h2>Properties</h2>
-
-
-    <h3>[property:String name]</h3>
-    <div>
-        The track's name can refer to [page:Geometry.morphTargets morph targets] or 
-        [page:SkinnedMesh bones] or possibly other values within an animated object. See 
-        [page:PropertyBinding.parseTrackName] for the forms of strings that can be parsed for property 
-        binding:<br /><br />
-
-        The name can specify the node either using its name or its uuid (although it needs to be in the 
-        subtree of the scene graph node passed into the mixer). Or, if the track name starts with a dot, 
-        the track applies to the root node that was passed into the mixer.<br /><br />
-
-        Usually after the node a property will be specified directly. But you can also specify a 
-        subproperty, such as .rotation[x], if you just want to drive the X component of the rotation 
-        via a float track.<br /><br />
-
-        You can also specify bones or multimaterials by using an object name, for example: 
-        .bones[R_hand].scale; the red channel of the diffuse color of the fourth material in a 
-        materials array - as a further example - can be accessed with .materials[3].diffuse[r].<br /><br />
-
-        PropertyBinding will also resolve morph target names, for example: .morphTargetInfluences[run].<br /><br />
-
-        Note: The track's name does not necessarily have to be unique. Multiple tracks can drive the same 
-        property. The result should be based on a weighted blend between the multiple tracks according to 
-        the weights of their respective actions.
-    </div>
-
-    <h3>[property:Float32Array times]</h3>
-    <div>
-        A [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array Float32Array], 
-        converted from the times array which is passed in the constructor.
-    </div>
-
-    <h3>[property:Float32Array values]</h3>
-    <div>
-        A [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array Float32Array], 
-        converted from the values array which is passed in the constructor.
-    </div>
-
-    <h3>[property:Constant DefaultInterpolation]</h3>
-    <div>
-        The default interpolation type: [page:Animation InterpolateLinear].
-    </div>    
-    
-    <h3>[property:Constant TimeBufferType ]</h3>
-    <div>
-        [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array Float32Array],
-        the type of the buffer internally used for the times.
-    </div>    
-    
-    <h3>[property:Constant ValueBufferType ]</h3>
-    <div>
-        [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array Float32Array],
-        the type of the buffer internally used for the values.
-    </div>
-
-
-    <h2>Methods</h2>
-    
-
-    <h3>[method:null createInterpolant]()</h3>
-    <div>
-      Creates a [page:LinearInterpolant LinearInterpolant], [page:CubicInterpolant CubicInterpolant]
-      or [page:DiscreteInterpolant DiscreteInterpolant], depending on the value of the interpolation 
-      parameter passed in the constructor.
-    </div>
-
-    <h3>[method:null getInterpolation]()</h3>
-    <div>
-      Returns the interpolation type.
-    </div>
-
-    <h3>[method:Number getValueSize]()</h3>
-    <div>
-      Returns the size of each value (that is the length of the [page:.values values] array divided 
-      by the length of the [page:.times times] array).
-    </div>
-
-    <h3>[method:DiscreteInterpolant InterpolantFactoryMethodDiscrete]( [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array result] )</h3>
-    <div>
-      Creates a new [page:DiscreteInterpolant DiscreteInterpolant] from the 
-      [page:KeyframeTrack.times times] and [page:KeyframeTrack.times values]. A Float32Array can be 
-      passed which will receive the results. Otherwise a new array with the appropriate size will be 
-      created automatically.
-    </div>
-
-    <h3>[method:null InterpolantFactoryMethodLinear]( [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array result] )</h3>
-    <div>
-      Creates a new [page:LinearInterpolant LinearInterpolant] from the 
-      [page:KeyframeTrack.times times] and [page:KeyframeTrack.times values]. A Float32Array can be 
-      passed which will receive the results. Otherwise a new array with the appropriate size will be 
-      created automatically.
-    </div>
-
-    <h3>[method:null InterpolantFactoryMethodSmooth]( [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array result] )</h3>
-    <div>
-      Create a new [page:CubicInterpolant CubicInterpolant] from the 
-      [page:KeyframeTrack.times times] and [page:KeyframeTrack.times values]. A Float32Array can be 
-      passed which will receive the results. Otherwise a new array with the appropriate size will be 
-      created automatically.
-    </div>
-
-    <h3>[method:null optimize]()</h3>
-    <div>
-      Removes equivalent sequential keys, which are common in morph target sequences. Called 
-      automatically by the constructor.
-    </div>
-
-    <h3>[method:null scale]()</h3>
-    <div>
-      Scales all keyframe times by a factor.<br /><br />
-      
-      Note: This is useful, for example, for conversions to a certain rate of frames per seconds (as it
-      is done internally by 
-      [page:AnimationClip.CreateFromMorphTargetSequence animationClip.CreateFromMorphTargetSequence]).
-    </div>
-
-    <h3>[method:null setInterpolation]( [page:Constant interpolationType] )</h3>
-    <div>
-      Sets the interpolation type. See [page:Animation Animation Constants] for choices.
-    </div>
-
-    <h3>[method:null shift]( [page:Number timeOffsetInSeconds] )</h3>
-    <div>
-      Moves all keyframes either forward or backward in time.
-    </div>
-
-
-    <h3>[method:null trim]( [page:Number startTimeInSeconds], [page:Number endTimeInSeconds] )</h3>
-    <div>
-      Removes keyframes before *startTime* and after *endTime*,
-      without changing any values within the range [*startTime*, *endTime*].
-    </div>
-
-    <h3>[method:null validate]()</h3>
-    <div>
-      Performs minimal validation on the tracks. Called automatically by the constructor.<br /><br />
-      This method logs errors to the console, if a track is empty, if the [page:.valueSize value size] is not valid, if an item
-      in the [page:.times times] or [page:.values values] array is not a valid number or if the items in the *times* array are out of order. 
-    </div>
-    
-    <h2>Static Methods</h2>
-
-    <h3>[method:KeyframeTrack parse]( [page:JSON json] )</h3>
-    <div>
-    Parses a JSON object and returns a new keyframe track of the correct type.
-    </div>    
-
-    <h3>[method:JSON toJSON]( [page:KeyframeTrack track] )</h3>
-    <div>
-    Converts the track to JSON.
-    </div>
-    
-
-    <h2>Source</h2>
-
-
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-  </body>
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+
+		<h1>[name]</h1>
+
+		<div class="desc">
+			A KeyframeTrack is a timed sequence of [link:https://en.wikipedia.org/wiki/Key_frame keyframes],
+			which are composed of lists of times and related values, and which are used to animate a
+			specific property of an object.<br /><br />
+
+			For an overview of the different elements of the three.js animation system see the
+			"Animation System" article in the "Next Steps" section of the manual.<br /><br />
+
+			In contrast to the animation hierarchy of the
+			[link:https://github.com/mrdoob/three.js/wiki/JSON-Model-format-3 JSON model format] a
+			*KeyframeTrack* doesn't store its single keyframes as objects in a "keys" array (holding the
+			times and the values for each frame together in one place).<br /><br />
+
+			Instead of this there are always two arrays in a *KeyframeTrack*: the [page:.times times] array
+			stores the time values for all keyframes of this track in sequential order, and the
+			[page:.values values] array contains the corresponding changing values of the animated property.<br /><br />
+
+			A single value, belonging to a certain point of time, can not only be a simple number, but (for
+			example) a vector (if a position is animated) or a quaternion (if a rotation is animated). For
+			this reason the values array (which is a flat array, too) might be three or four times as long as the
+			times array.<br /><br />
+
+			Corresponding to the different possible types of animated values there are several subclasses of
+			*KeyframeTrack*, inheriting the most properties and methods:
+
+			<ul>
+				<li>[page:BooleanKeyframeTrack]</li>
+				<li>[page:ColorKeyframeTrack]</li>
+				<li>[page:NumberKeyframeTrack]</li>
+				<li>[page:QuaternionKeyframeTrack]</li>
+				<li>[page:StringKeyframeTrack]</li>
+				<li>[page:VectorKeyframeTrack]</li>
+			</ul>
+			Some examples of how to manually create [page:AnimationClip AnimationClips] with different sorts
+			of KeyframeTracks can be found in the [link:https://threejs.org/examples/js/AnimationClipCreator.js]
+			file.<br /><br />
+
+			Since explicit values are only specified for the discrete points of time stored in the times array,
+			all values in between have to be interpolated.<br /><br />
+
+			The track's name is important for the connection of this track with a specific property of the
+			animated node (done by [page:PropertyBinding]).
+
+
+		</div>
+
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]( [page:String name], [page:Array times], [page:Array values], [page:Constant interpolation] )</h3>
+		<div>
+			[page:String name] - the identifier for the *KeyframeTrack*.<br />
+			[page:Array times] - an array of keyframe times, converted internally to a
+			[link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array Float32Array].<br />
+			[page:Array values] - an array with the values related to the times array, converted internally to a
+			[link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array Float32Array].<br />
+			[page:Constant interpolation] - the type of interpolation to use. See
+			[page:Animation Animation Constants] for possible values. Default is [page:Animation InterpolateLinear].
+		</div>
+
+
+		<h2>Properties</h2>
+
+
+		<h3>[property:String name]</h3>
+		<div>
+			The track's name can refer to [page:Geometry.morphTargets morph targets] or
+			[page:SkinnedMesh bones] or possibly other values within an animated object. See
+			[page:PropertyBinding.parseTrackName] for the forms of strings that can be parsed for property
+			binding:<br /><br />
+
+			The name can specify the node either using its name or its uuid (although it needs to be in the
+			subtree of the scene graph node passed into the mixer). Or, if the track name starts with a dot,
+			the track applies to the root node that was passed into the mixer.<br /><br />
+
+			Usually after the node a property will be specified directly. But you can also specify a
+			subproperty, such as .rotation[x], if you just want to drive the X component of the rotation
+			via a float track.<br /><br />
+
+			You can also specify bones or multimaterials by using an object name, for example:
+			.bones[R_hand].scale; the red channel of the diffuse color of the fourth material in a
+			materials array - as a further example - can be accessed with .materials[3].diffuse[r].<br /><br />
+
+			PropertyBinding will also resolve morph target names, for example: .morphTargetInfluences[run].<br /><br />
+
+			Note: The track's name does not necessarily have to be unique. Multiple tracks can drive the same
+			property. The result should be based on a weighted blend between the multiple tracks according to
+			the weights of their respective actions.
+		</div>
+
+		<h3>[property:Float32Array times]</h3>
+		<div>
+			A [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array Float32Array],
+			converted from the times array which is passed in the constructor.
+		</div>
+
+		<h3>[property:Float32Array values]</h3>
+		<div>
+			A [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array Float32Array],
+			converted from the values array which is passed in the constructor.
+		</div>
+
+		<h3>[property:Constant DefaultInterpolation]</h3>
+		<div>
+			The default interpolation type: [page:Animation InterpolateLinear].
+		</div>
+
+		<h3>[property:Constant TimeBufferType ]</h3>
+		<div>
+			[link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array Float32Array],
+			the type of the buffer internally used for the times.
+		</div>
+
+		<h3>[property:Constant ValueBufferType ]</h3>
+		<div>
+			[link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array Float32Array],
+			the type of the buffer internally used for the values.
+		</div>
+
+
+		<h2>Methods</h2>
+
+
+		<h3>[method:null createInterpolant]()</h3>
+		<div>
+			Creates a [page:LinearInterpolant LinearInterpolant], [page:CubicInterpolant CubicInterpolant]
+			or [page:DiscreteInterpolant DiscreteInterpolant], depending on the value of the interpolation
+			parameter passed in the constructor.
+		</div>
+
+		<h3>[method:null getInterpolation]()</h3>
+		<div>
+			Returns the interpolation type.
+		</div>
+
+		<h3>[method:Number getValueSize]()</h3>
+		<div>
+			Returns the size of each value (that is the length of the [page:.values values] array divided
+			by the length of the [page:.times times] array).
+		</div>
+
+		<h3>[method:DiscreteInterpolant InterpolantFactoryMethodDiscrete]( [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array result] )</h3>
+		<div>
+			Creates a new [page:DiscreteInterpolant DiscreteInterpolant] from the
+			[page:KeyframeTrack.times times] and [page:KeyframeTrack.times values]. A Float32Array can be
+			passed which will receive the results. Otherwise a new array with the appropriate size will be
+			created automatically.
+		</div>
+
+		<h3>[method:null InterpolantFactoryMethodLinear]( [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array result] )</h3>
+		<div>
+			Creates a new [page:LinearInterpolant LinearInterpolant] from the
+			[page:KeyframeTrack.times times] and [page:KeyframeTrack.times values]. A Float32Array can be
+			passed which will receive the results. Otherwise a new array with the appropriate size will be
+			created automatically.
+		</div>
+
+		<h3>[method:null InterpolantFactoryMethodSmooth]( [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array result] )</h3>
+		<div>
+			Create a new [page:CubicInterpolant CubicInterpolant] from the
+			[page:KeyframeTrack.times times] and [page:KeyframeTrack.times values]. A Float32Array can be
+			passed which will receive the results. Otherwise a new array with the appropriate size will be
+			created automatically.
+		</div>
+
+		<h3>[method:null optimize]()</h3>
+		<div>
+			Removes equivalent sequential keys, which are common in morph target sequences. Called
+			automatically by the constructor.
+		</div>
+
+		<h3>[method:null scale]()</h3>
+		<div>
+			Scales all keyframe times by a factor.<br /><br />
+
+			Note: This is useful, for example, for conversions to a certain rate of frames per seconds (as it
+			is done internally by
+			[page:AnimationClip.CreateFromMorphTargetSequence animationClip.CreateFromMorphTargetSequence]).
+		</div>
+
+		<h3>[method:null setInterpolation]( [page:Constant interpolationType] )</h3>
+		<div>
+			Sets the interpolation type. See [page:Animation Animation Constants] for choices.
+		</div>
+
+		<h3>[method:null shift]( [page:Number timeOffsetInSeconds] )</h3>
+		<div>
+			Moves all keyframes either forward or backward in time.
+		</div>
+
+
+		<h3>[method:null trim]( [page:Number startTimeInSeconds], [page:Number endTimeInSeconds] )</h3>
+		<div>
+			Removes keyframes before *startTime* and after *endTime*,
+			without changing any values within the range [*startTime*, *endTime*].
+		</div>
+
+		<h3>[method:null validate]()</h3>
+		<div>
+			Performs minimal validation on the tracks. Called automatically by the constructor.<br /><br />
+			This method logs errors to the console, if a track is empty, if the [page:.valueSize value size] is not valid, if an item
+			in the [page:.times times] or [page:.values values] array is not a valid number or if the items in the *times* array are out of order.
+		</div>
+
+		<h2>Static Methods</h2>
+
+		<h3>[method:KeyframeTrack parse]( [page:JSON json] )</h3>
+		<div>
+		Parses a JSON object and returns a new keyframe track of the correct type.
+		</div>
+
+		<h3>[method:JSON toJSON]( [page:KeyframeTrack track] )</h3>
+		<div>
+		Converts the track to JSON.
+		</div>
+
+
+		<h2>Source</h2>
+
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
 </html>

+ 88 - 88
docs/api/animation/PropertyBinding.html

@@ -1,131 +1,131 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <base href="../../" />
-    <script src="list.js"></script>
-    <script src="page.js"></script>
-    <link type="text/css" rel="stylesheet" href="page.css" />
-  </head>
-  <body>
-    <h1>[name]</h1>
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
 
-    <div class="desc">
-    This holds a reference to a real property in the scene graph; used internally.
-    </div>
+		<div class="desc">
+			This holds a reference to a real property in the scene graph; used internally.
+		</div>
 
 
-    <h2>Constructor</h2>
+		<h2>Constructor</h2>
 
 
-    <h3>[name]( [page:Object3D rootNode], path, parsedPath )</h3>
-    <div>
-      -- [page:Object3D rootNode]:
-      -- path
-      -- parsedPath (optional)
+		<h3>[name]( [page:Object3D rootNode], path, parsedPath )</h3>
+		<div>
+			-- [page:Object3D rootNode]:
+			-- path
+			-- parsedPath (optional)
 
-    </div>
+		</div>
 
-    <h2>Properties</h2>
+		<h2>Properties</h2>
 
-    <h3>[property:Number path]</h3>
-    <div>
+		<h3>[property:Number path]</h3>
+		<div>
 
-    </div>
+		</div>
 
-    <h3>[property:Number parsedPath]</h3>
-    <div>
+		<h3>[property:Number parsedPath]</h3>
+		<div>
 
-    </div>
+		</div>
 
-    <h3>[property:Number node]</h3>
-    <div>
+		<h3>[property:Number node]</h3>
+		<div>
 
-    </div>
+		</div>
 
-    <h3>[property:Number rootNode]</h3>
-    <div>
+		<h3>[property:Number rootNode]</h3>
+		<div>
 
-    </div>
+		</div>
 
-    <h3>[property:Object BindingType]</h3>
-    <div>
+		<h3>[property:Object BindingType]</h3>
+		<div>
 
-    </div>
+		</div>
 
-    <h3>[property:Object Versioning]</h3>
-    <div>
+		<h3>[property:Object Versioning]</h3>
+		<div>
 
-    </div>
+		</div>
 
-    <h3>[property:Array GetterByBindingType]</h3>
-    <div>
+		<h3>[property:Array GetterByBindingType]</h3>
+		<div>
 
-    </div>
+		</div>
 
-    <h3>[property:Array SetterByBindingTypeAndVersioning]</h3>
-    <div>
+		<h3>[property:Array SetterByBindingTypeAndVersioning]</h3>
+		<div>
 
-    </div>
+		</div>
 
 
 
-    <h2>Methods</h2>
+		<h2>Methods</h2>
 
-    <h3>[method:null getValue]( [page:Array targetArray], [page:Number offset] )</h3>
-    <div>
-    </div>
+		<h3>[method:null getValue]( [page:Array targetArray], [page:Number offset] )</h3>
+		<div>
+		</div>
 
-    <h3>[method:null setValue]( [page:Array sourceArray], [page:Number offset] )</h3>
-    <div>
-    </div>
+		<h3>[method:null setValue]( [page:Array sourceArray], [page:Number offset] )</h3>
+		<div>
+		</div>
 
-    <h3>[method:null bind]( )</h3>
-    <div>
-      Create getter / setter pair for a property in the scene graph. Used internally by
-      [page:PropertyBinding.getValue getValue] and [page:PropertyBinding.setValue setValue].
-    </div>
+		<h3>[method:null bind]( )</h3>
+		<div>
+			Create getter / setter pair for a property in the scene graph. Used internally by
+			[page:PropertyBinding.getValue getValue] and [page:PropertyBinding.setValue setValue].
+		</div>
 
-    <h3>[method:null unbind]( )</h3>
-    <div>
-      Unbind getter / setter pair for a property in the scene graph.
-    </div>
+		<h3>[method:null unbind]( )</h3>
+		<div>
+			Unbind getter / setter pair for a property in the scene graph.
+		</div>
 
-    <h3>[method:Constructor Composite]( targetGroup, path, optionalParsedPath )</h3>
-    <div>
-      Create a new Composite PropertyBinding.
-    </div>
+		<h3>[method:Constructor Composite]( targetGroup, path, optionalParsedPath )</h3>
+		<div>
+			Create a new Composite PropertyBinding.
+		</div>
 
-    <h3>[method:Constructor create]( root, path, parsedPath )</h3>
-    <div>
-      Create a new Composite PropertyBinding (if root is an [page:AnimationObjectGroup]) or PropertyBinding.
-    </div>
+		<h3>[method:Constructor create]( root, path, parsedPath )</h3>
+		<div>
+			Create a new Composite PropertyBinding (if root is an [page:AnimationObjectGroup]) or PropertyBinding.
+		</div>
 
-    <h3>[method:Constructor parseTrackName]( trackName )</h3>
-    <div>
-      Matches strings in the following forms:<br />
-      -- nodeName.property<br />
-      -- nodeName.property[accessor]<br />
-      -- nodeName.material.property[accessor]<br />
-      -- uuid.property[accessor]<br />
-      -- uuid.objectName[objectIndex].propertyName[propertyIndex]<br />
-      -- parentName/nodeName.property<br />
-      -- parentName/parentName/nodeName.property[index]<br />
-      -- .bone[Armature.DEF_cog].position<br />
-      -- scene:helium_balloon_model:helium_balloon_model.position
-    </div>
+		<h3>[method:Constructor parseTrackName]( trackName )</h3>
+		<div>
+			Matches strings in the following forms:<br />
+			-- nodeName.property<br />
+			-- nodeName.property[accessor]<br />
+			-- nodeName.material.property[accessor]<br />
+			-- uuid.property[accessor]<br />
+			-- uuid.objectName[objectIndex].propertyName[propertyIndex]<br />
+			-- parentName/nodeName.property<br />
+			-- parentName/parentName/nodeName.property[index]<br />
+			-- .bone[Armature.DEF_cog].position<br />
+			-- scene:helium_balloon_model:helium_balloon_model.position
+		</div>
 
-    <h3>[method:Constructor findNode]( root, nodeName )</h3>
-    <div>
-      Find a node in a node tree or [page:Skeleton Skeleton].
-    </div>
+		<h3>[method:Constructor findNode]( root, nodeName )</h3>
+		<div>
+			Find a node in a node tree or [page:Skeleton Skeleton].
+		</div>
 
 
 
 
-    <h2>Source</h2>
+		<h2>Source</h2>
 
 
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-  </body>
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
 </html>

+ 68 - 68
docs/api/animation/PropertyMixer.html

@@ -1,99 +1,99 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <base href="../../" />
-    <script src="list.js"></script>
-    <script src="page.js"></script>
-    <link type="text/css" rel="stylesheet" href="page.css" />
-  </head>
-  <body>
-    <h1>[name]</h1>
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
 
-    <div class="desc">
-    Buffered scene graph property that allows weighted accumulation; used internally.
-    </div>
+		<div class="desc">
+		Buffered scene graph property that allows weighted accumulation; used internally.
+		</div>
 
 
-    <h2>Constructor</h2>
+		<h2>Constructor</h2>
 
 
-    <h3>[name]( binding, typeName, valueSize )</h3>
-    <div>
-      -- binding <br />
-      -- typeName <br />
-      -- valueSize <br />
-    </div>
+		<h3>[name]( binding, typeName, valueSize )</h3>
+		<div>
+			-- binding <br />
+			-- typeName <br />
+			-- valueSize <br />
+		</div>
 
 
-    <h2>Properties</h2>
+		<h2>Properties</h2>
 
 
-    <h3>[property:Number binding]</h3>
-    <div>
+		<h3>[property:Number binding]</h3>
+		<div>
 
-    </div>
+		</div>
 
-    <h3>[property:Number buffer]</h3>
-    <div>
-      Buffer with size [page:PropertyMixer valueSize] * 4. <br /><br />
-      This has the layout: [ incoming | accu0 | accu1 | orig ]<br /><br />
-      Interpolators can use .buffer as their .result and the data then goes to 'incoming'.<br />
-      'accu0' and 'accu1' are used frame-interleaved for the cumulative result and
-      are compared to detect changes.<br />
-      'orig' stores the original state of the property.<br />
-    </div>
+		<h3>[property:Number buffer]</h3>
+		<div>
+			Buffer with size [page:PropertyMixer valueSize] * 4. <br /><br />
+			This has the layout: [ incoming | accu0 | accu1 | orig ]<br /><br />
+			Interpolators can use .buffer as their .result and the data then goes to 'incoming'.<br />
+			'accu0' and 'accu1' are used frame-interleaved for the cumulative result and
+			are compared to detect changes.<br />
+			'orig' stores the original state of the property.<br />
+		</div>
 
-    <h3>[property:Number cumulativeWeight]</h3>
-    <div>
-      Default is *0*.
-    </div>
+		<h3>[property:Number cumulativeWeight]</h3>
+		<div>
+			Default is *0*.
+		</div>
 
-    <h3>[property:Number valueSize]</h3>
-    <div>
+		<h3>[property:Number valueSize]</h3>
+		<div>
 
-    </div>
+		</div>
 
-    <h3>[property:Number referenceCount]</h3>
-    <div>
-      Default is *0*.
-    </div>
+		<h3>[property:Number referenceCount]</h3>
+		<div>
+			Default is *0*.
+		</div>
 
-    <h3>[property:Number useCount]</h3>
-    <div>
-      Default is *0*.
-    </div>
+		<h3>[property:Number useCount]</h3>
+		<div>
+			Default is *0*.
+		</div>
 
 
-    <h2>Methods</h2>
+		<h2>Methods</h2>
 
 
-    <h3>[method:null accumulate]( accuIndex, weight )</h3>
-    <div>
-      Accumulate data in [page:PropertyMixer.buffer buffer][accuIndex] 'incoming' region into 'accu[i]'.<br />
+		<h3>[method:null accumulate]( accuIndex, weight )</h3>
+		<div>
+			Accumulate data in [page:PropertyMixer.buffer buffer][accuIndex] 'incoming' region into 'accu[i]'.<br />
 
-      If weight is *0* this does nothing.
-    </div>
+			If weight is *0* this does nothing.
+		</div>
 
-    <h3>[method:null apply]( accuIndex )</h3>
-    <div>
-      Apply the state of [page:PropertyMixer.buffer buffer] 'accu[i]' to the binding when accus differ.
-    </div>
+		<h3>[method:null apply]( accuIndex )</h3>
+		<div>
+			Apply the state of [page:PropertyMixer.buffer buffer] 'accu[i]' to the binding when accus differ.
+		</div>
 
-    <h3>[method:null saveOriginalState]( )</h3>
-    <div>
-      Remember the state of the bound property and copy it to both accus.
-    </div>
+		<h3>[method:null saveOriginalState]( )</h3>
+		<div>
+			Remember the state of the bound property and copy it to both accus.
+		</div>
 
-    <h3>[method:null restoreOriginalState](  )</h3>
-    <div>
-      Apply the state previously taken via 'saveOriginalState' to the binding.
-    </div>
+		<h3>[method:null restoreOriginalState](  )</h3>
+		<div>
+			Apply the state previously taken via 'saveOriginalState' to the binding.
+		</div>
 
 
-    <h2>Source</h2>
+		<h2>Source</h2>
 
 
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-  </body>
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
 </html>

+ 75 - 75
docs/api/animation/tracks/BooleanKeyframeTrack.html

@@ -1,78 +1,78 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <base href="../../../" />
-    <script src="list.js"></script>
-    <script src="page.js"></script>
-    <link type="text/css" rel="stylesheet" href="page.css" />
-  </head>
-  <body>
-
-	[page:KeyframeTrack] &rarr;
-
-    <h1>[name]</h1>
-
-    <div class="desc">
-     A Track of boolean keyframe values.
-    </div>
-
-
-    <h2>Constructor</h2>
-
-
-    <h3>[name]( [page:String name], [page:Array times], [page:Array values] )</h3>
-    <div>
-      [page:String name] - (required) identifier for the KeyframeTrack.<br />
-      [page:Array times] - (required) array of keyframe times.<br />
-      [page:Array values] - values for the keyframes at the times specified.<br />
-    </div>
-    
-
-    <h2>Properties</h2>
-    
-    
-    <div class="desc">
-		See [page:KeyframeTrack] for inherited properties.
-    </div>
-
-    <h3>[property:Constant DefaultInterpolation]</h3>
-    <div>
-    The default interpolation type to use, [page:Animation InterpolateDiscrete].
-    </div>
-
-    <h3>[property:Array ValueBufferType]</h3>
-    <div>
-        A normal Array (no Float32Array in this case, unlike *ValueBufferType* of [page:KeyframeTrack]).
-    </div>
-
-    <h3>[property:String ValueTypeName]</h3>
-    <div>
-        String 'bool'.
-    </div>
-    
-
-    <h2>Methods</h2>
-    
-    
-    <div class="desc">
-		See [page:KeyframeTrack] for inherited methods.
-    </div>
-    
-    <h3>[method:null InterpolantFactoryMethodLinear ]()</h3>
-    <div>
-        The value of this method here is 'undefined', as it does not make sense for discrete properties.
-    </div>
-
-    <h3>[method:null InterpolantFactoryMethodSmooth ]()</h3>
-    <div>
-        The value of this method here is 'undefined', as it does not make sense for discrete properties.
-    </div>
-
-
-    <h2>Source</h2>
-
-
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-  </body>
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+
+		[page:KeyframeTrack] &rarr;
+
+		<h1>[name]</h1>
+
+		<div class="desc">
+			A Track of boolean keyframe values.
+		</div>
+
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]( [page:String name], [page:Array times], [page:Array values] )</h3>
+		<div>
+			[page:String name] - (required) identifier for the KeyframeTrack.<br />
+			[page:Array times] - (required) array of keyframe times.<br />
+			[page:Array values] - values for the keyframes at the times specified.<br />
+		</div>
+
+
+		<h2>Properties</h2>
+
+
+		<div class="desc">
+			See [page:KeyframeTrack] for inherited properties.
+		</div>
+
+		<h3>[property:Constant DefaultInterpolation]</h3>
+		<div>
+			The default interpolation type to use, [page:Animation InterpolateDiscrete].
+		</div>
+
+		<h3>[property:Array ValueBufferType]</h3>
+		<div>
+			A normal Array (no Float32Array in this case, unlike *ValueBufferType* of [page:KeyframeTrack]).
+		</div>
+
+		<h3>[property:String ValueTypeName]</h3>
+		<div>
+			String 'bool'.
+		</div>
+
+
+		<h2>Methods</h2>
+
+
+		<div class="desc">
+			See [page:KeyframeTrack] for inherited methods.
+		</div>
+
+		<h3>[method:null InterpolantFactoryMethodLinear ]()</h3>
+		<div>
+			The value of this method here is 'undefined', as it does not make sense for discrete properties.
+		</div>
+
+		<h3>[method:null InterpolantFactoryMethodSmooth ]()</h3>
+		<div>
+			The value of this method here is 'undefined', as it does not make sense for discrete properties.
+		</div>
+
+
+		<h2>Source</h2>
+
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
 </html>

+ 59 - 59
docs/api/animation/tracks/ColorKeyframeTrack.html

@@ -1,62 +1,62 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <base href="../../../" />
-    <script src="list.js"></script>
-    <script src="page.js"></script>
-    <link type="text/css" rel="stylesheet" href="page.css" />
-  </head>
-  <body>
-
-    [page:KeyframeTrack] &rarr;
-
-    <h1>[name]</h1>
-
-    <div class="desc">
-        A Track of keyframe values that represent color changes.<br /><br />
-        The very basic implementation of this subclass has nothing special yet. However, this is the place 
-        for color space parameterization.
-    </div>
-
-
-    <h2>Constructor</h2>
-
-
-    <h3>[name]( [page:String name], [page:Array times], [page:Array values] )</h3>
-    <div>
-        [page:String name] - (required) identifier for the KeyframeTrack.<br />
-        [page:Array times] - (required) array of keyframe times.<br />
-        [page:Array values] - values for the keyframes at the times specified.<br />
-        [page:Constant interpolation] - the type of interpolation to use. See 
-        [page:Animation Animation Constants] for possible values. Default is 
-        [page:Animation InterpolateLinear].
-    </div>
-    
-
-    <h2>Properties</h2>
-    
-    
-    <div class="desc">
-		See [page:KeyframeTrack] for inherited properties.
-    </div>
-    
-    <h3>[property:String ValueTypeName]</h3>
-    <div>
-        String 'color'.
-    </div>
-    
-
-    <h2>Methods</h2>
-    
-    
-    <div class="desc">
-		See [page:KeyframeTrack] for inherited methods.
-    </div>
-
-    <h2>Source</h2>
-
-
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-  </body>
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+
+		[page:KeyframeTrack] &rarr;
+
+		<h1>[name]</h1>
+
+		<div class="desc">
+			A Track of keyframe values that represent color changes.<br /><br />
+			The very basic implementation of this subclass has nothing special yet. However, this is the place
+			for color space parameterization.
+		</div>
+
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]( [page:String name], [page:Array times], [page:Array values] )</h3>
+		<div>
+			[page:String name] - (required) identifier for the KeyframeTrack.<br />
+			[page:Array times] - (required) array of keyframe times.<br />
+			[page:Array values] - values for the keyframes at the times specified.<br />
+			[page:Constant interpolation] - the type of interpolation to use. See
+			[page:Animation Animation Constants] for possible values. Default is
+			[page:Animation InterpolateLinear].
+		</div>
+
+
+		<h2>Properties</h2>
+
+
+		<div class="desc">
+			See [page:KeyframeTrack] for inherited properties.
+		</div>
+
+		<h3>[property:String ValueTypeName]</h3>
+		<div>
+			String 'color'.
+		</div>
+
+
+		<h2>Methods</h2>
+
+
+		<div class="desc">
+			See [page:KeyframeTrack] for inherited methods.
+		</div>
+
+		<h2>Source</h2>
+
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
 </html>

+ 59 - 59
docs/api/animation/tracks/NumberKeyframeTrack.html

@@ -1,62 +1,62 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <base href="../../../" />
-    <script src="list.js"></script>
-    <script src="page.js"></script>
-    <link type="text/css" rel="stylesheet" href="page.css" />
-  </head>
-  <body>
-
-    [page:KeyframeTrack] &rarr;
-
-    <h1>[name]</h1>
-
-    <div class="desc">
-        A Track of numeric keyframe values.
-    </div>
-
-
-    <h2>Constructor</h2>
-
-
-    <h3>[name]( [page:String name], [page:Array times], [page:Array values] )</h3>
-    <div>
-        [page:String name] - (required) identifier for the KeyframeTrack.<br />
-        [page:Array times] - (required) array of keyframe times.<br />
-        [page:Array values] - values for the keyframes at the times specified.<br />
-        [page:Constant interpolation] - the type of interpolation to use. See 
-        [page:Animation Animation Constants] for possible values. Default is 
-        [page:Animation InterpolateLinear].
-    </div>
-    
-
-    <h2>Properties</h2>
-    
-    
-    <div class="desc">
-		See [page:KeyframeTrack] for inherited properties.
-    </div>
-    
-
-    <h3>[property:String ValueTypeName]</h3>
-    <div>
-        String 'number'.
-    </div>
-    
-
-    <h2>Methods</h2>
-    
-    
-    <div class="desc">
-		See [page:KeyframeTrack] for inherited methods.
-    </div>
-    
-
-    <h2>Source</h2>
-
-
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-  </body>
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+
+		[page:KeyframeTrack] &rarr;
+
+		<h1>[name]</h1>
+
+		<div class="desc">
+			A Track of numeric keyframe values.
+		</div>
+
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]( [page:String name], [page:Array times], [page:Array values] )</h3>
+		<div>
+			[page:String name] - (required) identifier for the KeyframeTrack.<br />
+			[page:Array times] - (required) array of keyframe times.<br />
+			[page:Array values] - values for the keyframes at the times specified.<br />
+			[page:Constant interpolation] - the type of interpolation to use. See
+			[page:Animation Animation Constants] for possible values. Default is
+			[page:Animation InterpolateLinear].
+		</div>
+
+
+		<h2>Properties</h2>
+
+
+		<div class="desc">
+			See [page:KeyframeTrack] for inherited properties.
+		</div>
+
+
+		<h3>[property:String ValueTypeName]</h3>
+		<div>
+			String 'number'.
+		</div>
+
+
+		<h2>Methods</h2>
+
+
+		<div class="desc">
+			See [page:KeyframeTrack] for inherited methods.
+		</div>
+
+
+		<h2>Source</h2>
+
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
 </html>

+ 53 - 53
docs/api/animation/tracks/QuaternionKeyframeTrack.html

@@ -1,73 +1,73 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <base href="../../../" />
-    <script src="list.js"></script>
-    <script src="page.js"></script>
-    <link type="text/css" rel="stylesheet" href="page.css" />
-  </head>
-  <body>
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
 
-    [page:KeyframeTrack] &rarr;
+		[page:KeyframeTrack] &rarr;
 
-    <h1>[name]</h1>
+		<h1>[name]</h1>
 
-    <div class="desc">
-        A Track of quaternion keyframe values.
-    </div>
+		<div class="desc">
+			A Track of quaternion keyframe values.
+		</div>
 
 
-    <h2>Constructor</h2>
+		<h2>Constructor</h2>
 
 
-    <h3>[name]( [page:String name], [page:Array times], [page:Array values] )</h3>
-    <div>
-        [page:String name] (required) identifier for the KeyframeTrack.<br />
-        [page:Array times] (required) array of keyframe times.<br />
-        [page:Array values] values for the keyframes at the times specified.<br />
-        [page:Constant interpolation] the type of interpolation to use. See 
-        [page:Animation Animation Constants] for possible values. Default is 
-        [page:Animation InterpolateLinear].
-    </div>
-    
+		<h3>[name]( [page:String name], [page:Array times], [page:Array values] )</h3>
+		<div>
+			[page:String name] (required) identifier for the KeyframeTrack.<br />
+			[page:Array times] (required) array of keyframe times.<br />
+			[page:Array values] values for the keyframes at the times specified.<br />
+			[page:Constant interpolation] the type of interpolation to use. See
+			[page:Animation Animation Constants] for possible values. Default is
+			[page:Animation InterpolateLinear].
+		</div>
 
-    <h2>Properties</h2>
-    
 
-    <div class="desc">
-    	See [page:KeyframeTrack] for inherited properties.
-    </div>
+		<h2>Properties</h2>
 
-    <h3>[property:Constant DefaultInterpolation]</h3>
-    <div>
-        The default interpolation type to use, [page:Animation InterpolateLinear].
-    </div>
 
-    <h3>[property:String ValueTypeName]</h3>
-    <div>
-        String 'quaternion'.
-    </div>
+		<div class="desc">
+			See [page:KeyframeTrack] for inherited properties.
+		</div>
 
+		<h3>[property:Constant DefaultInterpolation]</h3>
+		<div>
+			The default interpolation type to use, [page:Animation InterpolateLinear].
+		</div>
 
-    <h2>Methods</h2>
-    
-    
-    <div class="desc">
-		See [page:KeyframeTrack] for inherited methods.
-    </div>    
+		<h3>[property:String ValueTypeName]</h3>
+		<div>
+			String 'quaternion'.
+		</div>
 
-    <h3>[method:null InterpolantFactoryMethodLinear]()</h3>
-    <div>
-        Returns a new [page:QuaternionLinearInterpolant QuaternionLinearInterpolant] based on the 
-        [page:KeyframeTrack.values values], [page:KeyframeTrack.times times] and 
-        [page:KeyframeTrack.valueSize valueSize] of the keyframes.
-    </div>
 
+		<h2>Methods</h2>
 
-    <h2>Source</h2>
-    
 
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-  </body>
+		<div class="desc">
+			See [page:KeyframeTrack] for inherited methods.
+		</div>
+
+		<h3>[method:null InterpolantFactoryMethodLinear]()</h3>
+		<div>
+			Returns a new [page:QuaternionLinearInterpolant QuaternionLinearInterpolant] based on the 
+			[page:KeyframeTrack.values values], [page:KeyframeTrack.times times] and 
+			[page:KeyframeTrack.valueSize valueSize] of the keyframes.
+		</div>
+
+
+		<h2>Source</h2>
+
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
 </html>

+ 58 - 58
docs/api/animation/tracks/StringKeyframeTrack.html

@@ -1,81 +1,81 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <base href="../../../" />
-    <script src="list.js"></script>
-    <script src="page.js"></script>
-    <link type="text/css" rel="stylesheet" href="page.css" />
-  </head>
-  <body>
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
 
-	[page:KeyframeTrack] &rarr;
+		[page:KeyframeTrack] &rarr;
 
-    <h1>[name]</h1>
+		<h1>[name]</h1>
 
-    <div class="desc">
-        A Track of string keyframe values.
-    </div>
+		<div class="desc">
+			A Track of string keyframe values.
+		</div>
 
 
-    <h2>Constructor</h2>
+		<h2>Constructor</h2>
 
 
-    <h3>[name]( [page:String name], [page:Array times], [page:Array values] )</h3>
-    <div>
-        [page:String name] - (required) identifier for the KeyframeTrack.<br />
-        [page:Array times] - (required) array of keyframe times.<br />
-        [page:Array values] - values for the keyframes at the times specified.<br />
-        [page:Constant interpolation] - the type of interpolation to use. See 
-        [page:Animation Animation Constants] for possible values. Default is 
-        [page:Animation InterpolateDiscrete].
-    </div>
-    
+		<h3>[name]( [page:String name], [page:Array times], [page:Array values] )</h3>
+		<div>
+			[page:String name] - (required) identifier for the KeyframeTrack.<br />
+			[page:Array times] - (required) array of keyframe times.<br />
+			[page:Array values] - values for the keyframes at the times specified.<br />
+			[page:Constant interpolation] - the type of interpolation to use. See
+			[page:Animation Animation Constants] for possible values. Default is
+			[page:Animation InterpolateDiscrete].
+		</div>
 
-    <h2>Properties</h2>
-    
 
-    <div class="desc">
-    	See [page:KeyframeTrack] for inherited properties.
-    </div>    
+		<h2>Properties</h2>
 
-    <h3>[property:Constant DefaultInterpolation]</h3>
-    <div>
-        The default interpolation type to use, [page:Animation InterpolateDiscrete].
-    </div>
 
-    <h3>[property:Array ValueBufferType]</h3>
-    <div>
-        A normal Array (no Float32Array in this case, unlike *ValueBufferType* of [page:KeyframeTrack]).
-    </div>
+		<div class="desc">
+			See [page:KeyframeTrack] for inherited properties.
+		</div>
 
-    <h3>[property:String ValueTypeName]</h3>
-    <div>
-        String 'string'.
-    </div>
+		<h3>[property:Constant DefaultInterpolation]</h3>
+		<div>
+			The default interpolation type to use, [page:Animation InterpolateDiscrete].
+		</div>
 
+		<h3>[property:Array ValueBufferType]</h3>
+		<div>
+			A normal Array (no Float32Array in this case, unlike *ValueBufferType* of [page:KeyframeTrack]).
+		</div>
 
-    <h2>Methods</h2>
-    
+		<h3>[property:String ValueTypeName]</h3>
+		<div>
+			String 'string'.
+		</div>
 
-    <div class="desc">
-		See [page:KeyframeTrack] for inherited methods.
-    </div>    
 
-    <h3>[method:null InterpolantFactoryMethodLinear]()</h3>
-    <div>
-      The value of this method here is 'undefined', as it does not make sense for discrete properties.
-    </div>
+		<h2>Methods</h2>
 
-    <h3>[method:null InterpolantFactoryMethodSmooth]()</h3>
-    <div>
-      The value of this method here is 'undefined', as it does not make sense for discrete properties.
-    </div>
-    
 
-    <h2>Source</h2>
+		<div class="desc">
+			See [page:KeyframeTrack] for inherited methods.
+		</div>
 
+		<h3>[method:null InterpolantFactoryMethodLinear]()</h3>
+		<div>
+		  The value of this method here is 'undefined', as it does not make sense for discrete properties.
+		</div>
 
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-  </body>
+		<h3>[method:null InterpolantFactoryMethodSmooth]()</h3>
+		<div>
+		  The value of this method here is 'undefined', as it does not make sense for discrete properties.
+		</div>
+
+
+		<h2>Source</h2>
+
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
 </html>

+ 44 - 44
docs/api/animation/tracks/VectorKeyframeTrack.html

@@ -1,61 +1,61 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <base href="../../../" />
-    <script src="list.js"></script>
-    <script src="page.js"></script>
-    <link type="text/css" rel="stylesheet" href="page.css" />
-  </head>
-  <body>
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
 
-    [page:KeyframeTrack] &rarr;
+		[page:KeyframeTrack] &rarr;
 
-    <h1>[name]</h1>
+		<h1>[name]</h1>
 
-    <div class="desc">
-        A Track of vector keyframe values.
-    </div>
+		<div class="desc">
+			A Track of vector keyframe values.
+		</div>
 
 
-    <h2>Constructor</h2>
+		<h2>Constructor</h2>
 
 
-    <h3>[name]( [page:String name], [page:Array times], [page:Array values] )</h3>
-    <div>
-        [page:String name] - (required) identifier for the KeyframeTrack.<br />
-        [page:Array times] - (required) array of keyframe times.<br />
-        [page:Array values] - values for the keyframes at the times specified.<br />
-        [page:Constant interpolation] - the type of interpolation to use. See 
-        [page:Animation Animation Constants] for possible values. Default is 
-        [page:Animation InterpolateLinear].
-    </div>
-    
+		<h3>[name]( [page:String name], [page:Array times], [page:Array values] )</h3>
+		<div>
+			[page:String name] - (required) identifier for the KeyframeTrack.<br />
+			[page:Array times] - (required) array of keyframe times.<br />
+			[page:Array values] - values for the keyframes at the times specified.<br />
+			[page:Constant interpolation] - the type of interpolation to use. See 
+			[page:Animation Animation Constants] for possible values. Default is 
+			[page:Animation InterpolateLinear].
+		</div>
 
-    <h2>Properties</h2>
-    
 
-    <div class="desc">
-    	See [page:KeyframeTrack] for inherited properties.
-    </div>  
+		<h2>Properties</h2>
 
-    <h3>[property:String ValueTypeName]</h3>
-    <div>
-        String 'vector'.
-    </div>
-    
 
-    <h2>Methods</h2>
-    
-    
-    <div class="desc">
-		See [page:KeyframeTrack] for inherited methods.
-    </div>
-    
+		<div class="desc">
+			See [page:KeyframeTrack] for inherited properties.
+		</div>
 
-    <h2>Source</h2>
+		<h3>[property:String ValueTypeName]</h3>
+		<div>
+			String 'vector'.
+		</div>
 
 
-    [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-  </body>
+		<h2>Methods</h2>
+
+
+		<div class="desc">
+			See [page:KeyframeTrack] for inherited methods.
+		</div>
+
+
+		<h2>Source</h2>
+
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
 </html>

+ 7 - 1
docs/api/core/Object3D.html

@@ -86,7 +86,13 @@
 		<div>Optional name of the object (doesn't need to be unique). Default is an empty string.</div>
 
 		<h3>[property:Matrix3 normalMatrix]</h3>
-		<div>This is passed to the shader and used to calculate lighting for the object.</div>
+		<div>
+		This is passed to the shader and used to calculate lighting for the object. It is the transpose of the inverse of the upper left 3x3 sub-matrix of this object's modelViewMatrix.<br /><br />
+
+		The reason for this special matrix is that simply using the modelViewMatrix could result in a non-unit length of normals (on scaling) or in a non-perpendicular direction (on non-uniform scaling).<br /><br />
+
+		On the other hand the translation part of the modelViewMatrix is not relevant for the calculation of normals. Thus a Matrix3 is sufficient.
+		</div>
 
 		<h3>[property:function onAfterRender]</h3>
 		<div>

+ 21 - 20
docs/api/extras/core/Curve.html

@@ -11,14 +11,10 @@
 		<h1>[name]</h1>
 
 		<div class="desc">
-		An abstract base class for creating a curve object that contains methods for interpolation.
-		For an array of Curves see [page:CurvePath].
+		An abstract base class for creating a [name] object that contains methods for interpolation.
+		For an array of [name]s see [page:CurvePath].
 		</div>
 
-		<h2>Examples</h2>
-
-		<h3>[example:webgl_geometry_extrude_splines geometry / extrude / splines ]</h3>
-
 		<h2>Constructor</h2>
 
 
@@ -27,26 +23,31 @@
 		This constructor creates a new [name].
 		</div>
 
+		<h2>Properties</h2>
+
+		<h3>[property:Integer arcLengthDivisions]</h3>
+		<div>This value determines the amount of divisions when calculating the cumulative segment lengths of a curve via [page:.getLengths].
+			To ensure precision when using methods like [page:.getSpacedPoints], it is recommended to increase [page:.arcLengthDivisions] if the curve is very large. Default is 200.</div>
 
 		<h2>Methods</h2>
 
-		<h3>[method:Vector getPoint]( [page:Number t] )</h3>
-		<div>Returns a vector for point t of the curve where t is between 0 and 1. Must be implemented in the extending class.</div>
+		<h3>[method:Vector getPoint]( [page:Float t] )</h3>
+		<div>Returns a vector for point t of the curve where t is between 0 and 1. Must be implemented in the extending curve.</div>
 
-		<h3>[method:Vector getPointAt]( [page:Number u] )</h3>
+		<h3>[method:Vector getPointAt]( [page:Float u] )</h3>
 		<div>
 			Returns a vector for point at a relative position in curve according to arc length.
 			u is in the range [0, 1].
 		</div>
 
-		<h3>[method:Array getPoints]( [page:integer divisions] )</h3>
+		<h3>[method:Array getPoints]( [page:Integer divisions] )</h3>
 		<div>
 			divisions -- number of pieces to divide the curve into. Default is *5*.<br /><br />
 
 			Returns a set of divisions + 1 points using getPoint( t ).
 		</div>
 
-		<h3>[method:Array getSpacedPoints]( divisions )</h3>
+		<h3>[method:Array getSpacedPoints]( [page:Integer divisions] )</h3>
 		<div>
 			divisions -- number of pieces to divide the curve into. Default is *5*.<br /><br />
 
@@ -56,33 +57,33 @@
 		<h3>[method:Float getLength]()</h3>
 		<div>Get total curve arc length.</div>
 
-		<h3>[method:Array getLengths]( divisions )</h3>
+		<h3>[method:Array getLengths]( [page:Integer divisions] )</h3>
 		<div>Get list of cumulative segment lengths.</div>
 
 		<h3>[method:null updateArcLengths]()</h3>
 		<div>Update the cumlative segment distance cache.</div>
 
-		<h3>[method:Float getUtoTmapping]( [page:Number u], distance )</h3>
+		<h3>[method:Float getUtoTmapping]( [page:Float u], [page:Float distance] )</h3>
 		<div>
-			Given u in the range ( 0 .. 1 ), returns [page:Number t] also in the range ( 0 .. 1 ).
+			Given u in the range ( 0 .. 1 ), returns [page:Float t] also in the range ( 0 .. 1 ).
 			u and t can then be used to give you points which are equidistant from the ends of the curve,
 			using [page:.getPoint].
 		 </div>
 
-		<h3>[method:Vector getTangent]( [page:Number t] )</h3>
+		<h3>[method:Vector getTangent]( [page:Float t] )</h3>
 		<div>
-			Returns a unit vector tangent at t. If the subclassed curve do not implement its
-			tangent derivation, 2 points a small delta apart will be used to find its gradient
-			which seems to give a reasonable approximation
+			Returns a unit vector tangent at t. If the derived curve does not implement its
+			tangent derivation, two points a small delta apart will be used to find its gradient
+			which seems to give a reasonable approximation.
 		</div>
 
-		<h3>[method:Vector getTangentAt]( [page:Number u] )</h3>
+		<h3>[method:Vector getTangentAt]( [page:Float u] )</h3>
 		<div>
 			Returns tangent at a point which is equidistant to the ends of the curve from the
 			point given in [page:.getTangent].
 		</div>
 
-		<h3>[method:Object computeFrenetFrames]( segments, closed )</h3>
+		<h3>[method:Object computeFrenetFrames]( [page:Integer segments], [page:Boolean closed] )</h3>
 		<div>
 		Generates the Frenet Frames. Used in geometries like [page:TubeGeometry] or [page:ExtrudeGeometry].
 		</div>

+ 3 - 1
docs/api/geometries/TubeBufferGeometry.html

@@ -35,7 +35,9 @@
 		<h2>Example</h2>
 
 		<code>
-		function CustomSinCurve( scale ){
+		function CustomSinCurve( scale ) {
+
+			THREE.Curve.call( this );
 
 			this.scale = ( scale === undefined ) ? 1 : scale;
 

+ 3 - 1
docs/api/geometries/TubeGeometry.html

@@ -35,7 +35,9 @@
 		<h2>Example</h2>
 
 		<code>
-		function CustomSinCurve( scale ){
+		function CustomSinCurve( scale ) {
+
+			THREE.Curve.call( this );
 
 			this.scale = ( scale === undefined ) ? 1 : scale;
 

+ 231 - 231
docs/api/loaders/ObjectLoader.html

@@ -1,232 +1,232 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
+<!DOCTYPE html>
+<html lang="en">
+	<head>
 		<meta charset="utf-8" />
-		<base href="../../" />
-		<script src="list.js"></script>
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		<h1>[name]</h1>
-
-		<div class="desc">
-			A loader for loading a JSON resource. Unlike the [page:JSONLoader], this one make use of the
-			<em>.type</em> attributes of objects to map them to their original classes.<br /><br />
-
-			Note that this loader can't load [page:Geometries]. Use [page:JSONLoader] instead for that.<br /><br />
-
-			This uses the [page:FileLoader] internally for loading files.
-		</div>
-
-		<h2>Example</h2>
-
-		<div>
-
-			[example:webgl_animation_scene WebGL / animation / scene]<br />
-			[example:webgl_loader_json_claraio WebGL / loader / json / claraio]<br />
-			[example:webgl_loader_msgpack WebGL / loader / msgpack]
-		</div>
-
-		<code>
-var loader = new THREE.ObjectLoader();
-
-loader.load(
-    // resource URL
-    "models/json/example.json"
-
-    // pass the loaded data to the onLoad function.
-		//Here it is assumed to be an object
-    function ( obj ) {
-				//add the loaded object to the scene
-        scene.add( obj );
-    },
-
-    // Function called when download progresses
-    function ( xhr ) {
-        console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
-    },
-
-    // Function called when download errors
-    function ( xhr ) {
-        console.error( 'An error happened' );
-    }
-);
-
-
-// Alternatively, to parse a previously loaded JSON structure
-var object = loader.parse( a_json_object );
-
-scene.add( object );
-		</code>
-
-
-
-		<h2>Constructor</h2>
-
-		<h3>[name]( [page:LoadingManager manager] )</h3>
-		<div>
-		[page:LoadingManager manager] — The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].<br /><br />
-
-		Creates a new [name].
-		</div>
-
-
-		<h2>Properties</h2>
-
-		<h3>[property:String crossOrigin]</h3>
-		<div>
-		If set, assigns the [link:https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes crossOrigin]
-	 attribute of the image to the value of *crossOrigin*,
-		prior to starting the load. Default is *undefined*.
-		</div>
-
-		<h3>[property:LoadingManager manager]</h3>
-		<div>
-			The [page:LoadingManager loadingManager]  the loader is using. Default is [page:DefaultLoadingManager].
-		</div>
-
-		<h3>[property:String texturePath]</h3>
-		<div>
-			The base path or URL from which textures will be loaded. See [page:.setTexturePath].
-			Default is the empty string.
-		</div>
-
-		<h2>Methods</h2>
-
-		<h3>[method:null load]( [page:String url], [page:Function onLoad], [page:Function onProgress], [page:Function onError] )</h3>
-		<div>
-		[page:String url] — the path or URL to the file. This can also be a
-			[link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs Data URI].<br />
-		[page:Function onLoad] — Will be called when load completes. The argument will be the loaded [page:Object3D object].<br />
-		[page:Function onProgress] — Will be called while load progresses. The argument will be the XmlHttpRequest instance, that contain .[page:Integer total] and .[page:Integer loaded] bytes.<br />
-		[page:Function onError] — Will be called when load errors.<br />
-		</div>
-		<div>
-		Begin loading from url and call onLoad with the parsed response content.
-		</div>
-
-
-		<h3>[method:Object3D parse]( [page:Object json], [page:Function onLoad]  )</h3>
-		<div>
-		[page:Object json] — required. The JSON source to parse.<br /><br />
-		[page:Function onLoad] — Will be called when parsed completes. The argument will be the parsed [page:Object3D object].<br />
-
-		Parse a <em>JSON</em> structure and return a threejs object.
-		This is used internally by [page:.load], but can also be used directly to parse
-		a previously loaded JSON structure.
-		</div>
-
-		<h3>[method:Object3D parseGeometries]( [page:Object json] )</h3>
-		<div>
-		[page:Object json] — required. The JSON source to parse.<br /><br />
-
-		This is used [page:.parse] to parse any [page:Geometry geometries] or [page:BufferGeometry buffer geometries]  in the JSON structure.
-		Internally it uses [page:JSONLoader] for geometries and [page:BufferGeometryLoader] for buffer geometries.
-		</div>
-
-		<h3>[method:Object3D parseMaterials]( [page:Object json] )</h3>
-		<div>
-		[page:Object json] — required. The JSON source to parse.<br /><br />
-
-		This is used [page:.parse] to parse any materials in the JSON structure using [page:MaterialLoader].
-		</div>
-
-		<h3>[method:Object3D parseAnimations]( [page:Object json] )</h3>
-		<div>
-		[page:Object json] — required. The JSON source to parse.<br /><br />
-
-		This is used [page:.parse] to parse any animations in the JSON structure, using [page:AnimationClip.parse].
-		</div>
-
-		<h3>[method:Object3D parseImages]( [page:Object json] )</h3>
-		<div>
-		[page:Object json] — required. The JSON source to parse.<br /><br />
-
-		This is used [page:.parse] to parse any images in the JSON structure, using [page:ImageLoader].
-		</div>
-
-		<h3>[method:Object3D parseTextures]( [page:Object json] )</h3>
-		<div>
-		[page:Object json] — required. The JSON source to parse.<br /><br />
-
-		This is used [page:.parse] to parse any textures in the JSON structure.
-		</div>
-
-		<h3>[method:Object3D parseObject]( [page:Object json] )</h3>
-		<div>
-		[page:Object json] — required. The JSON source to parse.<br /><br />
-
-		This is used [page:.parse] to parse any objects in the JSON structure.
-		Objects can be of the following types:
-
-		<ul>
-			<li>
-				[page:Scene]
-			</li>
-			<li>
-				[page:PerspectiveCamera]
-			</li>
-			<li>
-				[page:OrthographicCamera]
-			</li>
-			<li>
-				[page:AmbientLight]
-			</li>
-			<li>
-				[page:DirectionalLight]
-			</li>
-			<li>
-				[page:PointLight]
-			</li>
-			<li>
-				[page:SpotLight]
-			</li>
-			<li>
-				[page:HemisphereLight]
-			</li>
-			<li>
-				[page:Mesh]
-			</li>
-			<li>
-				[page:LOD]
-			</li>
-			<li>
-				[page:Line]
-			</li>
-			<li>
-				[page:LineSegments]
-			</li>
-			<li>
-				[page:Points]
-			</li>
-			<li>
-				[page:Sprite]
-			</li>
-			<li>
-				[page:Group]
-			</li>
-			<li>
-				[page:Object3D]
-			</li>
-
-		</ul>
-		</div>
-
-		<h3>[method:null setCrossOrigin]( [page:String value] )</h3>
-		<div>
-		[page:String value] — The crossOrigin string to implement CORS for loading the url from a different domain that allows CORS.
-		</div>
-
-		<h3>[method:null setTexturePath]( [page:String value] )</h3>
-		<div>
-		[page:String value] — The base path or URL from which textures will be loaded.<br /><br />
-
-
-		</div>
-
-		<h2>Source</h2>
-
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-	</body>
-</html>
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<div class="desc">
+			A loader for loading a JSON resource. Unlike the [page:JSONLoader], this one make use of the
+			<em>.type</em> attributes of objects to map them to their original classes.<br /><br />
+
+			Note that this loader can't load [page:Geometries]. Use [page:JSONLoader] instead for that.<br /><br />
+
+			This uses the [page:FileLoader] internally for loading files.
+		</div>
+
+		<h2>Example</h2>
+
+		<div>
+
+			[example:webgl_animation_scene WebGL / animation / scene]<br />
+			[example:webgl_loader_json_claraio WebGL / loader / json / claraio]<br />
+			[example:webgl_loader_msgpack WebGL / loader / msgpack]
+		</div>
+
+		<code>
+var loader = new THREE.ObjectLoader();
+
+loader.load(
+    // resource URL
+    "models/json/example.json",
+
+    // pass the loaded data to the onLoad function.
+		//Here it is assumed to be an object
+    function ( obj ) {
+				//add the loaded object to the scene
+        scene.add( obj );
+    },
+
+    // Function called when download progresses
+    function ( xhr ) {
+        console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
+    },
+
+    // Function called when download errors
+    function ( xhr ) {
+        console.error( 'An error happened' );
+    }
+);
+
+
+// Alternatively, to parse a previously loaded JSON structure
+var object = loader.parse( a_json_object );
+
+scene.add( object );
+		</code>
+
+
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [page:LoadingManager manager] )</h3>
+		<div>
+		[page:LoadingManager manager] — The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].<br /><br />
+
+		Creates a new [name].
+		</div>
+
+
+		<h2>Properties</h2>
+
+		<h3>[property:String crossOrigin]</h3>
+		<div>
+		If set, assigns the [link:https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes crossOrigin]
+	 attribute of the image to the value of *crossOrigin*,
+		prior to starting the load. Default is *undefined*.
+		</div>
+
+		<h3>[property:LoadingManager manager]</h3>
+		<div>
+			The [page:LoadingManager loadingManager]  the loader is using. Default is [page:DefaultLoadingManager].
+		</div>
+
+		<h3>[property:String texturePath]</h3>
+		<div>
+			The base path or URL from which textures will be loaded. See [page:.setTexturePath].
+			Default is the empty string.
+		</div>
+
+		<h2>Methods</h2>
+
+		<h3>[method:null load]( [page:String url], [page:Function onLoad], [page:Function onProgress], [page:Function onError] )</h3>
+		<div>
+		[page:String url] — the path or URL to the file. This can also be a
+			[link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs Data URI].<br />
+		[page:Function onLoad] — Will be called when load completes. The argument will be the loaded [page:Object3D object].<br />
+		[page:Function onProgress] — Will be called while load progresses. The argument will be the XmlHttpRequest instance, that contain .[page:Integer total] and .[page:Integer loaded] bytes.<br />
+		[page:Function onError] — Will be called when load errors.<br />
+		</div>
+		<div>
+		Begin loading from url and call onLoad with the parsed response content.
+		</div>
+
+
+		<h3>[method:Object3D parse]( [page:Object json], [page:Function onLoad]  )</h3>
+		<div>
+		[page:Object json] — required. The JSON source to parse.<br /><br />
+		[page:Function onLoad] — Will be called when parsed completes. The argument will be the parsed [page:Object3D object].<br />
+
+		Parse a <em>JSON</em> structure and return a threejs object.
+		This is used internally by [page:.load], but can also be used directly to parse
+		a previously loaded JSON structure.
+		</div>
+
+		<h3>[method:Object3D parseGeometries]( [page:Object json] )</h3>
+		<div>
+		[page:Object json] — required. The JSON source to parse.<br /><br />
+
+		This is used [page:.parse] to parse any [page:Geometry geometries] or [page:BufferGeometry buffer geometries]  in the JSON structure.
+		Internally it uses [page:JSONLoader] for geometries and [page:BufferGeometryLoader] for buffer geometries.
+		</div>
+
+		<h3>[method:Object3D parseMaterials]( [page:Object json] )</h3>
+		<div>
+		[page:Object json] — required. The JSON source to parse.<br /><br />
+
+		This is used [page:.parse] to parse any materials in the JSON structure using [page:MaterialLoader].
+		</div>
+
+		<h3>[method:Object3D parseAnimations]( [page:Object json] )</h3>
+		<div>
+		[page:Object json] — required. The JSON source to parse.<br /><br />
+
+		This is used [page:.parse] to parse any animations in the JSON structure, using [page:AnimationClip.parse].
+		</div>
+
+		<h3>[method:Object3D parseImages]( [page:Object json] )</h3>
+		<div>
+		[page:Object json] — required. The JSON source to parse.<br /><br />
+
+		This is used [page:.parse] to parse any images in the JSON structure, using [page:ImageLoader].
+		</div>
+
+		<h3>[method:Object3D parseTextures]( [page:Object json] )</h3>
+		<div>
+		[page:Object json] — required. The JSON source to parse.<br /><br />
+
+		This is used [page:.parse] to parse any textures in the JSON structure.
+		</div>
+
+		<h3>[method:Object3D parseObject]( [page:Object json] )</h3>
+		<div>
+		[page:Object json] — required. The JSON source to parse.<br /><br />
+
+		This is used [page:.parse] to parse any objects in the JSON structure.
+		Objects can be of the following types:
+
+		<ul>
+			<li>
+				[page:Scene]
+			</li>
+			<li>
+				[page:PerspectiveCamera]
+			</li>
+			<li>
+				[page:OrthographicCamera]
+			</li>
+			<li>
+				[page:AmbientLight]
+			</li>
+			<li>
+				[page:DirectionalLight]
+			</li>
+			<li>
+				[page:PointLight]
+			</li>
+			<li>
+				[page:SpotLight]
+			</li>
+			<li>
+				[page:HemisphereLight]
+			</li>
+			<li>
+				[page:Mesh]
+			</li>
+			<li>
+				[page:LOD]
+			</li>
+			<li>
+				[page:Line]
+			</li>
+			<li>
+				[page:LineSegments]
+			</li>
+			<li>
+				[page:Points]
+			</li>
+			<li>
+				[page:Sprite]
+			</li>
+			<li>
+				[page:Group]
+			</li>
+			<li>
+				[page:Object3D]
+			</li>
+
+		</ul>
+		</div>
+
+		<h3>[method:null setCrossOrigin]( [page:String value] )</h3>
+		<div>
+		[page:String value] — The crossOrigin string to implement CORS for loading the url from a different domain that allows CORS.
+		</div>
+
+		<h3>[method:null setTexturePath]( [page:String value] )</h3>
+		<div>
+		[page:String value] — The base path or URL from which textures will be loaded.<br /><br />
+
+
+		</div>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 6 - 0
docs/api/materials/Material.html

@@ -198,6 +198,12 @@
 		Default is *false*.
 		</div>
 
+		<h3>[property:Boolean dithering]</h3>
+		<div>
+		Whether to apply dithering to the color to remove the appearance of banding.
+		Default is *false*.
+		</div>
+
 		<h3>[property:Integer shading]</h3>
 		<div>
 		Defines how the material is shaded.

+ 37 - 37
docs/api/math/Color.html

@@ -44,22 +44,22 @@ var color = new THREE.Color( 1, 0, 0 );
 		<h2>Constructor</h2>
 
 
-		<h3>[name]( [page:Multi r], [page:Float g], [page:Float b] )</h3>
+		<h3>[name]( [page:Color_Hex_or_String r], [page:Float g], [page:Float b] )</h3>
 		<div>
-		[page:Multi r] - (optional) the red component of the color if arguments g and b are defined.
-		If they are not defined, it can be a [link:https://en.wikipedia.org/wiki/Web_colors#Hex_triplet hexadecimal triplet] (recommended) or a CSS-style string or another Color instance.<br />
-		[page:Float g] - (optional) The green component of the color if it is defined.<br />
-		[page:Float b] - (optional) The blue component of the color if it is defined.<br /><br />
+		[page:Color_Hex_or_String r] - (optional) If arguments [page:Float g] and [page:Float b] are defined, the red component of the color.
+		If they are not defined, it can be a [link:https://en.wikipedia.org/wiki/Web_colors#Hex_triplet hexadecimal triplet] (recommended), a CSS-style string, or another Color instance.<br />
+		[page:Float g] - (optional) If it is defined, the green component of the color.<br />
+		[page:Float b] - (optional) If it is defined, the blue component of the color.<br /><br />
 
 		Note that standard method of specifying color in three.js is with a [link:https://en.wikipedia.org/wiki/Web_colors#Hex_triplet hexadecimal triplet], and that method is used
 		throughout the rest of the documentation.<br /><br />
 
-		When all arguments are defined then r is the red component, g is the green component and b is the blue component of the color.<br />
-		When only r is defined:<br />
+		When all arguments are defined then [page:Color_Hex_or_String r] is the red component, [page:Float g] is the green component and [page:Float b] is the blue component of the color.<br />
+		When only [page:Color_Hex_or_String r] is defined:<br />
 		<ul>
 			<li>It can be a [link:https://en.wikipedia.org/wiki/Web_colors#Hex_triplet hexadecimal triplet] representing the color (recommended).</li>
 			<li>It can be an another Color instance.</li>
-			<li>It can be a CSS style string. For Instance:
+			<li>It can be a CSS-style string. For example:
 				<ul>
 					<li>'rgb(250, 0,0)'</li>
 					<li>'rgb(100%,0%,0%)'</li>
@@ -114,7 +114,7 @@ var color = new THREE.Color( 1, 0, 0 );
 		<div>Adds [page:Number s] to the RGB values of this color.</div>
 
 		<h3>[method:Color clone]() </h3>
-		<div>Returns a new Color with the same  [page:.r r], [page:.g g] and [page:.b b] parameters a this one.</div>
+		<div>Returns a new Color with the same [page:.r r], [page:.g g] and [page:.b b] values as this one.</div>
 
 		<h3>[method:Color copy]( [page:Color color] ) </h3>
 		<div>
@@ -122,17 +122,17 @@ var color = new THREE.Color( 1, 0, 0 );
 		</div>
 
 		<h3>[method:Color convertGammaToLinear]() </h3>
-		<div>Converts this color from gamma to linear space (squares the values of [page:.r r], [page:.g g] and [page:.b b] ).</div>
+		<div>Converts this color from gamma to linear space by squaring the values of [page:.r r], [page:.g g] and [page:.b b] ).</div>
 
 		<h3>[method:Color convertLinearToGamma]() </h3>
-		<div>Converts this color from linear to gamma space (takes the squareroot of [page:.r r], [page:.g g] and [page:.b b]).</div>
+		<div>Converts this color from linear to gamma space by taking the square root of [page:.r r], [page:.g g] and [page:.b b]).</div>
 
 		<h3>[method:Color copyGammaToLinear]( [page:Color color], [page:Float gammaFactor] ) </h3>
 		<div>
 		[page:Color color] — Color to copy.<br />
 		[page:Float gammaFactor] - (optional). Default is *2.0*.<br /><br />
 
-		Copies given color making conversion from gamma to linear space,
+		Copies the given color into this color while converting it from gamma to linear space
 		by taking [page:.r r], [page:.g g] and [page:.b b] to the power of [page:Float gammaFactor].
 		</div>
 
@@ -141,16 +141,16 @@ var color = new THREE.Color( 1, 0, 0 );
 		[page:Color color] — Color to copy.<br />
 		[page:Float gammaFactor] - (optional). Default is *2.0*.<br /><br />
 
-		Copies given color making conversion from linear to gamma space,
+		Copies the given color into this color while converting it from linear to gamma space
 		by taking [page:.r r], [page:.g g] and [page:.b b] to the power of 1 / [page:Float gammaFactor].
 		</div>
 
 		<h3>[method:Boolean equals]( [page:Color color] ) </h3>
-		<div>Compares [page:Color color] with this one and returns true if they are the same, false otherwise.</div>
+		<div>Compares the RGB values of [page:Color color] with those of this object. Returns true if they are the same, false otherwise.</div>
 
 		<h3>[method:Color fromArray]( [page:Array array], [page:Integer offset] ) </h3>
 		<div>
-		[page:Array array] - [page:Array] of float in the form [ [page:Float r], [page:Float g], [page:Float b] ].<br />
+		[page:Array array] - [page:Array] of floats in the form [ [page:Float r], [page:Float g], [page:Float b] ].<br />
 		[page:Integer offset] - An optional offset into the array.<br /><br />
 
 		Sets this color's components based on an array formatted like [ [page:Float r], [page:Float g], [page:Float b] ].
@@ -160,14 +160,14 @@ var color = new THREE.Color( 1, 0, 0 );
 		<div>Returns the hexadecimal value of this color.</div>
 
 		<h3>[method:String getHexString]()</h3>
-		<div>Returns the string formatted hexadecimal value of this color.</div>
+		<div>Returns the hexadecimal value of this color as a string (for example, 'FFFFFF').</div>
 
 		<h3>[method:Object getHSL]( [page:Object optionalTarget] )</h3>
 		<div>
-			[page:Object optionalTarget] — (optional) if specified, adds h, s and l keys to object (if not already present)
-			and sets the results there, otherwise a new Object will be created. <br /><br />
+			[page:Object optionalTarget] — (optional) if specified, adds h, s and l keys to the object (if not already present)
+			and sets the results there. Otherwise, a new Object will be created. <br /><br />
 
-			Convert the values [page:.r r], [page:.g g] and [page:.b b] to [link:https://en.wikipedia.org/wiki/HSL_and_HSV HSL]
+			Convert this Color's [page:.r r], [page:.g g] and [page:.b b] values to [link:https://en.wikipedia.org/wiki/HSL_and_HSV HSL]
 			format and returns an object of the form:
 
 			<code>
@@ -177,37 +177,37 @@ var color = new THREE.Color( 1, 0, 0 );
 		</div>
 
 		<h3>[method:String getStyle]()</h3>
-		<div>Returns the value of this color as a CSS-style string. Example: 'rgb(255,0,0)'.</div>
+		<div>Returns the value of this color as a CSS style string. Example: 'rgb(255,0,0)'.</div>
 
 		<h3>[method:Color lerp]( [page:Color color], [page:Float alpha] ) </h3>
 		<div>
 		[page:Color color] - color to converge on.<br />
 		[page:Float alpha] - interpolation factor in the closed interval [0, 1].<br /><br />
 
-		Linear interpolation of this colors RGB values and the RGB values of the passed argument.
-		The alpha argument can be thought of as the percent between the two colors, where 0 is
-		this color and 1 is the first argument.
+		Linearly interpolates this color's RGB values toward the RGB values of the passed argument.
+		The alpha argument can be thought of as the ratio between the two colors, where 0.0 is
+		this color and 1.0 is the first argument.
 		</div>
 
 		<h3>[method:Color multiply]( [page:Color color] ) </h3>
-		<div>Multiplies this color's RGB values by given [page:Color color]'s RGB values.</div>
+		<div>Multiplies this color's RGB values by the given [page:Color color]'s RGB values.</div>
 
 		<h3>[method:Color multiplyScalar]( [page:Number s] ) </h3>
 		<div>Multiplies this color's RGB values by [page:Number s].</div>
 
 		<h3>[method:Color offsetHSL]( [page:Float h], [page:Float s], [page:Float l] ) </h3>
 		<div>
-			Adds given [page:Float h], [page:Float s], and [page:Float l] to this color's existing values.
-			Internally this converts the [page:.r r], [page:.g g] and [page:.b b] values to HSL, adds
-			[page:Float h], [page:Float s], and [page:Float l] and then converts back to RGB.
+			Adds the given [page:Float h], [page:Float s], and [page:Float l] to this color's values.
+			Internally, this converts the color's [page:.r r], [page:.g g] and [page:.b b] values to HSL, adds
+			[page:Float h], [page:Float s], and [page:Float l], and then converts the color back to RGB.
 		</div>
 
-		<h3>[method:Color set]( [page:Multi value] ) </h3>
+		<h3>[method:Color set]( [page:Color_Hex_or_String value] ) </h3>
 		<div>
-		[page:Multi value] - Value to set this color to.<br /><br />
+		[page:Color_Hex_or_String value] - Value to set this color to.<br /><br />
 
-		See the Constructor above for full details of what [page:Multi value] can be.
-		Delegates to [page:.copy], .setStyle, or .setHex depending on input type.
+		See the Constructor above for full details of what [page:Color_Hex_or_String value] can be.
+		Delegates to [page:.copy], [page:.setStyle], or [page:.setHex] depending on input type.
 		</div>
 
 		<h3>[method:Color setHex]( [page:Integer hex] ) </h3>
@@ -228,9 +228,9 @@ var color = new THREE.Color( 1, 0, 0 );
 
 		<h3>[method:Color setRGB]( [page:Float r], [page:Float g], [page:Float b] ) </h3>
 		<div>
-		[page:Float r] — Red channel value between 0 and 1.<br />
-		[page:Float g] — Green channel value between 0 and 1.<br />
-		[page:Float b] — Blue channel value between 0 and 1.<br /><br />
+		[page:Float r] — Red channel value between 0.0 and 1.0.<br />
+		[page:Float g] — Green channel value between 0.0 and 1.0.<br />
+		[page:Float b] — Blue channel value between 0.0 and 1.0.<br /><br />
 
 		Sets this color from RGB values.
 		</div>
@@ -255,7 +255,7 @@ var color = new THREE.Color( 1, 0, 0 );
 		"red" ( or any [link:https://en.wikipedia.org/wiki/X11_color_names#Color_name_chart X11 color name]
 		- all 140 color names are supported ).<br />
 
-		Transluent colors such as "rgba(255, 0, 0, 0.5)" and "hsla(0, 100%, 50%, 0.5)" are also accepted,
+		Translucent colors such as "rgba(255, 0, 0, 0.5)" and "hsla(0, 100%, 50%, 0.5)" are also accepted,
 		but the alpha-channel coordinate will be discarded.<br /><br />
 
 		Note that for X11 color names, multiple words such as Dark Orange become the string 'darkorange' (all lowercase).
@@ -263,8 +263,8 @@ var color = new THREE.Color( 1, 0, 0 );
 
 		<h3>[method:Color sub]( [page:Color color] ) </h3>
 		<div>
-		Subtracts RGB components of the given color from the RGB components of this color.
-		If a component is negative, it is set to zero.
+		Subtracts the RGB components of the given color from the RGB components of this color.
+		If this results in a negative component, that component is set to zero.
 		</div>
 
 		<h3>[method:Array toArray]( [page:Array array], [page:Integer offset] ) </h3>

+ 12 - 0
docs/api/math/Matrix3.html

@@ -92,6 +92,9 @@ m.elements = [ 11, 21, 31,
 		[link:https://en.wikipedia.org/wiki/Determinant determinant] of this matrix.
 		</div>
 
+		<h3>[method:Boolean equals]( [page:Matrix3 m] )</h3>
+		<div>Return true if this matrix and [page:Matrix3 m] are equal.</div>
+
 		<h3>[method:Matrix3 fromArray]( [page:Array array], [page:Integer offset] )</h3>
 		<div>
 		[page:Array array] - the array to read the elements from.<br />
@@ -132,6 +135,12 @@ m.elements = [ 11, 21, 31,
 
 		</div>
 
+		<h3>[method:Matrix3 multiply]( [page:Matrix3 m] )</h3>
+		<div>Post-multiplies this matrix by [page:Matrix3 m].</div>
+
+		<h3>[method:Matrix3 multiplyMatrices]( [page:Matrix3 a], [page:Matrix3 b] )</h3>
+		<div>Sets this matrix to [page:Matrix3 a] x [page:Matrix3 b].</div>
+
 		<h3>[method:Matrix3 multiplyScalar]( [page:Float s] )</h3>
 		<div>Multiplies every component of the matrix by the scalar value *s*.</div>
 
@@ -154,6 +163,9 @@ m.elements = [ 11, 21, 31,
 		sequence of values.
 		</div>
 
+		<h3>[method:Matrix3 premultiply]( [page:Matrix3 m] )</h3>
+		<div>Pre-multiplies this matrix by [page:Matrix3 m].</div>
+
 		<h3>[method:Matrix3 setFromMatrix4]( [page:Matrix4 m] )</h3>
 		<div>Set this matrx to the upper 3x3 matrix of the Matrix4 [page:Matrix4 m].</div>
 

+ 7 - 5
docs/api/math/Matrix4.html

@@ -29,23 +29,25 @@
 					[page:Object3D.matrix]: This stores the local transform of the object.
 				</li>
 				<li>
-					[page:Object3D.matrixWorld]: The global or world transform of the object. This is the objects transformation relative to its parent.
+					[page:Object3D.matrixWorld]: The global or world transform of the object. This is the object's transformation relative to its parent.
 					If the object has no parent, then this is identical to the local transform.
 				</li>
 				<li>
-					[page:Object3D.modelViewMatrix]:
+					[page:Object3D.modelViewMatrix]: This represents the object's transformation relative to the camera's coordinate system.
+					An object's modelViewMatrix is the object's matrixWorld pre-multiplied by the camera's matrixWorldInverse.
 				</li>
 			</ul>
 
-			[page:Camera Cameras] have two additional matrix4s:
+			[page:Camera Cameras] have two additional Matrix4s:
 			<ul>
 				<li>
-					[page:Object3D.matrixWorldInverse]: The inverse of the [page:Object3D.matrixWorld] described above.
+					[page:Camera.matrixWorldInverse]: The view matrix - the inverse of the Camera's [page:Object3D.matrixWorld matrixWorld].
 				</li>
 				<li>
-					[page:Object3D.projectionMatrix]:
+					[page:Camera.projectionMatrix]: Represents the information how to project the scene to clip space.
 				</li>
 			</ul>
+			Note: [page:Object3D.normalMatrix] is not a Matrix4, but a [page:Matrix3].
 		</div>
 
 		<h2>A Note on Row-Major and Column-Major Ordering</h2>

+ 205 - 0
docs/index.css

@@ -0,0 +1,205 @@
+@font-face {
+	font-family: 'inconsolata';
+	src: url('files/inconsolata.woff') format('woff');
+	font-weight: normal;
+	font-style: normal;
+}
+
+*{
+	box-sizing: border-box;
+}
+
+html {
+	height: 100%;
+}
+
+body {
+	background-color: #ffffff;
+	margin: 0px;
+	height: 100%;
+	color: #555;
+	font-family: 'inconsolata';
+	font-size: 15px;
+	line-height: 18px;
+	overflow: hidden;
+}
+
+h1 {
+	margin-top: 30px;
+	margin-bottom: 40px;
+	margin-left: 20px;
+	font-size: 25px;
+	font-weight: normal;
+}
+
+h2 {
+	color: #454545;
+	font-size: 18px;
+	font-weight: normal;
+
+	margin-top: 20px;
+	margin-left: 20px;
+}
+
+h3 {
+	color: #666;
+	font-size: 16px;
+	font-weight: normal;
+
+	margin-top: 20px;
+	margin-left: 20px;
+}
+
+a {
+	color: #2194CE;
+	text-decoration: none;
+}
+
+#panel {
+	position: fixed;
+	left: 0px;
+	width: 260px;
+	height: 100%;
+	overflow: auto;
+	background: #fafafa;
+}
+
+#panel ul {
+	list-style-type: none;
+	padding: 0px;
+	margin-left: 20px;
+}
+
+iframe {
+	position: absolute;
+	border: 0px;
+	left: 260px;
+	width: calc(100% - 260px);
+	height: 100%;
+	overflow: auto;
+}
+
+.filterBlock{
+	margin: 20px;
+	position: relative;
+}
+
+.filterBlock p {
+	margin: 0;
+}
+
+#filterInput {
+	width: 100%;
+	padding: 5px;
+	font-family: inherit;
+	font-size: 15px;
+	outline: none;
+	border: 1px solid #dedede;
+}
+
+#filterInput:focus{
+	border: 1px solid #2194CE;
+}
+
+#clearFilterButton {
+	position: absolute;
+	right: 6px;
+	top: 50%;
+	margin-top: -8px;
+	width: 16px;
+	height: 16px;
+	font-size: 14px;
+	color: grey;
+	text-align: center;
+	line-height: 0;
+	padding-top: 7px;
+	opacity: .5;
+}
+
+#clearFilterButton:hover {
+	opacity: 1;
+}
+
+.hidden {
+	display: none;
+}
+
+#panel li b {
+	font-weight: bold;
+}
+
+/* mobile */
+
+#expandButton {
+	display: none;
+	position: absolute;
+	right: 20px;
+	top: 12px;
+	width: 32px;
+	height: 32px;
+}
+
+#expandButton span {
+	height: 2px;
+	background-color: #2194CE;
+	width: 16px;
+	position: absolute;
+	left: 8px;
+	top: 10px;
+}
+
+#expandButton span:nth-child(1) {
+	top: 16px;
+}
+
+#expandButton span:nth-child(2) {
+	top: 22px;
+}
+
+@media all and ( max-width: 640px ) {
+
+	h1{
+		margin-top: 20px;
+		margin-bottom: 20px;
+	}
+
+	#panel{
+		position: absolute;
+		left: 0;
+		top: 0;
+		height: 480px;
+		width: 100%;
+		right: 0;
+		z-index: 100;
+		overflow: hidden;
+		border-bottom: 1px solid #dedede;
+	}
+
+	#navigation{
+		position: absolute;
+		left: 0;
+		top: 90px;
+		right: 0;
+		bottom: 0;
+		font-size: 17px;
+		line-height: 22px;
+		overflow: auto;
+	}
+
+	iframe{
+		position: absolute;
+		left: 0;
+		top: 56px;
+		width: 100%;
+		height: calc(100% - 56px);
+	}
+
+	#expandButton{
+		display: block;
+	}
+
+	#panel.collapsed{
+		height: 56px;
+	}
+
+}

+ 326 - 322
docs/index.html

@@ -4,421 +4,424 @@
 		<meta charset="utf-8">
 		<title>three.js / documentation</title>
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
-		<style>
+		<link type="text/css" rel="stylesheet" href="index.css">
+	</head>
+	<body>
 
-			@font-face {
-				font-family: 'inconsolata';
-				src: url('files/inconsolata.woff') format('woff');
-				font-weight: normal;
-				font-style: normal;
-			}
+		<div id="panel" class="collapsed">
 
-			*{
-				box-sizing: border-box;
-			}
+			<h1><a href="http://threejs.org">three.js</a> / docs</h1>
 
-			html {
-				height: 100%;
-			}
+			<a id="expandButton" href="#">
+				<span></span>
+				<span></span>
+				<span></span>
+			</a>
 
-			body {
-				background-color: #ffffff;
-				margin: 0px;
-				height: 100%;
-				color: #555;
-				font-family: 'inconsolata';
-				font-size: 15px;
-				line-height: 18px;
-				overflow: hidden;
-			}
+			<div class="filterBlock" >
+				<input type="text" id="filterInput" placeholder="Type to filter">
+				<a href="#" id="clearFilterButton">x</a>
+			</div>
 
-				h1 {
-					margin-top: 30px;
-					margin-bottom: 40px;
-					margin-left: 20px;
-					font-size: 25px;
-					font-weight: normal;
-				}
+		</div>
 
-				h2 {
-					color: #454545;
-					font-size: 18px;
-					font-weight: normal;
+		<iframe></iframe>
 
-					margin-top: 20px;
-					margin-left: 20px;
-				}
+		<script src="list.js"></script>
 
-				h3 {
-					color: #666;
-					font-size: 16px;
-					font-weight: normal;
+		<script>
 
-					margin-top: 20px;
-					margin-left: 20px;
-				}
+			var panel = document.getElementById( 'panel' );
+			var clearFilterButton = document.getElementById( 'clearFilterButton' );
+			var expandButton = document.getElementById( 'expandButton' );
+			var filterInput = document.getElementById( 'filterInput' );
+			var iframe = document.querySelector( 'iframe' );
 
-			a {
-				color: #2194CE;
-				text-decoration: none;
-			}
+			var pageProperties = {};
+			var titles = {};
+			var categoryElements = [];
 
-			#panel {
-				position: fixed;
-				left: 0px;
-				width: 260px;
-				height: 100%;
-				overflow: auto;
-				background: #fafafa;
-			}
 
-				#panel ul {
-					list-style-type: none;
-					padding: 0px;
-					margin-left: 20px;
-				}
+// ----------------------------------------------------------------------------
+// Initialization
+// ----------------------------------------------------------------------------
 
-			#viewer {
-				position: absolute;
-				border: 0px;
-				left: 260px;
-				width: calc(100% - 260px);
-				height: 100%;
-				overflow: auto;
-			}
 
-			.filterBlock{
-				margin: 20px;
-				position: relative;
-			}
-			.filterBlock p {
-				margin: 0;
-			}
+			// Functionality for hamburger button (on small devices)
 
-			#filterInput {
-				width: 100%;
-				padding: 5px;
-				font-family: inherit;
-				font-size: 15px;
-				outline: none;
-				border: 1px solid #dedede;
-			}
+			expandButton.onclick = function ( event ) {
 
-			#filterInput:focus{
-				border: 1px solid #2194CE;
-			}
+				event.preventDefault();
+				panel.classList.toggle( 'collapsed' );
 
-			#clearFilterButton {
-				position: absolute;
-				right: 6px;
-				top: 50%;
-				margin-top: -8px;
-				width: 16px;
-				height: 16px;
-				font-size: 14px;
-				color: grey;
-				text-align: center;
-				line-height: 0;
-				padding-top: 7px;
-				opacity: .5;
-			}
+			};
 
-			#clearFilterButton:hover {
-				opacity: 1;
-			}
 
-			.filtered {
-				display: none;
-			}
+			// Functionality for search/filter input field
 
-			#panel li b {
-				font-weight: bold;
-			}
+			filterInput.oninput = function ( event ) {
 
-			/* mobile */
+				updateFilter();
 
-			#expandButton {
-				display: none;
-				position: absolute;
-				right: 20px;
-				top: 12px;
-				width: 32px;
-				height: 32px;
-			}
+			};
 
-				#expandButton span {
-					height: 2px;
-					background-color: #2194CE;
-					width: 16px;
-					position: absolute;
-					left: 8px;
-					top: 10px;
-				}
 
-				#expandButton span:nth-child(1) {
-					top: 16px;
-				}
+			// Functionality for filter clear button
 
-				#expandButton span:nth-child(2) {
-					top: 22px;
-				}
+			clearFilterButton.onclick = function ( event ) {
 
-			@media all and ( max-width: 640px ) {
-				h1{
-					margin-top: 20px;
-					margin-bottom: 20px;
-				}
-				#panel{
-					position: absolute;
-					left: 0;
-					top: 0;
-					height: 480px;
-					width: 100%;
-					right: 0;
-					z-index: 100;
-					overflow: hidden;
-					border-bottom: 1px solid #dedede;
-				}
-				#content{
-					position: absolute;
-					left: 0;
-					top: 90px;
-					right: 0;
-					bottom: 0;
-					font-size: 17px;
-					line-height: 22px;
-					overflow: auto;
-				}
-				#viewer{
-					position: absolute;
-					left: 0;
-					top: 56px;
-					width: 100%;
-					height: calc(100% - 56px);
-				}
-				#expandButton{
-					display: block;
-				}
-				#panel.collapsed{
-					height: 56px;
-				}
-			}
-		</style>
-	</head>
-	<body>
+				event.preventDefault();
 
-		<div id="panel" class="collapsed">
-			<h1><a href="http://threejs.org">three.js</a> / docs</h1>
-			<a id="expandButton" href="#">
-				<span></span>
-				<span></span>
-				<span></span>
-			</a>
-			<div class="filterBlock" >
-				<input type="text" id="filterInput" placeholder="Type to filter"/>
-				<a href="#" id="clearFilterButton" >x</a>
-			</div>
-			<div id="content"></div>
-		</div>
-		<iframe id="viewer"></iframe>
+				filterInput.value = '';
+				updateFilter();
 
-		<script src="list.js"></script>
-		<script>
+			};
 
-			function extractQuery() {
-				var p = window.location.search.indexOf( '?q=' );
-				if( p !== -1 ) {
-					return window.location.search.substr( 3 );
-				}
-				return ''
-			}
 
-			var panel = document.getElementById( 'panel' );
-			var viewer = document.getElementById( 'viewer' );
+			// Activate content and title change on browser navigation
 
-			var expandButton = document.getElementById( 'expandButton' );
-			expandButton.addEventListener( 'click', function ( event ) {
-				panel.classList.toggle( 'collapsed' );
-				event.preventDefault();
-			} );
+			window.onpopstate = createNewIframe;
 
-			var filterInput = document.getElementById( 'filterInput' );
-			var clearFilterButton = document.getElementById( 'clearFilterButton' );
 
-			var DELIMITER = '/';
-			var MEMBER_DELIMITER = '.';
-			var nameCategoryMap = {};
-			var sections = [];
+			// Create the navigation panel and configure the iframe
+
+			createNavigation();
+			createNewIframe();
+
+
+// ----------------------------------------------------------------------------
+// Navigation Panel
+// ----------------------------------------------------------------------------
+
+
+			function createNavigation () {
+
+				// Create the navigation panel using data from list.js
+
+				var navigation = createAndAppendDOMElement( { type: 'div', parent: panel } );
+
+				for ( var section in list ) {
+
+					// Create sections
 
-			var content = document.getElementById( 'content' );
+					var categories = list[ section ];
 
-			for ( var section in list ) {
+					var sectionHead = createAndAppendDOMElement( { type: 'h2', parent: navigation, content: section } )
 
-				var h2 = document.createElement( 'h2' );
-				h2.textContent = section;
+					for ( var category in categories ) {
 
-				content.appendChild( h2 );
+						// Create categories
 
-				for ( var category in list[ section ] ) {
+						var pages = categories[ category ];
 
-					var div = document.createElement( 'div' );
+						var categoryContainer = createAndAppendDOMElement( { type: 'div', parent: navigation } );
+						var categoryHead = createAndAppendDOMElement( { type: 'h3', parent: categoryContainer, content: category } );
+						var categoryContent = createAndAppendDOMElement( { type: 'ul', parent: categoryContainer } );
 
-					var h3 = document.createElement( 'h3' );
-					h3.textContent = category;
+						for ( var pageName in pages ) {
 
-					div.appendChild( h3 );
+							// Create page links
 
-					var ul = document.createElement( 'ul' );
-					div.appendChild( ul );
+							var pageURL = pages[ pageName ];
 
-					for ( var i = 0; i < list[ section ][ category ].length; i ++ ) {
+							var listElement = createAndAppendDOMElement( { type: 'li', parent: categoryContent } );
+							var linkElement = createAndAppendDOMElement( { type: 'a', parent: listElement, content: pageName } );
 
-						var page = list[ section ][ category ][ i ];
+							// The href attribute is only used for the option to create a new tab by right click
 
-						var li = document.createElement( 'li' );
-						var a = document.createElement( 'a' );
-						a.setAttribute( 'href', '#' );
-						( function( s, c, p ) {
-							a.addEventListener( 'click', function( e ) {
-								goTo( s, c, p );
-								e.preventDefault();
-							} )
-						} )( section, category, page[ 0 ] );
-						a.textContent = page[ 0 ];
-						li.appendChild( a );
-						ul.appendChild( li );
+							linkElement.setAttribute( 'href', '#' + pageURL );
 
-						nameCategoryMap[page[0]] = {
-							section: section,
-							category: category,
-							name: page[0],
-							element: a
-						};
+							addClickHandlers( linkElement, pageURL );
+
+							// Gather the main properties for the current subpage
+
+							pageProperties[ pageName ] = {
+								section: section,
+								category: category,
+								pageURL: pageURL,
+								linkElement: linkElement
+							};
+
+						// Gather the document titles (used for easy access on browser navigation)
+
+						titles[ pageURL ] = pageName;
+
+						}
+
+						// Gather the category elements for easy access on filtering
+
+						categoryElements.push( categoryContent );
 
 					}
 
-					content.appendChild( div );
-					sections.push( ul );
+				}
+
+			};
+
+
+			function createAndAppendDOMElement( properties ) {
+
+				// Helper function for creating and appending new DOM elements
+
+				var newElement = document.createElement( properties.type );
+
+				properties.parent.appendChild( newElement );
+
+				if ( properties.content ) {
+
+					newElement.textContent = properties.content;
 
 				}
 
+				return newElement;
 
 			}
 
-			panel.appendChild( content );
 
-			function layoutList() {
+			function addClickHandlers ( linkElement, pageURL ) {
+
+				// Helper function for adding ClickHandlers to the page links
+
+				linkElement.onclick = function ( event ) {
+
+					event.preventDefault();
+
+					window.location.hash = pageURL;
+					createNewIframe();
+
+					panel.classList.add( 'collapsed' );
+
+				};
+
+			};
+
+
+// ----------------------------------------------------------------------------
+// Query Strings (optional)
+// ----------------------------------------------------------------------------
+
+
+//			(uncomment the following lines and the first line in updateFilter(), if you want query strings):
+
+//			filterInput.value = extractFromQueryString();
+//			updateFilter();
+//
+//
+//			function extractFromQueryString() {
+//
+//				var queryString = window.location.search;
+//
+//				if ( queryString.indexOf( '?q=' ) === -1 ) return '';
+//
+//				return queryString.substr( 3 );
+//
+//			}
+//
+//			function updateQueryString() {
+//
+//				var searchString = filterInput.value;
+//				var query = '';
+//
+//				if (searchString !== '') {
+//
+//					query = '?q=' + searchString;
+//
+//				}
+//
+//				window.history.replaceState( {} , '', window.location.pathname + query + window.location.hash ); 
+//
+//			}
+
+
+// ----------------------------------------------------------------------------
+// Filtering
+// ----------------------------------------------------------------------------
+
+
+			function updateFilter() {
+
+//			(uncomment the following line and the "Query strings" section, if you want query strings):
+//			updateQueryString();
+
+				var regExp = new RegExp( filterInput.value, 'gi' );
+
+				for ( var pageName in pageProperties ) {
+
+					var linkElement = pageProperties[ pageName ].linkElement;
+					var categoryClassList = linkElement.parentElement.classList;
+					var filterResults = pageName.match( regExp );
+
+					if ( filterResults && filterResults.length > 0 ) {
+
+						// Accentuate matching characters
+
+						for ( var i = 0; i < filterResults.length; i++ ) {
+
+							var result = filterResults[ i ];
+
+							if ( result !== '' ) {
+								pageName = pageName.replace( result, '<b>' + result + '</b>' );
+							}
 
-				sections.forEach( function( el ) {
-					var collapsed = true;
-					Array.prototype.slice.apply( el.children ).forEach( function( item ) {
-						if( !item.classList.contains( 'filtered' ) ) {
-							collapsed = false;
 						}
-					} );
-					if( collapsed ) {
-						el.parentElement.classList.add( 'filtered' );
+
+						categoryClassList.remove( 'hidden' );
+						linkElement.innerHTML = pageName;
+
 					} else {
-						el.parentElement.classList.remove( 'filtered' );
+
+						// Hide all non-matching page names
+
+						categoryClassList.add( 'hidden' );
+
 					}
-				} );
+
+				}
+
+				displayFilteredPanel();
+
 			}
 
-			filterInput.addEventListener( 'input', function( e ) {
-				updateFilter();
-			} );
 
-			clearFilterButton.addEventListener( 'click', function( e ) {
-				filterInput.value = '';
-				updateFilter();
-				e.preventDefault();
-			} );
+			function displayFilteredPanel() {
 
-			function updateFilter() {
+				// Show/hide categories depending on their content
+				// First check if at least one page in this category is not hidden
 
-				var v = filterInput.value;
-				if( v !== '' ) {
-					window.history.replaceState( {} , '', '?q=' + v + window.location.hash );
-				} else {
-					window.history.replaceState( {} , '', window.location.pathname + window.location.hash );
-				}
+				categoryElements.forEach( function ( category ) {
+
+					var pages = category.children;
+					var pagesLength = pages.length;
+					var sectionClassList = category.parentElement.classList;
+
+					var hideCategory = true;
+
+					for ( var i = 0; i < pagesLength; i ++ ) {
+
+						var pageClassList = pages[ i ].classList;
+
+						if ( ! pageClassList.contains( 'hidden' ) ) {
+
+							hideCategory = false;
 
-				var exp = new RegExp( filterInput.value, 'gi' );
-				for( var j in nameCategoryMap ) {
-					var res = nameCategoryMap[ j ].name.match( exp );
-					if( res && res.length > 0 ) {
-						nameCategoryMap[ j ].element.parentElement.classList.remove( 'filtered' );
-						var str = nameCategoryMap[ j ].name;
-						for( var i = 0; i < res.length; i++ ) {
-							str = str.replace( res[ i ], '<b>' + res[ i ] + '</b>' );
 						}
-						nameCategoryMap[ j ].element.innerHTML = str;
+
+					}
+
+					// If and only if all page names are hidden, hide the whole category
+
+					if ( hideCategory ) {
+
+						sectionClassList.add( 'hidden' );
+
 					} else {
-						nameCategoryMap[ j ].element.parentElement.classList.add( 'filtered' );
-						nameCategoryMap[ j ].element.textContent = nameCategoryMap[ j ].name;
+
+						sectionClassList.remove( 'hidden' );
+
 					}
-				}
-				layoutList();
+
+				} );
 
 			}
 
-			function encodeUrl( path ) {
 
-				return path.replace(/\ \/\ /g, '.').replace(/\ /g, '_');
+// ----------------------------------------------------------------------------
+// Routing
+// ----------------------------------------------------------------------------
 
-			}
 
-			function decodeUrl( path ) {
+			function setUrlFragment( pageName ) {
 
-				return path.replace(/_/g, ' ').replace(/\./g, ' / ');
+				// Handle navigation from the subpages (iframes):
+				// First separate the memeber (if existing) from the page name,
+				// then identify the subpage's URL and set it as URL fragment (re-adding the member)
 
-			}
+				var splitPageName = decomposePageName( pageName, '.', '.' );
 
-			// Page loading
+				var currentProperties = pageProperties[ splitPageName[ 0 ] ];
 
-			function goTo( section, category, name, member ) {
-				var parts, location;
+				if ( currentProperties ) {
 
-				// Fully resolve links that only provide a name
-				if(arguments.length == 1) {
+					window.location.hash = currentProperties.pageURL + splitPageName[ 1 ];
 
-					// Resolve links of the form 'Class.member'
-					if(section.indexOf(MEMBER_DELIMITER) !== -1) {
-						parts = section.split(MEMBER_DELIMITER);
-						section = parts[0];
-						member = parts[1];
-					}
+					createNewIframe();
 
-					location = nameCategoryMap[section];
-					if (!location) return;
-					section = location.section;
-					category = location.category;
-					name = location.name;
 				}
 
-				var title = 'three.js - documentation - ' + section + ' - ' + name;
-				var url = encodeUrl(section) + DELIMITER + encodeUrl( category ) + DELIMITER + encodeUrl(name) + (!!member ? MEMBER_DELIMITER + encodeUrl(member) : '');
+			}
+
+
+			function createNewIframe() {
+
+				// Change the content displayed in the iframe
+				// First separate the member part of the fragment (if existing)
+
+				var hash = window.location.hash.substring( 1 );
+				var splitHash = decomposePageName( hash, '.', '#' );
 
-				window.location.hash = url;
-				window.document.title = title;
+				// Creating a new Iframe instead of assigning a new src is
+				// a cross-browser solution to allow normal browser navigation;
+				// - only assigning a new src would result in two history states each time.
 
-				viewer.src = pages[ section ][ category ][ name ] + '.html' + (!!member ? '#'+member : '');
+				// Note: iframe.contentWindow.location.replace(hash) should work, too,
+				// but it doesn't work in Edge with links from the subpages!
 
-				panel.classList.add( 'collapsed' );
+				var oldIframe;
+				var subtitle;
+
+				oldIframe = iframe;
+				iframe = oldIframe.cloneNode();
+
+				if(hash) {
+
+					iframe.src = splitHash[ 0 ] + '.html' + splitHash[ 1 ];
+					subtitle = ' - ' + titles[ splitHash[ 0 ] ] + splitHash[ 1 ];
+
+				} else {
+
+					iframe.src = '';
+					subtitle = '';
+
+				}
+
+				document.body.replaceChild( iframe, oldIframe );
+				document.title = 'three.js docs' + subtitle;
 
 			}
 
-			function goToHash() {
 
-				var hash = window.location.hash.substring( 1 ).split(DELIMITER);
-				var member = hash[2].split(MEMBER_DELIMITER);
-				goTo( decodeUrl(hash[0]), decodeUrl(hash[1]), decodeUrl(member[0]), decodeUrl(member.length > 1 ? member[1] : '') );
+			function decomposePageName ( pageName, oldDelimiter, newDelimiter ) {
+
+				// Helper function for separating the member (if existing) from the pageName
+				// For example: 'Geometry.morphTarget' can be converted to 
+				// ['Geometry', '.morphTarget'] or ['Geometry', '#morphTarget']
+				// Note: According RFC 3986 no '#' allowed inside of an URL fragment!
+
+				var parts = [];
+
+				var dotIndex = pageName.indexOf( oldDelimiter );
+
+				if ( dotIndex !== -1 ) {
+
+					parts = pageName.split( oldDelimiter );
+					parts[ 1 ] = newDelimiter + parts[ 1 ];
+
+				} else {
+
+					parts[ 0 ] = pageName;
+					parts[ 1 ] = '';
+
+				}
+
+				return parts;
 
 			}
 
-			window.addEventListener( 'hashchange', goToHash, false );
 
-			if ( window.location.hash.length > 0 ) goToHash();
+// ----------------------------------------------------------------------------
+// ASCII Art ;-)
+// ----------------------------------------------------------------------------
+
 
 			console.log([
 				'    __     __',
@@ -431,10 +434,11 @@
 				'                                         \\/____/\\/_____/'
 			].join('\n'));
 
-			filterInput.value = extractQuery();
-			updateFilter( )
 
 		</script>
+
 		<script src="../build/three.min.js"></script> <!-- console sandbox -->
+
 	</body>
+
 </html>

+ 373 - 391
docs/list.js

@@ -1,413 +1,395 @@
 var list = {
 
 	"Manual": {
-		"Getting Started": [
-			[ "Creating a scene", "manual/introduction/Creating-a-scene" ],
-			[ "Detecting WebGL and browser compatibility", "manual/introduction/Detecting-WebGL-and-browser-compatibility" ],
-			[ "How to run things locally", "manual/introduction/How-to-run-thing-locally" ],
-			[ "Drawing Lines", "manual/introduction/Drawing-lines" ],
-			[ "Creating Text", "manual/introduction/Creating-text" ],
-			[ "Migration Guide", "manual/introduction/Migration-guide" ],
-			[ "Code Style Guide", "manual/introduction/Code-style-guide" ],
-			[ "FAQ", "manual/introduction/FAQ" ],
-			[ "Useful links", "manual/introduction/Useful-links" ]
-		],
-
-		"Next Steps": [
-			[ "How to update things", "manual/introduction/How-to-update-things" ],
-			[ "Matrix transformations", "manual/introduction/Matrix-transformations" ],
-            [ "Animation System", "manual/introduction/Animation-system" ]
-		],
-
-		"Build Tools": [
-			[ "Testing with NPM", "manual/buildTools/Testing-with-NPM" ]
-		]
+
+		"Getting Started": {
+			"Creating a scene": "manual/introduction/Creating-a-scene",
+			"Detecting WebGL and browser compatibility": "manual/introduction/Detecting-WebGL-and-browser-compatibility",
+			"How to run things locally": "manual/introduction/How-to-run-thing-locally",
+			"Drawing Lines": "manual/introduction/Drawing-lines",
+			"Creating Text": "manual/introduction/Creating-text",
+			"Migration Guide": "manual/introduction/Migration-guide",
+			"Code Style Guide": "manual/introduction/Code-style-guide",
+			"FAQ": "manual/introduction/FAQ",
+			"Useful links": "manual/introduction/Useful-links"
+		},
+
+		"Next Steps": {
+			"How to update things": "manual/introduction/How-to-update-things",
+			"Matrix transformations": "manual/introduction/Matrix-transformations",
+			"Animation System": "manual/introduction/Animation-system"
+		},
+
+		"Build Tools": {
+			"Testing with NPM": "manual/buildTools/Testing-with-NPM"
+		}
+
 	},
 
 	"Reference": {
 
-		"Animation": [
-			[ "AnimationAction", "api/animation/AnimationAction" ],
-			[ "AnimationClip", "api/animation/AnimationClip" ],
-			[ "AnimationMixer", "api/animation/AnimationMixer" ],
-			[ "AnimationObjectGroup", "api/animation/AnimationObjectGroup" ],
-			[ "AnimationUtils", "api/animation/AnimationUtils" ],
-			[ "KeyframeTrack", "api/animation/KeyframeTrack" ],
-			[ "PropertyBinding", "api/animation/PropertyBinding" ],
-			[ "PropertyMixer", "api/animation/PropertyMixer" ]
-		],
-
-		"Animation / Tracks": [
-		  [ "BooleanKeyframeTrack", "api/animation/tracks/BooleanKeyframeTrack" ],
-		  [ "ColorKeyframeTrack", "api/animation/tracks/ColorKeyframeTrack" ],
-		  [ "NumberKeyframeTrack", "api/animation/tracks/NumberKeyframeTrack" ],
-		  [ "QuaternionKeyframeTrack", "api/animation/tracks/QuaternionKeyframeTrack" ],
-		  [ "StringKeyframeTrack", "api/animation/tracks/StringKeyframeTrack" ],
-		  [ "VectorKeyframeTrack", "api/animation/tracks/VectorKeyframeTrack" ]
-		],
-
-		"Audio": [
-			[ "Audio", "api/audio/Audio" ],
-			[ "AudioAnalyser", "api/audio/AudioAnalyser" ],
-			[ "AudioContext", "api/audio/AudioContext" ],
-			[ "AudioListener", "api/audio/AudioListener" ],
-			[ "PositionalAudio", "api/audio/PositionalAudio" ]
-		],
-
-		"Cameras": [
-			[ "Camera", "api/cameras/Camera" ],
-			[ "CubeCamera", "api/cameras/CubeCamera" ],
-			[ "OrthographicCamera", "api/cameras/OrthographicCamera" ],
-			[ "PerspectiveCamera", "api/cameras/PerspectiveCamera" ],
-			[ "StereoCamera", "api/cameras/StereoCamera" ]
-		],
-
-		"Constants": [
-			[ "Animation", "api/constants/Animation" ],
-			[ "Core", "api/constants/Core" ],
-			[ "CustomBlendingEquation", "api/constants/CustomBlendingEquations" ],
-			[ "DrawModes", "api/constants/DrawModes" ],
-			[ "Materials", "api/constants/Materials" ],
-			[ "Renderer", "api/constants/Renderer" ],
-			[ "Textures", "api/constants/Textures" ]
-		],
-
-		"Core": [
-			[ "BufferAttribute", "api/core/BufferAttribute" ],
-			[ "BufferGeometry", "api/core/BufferGeometry" ],
-			[ "Clock", "api/core/Clock" ],
-			[ "DirectGeometry", "api/core/DirectGeometry" ],
-			[ "EventDispatcher", "api/core/EventDispatcher" ],
-			[ "Face3", "api/core/Face3" ],
-			[ "Geometry", "api/core/Geometry" ],
-			[ "InstancedBufferAttribute", "api/core/InstancedBufferAttribute" ],
-			[ "InstancedBufferGeometry", "api/core/InstancedBufferGeometry" ],
-			[ "InstancedInterleavedBuffer", "api/core/InstancedInterleavedBuffer" ],
-			[ "InterleavedBuffer", "api/core/InterleavedBuffer" ],
-			[ "InterleavedBufferAttribute", "api/core/InterleavedBufferAttribute" ],
-			[ "Layers", "api/core/Layers" ],
-			[ "Object3D", "api/core/Object3D" ],
-			[ "Raycaster", "api/core/Raycaster" ],
-			[ "Uniform", "api/core/Uniform" ]
-		],
-
-		"Core / BufferAttributes": [
-			[ "BufferAttribute Types", "api/core/bufferAttributeTypes/BufferAttributeTypes" ]
-		],
-
-		"Deprecated": [
-			[ "DeprecatedList", "api/deprecated/DeprecatedList" ]
-		],
-
-		"Extras": [
-			[ "CurveUtils", "api/extras/CurveUtils" ],
-			[ "SceneUtils", "api/extras/SceneUtils" ],
-			[ "ShapeUtils", "api/extras/ShapeUtils" ]
-		],
-
-		"Extras / Core": [
-			[ "Curve", "api/extras/core/Curve" ],
-			[ "CurvePath", "api/extras/core/CurvePath" ],
-			[ "Font", "api/extras/core/Font" ],
-			[ "Path", "api/extras/core/Path" ],
-			[ "Shape", "api/extras/core/Shape" ],
-			[ "ShapePath", "api/extras/core/ShapePath" ]
-		],
-
-		"Extras / Curves": [
-			[ "ArcCurve", "api/extras/curves/ArcCurve" ],
-			[ "CatmullRomCurve3", "api/extras/curves/CatmullRomCurve3" ],
-			[ "CubicBezierCurve", "api/extras/curves/CubicBezierCurve" ],
-			[ "CubicBezierCurve3", "api/extras/curves/CubicBezierCurve3" ],
-			[ "EllipseCurve", "api/extras/curves/EllipseCurve" ],
-			[ "LineCurve", "api/extras/curves/LineCurve" ],
-			[ "LineCurve3", "api/extras/curves/LineCurve3" ],
-			[ "QuadraticBezierCurve", "api/extras/curves/QuadraticBezierCurve" ],
-			[ "QuadraticBezierCurve3", "api/extras/curves/QuadraticBezierCurve3" ],
-			[ "SplineCurve", "api/extras/curves/SplineCurve" ]
-		],
-
-		"Extras / Objects": [
-			[ "ImmediateRenderObject", "api/extras/objects/ImmediateRenderObject" ],
-			[ "MorphBlendMesh", "api/extras/objects/MorphBlendMesh" ]
-		],
-
-		"Geometries": [
-			[ "BoxBufferGeometry", "api/geometries/BoxBufferGeometry" ],
-			[ "BoxGeometry", "api/geometries/BoxGeometry" ],
-			[ "CircleBufferGeometry", "api/geometries/CircleBufferGeometry" ],
-			[ "CircleGeometry", "api/geometries/CircleGeometry" ],
-			[ "ConeBufferGeometry", "api/geometries/ConeBufferGeometry" ],
-			[ "ConeGeometry", "api/geometries/ConeGeometry" ],
-			[ "CylinderBufferGeometry", "api/geometries/CylinderBufferGeometry" ],
-			[ "CylinderGeometry", "api/geometries/CylinderGeometry" ],
-			[ "DodecahedronBufferGeometry", "api/geometries/DodecahedronBufferGeometry" ],
-			[ "DodecahedronGeometry", "api/geometries/DodecahedronGeometry" ],
-			[ "EdgesGeometry", "api/geometries/EdgesGeometry" ],
-			[ "ExtrudeGeometry", "api/geometries/ExtrudeGeometry" ],
-			[ "ExtrudeBufferGeometry", "api/geometries/ExtrudeBufferGeometry" ],
-			[ "IcosahedronBufferGeometry", "api/geometries/IcosahedronBufferGeometry" ],
-			[ "IcosahedronGeometry", "api/geometries/IcosahedronGeometry" ],
-			[ "LatheBufferGeometry", "api/geometries/LatheBufferGeometry" ],
-			[ "LatheGeometry", "api/geometries/LatheGeometry" ],
-			[ "OctahedronBufferGeometry", "api/geometries/OctahedronBufferGeometry" ],
-			[ "OctahedronGeometry", "api/geometries/OctahedronGeometry" ],
-			[ "ParametricBufferGeometry", "api/geometries/ParametricBufferGeometry" ],
-			[ "ParametricGeometry", "api/geometries/ParametricGeometry" ],
-			[ "PlaneBufferGeometry", "api/geometries/PlaneBufferGeometry" ],
-			[ "PlaneGeometry", "api/geometries/PlaneGeometry" ],
-			[ "PolyhedronBufferGeometry", "api/geometries/PolyhedronBufferGeometry" ],
-			[ "PolyhedronGeometry", "api/geometries/PolyhedronGeometry" ],
-			[ "RingBufferGeometry", "api/geometries/RingBufferGeometry" ],
-			[ "RingGeometry", "api/geometries/RingGeometry" ],
-			[ "ShapeBufferGeometry", "api/geometries/ShapeBufferGeometry" ],
-			[ "ShapeGeometry", "api/geometries/ShapeGeometry" ],
-			[ "SphereBufferGeometry", "api/geometries/SphereBufferGeometry" ],
-			[ "SphereGeometry", "api/geometries/SphereGeometry" ],
-			[ "TetrahedronBufferGeometry", "api/geometries/TetrahedronBufferGeometry" ],
-			[ "TetrahedronGeometry", "api/geometries/TetrahedronGeometry" ],
-			[ "TextGeometry", "api/geometries/TextGeometry" ],
-			[ "TorusBufferGeometry", "api/geometries/TorusBufferGeometry" ],
-			[ "TorusGeometry", "api/geometries/TorusGeometry" ],
-			[ "TorusKnotBufferGeometry", "api/geometries/TorusKnotBufferGeometry" ],
-			[ "TorusKnotGeometry", "api/geometries/TorusKnotGeometry" ],
-			[ "TubeGeometry", "api/geometries/TubeGeometry" ],
-			[ "TubeBufferGeometry", "api/geometries/TubeBufferGeometry" ],
-			[ "WireframeGeometry", "api/geometries/WireframeGeometry" ]
-		],
-
-		"Helpers": [
-			[ "ArrowHelper", "api/helpers/ArrowHelper" ],
-			[ "AxisHelper", "api/helpers/AxisHelper" ],
-			[ "BoxHelper", "api/helpers/BoxHelper" ],
-			[ "CameraHelper", "api/helpers/CameraHelper" ],
-			[ "DirectionalLightHelper", "api/helpers/DirectionalLightHelper" ],
-			[ "FaceNormalsHelper", "api/helpers/FaceNormalsHelper" ],
-			[ "GridHelper", "api/helpers/GridHelper" ],
-			[ "PolarGridHelper", "api/helpers/PolarGridHelper" ],
-			[ "HemisphereLightHelper", "api/helpers/HemisphereLightHelper" ],
-			[ "PointLightHelper", "api/helpers/PointLightHelper" ],
-			[ "RectAreaLightHelper", "api/helpers/RectAreaLightHelper" ],
-			[ "SkeletonHelper", "api/helpers/SkeletonHelper" ],
-			[ "SpotLightHelper", "api/helpers/SpotLightHelper" ],
-			[ "VertexNormalsHelper", "api/helpers/VertexNormalsHelper" ]
-		],
-
-		"Lights": [
-			[ "AmbientLight", "api/lights/AmbientLight" ],
-			[ "DirectionalLight", "api/lights/DirectionalLight" ],
-			[ "HemisphereLight", "api/lights/HemisphereLight" ],
-			[ "Light", "api/lights/Light" ],
-			[ "PointLight", "api/lights/PointLight" ],
-			[ "RectAreaLight", "api/lights/RectAreaLight" ],
-			[ "SpotLight", "api/lights/SpotLight" ]
-		],
-
-		"Lights / Shadows": [
-			[ "DirectionalLightShadow", "api/lights/shadows/DirectionalLightShadow" ],
-			[ "LightShadow", "api/lights/shadows/LightShadow" ],
-			[ "RectAreaLightShadow", "api/lights/shadows/RectAreaLightShadow" ],
-			[ "SpotLightShadow", "api/lights/shadows/SpotLightShadow" ]
-		],
-
-		"Loaders": [
-			[ "AnimationLoader", "api/loaders/AnimationLoader" ],
-			[ "AudioLoader", "api/loaders/AudioLoader" ],
-			[ "BufferGeometryLoader", "api/loaders/BufferGeometryLoader" ],
-			[ "Cache", "api/loaders/Cache" ],
-			[ "CompressedTextureLoader", "api/loaders/CompressedTextureLoader" ],
-			[ "CubeTextureLoader", "api/loaders/CubeTextureLoader" ],
-			[ "DataTextureLoader", "api/loaders/DataTextureLoader" ],
-			[ "FileLoader", "api/loaders/FileLoader" ],
-			[ "FontLoader", "api/loaders/FontLoader" ],
-			[ "ImageLoader", "api/loaders/ImageLoader" ],
-			[ "JSONLoader", "api/loaders/JSONLoader" ],
-			[ "Loader", "api/loaders/Loader" ],
-			[ "MaterialLoader", "api/loaders/MaterialLoader" ],
-			[ "ObjectLoader", "api/loaders/ObjectLoader" ],
-			[ "TextureLoader", "api/loaders/TextureLoader" ]
-		],
-
-		"Loaders / Managers": [
-			[ "DefaultLoadingManager", "api/loaders/managers/DefaultLoadingManager" ],
-			[ "LoadingManager", "api/loaders/managers/LoadingManager" ]
-		],
-
-		"Materials": [
-			[ "LineBasicMaterial", "api/materials/LineBasicMaterial" ],
-			[ "LineDashedMaterial", "api/materials/LineDashedMaterial" ],
-			[ "Material", "api/materials/Material" ],
-			[ "MeshBasicMaterial", "api/materials/MeshBasicMaterial" ],
-			[ "MeshDepthMaterial", "api/materials/MeshDepthMaterial" ],
-			[ "MeshLambertMaterial", "api/materials/MeshLambertMaterial" ],
-			[ "MeshNormalMaterial", "api/materials/MeshNormalMaterial" ],
-			[ "MeshPhongMaterial", "api/materials/MeshPhongMaterial" ],
-			[ "MeshPhysicalMaterial", "api/materials/MeshPhysicalMaterial" ],
-			[ "MeshStandardMaterial", "api/materials/MeshStandardMaterial" ],
-			[ "MeshToonMaterial", "api/materials/MeshToonMaterial" ],
-			[ "PointsMaterial", "api/materials/PointsMaterial" ],
-			[ "RawShaderMaterial", "api/materials/RawShaderMaterial" ],
-			[ "ShaderMaterial", "api/materials/ShaderMaterial" ],
-			[ "ShadowMaterial", "api/materials/ShadowMaterial" ],
-			[ "SpriteMaterial", "api/materials/SpriteMaterial" ]
-		],
-
-		"Math": [
-			[ "Box2", "api/math/Box2" ],
-			[ "Box3", "api/math/Box3" ],
-			[ "Color", "api/math/Color" ],
-			[ "Cylindrical", "api/math/Cylindrical" ],
-			[ "Euler", "api/math/Euler" ],
-			[ "Frustum", "api/math/Frustum" ],
-			[ "Interpolant", "api/math/Interpolant" ],
-			[ "Line3", "api/math/Line3" ],
-			[ "Math", "api/math/Math" ],
-			[ "Matrix3", "api/math/Matrix3" ],
-			[ "Matrix4", "api/math/Matrix4" ],
-			[ "Plane", "api/math/Plane" ],
-			[ "Quaternion", "api/math/Quaternion" ],
-			[ "Ray", "api/math/Ray" ],
-			[ "Sphere", "api/math/Sphere" ],
-			[ "Spherical", "api/math/Spherical" ],
-			[ "Triangle", "api/math/Triangle" ],
-			[ "Vector2", "api/math/Vector2" ],
-			[ "Vector3", "api/math/Vector3" ],
-			[ "Vector4", "api/math/Vector4" ]
-		],
-
-		"Math / Interpolants": [
-			[ "CubicInterpolant", "api/math/interpolants/CubicInterpolant" ],
-			[ "DiscreteInterpolant", "api/math/interpolants/DiscreteInterpolant" ],
-			[ "LinearInterpolant", "api/math/interpolants/LinearInterpolant" ],
-			[ "QuaternionLinearInterpolant", "api/math/interpolants/QuaternionLinearInterpolant" ]
-		],
-
-		"Objects": [
-			[ "Bone", "api/objects/Bone" ],
-			[ "Group", "api/objects/Group" ],
-			[ "LensFlare", "api/objects/LensFlare" ],
-			[ "Line", "api/objects/Line" ],
-			[ "LineLoop", "api/objects/LineLoop" ],
-			[ "LineSegments", "api/objects/LineSegments" ],
-			[ "LOD", "api/objects/LOD" ],
-			[ "Mesh", "api/objects/Mesh" ],
-			[ "Points", "api/objects/Points" ],
-			[ "Skeleton", "api/objects/Skeleton" ],
-			[ "SkinnedMesh", "api/objects/SkinnedMesh" ],
-			[ "Sprite", "api/objects/Sprite" ]
-		],
-
-		"Renderers": [
-			[ "WebGLRenderer", "api/renderers/WebGLRenderer" ],
-			[ "WebGLRenderTarget", "api/renderers/WebGLRenderTarget" ],
-			[ "WebGLRenderTargetCube", "api/renderers/WebGLRenderTargetCube" ]
-		],
-
-		"Renderers / Shaders": [
-			[ "ShaderChunk", "api/renderers/shaders/ShaderChunk" ],
-			[ "ShaderLib", "api/renderers/shaders/ShaderLib" ],
-			[ "UniformsLib", "api/renderers/shaders/UniformsLib" ],
-			[ "UniformsUtils", "api/renderers/shaders/UniformsUtils" ]
-		],
-
-		"Scenes": [
-			[ "Fog", "api/scenes/Fog" ],
-			[ "FogExp2", "api/scenes/FogExp2" ],
-			[ "Scene", "api/scenes/Scene" ]
-		],
-
-		"Textures": [
-			[ "CanvasTexture", "api/textures/CanvasTexture" ],
-			[ "CompressedTexture", "api/textures/CompressedTexture" ],
-			[ "CubeTexture", "api/textures/CubeTexture" ],
-			[ "DataTexture", "api/textures/DataTexture" ],
-			[ "DepthTexture", "api/textures/DepthTexture" ],
-			[ "Texture", "api/textures/Texture" ],
-			[ "VideoTexture", "api/textures/VideoTexture" ]
-		]
+		"Animation": {
+			"AnimationAction": "api/animation/AnimationAction",
+			"AnimationClip": "api/animation/AnimationClip",
+			"AnimationMixer": "api/animation/AnimationMixer",
+			"AnimationObjectGroup": "api/animation/AnimationObjectGroup",
+			"AnimationUtils": "api/animation/AnimationUtils",
+			"KeyframeTrack": "api/animation/KeyframeTrack",
+			"PropertyBinding": "api/animation/PropertyBinding",
+			"PropertyMixer": "api/animation/PropertyMixer"
+		},
+
+		"Animation / Tracks": {
+			"BooleanKeyframeTrack": "api/animation/tracks/BooleanKeyframeTrack",
+			"ColorKeyframeTrack": "api/animation/tracks/ColorKeyframeTrack",
+			"NumberKeyframeTrack": "api/animation/tracks/NumberKeyframeTrack",
+			"QuaternionKeyframeTrack": "api/animation/tracks/QuaternionKeyframeTrack",
+			"StringKeyframeTrack": "api/animation/tracks/StringKeyframeTrack",
+			"VectorKeyframeTrack": "api/animation/tracks/VectorKeyframeTrack"
+		},
+
+		"Audio": {
+			"Audio": "api/audio/Audio",
+			"AudioAnalyser": "api/audio/AudioAnalyser",
+			"AudioContext": "api/audio/AudioContext",
+			"AudioListener": "api/audio/AudioListener",
+			"PositionalAudio": "api/audio/PositionalAudio"
+		},
+
+		"Cameras": {
+			"Camera": "api/cameras/Camera",
+			"CubeCamera": "api/cameras/CubeCamera",
+			"OrthographicCamera": "api/cameras/OrthographicCamera",
+			"PerspectiveCamera": "api/cameras/PerspectiveCamera",
+			"StereoCamera": "api/cameras/StereoCamera"
+		},
+
+		"Constants": {
+			"Animation": "api/constants/Animation",
+			"Core": "api/constants/Core",
+			"CustomBlendingEquation": "api/constants/CustomBlendingEquations",
+			"DrawModes": "api/constants/DrawModes",
+			"Materials": "api/constants/Materials",
+			"Renderer": "api/constants/Renderer",
+			"Textures": "api/constants/Textures"
+		},
+
+		"Core": {
+			"BufferAttribute": "api/core/BufferAttribute",
+			"BufferGeometry": "api/core/BufferGeometry",
+			"Clock": "api/core/Clock",
+			"DirectGeometry": "api/core/DirectGeometry",
+			"EventDispatcher": "api/core/EventDispatcher",
+			"Face3": "api/core/Face3",
+			"Geometry": "api/core/Geometry",
+			"InstancedBufferAttribute": "api/core/InstancedBufferAttribute",
+			"InstancedBufferGeometry": "api/core/InstancedBufferGeometry",
+			"InstancedInterleavedBuffer": "api/core/InstancedInterleavedBuffer",
+			"InterleavedBuffer": "api/core/InterleavedBuffer",
+			"InterleavedBufferAttribute": "api/core/InterleavedBufferAttribute",
+			"Layers": "api/core/Layers",
+			"Object3D": "api/core/Object3D",
+			"Raycaster": "api/core/Raycaster",
+			"Uniform": "api/core/Uniform"
+		},
+
+		"Core / BufferAttributes": {
+			"BufferAttribute Types": "api/core/bufferAttributeTypes/BufferAttributeTypes"
+		},
+
+		"Deprecated": {
+			"DeprecatedList": "api/deprecated/DeprecatedList"
+		},
+
+		"Extras": {
+			"CurveUtils": "api/extras/CurveUtils",
+			"SceneUtils": "api/extras/SceneUtils",
+			"ShapeUtils": "api/extras/ShapeUtils"
+		},
+
+		"Extras / Core": {
+			"Curve": "api/extras/core/Curve",
+			"CurvePath": "api/extras/core/CurvePath",
+			"Font": "api/extras/core/Font",
+			"Path": "api/extras/core/Path",
+			"Shape": "api/extras/core/Shape",
+			"ShapePath": "api/extras/core/ShapePath"
+		},
+
+		"Extras / Curves": {
+			"ArcCurve": "api/extras/curves/ArcCurve",
+			"CatmullRomCurve3": "api/extras/curves/CatmullRomCurve3",
+			"CubicBezierCurve": "api/extras/curves/CubicBezierCurve",
+			"CubicBezierCurve3": "api/extras/curves/CubicBezierCurve3",
+			"EllipseCurve": "api/extras/curves/EllipseCurve",
+			"LineCurve": "api/extras/curves/LineCurve",
+			"LineCurve3": "api/extras/curves/LineCurve3",
+			"QuadraticBezierCurve": "api/extras/curves/QuadraticBezierCurve",
+			"QuadraticBezierCurve3": "api/extras/curves/QuadraticBezierCurve3",
+			"SplineCurve": "api/extras/curves/SplineCurve"
+		},
+
+		"Extras / Objects": {
+			"ImmediateRenderObject": "api/extras/objects/ImmediateRenderObject",
+			"MorphBlendMesh": "api/extras/objects/MorphBlendMesh"
+		},
+
+		"Geometries": {
+			"BoxBufferGeometry": "api/geometries/BoxBufferGeometry",
+			"BoxGeometry": "api/geometries/BoxGeometry",
+			"CircleBufferGeometry": "api/geometries/CircleBufferGeometry",
+			"CircleGeometry": "api/geometries/CircleGeometry",
+			"ConeBufferGeometry": "api/geometries/ConeBufferGeometry",
+			"ConeGeometry": "api/geometries/ConeGeometry",
+			"CylinderBufferGeometry": "api/geometries/CylinderBufferGeometry",
+			"CylinderGeometry": "api/geometries/CylinderGeometry",
+			"DodecahedronBufferGeometry": "api/geometries/DodecahedronBufferGeometry",
+			"DodecahedronGeometry": "api/geometries/DodecahedronGeometry",
+			"EdgesGeometry": "api/geometries/EdgesGeometry",
+			"ExtrudeGeometry": "api/geometries/ExtrudeGeometry",
+			"ExtrudeBufferGeometry": "api/geometries/ExtrudeBufferGeometry",
+			"IcosahedronBufferGeometry": "api/geometries/IcosahedronBufferGeometry",
+			"IcosahedronGeometry": "api/geometries/IcosahedronGeometry",
+			"LatheBufferGeometry": "api/geometries/LatheBufferGeometry",
+			"LatheGeometry": "api/geometries/LatheGeometry",
+			"OctahedronBufferGeometry": "api/geometries/OctahedronBufferGeometry",
+			"OctahedronGeometry": "api/geometries/OctahedronGeometry",
+			"ParametricBufferGeometry": "api/geometries/ParametricBufferGeometry",
+			"ParametricGeometry": "api/geometries/ParametricGeometry",
+			"PlaneBufferGeometry": "api/geometries/PlaneBufferGeometry",
+			"PlaneGeometry": "api/geometries/PlaneGeometry",
+			"PolyhedronBufferGeometry": "api/geometries/PolyhedronBufferGeometry",
+			"PolyhedronGeometry": "api/geometries/PolyhedronGeometry",
+			"RingBufferGeometry": "api/geometries/RingBufferGeometry",
+			"RingGeometry": "api/geometries/RingGeometry",
+			"ShapeBufferGeometry": "api/geometries/ShapeBufferGeometry",
+			"ShapeGeometry": "api/geometries/ShapeGeometry",
+			"SphereBufferGeometry": "api/geometries/SphereBufferGeometry",
+			"SphereGeometry": "api/geometries/SphereGeometry",
+			"TetrahedronBufferGeometry": "api/geometries/TetrahedronBufferGeometry",
+			"TetrahedronGeometry": "api/geometries/TetrahedronGeometry",
+			"TextGeometry": "api/geometries/TextGeometry",
+			"TorusBufferGeometry": "api/geometries/TorusBufferGeometry",
+			"TorusGeometry": "api/geometries/TorusGeometry",
+			"TorusKnotBufferGeometry": "api/geometries/TorusKnotBufferGeometry",
+			"TorusKnotGeometry": "api/geometries/TorusKnotGeometry",
+			"TubeGeometry": "api/geometries/TubeGeometry",
+			"TubeBufferGeometry": "api/geometries/TubeBufferGeometry",
+			"WireframeGeometry": "api/geometries/WireframeGeometry"
+		},
+
+		"Helpers": {
+			"ArrowHelper": "api/helpers/ArrowHelper",
+			"AxisHelper": "api/helpers/AxisHelper",
+			"BoxHelper": "api/helpers/BoxHelper",
+			"CameraHelper": "api/helpers/CameraHelper",
+			"DirectionalLightHelper": "api/helpers/DirectionalLightHelper",
+			"FaceNormalsHelper": "api/helpers/FaceNormalsHelper",
+			"GridHelper": "api/helpers/GridHelper",
+			"PolarGridHelper": "api/helpers/PolarGridHelper",
+			"HemisphereLightHelper": "api/helpers/HemisphereLightHelper",
+			"PointLightHelper": "api/helpers/PointLightHelper",
+			"RectAreaLightHelper": "api/helpers/RectAreaLightHelper",
+			"SkeletonHelper": "api/helpers/SkeletonHelper",
+			"SpotLightHelper": "api/helpers/SpotLightHelper",
+			"VertexNormalsHelper": "api/helpers/VertexNormalsHelper"
+		},
+
+		"Lights": {
+			"AmbientLight": "api/lights/AmbientLight",
+			"DirectionalLight": "api/lights/DirectionalLight",
+			"HemisphereLight": "api/lights/HemisphereLight",
+			"Light": "api/lights/Light",
+			"PointLight": "api/lights/PointLight",
+			"RectAreaLight": "api/lights/RectAreaLight",
+			"SpotLight": "api/lights/SpotLight"
+		},
+
+		"Lights / Shadows": {
+			"DirectionalLightShadow": "api/lights/shadows/DirectionalLightShadow",
+			"LightShadow": "api/lights/shadows/LightShadow",
+			"RectAreaLightShadow": "api/lights/shadows/RectAreaLightShadow",
+			"SpotLightShadow": "api/lights/shadows/SpotLightShadow"
+		},
+
+		"Loaders": {
+			"AnimationLoader": "api/loaders/AnimationLoader",
+			"AudioLoader": "api/loaders/AudioLoader",
+			"BufferGeometryLoader": "api/loaders/BufferGeometryLoader",
+			"Cache": "api/loaders/Cache",
+			"CompressedTextureLoader": "api/loaders/CompressedTextureLoader",
+			"CubeTextureLoader": "api/loaders/CubeTextureLoader",
+			"DataTextureLoader": "api/loaders/DataTextureLoader",
+			"FileLoader": "api/loaders/FileLoader",
+			"FontLoader": "api/loaders/FontLoader",
+			"ImageLoader": "api/loaders/ImageLoader",
+			"JSONLoader": "api/loaders/JSONLoader",
+			"Loader": "api/loaders/Loader",
+			"MaterialLoader": "api/loaders/MaterialLoader",
+			"ObjectLoader": "api/loaders/ObjectLoader",
+			"TextureLoader": "api/loaders/TextureLoader"
+		},
+
+		"Loaders / Managers": {
+			"DefaultLoadingManager": "api/loaders/managers/DefaultLoadingManager",
+			"LoadingManager": "api/loaders/managers/LoadingManager"
+		},
+
+		"Materials": {
+			"LineBasicMaterial": "api/materials/LineBasicMaterial",
+			"LineDashedMaterial": "api/materials/LineDashedMaterial",
+			"Material": "api/materials/Material",
+			"MeshBasicMaterial": "api/materials/MeshBasicMaterial",
+			"MeshDepthMaterial": "api/materials/MeshDepthMaterial",
+			"MeshLambertMaterial": "api/materials/MeshLambertMaterial",
+			"MeshNormalMaterial": "api/materials/MeshNormalMaterial",
+			"MeshPhongMaterial": "api/materials/MeshPhongMaterial",
+			"MeshPhysicalMaterial": "api/materials/MeshPhysicalMaterial",
+			"MeshStandardMaterial": "api/materials/MeshStandardMaterial",
+			"MeshToonMaterial": "api/materials/MeshToonMaterial",
+			"PointsMaterial": "api/materials/PointsMaterial",
+			"RawShaderMaterial": "api/materials/RawShaderMaterial",
+			"ShaderMaterial": "api/materials/ShaderMaterial",
+			"ShadowMaterial": "api/materials/ShadowMaterial",
+			"SpriteMaterial": "api/materials/SpriteMaterial"
+		},
+
+		"Math": {
+			"Box2": "api/math/Box2",
+			"Box3": "api/math/Box3",
+			"Color": "api/math/Color",
+			"Cylindrical": "api/math/Cylindrical",
+			"Euler": "api/math/Euler",
+			"Frustum": "api/math/Frustum",
+			"Interpolant": "api/math/Interpolant",
+			"Line3": "api/math/Line3",
+			"Math": "api/math/Math",
+			"Matrix3": "api/math/Matrix3",
+			"Matrix4": "api/math/Matrix4",
+			"Plane": "api/math/Plane",
+			"Quaternion": "api/math/Quaternion",
+			"Ray": "api/math/Ray",
+			"Sphere": "api/math/Sphere",
+			"Spherical": "api/math/Spherical",
+			"Triangle": "api/math/Triangle",
+			"Vector2": "api/math/Vector2",
+			"Vector3": "api/math/Vector3",
+			"Vector4": "api/math/Vector4"
+		},
+
+		"Math / Interpolants": {
+			"CubicInterpolant": "api/math/interpolants/CubicInterpolant",
+			"DiscreteInterpolant": "api/math/interpolants/DiscreteInterpolant",
+			"LinearInterpolant": "api/math/interpolants/LinearInterpolant",
+			"QuaternionLinearInterpolant": "api/math/interpolants/QuaternionLinearInterpolant"
+		},
+
+		"Objects": {
+			"Bone": "api/objects/Bone",
+			"Group": "api/objects/Group",
+			"LensFlare": "api/objects/LensFlare",
+			"Line": "api/objects/Line",
+			"LineLoop": "api/objects/LineLoop",
+			"LineSegments": "api/objects/LineSegments",
+			"LOD": "api/objects/LOD",
+			"Mesh": "api/objects/Mesh",
+			"Points": "api/objects/Points",
+			"Skeleton": "api/objects/Skeleton",
+			"SkinnedMesh": "api/objects/SkinnedMesh",
+			"Sprite": "api/objects/Sprite"
+		},
+
+		"Renderers": {
+			"WebGLRenderer": "api/renderers/WebGLRenderer",
+			"WebGLRenderTarget": "api/renderers/WebGLRenderTarget",
+			"WebGLRenderTargetCube": "api/renderers/WebGLRenderTargetCube"
+		},
+
+		"Renderers / Shaders": {
+			"ShaderChunk": "api/renderers/shaders/ShaderChunk",
+			"ShaderLib": "api/renderers/shaders/ShaderLib",
+			"UniformsLib": "api/renderers/shaders/UniformsLib",
+			"UniformsUtils": "api/renderers/shaders/UniformsUtils"
+		},
+
+		"Scenes": {
+			"Fog": "api/scenes/Fog",
+			"FogExp2": "api/scenes/FogExp2",
+			"Scene": "api/scenes/Scene"
+		},
+
+		"Textures": {
+			"CanvasTexture": "api/textures/CanvasTexture",
+			"CompressedTexture": "api/textures/CompressedTexture",
+			"CubeTexture": "api/textures/CubeTexture",
+			"DataTexture": "api/textures/DataTexture",
+			"DepthTexture": "api/textures/DepthTexture",
+			"Texture": "api/textures/Texture",
+			"VideoTexture": "api/textures/VideoTexture"
+		}
 
 	},
 
 	"Examples": {
 
-		"Collada Animation": [
-			[ "ColladaAnimation", "examples/collada/Animation" ],
-			[ "AnimationHandler", "examples/collada/AnimationHandler" ],
-			[ "KeyFrameAnimation", "examples/collada/KeyFrameAnimation" ]
-		],
-
-		"Geometries": [
-			[ "ConvexBufferGeometry", "examples/geometries/ConvexBufferGeometry" ],
-			[ "ConvexGeometry", "examples/geometries/ConvexGeometry" ]
-		],
-
-		"Loaders": [
-			[ "BabylonLoader", "examples/loaders/BabylonLoader" ],
-			[ "ColladaLoader", "examples/loaders/ColladaLoader" ],
-			[ "GLTF2Loader", "examples/loaders/GLTF2Loader" ],
-			[ "MTLLoader", "examples/loaders/MTLLoader" ],
-			[ "OBJLoader", "examples/loaders/OBJLoader" ],
-			[ "PCDLoader", "examples/loaders/PCDLoader" ],
-			[ "PDBLoader", "examples/loaders/PDBLoader" ],
-			[ "SVGLoader", "examples/loaders/SVGLoader" ],
-			[ "TGALoader", "examples/loaders/TGALoader" ]
-		],
-
-		"Plugins": [
-			[ "CombinedCamera", "examples/cameras/CombinedCamera" ],
-			[ "LookupTable", "examples/Lut" ],
-			[ "SpriteCanvasMaterial", "examples/SpriteCanvasMaterial" ]
-		],
-
-		"QuickHull": [
-			[ "Face", "examples/quickhull/Face" ],
-			[ "HalfEdge", "examples/quickhull/HalfEdge" ],
-			[ "QuickHull", "examples/quickhull/QuickHull" ],
-			[ "VertexNode", "examples/quickhull/VertexNode" ],
-			[ "VertexList", "examples/quickhull/VertexList" ]
-		],
-
-		"Renderers": [
-			[ "CanvasRenderer", "examples/renderers/CanvasRenderer" ]
-		]
+		"Collada Animation": {
+			"ColladaAnimation": "examples/collada/Animation",
+			"AnimationHandler": "examples/collada/AnimationHandler",
+			"KeyFrameAnimation": "examples/collada/KeyFrameAnimation"
+		},
+
+		"Geometries": {
+			"ConvexBufferGeometry": "examples/geometries/ConvexBufferGeometry",
+			"ConvexGeometry": "examples/geometries/ConvexGeometry"
+		},
+
+		"Loaders": {
+			"BabylonLoader": "examples/loaders/BabylonLoader",
+			"ColladaLoader": "examples/loaders/ColladaLoader",
+			"GLTF2Loader": "examples/loaders/GLTF2Loader",
+			"MTLLoader": "examples/loaders/MTLLoader",
+			"OBJLoader": "examples/loaders/OBJLoader",
+			"PCDLoader": "examples/loaders/PCDLoader",
+			"PDBLoader": "examples/loaders/PDBLoader",
+			"SVGLoader": "examples/loaders/SVGLoader",
+			"TGALoader": "examples/loaders/TGALoader"
+		},
+
+		"Plugins": {
+			"CombinedCamera": "examples/cameras/CombinedCamera",
+			"LookupTable": "examples/Lut",
+			"SpriteCanvasMaterial": "examples/SpriteCanvasMaterial"
+		},
+
+		"QuickHull": {
+			"Face": "examples/quickhull/Face",
+			"HalfEdge": "examples/quickhull/HalfEdge",
+			"QuickHull": "examples/quickhull/QuickHull",
+			"VertexNode": "examples/quickhull/VertexNode",
+			"VertexList": "examples/quickhull/VertexList"
+		},
+
+		"Renderers": {
+			"CanvasRenderer": "examples/renderers/CanvasRenderer"
+		}
 
 	},
 
 	"Developer Reference": {
-		"Polyfills": [
-			[ "Polyfills", "api/Polyfills" ]
-		],
-
-		"WebGLRenderer": [
-			[ "WebGLProgram", "api/renderers/webgl/WebGLProgram" ],
-			[ "WebGLShader", "api/renderers/webgl/WebGLShader" ],
-			[ "WebGLState", "api/renderers/webgl/WebGLState" ]
-		],
-
-		"WebGLRenderer / Plugins": [
-			[ "LensFlarePlugin", "api/renderers/webgl/plugins/LensFlarePlugin" ],
-			[ "SpritePlugin", "api/renderers/webgl/plugins/SpritePlugin" ]
-		]
-
-	}
-
-};
-
-var pages = {};
-
-for ( var section in list ) {
-
-	pages[ section ] = {};
-
-	for ( var category in list[ section ] ) {
-
-		pages[ section ][ category ] = {};
 
-		for ( var i = 0; i < list[ section ][ category ].length; i ++ ) {
+		"Polyfills": {
+			"Polyfills": "api/Polyfills"
+		},
 
-			var page = list[ section ][ category ][ i ];
-			pages[ section ][ category ][ page[ 0 ] ] = page[ 1 ];
+		"WebGLRenderer": {
+			"WebGLProgram": "api/renderers/webgl/WebGLProgram",
+			"WebGLShader": "api/renderers/webgl/WebGLShader",
+			"WebGLState": "api/renderers/webgl/WebGLState"
+		},
 
+		"WebGLRenderer / Plugins": {
+			"LensFlarePlugin": "api/renderers/webgl/plugins/LensFlarePlugin",
+			"SpritePlugin": "api/renderers/webgl/plugins/SpritePlugin"
 		}
 
 	}

+ 119 - 119
docs/manual/introduction/Animation-system.html

@@ -8,123 +8,123 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-    <h1>[name]</h1>
-       
-    <h2>Overview</h2>
-
-    <div class="desc">
-        Within the three.js animation system you can animate various properties of your models:
-        the bones of a [page:SkinnedMesh skinned and rigged model], 
-        [page:Geometry.morphTargets morph targets], different material properties (colors, 
-        opacity, booleans), visibility and transforms. The animated properties can be faded in,
-        faded out, crossfaded and warped. The weight and time scales of different simultaneous 
-        animations on the same object as well as on different objects can be changed 
-        independently. Various animations on the same and on different objects can be 
-        synchronized.<br /><br />
-
-        To achieve all this in one homogeneous system, the three.js animation system
-        [link:https://github.com/mrdoob/three.js/issues/6881 has completely changed in 2015]
-        (be aware of outdated information!), and it has now an architecture similar to 
-        Unity/Unreal Engine 4. This page gives a short overview of the main components of the 
-        system and how they work together.
-        
-    </div>
-           
-    <h3>Animation Clips</h3>
-        
-    <div class="desc">        
-       
-        If you have successfully imported an animated 3D object (it doesn't matter if it has
-        bones or morph targets or both) - for example exporting it from Blender with the 
-        [link:https://github.com/mrdoob/three.js/tree/master/utils/exporters/blender/addons/io_three Blender exporter] and
-        loading it into a three.js scene using [page:JSONLoader] -, one of the geometry's 
-        properties of the loaded mesh should be an array named "animations", containing the 
-        [page:AnimationClip AnimationClips] for this model (see a list of possible loaders below).<br /><br />
-
-        Each *AnimationClip* usually holds the data for a certain activity of the object. If the 
-        mesh is a character, for example, there may be one AnimationClip for a walkcycle, a second 
-        for a jump, a third for sidestepping and so on.
-
-    </div>
-           
-    <h3>Keyframe Tracks</h3>
-        
-    <div class="desc">        
-       
-        Inside of such an *AnimationClip* the data for each animated property are stored in a 
-        separate [page:KeyframeTrack]. Assumed a character object has a [page:Skeleton skeleton],
-        one keyframe track could store the data for the position changes of the lower arm bone 
-        over time, a different track the data for the rotation changes of the same bone, a third 
-        the track position, rotation or scaling of another bone, and so on. It should be clear, 
-        that an AnimationClip can be composed of lots of such tracks.<br /><br />
-        
-        Assumed the model has [page:Geometry.morphTargets morph targets] (for example one morph 
-        target showing a friendly face and another showing an angry face), each track holds the 
-        information as to how the [page:Mesh.morphTargetInfluences influence] of a certain morph 
-        target changes during the performance of the clip.
-
-    </div>
-           
-    <h3>Animation Mixer</h3>
-        
-    <div class="desc">
-        
-        The stored data form only the basis for the animations - actual playback is controlled by 
-        the [page:AnimationMixer]. You can imagine this not only as a player for animations, but 
-        as a simulation of a hardware like a real mixer console, which can control several animations 
-        simultaneously, blending and merging them.
-
-    </div>
-           
-    <h3>Animation Actions</h3>
-        
-    <div class="desc">
-              
-        The *AnimationMixer* itself has only very few (general) properties and methods, because it 
-        can be controlled by the [page:AnimationAction AnimationActions]. By configuring an 
-        *AnimationAction* you can determine when a certain *AnimationClip* shall be played, paused 
-        or stopped on one of the mixers, if and how often the clip has to be repeated, whether it 
-        shall be performed with a fade or a time scaling, and some additional things, such crossfading 
-        or synchronizing.
-
-    </div>          
-           
-    <h3>Animation Object Groups</h3>
-        
-    <div class="desc">
-              
-        If you want a group of objects to receive a shared animation state, you can use an
-        [page:AnimationObjectGroup].  
-
-    </div>          
-            
-    <h3>Supported Formats and Loaders</h3>
-        
-    <div class="desc">
-		Note that not all model formats include animation (OBJ notably does not), and that only some
-		three.js loaders support [page:AnimationClip AnimationClip] sequences. Several that <i>do</i>
-		support this animation type:
-    </div>
-
-		<ul>
-			<li>[page:JSONLoader THREE.JSONLoader]</li>
-			<li>[page:ObjectLoader THREE.ObjectLoader]</li>
-			<li>THREE.BVHLoader</li>
-			<li>THREE.FBXLoader</li>
-			<li>THREE.FBXLoader2</li>
-			<li>[page:GLTFLoader THREE.GLTFLoader]</li>
-			<li>THREE.MMDLoader</li>
-			<li>THREE.SEA3DLoader</li>
-		</ul>
-   
-    <div class="desc">
-		Note that 3ds max and Maya currently can't export multiple animations (meaning animations which are not
-        on the same timeline) directly to a single file.
-    </div>
-    
-	<h2>Example</h2>
-
-	<code>
+		<h1>[name]</h1>
+
+		<h2>Overview</h2>
+
+		<div class="desc">
+			Within the three.js animation system you can animate various properties of your models:
+			the bones of a [page:SkinnedMesh skinned and rigged model],
+			[page:Geometry.morphTargets morph targets], different material properties (colors,
+			opacity, booleans), visibility and transforms. The animated properties can be faded in,
+			faded out, crossfaded and warped. The weight and time scales of different simultaneous
+			animations on the same object as well as on different objects can be changed
+			independently. Various animations on the same and on different objects can be
+			synchronized.<br /><br />
+
+			To achieve all this in one homogeneous system, the three.js animation system
+			[link:https://github.com/mrdoob/three.js/issues/6881 has completely changed in 2015]
+			(be aware of outdated information!), and it has now an architecture similar to
+			Unity/Unreal Engine 4. This page gives a short overview of the main components of the
+			system and how they work together.
+
+		</div>
+
+		<h3>Animation Clips</h3>
+
+		<div class="desc">
+
+			If you have successfully imported an animated 3D object (it doesn't matter if it has
+			bones or morph targets or both) - for example exporting it from Blender with the
+			[link:https://github.com/mrdoob/three.js/tree/master/utils/exporters/blender/addons/io_three Blender exporter] and
+			loading it into a three.js scene using [page:JSONLoader] -, one of the geometry's
+			properties of the loaded mesh should be an array named "animations", containing the
+			[page:AnimationClip AnimationClips] for this model (see a list of possible loaders below).<br /><br />
+
+			Each *AnimationClip* usually holds the data for a certain activity of the object. If the
+			mesh is a character, for example, there may be one AnimationClip for a walkcycle, a second
+			for a jump, a third for sidestepping and so on.
+
+		</div>
+
+		<h3>Keyframe Tracks</h3>
+
+		<div class="desc">
+
+			Inside of such an *AnimationClip* the data for each animated property are stored in a
+			separate [page:KeyframeTrack]. Assumed a character object has a [page:Skeleton skeleton],
+			one keyframe track could store the data for the position changes of the lower arm bone
+			over time, a different track the data for the rotation changes of the same bone, a third
+			the track position, rotation or scaling of another bone, and so on. It should be clear,
+			that an AnimationClip can be composed of lots of such tracks.<br /><br />
+
+			Assumed the model has [page:Geometry.morphTargets morph targets] (for example one morph
+			target showing a friendly face and another showing an angry face), each track holds the
+			information as to how the [page:Mesh.morphTargetInfluences influence] of a certain morph
+			target changes during the performance of the clip.
+
+		</div>
+
+		<h3>Animation Mixer</h3>
+
+		<div class="desc">
+
+			The stored data form only the basis for the animations - actual playback is controlled by
+			the [page:AnimationMixer]. You can imagine this not only as a player for animations, but
+			as a simulation of a hardware like a real mixer console, which can control several animations
+			simultaneously, blending and merging them.
+
+		</div>
+
+		<h3>Animation Actions</h3>
+
+		<div class="desc">
+
+			The *AnimationMixer* itself has only very few (general) properties and methods, because it
+			can be controlled by the [page:AnimationAction AnimationActions]. By configuring an
+			*AnimationAction* you can determine when a certain *AnimationClip* shall be played, paused
+			or stopped on one of the mixers, if and how often the clip has to be repeated, whether it
+			shall be performed with a fade or a time scaling, and some additional things, such crossfading
+			or synchronizing.
+
+		</div>
+
+		<h3>Animation Object Groups</h3>
+
+		<div class="desc">
+
+			If you want a group of objects to receive a shared animation state, you can use an
+			[page:AnimationObjectGroup]. 
+
+		</div>
+
+		<h3>Supported Formats and Loaders</h3>
+
+		<div class="desc">
+			Note that not all model formats include animation (OBJ notably does not), and that only some
+			three.js loaders support [page:AnimationClip AnimationClip] sequences. Several that <i>do</i>
+			support this animation type:
+		</div>
+
+			<ul>
+				<li>[page:JSONLoader THREE.JSONLoader]</li>
+				<li>[page:ObjectLoader THREE.ObjectLoader]</li>
+				<li>THREE.BVHLoader</li>
+				<li>THREE.FBXLoader</li>
+				<li>THREE.FBXLoader2</li>
+				<li>[page:GLTF2Loader THREE.GLTF2Loader]</li>
+				<li>THREE.MMDLoader</li>
+				<li>THREE.SEA3DLoader</li>
+			</ul>
+
+		<div class="desc">
+			Note that 3ds max and Maya currently can't export multiple animations (meaning animations which are not
+			on the same timeline) directly to a single file.
+		</div>
+
+		<h2>Example</h2>
+
+		<code>
 		var mesh;
 
 		// Create an AnimationMixer, and get the list of AnimationClip instances
@@ -145,7 +145,7 @@
 		clips.forEach( function ( clip ) {
 			mixer.clipAction( clip ).play();
 		} );
-	</code>
+		</code>
 
-  </body>
+	</body>
 </html>

+ 1 - 1
docs/manual/introduction/Code-style-guide.html

@@ -16,7 +16,7 @@
 			if you are adding code to the library or examples then you must follow this guide.<br /><br />
 
 			You can find details
-			<a href="https://github.com/mrdoob/three.js/wiki/Mr.doob%27s-Code-Style%E2%84%A2">here</a>.
+			<a href="https://github.com/mrdoob/three.js/wiki/Mr.doob%27s-Code-Style%E2%84%A2" target="_blank">here</a>.
 		</div>
 	</body>
 </html>

+ 8 - 4
docs/page.css

@@ -17,6 +17,8 @@ body {
 
 a {
 	color: #1184CE;
+	cursor: pointer;
+	text-decoration: underline;
 }
 
 h1 {
@@ -56,6 +58,7 @@ pre, code {
 	margin-top: 20px;
 	margin-bottom: 20px;
 }
+
 code {
 	display: block;
 	width: -webkit-calc( 100% - 40px );
@@ -77,6 +80,7 @@ th {
 	padding: 10px;
 	text-decoration: underline;
 }
+
 td {
 	text-align: center;
 }
@@ -102,10 +106,10 @@ strong {
 	opacity: 0.5;
 }
 
-	#button:hover {
-		cursor: pointer;
-		opacity: 1;
-	}
+#button:hover {
+	cursor: pointer;
+	opacity: 1;
+}
 
 a.permalink {
 	float: right;

+ 29 - 3
docs/page.js

@@ -1,3 +1,29 @@
+if ( !window.frameElement && window.location.protocol !== 'file:' ) {
+
+	// If the page is not yet displayed as an iframe of the index page (navigation panel/working links),
+	// redirect to the index page (using the current URL without extension as the new fragment).
+	// If this URL itself has a fragment, append it with a dot (since '#' in an URL fragment is not allowed).
+
+	var href = window.location.href;
+	var splitIndex = href.lastIndexOf( '/docs/' ) + 6;
+	var docsBaseURL = href.substr( 0, splitIndex );
+
+	var hash = window.location.hash;
+
+	if ( hash !== '' ) {
+
+		href = href.replace( hash, '' );
+		hash = hash.replace( '#', '.' );
+
+	}
+
+	var pathSnippet = href.slice( splitIndex, -5 );
+
+	window.location.replace( docsBaseURL + '#' + pathSnippet + hash );
+
+}
+
+
 var onDocumentLoad = function ( event ) {
 
 	var path;
@@ -30,11 +56,11 @@ var onDocumentLoad = function ( event ) {
 	text = text.replace( /\[path\]/gi, path );
 	text = text.replace( /\[page:([\w\.]+)\]/gi, "[page:$1 $1]" ); // [page:name] to [page:name title]
 	text = text.replace( /\[page:\.([\w\.]+) ([\w\.\s]+)\]/gi, "[page:" + name + ".$1 $2]" ); // [page:.member title] to [page:name.member title]
-	text = text.replace( /\[page:([\w\.]+) ([\w\.\s]+)\]/gi, "<a href=\"javascript:window.parent.goTo('$1')\" title=\"$1\">$2</a>" ); // [page:name title]
-	// text = text.replace( /\[member:.([\w]+) ([\w\.\s]+)\]/gi, "<a href=\"javascript:window.parent.goTo('" + name + ".$1')\" title=\"$1\">$2</a>" );
+	text = text.replace( /\[page:([\w\.]+) ([\w\.\s]+)\]/gi, "<a onclick=\"window.parent.setUrlFragment('$1')\" title=\"$1\">$2</a>" ); // [page:name title]
+	// text = text.replace( /\[member:.([\w]+) ([\w\.\s]+)\]/gi, "<a onclick=\"window.parent.setUrlFragment('" + name + ".$1')\" title=\"$1\">$2</a>" );
 
 	text = text.replace( /\[(?:member|property|method):([\w]+)\]/gi, "[member:$1 $1]" ); // [member:name] to [member:name title]
-	text = text.replace( /\[(?:member|property|method):([\w]+) ([\w\.\s]+)\]/gi, "<a href=\"javascript:window.parent.goTo('" + name + ".$2')\" target=\"_parent\" title=\"" + name + ".$2\" class=\"permalink\">#</a> .<a href=\"javascript:window.parent.goTo('$1')\" title=\"$1\" id=\"$2\">$2</a> " );
+	text = text.replace( /\[(?:member|property|method):([\w]+) ([\w\.\s]+)\]/gi, "<a onclick=\"window.parent.setUrlFragment('" + name + ".$2')\" target=\"_parent\" title=\"" + name + ".$2\" class=\"permalink\">#</a> .<a onclick=\"window.parent.setUrlFragment('$1')\" title=\"$1\" id=\"$2\">$2</a> " );
 
 	text = text.replace( /\[link:([\w|\:|\/|\.|\-|\_]+)\]/gi, "[link:$1 $1]" ); // [link:url] to [link:url title]
 	text = text.replace( /\[link:([\w|\:|\/|\.|\-|\_|\(|\)|\#]+) ([\w|\:|\/|\.|\-|\_|\s]+)\]/gi, "<a href=\"$1\"  target=\"_blank\">$2</a>" ); // [link:url title]

+ 3 - 1
docs/scenes/js/geometry.js

@@ -91,7 +91,9 @@ function updateGroupGeometry( mesh, geometry ) {
 
 }
 
-function CustomSinCurve( scale ){
+function CustomSinCurve( scale ) {
+
+	THREE.Curve.call( this );
 
 	this.scale = ( scale === undefined ) ? 1 : scale;
 

+ 1 - 2
editor/js/Editor.js

@@ -75,8 +75,7 @@ var Editor = function () {
 
 		showGridChanged: new Signal(),
 		refreshSidebarObject3D: new Signal(),
-		historyChanged: new Signal(),
-		refreshScriptEditor: new Signal()
+		historyChanged: new Signal()
 
 	};
 

+ 3 - 22
editor/js/Script.js

@@ -84,7 +84,7 @@ var Script = function ( editor ) {
 
 				if ( value !== currentScript.source ) {
 
-					editor.execute( new SetScriptValueCommand( currentObject, currentScript, 'source', value, codemirror.getCursor(), codemirror.getScrollInfo() ) );
+					editor.execute( new SetScriptValueCommand( currentObject, currentScript, 'source', value ) );
 
 				}
 				return;
@@ -407,7 +407,8 @@ var Script = function ( editor ) {
 
 		container.setDisplay( '' );
 		codemirror.setValue( source );
-		if (mode === 'json' ) mode = { name: 'javascript', json: true };
+		codemirror.clearHistory();
+		if ( mode === 'json' ) mode = { name: 'javascript', json: true };
 		codemirror.setOption( 'mode', mode );
 
 	} );
@@ -422,26 +423,6 @@ var Script = function ( editor ) {
 
 	} );
 
-	signals.refreshScriptEditor.add( function ( object, script, cursorPosition, scrollInfo ) {
-
-		if ( currentScript !== script ) return;
-
-		// copying the codemirror history because "codemirror.setValue(...)" alters its history
-
-		var history = codemirror.getHistory();
-		title.setValue( object.name + ' / ' + script.name );
-		codemirror.setValue( script.source );
-
-		if ( cursorPosition !== undefined ) {
-
-			codemirror.setCursor( cursorPosition );
-			codemirror.scrollTo( scrollInfo.left, scrollInfo.top );
-
-		}
-		codemirror.setHistory( history ); // setting the history to previous state
-
-	} );
-
 	return container;
 
 };

+ 0 - 85
editor/js/Sidebar.Animation.js

@@ -19,91 +19,6 @@ Sidebar.Animation = function ( editor ) {
 	var animationsRow = new UI.Row();
 	container.add( animationsRow );
 
-	/*
-
-	var animations = {};
-
-	signals.objectAdded.add( function ( object ) {
-
-		object.traverse( function ( child ) {
-
-			if ( child instanceof THREE.SkinnedMesh ) {
-
-				var material = child.material;
-
-				if ( material instanceof THREE.MultiMaterial ) {
-
-					for ( var i = 0; i < material.materials.length; i ++ ) {
-
-						material.materials[ i ].skinning = true;
-
-					}
-
-				} else {
-
-					child.material.skinning = true;
-
-				}
-
-				animations[ child.id ] = new THREE.Animation( child, child.geometry.animation );
-
-			} else if ( child instanceof THREE.MorphAnimMesh ) {
-
-				var animation = new THREE.MorphAnimation( child );
-				animation.duration = 30;
-
-				// temporal hack for THREE.AnimationHandler
-				animation._play = animation.play;
-				animation.play = function () {
-					this._play();
-					THREE.AnimationHandler.play( this );
-				};
-				animation.resetBlendWeights = function () {};
-				animation.stop = function () {
-					this.pause();
-					THREE.AnimationHandler.stop( this );
-				};
-
-				animations[ child.id ] = animation;
-
-			}
-
-		} );
-
-	} );
-
-	signals.objectSelected.add( function ( object ) {
-
-		container.setDisplay( 'none' );
-
-		if ( object instanceof THREE.SkinnedMesh || object instanceof THREE.MorphAnimMesh ) {
-
-			animationsRow.clear();
-
-			var animation = animations[ object.id ];
-
-			var playButton = new UI.Button( 'Play' ).onClick( function () {
-
-				animation.play();
-
-			} );
-			animationsRow.add( playButton );
-
-			var pauseButton = new UI.Button( 'Stop' ).onClick( function () {
-
-				animation.stop();
-
-			} );
-			animationsRow.add( pauseButton );
-
-			container.setDisplay( 'block' );
-
-		}
-
-	} );
-
-	*/
-
 	return container;
 
 };

+ 2 - 1
editor/js/Sidebar.Material.js

@@ -1279,7 +1279,8 @@ Sidebar.Material = function ( editor ) {
 
 	signals.objectSelected.add( function ( object ) {
 
-		if ( object && object.material ) {
+		if ( object && object.material &&
+			Array.isArray( object.material ) === false ) {
 
 			var objectChanged = object !== currentObject;
 

+ 21 - 1
editor/js/Sidebar.Scene.js

@@ -23,6 +23,26 @@ Sidebar.Scene = function ( editor ) {
 
 	}
 
+	function getMaterialName( material ) {
+
+		if ( Array.isArray( material ) ) {
+
+			var array = [];
+
+			for ( var i = 0; i < material.length; i ++ ) {
+
+				array.push( material[ i ].name );
+
+			}
+
+			return array.join( ',' );
+
+		}
+
+		return material.name;
+
+	}
+
 	function buildHTML( object ) {
 
 		var html = '<span class="type ' + object.type + '"></span> ' + object.name;
@@ -33,7 +53,7 @@ Sidebar.Scene = function ( editor ) {
 			var material = object.material;
 
 			html += ' <span class="type ' + geometry.type + '"></span> ' + geometry.name;
-			html += ' <span class="type ' + material.type + '"></span> ' + material.name;
+			html += ' <span class="type ' + material.type + '"></span> ' + getMaterialName( material );
 
 		}
 

+ 1 - 1
editor/js/Viewport.js

@@ -379,7 +379,7 @@ var Viewport = function ( editor ) {
 		selectionBox.visible = false;
 		transformControls.detach();
 
-		if ( object !== null && object !== scene ) {
+		if ( object !== null && object !== scene && object !== camera ) {
 
 			box.setFromObject( object );
 

+ 1 - 13
editor/js/commands/SetScriptValueCommand.js

@@ -8,12 +8,10 @@
  * @param script javascript object
  * @param attributeName string
  * @param newValue string, object
- * @param cursorPosition javascript object with format {line: 2, ch: 3}
- * @param scrollInfo javascript object with values {left, top, width, height, clientWidth, clientHeight}
  * @constructor
  */
 
-var SetScriptValueCommand = function ( object, script, attributeName, newValue, cursorPosition, scrollInfo ) {
+var SetScriptValueCommand = function ( object, script, attributeName, newValue ) {
 
 	Command.call( this );
 
@@ -27,8 +25,6 @@ var SetScriptValueCommand = function ( object, script, attributeName, newValue,
 	this.attributeName = attributeName;
 	this.oldValue = ( script !== undefined ) ? script[ this.attributeName ] : undefined;
 	this.newValue = newValue;
-	this.cursorPosition = cursorPosition;
-	this.scrollInfo = scrollInfo;
 
 };
 
@@ -39,7 +35,6 @@ SetScriptValueCommand.prototype = {
 		this.script[ this.attributeName ] = this.newValue;
 
 		this.editor.signals.scriptChanged.dispatch();
-		this.editor.signals.refreshScriptEditor.dispatch( this.object, this.script, this.cursorPosition, this.scrollInfo );
 
 	},
 
@@ -48,14 +43,11 @@ SetScriptValueCommand.prototype = {
 		this.script[ this.attributeName ] = this.oldValue;
 
 		this.editor.signals.scriptChanged.dispatch();
-		this.editor.signals.refreshScriptEditor.dispatch( this.object, this.script, this.cursorPosition, this.scrollInfo );
 
 	},
 
 	update: function ( cmd ) {
 
-		this.cursorPosition = cmd.cursorPosition;
-		this.scrollInfo = cmd.scrollInfo;
 		this.newValue = cmd.newValue;
 
 	},
@@ -69,8 +61,6 @@ SetScriptValueCommand.prototype = {
 		output.attributeName = this.attributeName;
 		output.oldValue = this.oldValue;
 		output.newValue = this.newValue;
-		output.cursorPosition = this.cursorPosition;
-		output.scrollInfo = this.scrollInfo;
 
 		return output;
 
@@ -85,8 +75,6 @@ SetScriptValueCommand.prototype = {
 		this.attributeName = json.attributeName;
 		this.object = this.editor.objectByUuid( json.objectUuid );
 		this.script = this.editor.scripts[ json.objectUuid ][ json.index ];
-		this.cursorPosition = json.cursorPosition;
-		this.scrollInfo = json.scrollInfo;
 
 	}
 

+ 5 - 2
examples/files.js

@@ -102,6 +102,9 @@ var files = {
 		"webgl_loader_msgpack",
 		"webgl_loader_obj",
 		"webgl_loader_obj_mtl",
+		"webgl_loader_obj2",
+		"webgl_loader_obj2_ww",
+		"webgl_loader_obj2_ww_parallels",
 		"webgl_loader_nrrd",
 		"webgl_loader_pcd",
 		"webgl_loader_pdb",
@@ -114,7 +117,6 @@ var files = {
 		"webgl_loader_sea3d_physics",
 		"webgl_loader_sea3d_skinning",
 		"webgl_loader_sea3d_sound",
-		"webgl_loader_scene",
 		"webgl_loader_stl",
 		"webgl_loader_utf8",
 		"webgl_loader_vrml",
@@ -280,10 +282,11 @@ var files = {
 		"webvr_daydream",
 		"webvr_panorama",
 		"webvr_rollercoaster",
-		"webvr_shadow",
+		"webvr_sandbox",
 		"webvr_video",
 		"webvr_vive",
 		"webvr_vive_dragging",
+		"webvr_vive_camerarig",
 		"webvr_vive_paint",
 		"webvr_vive_sculpt"
 	],

+ 37 - 3
examples/js/CurveExtras.js

@@ -15,7 +15,11 @@
 
 	// GrannyKnot
 
-	function GrannyKnot() {}
+	function GrannyKnot() {
+
+		THREE.Curve.call( this );
+
+	}
 
 	GrannyKnot.prototype = Object.create( THREE.Curve.prototype );
 	GrannyKnot.prototype.constructor = GrannyKnot;
@@ -36,6 +40,8 @@
 
 	function HeartCurve( s ) {
 
+		THREE.Curve.call( this );
+
 		this.scale = ( s === undefined ) ? 5 : s;
 
 	}
@@ -59,6 +65,8 @@
 
 	function VivianiCurve( radius ) {
 
+		THREE.Curve.call( this );
+
 		this.radius = radius;
 
 	}
@@ -81,7 +89,11 @@
 
 	// KnotCurve
 
-	function KnotCurve() {}
+	function KnotCurve() {
+
+		THREE.Curve.call( this );
+
+	}
 
 	KnotCurve.prototype = Object.create( THREE.Curve.prototype );
 	KnotCurve.prototype.constructor = KnotCurve;
@@ -103,7 +115,11 @@
 
 	// HelixCurve
 
-	function HelixCurve() {}
+	function HelixCurve() {
+
+		THREE.Curve.call( this );
+
+	}
 
 	HelixCurve.prototype = Object.create( THREE.Curve.prototype );
 	HelixCurve.prototype.constructor = HelixCurve;
@@ -127,6 +143,8 @@
 
 	function TrefoilKnot( s ) {
 
+		THREE.Curve.call( this );
+
 		this.scale = ( s === undefined ) ? 10 : s;
 
 	}
@@ -150,6 +168,8 @@
 
 	function TorusKnot( s ) {
 
+		THREE.Curve.call( this );
+
 		this.scale = ( s === undefined ) ? 10 : s;
 
 	}
@@ -176,6 +196,8 @@
 
 	function CinquefoilKnot( s ) {
 
+		THREE.Curve.call( this );
+
 		this.scale = ( s === undefined ) ? 10 : s;
 
 	}
@@ -202,6 +224,8 @@
 
 	function TrefoilPolynomialKnot( s ) {
 
+		THREE.Curve.call( this );
+
 		this.scale = ( s === undefined ) ? 10 : s;
 
 	}
@@ -232,6 +256,8 @@
 
 	function FigureEightPolynomialKnot( s ) {
 
+		THREE.Curve.call( this );
+
 		this.scale = ( s === undefined ) ? 1 : s;
 
 	}
@@ -255,6 +281,8 @@
 
 	function DecoratedTorusKnot4a( s ) {
 
+		THREE.Curve.call( this );
+
 		this.scale = ( s === undefined ) ? 40 : s;
 
 	}
@@ -278,6 +306,8 @@
 
 	function DecoratedTorusKnot4b( s ) {
 
+		THREE.Curve.call( this );
+
 		this.scale = ( s === undefined ) ? 40 : s;
 
 	}
@@ -301,6 +331,8 @@
 
 	function DecoratedTorusKnot5a( s ) {
 
+		THREE.Curve.call( this );
+
 		this.scale = ( s === undefined ) ? 40 : s;
 
 	}
@@ -324,6 +356,8 @@
 
 	function DecoratedTorusKnot5c( s ) {
 
+		THREE.Curve.call( this );
+
 		this.scale = ( s === undefined ) ? 40 : s;
 
 	}

+ 0 - 73
examples/js/MorphAnimation.js

@@ -1,73 +0,0 @@
-/**
- * @author mrdoob / http://mrdoob.com
- * @author willy-vvu / http://willy-vvu.github.io
- */
-
-THREE.MorphAnimation = function ( mesh ) {
-
-	this.mesh = mesh;
-	this.frames = mesh.morphTargetInfluences.length;
-	this.currentTime = 0;
-	this.duration = 1000;
-	this.loop = true;
-	this.lastFrame = 0;
-	this.currentFrame = 0;
-
-	this.isPlaying = false;
-
-};
-
-THREE.MorphAnimation.prototype = {
-
-	constructor: THREE.MorphAnimation,
-
-	play: function () {
-
-		this.isPlaying = true;
-
-	},
-
-	pause: function () {
-
-		this.isPlaying = false;
-
-	},
-
-	update: function ( delta ) {
-
-		if ( this.isPlaying === false ) return;
-
-		this.currentTime += delta;
-
-		if ( this.loop === true && this.currentTime > this.duration ) {
-
-			this.currentTime %= this.duration;
-
-		}
-
-		this.currentTime = Math.min( this.currentTime, this.duration );
-
-		var frameTime = this.duration / this.frames;
-		var frame = Math.floor( this.currentTime / frameTime );
-
-		var influences = this.mesh.morphTargetInfluences;
-
-		if ( frame !== this.currentFrame ) {
-
-			influences[ this.lastFrame ] = 0;
-			influences[ this.currentFrame ] = 1;
-			influences[ frame ] = 0;
-
-			this.lastFrame = this.currentFrame;
-			this.currentFrame = frame;
-
-		}
-
-		var mix = ( this.currentTime % frameTime ) / frameTime;
-
-		influences[ frame ] = mix;
-		influences[ this.lastFrame ] = 1 - mix;
-
-	}
-
-};

+ 43 - 21
examples/js/ParametricGeometries.js

@@ -7,7 +7,9 @@
 
 THREE.ParametricGeometries = {
 
-	klein: function ( v, u ) {
+	klein: function ( v, u, optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
 
 		u *= Math.PI;
 		v *= 2 * Math.PI;
@@ -28,25 +30,29 @@ THREE.ParametricGeometries = {
 
 		y = - 2 * ( 1 - Math.cos( u ) / 2 ) * Math.sin( v );
 
-		return new THREE.Vector3( x, y, z );
+		return result.set( x, y, z );
 
 	},
 
 	plane: function ( width, height ) {
 
-		return function( u, v ) {
+		return function( u, v, optionalTarget ) {
+
+			var result = optionalTarget || new THREE.Vector3();
 
 			var x = u * width;
 			var y = 0;
 			var z = v * height;
 
-			return new THREE.Vector3( x, y, z );
+			return result.set( x, y, z );
 
 		};
 
 	},
 
-	mobius: function( u, t ) {
+	mobius: function( u, t, optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
 
 		// flat mobius strip
 		// http://www.wolframalpha.com/input/?i=M%C3%B6bius+strip+parametric+equations&lk=1&a=ClashPrefs_*Surface.MoebiusStrip.SurfaceProperty.ParametricEquations-
@@ -56,28 +62,36 @@ THREE.ParametricGeometries = {
 		var x, y, z;
 
 		var a = 2;
+
 		x = Math.cos( v ) * ( a + u * Math.cos( v / 2 ) );
 		y = Math.sin( v ) * ( a + u * Math.cos( v / 2 ) );
 		z = u * Math.sin( v / 2 );
-		return new THREE.Vector3( x, y, z );
+
+		return result.set( x, y, z );
 
 	},
 
-	mobius3d: function( u, t ) {
+	mobius3d: function( u, t, optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
 
 		// volumetric mobius strip
+
 		u *= Math.PI;
 		t *= 2 * Math.PI;
 
 		u = u * 2;
 		var phi = u / 2;
 		var major = 2.25, a = 0.125, b = 0.65;
+
 		var x, y, z;
+
 		x = a * Math.cos( t ) * Math.cos( phi ) - b * Math.sin( t ) * Math.sin( phi );
 		z = a * Math.cos( t ) * Math.sin( phi ) + b * Math.sin( t ) * Math.cos( phi );
 		y = ( major + x ) * Math.sin( u );
 		x = ( major + x ) * Math.cos( u );
-		return new THREE.Vector3( x, y, z );
+
+		return result.set( x, y, z );
 
 	}
 
@@ -108,7 +122,7 @@ THREE.ParametricGeometries.TubeGeometry = function( path, segments, radius, segm
 
 		x, y, z, tx, ty, tz, u, v,
 
-		cx, cy, pos, pos2 = new THREE.Vector3(),
+		cx, cy, pos,
 		i, j, ip, jp, a, b, c, d, uva, uvb, uvc, uvd;
 
 	var frames = path.computeFrenetFrames( segments, closed ),
@@ -123,7 +137,9 @@ THREE.ParametricGeometries.TubeGeometry = function( path, segments, radius, segm
 
 
 
-	var ParametricTube = function( u, v ) {
+	var ParametricTube = function( u, v, optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
 
 		v *= 2 * Math.PI;
 
@@ -147,12 +163,11 @@ THREE.ParametricGeometries.TubeGeometry = function( path, segments, radius, segm
 		cx = - scope.radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
 		cy = scope.radius * Math.sin( v );
 
-		pos2.copy( pos );
-		pos2.x += cx * normal.x + cy * binormal.x;
-		pos2.y += cx * normal.y + cy * binormal.y;
-		pos2.z += cx * normal.z + cy * binormal.z;
+		pos.x += cx * normal.x + cy * binormal.x;
+		pos.y += cx * normal.y + cy * binormal.y;
+		pos.z += cx * normal.z + cy * binormal.z;
 
-		return pos2.clone();
+		return result.copy( pos );
 
 	};
 
@@ -180,7 +195,11 @@ THREE.ParametricGeometries.TorusKnotGeometry = function ( radius, tube, segments
 	this.p = p || 2;
 	this.q = q || 3;
 
-	function TorusKnotCurve() {}
+	function TorusKnotCurve() {
+
+		THREE.Curve.call( this );
+
+	}
 
 	TorusKnotCurve.prototype = Object.create( THREE.Curve.prototype );
 	TorusKnotCurve.prototype.constructor = TorusKnotCurve;
@@ -218,7 +237,9 @@ THREE.ParametricGeometries.TorusKnotGeometry.prototype.constructor = THREE.Param
   *********************************************/
 THREE.ParametricGeometries.SphereGeometry = function( size, u, v ) {
 
-	function sphere( u, v ) {
+	function sphere( u, v, optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
 
 		u *= Math.PI;
 		v *= 2 * Math.PI;
@@ -227,8 +248,7 @@ THREE.ParametricGeometries.SphereGeometry = function( size, u, v ) {
 		var y = size * Math.sin( u ) * Math.sin( v );
 		var z = size * Math.cos( u );
 
-
-		return new THREE.Vector3( x, y, z );
+		return result.set( x, y, z );
 
 	}
 
@@ -248,13 +268,15 @@ THREE.ParametricGeometries.SphereGeometry.prototype.constructor = THREE.Parametr
 
 THREE.ParametricGeometries.PlaneGeometry = function( width, depth, segmentsWidth, segmentsDepth ) {
 
-	function plane( u, v ) {
+	function plane( u, v, optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
 
 		var x = u * width;
 		var y = 0;
 		var z = v * depth;
 
-		return new THREE.Vector3( x, y, z );
+		return result.set( x, y, z );
 
 	}
 

+ 59 - 59
examples/js/RollerCoaster.js

@@ -2,7 +2,7 @@
  * @author mrdoob / http://mrdoob.com/
  */
 
-var RollerCoasterGeometry = function ( curve, size ) {
+function RollerCoasterGeometry( curve, divisions ) {
 
 	THREE.BufferGeometry.call( this );
 
@@ -28,13 +28,13 @@ var RollerCoasterGeometry = function ( curve, size ) {
 	// shapes
 
 	var step = [
-		new THREE.Vector3( -2.25,  0, 0 ),
-		new THREE.Vector3(  0,  -0.5, 0 ),
-		new THREE.Vector3(  0, -1.75, 0 ),
+		new THREE.Vector3( -0.225,  0, 0 ),
+		new THREE.Vector3(  0, -0.050, 0 ),
+		new THREE.Vector3(  0, -0.175, 0 ),
 
-		new THREE.Vector3(  0,  -0.5, 0 ),
-		new THREE.Vector3(  2.25,  0, 0 ),
-		new THREE.Vector3(  0, -1.75, 0 )
+		new THREE.Vector3(  0, -0.050, 0 ),
+		new THREE.Vector3(  0.225,  0, 0 ),
+		new THREE.Vector3(  0, -0.175, 0 )
 	];
 
 	var PI2 = Math.PI * 2;
@@ -45,7 +45,7 @@ var RollerCoasterGeometry = function ( curve, size ) {
 	for ( var i = 0; i < sides; i ++ ) {
 
 		var angle = ( i / sides ) * PI2;
-		tube1.push( new THREE.Vector3( Math.sin( angle ) * 0.6, Math.cos( angle ) * 0.6, 0 ) );
+		tube1.push( new THREE.Vector3( Math.sin( angle ) * 0.06, Math.cos( angle ) * 0.06, 0 ) );
 
 	}
 
@@ -55,14 +55,14 @@ var RollerCoasterGeometry = function ( curve, size ) {
 	for ( var i = 0; i < sides; i ++ ) {
 
 		var angle = ( i / sides ) * PI2;
-		tube2.push( new THREE.Vector3( Math.sin( angle ) * 0.25, Math.cos( angle ) * 0.25, 0 ) );
+		tube2.push( new THREE.Vector3( Math.sin( angle ) * 0.025, Math.cos( angle ) * 0.025, 0 ) );
 
 	}
 
 	var vector = new THREE.Vector3();
 	var normal = new THREE.Vector3();
 
-	var drawShape = function ( shape, color ) {
+	function drawShape( shape, color ) {
 
 		normal.set( 0, 0, -1 ).applyQuaternion( quaternion );
 
@@ -104,7 +104,7 @@ var RollerCoasterGeometry = function ( curve, size ) {
 	var normal3 = new THREE.Vector3();
 	var normal4 = new THREE.Vector3();
 
-	var extrudeShape = function ( shape, offset, color ) {
+	function extrudeShape( shape, offset, color ) {
 
 		for ( var j = 0, jl = shape.length; j < jl; j ++ ) {
 
@@ -175,9 +175,9 @@ var RollerCoasterGeometry = function ( curve, size ) {
 
 	var offset = new THREE.Vector3();
 
-	for ( var i = 1; i <= size; i ++ ) {
+	for ( var i = 1; i <= divisions; i ++ ) {
 
-		point.copy( curve.getPointAt( i / size ) );
+		point.copy( curve.getPointAt( i / divisions ) );
 
 		up.set( 0, 1, 0 );
 
@@ -195,9 +195,9 @@ var RollerCoasterGeometry = function ( curve, size ) {
 
 		}
 
-		extrudeShape( tube1, offset.set( 0, -1.25, 0 ), color2 );
-		extrudeShape( tube2, offset.set( 2, 0, 0 ), color1 );
-		extrudeShape( tube2, offset.set( -2, 0, 0 ), color1 );
+		extrudeShape( tube1, offset.set(  0,  -0.125, 0 ), color2 );
+		extrudeShape( tube2, offset.set(  0.2, 0,     0 ), color1 );
+		extrudeShape( tube2, offset.set( -0.2, 0,     0 ), color1 );
 
 		prevPoint.copy( point );
 		prevQuaternion.copy( quaternion );
@@ -214,7 +214,7 @@ var RollerCoasterGeometry = function ( curve, size ) {
 
 RollerCoasterGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
 
-var RollerCoasterLiftersGeometry = function ( curve, size ) {
+function RollerCoasterLiftersGeometry( curve, divisions ) {
 
 	THREE.BufferGeometry.call( this );
 
@@ -231,21 +231,21 @@ var RollerCoasterLiftersGeometry = function ( curve, size ) {
 	// shapes
 
 	var tube1 = [
-		new THREE.Vector3(  0,  0.5, -0.5 ),
-		new THREE.Vector3(  0,  0.5,  0.5 ),
-		new THREE.Vector3(  0, -0.5,  0 )
+		new THREE.Vector3(  0,  0.05, -0.05 ),
+		new THREE.Vector3(  0,  0.05,  0.05 ),
+		new THREE.Vector3(  0, -0.05,  0 )
 	];
 
 	var tube2 = [
-		new THREE.Vector3( -0.5, 0,  0.5 ),
-		new THREE.Vector3( -0.5, 0, -0.5 ),
-		new THREE.Vector3(  0.5, 0,  0 )
+		new THREE.Vector3( -0.05, 0,  0.05 ),
+		new THREE.Vector3( -0.05, 0, -0.05 ),
+		new THREE.Vector3(  0.05, 0,  0 )
 	];
 
 	var tube3 = [
-		new THREE.Vector3(  0.5, 0, -0.5 ),
-		new THREE.Vector3(  0.5, 0,  0.5 ),
-		new THREE.Vector3( -0.5, 0,  0 )
+		new THREE.Vector3(  0.05, 0, -0.05 ),
+		new THREE.Vector3(  0.05, 0,  0.05 ),
+		new THREE.Vector3( -0.05, 0,  0 )
 	];
 
 	var vector1 = new THREE.Vector3();
@@ -258,7 +258,7 @@ var RollerCoasterLiftersGeometry = function ( curve, size ) {
 	var normal3 = new THREE.Vector3();
 	var normal4 = new THREE.Vector3();
 
-	var extrudeShape = function ( shape, fromPoint, toPoint ) {
+	function extrudeShape( shape, fromPoint, toPoint ) {
 
 		for ( var j = 0, jl = shape.length; j < jl; j ++ ) {
 
@@ -322,10 +322,10 @@ var RollerCoasterLiftersGeometry = function ( curve, size ) {
 	var fromPoint = new THREE.Vector3();
 	var toPoint = new THREE.Vector3();
 
-	for ( var i = 1; i <= size; i ++ ) {
+	for ( var i = 1; i <= divisions; i ++ ) {
 
-		point.copy( curve.getPointAt( i / size ) );
-		tangent.copy( curve.getTangentAt( i / size ) );
+		point.copy( curve.getPointAt( i / divisions ) );
+		tangent.copy( curve.getTangentAt( i / divisions ) );
 
 		var angle = Math.atan2( tangent.x, tangent.z );
 
@@ -333,33 +333,33 @@ var RollerCoasterLiftersGeometry = function ( curve, size ) {
 
 		//
 
-		if ( point.y > 100 ) {
+		if ( point.y > 10 ) {
 
-			fromPoint.set( -7.5, -3.5, 0 );
+			fromPoint.set( -0.75, -0.35, 0 );
 			fromPoint.applyQuaternion( quaternion );
 			fromPoint.add( point );
 
-			toPoint.set( 7.5, -3.5, 0 );
+			toPoint.set( 0.75, -0.35, 0 );
 			toPoint.applyQuaternion( quaternion );
 			toPoint.add( point );
 
 			extrudeShape( tube1, fromPoint, toPoint );
 
-			fromPoint.set( -7, -3, 0 );
+			fromPoint.set( -0.7, -0.3, 0 );
 			fromPoint.applyQuaternion( quaternion );
 			fromPoint.add( point );
 
-			toPoint.set( -7, -point.y, 0 );
+			toPoint.set( -0.7, -point.y, 0 );
 			toPoint.applyQuaternion( quaternion );
 			toPoint.add( point );
 
 			extrudeShape( tube2, fromPoint, toPoint );
 
-			fromPoint.set( 7, -3, 0 );
+			fromPoint.set( 0.7, -0.3, 0 );
 			fromPoint.applyQuaternion( quaternion );
 			fromPoint.add( point );
 
-			toPoint.set( 7, -point.y, 0 );
+			toPoint.set( 0.7, -point.y, 0 );
 			toPoint.applyQuaternion( quaternion );
 			toPoint.add( point );
 
@@ -367,7 +367,7 @@ var RollerCoasterLiftersGeometry = function ( curve, size ) {
 
 		} else {
 
-			fromPoint.set( 0, -2, 0 );
+			fromPoint.set( 0, -0.2, 0 );
 			fromPoint.applyQuaternion( quaternion );
 			fromPoint.add( point );
 
@@ -388,7 +388,7 @@ var RollerCoasterLiftersGeometry = function ( curve, size ) {
 
 RollerCoasterLiftersGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
 
-var RollerCoasterShadowGeometry = function ( curve, size ) {
+function RollerCoasterShadowGeometry( curve, divisions ) {
 
 	THREE.BufferGeometry.call( this );
 
@@ -412,9 +412,9 @@ var RollerCoasterShadowGeometry = function ( curve, size ) {
 	var vector3 = new THREE.Vector3();
 	var vector4 = new THREE.Vector3();
 
-	for ( var i = 1; i <= size; i ++ ) {
+	for ( var i = 1; i <= divisions; i ++ ) {
 
-		point.copy( curve.getPointAt( i / size ) );
+		point.copy( curve.getPointAt( i / divisions ) );
 		point.y = 0;
 
 		forward.subVectors( point, prevPoint );
@@ -423,19 +423,19 @@ var RollerCoasterShadowGeometry = function ( curve, size ) {
 
 		quaternion.setFromAxisAngle( up, angle );
 
-		vector1.set( -3, 0, 0 );
+		vector1.set( -0.3, 0, 0 );
 		vector1.applyQuaternion( quaternion );
 		vector1.add( point );
 
-		vector2.set(  3, 0, 0 );
+		vector2.set(  0.3, 0, 0 );
 		vector2.applyQuaternion( quaternion );
 		vector2.add( point );
 
-		vector3.set(  3, 0, 0 );
+		vector3.set(  0.3, 0, 0 );
 		vector3.applyQuaternion( prevQuaternion );
 		vector3.add( prevPoint );
 
-		vector4.set( -3, 0, 0 );
+		vector4.set( -0.3, 0, 0 );
 		vector4.applyQuaternion( prevQuaternion );
 		vector4.add( prevPoint );
 
@@ -458,7 +458,7 @@ var RollerCoasterShadowGeometry = function ( curve, size ) {
 
 RollerCoasterShadowGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
 
-var SkyGeometry = function () {
+function SkyGeometry() {
 
 	THREE.BufferGeometry.call( this );
 
@@ -466,11 +466,11 @@ var SkyGeometry = function () {
 
 	for ( var i = 0; i < 100; i ++ ) {
 
-		var x = Math.random() * 8000 - 4000;
-		var y = Math.random() * 500 + 500;
-		var z = Math.random() * 8000 - 4000;
+		var x = Math.random() * 800 - 400;
+		var y = Math.random() * 50 + 50;
+		var z = Math.random() * 800 - 400;
 
-		var size = Math.random() * 400 + 200;
+		var size = Math.random() * 40 + 20;
 
 		vertices.push( x - size, y, z - size );
 		vertices.push( x + size, y, z - size );
@@ -489,7 +489,7 @@ var SkyGeometry = function () {
 
 SkyGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
 
-var TreesGeometry = function ( landscape ) {
+function TreesGeometry( landscape ) {
 
 	THREE.BufferGeometry.call( this );
 
@@ -501,10 +501,10 @@ var TreesGeometry = function ( landscape ) {
 
 	for ( var i = 0; i < 2000; i ++ ) {
 
-		var x = Math.random() * 5000 - 2500;
-		var z = Math.random() * 5000 - 2500;
+		var x = Math.random() * 500 - 250;
+		var z = Math.random() * 500 - 250;
 
-		raycaster.ray.origin.set( x, 500, z );
+		raycaster.ray.origin.set( x, 50, z );
 
 		var intersections = raycaster.intersectObject( landscape );
 
@@ -512,19 +512,19 @@ var TreesGeometry = function ( landscape ) {
 
 		var y = intersections[ 0 ].point.y;
 
-		var height = Math.random() * 50 + 5;
+		var height = Math.random() * 5 + 0.5;
 
 		var angle = Math.random() * Math.PI * 2;
 
-		vertices.push( x + Math.sin( angle ) * 10, y, z + Math.cos( angle ) * 10 );
+		vertices.push( x + Math.sin( angle ), y, z + Math.cos( angle ) );
 		vertices.push( x, y + height, z );
-		vertices.push( x + Math.sin( angle + Math.PI ) * 10, y, z + Math.cos( angle + Math.PI ) * 10 );
+		vertices.push( x + Math.sin( angle + Math.PI ), y, z + Math.cos( angle + Math.PI ) );
 
 		angle += Math.PI / 2;
 
-		vertices.push( x + Math.sin( angle ) * 10, y, z + Math.cos( angle ) * 10 );
+		vertices.push( x + Math.sin( angle ), y, z + Math.cos( angle ) );
 		vertices.push( x, y + height, z );
-		vertices.push( x + Math.sin( angle + Math.PI ) * 10, y, z + Math.cos( angle + Math.PI ) * 10 );
+		vertices.push( x + Math.sin( angle + Math.PI ), y, z + Math.cos( angle + Math.PI ) );
 
 		var random = Math.random() * 0.1;
 

+ 28 - 1
examples/js/WaterShader.js

@@ -210,9 +210,36 @@ THREE.Water = function ( renderer, camera, scene, options ) {
 
 };
 
-THREE.Water.prototype = Object.create( THREE.Mirror.prototype );
+THREE.Water.prototype = Object.create( THREE.Object3D.prototype );
 THREE.Water.prototype.constructor = THREE.Water;
 
+THREE.Water.prototype.render = function () {
+
+	if ( this.matrixNeedsUpdate ) this.updateTextureMatrix();
+
+	this.matrixNeedsUpdate = true;
+
+	// Render the mirrored view of the current scene into the target texture
+	var scene = this;
+
+	while ( scene.parent !== null ) {
+
+		scene = scene.parent;
+
+	}
+
+	if ( scene !== undefined && scene instanceof THREE.Scene ) {
+
+		this.material.visible = false;
+
+		this.renderer.render( scene, this.mirrorCamera, this.renderTarget, true );
+
+		this.material.visible = true;
+
+	}
+
+};
+
 
 THREE.Water.prototype.updateTextureMatrix = function () {
 

+ 4 - 2
examples/js/controls/DragControls.js

@@ -52,8 +52,10 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 
 		event.preventDefault();
 
-		_mouse.x = ( event.clientX / _domElement.clientWidth ) * 2 - 1;
-		_mouse.y = - ( event.clientY / _domElement.clientHeight ) * 2 + 1;
+		var rect = _domElement.getBoundingClientRect();
+
+		_mouse.x = ( (event.clientX - rect.left) / rect.width ) * 2 - 1;
+		_mouse.y = - ( (event.clientY - rect.top) / rect.height ) * 2 + 1;
 
 		_raycaster.setFromCamera( _mouse, _camera );
 

+ 0 - 24
examples/js/controls/VRControls.js

@@ -140,30 +140,6 @@ THREE.VRControls = function ( object, onError ) {
 
 	};
 
-	this.resetPose = function () {
-
-		if ( vrDisplay ) {
-
-			vrDisplay.resetPose();
-
-		}
-
-	};
-
-	this.resetSensor = function () {
-
-		console.warn( 'THREE.VRControls: .resetSensor() is now .resetPose().' );
-		this.resetPose();
-
-	};
-
-	this.zeroSensor = function () {
-
-		console.warn( 'THREE.VRControls: .zeroSensor() is now .resetPose().' );
-		this.resetPose();
-
-	};
-
 	this.dispose = function () {
 
 		vrDisplay = null;

+ 2 - 1
examples/js/curves/NURBSCurve.js

@@ -15,6 +15,8 @@
 
 THREE.NURBSCurve = function ( degree, knots /* array of reals */, controlPoints /* array of Vector(2|3|4) */, startKnot /* index in knots */, endKnot /* index in knots */ ) {
 
+	THREE.Curve.call( this );
+
 	this.degree = degree;
 	this.knots = knots;
 	this.controlPoints = [];
@@ -65,4 +67,3 @@ THREE.NURBSCurve.prototype.getTangent = function ( t ) {
 	return tangent;
 
 };
-

+ 5 - 0
examples/js/exporters/OBJExporter.js

@@ -49,6 +49,11 @@ THREE.OBJExporter.prototype = {
 				// name of the mesh object
 				output += 'o ' + mesh.name + '\n';
 
+				// name of the mesh material
+				if ( mesh.material && mesh.material.name ) {
+					output += 'usemtl ' + mesh.material.name + '\n';
+				}
+
 				// vertices
 
 				if( vertices !== undefined ) {

+ 5 - 2
examples/js/loaders/ColladaLoader.js

@@ -907,12 +907,14 @@ THREE.ColladaLoader = function () {
 						var transforms = jointData.transforms;
 
 						var matrix = new THREE.Matrix4();
+						var m1 = new THREE.Matrix4();
 
 						for (i = 0; i < transforms.length; i ++ ) {
 
 							var transform = transforms[ i ];
 
 							// kinda ghetto joint detection
+
 							if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== -1 ) {
 
 								// apply actual joint value here
@@ -937,8 +939,6 @@ THREE.ColladaLoader = function () {
 
 							} else {
 
-								var m1 = new THREE.Matrix4();
-
 								switch ( transform.type ) {
 
 									case 'matrix':
@@ -988,6 +988,9 @@ THREE.ColladaLoader = function () {
 
 						threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor );
 						threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale );
+
+						jointMap[ jointIndex ].position = value;
+
 					}
 
 				} else {

+ 51 - 40
examples/js/loaders/DRACOLoader.js

@@ -44,49 +44,37 @@ THREE.DRACOLoader.prototype = {
         this.verbosity = level;
     },
 
-    decodeDracoFile: ( function() {
-        let dracoDecoder;
+    decodeDracoFile: function(rawBuffer) {
+      const dracoDecoder = THREE.DRACOLoader.getDecoder();
+      /*
+       * Here is how to use Draco Javascript decoder and get the geometry.
+       */
+      const buffer = new dracoDecoder.DecoderBuffer();
+      buffer.Init(new Int8Array(rawBuffer), rawBuffer.byteLength);
+      const wrapper = new dracoDecoder.WebIDLWrapper();
 
-        if (typeof DracoModule === 'function') {
-          dracoDecoder = DracoModule();
-        } else {
-          console.error('THREE.DRACOLoader: DracoModule not found.');
-          return;
+      /*
+       * Determine what type is this file: mesh or point cloud.
+       */
+      const geometryType = wrapper.GetEncodedGeometryType(buffer);
+      if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
+        if (this.verbosity > 0) {
+          console.log('Loaded a mesh.');
         }
-
-        return function(rawBuffer) {
-          const scope = this;
-          /*
-           * Here is how to use Draco Javascript decoder and get the geometry.
-           */
-          const buffer = new dracoDecoder.DecoderBuffer();
-          buffer.Init(new Int8Array(rawBuffer), rawBuffer.byteLength);
-          const wrapper = new dracoDecoder.WebIDLWrapper();
-
-          /*
-           * Determine what type is this file: mesh or point cloud.
-           */
-          const geometryType = wrapper.GetEncodedGeometryType(buffer);
-          if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
-            if (this.verbosity > 0) {
-              console.log('Loaded a mesh.');
-            }
-          } else if (geometryType == dracoDecoder.POINT_CLOUD) {
-            if (this.verbosity > 0) {
-              console.log('Loaded a point cloud.');
-            }
-          } else {
-            const errorMsg = 'THREE.DRACOLoader: Unknown geometry type.'
-            console.error(errorMsg);
-            throw new Error(errorMsg);
-          }
-          return scope.convertDracoGeometryTo3JS(wrapper, geometryType, buffer,
-                                                 dracoDecoder);
+      } else if (geometryType == dracoDecoder.POINT_CLOUD) {
+        if (this.verbosity > 0) {
+          console.log('Loaded a point cloud.');
         }
-    } )(),
+      } else {
+        const errorMsg = 'THREE.DRACOLoader: Unknown geometry type.'
+        console.error(errorMsg);
+        throw new Error(errorMsg);
+      }
+      return this.convertDracoGeometryTo3JS(wrapper, geometryType, buffer);
+    },
 
-    convertDracoGeometryTo3JS: function(wrapper, geometryType, buffer,
-                                        dracoDecoder) {
+    convertDracoGeometryTo3JS: function(wrapper, geometryType, buffer) {
+        const dracoDecoder = THREE.DRACOLoader.getDecoder();
         let dracoGeometry;
         const start_time = performance.now();
         if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
@@ -273,5 +261,28 @@ THREE.DRACOLoader.prototype = {
           console.log('Import time: ' + this.import_time);
         }
         return geometry;
+    },
+
+    isVersionSupported: function(version) {
+        return THREE.DRACOLoader.getDecoder().isVersionSupported(version);
     }
-};
+};
+
+/**
+ * Returns a singleton instance of the DracoModule decoder. Creating multiple
+ * copies of the decoder is expensive.
+ */
+THREE.DRACOLoader.getDecoder = (function() {
+    let decoder;
+
+    return function() {
+        if (typeof DracoModule === 'undefined') {
+          throw new Error('THREE.DRACOLoader: DracoModule not found.');
+        }
+
+        decoder = decoder || DracoModule();
+
+        return decoder;
+    };
+
+})();

File diff suppressed because it is too large
+ 1114 - 1965
examples/js/loaders/FBXLoader2.js


+ 10 - 1
examples/js/loaders/GLTF2Loader.js

@@ -735,6 +735,11 @@ THREE.GLTF2Loader = ( function () {
 					shaderText = shaderText.replace( regEx, 'uv' );
 					break;
 
+				case 'TEXCOORD_1':
+
+					shaderText = shaderText.replace( regEx, 'uv2' );
+					break;
+
 				case 'COLOR_0':
 				case 'COLOR0':
 				case 'COLOR':
@@ -1762,6 +1767,10 @@ THREE.GLTF2Loader = ( function () {
 									geometry.addAttribute( 'uv', bufferAttribute );
 									break;
 
+								case 'TEXCOORD_1':
+									geometry.addAttribute( 'uv2', bufferAttribute );
+									break;
+
 								case 'COLOR_0':
 								case 'COLOR0':
 								case 'COLOR':
@@ -1954,7 +1963,7 @@ THREE.GLTF2Loader = ( function () {
 					if ( sampler ) {
 
 						var target = channel.target;
-						var name = target.id;
+						var name = target.node || target.id; // NOTE: target.id is deprecated.
 						var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input;
 						var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output;
 

+ 9 - 0
examples/js/loaders/GLTFLoader.js

@@ -745,6 +745,11 @@ THREE.GLTFLoader = ( function () {
 					shaderText = shaderText.replace( regEx, 'uv' );
 					break;
 
+				case 'TEXCOORD_1':
+
+					shaderText = shaderText.replace( regEx, 'uv2' );
+					break;
+
 				case 'COLOR_0':
 				case 'COLOR0':
 				case 'COLOR':
@@ -1625,6 +1630,10 @@ THREE.GLTFLoader = ( function () {
 									geometry.addAttribute( 'uv', bufferAttribute );
 									break;
 
+								case 'TEXCOORD_1':
+									geometry.addAttribute( 'uv2', bufferAttribute );
+									break;
+
 								case 'COLOR_0':
 								case 'COLOR0':
 								case 'COLOR':

+ 1 - 2
examples/js/loaders/OBJLoader.js

@@ -720,8 +720,7 @@ THREE.OBJLoader.prototype = {
 
 				}
 
-				var multiMaterial = new THREE.MultiMaterial( createdMaterials );
-				mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, multiMaterial ) : new THREE.LineSegments( buffergeometry, multiMaterial ) );
+				mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, createdMaterials ) : new THREE.LineSegments( buffergeometry, createdMaterials ) );
 
 			} else {
 

+ 1002 - 0
examples/js/loaders/OBJLoader2.js

@@ -0,0 +1,1002 @@
+/**
+  * @author Kai Salmen / https://kaisalmen.de
+  * Development repository: https://github.com/kaisalmen/WWOBJLoader
+  */
+
+'use strict';
+
+if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
+THREE.OBJLoader2.version = '1.1.0';
+
+/**
+ * Use this class to load OBJ data from files or to parse OBJ data from arraybuffer or text
+ * @class
+ *
+ * @param {THREE.DefaultLoadingManager} [manager] Extension of {@link THREE.DefaultLoadingManager}
+ */
+THREE.OBJLoader2 = (function () {
+
+	function OBJLoader2( manager ) {
+		this.manager = ( manager == null ) ? THREE.DefaultLoadingManager : manager;
+
+		this.path = '';
+		this.fileLoader = new THREE.FileLoader( this.manager );
+
+		this.meshCreator = new MeshCreator();
+		this.parser = new Parser( this.meshCreator );
+
+		this.validated = false;
+	}
+
+	/**
+	 * Base path to use
+	 * @memberOf THREE.OBJLoader2
+	 *
+	 * @param {string} path The basepath
+	 */
+	OBJLoader2.prototype.setPath = function ( path ) {
+		this.path = ( path == null ) ? this.path : path;
+	};
+
+	/**
+	 * Set the node where the loaded objects will be attached
+	 * @memberOf THREE.OBJLoader2
+	 *
+	 * @param {THREE.Object3D} sceneGraphBaseNode Scenegraph object where meshes will be attached
+	 */
+	OBJLoader2.prototype.setSceneGraphBaseNode = function ( sceneGraphBaseNode ) {
+		this.meshCreator.setSceneGraphBaseNode( sceneGraphBaseNode );
+	};
+
+	/**
+	 * Set materials loaded by MTLLoader
+	 * @memberOf THREE.OBJLoader2
+	 *
+	 * @param {THREE.MTLLoader.MaterialCreator.materials[]} materials {@link THREE.MTLLoader.MaterialCreator.materials}
+	 */
+	OBJLoader2.prototype.setMaterials = function ( materials ) {
+		this.meshCreator.setMaterials( materials );
+	};
+
+	/**
+	 * Allows to set debug mode for the parser and the meshCreator
+	 * @memberOf THREE.OBJLoader2
+	 *
+	 * @param {boolean} parserDebug {@link Parser} will produce debug output
+	 * @param {boolean} meshCreatorDebug {@link THREE.OBJLoader2.MeshCreator} will produce debug output
+	 */
+	OBJLoader2.prototype.setDebug = function ( parserDebug, meshCreatorDebug ) {
+		this.parser.setDebug( parserDebug );
+		this.meshCreator.setDebug( meshCreatorDebug );
+	};
+
+	/**
+	 * Use this convenient method to load an OBJ file at the given URL. Per default the fileLoader uses an arraybuffer
+	 * @memberOf THREE.OBJLoader2
+	 *
+	 * @param {string} url URL of the file to load
+	 * @param {callback} onLoad Called after loading was successfully completed
+	 * @param {callback} onProgress Called to report progress of loading
+	 * @param {callback} onError Called after an error occurred during loading
+	 * @param {boolean} [useArrayBuffer=true] Set this to false to force string based parsing
+	 */
+	OBJLoader2.prototype.load = function ( url, onLoad, onProgress, onError, useArrayBuffer ) {
+		this._validate();
+		this.fileLoader.setPath( this.path );
+		this.fileLoader.setResponseType( ( useArrayBuffer || useArrayBuffer == null ) ? 'arraybuffer' : 'text' );
+
+		var scope = this;
+		scope.fileLoader.load( url, function ( content ) {
+
+			// only use parseText if useArrayBuffer is explicitly set to false
+			onLoad( ( useArrayBuffer || useArrayBuffer == null ) ? scope.parse( content ) : scope.parseText( content ) );
+
+		}, onProgress, onError );
+	};
+
+	/**
+	 * Default parse function: Parses OBJ file content stored in arrayBuffer and returns the sceneGraphBaseNode
+	 * @memberOf THREE.OBJLoader2
+	 *
+	 * @param {Uint8Array} arrayBuffer OBJ data as Uint8Array
+	 */
+	OBJLoader2.prototype.parse = function ( arrayBuffer ) {
+		// fast-fail on bad type
+		if ( ! ( arrayBuffer instanceof ArrayBuffer || arrayBuffer instanceof Uint8Array ) ) {
+
+			throw 'Provided input is not of type arraybuffer! Aborting...';
+
+		}
+		console.log( 'Parsing arrayBuffer...' );
+		console.time( 'parseArrayBuffer' );
+
+		this._validate();
+		this.parser.parseArrayBuffer( arrayBuffer );
+		var sceneGraphAttach = this._finalize();
+
+		console.timeEnd( 'parseArrayBuffer' );
+
+		return sceneGraphAttach;
+	};
+
+	/**
+	 * Legacy parse function: Parses OBJ file content stored in string and returns the sceneGraphBaseNode
+	 * @memberOf THREE.OBJLoader2
+	 *
+	 * @param {string} text OBJ data as string
+	 */
+	OBJLoader2.prototype.parseText = function ( text ) {
+		// fast-fail on bad type
+		if ( ! ( typeof( text ) === 'string' || text instanceof String ) ) {
+
+			throw 'Provided input is not of type String! Aborting...';
+
+		}
+		console.log( 'Parsing text...' );
+		console.time( 'parseText' );
+
+		this._validate();
+		this.parser.parseText( text );
+		var sceneGraphBaseNode = this._finalize();
+
+		console.timeEnd( 'parseText' );
+
+		return sceneGraphBaseNode;
+	};
+
+	OBJLoader2.prototype._validate = function () {
+		if ( this.validated ) return;
+
+		this.fileLoader = ( this.fileLoader == null ) ? new THREE.FileLoader( this.manager ) : this.fileLoader;
+		this.setPath();
+		this.parser.validate();
+		this.meshCreator.validate();
+
+		this.validated = true;
+	};
+
+	OBJLoader2.prototype._finalize = function () {
+		console.log( 'Global output object count: ' + this.meshCreator.globalObjectCount );
+
+		this.parser.finalize();
+		this.fileLoader = null;
+		var sceneGraphBaseNode = this.meshCreator.sceneGraphBaseNode;
+		this.meshCreator.finalize();
+		this.validated = false;
+
+		return sceneGraphBaseNode;
+	};
+
+	/**
+	 * Constants used by THREE.OBJLoader2
+	 */
+	var Consts = {
+		CODE_LF: 10,
+		CODE_CR: 13,
+		CODE_SPACE: 32,
+		CODE_SLASH: 47,
+		STRING_LF: '\n',
+		STRING_CR: '\r',
+		STRING_SPACE: ' ',
+		STRING_SLASH: '/',
+		LINE_F: 'f',
+		LINE_G: 'g',
+		LINE_L: 'l',
+		LINE_O: 'o',
+		LINE_S: 's',
+		LINE_V: 'v',
+		LINE_VT: 'vt',
+		LINE_VN: 'vn',
+		LINE_MTLLIB: 'mtllib',
+		LINE_USEMTL: 'usemtl',
+		/*
+		 * Build Face/Quad: first element in indexArray is the line identification, therefore offset of one needs to be taken into account
+		 * N-Gons are not supported
+		 * Quad Faces: FaceA: 0, 1, 2  FaceB: 2, 3, 0
+		 *
+		 * 0: "f vertex/uv/normal	vertex/uv/normal	vertex/uv/normal	(vertex/uv/normal)"
+		 * 1: "f vertex/uv		  	vertex/uv		   	vertex/uv		   	(vertex/uv		 )"
+		 * 2: "f vertex//normal	 	vertex//normal	  	vertex//normal	  	(vertex//normal  )"
+		 * 3: "f vertex			 	vertex			  	vertex			  	(vertex		  	 )"
+		 *
+		 * @param indexArray
+		 * @param faceType
+		 */
+		QUAD_INDICES_1: [ 1, 2, 3, 3, 4, 1 ],
+		QUAD_INDICES_2: [ 1, 3, 5, 5, 7, 1 ],
+		QUAD_INDICES_3: [ 1, 4, 7, 7, 10, 1 ]
+	};
+
+	/**
+	 * Parse OBJ data either from ArrayBuffer or string
+	 * @class
+	 */
+	var Parser = (function () {
+
+		function Parser( meshCreator ) {
+			this.meshCreator = meshCreator;
+			this.rawObject = null;
+			this.inputObjectCount = 1;
+			this.debug = false;
+		}
+
+		Parser.prototype.setDebug = function ( debug ) {
+			this.debug = ( debug == null ) ? this.debug : debug;
+		};
+
+		Parser.prototype.validate = function () {
+			this.rawObject = new RawObject();
+			this.inputObjectCount = 1;
+		};
+
+		/**
+		 * Parse the provided arraybuffer
+		 * @memberOf Parser
+		 *
+		 * @param {Uint8Array} arrayBuffer OBJ data as Uint8Array
+		 */
+		Parser.prototype.parseArrayBuffer = function ( arrayBuffer ) {
+			var arrayBufferView = new Uint8Array( arrayBuffer );
+			var length = arrayBufferView.byteLength;
+			var buffer = new Array( 32 );
+			var bufferPointer = 0;
+			var slashes = new Array( 32 );
+			var slashesPointer = 0;
+			var reachedFaces = false;
+			var code;
+			var word = '';
+			for ( var i = 0; i < length; i++ ) {
+
+				code = arrayBufferView[ i ];
+				switch ( code ) {
+					case Consts.CODE_SPACE:
+						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
+						word = '';
+						break;
+
+					case Consts.CODE_SLASH:
+						slashes[ slashesPointer++ ] = i;
+						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
+						word = '';
+						break;
+
+					case Consts.CODE_LF:
+						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
+						word = '';
+						reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces );
+						slashesPointer = 0;
+						bufferPointer = 0;
+						break;
+
+					case Consts.CODE_CR:
+						break;
+
+					default:
+						word += String.fromCharCode( code );
+						break;
+				}
+			}
+		};
+
+		/**
+		 * Parse the provided text
+		 * @memberOf Parser
+		 *
+		 * @param {string} text OBJ data as string
+		 */
+		Parser.prototype.parseText = function ( text ) {
+			var length = text.length;
+			var buffer = new Array( 32 );
+			var bufferPointer = 0;
+			var slashes = new Array( 32 );
+			var slashesPointer = 0;
+			var reachedFaces = false;
+			var char;
+			var word = '';
+			for ( var i = 0; i < length; i++ ) {
+
+				char = text[ i ];
+				switch ( char ) {
+					case Consts.STRING_SPACE:
+						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
+						word = '';
+						break;
+
+					case Consts.STRING_SLASH:
+						slashes[ slashesPointer++ ] = i;
+						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
+						word = '';
+						break;
+
+					case Consts.STRING_LF:
+						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
+						word = '';
+						reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces );
+						slashesPointer = 0;
+						bufferPointer = 0;
+						break;
+
+					case Consts.STRING_CR:
+						break;
+
+					default:
+						word += char;
+				}
+			}
+		};
+
+		Parser.prototype.processLine = function ( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ) {
+			if ( bufferPointer < 1 ) return reachedFaces;
+
+			var bufferLength = bufferPointer - 1;
+			switch ( buffer[ 0 ] ) {
+				case Consts.LINE_V:
+
+					// object complete instance required if reached faces already (= reached next block of v)
+					if ( reachedFaces ) {
+
+						this.processCompletedObject( null, this.rawObject.groupName );
+						reachedFaces = false;
+
+					}
+					this.rawObject.pushVertex( buffer );
+					break;
+
+				case Consts.LINE_VT:
+					this.rawObject.pushUv( buffer );
+					break;
+
+				case Consts.LINE_VN:
+					this.rawObject.pushNormal( buffer );
+					break;
+
+				case Consts.LINE_F:
+					reachedFaces = true;
+					/*
+					 * 0: "f vertex/uv/normal ..."
+					 * 1: "f vertex/uv ..."
+					 * 2: "f vertex//normal ..."
+					 * 3: "f vertex ..."
+					 */
+					var haveQuad = bufferLength % 4 === 0;
+					if ( slashesPointer > 1 && ( slashes[ 1 ] - slashes[ 0 ] ) === 1 ) {
+
+						if ( haveQuad ) {
+							this.rawObject.buildQuadVVn( buffer );
+						} else {
+							this.rawObject.buildFaceVVn( buffer );
+						}
+
+					} else if ( bufferLength === slashesPointer * 2 ) {
+
+						if ( haveQuad ) {
+							this.rawObject.buildQuadVVt( buffer );
+						} else {
+							this.rawObject.buildFaceVVt( buffer );
+						}
+
+					} else if ( bufferLength * 2 === slashesPointer * 3 ) {
+
+						if ( haveQuad ) {
+							this.rawObject.buildQuadVVtVn( buffer );
+						} else {
+							this.rawObject.buildFaceVVtVn( buffer );
+						}
+
+					} else {
+
+						if ( haveQuad ) {
+							this.rawObject.buildQuadV( buffer );
+						} else {
+							this.rawObject.buildFaceV( buffer );
+						}
+
+					}
+					break;
+
+				case Consts.LINE_L:
+					if ( bufferLength === slashesPointer * 2 ) {
+
+						this.rawObject.buildLineVvt( buffer );
+
+					} else {
+
+						this.rawObject.buildLineV( buffer );
+
+					}
+					break;
+
+				case Consts.LINE_S:
+					this.rawObject.pushSmoothingGroup( buffer[ 1 ] );
+					break;
+
+				case Consts.LINE_G:
+					this.processCompletedGroup( buffer[ 1 ] );
+					break;
+
+				case Consts.LINE_O:
+					if ( this.rawObject.vertices.length > 0 ) {
+
+						this.processCompletedObject( buffer[ 1 ], null );
+						reachedFaces = false;
+
+					} else {
+
+						this.rawObject.pushObject( buffer[ 1 ] );
+
+					}
+					break;
+
+				case Consts.LINE_MTLLIB:
+					this.rawObject.pushMtllib( buffer[ 1 ] );
+					break;
+
+				case Consts.LINE_USEMTL:
+					this.rawObject.pushUsemtl( buffer[ 1 ] );
+					break;
+
+				default:
+					break;
+			}
+			return reachedFaces;
+		};
+
+		Parser.prototype.processCompletedObject = function ( objectName, groupName ) {
+			this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug );
+			this.inputObjectCount++;
+			this.rawObject = this.rawObject.newInstanceFromObject( objectName, groupName );
+		};
+
+		Parser.prototype.processCompletedGroup = function ( groupName ) {
+			var notEmpty = this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug );
+			if ( notEmpty ) {
+
+				this.inputObjectCount ++;
+				this.rawObject = this.rawObject.newInstanceFromGroup( groupName );
+
+			} else {
+
+				// if a group was set that did not lead to object creation in finalize, then the group name has to be updated
+				this.rawObject.pushGroup( groupName );
+
+			}
+		};
+
+		Parser.prototype.finalize = function () {
+			this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug );
+			this.inputObjectCount++;
+		};
+
+		return Parser;
+	})();
+
+	/**
+	 * {@link RawObject} is only used by {@link Parser}.
+	 * The user of OBJLoader2 does not need to care about this class.
+	 * It is defined publicly for inclusion in web worker based OBJ loader ({@link THREE.OBJLoader2.WWOBJLoader2})
+	 */
+	var RawObject = (function () {
+
+		function RawObject( objectName, groupName, mtllibName ) {
+			this.globalVertexOffset = 1;
+			this.globalUvOffset = 1;
+			this.globalNormalOffset = 1;
+
+			this.vertices = [];
+			this.normals = [];
+			this.uvs = [];
+
+			// faces are stored according combined index of group, material and smoothingGroup (0 or not)
+			this.mtllibName = ( mtllibName != null ) ? mtllibName : 'none';
+			this.objectName = ( objectName != null ) ? objectName : 'none';
+			this.groupName = ( groupName != null ) ? groupName : 'none';
+			this.activeMtlName = 'none';
+			this.activeSmoothingGroup = 1;
+
+			this.mtlCount = 0;
+			this.smoothingGroupCount = 0;
+
+			this.rawObjectDescriptions = [];
+			// this default index is required as it is possible to define faces without 'g' or 'usemtl'
+			var index = this.buildIndex( this.activeMtlName, this.activeSmoothingGroup );
+			this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup );
+			this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse;
+		}
+
+		RawObject.prototype.buildIndex = function ( materialName, smoothingGroup) {
+			return materialName + '|' + smoothingGroup;
+		};
+
+		RawObject.prototype.newInstanceFromObject = function ( objectName, groupName ) {
+			var newRawObject = new RawObject( objectName, groupName, this.mtllibName );
+
+			// move indices forward
+			newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3;
+			newRawObject.globalUvOffset = this.globalUvOffset + this.uvs.length / 2;
+			newRawObject.globalNormalOffset = this.globalNormalOffset + this.normals.length / 3;
+
+			return newRawObject;
+		};
+
+		RawObject.prototype.newInstanceFromGroup = function ( groupName ) {
+			var newRawObject = new RawObject( this.objectName, groupName, this.mtllibName );
+
+			// keep current buffers and indices forward
+			newRawObject.vertices = this.vertices;
+			newRawObject.uvs = this.uvs;
+			newRawObject.normals = this.normals;
+			newRawObject.globalVertexOffset = this.globalVertexOffset;
+			newRawObject.globalUvOffset = this.globalUvOffset;
+			newRawObject.globalNormalOffset = this.globalNormalOffset;
+
+			return newRawObject;
+		};
+
+		RawObject.prototype.pushVertex = function ( buffer ) {
+			this.vertices.push( parseFloat( buffer[ 1 ] ) );
+			this.vertices.push( parseFloat( buffer[ 2 ] ) );
+			this.vertices.push( parseFloat( buffer[ 3 ] ) );
+		};
+
+		RawObject.prototype.pushUv = function ( buffer ) {
+			this.uvs.push( parseFloat( buffer[ 1 ] ) );
+			this.uvs.push( parseFloat( buffer[ 2 ] ) );
+		};
+
+		RawObject.prototype.pushNormal = function ( buffer ) {
+			this.normals.push( parseFloat( buffer[ 1 ] ) );
+			this.normals.push( parseFloat( buffer[ 2 ] ) );
+			this.normals.push( parseFloat( buffer[ 3 ] ) );
+		};
+
+		RawObject.prototype.pushObject = function ( objectName ) {
+			this.objectName = objectName;
+		};
+
+		RawObject.prototype.pushMtllib = function ( mtllibName ) {
+			this.mtllibName = mtllibName;
+		};
+
+		RawObject.prototype.pushGroup = function ( groupName ) {
+			this.groupName = groupName;
+			this.verifyIndex();
+		};
+
+		RawObject.prototype.pushUsemtl = function ( mtlName ) {
+			if ( this.activeMtlName === mtlName || mtlName == null ) return;
+			this.activeMtlName = mtlName;
+			this.mtlCount++;
+
+			this.verifyIndex();
+		};
+
+		RawObject.prototype.pushSmoothingGroup = function ( activeSmoothingGroup ) {
+			var normalized = activeSmoothingGroup === 'off' ? 0 : activeSmoothingGroup;
+			if ( this.activeSmoothingGroup === normalized ) return;
+			this.activeSmoothingGroup = normalized;
+			this.smoothingGroupCount++;
+
+			this.verifyIndex();
+		};
+
+		RawObject.prototype.verifyIndex = function () {
+			var index = this.buildIndex( this.activeMtlName, ( this.activeSmoothingGroup === 0 ) ? 0 : 1 );
+			if ( this.rawObjectDescriptions[ index ] == null ) {
+
+				this.rawObjectDescriptionInUse = this.rawObjectDescriptions[ index ] =
+					new RawObjectDescription(
+						this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup
+					);
+
+			} else {
+
+				this.rawObjectDescriptionInUse = this.rawObjectDescriptions[ index ];
+
+			}
+		};
+
+		RawObject.prototype.buildQuadVVtVn = function ( indexArray ) {
+			for ( var i = 0; i < 6; i ++ ) {
+				this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_3[ i ] ] );
+				this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_3[ i ] + 1 ] );
+				this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_3[ i ] + 2 ] );
+			}
+		};
+
+		RawObject.prototype.buildQuadVVt = function ( indexArray ) {
+			for ( var i = 0; i < 6; i ++ ) {
+				this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] );
+				this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] );
+			}
+		};
+
+		RawObject.prototype.buildQuadVVn = function ( indexArray ) {
+			for ( var i = 0; i < 6; i ++ ) {
+				this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] );
+				this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] );
+			}
+		};
+
+		RawObject.prototype.buildQuadV = function ( indexArray ) {
+			for ( var i = 0; i < 6; i ++ ) {
+				this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_1[ i ] ] );
+			}
+		};
+
+		RawObject.prototype.buildFaceVVtVn = function ( indexArray ) {
+			for ( var i = 1; i < 10; i += 3 ) {
+				this.attachFaceV_( indexArray[ i ] );
+				this.attachFaceVt( indexArray[ i + 1 ] );
+				this.attachFaceVn( indexArray[ i + 2 ] );
+			}
+		};
+
+		RawObject.prototype.buildFaceVVt = function ( indexArray ) {
+			for ( var i = 1; i < 7; i += 2 ) {
+				this.attachFaceV_( indexArray[ i ] );
+				this.attachFaceVt( indexArray[ i + 1 ] );
+			}
+		};
+
+		RawObject.prototype.buildFaceVVn = function ( indexArray ) {
+			for ( var i = 1; i < 7; i += 2 ) {
+				this.attachFaceV_( indexArray[ i ] );
+				this.attachFaceVn( indexArray[ i + 1 ] );
+			}
+		};
+
+		RawObject.prototype.buildFaceV = function ( indexArray ) {
+			for ( var i = 1; i < 4; i ++ ) {
+				this.attachFaceV_( indexArray[ i ] );
+			}
+		};
+
+		RawObject.prototype.attachFaceV_ = function ( faceIndex ) {
+			var faceIndexInt =  parseInt( faceIndex );
+			var index = ( faceIndexInt - this.globalVertexOffset ) * 3;
+
+			var rodiu = this.rawObjectDescriptionInUse;
+			rodiu.vertices.push( this.vertices[ index++ ] );
+			rodiu.vertices.push( this.vertices[ index++ ] );
+			rodiu.vertices.push( this.vertices[ index ] );
+		};
+
+		RawObject.prototype.attachFaceVt = function ( faceIndex ) {
+			var faceIndexInt =  parseInt( faceIndex );
+			var index = ( faceIndexInt - this.globalUvOffset ) * 2;
+
+			var rodiu = this.rawObjectDescriptionInUse;
+			rodiu.uvs.push( this.uvs[ index++ ] );
+			rodiu.uvs.push( this.uvs[ index ] );
+		};
+
+		RawObject.prototype.attachFaceVn = function ( faceIndex ) {
+			var faceIndexInt =  parseInt( faceIndex );
+			var index = ( faceIndexInt - this.globalNormalOffset ) * 3;
+
+			var rodiu = this.rawObjectDescriptionInUse;
+			rodiu.normals.push( this.normals[ index++ ] );
+			rodiu.normals.push( this.normals[ index++ ] );
+			rodiu.normals.push( this.normals[ index ] );
+		};
+
+		/*
+		 * Support for lines with or without texture. irst element in indexArray is the line identification
+		 * 0: "f vertex/uv		vertex/uv 		..."
+		 * 1: "f vertex			vertex 			..."
+		 */
+		RawObject.prototype.buildLineVvt = function ( lineArray ) {
+			var length = lineArray.length;
+			for ( var i = 1; i < length; i ++ ) {
+				this.vertices.push( parseInt( lineArray[ i ] ) );
+				this.uvs.push( parseInt( lineArray[ i ] ) );
+			}
+		};
+
+		RawObject.prototype.buildLineV = function ( lineArray ) {
+			var length = lineArray.length;
+			for ( var i = 1; i < length; i++ ) {
+				this.vertices.push( parseInt( lineArray[ i ] ) );
+			}
+		};
+
+		/**
+		 * Clear any empty rawObjectDescription and calculate absolute vertex, normal and uv counts
+		 */
+		RawObject.prototype.finalize = function ( meshCreator, inputObjectCount, debug ) {
+			var temp = this.rawObjectDescriptions;
+			this.rawObjectDescriptions = [];
+			var rawObjectDescription;
+			var index = 0;
+			var absoluteVertexCount = 0;
+			var absoluteNormalCount = 0;
+			var absoluteUvCount = 0;
+
+			for ( var name in temp ) {
+
+				rawObjectDescription = temp[ name ];
+				if ( rawObjectDescription.vertices.length > 0 ) {
+
+					if ( rawObjectDescription.objectName === 'none' ) rawObjectDescription.objectName = rawObjectDescription.groupName;
+					this.rawObjectDescriptions[ index++ ] = rawObjectDescription;
+					absoluteVertexCount += rawObjectDescription.vertices.length;
+					absoluteUvCount += rawObjectDescription.uvs.length;
+					absoluteNormalCount += rawObjectDescription.normals.length;
+
+				}
+			}
+
+			// don not continue if no result
+			var notEmpty = false;
+			if ( index > 0 ) {
+
+				if ( debug ) this.createReport( inputObjectCount, true );
+				meshCreator.buildMesh(
+					this.rawObjectDescriptions,
+					inputObjectCount,
+					absoluteVertexCount,
+					absoluteNormalCount,
+					absoluteUvCount
+				);
+				notEmpty = true;
+
+			}
+			return notEmpty;
+		};
+
+		RawObject.prototype.createReport = function ( inputObjectCount, printDirectly ) {
+			var report = {
+				name: this.objectName ? this.objectName : 'groups',
+				mtllibName: this.mtllibName,
+				vertexCount: this.vertices.length / 3,
+				normalCount: this.normals.length / 3,
+				uvCount: this.uvs.length / 2,
+				smoothingGroupCount: this.smoothingGroupCount,
+				mtlCount: this.mtlCount,
+				rawObjectDescriptions: this.rawObjectDescriptions.length
+			};
+
+			if ( printDirectly ) {
+				console.log( 'Input Object number: ' + inputObjectCount + ' Object name: ' + report.name );
+				console.log( 'Mtllib name: ' + report.mtllibName );
+				console.log( 'Vertex count: ' + report.vertexCount );
+				console.log( 'Normal count: ' + report.normalCount );
+				console.log( 'UV count: ' + report.uvCount );
+				console.log( 'SmoothingGroup count: ' + report.smoothingGroupCount );
+				console.log( 'Material count: ' + report.mtlCount );
+				console.log( 'Real RawObjectDescription count: ' + report.rawObjectDescriptions );
+				console.log( '' );
+			}
+
+			return report;
+		};
+
+		return RawObject;
+	})();
+
+	/**
+	 * Descriptive information and data (vertices, normals, uvs) to passed on to mesh building function.
+	 * @class
+	 *
+	 * @param {string} objectName Name of the mesh
+	 * @param {string} groupName Name of the group
+	 * @param {string} materialName Name of the material
+	 * @param {number} smoothingGroup Normalized smoothingGroup (0: THREE.FlatShading, 1: THREE.SmoothShading)
+	 */
+	var RawObjectDescription = (function () {
+
+		function RawObjectDescription( objectName, groupName, materialName, smoothingGroup ) {
+			this.objectName = objectName;
+			this.groupName = groupName;
+			this.materialName = materialName;
+			this.smoothingGroup = smoothingGroup;
+			this.vertices = [];
+			this.uvs = [];
+			this.normals = [];
+		}
+
+		return RawObjectDescription;
+	})();
+
+	/**
+	 * MeshCreator is used to transform RawObjectDescriptions to THREE.Mesh
+	 *
+	 * @class
+	 */
+	var MeshCreator = (function () {
+
+		function MeshCreator() {
+			this.sceneGraphBaseNode = null;
+			this.materials = null;
+			this.debug = false;
+			this.globalObjectCount = 1;
+
+			this.validated = false;
+		}
+
+		MeshCreator.prototype.setSceneGraphBaseNode = function ( sceneGraphBaseNode ) {
+			this.sceneGraphBaseNode = ( sceneGraphBaseNode == null ) ? ( this.sceneGraphBaseNode == null ? new THREE.Group() : this.sceneGraphBaseNode ) : sceneGraphBaseNode;
+		};
+
+		MeshCreator.prototype.setMaterials = function ( materials ) {
+			this.materials = ( materials == null ) ? ( this.materials == null ? { materials: [] } : this.materials ) : materials;
+		};
+
+		MeshCreator.prototype.setDebug = function ( debug ) {
+			this.debug = ( debug == null ) ? this.debug : debug;
+		};
+
+		MeshCreator.prototype.validate = function () {
+			if ( this.validated ) return;
+
+			this.setSceneGraphBaseNode( null );
+			this.setMaterials( null );
+			this.setDebug( null );
+			this.globalObjectCount = 1;
+		};
+
+		MeshCreator.prototype.finalize = function () {
+			this.sceneGraphBaseNode = null;
+			this.materials = null;
+			this.validated = false;
+		};
+
+		/**
+		 * This is an internal function, but due to its importance to Parser it is documented.
+		 * RawObjectDescriptions are transformed to THREE.Mesh.
+		 * It is ensured that rawObjectDescriptions only contain objects with vertices (no need to check).
+		 * This method shall be overridden by the web worker implementation
+		 *
+		 * @param {RawObjectDescription[]} rawObjectDescriptions Array of descriptive information and data (vertices, normals, uvs) about the parsed object(s)
+		 * @param {number} inputObjectCount Number of objects already retrieved from OBJ
+		 * @param {number} absoluteVertexCount Sum of all vertices of all rawObjectDescriptions
+		 * @param {number} absoluteNormalCount Sum of all normals of all rawObjectDescriptions
+		 * @param {number} absoluteUvCount Sum of all uvs of all rawObjectDescriptions
+		 */
+		MeshCreator.prototype.buildMesh = function ( rawObjectDescriptions, inputObjectCount, absoluteVertexCount, absoluteNormalCount, absoluteUvCount ) {
+
+			if ( this.debug ) console.log( 'MeshCreator.buildRawMeshData:\nInput object no.: ' + inputObjectCount );
+
+			var bufferGeometry = new THREE.BufferGeometry();
+			var vertexBA = new THREE.BufferAttribute( new Float32Array( absoluteVertexCount ), 3 );
+			bufferGeometry.addAttribute( 'position', vertexBA );
+
+			var normalBA;
+			if ( absoluteNormalCount > 0 ) {
+
+				normalBA = new THREE.BufferAttribute( new Float32Array( absoluteNormalCount ), 3 );
+				bufferGeometry.addAttribute( 'normal', normalBA );
+
+			}
+			var uvBA;
+			if ( absoluteUvCount > 0 ) {
+
+				uvBA = new THREE.BufferAttribute( new Float32Array( absoluteUvCount ), 2 );
+				bufferGeometry.addAttribute( 'uv', uvBA );
+
+			}
+
+			if ( this.debug ) console.log( 'Creating Multi-Material for object no.: ' + this.globalObjectCount );
+
+			var rawObjectDescription;
+			var material;
+			var materialName;
+			var createMultiMaterial = rawObjectDescriptions.length > 1;
+			var materials = [];
+			var materialIndex = 0;
+			var materialIndexMapping = [];
+			var selectedMaterialIndex;
+
+			var vertexBAOffset = 0;
+			var vertexGroupOffset = 0;
+			var vertexLength;
+			var normalOffset = 0;
+			var uvOffset = 0;
+
+			for ( var oodIndex in rawObjectDescriptions ) {
+				rawObjectDescription = rawObjectDescriptions[ oodIndex ];
+
+				materialName = rawObjectDescription.materialName;
+				material = this.materials[ materialName ];
+				if ( ! material ) {
+
+					material = this.materials[ 'defaultMaterial' ];
+					if ( ! material ) {
+
+						material = new THREE.MeshStandardMaterial( { color: 0xDCF1FF} );
+						material.name = 'defaultMaterial';
+						this.materials[ 'defaultMaterial' ] = material;
+
+					}
+					console.warn( 'object_group "' + rawObjectDescription.objectName + '_' + rawObjectDescription.groupName + '" was defined without material! Assigning "defaultMaterial".' );
+
+				}
+				// clone material in case flat shading is needed due to smoothingGroup 0
+				if ( rawObjectDescription.smoothingGroup === 0 ) {
+
+					materialName = material.name + '_flat';
+					var materialClone = this.materials[ materialName ];
+					if ( ! materialClone ) {
+
+						materialClone = material.clone();
+						materialClone.name = materialName;
+						materialClone.shading = THREE.FlatShading;
+						this.materials[ materialName ] = name;
+
+					}
+
+				}
+
+				vertexLength = rawObjectDescription.vertices.length;
+				if ( createMultiMaterial ) {
+
+					// re-use material if already used before. Reduces materials array size and eliminates duplicates
+					selectedMaterialIndex = materialIndexMapping[ materialName ];
+					if ( ! selectedMaterialIndex ) {
+
+						selectedMaterialIndex = materialIndex;
+						materialIndexMapping[ materialName ] = materialIndex;
+						materials.push( material );
+						materialIndex++;
+
+					}
+
+					bufferGeometry.addGroup( vertexGroupOffset, vertexLength / 3, selectedMaterialIndex );
+					vertexGroupOffset += vertexLength / 3;
+				}
+
+				vertexBA.set( rawObjectDescription.vertices, vertexBAOffset );
+				vertexBAOffset += vertexLength;
+
+				if ( normalBA ) {
+
+					normalBA.set( rawObjectDescription.normals, normalOffset );
+					normalOffset += rawObjectDescription.normals.length;
+
+				}
+				if ( uvBA ) {
+
+					uvBA.set( rawObjectDescription.uvs, uvOffset );
+					uvOffset += rawObjectDescription.uvs.length;
+
+				}
+				if ( this.debug ) this.printReport( rawObjectDescription, selectedMaterialIndex );
+
+			}
+			if ( ! normalBA ) bufferGeometry.computeVertexNormals();
+
+			if ( createMultiMaterial ) material = materials;
+			var mesh = new THREE.Mesh( bufferGeometry, material );
+			this.sceneGraphBaseNode.add( mesh );
+
+			this.globalObjectCount++;
+		};
+
+		MeshCreator.prototype.printReport = function ( rawObjectDescription, selectedMaterialIndex ) {
+			console.log(
+				' Output Object no.: ' + this.globalObjectCount +
+				'\n objectName: ' + rawObjectDescription.objectName +
+				'\n groupName: ' + rawObjectDescription.groupName +
+				'\n materialName: ' + rawObjectDescription.materialName +
+				'\n materialIndex: ' + selectedMaterialIndex +
+				'\n smoothingGroup: ' + rawObjectDescription.smoothingGroup +
+				'\n #vertices: ' + rawObjectDescription.vertices.length / 3 +
+				'\n #uvs: ' + rawObjectDescription.uvs.length / 2 +
+				'\n #normals: ' + rawObjectDescription.normals.length / 3
+			);
+		};
+
+		return MeshCreator;
+	})();
+
+	OBJLoader2.prototype._buildWebWorkerCode = function ( funcBuildObject, funcBuildSingelton ) {
+		var workerCode = '';
+		workerCode += funcBuildObject( 'Consts', Consts );
+		workerCode += funcBuildSingelton( 'Parser', 'Parser', Parser );
+		workerCode += funcBuildSingelton( 'RawObject', 'RawObject', RawObject );
+		workerCode += funcBuildSingelton( 'RawObjectDescription', 'RawObjectDescription', RawObjectDescription );
+		return workerCode;
+	};
+
+	return OBJLoader2;
+})();

+ 135 - 103
examples/js/loaders/VTKLoader.js

@@ -1,6 +1,12 @@
 /**
  * @author mrdoob / http://mrdoob.com/
  * @author Alex Pletzer
+ * 
+ * Updated on 22.03.2017
+ * VTK header is now parsed and used to extract all the compressed data
+ * @author Andrii Iudin https://github.com/andreyyudin
+ * @author Paul Kibet Korir https://github.com/polarise
+ * @author Sriram Somasundharam https://github.com/raamssundar
  */
 
 THREE.VTKLoader = function( manager ) {
@@ -526,6 +532,26 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 
 		}
 
+		function Float32Concat(first, second) {
+		    var firstLength = first.length,
+		        result = new Float32Array(firstLength + second.length);
+
+		    result.set(first);
+		    result.set(second, firstLength);
+
+		    return result;
+		}
+
+		function Int32Concat(first, second) {
+		    var firstLength = first.length,
+		        result = new Int32Array(firstLength + second.length);
+
+		    result.set(first);
+		    result.set(second, firstLength);
+
+		    return result;
+		}
+
 		function parseXML( stringFile ) {
 
 			// Changes XML to JSON, based on https://davidwalsh.name/convert-xml-json
@@ -666,132 +692,144 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 			}
 
 			function parseDataArray( ele, compressed ) {
+				var numBytes = 0;
+				if ( json.attributes.header_type == 'UInt64' )
+					numBytes = 8;
+				else if( json.attributes.header_type == 'UInt32' )
+					numBytes = 4;
 
 				// Check the format
+				if ( ele.attributes.format == 'binary' && compressed ) {
+						var rawData, content, byteData, blocks, cSizeStart, headerSize, padding, dataOffsets, currentOffset;
 
-				if ( ele.attributes.format == 'binary' ) {
-
-					if ( compressed ) {
-
-						// Split the blob_header and compressed Data
-						if ( ele[ '#text' ].indexOf( '==' ) != - 1 ) {
-
-							var data = ele[ '#text' ].split( '==' );
-
-							// console.log( data );
+						if ( ele.attributes.type == 'Float32' ) {
+							var txt = new Float32Array( );
+						} else if ( ele.attributes.type === 'Int64' ) {
+							var txt = new Int32Array( );
+						}
 
-							if ( data.length == 2 ) {
+						// VTP data with the header has the following structure:
+						// [#blocks][#u-size][#p-size][#c-size-1][#c-size-2]...[#c-size-#blocks][DATA]
+						//
+						// Each token is an integer value whose type is specified by "header_type" at the top of the file (UInt32 if no type specified). The token meanings are:
+						// [#blocks] = Number of blocks
+						// [#u-size] = Block size before compression
+						// [#p-size] = Size of last partial block (zero if it not needed)
+						// [#c-size-i] = Size in bytes of block i after compression
+						//
+						// The [DATA] portion stores contiguously every block appended together. The offset from the beginning of the data section to the beginning of a block is 
+						// computed by summing the compressed block sizes from preceding blocks according to the header. 
+
+						rawData = ele[ '#text' ];
+
+						byteData = Base64toByteArray( rawData );
+
+						blocks = byteData[0]
+						for ( var i = 1; i<numBytes-1; i++ ) {
+							blocks = blocks | ( byteData[i] << (i*numBytes) );
+						}
 
-								var blob = data.shift();
-								var content = data.shift();
+						headerSize = (blocks + 3) * numBytes;
+						padding = ( (headerSize % 3) > 0 ) ? 3 - (headerSize % 3) : 0;
+						headerSize = headerSize + padding;
+						
+						dataOffsets = [];
+						currentOffset = headerSize;
+						dataOffsets.push( currentOffset );
+
+						// Get the blocks sizes after the compression.
+						// There are three blocks before c-size-i, so we skip 3*numBytes
+						cSizeStart = 3*numBytes;
+						for ( var i = 0; i<blocks; i++ ) {
+							var currentBlockSize = byteData[i*numBytes + cSizeStart];
+							for ( var j = 1; j<numBytes-1; j++ ) {
+								currentBlockSize = currentBlockSize | ( byteData[i*numBytes + cSizeStart + j] << (j*numBytes) );
+							}
+							
+							currentOffset = currentOffset + currentBlockSize;
+							dataOffsets.push( currentOffset );
+						}
 
-								if ( content === '' ) {
+						for ( var i=0; i<dataOffsets.length-1; i++ ) {
+							var inflate = new Zlib.Inflate( byteData.slice( dataOffsets[i], dataOffsets[i+1] ), { resize: true, verify: true } );
+							content = inflate.decompress();
+							content = content.buffer;
+
+							if ( ele.attributes.type == 'Float32' ) {
+								content = new Float32Array( content );
+								txt = Float32Concat(txt, content);
+							} else if ( ele.attributes.type === 'Int64' ) {
+								content = new Int32Array( content );
+								txt = Int32Concat(txt, content);
+							}
+						}
 
-									content = blob + '==';
+						delete ele[ '#text' ];
 
+						// Get the content and optimize it
+						if ( ele.attributes.type == 'Float32' ) {
+							if ( ele.attributes.format == 'binary' ) {
+								if ( ! compressed ) {
+									txt = txt.filter( function( el, idx, arr ) {
+										if ( idx !== 0 ) return true;
+									} );
 								}
-
-							} else if ( data.length > 2 ) {
-
-								var blob = data.shift();
-								var content = data.shift();
-								content = content + '==';
-
-							} else if ( data.length < 2 ) {
-
-								var content = data.shift();
-								content = content + '==';
-
 							}
 
-							// Convert to bytearray
-							var arr = Base64toByteArray( content );
-
-							// decompress
-							var inflate = new Zlib.Inflate( arr, { resize: true, verify: true } );
-							var content = inflate.decompress();
-
-						} else {
-
-							var content = Base64toByteArray( ele[ '#text' ] );
+						} else if ( ele.attributes.type === 'Int64' ) {
+							if ( ele.attributes.format == 'binary' ) {
+								if ( ! compressed ) {
+									txt = txt.filter( function ( el, idx, arr ) {
+										if ( idx !== 0 ) return true;
+									} );
+								}
 
+								txt = txt.filter( function ( el, idx, arr ) {
+									if ( idx % 2 !== 1 ) return true;
+								} );
+							}
 						}
 
-					} else {
-
-						var content = Base64toByteArray( ele[ '#text' ] );
-
-					}
-
-					var content = content.buffer;
-
 				} else {
+					if ( ele.attributes.format == 'binary' && ! compressed ) {
 
-					if ( ele[ '#text' ] ) {
-
-						var content = ele[ '#text' ].replace( /\n/g, ' ' ).split( ' ' ).filter( function ( el, idx, arr ) {
-
-							if ( el !== '' ) return el;
+						var content = Base64toByteArray( ele[ '#text' ] );
 
-						} );
+						//  VTP data for the uncompressed case has the following structure:
+						// [#bytes][DATA]
+						// where "[#bytes]" is an integer value specifying the number of bytes in the block of data following it.
+						content = content.slice(numBytes).buffer;
 
 					} else {
 
-						var content = new Int32Array( 0 ).buffer;
-
-					}
-
-				}
-
-				delete ele[ '#text' ];
-
-				// Get the content and optimize it
-
-				if ( ele.attributes.type == 'Float32' ) {
-
-					var txt = new Float32Array( content );
-
-					if ( ele.attributes.format == 'binary' ) {
-
-						if ( ! compressed ) {
-
-							txt = txt.filter( function( el, idx, arr ) {
-
-								if ( idx !== 0 ) return true;
-
+						if ( ele[ '#text' ] ) {
+							var content = ele[ '#text' ].replace( /\n/g, ' ' ).split( ' ' ).filter( function ( el, idx, arr ) {
+								if ( el !== '' ) return el;
 							} );
-
+						} else {
+							var content = new Int32Array( 0 ).buffer;
 						}
 
 					}
 
-				} else if ( ele.attributes.type === 'Int64' ) {
+					delete ele[ '#text' ];
 
-					var txt = new Int32Array( content );
+					// Get the content and optimize it
+					if ( ele.attributes.type == 'Float32' ) {
 
-					if ( ele.attributes.format == 'binary' ) {
+						var txt = new Float32Array( content );
 
-						if ( ! compressed ) {
+					} else if ( ele.attributes.type === 'Int64' ) {
+						var txt = new Int32Array( content );
 
+						if ( ele.attributes.format == 'binary' ) {
 							txt = txt.filter( function ( el, idx, arr ) {
-
-								if ( idx !== 0 ) return true;
-
+								if ( idx % 2 !== 1 ) return true;
 							} );
-
 						}
-
-						txt = txt.filter( function ( el, idx, arr ) {
-
-							if ( idx % 2 !== 1 ) return true;
-
-						} );
-
 					}
 
-				}
-
-				// console.log( txt );
+				} // endif ( ele.attributes.format == 'binary' && compressed )
 
 				return txt;
 
@@ -881,8 +919,10 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 						while ( dataArrayIndex < numberOfDataArrays ) {
 
 							// Parse the DataArray
-							arr[ dataArrayIndex ].text = parseDataArray( arr[ dataArrayIndex ], compressed );
-							dataArrayIndex ++;
+							if ( ('#text' in arr[ dataArrayIndex ]) && (arr[ dataArrayIndex ][ '#text' ].length > 0) ) {
+								arr[ dataArrayIndex ].text = parseDataArray( arr[ dataArrayIndex ], compressed );
+							}
+								dataArrayIndex ++;
 
 						}
 
@@ -910,8 +950,6 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 
 								}
 
-								// console.log('Normals', normals);
-
 								break;
 
 							// if it is points
@@ -927,8 +965,6 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 
 								}
 
-								// console.log('Points', points);
-
 								break;
 
 							// if it is strips
@@ -984,8 +1020,6 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 
 								}
 
-								//console.log('Strips', indices);
-
 								break;
 
 							// if it is polys
@@ -1034,7 +1068,7 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 									}
 
 								}
-								//console.log('Polys', indices);
+
 								break;
 
 							default:
@@ -1058,8 +1092,6 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 
 				}
 
-				// console.log( json );
-
 				return geometry;
 
 			} else {

+ 1170 - 0
examples/js/loaders/WWOBJLoader2.js

@@ -0,0 +1,1170 @@
+/**
+  * @author Kai Salmen / https://kaisalmen.de
+  * Development repository: https://github.com/kaisalmen/WWOBJLoader
+  */
+
+'use strict';
+
+if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
+THREE.OBJLoader2.version = '1.1.0';
+
+/**
+ * OBJ data will be loaded by dynamically created web worker.
+ * First feed instructions with: prepareRun
+ * Then: Execute with: run
+ * @class
+ */
+THREE.OBJLoader2.WWOBJLoader2 = (function () {
+
+	function WWOBJLoader2() {
+		this._init();
+	}
+
+	WWOBJLoader2.prototype._init = function () {
+		// check worker support first
+		if ( window.Worker === undefined ) throw "This browser does not support web workers!";
+		if ( window.Blob === undefined  ) throw "This browser does not support Blob!";
+		if ( ! typeof window.URL.createObjectURL === 'function'  ) throw "This browser does not support Object creation from URL!";
+
+		this.instanceNo = 0;
+		this.worker = null;
+		this.workerCode = null;
+		this.debug = false;
+
+		this.sceneGraphBaseNode = null;
+		this.streamMeshes = true;
+		this.meshStore = null;
+		this.modelName = 'none';
+		this.validated = false;
+		this.running = false;
+		this.requestTerminate = false;
+
+		this.callbacks = {
+			progress: null,
+			completedLoading: null,
+			errorWhileLoading: null,
+			materialsLoaded: null,
+			meshLoaded: null,
+			director: {
+				completedLoading: null,
+				errorWhileLoading: null
+			}
+		};
+
+		this.manager = THREE.DefaultLoadingManager;
+		this.fileLoader = new THREE.FileLoader( this.manager );
+		this.mtlLoader = null;
+		this.crossOrigin = null;
+
+		this.dataAvailable = false;
+		this.objAsArrayBuffer = null;
+		this.fileObj = null;
+		this.pathObj = null;
+
+		this.fileMtl = null;
+		this.mtlAsString = null;
+		this.texturePath = null;
+
+		this.materials = [];
+		this.counter = 0;
+	};
+
+	/**
+	 * Set enable or disable debug logging
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
+	 *
+	 * @param {boolean} enabled
+	 */
+	WWOBJLoader2.prototype.setDebug = function ( enabled ) {
+		this.debug = enabled;
+	};
+
+	/**
+	 * Sets the CORS string to be used.
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
+	 *
+	 * @param {string} crossOrigin CORS value
+	 */
+	WWOBJLoader2.prototype.setCrossOrigin = function ( crossOrigin ) {
+		this.crossOrigin = crossOrigin;
+	};
+
+	/**
+	 * Register callback function that is invoked by internal function "_announceProgress" to print feedback
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
+	 *
+	 * @param {callback} callbackProgress Callback function for described functionality
+	 */
+	WWOBJLoader2.prototype.registerCallbackProgress = function ( callbackProgress ) {
+		if ( callbackProgress != null ) this.callbacks.progress = callbackProgress;
+	};
+
+	/**
+	 * Register callback function that is called once loading of the complete model is completed
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
+	 *
+	 * @param {callback} callbackCompletedLoading Callback function for described functionality
+	 */
+	WWOBJLoader2.prototype.registerCallbackCompletedLoading = function ( callbackCompletedLoading ) {
+		if ( callbackCompletedLoading != null ) this.callbacks.completedLoading = callbackCompletedLoading;
+	};
+
+	/**
+	 * Register callback function that is called once materials have been loaded. It allows to alter and return materials
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
+	 *
+	 * @param {callback} callbackMaterialsLoaded Callback function for described functionality
+	 */
+	WWOBJLoader2.prototype.registerCallbackMaterialsLoaded = function ( callbackMaterialsLoaded ) {
+		if ( callbackMaterialsLoaded != null ) this.callbacks.materialsLoaded = callbackMaterialsLoaded;
+	};
+
+	/**
+	 * Register callback function that is called every time a mesh was loaded
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
+	 *
+	 * @param {callback} callbackMeshLoaded Callback function for described functionality
+	 */
+	WWOBJLoader2.prototype.registerCallbackMeshLoaded = function ( callbackMeshLoaded ) {
+		if ( callbackMeshLoaded != null ) this.callbacks.meshLoaded = callbackMeshLoaded;
+	};
+
+	/**
+	 * Report if an error prevented loading
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
+	 *
+	 * @param {callback} callbackErrorWhileLoading Callback function for described functionality
+	 */
+	WWOBJLoader2.prototype.registerCallbackErrorWhileLoading = function ( callbackErrorWhileLoading ) {
+		if ( callbackErrorWhileLoading != null ) this.callbacks.errorWhileLoading = callbackErrorWhileLoading;
+	};
+
+	/**
+	 * Call requestTerminate to terminate the web worker and free local resource after execution
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
+	 *
+	 * @param {boolean} requestTerminate
+	 */
+	WWOBJLoader2.prototype.setRequestTerminate = function ( requestTerminate ) {
+		this.requestTerminate = ( requestTerminate != null && requestTerminate ) ? true : false;
+	};
+
+	WWOBJLoader2.prototype._validate = function () {
+		if ( this.validated ) return;
+		if ( this.worker == null ) {
+
+			this._buildWebWorkerCode();
+			var blob = new Blob( [ this.workerCode ], { type: 'text/plain' } );
+			this.worker = new Worker( window.URL.createObjectURL( blob ) );
+
+			var scope = this;
+			var scopeFunction = function ( e ) {
+				scope._receiveWorkerMessage( e );
+			};
+			this.worker.addEventListener( 'message', scopeFunction, false );
+
+		}
+
+		this.sceneGraphBaseNode = null;
+		this.streamMeshes = true;
+		this.meshStore = [];
+		this.modelName = 'none';
+		this.validated = true;
+		this.running = true;
+		this.requestTerminate = false;
+
+		this.fileLoader = ( this.fileLoader == null ) ? new THREE.FileLoader( this.manager ) : this.fileLoader;
+		this.mtlLoader = ( this.mtlLoader == null ) ?  new THREE.MTLLoader() : this.mtlLoader;
+		if ( this.crossOrigin != null ) this.mtlLoader.setCrossOrigin( this.crossOrigin );
+
+		this.dataAvailable = false;
+		this.fileObj = null;
+		this.pathObj = null;
+		this.fileMtl = null;
+		this.texturePath = null;
+
+		this.objAsArrayBuffer = null;
+		this.mtlAsString = null;
+
+		this.materials = [];
+		var defaultMaterial = new THREE.MeshStandardMaterial( { color: 0xDCF1FF } );
+		defaultMaterial.name = 'defaultMaterial';
+		this.materials[ defaultMaterial.name ] = defaultMaterial;
+
+		this.counter = 0;
+	};
+
+	/**
+	 * Set all parameters for required for execution of "run".
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
+	 *
+	 * @param {Object} params Either {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer} or {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataFile}
+	 */
+	WWOBJLoader2.prototype.prepareRun = function ( params ) {
+		this._validate();
+		this.dataAvailable = params.dataAvailable;
+		this.modelName = params.modelName;
+		console.time( 'WWOBJLoader2' );
+		if ( this.dataAvailable ) {
+
+			// fast-fail on bad type
+			if ( ! params.objAsArrayBuffer instanceof Uint8Array ) {
+				throw 'Provided input is not of type arraybuffer! Aborting...';
+			}
+
+			this.worker.postMessage( {
+				cmd: 'init',
+				debug: this.debug
+			} );
+
+			this.objAsArrayBuffer = params.objAsArrayBuffer;
+			this.mtlAsString = params.mtlAsString;
+
+		} else {
+
+			// fast-fail on bad type
+			if ( ! ( typeof( params.fileObj ) === 'string' || params.fileObj instanceof String ) ) {
+				throw 'Provided file is not properly defined! Aborting...';
+			}
+
+			this.worker.postMessage( {
+				cmd: 'init',
+				debug: this.debug
+			} );
+
+			this.fileObj = params.fileObj;
+			this.pathObj = params.pathObj;
+			this.fileMtl = params.fileMtl;
+
+		}
+		this.setRequestTerminate( params.requestTerminate );
+		this.pathTexture = params.pathTexture;
+		this.sceneGraphBaseNode = params.sceneGraphBaseNode;
+		this.streamMeshes = params.streamMeshes;
+		if ( ! this.streamMeshes ) this.meshStore = [];
+	};
+
+	/**
+	 * Run the loader according the preparation instruction provided in "prepareRun".
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2
+	 */
+	WWOBJLoader2.prototype.run = function () {
+		var scope = this;
+		var processLoadedMaterials = function ( materialCreator ) {
+			var materialCreatorMaterials = [];
+			var materialNames = [];
+			if ( materialCreator != null ) {
+
+				materialCreator.preload();
+				materialCreatorMaterials = materialCreator.materials;
+				for ( var materialName in materialCreatorMaterials ) {
+
+					if ( materialCreatorMaterials.hasOwnProperty( materialName ) ) {
+
+						materialNames.push( materialName );
+						scope.materials[ materialName ] = materialCreatorMaterials[ materialName ];
+
+					}
+
+				}
+
+			}
+			scope.worker.postMessage( {
+				cmd: 'setMaterials',
+				materialNames: materialNames
+			} );
+
+			if ( scope.callbacks.materialsLoaded != null ) {
+
+				var materialsCallback = scope.callbacks.materialsLoaded( scope.materials );
+				if ( materialsCallback != null ) scope.materials = materialsCallback;
+
+			}
+
+			if ( scope.dataAvailable && scope.objAsArrayBuffer ) {
+
+				scope.worker.postMessage({
+					cmd: 'run',
+					objAsArrayBuffer: scope.objAsArrayBuffer
+				}, [ scope.objAsArrayBuffer.buffer ] );
+
+			} else {
+
+				var refPercentComplete = 0;
+				var percentComplete = 0;
+				var output;
+				var onLoad = function ( objAsArrayBuffer ) {
+
+					scope._announceProgress( 'Running web worker!' );
+					scope.objAsArrayBuffer = new Uint8Array( objAsArrayBuffer );
+					scope.worker.postMessage( {
+						cmd: 'run',
+						objAsArrayBuffer: scope.objAsArrayBuffer
+					}, [ scope.objAsArrayBuffer.buffer ] );
+
+				};
+
+				var onProgress = function ( event ) {
+					if ( ! event.lengthComputable ) return;
+
+					percentComplete = Math.round( event.loaded / event.total * 100 );
+					if ( percentComplete > refPercentComplete ) {
+
+						refPercentComplete = percentComplete;
+						output = 'Download of "' + scope.fileObj + '": ' + percentComplete + '%';
+						console.log( output );
+						scope._announceProgress( output );
+
+					}
+				};
+
+				var onError = function ( event ) {
+					output = 'Error occurred while downloading "' + scope.fileObj + '"';
+					console.error( output + ': ' + event );
+					scope._announceProgress( output );
+					scope._finalize( 'error' );
+
+				};
+
+				scope.fileLoader.setPath( scope.pathObj );
+				scope.fileLoader.setResponseType( 'arraybuffer' );
+				scope.fileLoader.load( scope.fileObj, onLoad, onProgress, onError );
+			}
+			console.timeEnd( 'Loading MTL textures' );
+		};
+
+
+		this.mtlLoader.setPath( this.pathTexture );
+		if ( this.dataAvailable ) {
+
+			processLoadedMaterials( ( this.mtlAsString != null ) ? this.mtlLoader.parse( this.mtlAsString ) : null );
+
+		} else {
+
+			if ( this.fileMtl == null ) {
+
+				processLoadedMaterials();
+
+			} else {
+
+				this.mtlLoader.load( this.fileMtl, processLoadedMaterials );
+
+			}
+
+		}
+	};
+
+	WWOBJLoader2.prototype._receiveWorkerMessage = function ( event ) {
+		var payload = event.data;
+
+		switch ( payload.cmd ) {
+			case 'objData':
+
+				this.counter ++;
+				var bufferGeometry = new THREE.BufferGeometry();
+
+				bufferGeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( payload.vertices ), 3 ) );
+				if ( payload.normals !== null ) {
+
+					bufferGeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( payload.normals ), 3 ) );
+
+				} else {
+
+					bufferGeometry.computeVertexNormals();
+
+				}
+				if ( payload.uvs !== null ) {
+
+					bufferGeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( payload.uvs ), 2 ) );
+
+				}
+
+				var materialDescriptions = payload.materialDescriptions;
+				var materialDescription;
+				var material;
+				var materialName;
+				var createMultiMaterial = payload.multiMaterial;
+				var multiMaterials = [];
+
+				for ( var key in materialDescriptions ) {
+
+					materialDescription = materialDescriptions[ key ];
+					material = this.materials[ materialDescription.name ];
+
+					if ( materialDescription.default ) {
+
+						material = this.materials[ 'defaultMaterial' ];
+
+					} else if ( materialDescription.clone ) {
+
+						materialName = material.name + '_flat';
+						var materialClone = this.materials[ materialName ];
+						if ( ! materialClone ) {
+
+							materialClone = material.clone();
+							materialClone.name = materialName;
+							materialClone.shading = THREE.FlatShading;
+							this.materials[ materialName ] = name;
+
+						}
+
+					} else if ( ! material ) {
+
+						material = this.materials[ 'defaultMaterial' ];
+
+					}
+					if ( createMultiMaterial ) multiMaterials.push( material );
+
+				}
+				if ( createMultiMaterial ) {
+
+					material = multiMaterials;
+					var materialGroups = payload.materialGroups;
+					var materialGroup;
+					for ( var key in materialGroups ) {
+
+						materialGroup = materialGroups[ key ];
+						bufferGeometry.addGroup( materialGroup.start, materialGroup.count, materialGroup.index );
+
+					}
+
+				}
+				if ( this.callbacks.meshLoaded !== null ) {
+
+					var materialOverride = this.callbacks.meshLoaded( payload.meshName, material );
+					if ( materialOverride != null ) material = materialOverride;
+
+				}
+				var mesh = new THREE.Mesh( bufferGeometry, material );
+				mesh.name = payload.meshName;
+				if ( this.streamMeshes ) {
+
+					this.sceneGraphBaseNode.add( mesh );
+
+				} else {
+
+					this.meshStore.push( mesh );
+
+				}
+				var output = '(' + this.counter + '): ' + payload.meshName;
+				this._announceProgress( 'Adding mesh', output );
+				break;
+
+			case 'complete':
+
+				if ( ! this.streamMeshes ) {
+
+					for ( var key in this.meshStore ) {
+
+						this.sceneGraphBaseNode.add( this.meshStore[ key ] );
+
+					}
+
+				}
+
+				console.timeEnd( 'WWOBJLoader2' );
+				if ( payload.msg != null ) {
+
+					this._announceProgress( payload.msg );
+
+				} else {
+
+					this._announceProgress( '' );
+
+				}
+
+				this._finalize( 'complete' );
+				break;
+
+			case 'report_progress':
+				this._announceProgress( '', payload.output );
+				break;
+
+			default:
+				console.error( 'Received unknown command: ' + payload.cmd );
+				break;
+
+		}
+	};
+
+	WWOBJLoader2.prototype._terminate = function () {
+		if ( this.worker != null ) {
+
+			if ( this.running ) throw 'Unable to gracefully terminate worker as it is currently running!';
+
+			this.worker.terminate();
+			this.worker = null;
+			this.workerCode = null;
+			this._finalize( 'terminate' );
+
+		}
+		this.fileLoader = null;
+		this.mtlLoader = null;
+	};
+
+	WWOBJLoader2.prototype._finalize = function ( reason, requestTerminate ) {
+		this.running = false;
+		if ( reason === 'complete' ) {
+
+			if ( this.callbacks.completedLoading != null ) this.callbacks.completedLoading( this.modelName, this.instanceNo, this.requestTerminate );
+			if ( this.callbacks.director.completedLoading != null ) this.callbacks.director.completedLoading( this.modelName, this.instanceNo, this.requestTerminate );
+
+		} else if ( reason === 'error' ) {
+
+			if ( this.callbacks.errorWhileLoading != null ) this.callbacks.errorWhileLoading( this.modelName, this.instanceNo, this.requestTerminate );
+			if ( this.callbacks.director.errorWhileLoading != null ) this.callbacks.director.errorWhileLoading( this.modelName, this.instanceNo, this.requestTerminate );
+
+		}
+		this.validated = false;
+
+		this.setRequestTerminate( requestTerminate );
+
+		if ( this.requestTerminate ) {
+			this._terminate();
+		}
+	};
+
+	WWOBJLoader2.prototype._announceProgress = function ( baseText, text ) {
+		var output = "";
+		if ( baseText !== null && baseText !== undefined ) {
+
+			output = baseText;
+
+		}
+		if ( text !== null && text !== undefined ) {
+
+			output = output + " " + text;
+
+		}
+		if ( this.callbacks.progress !== null ) {
+
+			this.callbacks.progress( output );
+
+		}
+		if ( this.debug ) {
+
+			console.log( output );
+
+		}
+	};
+
+	WWOBJLoader2.prototype._buildWebWorkerCode = function ( existingWorkerCode ) {
+		if ( existingWorkerCode != null ) this.workerCode = existingWorkerCode;
+		if ( this.workerCode == null ) {
+
+			console.time( 'buildWebWorkerCode' );
+			var wwDef = (function () {
+
+				function WWOBJLoader() {
+					this.wwMeshCreator = new WWMeshCreator();
+					this.parser = new Parser( this.wwMeshCreator );
+					this.validated = false;
+					this.cmdState = 'created';
+
+					this.debug = false;
+				}
+
+				/**
+				 * Allows to set debug mode for the parser and the meshCreatorDebug
+				 *
+				 * @param parserDebug
+				 * @param meshCreatorDebug
+				 */
+				WWOBJLoader.prototype.setDebug = function ( parserDebug, meshCreatorDebug ) {
+					this.parser.setDebug( parserDebug );
+					this.wwMeshCreator.setDebug( meshCreatorDebug );
+				};
+
+				/**
+				 * Validate status, then parse arrayBuffer, finalize and return objGroup
+				 *
+				 * @param arrayBuffer
+				 */
+				WWOBJLoader.prototype.parse = function ( arrayBuffer ) {
+					console.log( 'Parsing arrayBuffer...' );
+					console.time( 'parseArrayBuffer' );
+
+					this.validate();
+					this.parser.parseArrayBuffer( arrayBuffer );
+					var objGroup = this._finalize();
+
+					console.timeEnd( 'parseArrayBuffer' );
+
+					return objGroup;
+				};
+
+				WWOBJLoader.prototype.validate = function () {
+					if ( this.validated ) return;
+
+					this.parser.validate();
+					this.wwMeshCreator.validate();
+
+					this.validated = true;
+				};
+
+				WWOBJLoader.prototype._finalize = function () {
+					console.log( 'Global output object count: ' + this.wwMeshCreator.globalObjectCount );
+					this.parser.finalize();
+					this.wwMeshCreator.finalize();
+					this.validated = false;
+				};
+
+				WWOBJLoader.prototype.init = function ( payload ) {
+					this.cmdState = 'init';
+					this.setDebug( payload.debug, payload.debug );
+				};
+
+				WWOBJLoader.prototype.setMaterials = function ( payload ) {
+					this.cmdState = 'setMaterials';
+					this.wwMeshCreator.setMaterials( payload.materialNames );
+				};
+
+				WWOBJLoader.prototype.run = function ( payload ) {
+					this.cmdState = 'run';
+
+					this.parse( payload.objAsArrayBuffer );
+					console.log( 'OBJ loading complete!' );
+
+					this.cmdState = 'complete';
+					self.postMessage( {
+						cmd: this.cmdState,
+						msg: null
+					} );
+				};
+
+				return WWOBJLoader;
+			})();
+
+			var wwMeshCreatorDef = (function () {
+
+				function WWMeshCreator() {
+					this.materials = null;
+					this.debug = false;
+					this.globalObjectCount = 1;
+					this.validated = false;
+				}
+
+				WWMeshCreator.prototype.setMaterials = function ( materials ) {
+					this.materials = ( materials == null ) ? ( this.materials == null ? { materials: [] } : this.materials ) : materials;
+				};
+
+				WWMeshCreator.prototype.setDebug = function ( debug ) {
+					this.debug = ( debug == null ) ? this.debug : debug;
+				};
+
+				WWMeshCreator.prototype.validate = function () {
+					if ( this.validated ) return;
+
+					this.setMaterials( null );
+					this.setDebug( null );
+					this.globalObjectCount = 1;
+				};
+
+				WWMeshCreator.prototype.finalize = function () {
+					this.materials = null;
+					this.validated = false;
+				};
+
+				/**
+				 * RawObjectDescriptions are transformed to THREE.Mesh.
+				 * It is ensured that rawObjectDescriptions only contain objects with vertices (no need to check).
+				 *
+				 * @param rawObjectDescriptions
+				 * @param inputObjectCount
+				 * @param absoluteVertexCount
+				 * @param absoluteNormalCount
+				 * @param absoluteUvCount
+				 */
+				WWMeshCreator.prototype.buildMesh = function ( rawObjectDescriptions, inputObjectCount, absoluteVertexCount, absoluteNormalCount, absoluteUvCount ) {
+					if ( this.debug ) console.log( 'OBJLoader.buildMesh:\nInput object no.: ' + inputObjectCount );
+
+					var vertexFa = new Float32Array( absoluteVertexCount );
+					var normalFA = ( absoluteNormalCount > 0 ) ? new Float32Array( absoluteNormalCount ) : null;
+					var uvFA = ( absoluteUvCount > 0 ) ? new Float32Array( absoluteUvCount ) : null;
+
+					var rawObjectDescription;
+					var materialDescription;
+					var materialDescriptions = [];
+
+					var createMultiMaterial = ( rawObjectDescriptions.length > 1 );
+					var materialIndex = 0;
+					var materialIndexMapping = [];
+					var selectedMaterialIndex;
+					var materialGroup;
+					var materialGroups = [];
+
+					var vertexBAOffset = 0;
+					var vertexGroupOffset = 0;
+					var vertexLength;
+					var normalOffset = 0;
+					var uvOffset = 0;
+
+					for ( var oodIndex in rawObjectDescriptions ) {
+						rawObjectDescription = rawObjectDescriptions[ oodIndex ];
+
+						materialDescription = { name: rawObjectDescription.materialName, flat: false, default: false };
+						if ( this.materials[ materialDescription.name ] === null ) {
+
+							materialDescription.default = true;
+							console.warn( 'object_group "' + rawObjectDescription.objectName + '_' + rawObjectDescription.groupName + '" was defined without material! Assigning "defaultMaterial".' );
+
+						}
+						// Attach '_flat' to materialName in case flat shading is needed due to smoothingGroup 0
+						if ( rawObjectDescription.smoothingGroup === 0 ) materialDescription.flat = true;
+
+						vertexLength = rawObjectDescription.vertices.length;
+						if ( createMultiMaterial ) {
+
+							// re-use material if already used before. Reduces materials array size and eliminates duplicates
+
+							selectedMaterialIndex = materialIndexMapping[ materialDescription.name ];
+							if ( ! selectedMaterialIndex ) {
+
+								selectedMaterialIndex = materialIndex;
+								materialIndexMapping[ materialDescription.name ] = materialIndex;
+								materialDescriptions.push( materialDescription );
+								materialIndex++;
+
+							}
+							materialGroup = {
+								start: vertexGroupOffset,
+								count: vertexLength / 3,
+								index: selectedMaterialIndex
+							};
+							materialGroups.push( materialGroup );
+							vertexGroupOffset += vertexLength / 3;
+
+						} else {
+
+							materialDescriptions.push( materialDescription );
+
+						}
+
+						vertexFa.set( rawObjectDescription.vertices, vertexBAOffset );
+						vertexBAOffset += vertexLength;
+
+						if ( normalFA ) {
+
+							normalFA.set( rawObjectDescription.normals, normalOffset );
+							normalOffset += rawObjectDescription.normals.length;
+
+						}
+						if ( uvFA ) {
+
+							uvFA.set( rawObjectDescription.uvs, uvOffset );
+							uvOffset += rawObjectDescription.uvs.length;
+
+						}
+						if ( this.debug ) this.printReport( rawObjectDescription, selectedMaterialIndex );
+
+					}
+
+					self.postMessage( {
+						cmd: 'objData',
+						meshName: rawObjectDescription.objectName,
+						multiMaterial: createMultiMaterial,
+						materialDescriptions: materialDescriptions,
+						materialGroups: materialGroups,
+						vertices: vertexFa,
+						normals: normalFA,
+						uvs: uvFA
+					}, [ vertexFa.buffer ], normalFA !== null ? [ normalFA.buffer ] : null, uvFA !== null ? [ uvFA.buffer ] : null );
+
+					this.globalObjectCount++;
+				};
+
+				return WWMeshCreator;
+			})();
+
+			var wwObjLoaderRunnerDef = (function () {
+
+				function WWOBJLoaderRunner() {
+					self.addEventListener( 'message', this.runner, false );
+				}
+
+				WWOBJLoaderRunner.prototype.runner = function ( event ) {
+					var payload = event.data;
+
+					console.log( 'Command state before: ' + WWOBJLoaderRef.cmdState );
+
+					switch ( payload.cmd ) {
+						case 'init':
+
+							WWOBJLoaderRef.init( payload );
+							break;
+
+						case 'setMaterials':
+
+							WWOBJLoaderRef.setMaterials( payload );
+							break;
+
+						case 'run':
+
+							WWOBJLoaderRef.run( payload );
+							break;
+
+						default:
+
+							console.error( 'OBJLoader: Received unknown command: ' + payload.cmd );
+							break;
+
+					}
+
+					console.log( 'Command state after: ' + WWOBJLoaderRef.cmdState );
+				};
+
+				return WWOBJLoaderRunner;
+			})();
+
+			var buildObject = function ( fullName, object ) {
+				var objectString = fullName + ' = {\n';
+				var part;
+				for ( var name in object ) {
+
+					part = object[ name ];
+					if ( typeof( part ) === 'string' || part instanceof String ) {
+
+						part = part.replace( '\n', '\\n' );
+						part = part.replace( '\r', '\\r' );
+						objectString += '\t' + name + ': "' + part + '",\n';
+
+					} else if ( part instanceof Array ) {
+
+						objectString += '\t' + name + ': [' + part + '],\n';
+
+					} else if ( Number.isInteger( part ) ) {
+
+						objectString += '\t' + name + ': ' + part + ',\n';
+
+					} else if ( typeof part === 'function' ) {
+
+						objectString += '\t' + name + ': ' + part + ',\n';
+
+					}
+
+				}
+				objectString += '}\n\n';
+
+				return objectString;
+			};
+
+			var buildSingelton = function ( fullName, internalName, object ) {
+				var objectString = fullName + ' = (function () {\n\n';
+				objectString += '\t' + object.prototype.constructor.toString() + '\n\n';
+
+				var funcString;
+				var objectPart;
+				for ( var name in object.prototype ) {
+
+					objectPart = object.prototype[ name ];
+					if ( typeof objectPart === 'function' ) {
+
+						funcString = objectPart.toString();
+						objectString += '\t' + internalName + '.prototype.' + name + ' = ' + funcString + ';\n\n';
+
+					}
+
+				}
+				objectString += '\treturn ' + internalName + ';\n';
+				objectString += '})();\n\n';
+
+				return objectString;
+			};
+
+			this.workerCode = '';
+			this.workerCode += '/**\n';
+			this.workerCode += '  * This code was constructed by WWOBJLoader2._buildWebWorkerCode\n';
+			this.workerCode += '  */\n\n';
+
+			// parser re-construction
+			var objLoaderHelper = new THREE.OBJLoader2();
+			this.workerCode += objLoaderHelper._buildWebWorkerCode( buildObject, buildSingelton );
+
+			// web worker construction
+			this.workerCode += buildSingelton( 'WWOBJLoader', 'WWOBJLoader', wwDef );
+			this.workerCode += buildSingelton( 'WWMeshCreator', 'WWMeshCreator', wwMeshCreatorDef );
+			this.workerCode += 'WWOBJLoaderRef = new WWOBJLoader();\n\n';
+			this.workerCode += buildSingelton( 'WWOBJLoaderRunner', 'WWOBJLoaderRunner', wwObjLoaderRunnerDef );
+			this.workerCode += 'new WWOBJLoaderRunner();\n\n';
+
+			console.timeEnd( 'buildWebWorkerCode' );
+		}
+
+		return this.workerCode;
+	};
+
+	return WWOBJLoader2;
+
+})();
+
+/**
+ * Instruction to configure {@link THREE.OBJLoader2.WWOBJLoader2}.prepareRun to load OBJ from given ArrayBuffer and MTL from given String
+ *
+ * @param {string} modelName Overall name of the model
+ * @param {Uint8Array} objAsArrayBuffer OBJ file content as ArrayBuffer
+ * @param {string} pathTexture Path to texture files
+ * @param {string} mtlAsString MTL file content as string
+ * @param {THREE.Object3D} sceneGraphBaseNode {@link THREE.Object3D} where meshes will be attached
+ * @param {boolean} streamMeshes=true Singles meshes are directly integrated into scene when loaded or later
+ * @param {boolean} [requestTerminate=false] Request termination of web worker and free local resources after execution
+ *
+ * @returns {{modelName: string, dataAvailable: boolean, objAsArrayBuffer: null, pathTexture: null, mtlAsString: null, sceneGraphBaseNode: null, streamMeshes: boolean, requestTerminate: boolean}}
+ * @constructor
+ */
+THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer = function ( modelName, objAsArrayBuffer, pathTexture, mtlAsString, sceneGraphBaseNode, streamMeshes, requestTerminate ) {
+
+	var data = {
+		modelName: ( modelName == null ) ? 'none' : modelName,
+		dataAvailable: true,
+		objAsArrayBuffer: ( objAsArrayBuffer == null ) ? null : objAsArrayBuffer,
+		pathTexture: ( pathTexture == null ) ? null : pathTexture,
+		mtlAsString: ( mtlAsString == null ) ? null : mtlAsString,
+		sceneGraphBaseNode: ( sceneGraphBaseNode == null ) ? null : sceneGraphBaseNode,
+		streamMeshes: ( streamMeshes == null ) ? true : streamMeshes,
+		requestTerminate: ( requestTerminate == null ) ? false : requestTerminate
+	};
+
+	return data;
+};
+
+/**
+ * Instruction to configure {@link THREE.OBJLoader2.WWOBJLoader2}.prepareRun to load OBJ and MTL from files
+ *
+ * @param {string} modelName Overall name of the model
+ * @param {string} pathObj Path to OBJ file
+ * @param {string} fileObj OBJ file name
+ * @param {string} pathTexture Path to texture files
+ * @param {string} fileMtl MTL file name
+ * @param {THREE.Object3D} sceneGraphBaseNode {@link THREE.Object3D} where meshes will be attached
+ * @param {boolean} streamMeshes=true Singles meshes are directly integrated into scene when loaded or later
+ * @param {boolean} [requestTerminate=false] Request termination of web worker and free local resources after execution
+ *
+ * @returns {{modelName: string, dataAvailable: boolean, pathObj: null, fileObj: null, pathTexture: null, fileMtl: null, sceneGraphBaseNode: null, streamMeshes: boolean,  requestTerminate: boolean}}
+ * @constructor
+ */
+THREE.OBJLoader2.WWOBJLoader2.PrepDataFile = function ( modelName, pathObj, fileObj, pathTexture, fileMtl, sceneGraphBaseNode, streamMeshes, requestTerminate ) {
+
+	var data = {
+		modelName: ( modelName == null ) ? 'none' : modelName,
+		dataAvailable: false,
+		pathObj: ( pathObj == null ) ? null : pathObj,
+		fileObj: ( fileObj == null ) ? null : fileObj,
+		pathTexture: ( pathTexture == null ) ? null : pathTexture,
+		fileMtl: ( fileMtl == null ) ? null : fileMtl,
+		sceneGraphBaseNode: ( sceneGraphBaseNode == null ) ? null : sceneGraphBaseNode,
+		streamMeshes: ( streamMeshes == null ) ? true : streamMeshes,
+		requestTerminate: ( requestTerminate == null ) ? false : requestTerminate
+	};
+
+	return data;
+};
+/**
+ * Orchestrate loading of multiple OBJ files/data from an instruction queue with a configurable amount of workers (1-16).
+ * Use:
+ *   prepareWorkers
+ *   enqueueForRun
+ *   processQueue
+ *   deregister
+ *
+ * @class
+ */
+THREE.OBJLoader2.WWOBJLoader2Director = (function () {
+
+	var MAX_WEB_WORKER = 16;
+	var MAX_QUEUE_SIZE = 1024;
+
+	function WWOBJLoader2Director() {
+		this.maxQueueSize = MAX_QUEUE_SIZE ;
+		this.maxWebWorkers = MAX_WEB_WORKER;
+		this.crossOrigin = null;
+
+		this.workerDescription = {
+			prototypeDef: THREE.OBJLoader2.WWOBJLoader2.prototype,
+			callbacks: {},
+			webWorkers: [],
+			codeBuffer: null
+		};
+		this.objectsCompleted = 0;
+		this.instructionQueue = [];
+	}
+
+	/**
+	 * Returns the maximum length of the instruction queue.
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
+	 *
+	 * @returns {number}
+	 */
+	WWOBJLoader2Director.prototype.getMaxQueueSize = function () {
+		return this.maxQueueSize;
+	};
+
+	/**
+	 * Returns the maximum number of workers.
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
+	 *
+	 * @returns {number}
+	 */
+	WWOBJLoader2Director.prototype.getMaxWebWorkers = function () {
+		return this.maxWebWorkers;
+	};
+
+	/**
+	 * Sets the CORS string to be used.
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
+	 *
+	 * @param {string} crossOrigin CORS value
+	 */
+	WWOBJLoader2Director.prototype.setCrossOrigin = function ( crossOrigin ) {
+		this.crossOrigin = crossOrigin;
+	};
+
+	/**
+	 * Create or destroy workers according limits. Set the name and register callbacks for dynamically created web workers.
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
+	 *
+	 * @param {callback[]} callbacks Register callbacks for all web workers:
+	 * 		{ progress: null, completedLoading: null, errorWhileLoading: null, materialsLoaded: null, meshLoaded: null }
+	 * @param {number} maxQueueSize Set the maximum size of the instruction queue (1-1024)
+	 * @param {number} maxWebWorkers Set the maximum amount of workers (1-16)
+	 */
+	WWOBJLoader2Director.prototype.prepareWorkers = function ( callbacks, maxQueueSize, maxWebWorkers ) {
+		if ( callbacks != null ) {
+
+			for ( var key in callbacks ) {
+
+				if ( callbacks.hasOwnProperty( key ) ) this.workerDescription.callbacks[ key ] = callbacks[ key ];
+
+			}
+
+		}
+
+		this.maxQueueSize = Math.min( maxQueueSize, MAX_QUEUE_SIZE );
+		this.maxWebWorkers = Math.min( maxWebWorkers, MAX_WEB_WORKER );
+		this.objectsCompleted = 0;
+		this.instructionQueue = [];
+
+		var start = this.workerDescription.webWorkers.length;
+		if ( start < this.maxWebWorkers ) {
+
+			for ( i = start; i < this.maxWebWorkers; i ++ ) {
+
+				webWorker = this._buildWebWorker();
+				this.workerDescription.webWorkers[ i ] = webWorker;
+
+			}
+
+		} else {
+
+			for ( var webWorker, i = start - 1; i >= this.maxWebWorkers; i-- ) {
+
+				webWorker = this.workerDescription.webWorkers[ i ];
+				webWorker.setRequestTerminate( true );
+
+				this.workerDescription.webWorkers.pop();
+
+			}
+
+		}
+	};
+
+	/**
+	 * Store run instructions in internal instructionQueue
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
+	 *
+	 * @param {Object} params Either {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer} or {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataFile}
+	 */
+	WWOBJLoader2Director.prototype.enqueueForRun = function ( runParams ) {
+		if ( this.instructionQueue.length < this.maxQueueSize ) {
+			this.instructionQueue.push( runParams );
+		}
+	};
+
+	/**
+	 * Process the instructionQueue until it is depleted
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
+	 */
+	WWOBJLoader2Director.prototype.processQueue = function () {
+		if ( this.instructionQueue.length === 0 ) return;
+
+		var webWorker;
+		var runParams;
+		var length = Math.min( this.maxWebWorkers, this.instructionQueue.length );
+		for ( var i = 0; i < length; i++ ) {
+
+			webWorker = this.workerDescription.webWorkers[ i ];
+			runParams = this.instructionQueue[ 0 ];
+			webWorker.prepareRun( runParams );
+			webWorker.run();
+			this.instructionQueue.shift();
+
+		}
+	};
+
+	WWOBJLoader2Director.prototype._buildWebWorker = function () {
+		var webWorker = Object.create( this.workerDescription.prototypeDef );
+		webWorker._init();
+		if ( this.crossOrigin != null )	webWorker.setCrossOrigin( this.crossOrigin );
+
+		// Ensure code string is built once and then it is just passed on to every new instance
+		if ( this.workerDescription.codeBuffer == null ) {
+
+			this.workerDescription.codeBuffer = webWorker._buildWebWorkerCode();
+
+		} else {
+
+			webWorker._buildWebWorkerCode( this.workerDescription.codeBuffer );
+
+		}
+		for ( var key in this.workerDescription.callbacks ) {
+
+			if ( webWorker.callbacks.hasOwnProperty( key ) && this.workerDescription.callbacks.hasOwnProperty( key ) ) {
+
+				webWorker.callbacks[ key ] = this.workerDescription.callbacks[ key ];
+
+			}
+
+		}
+		var scope = this;
+		var managerCompletedLoading = function ( modelName, instanceNo, requestTerminate ) {
+			scope.objectsCompleted++;
+			if ( ! requestTerminate ) {
+
+				var rekick = scope.workerDescription.webWorkers[ instanceNo ];
+				var runParams = scope.instructionQueue[ 0 ];
+				if ( runParams != null ) {
+
+					rekick.prepareRun( runParams );
+					rekick.run();
+					scope.instructionQueue.shift();
+
+				}
+
+			}
+		};
+
+		webWorker.callbacks.director[ 'completedLoading' ] = managerCompletedLoading;
+		webWorker.instanceNo = this.workerDescription.webWorkers.length;
+		this.workerDescription.webWorkers.push( webWorker );
+		return webWorker;
+	};
+
+	/**
+	 * Terminate all workers
+	 * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
+	 */
+	WWOBJLoader2Director.prototype.deregister = function () {
+		console.log( 'WWOBJLoader2Director received the unregister call. Terminating all workers!' );
+		for ( var i = 0, webWorker, length = this.workerDescription.webWorkers.length; i < length; i++ ) {
+
+			webWorker = this.workerDescription.webWorkers[ i ];
+			webWorker.setRequestTerminate( true );
+
+		}
+		this.workerDescription.callbacks = {};
+		this.workerDescription.webWorkers = [];
+		this.workerDescription.codeBuffer = null;
+	};
+
+	return WWOBJLoader2Director;
+
+})();

+ 0 - 1132
examples/js/loaders/deprecated/SceneLoader.js

@@ -1,1132 +0,0 @@
-/**
- * @author alteredq / http://alteredqualia.com/
- */
-
-THREE.SceneLoader = function ( manager ) {
-
-	this.onLoadStart = function () {};
-	this.onLoadProgress = function() {};
-	this.onLoadComplete = function () {};
-
-	this.callbackSync = function () {};
-	this.callbackProgress = function () {};
-
-	this.geometryHandlers = {};
-	this.hierarchyHandlers = {};
-
-	this.addGeometryHandler( "ascii", THREE.JSONLoader );
-
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-
-};
-
-THREE.SceneLoader.prototype = {
-
-	constructor: THREE.SceneLoader,
-
-	load: function ( url, onLoad, onProgress, onError ) {
-
-		var scope = this;
-
-		var loader = new THREE.FileLoader( scope.manager );
-		loader.load( url, function ( text ) {
-
-			scope.parse( JSON.parse( text ), onLoad, url );
-
-		}, onProgress, onError );
-
-	},
-
-	addGeometryHandler: function ( typeID, loaderClass ) {
-
-		this.geometryHandlers[ typeID ] = { "loaderClass": loaderClass };
-
-	},
-
-	addHierarchyHandler: function ( typeID, loaderClass ) {
-
-		this.hierarchyHandlers[ typeID ] = { "loaderClass": loaderClass };
-
-	},
-
-	parse: function ( json, callbackFinished, url ) {
-
-		var scope = this;
-
-		var urlBase = THREE.Loader.prototype.extractUrlBase( url );
-
-		var geometry, material, camera, fog,
-			texture, images, color,
-			light, hex, intensity,
-			counter_models, counter_textures,
-			total_models, total_textures,
-			result;
-
-		var target_array = [];
-
-		var data = json;
-
-		// async geometry loaders
-
-		for ( var typeID in this.geometryHandlers ) {
-
-			var loaderClass = this.geometryHandlers[ typeID ][ "loaderClass" ];
-			this.geometryHandlers[ typeID ][ "loaderObject" ] = new loaderClass();
-
-		}
-
-		// async hierachy loaders
-
-		for ( var typeID in this.hierarchyHandlers ) {
-
-			var loaderClass = this.hierarchyHandlers[ typeID ][ "loaderClass" ];
-			this.hierarchyHandlers[ typeID ][ "loaderObject" ] = new loaderClass();
-
-		}
-
-		counter_models = 0;
-		counter_textures = 0;
-
-		result = {
-
-			scene: new THREE.Scene(),
-			geometries: {},
-			face_materials: {},
-			materials: {},
-			textures: {},
-			objects: {},
-			cameras: {},
-			lights: {},
-			fogs: {},
-			empties: {},
-			groups: {}
-
-		};
-
-		if ( data.transform ) {
-
-			var position = data.transform.position,
-				rotation = data.transform.rotation,
-				scale = data.transform.scale;
-
-			if ( position ) {
-
-				result.scene.position.fromArray( position );
-
-			}
-
-			if ( rotation ) {
-
-				result.scene.rotation.fromArray( rotation );
-
-			}
-
-			if ( scale ) {
-
-				result.scene.scale.fromArray( scale );
-
-			}
-
-			if ( position || rotation || scale ) {
-
-				result.scene.updateMatrix();
-				result.scene.updateMatrixWorld();
-
-			}
-
-		}
-
-		function get_url( source_url, url_type ) {
-
-			if ( url_type == "relativeToHTML" ) {
-
-				return source_url;
-
-			} else {
-
-				return urlBase + source_url;
-
-			}
-
-		}
-
-		// toplevel loader function, delegates to handle_children
-
-		function handle_objects() {
-
-			handle_children( result.scene, data.objects );
-
-		}
-
-		// handle all the children from the loaded json and attach them to given parent
-
-		function handle_children( parent, children ) {
-
-			var mat, dst, pos, rot, scl, quat;
-
-			for ( var objID in children ) {
-
-				// check by id if child has already been handled,
-				// if not, create new object
-
-				var object = result.objects[ objID ];
-				var objJSON = children[ objID ];
-
-				if ( object === undefined ) {
-
-					// meshes
-
-					if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) {
-
-						if ( objJSON.loading === undefined ) {
-
-							material = result.materials[ objJSON.material ];
-
-							objJSON.loading = true;
-
-							var loader = scope.hierarchyHandlers[ objJSON.type ][ "loaderObject" ];
-
-							// ColladaLoader
-
-							if ( loader.options ) {
-
-								loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ) );
-
-							// UTF8Loader
-							// OBJLoader
-
-							} else {
-
-								loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ) );
-
-							}
-
-						}
-
-					} else if ( objJSON.geometry !== undefined ) {
-
-						geometry = result.geometries[ objJSON.geometry ];
-
-						// geometry already loaded
-
-						if ( geometry ) {
-
-							material = result.materials[ objJSON.material ];
-
-							pos = objJSON.position;
-							rot = objJSON.rotation;
-							scl = objJSON.scale;
-							mat = objJSON.matrix;
-							quat = objJSON.quaternion;
-
-							// use materials from the model file
-							// if there is no material specified in the object
-
-							if ( ! objJSON.material ) {
-
-								material = new THREE.MultiMaterial( result.face_materials[ objJSON.geometry ] );
-
-							}
-
-							// use materials from the model file
-							// if there is just empty face material
-							// (must create new material as each model has its own face material)
-
-							if ( ( material instanceof THREE.MultiMaterial ) && material.materials.length === 0 ) {
-
-								material = new THREE.MultiMaterial( result.face_materials[ objJSON.geometry ] );
-
-							}
-
-							if ( objJSON.skin ) {
-
-								object = new THREE.SkinnedMesh( geometry, material );
-
-							} else if ( objJSON.morph ) {
-
-								object = new THREE.MorphAnimMesh( geometry, material );
-
-								if ( objJSON.duration !== undefined ) {
-
-									object.duration = objJSON.duration;
-
-								}
-
-								if ( objJSON.time !== undefined ) {
-
-									object.time = objJSON.time;
-
-								}
-
-								if ( objJSON.mirroredLoop !== undefined ) {
-
-									object.mirroredLoop = objJSON.mirroredLoop;
-
-								}
-
-								if ( material.morphNormals ) {
-
-									geometry.computeMorphNormals();
-
-								}
-
-							} else {
-
-								object = new THREE.Mesh( geometry, material );
-
-							}
-
-							object.name = objID;
-
-							if ( mat ) {
-
-								object.matrixAutoUpdate = false;
-								object.matrix.set(
-									mat[ 0 ],  mat[ 1 ],  mat[ 2 ],  mat[ 3 ],
-									mat[ 4 ],  mat[ 5 ],  mat[ 6 ],  mat[ 7 ],
-									mat[ 8 ],  mat[ 9 ],  mat[ 10 ], mat[ 11 ],
-									mat[ 12 ], mat[ 13 ], mat[ 14 ], mat[ 15 ]
-								);
-
-							} else {
-
-								object.position.fromArray( pos );
-
-								if ( quat ) {
-
-									object.quaternion.fromArray( quat );
-
-								} else {
-
-									object.rotation.fromArray( rot );
-
-								}
-
-								object.scale.fromArray( scl );
-
-							}
-
-							object.visible = objJSON.visible;
-							object.castShadow = objJSON.castShadow;
-							object.receiveShadow = objJSON.receiveShadow;
-
-							parent.add( object );
-
-							result.objects[ objID ] = object;
-
-						}
-
-					// lights
-
-					} else if ( objJSON.type === "AmbientLight" || objJSON.type === "PointLight" ||
-						objJSON.type === "DirectionalLight" || objJSON.type === "SpotLight" ||
-						objJSON.type === "HemisphereLight" ) {
-
-						var color = objJSON.color;
-						var intensity = objJSON.intensity;
-						var distance = objJSON.distance;
-						var position = objJSON.position;
-						var rotation = objJSON.rotation;
-
-						switch ( objJSON.type ) {
-
-							case 'AmbientLight':
-								light = new THREE.AmbientLight( color );
-								break;
-
-							case 'PointLight':
-								light = new THREE.PointLight( color, intensity, distance );
-								light.position.fromArray( position );
-								break;
-
-							case 'DirectionalLight':
-								light = new THREE.DirectionalLight( color, intensity );
-								light.position.fromArray( objJSON.direction );
-								break;
-
-							case 'SpotLight':
-								light = new THREE.SpotLight( color, intensity, distance );
-								light.angle = objJSON.angle;
-								light.position.fromArray( position );
-								light.target.set( position[ 0 ], position[ 1 ] - distance, position[ 2 ] );
-								light.target.applyEuler( new THREE.Euler( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ], 'XYZ' ) );
-								break;
-
-							case 'HemisphereLight':
-								light = new THREE.DirectionalLight( color, intensity, distance );
-								light.target.set( position[ 0 ], position[ 1 ] - distance, position[ 2 ] );
-								light.target.applyEuler( new THREE.Euler( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ], 'XYZ' ) );
-								break;
-
-						}
-
-						parent.add( light );
-
-						light.name = objID;
-						result.lights[ objID ] = light;
-						result.objects[ objID ] = light;
-
-					// cameras
-
-					} else if ( objJSON.type === "PerspectiveCamera" || objJSON.type === "OrthographicCamera" ) {
-
-						pos = objJSON.position;
-						rot = objJSON.rotation;
-						quat = objJSON.quaternion;
-
-						if ( objJSON.type === "PerspectiveCamera" ) {
-
-							camera = new THREE.PerspectiveCamera( objJSON.fov, objJSON.aspect, objJSON.near, objJSON.far );
-
-						} else if ( objJSON.type === "OrthographicCamera" ) {
-
-							camera = new THREE.OrthographicCamera( objJSON.left, objJSON.right, objJSON.top, objJSON.bottom, objJSON.near, objJSON.far );
-
-						}
-
-						camera.name = objID;
-						camera.position.fromArray( pos );
-
-						if ( quat !== undefined ) {
-
-							camera.quaternion.fromArray( quat );
-
-						} else if ( rot !== undefined ) {
-
-							camera.rotation.fromArray( rot );
-
-						} else if ( objJSON.target ) {
-
-							camera.lookAt( new THREE.Vector3().fromArray( objJSON.target ) );
-
-						}
-
-						parent.add( camera );
-
-						result.cameras[ objID ] = camera;
-						result.objects[ objID ] = camera;
-
-					// pure Object3D
-
-					} else {
-
-						pos = objJSON.position;
-						rot = objJSON.rotation;
-						scl = objJSON.scale;
-						quat = objJSON.quaternion;
-
-						object = new THREE.Object3D();
-						object.name = objID;
-						object.position.fromArray( pos );
-
-						if ( quat ) {
-
-							object.quaternion.fromArray( quat );
-
-						} else {
-
-							object.rotation.fromArray( rot );
-
-						}
-
-						object.scale.fromArray( scl );
-						object.visible = ( objJSON.visible !== undefined ) ? objJSON.visible : false;
-
-						parent.add( object );
-
-						result.objects[ objID ] = object;
-						result.empties[ objID ] = object;
-
-					}
-
-					if ( object ) {
-
-						if ( objJSON.userData !== undefined ) {
-
-							for ( var key in objJSON.userData ) {
-
-								var value = objJSON.userData[ key ];
-								object.userData[ key ] = value;
-
-							}
-
-						}
-
-						if ( objJSON.groups !== undefined ) {
-
-							for ( var i = 0; i < objJSON.groups.length; i ++ ) {
-
-								var groupID = objJSON.groups[ i ];
-
-								if ( result.groups[ groupID ] === undefined ) {
-
-									result.groups[ groupID ] = [];
-
-								}
-
-								result.groups[ groupID ].push( objID );
-
-							}
-
-						}
-
-					}
-
-				}
-
-				if ( object !== undefined && objJSON.children !== undefined ) {
-
-					handle_children( object, objJSON.children );
-
-				}
-
-			}
-
-		}
-
-		function handle_mesh( geo, mat, id ) {
-
-			result.geometries[ id ] = geo;
-			result.face_materials[ id ] = mat;
-			handle_objects();
-
-		}
-
-		function handle_hierarchy( node, id, parent, material, obj ) {
-
-			var p = obj.position;
-			var r = obj.rotation;
-			var q = obj.quaternion;
-			var s = obj.scale;
-
-			node.position.fromArray( p );
-
-			if ( q ) {
-
-				node.quaternion.fromArray( q );
-
-			} else {
-
-				node.rotation.fromArray( r );
-
-			}
-
-			node.scale.fromArray( s );
-
-			// override children materials
-			// if object material was specified in JSON explicitly
-
-			if ( material ) {
-
-				node.traverse( function ( child ) {
-
-					child.material = material;
-
-				} );
-
-			}
-
-			// override children visibility
-			// with root node visibility as specified in JSON
-
-			var visible = ( obj.visible !== undefined ) ? obj.visible : true;
-
-			node.traverse( function ( child ) {
-
-				child.visible = visible;
-
-			} );
-
-			parent.add( node );
-
-			node.name = id;
-
-			result.objects[ id ] = node;
-			handle_objects();
-
-		}
-
-		function create_callback_geometry( id ) {
-
-			return function ( geo, mat ) {
-
-				geo.name = id;
-
-				handle_mesh( geo, mat, id );
-
-				counter_models -= 1;
-
-				scope.onLoadComplete();
-
-				async_callback_gate();
-
-			}
-
-		}
-
-		function create_callback_hierachy( id, parent, material, obj ) {
-
-			return function ( event ) {
-
-				var result;
-
-				// loaders which use EventDispatcher
-
-				if ( event.content ) {
-
-					result = event.content;
-
-				// ColladaLoader
-
-				} else if ( event.dae ) {
-
-					result = event.scene;
-
-
-				// UTF8Loader
-
-				} else {
-
-					result = event;
-
-				}
-
-				handle_hierarchy( result, id, parent, material, obj );
-
-				counter_models -= 1;
-
-				scope.onLoadComplete();
-
-				async_callback_gate();
-
-			}
-
-		}
-
-		function create_callback_embed( id ) {
-
-			return function ( geo, mat ) {
-
-				geo.name = id;
-
-				result.geometries[ id ] = geo;
-				result.face_materials[ id ] = mat;
-
-			}
-
-		}
-
-		function async_callback_gate() {
-
-			var progress = {
-
-				totalModels : total_models,
-				totalTextures : total_textures,
-				loadedModels : total_models - counter_models,
-				loadedTextures : total_textures - counter_textures
-
-			};
-
-			scope.callbackProgress( progress, result );
-
-			scope.onLoadProgress();
-
-			if ( counter_models === 0 && counter_textures === 0 ) {
-
-				finalize();
-				callbackFinished( result );
-
-			}
-
-		}
-
-		function finalize() {
-
-			// take care of targets which could be asynchronously loaded objects
-
-			for ( var i = 0; i < target_array.length; i ++ ) {
-
-				var ta = target_array[ i ];
-
-				var target = result.objects[ ta.targetName ];
-
-				if ( target ) {
-
-					ta.object.target = target;
-
-				} else {
-
-					// if there was error and target of specified name doesn't exist in the scene file
-					// create instead dummy target
-					// (target must be added to scene explicitly as parent is already added)
-
-					ta.object.target = new THREE.Object3D();
-					result.scene.add( ta.object.target );
-
-				}
-
-				ta.object.target.userData.targetInverse = ta.object;
-
-			}
-
-		}
-
-		var callbackTexture = function ( count ) {
-
-			counter_textures -= count;
-			async_callback_gate();
-
-			scope.onLoadComplete();
-
-		};
-
-		// must use this instead of just directly calling callbackTexture
-		// because of closure in the calling context loop
-
-		var generateTextureCallback = function ( count ) {
-
-			return function () {
-
-				callbackTexture( count );
-
-			};
-
-		};
-
-		function traverse_json_hierarchy( objJSON, callback ) {
-
-			callback( objJSON );
-
-			if ( objJSON.children !== undefined ) {
-
-				for ( var objChildID in objJSON.children ) {
-
-					traverse_json_hierarchy( objJSON.children[ objChildID ], callback );
-
-				}
-
-			}
-
-		}
-
-		// first go synchronous elements
-
-		// fogs
-
-		var fogID, fogJSON;
-
-		for ( fogID in data.fogs ) {
-
-			fogJSON = data.fogs[ fogID ];
-
-			if ( fogJSON.type === "linear" ) {
-
-				fog = new THREE.Fog( 0x000000, fogJSON.near, fogJSON.far );
-
-			} else if ( fogJSON.type === "exp2" ) {
-
-				fog = new THREE.FogExp2( 0x000000, fogJSON.density );
-
-			}
-
-			color = fogJSON.color;
-			fog.color.setRGB( color[ 0 ], color[ 1 ], color[ 2 ] );
-
-			result.fogs[ fogID ] = fog;
-
-		}
-
-		// now come potentially asynchronous elements
-
-		// geometries
-
-		// count how many geometries will be loaded asynchronously
-
-		var geoID, geoJSON;
-
-		for ( geoID in data.geometries ) {
-
-			geoJSON = data.geometries[ geoID ];
-
-			if ( geoJSON.type in this.geometryHandlers ) {
-
-				counter_models += 1;
-
-				scope.onLoadStart();
-
-			}
-
-		}
-
-		// count how many hierarchies will be loaded asynchronously
-
-		for ( var objID in data.objects ) {
-
-			traverse_json_hierarchy( data.objects[ objID ], function ( objJSON ) {
-
-				if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) {
-
-					counter_models += 1;
-
-					scope.onLoadStart();
-
-				}
-
-			} );
-
-		}
-
-		total_models = counter_models;
-
-		for ( geoID in data.geometries ) {
-
-			geoJSON = data.geometries[ geoID ];
-
-			if ( geoJSON.type === "cube" ) {
-
-				geometry = new THREE.BoxGeometry( geoJSON.width, geoJSON.height, geoJSON.depth, geoJSON.widthSegments, geoJSON.heightSegments, geoJSON.depthSegments );
-				geometry.name = geoID;
-				result.geometries[ geoID ] = geometry;
-
-			} else if ( geoJSON.type === "plane" ) {
-
-				geometry = new THREE.PlaneGeometry( geoJSON.width, geoJSON.height, geoJSON.widthSegments, geoJSON.heightSegments );
-				geometry.name = geoID;
-				result.geometries[ geoID ] = geometry;
-
-			} else if ( geoJSON.type === "sphere" ) {
-
-				geometry = new THREE.SphereGeometry( geoJSON.radius, geoJSON.widthSegments, geoJSON.heightSegments );
-				geometry.name = geoID;
-				result.geometries[ geoID ] = geometry;
-
-			} else if ( geoJSON.type === "cylinder" ) {
-
-				geometry = new THREE.CylinderGeometry( geoJSON.topRad, geoJSON.botRad, geoJSON.height, geoJSON.radSegs, geoJSON.heightSegs );
-				geometry.name = geoID;
-				result.geometries[ geoID ] = geometry;
-
-			} else if ( geoJSON.type === "torus" ) {
-
-				geometry = new THREE.TorusGeometry( geoJSON.radius, geoJSON.tube, geoJSON.segmentsR, geoJSON.segmentsT );
-				geometry.name = geoID;
-				result.geometries[ geoID ] = geometry;
-
-			} else if ( geoJSON.type === "icosahedron" ) {
-
-				geometry = new THREE.IcosahedronGeometry( geoJSON.radius, geoJSON.subdivisions );
-				geometry.name = geoID;
-				result.geometries[ geoID ] = geometry;
-
-			} else if ( geoJSON.type in this.geometryHandlers ) {
-
-				var loader = this.geometryHandlers[ geoJSON.type ][ "loaderObject" ];
-				loader.load( get_url( geoJSON.url, data.urlBaseType ), create_callback_geometry( geoID ) );
-
-			} else if ( geoJSON.type === "embedded" ) {
-
-				var modelJson = data.embeds[ geoJSON.id ],
-					texture_path = "";
-
-				// pass metadata along to jsonLoader so it knows the format version
-
-				modelJson.metadata = data.metadata;
-
-				if ( modelJson ) {
-
-					var jsonLoader = this.geometryHandlers[ "ascii" ][ "loaderObject" ];
-					var model = jsonLoader.parse( modelJson, texture_path );
-					create_callback_embed( geoID )( model.geometry, model.materials );
-
-				}
-
-			}
-
-		}
-
-		// textures
-
-		// count how many textures will be loaded asynchronously
-
-		var textureID, textureJSON;
-
-		for ( textureID in data.textures ) {
-
-			textureJSON = data.textures[ textureID ];
-
-			if ( Array.isArray( textureJSON.url ) ) {
-
-				counter_textures += textureJSON.url.length;
-
-				for ( var n = 0; n < textureJSON.url.length; n ++ ) {
-
-					scope.onLoadStart();
-
-				}
-
-			} else {
-
-				counter_textures += 1;
-
-				scope.onLoadStart();
-
-			}
-
-		}
-
-		total_textures = counter_textures;
-
-		for ( textureID in data.textures ) {
-
-			textureJSON = data.textures[ textureID ];
-
-			if ( textureJSON.mapping !== undefined && THREE[ textureJSON.mapping ] !== undefined ) {
-
-				textureJSON.mapping = THREE[ textureJSON.mapping ];
-
-			}
-
-			var texture;
-
-			if ( Array.isArray( textureJSON.url ) ) {
-
-				var count = textureJSON.url.length;
-				var urls = [];
-
-				for ( var i = 0; i < count; i ++ ) {
-
-					urls[ i ] = get_url( textureJSON.url[ i ], data.urlBaseType );
-
-				}
-
-				var loader = THREE.Loader.Handlers.get( urls[ 0 ] );
-
-				if ( loader !== null ) {
-
-					texture = loader.load( urls, generateTextureCallback( count ) );
-
-					if ( textureJSON.mapping !== undefined )
-						texture.mapping = textureJSON.mapping;
-
-				} else {
-
-					texture = new THREE.CubeTextureLoader().load( urls, generateTextureCallback( count ) );
-					texture.mapping = textureJSON.mapping;
-
-				}
-
-			} else {
-
-				var fullUrl = get_url( textureJSON.url, data.urlBaseType );
-				var textureCallback = generateTextureCallback( 1 );
-
-				var loader = THREE.Loader.Handlers.get( fullUrl );
-
-				if ( loader !== null ) {
-
-					texture = loader.load( fullUrl, textureCallback );
-
-				} else {
-
-					texture = new THREE.Texture();
-					loader = new THREE.ImageLoader();
-
-					( function ( texture ) {
-
-						loader.load( fullUrl, function ( image ) {
-
-							texture.image = image;
-							texture.needsUpdate = true;
-
-							textureCallback();
-
-						} );
-
-					} )( texture )
-
-
-				}
-
-				if ( textureJSON.mapping !== undefined )
-					texture.mapping = textureJSON.mapping;
-
-				if ( THREE[ textureJSON.minFilter ] !== undefined )
-					texture.minFilter = THREE[ textureJSON.minFilter ];
-
-				if ( THREE[ textureJSON.magFilter ] !== undefined )
-					texture.magFilter = THREE[ textureJSON.magFilter ];
-
-				if ( textureJSON.anisotropy ) texture.anisotropy = textureJSON.anisotropy;
-
-				if ( textureJSON.repeat ) {
-
-					texture.repeat.set( textureJSON.repeat[ 0 ], textureJSON.repeat[ 1 ] );
-
-					if ( textureJSON.repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping;
-					if ( textureJSON.repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping;
-
-				}
-
-				if ( textureJSON.offset ) {
-
-					texture.offset.set( textureJSON.offset[ 0 ], textureJSON.offset[ 1 ] );
-
-				}
-
-				// handle wrap after repeat so that default repeat can be overriden
-
-				if ( textureJSON.wrap ) {
-
-					var wrapMap = {
-						"repeat": THREE.RepeatWrapping,
-						"mirror": THREE.MirroredRepeatWrapping
-					};
-
-					if ( wrapMap[ textureJSON.wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ textureJSON.wrap[ 0 ] ];
-					if ( wrapMap[ textureJSON.wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ textureJSON.wrap[ 1 ] ];
-
-				}
-
-			}
-
-			result.textures[ textureID ] = texture;
-
-		}
-
-		// materials
-
-		var matID, matJSON;
-		var parID;
-
-		for ( matID in data.materials ) {
-
-			matJSON = data.materials[ matID ];
-
-			for ( parID in matJSON.parameters ) {
-
-				if ( parID === "envMap" || parID === "map" || parID === "lightMap" || parID === "bumpMap" || parID === "normalMap" || parID === "alphaMap" ) {
-
-					matJSON.parameters[ parID ] = result.textures[ matJSON.parameters[ parID ] ];
-
-				} else if ( parID === "shading" ) {
-
-					matJSON.parameters[ parID ] = ( matJSON.parameters[ parID ] === "flat" ) ? THREE.FlatShading : THREE.SmoothShading;
-
-				} else if ( parID === "side" ) {
-
-					if ( matJSON.parameters[ parID ] == "double" ) {
-
-						matJSON.parameters[ parID ] = THREE.DoubleSide;
-
-					} else if ( matJSON.parameters[ parID ] == "back" ) {
-
-						matJSON.parameters[ parID ] = THREE.BackSide;
-
-					} else {
-
-						matJSON.parameters[ parID ] = THREE.FrontSide;
-
-					}
-
-				} else if ( parID === "blending" ) {
-
-					matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.NormalBlending;
-
-				} else if ( parID === "combine" ) {
-
-					matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.MultiplyOperation;
-
-				} else if ( parID === "vertexColors" ) {
-
-					if ( matJSON.parameters[ parID ] == "face" ) {
-
-						matJSON.parameters[ parID ] = THREE.FaceColors;
-
-					// default to vertex colors if "vertexColors" is anything else face colors or 0 / null / false
-
-					} else if ( matJSON.parameters[ parID ] ) {
-
-						matJSON.parameters[ parID ] = THREE.VertexColors;
-
-					}
-
-				} else if ( parID === "wrapRGB" ) {
-
-					var v3 = matJSON.parameters[ parID ];
-					matJSON.parameters[ parID ] = new THREE.Vector3( v3[ 0 ], v3[ 1 ], v3[ 2 ] );
-
-				} else if ( parID === "normalScale" ) {
-
-					var v2 = matJSON.parameters[ parID ];
-					matJSON.parameters[ parID ] = new THREE.Vector2( v2[ 0 ], v2[ 1 ] );
-
-				}
-
-			}
-
-			if ( matJSON.parameters.opacity !== undefined && matJSON.parameters.opacity < 1.0 ) {
-
-				matJSON.parameters.transparent = true;
-
-			}
-
-			material = new THREE[ matJSON.type ]( matJSON.parameters );
-			material.name = matID;
-
-			result.materials[ matID ] = material;
-
-		}
-
-		// second pass through all materials to initialize MultiMaterials
-		// that could be referring to other materials out of order
-
-		for ( matID in data.materials ) {
-
-			matJSON = data.materials[ matID ];
-
-			if ( matJSON.parameters.materials ) {
-
-				var materialArray = [];
-
-				for ( var i = 0; i < matJSON.parameters.materials.length; i ++ ) {
-
-					var label = matJSON.parameters.materials[ i ];
-					materialArray.push( result.materials[ label ] );
-
-				}
-
-				result.materials[ matID ].materials = materialArray;
-
-			}
-
-		}
-
-		// objects ( synchronous init of procedural primitives )
-
-		handle_objects();
-
-		// defaults
-
-		if ( result.cameras && data.defaults.camera ) {
-
-			result.currentCamera = result.cameras[ data.defaults.camera ];
-
-		}
-
-		if ( result.fogs && data.defaults.fog ) {
-
-			result.scene.fog = result.fogs[ data.defaults.fog ];
-
-		}
-
-		// synchronous callback
-
-		scope.callbackSync( result );
-
-		// just in case there are no async elements
-
-		async_callback_gate();
-
-	}
-
-};

File diff suppressed because it is too large
+ 0 - 0
examples/js/loaders/draco_decoder.js


+ 18 - 16
examples/js/vr/WebVR.js

@@ -24,12 +24,7 @@ var WEBVR = {
 
 			navigator.getVRDisplays()
 				.then( function ( displays ) {
-
 					onDisplay( displays[ 0 ] );
-
-				} )
-				.catch( function () {
-					// no displays
 				} );
 
 		}
@@ -85,14 +80,12 @@ var WEBVR = {
 
 	getButton: function ( display, canvas ) {
 
-		/*
-		if ( display instanceof VRDisplay === false ) {
+		if ( 'VREffect' in THREE && display instanceof THREE.VREffect ) {
 
 			console.error( 'WebVR.getButton() now expects a VRDisplay.' );
-			return;
+			return document.createElement( 'button' );
 
 		}
-		*/
 
 		var button = document.createElement( 'button' );
 		button.style.position = 'absolute';
@@ -109,18 +102,27 @@ var WEBVR = {
 		button.style.fontStyle = 'normal';
 		button.style.textAlign = 'center';
 		button.style.zIndex = '999';
-		button.textContent = 'ENTER VR';
-		button.onclick = function () {
 
-			display.isPresenting ? display.exitPresent() : display.requestPresent( [ { source: canvas } ] );
+		if ( display ) {
+
+			button.textContent = 'ENTER VR';
+			button.onclick = function () {
+
+				display.isPresenting ? display.exitPresent() : display.requestPresent( [ { source: canvas } ] );
+
+			};
 
-		};
+			window.addEventListener( 'vrdisplaypresentchange', function () {
 
-		window.addEventListener( 'vrdisplaypresentchange', function () {
+				button.textContent = display.isPresenting ? 'EXIT VR' : 'ENTER VR';
 
-			button.textContent = display.isPresenting ? 'EXIT VR' : 'ENTER VR';
+			}, false );
 
-		}, false );
+		} else {
+
+			button.textContent = 'NO VR DISPLAY';
+
+		}
 
 		return button;
 

BIN
examples/models/gltf/BoomBox/glTF-pbrSpecularGlossiness/BoomBox_metallicRoughness.png


BIN
examples/models/gltf/BoomBox/glTF/BoomBox_metallicRoughness.png


+ 3 - 2
examples/models/skinned/marine/marine_anims_core.json

@@ -51541,7 +51541,7 @@
         "vertexColors":0,
         "name":"MaleMarineC",
         "type":"MeshPhongMaterial",
-        "uuid":"CB03D851-DD70-39D9-879D-B50FD9EA22B2",        
+        "uuid":"CB03D851-DD70-39D9-879D-B50FD9EA22B2",
         "map":"57C8C518-0CD0-3866-88FE-BBB14474EBD7",
         "transparent":false,
         "depthTest":true,
@@ -51549,7 +51549,8 @@
         "shininess":9,
         "emissive":0,
         "depthWrite":true,
-        "specular":3223857
+        "specular":3223857,
+        "skinning":true
     }],
     "object":{
         "children":[{

+ 0 - 769
examples/scenes/test_scene.js

@@ -1,769 +0,0 @@
-{
-
-	"metadata": {
-		"formatVersion": 3.2,
-		"type" : "scene"
-	},
-
-	"urlBaseType" : "relativeToHTML",
-
-	"objects": {
-
-		"group" : {
-			"position" : [ 0, 0, 0 ],
-			"rotation" : [ 0, 0, 0 ],
-			"scale"	   : [ 1, 1, 1 ],
-			"visible"  : true,
-			"children" : {
-
-				"cube1" : {
-					"geometry" : "cubeNormals",
-					"material" : "phong_red",
-					"position" : [ 0, 0, 0 ],
-					"rotation" : [ 0, -0.3, 0 ],
-					"scale"	   : [ 1, 1, 1 ],
-					"visible"  : true,
-					"userData" : {
-						"rotating" : true,
-						"rotateX"  : true,
-						"rotateY"  : true
-					}
-				},
-
-				"cube2" : {
-					"geometry" : "cubeWire",
-					"material" : "basic_white",
-					"position" : [ 0, 0, 0 ],
-					"rotation" : [ 0, -0.3, 0 ],
-					"scale"	   : [ 2, 2, 2 ],
-					"visible"  : true
-				},
-
-				"cube3" : {
-					"geometry" : "cube",
-					"material" : "minecraft",
-					"position" : [ -30, -5, 25 ],
-					"rotation" : [ 0, 0.8, 0 ],
-					"scale"	   : [ 1, 1, 1 ],
-					"visible"  : true
-				},
-
-				"sphere_lambert" : {
-					"geometry" : "sphere",
-					"material" : "lambert_green",
-					"position" : [ -20, -5, 15 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 1, 1, 1 ],
-					"visible"  : true
-				},
-
-				"sphere_refraction" : {
-					"geometry" : "sphere",
-					"material" : "basic_refraction",
-					"position" : [ 50, 45, -50 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 1, 1, 1 ],
-					"visible"  : true
-				},
-
-				"icosahedron" : {
-					"geometry" : "icosahedron",
-					"material" : "faceted_white",
-					"position" : [ 20, 10, -60 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 1, 1, 1 ],
-					"visible"  : true
-				},
-
-				"torus" : {
-					"geometry" : "torus",
-					"material" : "phong_orange",
-					"position" : [ -20, 5, -50 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 2, 2, 2 ],
-					"visible"  : true
-				},
-
-				"cone" : {
-					"geometry" : "cone",
-					"material" : "lambert_blue",
-					"position" : [ -50, 15, -50 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 1, 1, 1 ],
-					"visible"  : true
-				},
-
-				"cylinder" : {
-					"geometry" : "cylinder",
-					"material" : "lambert_blue",
-					"position" : [ 50, 15, -50 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 1, 1, 1 ],
-					"visible"  : true
-				},
-
-				"colorcube1" : {
-					"geometry" : "colorcube",
-					"material" : "face",
-					"position" : [ -10, -5, 30 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 5, 5, 5 ],
-					"visible"  : true,
-					"children" : {
-						"colorcube2" : {
-							"geometry" : "colorcube",
-							"material" : "face",
-							"position" : [ 0, 2, 0 ],
-							"rotation" : [ 0.1, 0.1, 0.1 ],
-							"scale"	: [ 0.5, 0.5, 0.5 ],
-							"visible"  : true,
-							"children" : {
-								"colorcube3" : {
-									"geometry" : "colorcube",
-									"material" : "face",
-									"position" : [ 0, 2, 0 ],
-									"rotation" : [ 0.1, 0.1, 0.1 ],
-									"scale"	: [ 0.5, 0.5, 0.5 ],
-									"visible"  : true,
-									"children" : {
-										"colorcube4" : {
-											"geometry" : "colorcube",
-											"material" : "face",
-											"position" : [ 0, 2, 0 ],
-											"rotation" : [ 0.1, 0.1, 0.1 ],
-											"scale"	: [ 0.5, 0.5, 0.5 ],
-											"visible"  : true
-										}
-									}
-								}
-							}
-						}
-					}
-				},
-
-				"veyron" : {
-					"geometry" : "veyron",
-					"material" : "multi_veyron",
-					"position" : [ 40, -1, 0 ],
-					"rotation" : [ 0, 0.3, 0 ],
-					"scale"	   : [ 0.25, 0.25, 0.25 ],
-					"visible"  : true
-				},
-
-				"walt" : {
-					"geometry" : "WaltHead",
-					"material" : "lambert_cube",
-					"position" : [ -75, 10, -30 ],
-					"rotation" : [ 0, 0.5, 0 ],
-					"scale"	   : [ 0.5, 0.5, 0.5 ],
-					"visible"  : true
-				},
-
-				"ben" : {
-					"geometry" : "ben",
-					"material" : "phong_ben",
-					"position" : [ -45, -10, 0 ],
-					"rotation" : [ 0, 0.5, 0 ],
-					"scale"	   : [ 55, 55, 55 ],
-					"visible"  : true
-				},
-
-				"sittingBox" : {
-					"geometry" : "sittingBox",
-					"material" : "phong_morph",
-					"position" : [ -60, -10, 10 ],
-					"rotation" : [ 0, 0.5, 0 ],
-					"scale"	   : [ 23, 23, 23 ],
-					"visible"  : true,
-					"morph"	   : true,
-					"duration" : 8000,
-					"mirroredLoop" : true
-				},
-
-				"knight" : {
-					"geometry" : "knight",
-					"material" : "phong_skin",
-					"position" : [ 70, -10, 10 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 2.5, 2.5, 2.5 ],
-					"visible"  : true,
-					"skin"	   : true
-				},
-
-				"man" : {
-					"type": "obj",
-					"url" : "obj/male02/male02.obj",
-					"material" : "phong_man",
-					"position" : [ -10, -10, -25 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 0.2, 0.2, 0.2 ],
-					"visible"  : true
-				},
-
-				"man_clone_1" : {
-					"geometry" : "man",
-					"material" : "multi_1",
-					"position" : [ 2.5, -10, -25 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 0.2, 0.2, 0.2 ],
-					"visible"  : true
-				},
-
-				"man_clone_2" : {
-					"geometry" : "man",
-					"material" : "multi_2",
-					"position" : [ 15, -10, -25 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 0.2, 0.2, 0.2 ],
-					"visible"  : true
-				},
-
-				"monster" : {
-					"type": "dae",
-					"url" : "models/collada/monster/monster.dae",
-					"position" : [ -43, -10, 27 ],
-					"rotation" : [ -1.57, 0, 0 ],
-					"scale"	   : [ 0.01, 0.01, 0.01 ],
-					"visible"  : true
-				},
-
-				"hand" : {
-					"type" : "utf8",
-					"url"  : "models/utf8/hand.js",
-					"material" : "phong_hand",
-					"position" : [ -28, -1, 29 ],
-					"rotation" : [ 0, 0.5, 0 ],
-					"scale"	   : [ 12, 12, 12 ],
-					"visible"  : true,
-					"userData" : {
-						"rotating" : true,
-						"rotateY"  : true
-					}
-				},
-
-				"bunny" : {
-					"geometry" : "bunny",
-					"material" : "phong_bunny",
-					"position" : [ -25, -14, 0 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 100, 100, 100 ],
-					"visible"  : true
-				},
-
-				"disk" : {
-					"geometry" : "disk",
-					"material" : "phong_disk",
-					"position" : [ 7, -10, 30 ],
-					"rotation" : [ 1.57, 0, 0 ],
-					"scale"	   : [ 10, 10, 10 ],
-					"visible"  : true
-				},
-
-				"quad_bg" : {
-					"geometry" : "quad",
-					"material" : "textured_bg",
-					"position" : [ 0, 15, -90 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 20, 20, 20 ],
-					"visible"  : true
-				},
-
-				"quad_texture1" : {
-					"geometry" : "quad",
-					"material" : "textured_compressed_dxt3",
-					"position" : [ 15, -5, 20 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 1, 1, 1 ],
-					"visible"  : true
-				},
-
-				"quad_texture2" : {
-					"geometry" : "quad",
-					"material" : "textured_compressed_dxt5",
-					"position" : [ 15, -5, 25 ],
-					"rotation" : [ 0, 0, 0 ],
-					"scale"	   : [ 1, 1, 1 ],
-					"visible"  : true
-				},
-
-				"ground" : {
-					"geometry" : "plane",
-					"material" : "basic_gray",
-					"position" : [ 0, -10, 0 ],
-					"rotation" : [ -1.57, 0, 0 ],
-					"scale"	   : [ 100, 100, 100 ],
-					"visible"  : true
-				},
-
-				"light1": {
-					"type"		 : "DirectionalLight",
-					"direction"	 : [ 0, 1, 1 ],
-					"color" 	 : 16777215,
-					"intensity"	 : 1
-				},
-
-				"light2": {
-					"type"	  : "PointLight",
-					"position": [ 0, 0, 0 ],
-					"color"   : 16777215,
-					"intensity"	 : 1.25
-				},
-
-				"camera1": {
-					"type"  : "PerspectiveCamera",
-					"fov"   : 50,
-					"aspect": 1.33333,
-					"near"  : 1,
-					"far"   : 1000,
-					"position": [ 0, 0, 100 ],
-					"target"  : [ 0, 0, 0 ]
-				},
-
-				"camera2": {
-					"type"  : "OrthographicCamera",
-					"left"  : 0,
-					"right" : 1024,
-					"top"   : 0,
-					"bottom": 1024,
-					"near"  : 1,
-					"far"   : 1000,
-					"position": [ 0, 0, 0 ],
-					"target"  : [ 0, 0, 0 ]
-				}
-			}
-		}
-
-	},
-
-	"geometries": {
-
-		"cube": {
-			"type"  : "cube",
-			"width" : 10,
-			"height": 10,
-			"depth" : 10,
-			"widthSegments"  : 1,
-			"heightSegments" : 1,
-			"depthSegments"  : 1
-		},
-
-		"cubeNormals": {
-			"type"  : "cube",
-			"width" : 10,
-			"height": 10,
-			"depth" : 10,
-			"widthSegments"  : 1,
-			"heightSegments" : 1,
-			"depthSegments"  : 1
-		},
-
-		"cubeWire": {
-			"type"  : "cube",
-			"width" : 10,
-			"height": 10,
-			"depth" : 10,
-			"widthSegments"  : 1,
-			"heightSegments" : 1,
-			"depthSegments"  : 1
-		},
-
-		"plane": {
-			"type"   : "plane",
-			"width"  : 10,
-			"height" : 10,
-			"widthSegments"  : 50,
-			"heightSegments" : 50
-		},
-
-		"quad": {
-			"type"   : "plane",
-			"width"  : 10,
-			"height" : 10,
-			"widthSegments"  : 1,
-			"heightSegments" : 1
-		},
-
-		"sphere": {
-			"type"	: "sphere",
-			"radius"  : 5,
-			"widthSegments"  : 32,
-			"heightSegments" : 16
-		},
-
-		"sphere_uvs": {
-			"type"	: "sphere",
-			"radius"  : 5,
-			"widthSegments"  : 32,
-			"heightSegments" : 16
-		},
-
-		"icosahedron": {
-			"type"	: "icosahedron",
-			"radius"  : 20,
-			"subdivisions"  : 2
-		},
-
-		"torus": {
-			"type"	: "torus",
-			"radius"  : 5,
-			"tube"	  : 2,
-			"segmentsR" : 16,
-			"segmentsT" : 32
-		},
-
-		"cylinder": {
-			"type"	: "cylinder",
-			"topRad"   : 5,
-			"botRad"   : 5,
-			"height"   : 50,
-			"radSegs"  : 32,
-			"heightSegs": 1
-		},
-
-		"cone": {
-			"type"	: "cylinder",
-			"topRad"   : 0,
-			"botRad"   : 5,
-			"height"   : 50,
-			"radSegs"  : 32,
-			"heightSegs" : 1
-		},
-
-		"WaltHead": {
-			"type": "binary",
-			"url" : "obj/walt/WaltHead_bin.js"
-		},
-
-		"veyron": {
-			"type": "binary",
-			"url" : "obj/veyron/VeyronNoUv_bin.js"
-		},
-
-		"sittingBox": {
-			"type": "ascii",
-			"url" : "models/animated/sittingBox.js"
-		},
-
-		"knight": {
-			"type": "ascii",
-			"url" : "models/skinned/knight.js"
-		},
-
-		"man": {
-			"type": "binary",
-			"url" : "obj/male02/Male02_bin.js"
-		},
-
-		"ben": {
-			"type": "ctm",
-			"url" : "models/ctm/ben.ctm",
-			"useWorkers" : true
-		},
-
-		"bunny": {
-			"type": "vtk",
-			"url" : "models/vtk/bunny.vtk"
-		},
-
-		"disk": {
-			"type": "stl",
-			"url" : "models/stl/ascii/slotted_disk.stl"
-		},
-
-		"colorcube": {
-			"type": "embedded",
-			"id"  : "cube_fvc"
-		}
-
-	},
-
-	"embeds": {
-
-		"cube_fvc": {
-
-			"metadata" : {
-				"formatVersion" : 3
-			},
-
-			"scale" : 1.0,
-
-			"materials": [{
-				"DbgColor" : 15658734,
-				"DbgIndex" : 0,
-				"DbgName" : "Material",
-				"colorDiffuse" : [0.8, 0.8, 0.8],
-				"colorSpecular" : [0.5, 0.5, 0.5],
-				"specularCoef" : 50,
-				"opacity" : 1.0,
-				"vertexColors" : true
-			}],
-
-			"vertices": [1.000000,-1.000000,-1.000000,1.000000,-1.000000,1.000000,-1.000000,-1.000000,1.000000,-1.000000,-1.000000,-1.000000,1.000000,1.000000,-1.000000,0.999999,1.000000,1.000001,-1.000000,1.000000,1.000000,-1.000000,1.000000,-1.000000],
-
-			"morphTargets": [],
-
-			"normals": [],
-
-			"colors": [16777215,16769421,16769424,8454135,15195931,7299839,16586715,16711687,1056014,6029475,13762484,9044089,7962349,6772991,16774622,4144383,11973887,1966063,1056285,9081232,13696943,5002581],
-
-			"uvs": [[]],
-
-			"faces": [131,0,1,2,3,0,0,1,2,3,131,4,7,6,5,0,4,5,6,7,131,0,4,5,1,0,0,8,9,10,131,1,5,6,2,0,0,11,12,13,131,2,6,7,3,0,14,15,16,17,131,4,0,3,7,0,18,19,20,21]
-
-		}
-
-	},
-
-	"materials": {
-
-		"basic_gray": {
-			"type": "MeshBasicMaterial",
-			"parameters": { "color": 6710886, "wireframe": true }
-		},
-
-		"basic_white": {
-			"type": "MeshBasicMaterial",
-			"parameters": { "color": 16777215, "wireframe": true }
-		},
-
-		"faceted_white": {
-			"type": "MeshLambertMaterial",
-			"parameters": { "color": 16777215, "shading": "flat" }
-		},
-
-		"phong_red": {
-			"type": "MeshPhongMaterial",
-			"parameters": { "color": 16711680, "specular": 16711680, "shininess": 25, "bumpMap": "texture_bump", "bumpScale": -0.75 }
-		},
-
-		"phong_ben": {
-			"type": "MeshPhongMaterial",
-			"parameters": { "color": 1118481, "specular": 5601245, "shininess": 12, "bumpMap": "texture_bump_repeat", "bumpScale": 0.125 }
-		},
-
-		"phong_man": {
-			"type": "MeshPhongMaterial",
-			"parameters": { "color": 16737894, "specular": 2236962, "shininess": 40 }
-		},
-
-		"phong_hand": {
-			"type": "MeshPhongMaterial",
-			"parameters": { "color": 14531481, "specular": 2236962, "shininess": 40 }
-		},
-
-		"phong_bunny": {
-			"type": "MeshPhongMaterial",
-			"parameters": { "color": 16777215, "specular": 1118481, "shininess": 10 }
-		},
-
-		"phong_monster": {
-			"type": "MeshPhongMaterial",
-			"parameters": { "color": 16777215, "specular": 1118481, "shininess": 10 }
-		},
-
-		"phong_disk": {
-			"type": "MeshPhongMaterial",
-			"parameters": { "color": 16733491, "specular": 1118481, "shininess": 30, "wireframe": false }
-		},
-
-		"phong_morph": {
-			"type": "MeshPhongMaterial",
-			"parameters": { "color": 0, "specular": 16777215, "shininess": 50, "envMap": "cube_reflection", "reflectivity": 0.125, "combine": "MixOperation", "shading": "flat", "side": "double", "morphTargets": true }
-		},
-
-		"phong_skin": {
-			"type": "MeshPhongMaterial",
-			"parameters": { "color": 0, "specular": 16777215, "shininess": 50, "envMap": "cube_reflection", "reflectivity": 0.5, "combine": "MixOperation", "skinning": true, "morphTargets": true }
-		},
-
-		"lambert_green": {
-			"type": "MeshLambertMaterial",
-			"parameters": { "color": 30481, "blending": "AdditiveBlending", "transparent": true }
-		},
-
-		"lambert_blue": {
-			"type": "MeshLambertMaterial",
-			"parameters": { "color": 21930 }
-		},
-
-		"phong_orange": {
-			"type": "MeshPhongMaterial",
-			"parameters": { "color": 0, "specular": 11162880 }
-		},
-
-		"basic_refraction": {
-			"type": "MeshBasicMaterial",
-			"parameters": { "color": 16777215, "envMap": "cube_refraction", "refractionRatio": 0.95 }
-		},
-
-		"lambert_cube": {
-			"type": "MeshLambertMaterial",
-			"parameters": { "color": 16737792, "envMap": "cube_reflection", "combine": "MixOperation", "reflectivity": 0.3 }
-		},
-
-		"chrome": {
-			"type": "MeshLambertMaterial",
-			"parameters": { "color": 16777215, "envMap": "cube_reflection" }
-		},
-
-		"darkerchrome": {
-			"type": "MeshLambertMaterial",
-			"parameters": { "color": 2236962, "envMap": "cube_reflection" }
-		},
-
-		"glass": {
-			"type": "MeshLambertMaterial",
-			"parameters": { "color": 1052742, "envMap": "cube_reflection", "opacity": 0.25, "transparent": true }
-		},
-
-		"interior": {
-			"type": "MeshLambertMaterial",
-			"parameters": { "color": 328965 }
-		},
-
-		"backlights": {
-			"type": "MeshLambertMaterial",
-			"parameters": { "color": 16711680, "opacity": 0.5 }
-		},
-
-		"backsignals": {
-			"type": "MeshLambertMaterial",
-			"parameters": { "color": 16759552, "opacity": 0.5 }
-		},
-
-		"textured_bg": {
-			"type": "MeshBasicMaterial",
-			"parameters": { "color": 16777215, "map": "texture_bg" }
-		},
-
-		"textured_compressed_dxt3": {
-			"type": "MeshBasicMaterial",
-			"parameters": { "color": 16777215, "map": "texture_compressed_dxt3", "transparent": true }
-		},
-
-		"textured_compressed_dxt5": {
-			"type": "MeshBasicMaterial",
-			"parameters": { "color": 16777215, "map": "texture_compressed_dxt5", "transparent": true, "blending": "AdditiveBlending" }
-		},
-
-		"minecraft": {
-			"type": "MeshBasicMaterial",
-			"parameters": { "color": 16777215, "map": "texture_minecraft" }
-		},
-
-		"face": {
-			"type": "MultiMaterial",
-			"parameters": {}
-		},
-
-		"multi_1": {
-			"type": "MultiMaterial",
-			"parameters": {}
-		},
-
-		"multi_2": {
-			"type": "MultiMaterial",
-			"parameters": { "materials": [ "basic_refraction", "phong_man", "phong_hand", "minecraft", "backsignals" ] }
-		},
-
-		"multi_veyron": {
-			"type": "MultiMaterial",
-			"parameters": { "materials": [ "interior", "chrome", "darkerchrome", "glass", "chrome", "chrome", "backlights", "backsignals" ] }
-		}
-
-	},
-
-	"textures": {
-
-		"cube_reflection": {
-			"url": [
-				"textures/cube/SwedishRoyalCastle/px.jpg",
-				"textures/cube/SwedishRoyalCastle/nx.jpg",
-				"textures/cube/SwedishRoyalCastle/py.jpg",
-				"textures/cube/SwedishRoyalCastle/ny.jpg",
-				"textures/cube/SwedishRoyalCastle/pz.jpg",
-				"textures/cube/SwedishRoyalCastle/nz.jpg"
-			]
-		},
-
-		"cube_refraction": {
-			"url": [
-				"textures/cube/SwedishRoyalCastle/px.jpg",
-				"textures/cube/SwedishRoyalCastle/nx.jpg",
-				"textures/cube/SwedishRoyalCastle/py.jpg",
-				"textures/cube/SwedishRoyalCastle/ny.jpg",
-				"textures/cube/SwedishRoyalCastle/nz.jpg",
-				"textures/cube/SwedishRoyalCastle/pz.jpg"
-			],
-			"mapping": "CubeRefractionMapping"
-		},
-
-		"texture_bg": {
-			"url": "textures/cube/SwedishRoyalCastle/pz.jpg",
-			"anisotropy": 4
-		},
-
-		"texture_compressed_dxt3": {
-			"url": "textures/compressed/hepatica_dxt3_mip.dds",
-			"anisotropy": 4
-		},
-
-		"texture_compressed_dxt5": {
-			"url": "textures/compressed/explosion_dxt5_mip.dds",
-			"anisotropy": 4
-		},
-
-		"texture_bump": {
-			"url": "textures/water.jpg",
-			"anisotropy": 4
-		},
-
-		"texture_bump_repeat": {
-			"url": "textures/water.jpg",
-			"repeat" : [ 20, 20 ],
-			"anisotropy": 4
-		},
-
-		"texture_bump_repeat_2": {
-			"url": "textures/water.jpg",
-			"repeat" : [ 4, 2 ],
-			"anisotropy": 4
-		},
-
-		"texture_minecraft": {
-			"url": "textures/minecraft/grass.png",
-			"magFilter": "NearestFilter",
-			"minFilter": "LinearMipMapLinearFilter"
-		}
-
-	},
-
-	"fogs":	{
-		"basic": {
-			"type": "linear",
-			"color": [1,0,0],
-			"near": 1,
-			"far": 1000
-		},
-
-		"exponential": {
-			"type": "exp2",
-			"color": [1,1,1],
-			"density": 0.005
-		},
-
-		"black": {
-			"type": "exp2",
-			"color": [0,0,0],
-			"density": 0.005
-		}
-	},
-
-	"defaults": {
-		"bgcolor": [0,0,0],
-		"bgalpha": 1,
-		"camera": "camera1",
-		"fog": "black"
-	}
-
-}

+ 100 - 114
examples/webgl_geometry_extrude_splines.html

@@ -11,39 +11,42 @@
 				margin: 0px;
 				overflow: hidden;
 			}
+
+			#info {
+				position: absolute;
+				top: 0px;
+				width: 100%;
+				padding: 5px;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+			}
 		</style>
 	</head>
 	<body>
 
+		<div id="container"></div>
+		<div id="info"><a href="https://threejs.org" target="_blank">three.js</a> - geometry - spline extrusion examples by <a href="http://www.lab4games.net/zz85/blog" target="_blank">zz85</a></div>
+
 		<script src="../build/three.js"></script>
 		<script src="js/controls/OrbitControls.js"></script>
 
 		<!-- where curves formulas are defined -->
+
 		<script src="js/CurveExtras.js"></script>
 
 		<script src="js/libs/stats.min.js"></script>
-
+		<script src="js/libs/dat.gui.min.js"></script>
 
 		<script>
+
 		var container, stats;
 
 		var camera, scene, renderer, splineCamera, cameraHelper, cameraEye;
 
-		var text, plane;
-
-		var targetRotation = 0;
-		var targetRotationOnMouseDown = 0;
-
-		var mouseX = 0;
-		var mouseXOnMouseDown = 0;
-
-		var windowHalfX = window.innerWidth / 2;
-		var windowHalfY = window.innerHeight / 2;
-
 		var binormal = new THREE.Vector3();
 		var normal = new THREE.Vector3();
 
-
 		var pipeSpline = new THREE.CatmullRomCurve3( [
 				new THREE.Vector3( 0, 10, -10 ), new THREE.Vector3( 10, 0, -10 ),
 				new THREE.Vector3( 20, 0, 0 ), new THREE.Vector3( 30, 0, 10 ),
@@ -92,89 +95,65 @@
 			SampleClosedSpline: sampleClosedSpline
 		};
 
-		extrudePath = new THREE.Curves.TrefoilKnot();
-
-		var dropdown = '<select id="dropdown" onchange="addTube(this.value)">';
-
-		var s;
-		for ( s in splines ) {
-
-			dropdown += '<option value="' + s + '"';
-			dropdown += '>' + s + '</option>';
-
-		}
+		var parent, tubeGeometry, group;
+
+		var params = {
+			spline: 'GrannyKnot',
+			scale: 4,
+			extrusionSegments: 100,
+			radiusSegments: 3,
+			closed: true,
+			animationView: false,
+			lookAhead: false,
+			cameraHelper: false,
+		};
 
-		dropdown += '</select>';
+		var material = new THREE.MeshLambertMaterial( { color: 0xff00ff } );
 
-		var closed2 = true;
-		var parent;
-		var tube, tubeMesh;
-		var animation = false, lookAhead = false;
-		var scale;
-		var showCameraHelper = false;
+		var wireframeMaterial = new THREE.MeshBasicMaterial( { color: 0x000000, opacity: 0.3, wireframe: true, transparent: true } );
 
 		function addTube() {
 
-			var value = document.getElementById( 'dropdown' ).value;
+			if ( group !== undefined ) {
+
+				parent.remove( group );
 
-			var segments = parseInt( document.getElementById( 'segments' ).value );
-			closed2 = document.getElementById( 'closed' ).checked;
+				group.children[ 0 ].geometry.dispose();
+				group.children[ 1 ].geometry.dispose();
 
-			var radiusSegments = parseInt( document.getElementById( 'radiusSegments' ).value );
+			}
 
-			if ( tubeMesh !== undefined ) parent.remove( tubeMesh );
+			extrudePath = splines[ params.spline ];
 
-			extrudePath = splines[ value ];
+			tubeGeometry = new THREE.TubeBufferGeometry( extrudePath, params.extrusionSegments, 2, params.radiusSegments, params.closed );
 
-			tube = new THREE.TubeBufferGeometry( extrudePath, segments, 2, radiusSegments, closed2 );
+			addGeometry( tubeGeometry );
 
-			addGeometry( tube, 0xff00ff );
 			setScale();
 
 		}
 
 		function setScale() {
 
-			scale = parseInt( document.getElementById( 'scale' ).value );
-			tubeMesh.scale.set( scale, scale, scale );
+			group.scale.set( params.scale, params.scale, params.scale );
 
 		}
 
 
-		function addGeometry( geometry, color ) {
+		function addGeometry( geometry ) {
 
-			// 3d shape
+			// 3D shape
 
-			tubeMesh = THREE.SceneUtils.createMultiMaterialObject( geometry, [
-				new THREE.MeshLambertMaterial( {
-					color: color
-				} ),
-				new THREE.MeshBasicMaterial( {
-					color: 0x000000,
-					opacity: 0.3,
-					wireframe: true,
-					transparent: true
-			} ) ] );
+			group = THREE.SceneUtils.createMultiMaterialObject( geometry, [ material, wireframeMaterial ] );
 
-			parent.add( tubeMesh );
+			parent.add( group );
 
 		}
 
-		function animateCamera( toggle ) {
-
-			if ( toggle === true ) {
-
-				animation = animation === false;
-				document.getElementById( 'animation' ).value = 'Camera Spline Animation View: ' + ( animation ? 'ON' : 'OFF' );
-
-			}
-
-			lookAhead = document.getElementById( 'lookAhead' ).checked;
+		function animateCamera() {
 
-			showCameraHelper = document.getElementById( 'cameraHelper' ).checked;
-
-			cameraHelper.visible = showCameraHelper;
-			cameraEye.visible = showCameraHelper;
+			cameraHelper.visible = params.cameraHelper;
+			cameraEye.visible = params.cameraHelper;
 
 		}
 
@@ -183,38 +162,25 @@
 
 		function init() {
 
-			container = document.createElement( 'div' );
-			document.body.appendChild( container );
-
-			var info = document.createElement( 'div' );
-			info.style.position = 'absolute';
-			info.style.top = '10px';
-			info.style.width = '100%';
-			info.style.textAlign = 'center';
-			info.innerHTML = 'Spline Extrusion Examples by <a href="http://www.lab4games.net/zz85/blog">zz85</a><br/>Select spline:';
-
-			info.innerHTML += dropdown;
-
-			info.innerHTML += '<br/>Scale: <select id="scale" onchange="setScale()"><option>1</option><option>2</option><option selected>4</option><option>6</option><option>10</option></select>';
-			info.innerHTML += '<br/>Extrusion Segments: <select onchange="addTube()" id="segments"><option>50</option><option selected>100</option><option>200</option><option>400</option></select>';
-			info.innerHTML += '<br/>Radius Segments: <select id="radiusSegments" onchange="addTube()"><option>1</option><option>2</option><option selected>3</option><option>4</option><option>5</option><option>6</option><option>8</option><option>12</option></select>';
-			info.innerHTML += '<br/>Closed:<input id="closed" onchange="addTube()" type="checkbox" checked />';
-
-			info.innerHTML += '<br/><br/><input id="animation" type="button" onclick="animateCamera(true)" value="Camera Spline Animation View: OFF"/><br/> Look Ahead <input id="lookAhead" type="checkbox" onchange="animateCamera()" /> Camera Helper <input id="cameraHelper" type="checkbox" onchange="animateCamera()" />';
-
-			container.appendChild( info );
+			container = document.getElementById( 'container' );
 
-			//
+			// camera
 
 			camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.01, 10000 );
 			camera.position.set( 0, 50, 500 );
 
+			// scene
+
 			scene = new THREE.Scene();
 
+			// light
+
 			var light = new THREE.DirectionalLight( 0xffffff );
 			light.position.set( 0, 0, 1 );
 			scene.add( light );
 
+			// tube
+
 			parent = new THREE.Object3D();
 			scene.add( parent );
 
@@ -231,10 +197,10 @@
 			cameraEye = new THREE.Mesh( new THREE.SphereGeometry( 5 ), new THREE.MeshBasicMaterial( { color: 0xdddddd } ) );
 			parent.add( cameraEye );
 
-			cameraHelper.visible = showCameraHelper;
-			cameraEye.visible = showCameraHelper;
+			cameraHelper.visible = params.cameraHelper;
+			cameraEye.visible = params.cameraHelper;
 
-			//
+			// renderer
 
 			renderer = new THREE.WebGLRenderer( { antialias: true } );
 			renderer.setClearColor( 0xf0f0f0 );
@@ -242,14 +208,34 @@
 			renderer.setSize( window.innerWidth, window.innerHeight );
 			container.appendChild( renderer.domElement );
 
+			// stats
+
 			stats = new Stats();
 			container.appendChild( stats.dom );
 
-			//
+			// dat.GUI
+
+			var gui = new dat.GUI( { width: 300 } );
+
+			var folderGeometry = gui.addFolder( 'Geometry' );
+			folderGeometry.add( params, 'spline', Object.keys( splines ) ).onChange( function( value ) { addTube(); } );
+			folderGeometry.add( params, 'scale', 2, 10 ).step( 2 ).onChange( function( value ) { setScale(); } );
+			folderGeometry.add( params, 'extrusionSegments', 50, 500 ).step( 50 ).onChange( function( value ) { addTube(); } );
+			folderGeometry.add( params, 'radiusSegments', 2, 12 ).step( 1 ).onChange( function( value ) { addTube(); } );
+			folderGeometry.add( params, 'closed').onChange( function( value ) { addTube(); } );
+			folderGeometry.open();
+
+			var folderCamera = gui.addFolder( 'Camera' );
+			folderCamera.add( params, 'animationView').onChange( function( value ) { animateCamera( true ); } );
+			folderCamera.add( params, 'lookAhead').onChange( function( value ) { animateCamera(); } );
+			folderCamera.add( params, 'cameraHelper').onChange( function( value ) { animateCamera(); } );
+			folderCamera.open();
+
+			// controls
 
 			controls = new THREE.OrbitControls( camera, renderer.domElement );
 
-			//
+			// event listener
 
 			window.addEventListener( 'resize', onWindowResize, false );
 
@@ -257,9 +243,6 @@
 
 		function onWindowResize() {
 
-			windowHalfX = window.innerWidth / 2;
-			windowHalfY = window.innerHeight / 2;
-
 			camera.aspect = window.innerWidth / window.innerHeight;
 			camera.updateProjectionMatrix();
 
@@ -280,50 +263,53 @@
 
 		function render() {
 
-			// Try Animate Camera Along Spline
+			// animate camera along spline
+
 			var time = Date.now();
 			var looptime = 20 * 1000;
 			var t = ( time % looptime ) / looptime;
 
-			var pos = tube.parameters.path.getPointAt( t );
-			pos.multiplyScalar( scale );
+			var pos = tubeGeometry.parameters.path.getPointAt( t );
+			pos.multiplyScalar( params.scale );
 
 			// interpolation
-			var segments = tube.tangents.length;
+
+			var segments = tubeGeometry.tangents.length;
 			var pickt = t * segments;
 			var pick = Math.floor( pickt );
 			var pickNext = ( pick + 1 ) % segments;
 
-			binormal.subVectors( tube.binormals[ pickNext ], tube.binormals[ pick ] );
-			binormal.multiplyScalar( pickt - pick ).add( tube.binormals[ pick ] );
-
-
-			var dir = tube.parameters.path.getTangentAt( t );
+			binormal.subVectors( tubeGeometry.binormals[ pickNext ], tubeGeometry.binormals[ pick ] );
+			binormal.multiplyScalar( pickt - pick ).add( tubeGeometry.binormals[ pick ] );
 
+			var dir = tubeGeometry.parameters.path.getTangentAt( t );
 			var offset = 15;
 
 			normal.copy( binormal ).cross( dir );
 
-			// We move on a offset on its binormal
+			// we move on a offset on its binormal
+
 			pos.add( normal.clone().multiplyScalar( offset ) );
 
 			splineCamera.position.copy( pos );
 			cameraEye.position.copy( pos );
 
-			// Using arclength for stablization in look ahead.
-			var lookAt = tube.parameters.path.getPointAt( ( t + 30 / tube.parameters.path.getLength() ) % 1 ).multiplyScalar( scale );
+			// using arclength for stablization in look ahead
+
+			var lookAt = tubeGeometry.parameters.path.getPointAt( ( t + 30 / tubeGeometry.parameters.path.getLength() ) % 1 ).multiplyScalar( params.scale );
 
-			// Camera Orientation 2 - up orientation via normal
-			if ( !lookAhead ) lookAt.copy( pos ).add( dir );
-			splineCamera.matrix.lookAt(splineCamera.position, lookAt, normal);
+			// camera orientation 2 - up orientation via normal
+
+			if ( ! params.lookAhead ) lookAt.copy( pos ).add( dir );
+			splineCamera.matrix.lookAt( splineCamera.position, lookAt, normal );
 			splineCamera.rotation.setFromRotationMatrix( splineCamera.matrix, splineCamera.rotation.order );
 
 			cameraHelper.update();
 
-			renderer.render( scene, animation === true ? splineCamera : camera );
+			renderer.render( scene, params.animationView === true ? splineCamera : camera );
 
 		}
-	</script>
 
+	</script>
 	</body>
 </html>

+ 74 - 51
examples/webgl_gpu_particle_system.html

@@ -5,36 +5,46 @@
 	<meta charset="utf-8">
 	<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 	<style>
-		body {
-			background-color: #000000;
-			margin: 0px;
-			overflow: hidden;
-		}
-
-		a {
-			color: #0078ff;
-		}
-
-		.dg {
-			right: auto!important;
-			left: 20px!important;
-		}
+	body {
+		font-family: Monospace;
+		background-color: #f0f0f0;
+		margin: 0px;
+		overflow: hidden;
+	}
+
+	#info {
+		position: absolute;
+		top: 0px;
+		width: 100%;
+		padding: 5px;
+		font-family:Monospace;
+		font-size:13px;
+		text-align:center;
+		color: #ffffff;
+	}
+
+	a {
+		color: #ffffff;
+	}
 	</style>
 </head>
 
 <body>
-	<div style="position: absolute; top: 10px; width: 100%; text-align: center; color:#eee">
-		<a href="http://threejs.org" target="_blank">three.js</a> - GPU particle system plugin by <a href="http://charliehoey.com">Charlie Hoey</a>.</div>
+
+	<div id="container"></div>
+	<div id="info">
+		<a href="https://threejs.org" target="_blank">three.js</a> - GPU particle system plugin by <a href="http://charliehoey.com">Charlie Hoey</a>.
+	</div>
 
 	<script src="../build/three.js"></script>
 	<script src="./js/controls/TrackballControls.js"></script>
 	<script src="./js/libs/dat.gui.min.js"></script>
-	<script src="./js/GPUParticleSystem.js" charset="utf-8"></script>
+	<script src="./js/GPUParticleSystem.js"></script>
 
 	<script>
 		var camera, tick = 0,
-			scene, renderer, clock = new THREE.Clock(true),
-			controls, container, gui = new dat.GUI(),
+			scene, renderer, clock = new THREE.Clock(),
+			controls, container, gui = new dat.GUI( { width: 350 } ),
 			options, spawnerOptions, particleSystem;
 
 		init();
@@ -42,11 +52,11 @@
 
 		function init() {
 
+			//
 
-			container = document.createElement('div');
-			document.body.appendChild(container);
+			container = document.getElementById( 'container' );
 
-			camera = new THREE.PerspectiveCamera(28, window.innerWidth / window.innerHeight, 1, 10000);
+			camera = new THREE.PerspectiveCamera( 28, window.innerWidth / window.innerHeight, 1, 10000 );
 			camera.position.z = 100;
 
 			scene = new THREE.Scene();
@@ -55,13 +65,15 @@
 			// as you would any other scene graph component.	Particle positions will be
 			// relative to the position of the particle system, but you will probably only need one
 			// system for your whole scene
-			particleSystem = new THREE.GPUParticleSystem({
+
+			particleSystem = new THREE.GPUParticleSystem( {
 				maxParticles: 250000
-			});
-			scene.add( particleSystem);
+			} );
 
+			scene.add( particleSystem );
 
 			// options passed during each spawned
+
 			options = {
 				position: new THREE.Vector3(),
 				positionRandomness: .3,
@@ -82,30 +94,35 @@
 				timeScale: 1
 			};
 
-			gui.add(options, "velocityRandomness", 0, 3);
-			gui.add(options, "positionRandomness", 0, 3);
-			gui.add(options, "size", 1, 20);
-			gui.add(options, "sizeRandomness", 0, 25);
-			gui.add(options, "colorRandomness", 0, 1);
-			gui.add(options, "lifetime", .1, 10);
-			gui.add(options, "turbulence", 0, 1);
+			//
+
+			gui.add( options, "velocityRandomness", 0, 3 );
+			gui.add( options, "positionRandomness", 0, 3 );
+			gui.add( options, "size", 1, 20 );
+			gui.add( options, "sizeRandomness", 0, 25 );
+			gui.add( options, "colorRandomness", 0, 1 );
+			gui.add( options, "lifetime", .1, 10 );
+			gui.add( options, "turbulence", 0, 1 );
 
-			gui.add(spawnerOptions, "spawnRate", 10, 30000);
-			gui.add(spawnerOptions, "timeScale", -1, 1);
+			gui.add( spawnerOptions, "spawnRate", 10, 30000 );
+			gui.add( spawnerOptions, "timeScale", -1, 1 );
+
+			//
 
 			renderer = new THREE.WebGLRenderer();
-			renderer.setPixelRatio(window.devicePixelRatio);
-			renderer.setSize(window.innerWidth, window.innerHeight);
-			container.appendChild(renderer.domElement);
+			renderer.setPixelRatio( window.devicePixelRatio );
+			renderer.setSize( window.innerWidth, window.innerHeight );
+			container.appendChild( renderer.domElement );
+
+			//
 
-			// setup controls
-			controls = new THREE.TrackballControls(camera, renderer.domElement);
+			controls = new THREE.TrackballControls( camera, renderer.domElement );
 			controls.rotateSpeed = 5.0;
 			controls.zoomSpeed = 2.2;
 			controls.panSpeed = 1;
 			controls.dynamicDampingFactor = 0.3;
 
-			window.addEventListener('resize', onWindowResize, false);
+			window.addEventListener( 'resize', onWindowResize, false );
 
 		}
 
@@ -114,34 +131,39 @@
 			camera.aspect = window.innerWidth / window.innerHeight;
 			camera.updateProjectionMatrix();
 
-			renderer.setSize(window.innerWidth, window.innerHeight);
+			renderer.setSize( window.innerWidth, window.innerHeight );
 
 		}
 
 		function animate() {
 
-			requestAnimationFrame(animate);
+			requestAnimationFrame( animate );
 
 			controls.update();
 
 			var delta = clock.getDelta() * spawnerOptions.timeScale;
 			tick += delta;
 
-			if (tick < 0) tick = 0;
+			if ( tick < 0 ) tick = 0;
+
+			if ( delta > 0 ) {
+
+				options.position.x = Math.sin( tick * spawnerOptions.horizontalSpeed ) * 20;
+				options.position.y = Math.sin( tick * spawnerOptions.verticalSpeed ) * 10;
+				options.position.z = Math.sin( tick * spawnerOptions.horizontalSpeed + spawnerOptions.verticalSpeed ) * 5;
 
-			if (delta > 0) {
-				options.position.x = Math.sin(tick * spawnerOptions.horizontalSpeed) * 20;
-				options.position.y = Math.sin(tick * spawnerOptions.verticalSpeed) * 10;
-				options.position.z = Math.sin(tick * spawnerOptions.horizontalSpeed + spawnerOptions.verticalSpeed) * 5;
+				for ( var x = 0; x < spawnerOptions.spawnRate * delta; x++ ) {
 
-				for (var x = 0; x < spawnerOptions.spawnRate * delta; x++) {
 					// Yep, that's really it.	Spawning particles is super cheap, and once you spawn them, the rest of
 					// their lifecycle is handled entirely on the GPU, driven by a time uniform updated below
-					particleSystem.spawnParticle(options);
+
+					particleSystem.spawnParticle( options );
+
 				}
+
 			}
 
-			particleSystem.update(tick);
+			particleSystem.update( tick );
 
 			render();
 
@@ -149,9 +171,10 @@
 
 		function render() {
 
-			renderer.render(scene, camera);
+			renderer.render( scene, camera );
 
 		}
+
 	</script>
 </body>
 

+ 2 - 2
examples/webgl_lights_spotlight.html

@@ -54,8 +54,8 @@
 
 			var scene = new THREE.Scene();
 
-			var matFloor = new THREE.MeshPhongMaterial();
-			var matBox = new THREE.MeshPhongMaterial( { color: 0x4080ff } );
+			var matFloor = new THREE.MeshPhongMaterial( { dithering: true } );
+			var matBox = new THREE.MeshPhongMaterial( { color: 0x4080ff, dithering: true } );
 
 			var geoFloor = new THREE.BoxGeometry( 2000, 1, 2000 );
 			var geoBox = new THREE.BoxGeometry( 3, 1, 2 );

+ 47 - 93
examples/webgl_loader_collada.html

@@ -6,39 +6,41 @@
 		<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: #000000;
-				margin: 0px;
-				overflow: hidden;
+				background:#777;
+				padding:0;
+				margin:0;
+				font-weight: bold;
+				overflow:hidden;
 			}
 
 			#info {
-				color: #fff;
 				position: absolute;
-				top: 10px;
+				top: 0px;
 				width: 100%;
-				text-align: center;
-				z-index: 100;
-				display:block;
-
+				color: #ffffff;
+				padding: 5px;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
 			}
 
-			a { color: skyblue }
+			a {
+				color: #ffffff;
+			}
 		</style>
 	</head>
 	<body>
+
+		<div id="container"></div>
 		<div id="info">
-			<a href="http://threejs.org" target="_blank">three.js</a> -
+			<a href="https://threejs.org" target="_blank">three.js</a> -
 			monster by <a href="http://www.3drt.com/downloads.htm" target="_blank">3drt</a>
 		</div>
 
 		<script src="../build/three.js"></script>
-		<script src="js/loaders/collada/Animation.js"></script>
-		<script src="js/loaders/collada/AnimationHandler.js"></script>
-		<script src="js/loaders/collada/KeyFrameAnimation.js"></script>
 
 		<script src="js/loaders/ColladaLoader.js"></script>
-
+		<script src="js/controls/OrbitControls.js"></script>
 		<script src="js/Detector.js"></script>
 		<script src="js/libs/stats.min.js"></script>
 
@@ -47,92 +49,62 @@
 			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
 
 			var container, stats;
+			var camera, scene, renderer, controls;
 
-			var camera, scene, renderer, objects;
-			var particleLight;
-			var dae;
-
-			var loader = new THREE.ColladaLoader();
-			loader.options.convertUpAxis = true;
-			loader.load( './models/collada/monster/monster.dae', function ( collada ) {
-
-				dae = collada.scene;
-
-				dae.traverse( function ( child ) {
-
-					if ( child instanceof THREE.SkinnedMesh ) {
-
-						var animation = new THREE.Animation( child, child.geometry.animation );
-						animation.play();
-
-					}
-
-				} );
-
-				dae.scale.x = dae.scale.y = dae.scale.z = 0.002;
-				dae.updateMatrix();
-
-				init();
-				animate();
-
-			} );
+			init();
+			animate();
 
 			function init() {
 
-				container = document.createElement( 'div' );
-				document.body.appendChild( container );
+				container = document.getElementById( 'container' );
 
 				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
-				camera.position.set( 2, 2, 3 );
+				camera.position.set( 7, 5, 7 );
 
 				scene = new THREE.Scene();
 
-				// Grid
-
-				var size = 14, step = 1;
-
-				var geometry = new THREE.Geometry();
-				var material = new THREE.LineBasicMaterial( { color: 0x303030 } );
+				// collada
 
-				for ( var i = - size; i <= size; i += step ) {
+				var loader = new THREE.ColladaLoader();
+				loader.options.convertUpAxis = true;
+				loader.load( './models/collada/monster/monster.dae', function ( collada ) {
 
-					geometry.vertices.push( new THREE.Vector3( - size, - 0.04, i ) );
-					geometry.vertices.push( new THREE.Vector3(   size, - 0.04, i ) );
+					var object = collada.scene;
 
-					geometry.vertices.push( new THREE.Vector3( i, - 0.04, - size ) );
-					geometry.vertices.push( new THREE.Vector3( i, - 0.04,   size ) );
+					object.scale.set( 0.0025, 0.0025, 0.0025 );
+					object.position.set( - 2, 0.2, 0 );
 
-				}
+					scene.add( object );
 
-				var line = new THREE.LineSegments( geometry, material );
-				scene.add( line );
-
-				// Add the COLLADA
+				} );
 
-				scene.add( dae );
+				//
 
-				particleLight = new THREE.Mesh( new THREE.SphereGeometry( 4, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) );
-				scene.add( particleLight );
+				var gridHelper = new THREE.GridHelper( 10, 20 );
+				scene.add( gridHelper );
 
-				// Lights
+				//
 
-				scene.add( new THREE.AmbientLight( 0xcccccc ) );
+				var ambientLight = new THREE.AmbientLight( 0xcccccc );
+				scene.add( ambientLight );
 
-				var directionalLight = new THREE.DirectionalLight(/*Math.random() * 0xffffff*/0xeeeeee );
-				directionalLight.position.x = Math.random() - 0.5;
-				directionalLight.position.y = Math.random() - 0.5;
-				directionalLight.position.z = Math.random() - 0.5;
-				directionalLight.position.normalize();
+				var directionalLight = new THREE.DirectionalLight( 0xffffff );
+				directionalLight.position.set( 0, 1, -1 ).normalize();
 				scene.add( directionalLight );
 
-				var pointLight = new THREE.PointLight( 0xffffff, 4 );
-				particleLight.add( pointLight );
+				//
 
 				renderer = new THREE.WebGLRenderer();
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				container.appendChild( renderer.domElement );
 
+				//
+
+				controls = new THREE.OrbitControls( camera, renderer.domElement );
+
+				//
+
 				stats = new Stats();
 				container.appendChild( stats.dom );
 
@@ -151,8 +123,6 @@
 
 			}
 
-			//
-
 			function animate() {
 
 				requestAnimationFrame( animate );
@@ -162,24 +132,8 @@
 
 			}
 
-			var clock = new THREE.Clock();
-
 			function render() {
 
-				var timer = Date.now() * 0.0005;
-
-				camera.position.x = Math.cos( timer ) * 10;
-				camera.position.y = 2;
-				camera.position.z = Math.sin( timer ) * 10;
-
-				camera.lookAt( scene.position );
-
-				particleLight.position.x = Math.sin( timer * 4 ) * 3009;
-				particleLight.position.y = Math.cos( timer * 5 ) * 4000;
-				particleLight.position.z = Math.cos( timer * 4 ) * 3009;
-
-				THREE.AnimationHandler.update( clock.getDelta() );
-
 				renderer.render( scene, camera );
 
 			}

+ 78 - 49
examples/webgl_loader_collada_skinning.html

@@ -6,42 +6,39 @@
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<style>
 			body {
-				color: #000;
-				font-family:Monospace;
-				font-size:13px;
-				text-align:center;
-
-				background-color: #000;
-				margin: 0px;
-				overflow: hidden;
+				background:#777;
+				padding:0;
+				margin:0;
+				font-weight: bold;
+				overflow:hidden;
 			}
 
 			#info {
 				position: absolute;
-				top: 0px; width: 100%;
+				top: 0px;
+				width: 100%;
+				color: #ffffff;
 				padding: 5px;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
 			}
 
 			a {
-
-				color: #f00;
+				color: #ffffff;
 			}
-
 		</style>
 	</head>
 	<body>
 
 		<div id="container"></div>
-
 		<div id="info">
-		<a href="http://threejs.org" target="_blank">three.js</a> webgl - collada - skinning
+			<a href="https://threejs.org" target="_blank">three.js</a> webgl - collada - skinning
 		</div>
 
 		<script src="../build/three.js"></script>
-		<script src="js/loaders/collada/Animation.js"></script>
-		<script src="js/loaders/collada/AnimationHandler.js"></script>
-		<script src="js/loaders/collada/KeyFrameAnimation.js"></script>
 		<script src="js/loaders/ColladaLoader.js"></script>
+		<script src="js/controls/OrbitControls.js"></script>
 		<script src="js/Detector.js"></script>
 		<script src="js/libs/stats.min.js"></script>
 
@@ -49,60 +46,82 @@
 
 			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
 
-			var container, stats;
-			var camera, scene, renderer;
-			var clock = new THREE.Clock();
+			var container, stats, clock;
+			var camera, scene, renderer, mixer;
 
 			init();
+			animate();
 
 			function init() {
 
 				container = document.getElementById( 'container' );
 
 				camera = new THREE.PerspectiveCamera( 25, window.innerWidth / window.innerHeight, 1, 10000 );
-				camera.position.set( -5, -5, 5 );
-				camera.up.set( 0, 0, 1 );
+				camera.position.set( - 7, 4, 7 );
 
 				scene = new THREE.Scene();
 
-				var light = new THREE.DirectionalLight( 0xffffff, 1.5 );
-				light.position.set( 0, -4, -4 ).normalize();
-				scene.add( light );
+				clock = new THREE.Clock();
 
-				renderer = new THREE.WebGLRenderer( { antialias: true } );
-				renderer.setClearColor( 0xfff4e5 );
-				renderer.setPixelRatio( window.devicePixelRatio );
-				renderer.setSize( window.innerWidth, window.innerHeight );
-				renderer.sortObjects = false;
-				container.appendChild( renderer.domElement );
-
-				stats = new Stats();
-				container.appendChild( stats.dom );
-				
+				// collada
 
 				var loader = new THREE.ColladaLoader();
+				loader.options.convertUpAxis = true;
 				loader.load( "./models/collada/avatar.dae", function ( collada ) {
-				
-					collada.scene.traverse( function ( child ) {
 
-						if ( child instanceof THREE.SkinnedMesh ) {
+					var object = collada.scene;
+
+					mixer = new THREE.AnimationMixer( object );
 
-							var animation = new THREE.Animation( child, child.geometry.animation );
-							animation.play();
+					object.traverse( function ( child ) {
 
-							camera.lookAt( child.position );
+						if ( child instanceof THREE.SkinnedMesh ) {
+
+							var clip = THREE.AnimationClip.parseAnimation( child.geometry.animation, child.geometry.bones );
+							mixer.clipAction( clip, child ).play();
 
 						}
 
 					} );
 
-					scene.add( collada.scene );
+					scene.add( object );
 
 				} );
 
-				window.addEventListener( 'resize', onWindowResize, false );
+				//
+
+				var gridHelper = new THREE.GridHelper( 5, 20 );
+				scene.add( gridHelper );
+
+				//
+
+				var ambientLight = new THREE.AmbientLight( 0xcccccc );
+				scene.add( ambientLight );
+
+				var directionalLight = new THREE.DirectionalLight( 0xffffff );
+				directionalLight.position.set( -1, 0.5, -1 ).normalize();
+				scene.add( directionalLight );
 
-				animate();
+				//
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.sortObjects = false;
+				container.appendChild( renderer.domElement );
+
+				//
+
+				controls = new THREE.OrbitControls( camera, renderer.domElement );
+
+				//
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
 
 			}
 
@@ -117,13 +136,24 @@
 
 			function animate() {
 
-				requestAnimationFrame( animate, renderer.domElement );
+				requestAnimationFrame( animate );
 
-				THREE.AnimationHandler.update( clock.getDelta() );
+				render();
+				stats.update();
 
-				renderer.render( scene, camera );
+			}
 
-				stats.update();
+			function render() {
+
+				var delta = clock.getDelta();
+
+				if ( mixer !== undefined ) {
+
+					mixer.update( delta );
+
+				}
+
+				renderer.render( scene, camera );
 
 			}
 
@@ -131,4 +161,3 @@
 
 	</body>
 </html>
-

+ 45 - 1
examples/webgl_loader_gltf2.html

@@ -254,6 +254,25 @@
 					if (sceneInfo.objectScale)
 						object.scale.copy(sceneInfo.objectScale);
 
+					if ( sceneInfo.addEnvMap ) {
+
+						var envMap = getEnvMap();
+
+						object.traverse( function( node ) {
+
+							if ( node.material && node.material.isMeshStandardMaterial ) {
+
+								node.material.envMap = envMap;
+								node.material.needsUpdate = true;
+
+							}
+
+						} );
+
+						scene.background = envMap;
+
+					}
+
 					cameraIndex = 0;
 					cameras = [];
 					cameraNames = [];
@@ -368,13 +387,38 @@
 
 			}
 
+			var envMap;
+
+			function getEnvMap() {
+
+				if ( envMap ) {
+
+					return envMap;
+
+				}
+
+				var path = 'textures/cube/Park2/';
+				var format = '.jpg';
+				var urls = [
+					path + 'posx' + format, path + 'negx' + format,
+					path + 'posy' + format, path + 'negy' + format,
+					path + 'posz' + format, path + 'negz' + format
+				];
+
+				envMap = new THREE.CubeTextureLoader().load( urls );
+				envMap.format = THREE.RGBFormat;
+				return envMap;
+
+			}
+
 			var sceneList = [
 				{
 					name : "BoomBox (PBR)", url : "./models/gltf/BoomBox/%s/BoomBox.gltf",
 					cameraPos: new THREE.Vector3(2, 1, 3),
 					objectRotation: new THREE.Euler(0, Math.PI, 0),
 					addLights:true,
-					extensions: ["glTF", "glTF-pbrSpecularGlossiness", "glTF-Binary"]
+					extensions: ["glTF", "glTF-pbrSpecularGlossiness", "glTF-Binary"],
+					addEnvMap: true
 				}
 			];
 

+ 37 - 84
examples/webgl_loader_json_blender.html

@@ -1,44 +1,43 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<title>three.js webgl - collada - blender</title>
+		<title>three.js webgl - blender -json</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: #000000;
-				margin: 0px;
-				overflow: hidden;
+				background:#777;
+				padding:0;
+				margin:0;
+				font-weight: bold;
+				overflow:hidden;
 			}
 
 			#info {
-				color: #fff;
 				position: absolute;
-				top: 10px;
+				top: 0px;
 				width: 100%;
-				text-align: center;
-				z-index: 100;
-				display:block;
-
+				color: #ffffff;
+				padding: 5px;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
 			}
 
-			a { color: red }
+			a {
+				color: #ffffff;
+			}
 		</style>
 	</head>
-
 	<body>
+
+		<div id="container"></div>
 		<div id="info">
-			<a href="http://threejs.org" target="_blank">three.js</a> -
+			<a href="https://threejs.org" target="_blank">three.js</a> -
 			monster by <a href="http://www.3drt.com/downloads.htm" target="_blank">3drt</a>
 		</div>
 
 		<script src="../build/three.js"></script>
-		<script src="js/loaders/collada/Animation.js"></script>
-		<script src="js/loaders/collada/AnimationHandler.js"></script>
-		<script src="js/loaders/collada/KeyFrameAnimation.js"></script>
-
-		<script src="js/loaders/ColladaLoader.js"></script>
 
 		<script src="js/Detector.js"></script>
 		<script src="js/libs/stats.min.js"></script>
@@ -47,56 +46,24 @@
 
 			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
 
-			var container, stats;
-
+			var container, stats, clock, mixer;
 			var camera, scene, renderer, objects;
-			var particleLight, pointLight;
-			var dae;
-
-			var clock = new THREE.Clock();
-			var mixer;
-
-			// Collada model
-
-			var loader = new THREE.ColladaLoader();
-			loader.options.convertUpAxis = true;
-			loader.load( 'models/collada/monster/monster.dae', function ( collada ) {
-
-				dae = collada.scene;
-
-				dae.traverse( function ( child ) {
-
-					if ( child instanceof THREE.SkinnedMesh ) {
-
-						var animation = new THREE.Animation( child, child.geometry.animation );
-						animation.play();
-
-					}
-
-				} );
-
-				dae.scale.x = dae.scale.y = dae.scale.z = 0.002;
-				dae.position.x = -1;
-				dae.updateMatrix();
-
-				init();
-				animate();
 
-			} );
+			init();
+			animate();
 
 			function init() {
 
-				container = document.createElement( 'div' );
-				document.body.appendChild( container );
+				container = document.getElementById( 'container' );
 
 				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 2000 );
 				camera.position.set( 2, 4, 5 );
 
+				clock = new THREE.Clock();
+
 				scene = new THREE.Scene();
 				scene.fog = new THREE.FogExp2( 0x000000, 0.035 );
 
-				// Add Blender exported Collada model
-
 				mixer = new THREE.AnimationMixer( scene );
 
 				var loader = new THREE.JSONLoader();
@@ -110,21 +77,18 @@
 
 					for ( var i = 0; i < 729; i ++ ) {
 
+						mesh = new THREE.Mesh( geometry, materials );
+
 						// random placement in a grid
 
 						var x = ( ( i % 27 )  - 13.5 ) * 2 + THREE.Math.randFloatSpread( 1 );
 						var z = ( Math.floor( i / 27 ) - 13.5 ) * 2 + THREE.Math.randFloatSpread( 1 );
 
-						// leave space for big monster
-
-						if ( Math.abs( x ) < 2 && Math.abs( z ) < 2 ) continue;
-
-						mesh = new THREE.Mesh( geometry, materials );
+						mesh.position.set( x, 0, z );
 
 						var s = THREE.Math.randFloat( 0.00075, 0.001 );
 						mesh.scale.set( s, s, s );
 
-						mesh.position.set( x, 0, z );
 						mesh.rotation.y = THREE.Math.randFloat( -0.25, 0.25 );
 
 						mesh.matrixAutoUpdate = false;
@@ -132,7 +96,7 @@
 
 						scene.add( mesh );
 
-						mixer.clipAction( geometry.animations[0], mesh )
+						mixer.clipAction( geometry.animations[ 0 ], mesh )
 								.setDuration( 1 )			// one second
 								.startAt( - Math.random() )	// random phase (already running)
 								.play();					// let's go
@@ -141,32 +105,28 @@
 
 				} );
 
+				// lights
 
-				// Add the COLLADA
+				var ambientLight = new THREE.AmbientLight( 0xcccccc );
+				scene.add( ambientLight );
 
-				scene.add( dae );
-
-				// Lights
-
-				scene.add( new THREE.AmbientLight( 0xcccccc ) );
-
-				pointLight = new THREE.PointLight( 0xff4400, 5, 30 );
+				var pointLight = new THREE.PointLight( 0xff4400, 5, 30 );
 				pointLight.position.set( 5, 0, 0 );
 				scene.add( pointLight );
 
-				// Renderer
+				// renderer
 
 				renderer = new THREE.WebGLRenderer();
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				container.appendChild( renderer.domElement );
 
-				// Stats
+				// stats
 
 				stats = new Stats();
 				container.appendChild( stats.dom );
 
-				// Events
+				// events
 
 				window.addEventListener( 'resize', onWindowResize, false );
 
@@ -189,15 +149,6 @@
 
 				requestAnimationFrame( animate );
 
-				var delta = clock.getDelta();
-
-				// animate Collada model
-
-				THREE.AnimationHandler.update( delta );
-
-				mixer.update( delta );
-
-
 				render();
 				stats.update();
 
@@ -211,6 +162,8 @@
 				camera.position.y = 4;
 				camera.position.z = Math.sin( timer ) * 10;
 
+				mixer.update( clock.getDelta() );
+
 				camera.lookAt( scene.position );
 
 				renderer.render( scene, camera );

+ 343 - 0
examples/webgl_loader_obj2.html

@@ -0,0 +1,343 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - OBJLoader2</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: 0 0 0 0;
+				padding: 0 0 0 0;
+				border: none;
+				cursor: default;
+			}
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				z-index: 100;
+				display:block;
+			}
+			#info a {
+				color: #f00;
+				font-weight: bold;
+				text-decoration: underline;
+				cursor: pointer
+			}
+			#glFullscreen {
+				width: 100%;
+				height: 100vh;
+				min-width: 640px;
+				min-height: 360px;
+				position: relative;
+				overflow: hidden;
+				z-index: 0;
+			}
+			#example {
+				width: 100%;
+				height: 100%;
+				top: 0;
+				left: 0;
+				background-color: #000000;
+			}
+			#feedback {
+				color: darkorange;
+			}
+			#dat {
+				user-select: none;
+				position: absolute;
+				left: 0;
+				top: 0;
+				z-Index: 200;
+			}
+		</style>
+
+	</head>
+
+	<body>
+		<div id="glFullscreen">
+			<canvas id="example"></canvas>
+		</div>
+		<div id="dat">
+
+		</div>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank">three.js</a> - OBJLoader2 direct loader test
+			<div id="feedback"></div>
+		</div>
+
+		<script src="js/Detector.js"></script>
+		<script src="../build/three.js"></script>
+		<script src="js/controls/TrackballControls.js"></script>
+		<script src="js/loaders/MTLLoader.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<script src="js/loaders/OBJLoader2.js"></script>
+		<script>
+
+			'use strict';
+
+			var OBJLoader2Example = (function () {
+
+				function OBJLoader2Example( elementToBindTo ) {
+					this.renderer = null;
+					this.canvas = elementToBindTo;
+					this.aspectRatio = 1;
+					this.recalcAspectRatio();
+
+					this.scene = null;
+					this.cameraDefaults = {
+						posCamera: new THREE.Vector3( 0.0, 175.0, 500.0 ),
+						posCameraTarget: new THREE.Vector3( 0, 0, 0 ),
+						near: 0.1,
+						far: 10000,
+						fov: 45
+					};
+					this.camera = null;
+					this.cameraTarget = this.cameraDefaults.posCameraTarget;
+
+					this.controls = null;
+
+					this.smoothShading = true;
+					this.doubleSide = false;
+
+					this.cube = null;
+					this.pivot = null;
+				}
+
+				OBJLoader2Example.prototype.initGL = function () {
+					this.renderer = new THREE.WebGLRenderer( {
+						canvas: this.canvas,
+						antialias: true,
+						autoClear: true
+					} );
+					this.renderer.setClearColor( 0x050505 );
+
+					this.scene = new THREE.Scene();
+
+					this.camera = new THREE.PerspectiveCamera( this.cameraDefaults.fov, this.aspectRatio, this.cameraDefaults.near, this.cameraDefaults.far );
+					this.resetCamera();
+					this.controls = new THREE.TrackballControls( this.camera, this.renderer.domElement );
+
+					var ambientLight = new THREE.AmbientLight( 0x404040 );
+					var directionalLight1 = new THREE.DirectionalLight( 0xC0C090 );
+					var directionalLight2 = new THREE.DirectionalLight( 0xC0C090 );
+
+					directionalLight1.position.set( -100, -50, 100 );
+					directionalLight2.position.set( 100, 50, -100 );
+
+					this.scene.add( directionalLight1 );
+					this.scene.add( directionalLight2 );
+					this.scene.add( ambientLight );
+
+					var helper = new THREE.GridHelper( 1200, 60, 0xFF4444, 0x404040 );
+					this.scene.add( helper );
+
+					var geometry = new THREE.BoxGeometry( 10, 10, 10 );
+					var material = new THREE.MeshNormalMaterial();
+					this.cube = new THREE.Mesh( geometry, material );
+					this.cube.position.set( 0, 0, 0 );
+					this.scene.add( this.cube );
+
+					this.pivot = new THREE.Object3D();
+					this.pivot.name = 'Pivot';
+					this.scene.add( this.pivot );
+				};
+
+				OBJLoader2Example.prototype.initPostGL = function ( objDef ) {
+					var scope = this;
+
+					var mtlLoader = new THREE.MTLLoader();
+					mtlLoader.setPath( objDef.texturePath );
+					mtlLoader.setCrossOrigin( 'anonymous' );
+					mtlLoader.load( objDef.fileMtl, function( materials ) {
+
+						materials.preload();
+
+						var objLoader = new THREE.OBJLoader2();
+						objLoader.setSceneGraphBaseNode( scope.pivot );
+						objLoader.setMaterials( materials.materials );
+						objLoader.setPath( objDef.path );
+						objLoader.setDebug( false, false );
+
+						var onSuccess = function ( object3d ) {
+							console.log( 'Loading complete. Meshes were attached to: ' + object3d.name );
+						};
+
+						var onProgress = function ( event ) {
+							if ( event.lengthComputable ) {
+
+								var percentComplete = event.loaded / event.total * 100;
+								var output = 'Download of "' + objDef.fileObj + '": ' + Math.round( percentComplete ) + '%';
+								console.log(output);
+
+							}
+						};
+
+						var onError = function ( event ) {
+							console.error( 'Error of type "' + event.type + '" occurred when trying to load: ' + event.src );
+						};
+
+						objLoader.load( objDef.fileObj, onSuccess, onProgress, onError );
+
+					});
+
+					return true;
+				};
+
+				OBJLoader2Example.prototype.resizeDisplayGL = function () {
+					this.controls.handleResize();
+
+					this.recalcAspectRatio();
+					this.renderer.setSize( this.canvas.offsetWidth, this.canvas.offsetHeight, false );
+
+					this.updateCamera();
+				};
+
+				OBJLoader2Example.prototype.recalcAspectRatio = function () {
+					this.aspectRatio = ( this.canvas.offsetHeight === 0 ) ? 1 : this.canvas.offsetWidth / this.canvas.offsetHeight;
+				};
+
+				OBJLoader2Example.prototype.resetCamera = function () {
+					this.camera.position.copy( this.cameraDefaults.posCamera );
+					this.cameraTarget.copy( this.cameraDefaults.posCameraTarget );
+
+					this.updateCamera();
+				};
+
+				OBJLoader2Example.prototype.updateCamera = function () {
+					this.camera.aspect = this.aspectRatio;
+					this.camera.lookAt( this.cameraTarget );
+					this.camera.updateProjectionMatrix();
+				};
+
+				OBJLoader2Example.prototype.render = function () {
+					if ( ! this.renderer.autoClear ) this.renderer.clear();
+
+					this.controls.update();
+
+					this.cube.rotation.x += 0.05;
+					this.cube.rotation.y += 0.05;
+
+					this.renderer.render( this.scene, this.camera );
+				};
+
+				OBJLoader2Example.prototype.alterSmoothShading = function () {
+
+					var scope = this;
+					scope.smoothShading = ! scope.smoothShading;
+					console.log( scope.smoothShading ? 'Enabling SmoothShading' : 'Enabling FlatShading');
+
+					scope.traversalFunction = function ( material ) {
+						material.shading = scope.smoothShading ? THREE.SmoothShading : THREE.FlatShading;
+						material.needsUpdate = true;
+					};
+					var scopeTraverse = function ( object3d ) {
+						scope.traverseScene( object3d );
+					};
+					scope.pivot.traverse( scopeTraverse );
+				};
+
+				OBJLoader2Example.prototype.alterDouble = function () {
+
+					var scope = this;
+					scope.doubleSide = ! scope.doubleSide;
+					console.log( scope.doubleSide ? 'Enabling DoubleSide materials' : 'Enabling FrontSide materials');
+
+					scope.traversalFunction  = function ( material ) {
+						material.side = scope.doubleSide ? THREE.DoubleSide : THREE.FrontSide;
+					};
+
+					var scopeTraverse = function ( object3d ) {
+						scope.traverseScene( object3d );
+					};
+					scope.pivot.traverse( scopeTraverse );
+				};
+
+				OBJLoader2Example.prototype.traverseScene = function ( object3d ) {
+
+					if ( object3d.material instanceof THREE.MultiMaterial ) {
+
+						for ( var matName in object3d.material.materials ) {
+
+							this.traversalFunction( object3d.material.materials[ matName ] );
+
+						}
+
+					} else if ( object3d.material ) {
+
+						this.traversalFunction( object3d.material );
+
+					}
+
+				};
+
+				return OBJLoader2Example;
+
+			})();
+
+			var app = new OBJLoader2Example( document.getElementById( 'example' ) );
+
+			// Init dat.gui and controls
+			var OBJLoader2Control = function() {
+				this.smoothShading = app.smoothShading;
+				this.doubleSide = app.doubleSide;
+			};
+			var objLoader2Control = new OBJLoader2Control();
+
+			var gui = new dat.GUI( {
+				autoPlace: false,
+				width: 320
+			} );
+
+			var menuDiv = document.getElementById( 'dat' );
+			menuDiv.appendChild(gui.domElement);
+			var folderQueue = gui.addFolder( 'OBJLoader2 Options' );
+			var controlSmooth = folderQueue.add( objLoader2Control, 'smoothShading' ).name( 'Smooth Shading' );
+			controlSmooth.onChange( function( value ) {
+				console.log( 'Setting smoothShading to: ' + value );
+				app.alterSmoothShading();
+			});
+
+			var controlDouble = folderQueue.add( objLoader2Control, 'doubleSide' ).name( 'Double Side Materials' );
+			controlDouble.onChange( function( value ) {
+				console.log( 'Setting doubleSide to: ' + value );
+				app.alterDouble();
+			});
+			folderQueue.open();
+
+
+
+			// init three.js example application
+			var resizeWindow = function () {
+				app.resizeDisplayGL();
+			};
+
+			var render = function () {
+				requestAnimationFrame( render );
+				app.render();
+			};
+
+			window.addEventListener( 'resize', resizeWindow, false );
+
+			console.log( 'Starting initialisation phase...' );
+			app.initGL();
+			app.resizeDisplayGL();
+			app.initPostGL( {
+				path: 'obj/female02/',
+				fileObj: 'female02.obj',
+				texturePath: 'obj/female02/',
+				fileMtl: 'female02.mtl'
+			} );
+
+			render();
+
+		</script>
+	</body>
+</html>

+ 491 - 0
examples/webgl_loader_obj2_ww.html

@@ -0,0 +1,491 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - WWOBJLoader2</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: 0 0 0 0;
+				padding: 0 0 0 0;
+				border: none;
+				cursor: default;
+			}
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				z-index: 100;
+				display:block;
+			}
+			#info a {
+				color: #f00;
+				font-weight: bold;
+				text-decoration: underline;
+				cursor: pointer
+			}
+			#glFullscreen {
+				width: 100%;
+				height: 100vh;
+				min-width: 640px;
+				min-height: 360px;
+				position: relative;
+				overflow: hidden;
+				z-index: 0;
+			}
+			#example {
+				width: 100%;
+				height: 100%;
+				top: 0;
+				left: 0;
+				background-color: #000000;
+			}
+			#feedback {
+				color: darkorange;
+			}
+			#dat {
+				user-select: none;
+				position: absolute;
+				left: 0;
+				top: 0;
+				z-Index: 200;
+			}
+			#fileUploadInput {
+				display: none;
+			}
+		</style>
+
+	</head>
+
+	<body>
+		<div id="glFullscreen">
+			<canvas id="example"></canvas>
+		</div>
+		<div id="dat">
+
+		</div>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank">three.js</a> - OBJLoader2 direct loader test
+			<div id="feedback"></div>
+		</div>
+		<input id="fileUploadInput" type="file" name="files[]" multiple accept=".obj,.mtl" />
+
+		<script src="js/Detector.js"></script>
+		<script src="../build/three.js"></script>
+		<script src="js/controls/TrackballControls.js"></script>
+		<script src="js/loaders/MTLLoader.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<script src="js/loaders/OBJLoader2.js"></script>
+		<script src="js/loaders/WWOBJLoader2.js"></script>
+		<script>
+
+			'use strict';
+
+			var WWOBJLoader2Example = (function () {
+
+				function WWOBJLoader2Example( elementToBindTo ) {
+					this.renderer = null;
+					this.canvas = elementToBindTo;
+					this.aspectRatio = 1;
+					this.recalcAspectRatio();
+
+					this.scene = null;
+					this.cameraDefaults = {
+						posCamera: new THREE.Vector3( 0.0, 175.0, 500.0 ),
+						posCameraTarget: new THREE.Vector3( 0, 0, 0 ),
+						near: 0.1,
+						far: 10000,
+						fov: 45
+					};
+					this.camera = null;
+					this.cameraTarget = this.cameraDefaults.posCameraTarget;
+
+					this.controls = null;
+
+					this.smoothShading = true;
+					this.doubleSide = false;
+					this.streamMeshes = true;
+
+					this.cube = null;
+					this.pivot = null;
+
+					this.wwObjLoader2 = new THREE.OBJLoader2.WWOBJLoader2();
+					this.wwObjLoader2.setCrossOrigin( 'anonymous' );
+
+					// Check for the various File API support.
+					this.fileApiAvailable = true;
+					if ( window.File && window.FileReader && window.FileList && window.Blob ) {
+
+						console.log( 'File API is supported! Enabling all features.' );
+
+					} else {
+
+						this.fileApiAvailable = false;
+						console.warn( 'File API is not supported! Disabling file loading.' );
+
+					}
+				}
+
+				WWOBJLoader2Example.prototype.initGL = function () {
+					this.renderer = new THREE.WebGLRenderer( {
+						canvas: this.canvas,
+						antialias: true,
+						autoClear: true
+					} );
+					this.renderer.setClearColor( 0x050505 );
+
+					this.scene = new THREE.Scene();
+
+					this.camera = new THREE.PerspectiveCamera( this.cameraDefaults.fov, this.aspectRatio, this.cameraDefaults.near, this.cameraDefaults.far );
+					this.resetCamera();
+					this.controls = new THREE.TrackballControls( this.camera, this.renderer.domElement );
+
+					var ambientLight = new THREE.AmbientLight( 0x404040 );
+					var directionalLight1 = new THREE.DirectionalLight( 0xC0C090 );
+					var directionalLight2 = new THREE.DirectionalLight( 0xC0C090 );
+
+					directionalLight1.position.set( -100, -50, 100 );
+					directionalLight2.position.set( 100, 50, -100 );
+
+					this.scene.add( directionalLight1 );
+					this.scene.add( directionalLight2 );
+					this.scene.add( ambientLight );
+
+					var helper = new THREE.GridHelper( 1200, 60, 0xFF4444, 0x404040 );
+					this.scene.add( helper );
+
+					var geometry = new THREE.BoxGeometry( 10, 10, 10 );
+					var material = new THREE.MeshNormalMaterial();
+					this.cube = new THREE.Mesh( geometry, material );
+					this.cube.position.set( 0, 0, 0 );
+					this.scene.add( this.cube );
+
+					this.createPivot();
+				};
+
+				WWOBJLoader2Example.prototype.createPivot = function () {
+					this.pivot = new THREE.Object3D();
+					this.pivot.name = 'Pivot';
+					this.scene.add( this.pivot );
+				};
+
+				WWOBJLoader2Example.prototype.initPostGL = function () {
+					var reportProgress = function ( content ) {
+						console.log( 'Progress: ' + content );
+					};
+					var materialsLoaded = function ( materials ) {
+						var count = 0;
+						console.log( 'The following materials have been loaded:' );
+						for ( var mat in materials ) {
+							count++;
+						}
+						console.log( 'Loaded #' + count + ' materials.' );
+					};
+					var completedLoading = function () {
+						console.log( 'Loading complete!' );
+					};
+					this.wwObjLoader2.registerCallbackProgress( reportProgress );
+					this.wwObjLoader2.registerCallbackCompletedLoading( completedLoading );
+					this.wwObjLoader2.registerCallbackMaterialsLoaded( materialsLoaded );
+
+					return true;
+				};
+
+				WWOBJLoader2Example.prototype.loadFiles = function ( prepData ) {
+					prepData.sceneGraphBaseNode = this.pivot;
+					prepData.streamMeshes = this.streamMeshes;
+					this.wwObjLoader2.prepareRun( prepData );
+					this.wwObjLoader2.run();
+				};
+
+				WWOBJLoader2Example.prototype._handleFileSelect = function ( event, pathTexture ) {
+					var fileObj = null;
+					var fileMtl = null;
+					var files = event.target.files;
+
+					for ( var i = 0, file; file = files[ i ]; i++) {
+
+						if ( file.name.indexOf( '\.obj' ) > 0 && fileObj === null ) {
+							fileObj = file;
+						}
+
+						if ( file.name.indexOf( '\.mtl' ) > 0 && fileMtl === null ) {
+							fileMtl = file;
+						}
+
+					}
+
+					if ( fileObj == null ) {
+						alert( 'Unable to load OBJ file from given files.' );
+					}
+
+					var fileReader = new FileReader();
+					fileReader.onload = function( fileDataObj ) {
+
+						var uint8Array = new Uint8Array( fileDataObj.target.result );
+						if ( fileMtl === null ) {
+
+							app.loadFilesUser({
+								name: 'userObj',
+								objAsArrayBuffer: uint8Array,
+								pathTexture: pathTexture,
+								mtlAsString: null
+							})
+
+						} else {
+
+							fileReader.onload = function( fileDataMtl ) {
+
+								app.loadFilesUser({
+									name: 'userObj',
+									objAsArrayBuffer: uint8Array,
+									pathTexture: pathTexture,
+									mtlAsString: fileDataMtl.target.result
+								})
+							};
+							fileReader.readAsText( fileMtl );
+
+						}
+
+					};
+					fileReader.readAsArrayBuffer( fileObj );
+
+				};
+
+				WWOBJLoader2Example.prototype.loadFilesUser = function ( objDef ) {
+					var prepData = new THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer(
+						objDef.name, objDef.objAsArrayBuffer, objDef.pathTexture, objDef.mtlAsString, this.pivot, this.streamMeshes
+					);
+					this.wwObjLoader2.prepareRun( prepData );
+					this.wwObjLoader2.run();
+				};
+
+				WWOBJLoader2Example.prototype.resizeDisplayGL = function () {
+					this.controls.handleResize();
+
+					this.recalcAspectRatio();
+					this.renderer.setSize( this.canvas.offsetWidth, this.canvas.offsetHeight, false );
+
+					this.updateCamera();
+				};
+
+				WWOBJLoader2Example.prototype.recalcAspectRatio = function () {
+					this.aspectRatio = ( this.canvas.offsetHeight === 0 ) ? 1 : this.canvas.offsetWidth / this.canvas.offsetHeight;
+				};
+
+				WWOBJLoader2Example.prototype.resetCamera = function () {
+					this.camera.position.copy( this.cameraDefaults.posCamera );
+					this.cameraTarget.copy( this.cameraDefaults.posCameraTarget );
+
+					this.updateCamera();
+				};
+
+				WWOBJLoader2Example.prototype.updateCamera = function () {
+					this.camera.aspect = this.aspectRatio;
+					this.camera.lookAt( this.cameraTarget );
+					this.camera.updateProjectionMatrix();
+				};
+
+				WWOBJLoader2Example.prototype.render = function () {
+					if ( ! this.renderer.autoClear ) this.renderer.clear();
+
+					this.controls.update();
+
+					this.cube.rotation.x += 0.05;
+					this.cube.rotation.y += 0.05;
+
+					this.renderer.render( this.scene, this.camera );
+				};
+
+				WWOBJLoader2Example.prototype.alterSmoothShading = function () {
+
+					var scope = this;
+					scope.smoothShading = ! scope.smoothShading;
+					console.log( scope.smoothShading ? 'Enabling SmoothShading' : 'Enabling FlatShading');
+
+					scope.traversalFunction = function ( material ) {
+						material.shading = scope.smoothShading ? THREE.SmoothShading : THREE.FlatShading;
+						material.needsUpdate = true;
+					};
+					var scopeTraverse = function ( object3d ) {
+						scope.traverseScene( object3d );
+					};
+					scope.pivot.traverse( scopeTraverse );
+				};
+
+				WWOBJLoader2Example.prototype.alterDouble = function () {
+
+					var scope = this;
+					scope.doubleSide = ! scope.doubleSide;
+					console.log( scope.doubleSide ? 'Enabling DoubleSide materials' : 'Enabling FrontSide materials');
+
+					scope.traversalFunction  = function ( material ) {
+						material.side = scope.doubleSide ? THREE.DoubleSide : THREE.FrontSide;
+					};
+
+					var scopeTraverse = function ( object3d ) {
+						scope.traverseScene( object3d );
+					};
+					scope.pivot.traverse( scopeTraverse );
+				};
+
+				WWOBJLoader2Example.prototype.traverseScene = function ( object3d ) {
+
+					if ( object3d.material instanceof THREE.MultiMaterial ) {
+
+						for ( var matName in object3d.material.materials ) {
+
+							this.traversalFunction( object3d.material.materials[ matName ] );
+
+						}
+
+					} else if ( object3d.material ) {
+
+						this.traversalFunction( object3d.material );
+
+					}
+
+				};
+
+				WWOBJLoader2Example.prototype.clearAllAssests = function () {
+					var scope = this;
+					var remover = function ( object3d ) {
+
+						if ( object3d === scope.pivot ) {
+							return;
+						}
+						console.log( 'Removing: ' + object3d.name );
+						scope.scene.remove( object3d );
+
+						if ( object3d.hasOwnProperty( 'geometry' ) ) {
+							object3d.geometry.dispose();
+						}
+						if ( object3d.hasOwnProperty( 'material' ) ) {
+
+							var mat = object3d.material;
+							if ( mat.hasOwnProperty( 'materials' ) ) {
+
+								for ( var mmat in mat.materials ) {
+									mat.materials[ mmat ].dispose();
+								}
+							}
+						}
+						if ( object3d.hasOwnProperty( 'texture' ) ) {
+							object3d.texture.dispose();
+						}
+					};
+
+					scope.scene.remove( scope.pivot );
+					scope.pivot.traverse( remover );
+					scope.createPivot();
+				};
+
+				return WWOBJLoader2Example;
+
+			})();
+
+			var app = new WWOBJLoader2Example( document.getElementById( 'example' ) );
+
+			// Init dat.gui and controls
+			var elemFileInput = document.getElementById( 'fileUploadInput' );
+			var WWOBJLoader2Control = function() {
+				this.smoothShading = app.smoothShading;
+				this.doubleSide = app.doubleSide;
+				this.streamMeshes = app.streamMeshes;
+			};
+			var wwObjLoader2Control = new WWOBJLoader2Control();
+
+			var gui = new dat.GUI( {
+				autoPlace: false,
+				width: 320
+			} );
+
+			var menuDiv = document.getElementById( 'dat' );
+			menuDiv.appendChild(gui.domElement);
+			var folderOptions = gui.addFolder( 'WWOBJLoader2 Options' );
+			var controlSmooth = folderOptions.add( wwObjLoader2Control, 'smoothShading' ).name( 'Smooth Shading' );
+			controlSmooth.onChange( function( value ) {
+				console.log( 'Setting smoothShading to: ' + value );
+				app.alterSmoothShading();
+			});
+
+			var controlDouble = folderOptions.add( wwObjLoader2Control, 'doubleSide' ).name( 'Double Side Materials' );
+			controlDouble.onChange( function( value ) {
+				console.log( 'Setting doubleSide to: ' + value );
+				app.alterDouble();
+			});
+
+			var controlStreamMeshes = folderOptions.add( wwObjLoader2Control, 'streamMeshes' ).name( 'Stream Meshes' );
+			controlStreamMeshes.onChange( function( value ) {
+				console.log( 'Setting streamMeshes to: ' + value );
+				app.streamMeshes = value;
+			});
+
+			if ( app.fileApiAvailable ) {
+
+				wwObjLoader2Control.pathTexture = 'obj/female02/';
+				var controlPathTexture = folderOptions.add( wwObjLoader2Control, 'pathTexture' ).name( 'Relative path to textures' );
+				controlPathTexture.onChange( function( value ) {
+					console.log( 'Setting pathTexture to: ' + value );
+					app.pathTexture = value + '/';
+				});
+
+				wwObjLoader2Control.loadObjFile = function () {
+					elemFileInput.click();
+				};
+				folderOptions.add( wwObjLoader2Control, 'loadObjFile' ).name( 'Load OBJ/MTL Files' );
+
+				var handleFileSelect = function ( object3d ) {
+					app._handleFileSelect( object3d, wwObjLoader2Control.pathTexture );
+				};
+				elemFileInput.addEventListener( 'change' , handleFileSelect, false );
+
+				wwObjLoader2Control.clearAllAssests = function () {
+					app.clearAllAssests();
+				};
+				folderOptions.add( wwObjLoader2Control, 'clearAllAssests' ).name( 'Clear Scene' );
+
+			}
+			folderOptions.open();
+
+
+
+			// init three.js example application
+			var resizeWindow = function () {
+				app.resizeDisplayGL();
+			};
+
+			var render = function () {
+				requestAnimationFrame( render );
+				app.render();
+			};
+
+			window.addEventListener( 'resize', resizeWindow, false );
+
+			console.log( 'Starting initialisation phase...' );
+			app.initGL();
+			app.resizeDisplayGL();
+			app.initPostGL();
+
+			var prepData = new THREE.OBJLoader2.WWOBJLoader2.PrepDataFile(
+				'male02',
+				'obj/male02/',
+				'male02.obj',
+				'obj/male02/',
+				'male02.mtl'
+			);
+			app.loadFiles( prepData );
+
+			// kick render loop
+			render();
+
+		</script>
+	</body>
+</html>

+ 409 - 0
examples/webgl_loader_obj2_ww_parallels.html

@@ -0,0 +1,409 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - Web Worker Parallel Demo</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: 0 0 0 0;
+				padding: 0 0 0 0;
+				border: none;
+				cursor: default;
+			}
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				z-index: 100;
+				display:block;
+			}
+			#info a {
+				color: #f00;
+				font-weight: bold;
+				text-decoration: underline;
+				cursor: pointer
+			}
+			#glFullscreen {
+				width: 100%;
+				height: 100vh;
+				min-width: 640px;
+				min-height: 360px;
+				position: relative;
+				overflow: hidden;
+				z-index: 0;
+			}
+			#example {
+				width: 100%;
+				height: 100%;
+				top: 0;
+				left: 0;
+				background-color: #000000;
+			}
+			#feedback {
+				position: absolute;
+				color: darkorange;
+				text-align: left;
+				bottom: 0%;
+				left: 0%;
+				width: auto;
+				padding: 0px 0px 4px 4px;
+			}
+			#dat {
+				user-select: none;
+				position: absolute;
+				left: 0px;
+				top: 0px;
+				z-Index: 2;
+			}
+		</style>
+	</head>
+
+	<body>
+		<div id="glFullscreen">
+			<canvas id="example"></canvas>
+		</div>
+		<div id="dat">
+
+		</div>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank">three.js</a> - WWOBJLoader2Director Parallels Demo
+		</div>
+		<div id="feedback">
+		</div>
+
+		<script src="js/Detector.js"></script>
+		<script src="../build/three.js"></script>
+		<script src="js/controls/TrackballControls.js"></script>
+		<script src="js/loaders/MTLLoader.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<script src="js/loaders/OBJLoader2.js"></script>
+		<script src="js/loaders/WWOBJLoader2.js"></script>
+		<script>
+
+			'use strict';
+
+			var WWParallels = (function () {
+
+				function WWParallels( elementToBindTo ) {
+					this.renderer = null;
+					this.canvas = elementToBindTo;
+					this.aspectRatio = 1;
+					this.recalcAspectRatio();
+
+					this.scene = null;
+					this.cameraDefaults = {
+						posCamera: new THREE.Vector3( 0.0, 175.0, 500.0 ),
+						posCameraTarget: new THREE.Vector3( 0, 0, 0 ),
+						near: 0.1,
+						far: 10000,
+						fov: 45
+					};
+					this.camera = null;
+					this.cameraTarget = this.cameraDefaults.posCameraTarget;
+
+					this.wwDirector = new THREE.OBJLoader2.WWOBJLoader2Director();
+					this.wwDirector.setCrossOrigin( 'anonymous' );
+
+					this.controls = null;
+					this.cube = null;
+
+					this.allAssets = [];
+					this.feedbackArray = null;
+				}
+
+				WWParallels.prototype.initGL = function () {
+					this.renderer = new THREE.WebGLRenderer( {
+						canvas: this.canvas,
+						antialias: true,
+						autoClear: true
+					} );
+					this.renderer.setClearColor( 0x050505 );
+
+					this.scene = new THREE.Scene();
+
+					this.camera = new THREE.PerspectiveCamera( this.cameraDefaults.fov, this.aspectRatio, this.cameraDefaults.near, this.cameraDefaults.far );
+					this.resetCamera();
+					this.controls = new THREE.TrackballControls( this.camera, this.renderer.domElement );
+
+					var ambientLight = new THREE.AmbientLight( 0x404040 );
+					var directionalLight1 = new THREE.DirectionalLight( 0xC0C090 );
+					var directionalLight2 = new THREE.DirectionalLight( 0xC0C090 );
+
+					directionalLight1.position.set( -100, -50, 100 );
+					directionalLight2.position.set( 100, 50, -100 );
+
+					this.scene.add( directionalLight1 );
+					this.scene.add( directionalLight2 );
+					this.scene.add( ambientLight );
+
+					var geometry = new THREE.BoxGeometry( 10, 10, 10 );
+					var material = new THREE.MeshNormalMaterial();
+					this.cube = new THREE.Mesh( geometry, material );
+					this.cube.position.set( 0, 0, 0 );
+					this.scene.add( this.cube );
+				};
+
+				WWParallels.prototype.resizeDisplayGL = function () {
+					this.controls.handleResize();
+
+					this.recalcAspectRatio();
+					this.renderer.setSize( this.canvas.offsetWidth, this.canvas.offsetHeight, false );
+
+					this.updateCamera();
+				};
+
+				WWParallels.prototype.recalcAspectRatio = function () {
+					this.aspectRatio = ( this.canvas.offsetHeight === 0 ) ? 1 : this.canvas.offsetWidth / this.canvas.offsetHeight;
+				};
+
+				WWParallels.prototype.resetCamera = function () {
+					this.camera.position.copy( this.cameraDefaults.posCamera );
+					this.cameraTarget.copy( this.cameraDefaults.posCameraTarget );
+
+					this.updateCamera();
+				};
+
+				WWParallels.prototype.updateCamera = function () {
+					this.camera.aspect = this.aspectRatio;
+					this.camera.lookAt( this.cameraTarget );
+					this.camera.updateProjectionMatrix();
+				};
+
+				WWParallels.prototype.render = function () {
+					if ( ! this.renderer.autoClear ) this.renderer.clear();
+
+					this.controls.update();
+
+					this.cube.rotation.x += 0.05;
+					this.cube.rotation.y += 0.05;
+
+					this.renderer.render( this.scene, this.camera );
+				};
+				WWParallels.prototype.reportProgress = function( text ) {
+					document.getElementById( 'feedback' ).innerHTML = text;
+				};
+
+				WWParallels.prototype.enqueueAllAssests = function ( maxQueueSize, maxWebWorkers, streamMeshes ) {
+					var scope = this;
+					scope.wwDirector.objectsCompleted = 0;
+					scope.feedbackArray = new Array( maxWebWorkers );
+					for ( var i = 0; i < maxWebWorkers; i++ ) {
+						scope.feedbackArray[ i ] = 'Worker #' + i + ': Awaiting feedback';
+					}
+					scope.reportProgress( scope.feedbackArray.join( '\<br\>' ) );
+
+					var callbackCompletedLoading = function ( modelName, instanceNo ) {
+						var msg = 'Worker #' + instanceNo + ': Completed loading: ' + modelName + ' (#' + scope.wwDirector.objectsCompleted + ')';
+						console.log( msg );
+						scope.feedbackArray[ instanceNo ] = msg;
+						scope.reportProgress( scope.feedbackArray.join( '\<br\>' ) );
+					};
+					var callbackMeshLoaded = function ( meshName, material ) {
+						var replacedMaterial = null;
+
+						if ( material != null && material.name === 'defaultMaterial' || meshName === 'Mesh_Mesh_head_geo.001' ) {
+							replacedMaterial = material;
+							replacedMaterial.color = new THREE.Color( Math.random(), Math.random(), Math.random() );
+						}
+
+						return replacedMaterial;
+					};
+
+					this.wwDirector.prepareWorkers(
+						{
+							completedLoading: callbackCompletedLoading,
+							meshLoaded: callbackMeshLoaded
+						},
+						maxQueueSize,
+						maxWebWorkers
+					);
+					console.log( 'Configuring WWManager with queue size ' + this.wwDirector.getMaxQueueSize() + ' and ' + this.wwDirector.getMaxWebWorkers() + ' workers.' );
+
+					var models = [];
+					models.push( {
+						modelName: 'male02',
+						dataAvailable: false,
+						pathObj: 'obj/male02/',
+						fileObj: 'male02.obj',
+						pathTexture: 'obj/male02/',
+						fileMtl: 'male02.mtl'
+					} );
+
+					models.push( {
+						modelName: 'female02',
+						dataAvailable: false,
+						pathObj: 'obj/female02/',
+						fileObj: 'female02.obj',
+						pathTexture: 'obj/female02/',
+						fileMtl: 'female02.mtl'
+					} );
+
+					models.push( {
+						modelName: 'viveController',
+						dataAvailable: false,
+						pathObj: 'models/obj/vive-controller/',
+						fileObj: 'vr_controller_vive_1_5.obj',
+						scale: 400.0
+					} );
+
+					models.push( {
+						modelName: 'cerberus',
+						dataAvailable: false,
+						pathObj: 'models/obj/cerberus/',
+						fileObj: 'Cerberus.obj',
+						scale: 50.0
+					} );
+					models.push( {
+						modelName:'WaltHead',
+						dataAvailable: false,
+						pathObj: 'obj/walt/',
+						fileObj: 'WaltHead.obj',
+						pathTexture: 'obj/walt/',
+						fileMtl: 'WaltHead.mtl'
+					} );
+
+					var pivot;
+					var distributionBase = -500;
+					var distributionMax = 1000;
+					var modelIndex = 0;
+					var model;
+					var runParams;
+					for ( var i = 0; i < maxQueueSize; i++ ) {
+
+						modelIndex = Math.floor( Math.random() * 5 );
+						model = models[ modelIndex ];
+
+						pivot = new THREE.Object3D();
+						pivot.position.set(
+							distributionBase + distributionMax * Math.random(),
+							distributionBase + distributionMax * Math.random(),
+							distributionBase + distributionMax * Math.random()
+						);
+						if ( model.scale != null ) pivot.scale.set( model.scale, model.scale, model.scale );
+
+						this.scene.add( pivot );
+
+						model.sceneGraphBaseNode = pivot;
+
+						runParams = new THREE.OBJLoader2.WWOBJLoader2.PrepDataFile(
+							model.modelName, model.pathObj, model.fileObj, model.pathTexture, model.fileMtl, model.sceneGraphBaseNode, streamMeshes
+						);
+						this.wwDirector.enqueueForRun( runParams );
+						this.allAssets.push( runParams );
+					}
+
+					this.wwDirector.processQueue();
+				};
+
+				WWParallels.prototype.clearAllAssests = function () {
+					var ref;
+					var scope = this;
+
+					for ( var asset in this.allAssets ) {
+						ref = this.allAssets[asset];
+
+						var remover = function ( object3d ) {
+
+							if ( object3d === ref.sceneGraphBaseNode ) {
+								return;
+							}
+							console.log( 'Removing ' + object3d.name );
+							scope.scene.remove( object3d );
+
+							if ( object3d.hasOwnProperty( 'geometry' ) ) {
+								object3d.geometry.dispose();
+							}
+							if ( object3d.hasOwnProperty( 'material' ) ) {
+
+								var mat = object3d.material;
+								if ( mat.hasOwnProperty( 'materials' ) ) {
+
+									for ( var mmat in mat.materials ) {
+										mat.materials[mmat].dispose();
+									}
+								}
+							}
+							if ( object3d.hasOwnProperty( 'texture' ) ) {
+								object3d.texture.dispose();
+							}
+						};
+						scope.scene.remove( ref.sceneGraphBaseNode );
+						ref.sceneGraphBaseNode.traverse( remover );
+						ref.sceneGraphBaseNode = null;
+					}
+					this.allAssets = [];
+				};
+
+				WWParallels.prototype.terminateManager = function () {
+					this.wwDirector.deregister();
+				};
+
+				return WWParallels;
+
+			})();
+
+			var app = new WWParallels( document.getElementById( 'example' ) );
+
+			var WWParallelsControl = function() {
+				this.queueLength = 128;
+				this.workerCount = 4;
+				this.streamMeshes = true;
+				this.run = function () {
+					app.enqueueAllAssests( this.queueLength, this.workerCount, this.streamMeshes );
+				};
+				this.terminate = function () {
+					app.terminateManager();
+				};
+				this.clearAllAssests = function () {
+					app.terminateManager();
+					app.clearAllAssests();
+				};
+			};
+			var wwParallelsControl = new WWParallelsControl();
+
+			var gui = new dat.GUI( {
+				autoPlace: false,
+				width: 320
+			} );
+
+			var menuDiv = document.getElementById( 'dat' );
+			menuDiv.appendChild(gui.domElement);
+			var folderQueue = gui.addFolder( 'Web Worker Director Queue Control' );
+			folderQueue.add( wwParallelsControl, 'queueLength' ).min( 1 ).max( 1024 ).step( 1 );
+			folderQueue.add( wwParallelsControl, 'workerCount' ).min( 1 ).max( 16 ).step( 1 );
+			folderQueue.add( wwParallelsControl, 'streamMeshes' );
+			folderQueue.add( wwParallelsControl, 'run' ).name( 'Run Queue' );
+			folderQueue.open();
+
+			var folderWWControl = gui.addFolder( 'Resource Management' );
+			folderWWControl.add( wwParallelsControl, 'terminate' ).name( 'Terminate WWManager' );
+			folderWWControl.add( wwParallelsControl, 'clearAllAssests' ).name( 'Clear Scene' );
+
+			var resizeWindow = function () {
+				app.resizeDisplayGL();
+			};
+
+			var render = function () {
+				requestAnimationFrame( render );
+				app.render();
+			};
+
+			window.addEventListener( 'resize', resizeWindow, false );
+
+			console.log( 'Starting initialisation phase...' );
+			app.initGL();
+			app.resizeDisplayGL();
+
+			render();
+
+		</script>
+	</body>
+</html>

+ 0 - 374
examples/webgl_loader_scene.html

@@ -1,374 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<title>three.js webgl - io - scene loader</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 {
-				background:#000;
-				color:#fff;
-				padding:0;
-				margin:0;
-				overflow:hidden;
-				font-family:georgia;
-				text-align:center;
-			}
-
-			#info {
-				position: absolute;
-				top: 0px; width: 100%;
-				padding: 5px;
-				font-family: Monospace;
-				font-size: 13px;
-				text-align: center;
-				z-index:100;
-			}
-
-			#progress {
-				color:red;
-				top:7em;
-				width: 100%;
-				font-size:3em;
-				font-variant:small-caps;
-				font-weight:bold;
-				position:absolute;
-				z-index:100;
-				text-align: center;
-				text-shadow: #000 0px 0px 10px;
-				display:none;
-			}
-
-			.shadow {
-				-moz-box-shadow: 0px 0px 5px #000;
-				-webkit-box-shadow: 0px 0px 5px #000;
-				box-shadow: 0px 0px 5px #000;
-			}
-
-			#progressbar {
-				text-align: center;
-				background: white;
-				width: 250px;
-				height: 10px;
-			}
-
-			#bar {
-				background:#d00;
-				width:50px;
-				height:10px;
-			}
-
-			.enabled {
-				color: lime!important;
-				cursor:pointer;
-			}
-
-			.enabled:hover {
-				text-shadow: #0f0 0px 0px 5px !important;
-			}
-
-			.disabled {
-				background:gray;
-				cursor:default;
-			}
-
-			a { color:red }
-			canvas { pointer-events:none; z-index:10; }
-
-		</style>
-	</head>
-
-	<body>
-		<div id="info">
-			<a href="http://threejs.org">three.js</a> - scene loader test
-		</div>
-
-		<div id="progress">
-			<span id="message">Loading ...</span>
-
-			<center>
-				<div id="progressbar" class="shadow"><div id="bar" class="shadow"></div></div>
-			</center>
-		</div>
-
-		<script src="../build/three.js"></script>
-		<script src="js/MorphAnimMesh.js"></script>
-		<script src="js/loaders/collada/Animation.js"></script>
-		<script src="js/loaders/collada/AnimationHandler.js"></script>
-		<script src="js/loaders/collada/KeyFrameAnimation.js"></script>
-
-		<script src="js/loaders/DDSLoader.js"></script>
-
-		<script src="js/loaders/ctm/lzma.js"></script>
-		<script src="js/loaders/ctm/ctm.js"></script>
-		<script src="js/loaders/ctm/CTMLoader.js"></script>
-
-		<script src="js/loaders/deprecated/SceneLoader.js"></script>
-		<script src="js/loaders/BinaryLoader.js"></script>
-		<script src="js/loaders/OBJLoader.js"></script>
-		<script src="js/loaders/VTKLoader.js"></script>
-		<script src="js/loaders/STLLoader.js"></script>
-		<script src="js/loaders/ColladaLoader.js"></script>
-		<script src="js/loaders/UTF8Loader.js"></script>
-		<script src="js/loaders/MTLLoader.js"></script>
-
-		<script src="js/Detector.js"></script>
-		<script src="js/libs/stats.min.js"></script>
-
-		<script>
-
-			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
-
-			var SCREEN_WIDTH = window.innerWidth;
-			var SCREEN_HEIGHT = window.innerHeight;
-
-			var container,stats;
-
-			var camera, scene, loaded;
-			var renderer;
-
-			var mouseX = 0, mouseY = 0;
-
-			var mixers = [];
-
-			var windowHalfX = window.innerWidth / 2;
-			var windowHalfY = window.innerHeight / 2;
-
-			var rotatingObjects = [];
-
-			var clock = new THREE.Clock();
-
-			document.addEventListener( 'mousemove', onDocumentMouseMove, false );
-
-			THREE.DefaultLoadingManager.onProgress = function ( item, loaded, total ) {
-
-				console.log( item, loaded, total );
-
-			};
-
-			init();
-			animate();
-
-			function $( id ) {
-
-				return document.getElementById( id );
-
-			}
-
-			function init() {
-
-				container = document.createElement( 'div' );
-				document.body.appendChild( container );
-
-				var loadScene = createLoadScene();
-
-				camera = loadScene.camera;
-				scene = loadScene.scene;
-
-				renderer = new THREE.WebGLRenderer( { antialias: true } );
-				renderer.setPixelRatio( window.devicePixelRatio );
-				renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
-				renderer.domElement.style.position = "relative";
-				container.appendChild( renderer.domElement );
-
-				renderer.gammaInput = true;
-				renderer.gammaOutput = true;
-
-				stats = new Stats();
-				container.appendChild( stats.dom );
-
-				var callbackProgress = function( progress, result ) {
-
-					var bar = 250,
-						total = progress.totalModels + progress.totalTextures,
-						loaded = progress.loadedModels + progress.loadedTextures;
-
-					if ( total )
-						bar = Math.floor( bar * loaded / total );
-
-					$( "bar" ).style.width = bar + "px";
-
-				};
-
-				var callbackFinished = function ( result ) {
-
-					loaded = result;
-
-					$( "message" ).style.display = "none";
-					$( "progressbar" ).style.display = "none";
-
-					result.scene.traverse( function ( object ) {
-
-						if ( object.userData.rotating === true ) {
-
-							rotatingObjects.push( object );
-
-						}
-
-						if ( object instanceof THREE.Mesh ) {
-
-							if( object.geometry && object.geometry.animations && object.geometry.animations.length > 0 ) {
-
-								var mixer = new THREE.AnimationMixer( object );
-								mixer.clipAction( object.geometry.animations[0] ).play();
-								mixers.push( mixer );
-
-							}
-
-						}
-
-					} );
-
-					//
-
-					$( "progress" ).style.display = "none";
-
-					camera = loaded.currentCamera;
-					camera.aspect = window.innerWidth / window.innerHeight;
-					camera.updateProjectionMatrix();
-
-					scene = loaded.scene;
-
-				};
-
-				$( "progress" ).style.display = "block";
-
-				THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
-
-				var loader = new THREE.SceneLoader();
-
-				loader.addGeometryHandler( "binary", THREE.BinaryLoader );
-				loader.addGeometryHandler( "ctm", THREE.CTMLoader );
-				loader.addGeometryHandler( "vtk", THREE.VTKLoader );
-				loader.addGeometryHandler( "stl", THREE.STLLoader );
-
-				loader.addHierarchyHandler( "obj", THREE.OBJLoader );
-				loader.addHierarchyHandler( "dae", THREE.ColladaLoader );
-				loader.addHierarchyHandler( "utf8", THREE.UTF8Loader );
-
-				loader.callbackProgress = callbackProgress;
-
-				loader.load( "scenes/test_scene.js", callbackFinished );
-
-				//
-
-				window.addEventListener( 'resize', onWindowResize, false );
-
-			}
-
-			function onWindowResize() {
-
-				windowHalfX = window.innerWidth / 2;
-				windowHalfY = window.innerHeight / 2;
-
-				camera.aspect = window.innerWidth / window.innerHeight;
-				camera.updateProjectionMatrix();
-
-				renderer.setSize( window.innerWidth, window.innerHeight );
-
-			}
-
-			function onDocumentMouseMove( event ) {
-
-				mouseX = ( event.clientX - windowHalfX );
-				mouseY = ( event.clientY - windowHalfY );
-
-			}
-
-			function createLoadScene() {
-
-				var result = {
-
-					scene:  new THREE.Scene(),
-					camera: new THREE.PerspectiveCamera( 65, window.innerWidth / window.innerHeight, 1, 1000 )
-
-				};
-
-				result.camera.position.z = 100;
-				result.scene.add( result.camera );
-
-				var object, geometry, material, light, count = 500, range = 200;
-
-				material = new THREE.MeshLambertMaterial( { color:0xffffff } );
-				geometry = new THREE.BoxGeometry( 5, 5, 5 );
-
-				for( var i = 0; i < count; i++ ) {
-
-					object = new THREE.Mesh( geometry, material );
-
-					object.position.x = ( Math.random() - 0.5 ) * range;
-					object.position.y = ( Math.random() - 0.5 ) * range;
-					object.position.z = ( Math.random() - 0.5 ) * range;
-
-					object.rotation.x = Math.random() * 6;
-					object.rotation.y = Math.random() * 6;
-					object.rotation.z = Math.random() * 6;
-
-					object.matrixAutoUpdate = false;
-					object.updateMatrix();
-
-					result.scene.add( object );
-
-				}
-
-				result.scene.matrixAutoUpdate = false;
-
-				light = new THREE.PointLight( 0xffffff );
-				result.scene.add( light );
-
-				light = new THREE.DirectionalLight( 0x111111 );
-				light.position.x = 1;
-				result.scene.add( light );
-
-				return result;
-
-			}
-
-			//
-
-			function animate() {
-
-				requestAnimationFrame( animate );
-
-				render();
-				stats.update();
-
-			}
-
-			function render() {
-
-				var delta = clock.getDelta();
-
-				camera.position.x += ( mouseX - camera.position.x ) * .001;
-				camera.position.y += ( - mouseY - camera.position.y ) * .001;
-
-				camera.lookAt( scene.position );
-
-				// update skinning
-
-				THREE.AnimationHandler.update( delta * 0.75 );
-
-				for ( var i = 0; i < rotatingObjects.length; i ++ ) {
-
-					var object = rotatingObjects[ i ];
-
-					if ( object.userData.rotateX ) object.rotation.x += 1 * delta;
-					if ( object.userData.rotateY ) object.rotation.y += 0.5 * delta;
-
-				}
-
-				
-				for ( var i = 0; i < mixers.length; i ++ ) {
-
-					mixers[ i ].update( delta );
-
-				}
-
-				renderer.render( scene, camera );
-
-			}
-
-		</script>
-
-	</body>
-</html>

+ 1 - 1
examples/webgl_marchingcubes.html

@@ -51,7 +51,7 @@
 	<div id="info">
 		<a href="http://threejs.org" target="_blank">three.js</a> -
 		marching cubes -
-		[based on greggman's <a href="http://webglsamples.googlecode.com/hg/blob/blob.html">blob</a>, original code by Henrik Rydgård]
+		[based on greggman's <a href="https://webglsamples.org/blob/blob.html">blob</a>, original code by Henrik Rydgård]
 	</div>
 
 	<script src="../build/three.js"></script>

+ 12 - 27
examples/webgl_materials.html

@@ -27,7 +27,7 @@
 			var container, stats;
 
 			var camera, scene, renderer;
-			var particleLight;
+			var pointLight;
 
 			var objects = [], materials = [];
 
@@ -46,22 +46,9 @@
 
 				// Grid
 
-				var line_material = new THREE.LineBasicMaterial( { color: 0x303030 } ),
-					geometry = new THREE.Geometry(),
-					floor = -75, step = 25;
-
-				for ( var i = 0; i <= 40; i ++ ) {
-
-					geometry.vertices.push( new THREE.Vector3( - 500, floor, i * step - 500 ) );
-					geometry.vertices.push( new THREE.Vector3(   500, floor, i * step - 500 ) );
-
-					geometry.vertices.push( new THREE.Vector3( i * step - 500, floor, -500 ) );
-					geometry.vertices.push( new THREE.Vector3( i * step - 500, floor,  500 ) );
-
-				}
-
-				var line = new THREE.LineSegments( geometry, line_material );
-				scene.add( line );
+				var helper = new THREE.GridHelper( 1000, 40, 0x303030, 0x303030 );
+				helper.position.y = - 75;
+				scene.add( helper );
 
 				// Materials
 
@@ -110,25 +97,23 @@
 
 				addMesh( geometry, materials );
 
-				particleLight = new THREE.Mesh( new THREE.SphereGeometry( 4, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) );
-				scene.add( particleLight );
-
 				// Lights
 
 				scene.add( new THREE.AmbientLight( 0x111111 ) );
 
-				var directionalLight = new THREE.DirectionalLight( /*Math.random() * */ 0xffffff, 0.125 );
+				var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.125 );
 
 				directionalLight.position.x = Math.random() - 0.5;
 				directionalLight.position.y = Math.random() - 0.5;
 				directionalLight.position.z = Math.random() - 0.5;
-
 				directionalLight.position.normalize();
 
 				scene.add( directionalLight );
 
-				var pointLight = new THREE.PointLight( 0xffffff, 1 );
-				particleLight.add( pointLight );
+				pointLight = new THREE.PointLight( 0xffffff, 1 );
+				scene.add( pointLight );
+
+				pointLight.add( new THREE.Mesh( new THREE.SphereGeometry( 4, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) ) );
 
 				//
 
@@ -235,9 +220,9 @@
 				materials[ materials.length - 2 ].emissive.setHSL( 0.54, 1, 0.35 * ( 0.5 + 0.5 * Math.sin( 35 * timer ) ) );
 				materials[ materials.length - 3 ].emissive.setHSL( 0.04, 1, 0.35 * ( 0.5 + 0.5 * Math.cos( 35 * timer ) ) );
 
-				particleLight.position.x = Math.sin( timer * 7 ) * 300;
-				particleLight.position.y = Math.cos( timer * 5 ) * 400;
-				particleLight.position.z = Math.cos( timer * 3 ) * 300;
+				pointLight.position.x = Math.sin( timer * 7 ) * 300;
+				pointLight.position.y = Math.cos( timer * 5 ) * 400;
+				pointLight.position.z = Math.cos( timer * 3 ) * 300;
 
 				renderer.render( scene, camera );
 

+ 69 - 32
examples/webgl_materials_variations_physical.html

@@ -26,13 +26,20 @@
 	<body>
 
 		<div id="container"></div>
-		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> - Physical Material Variations by <a href="http://clara.io/" target="_blank">Ben Houston</a>.</div>
+		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> - Physical Material Variations by <a href="http://clara.io/" target="_blank">Ben Houston</a>.<br/><br/>
+		Note: Every second sphere has an IBL environment map on it.</div>
 
 		<script src="../build/three.js"></script>
 		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/loaders/RGBELoader.js"></script>
+		<script src="js/loaders/HDRCubeTextureLoader.js"></script>
+
+		<script src="js/pmrem/PMREMGenerator.js"></script>
+		<script src="js/pmrem/PMREMCubeUVPacker.js"></script>
 
 		<script src="js/Detector.js"></script>
 		<script src="js/libs/stats.min.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
 
 		<script>
 
@@ -61,54 +68,82 @@
 
 				//
 
-				var reflectionCube = new THREE.CubeTextureLoader()
-					.setPath( 'textures/cube/SwedishRoyalCastle/' )
-					.load( [ 'px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg' ] );
-				reflectionCube.format = THREE.RGBFormat;
+				var genCubeUrls = function( prefix, postfix ) {
+					return [
+						prefix + 'px' + postfix, prefix + 'nx' + postfix,
+						prefix + 'py' + postfix, prefix + 'ny' + postfix,
+						prefix + 'pz' + postfix, prefix + 'nz' + postfix
+					];
+				};
+
+
+				var textureCube = new THREE.CubeTextureLoader()
+					.setPath( 'textures/cube/pisa/' )
+					.load( [ 'px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png' ] );
 
 				scene = new THREE.Scene();
-				scene.background = reflectionCube;
+				scene.background = textureCube;
+				
+				var hdrUrls = genCubeUrls( './textures/cube/pisaHDR/', '.hdr' );
+				var hdrCubeRenderTarget = null;
+
+				new THREE.HDRCubeTextureLoader().load( THREE.UnsignedByteType, hdrUrls, function ( hdrCubeMap ) {
+
+					var pmremGenerator = new THREE.PMREMGenerator( hdrCubeMap );
+					pmremGenerator.update( renderer );
+
+					var pmremCubeUVPacker = new THREE.PMREMCubeUVPacker( pmremGenerator.cubeLods );
+					pmremCubeUVPacker.update( renderer );
 
-				// Materials
+					hdrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+					 
+					// Materials
 
-				var cubeWidth = 400;
-				var numberOfSphersPerSide = 5;
-				var sphereRadius = ( cubeWidth / numberOfSphersPerSide ) * 0.8 * 0.5;
-				var stepSize = 1.0 / numberOfSphersPerSide;
+					var cubeWidth = 400;
+					var numberOfSphersPerSide = 5;
+					var sphereRadius = ( cubeWidth / numberOfSphersPerSide ) * 0.8 * 0.5;
+					var stepSize = 1.0 / numberOfSphersPerSide;
 
-				var geometry = new THREE.SphereBufferGeometry( sphereRadius, 32, 16 );
+					var geometry = new THREE.SphereBufferGeometry( sphereRadius, 32, 16 );
 
-				for ( var alpha = 0; alpha <= 1.0; alpha += stepSize ) {
+					var index = 0;
 
-					for ( var beta = 0; beta <= 1.0; beta += stepSize ) {
+					for ( var alpha = 0; alpha <= 1.0; alpha += stepSize ) {
 
-						for ( var gamma = 0; gamma <= 1.0; gamma += stepSize ) {
+						for ( var beta = 0; beta <= 1.0; beta += stepSize ) {
 
-							var diffuseColor = new THREE.Color().setHSL( alpha, 0.5, 0.5 );
+							for ( var gamma = 0; gamma <= 1.0; gamma += stepSize ) {
 
-							var material = new THREE.MeshPhysicalMaterial( {
-								color: diffuseColor,
-								metalness: 0,
-								roughness: 0.5,
-								clearCoat:  1.0 - alpha,
-								clearCoatRoughness: 1.0 - beta,
-								reflectivity: 1.0 - gamma,
-								envMap: reflectionCube
-							} );
+								var diffuseColor = new THREE.Color().setHSL( alpha, 0.5, 0.25 );
 
-							var mesh = new THREE.Mesh( geometry, material );
+								var material = new THREE.MeshPhysicalMaterial( {
+									color: diffuseColor,
+									metalness: 0,
+									roughness: 0.5,
+									clearCoat:  1.0 - alpha,
+									clearCoatRoughness: 1.0 - beta,
+									reflectivity: 1.0 - gamma,
+									envMap: ( index % 2 ) == 1 ? hdrCubeRenderTarget.texture : null
+								} );
 
-							mesh.position.x = alpha * 400 - 200;
-							mesh.position.y = beta * 400 - 200;
-							mesh.position.z = gamma * 400 - 200;
+								index ++;
 
-							scene.add( mesh );
+								var mesh = new THREE.Mesh( geometry, material );
+
+								mesh.position.x = alpha * 400 - 200;
+								mesh.position.y = beta * 400 - 200;
+								mesh.position.z = gamma * 400 - 200;
+
+								scene.add( mesh );
+
+							}
+							index ++;
 
 						}
+						index ++;
 
 					}
-
-				}
+				});
 
 				function addLabel( name, location ) {
 
@@ -161,6 +196,8 @@
 
 				renderer.gammaInput = true;
 				renderer.gammaOutput = true;
+				renderer.toneMapping = THREE.Uncharted2ToneMapping;
+				renderer.toneMappingExposure = 0.75;
 
 				//
 

+ 70 - 36
examples/webgl_materials_variations_standard.html

@@ -26,10 +26,16 @@
 	<body>
 
 		<div id="container"></div>
-		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> - Standard Material Variations by <a href="http://clara.io/" target="_blank">Ben Houston</a>.</div>
+		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> - Standard Material Variations by <a href="http://clara.io/" target="_blank">Ben Houston</a>.<br/><br/>
+		Note: Every second sphere has an IBL environment map on it.</div>
 
 		<script src="../build/three.js"></script>
 		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/loaders/RGBELoader.js"></script>
+		<script src="js/loaders/HDRCubeTextureLoader.js"></script>
+
+		<script src="js/pmrem/PMREMGenerator.js"></script>
+		<script src="js/pmrem/PMREMCubeUVPacker.js"></script>
 
 		<script src="js/Detector.js"></script>
 		<script src="js/libs/stats.min.js"></script>
@@ -60,14 +66,23 @@
 				camera.position.set( 0.0, 400, 400 * 3.5 );
 
 				//
-
-				var reflectionCube = new THREE.CubeTextureLoader()
-					.setPath( 'textures/cube/SwedishRoyalCastle/' )
-					.load( [ 'px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg' ] );
-				reflectionCube.format = THREE.RGBFormat;
+				var genCubeUrls = function( prefix, postfix ) {
+					return [
+						prefix + 'px' + postfix, prefix + 'nx' + postfix,
+						prefix + 'py' + postfix, prefix + 'ny' + postfix,
+						prefix + 'pz' + postfix, prefix + 'nz' + postfix
+					];
+				};
+
+				var textureCube = new THREE.CubeTextureLoader()
+					.setPath( 'textures/cube/pisa/' )
+					.load( [ 'px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png' ] );
 
 				scene = new THREE.Scene();
-				scene.background = reflectionCube;
+				scene.background = textureCube;
+				
+				var hdrUrls = genCubeUrls( './textures/cube/pisaHDR/', '.hdr' );
+				var hdrCubeRenderTarget = null;
 
 				// Materials
 
@@ -76,50 +91,67 @@
 				imgTexture.anisotropy = 16;
 				imgTexture = null;
 
-				var shininess = 50, specular = 0x333333, bumpScale = 1, shading = THREE.SmoothShading;
+				new THREE.HDRCubeTextureLoader().load( THREE.UnsignedByteType, hdrUrls, function ( hdrCubeMap ) {
+
+					var pmremGenerator = new THREE.PMREMGenerator( hdrCubeMap );
+					pmremGenerator.update( renderer );
+
+					var pmremCubeUVPacker = new THREE.PMREMCubeUVPacker( pmremGenerator.cubeLods );
+					pmremCubeUVPacker.update( renderer );
+
+					hdrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
 
-				var materials = [];
+					var shininess = 50, specular = 0x333333, bumpScale = 1, shading = THREE.SmoothShading;
 
-				var cubeWidth = 400;
-				var numberOfSphersPerSide = 5;
-				var sphereRadius = ( cubeWidth / numberOfSphersPerSide ) * 0.8 * 0.5;
-				var stepSize = 1.0 / numberOfSphersPerSide;
+					var materials = [];
 
-				var geometry = new THREE.SphereBufferGeometry( sphereRadius, 32, 16 );
+					var cubeWidth = 400;
+					var numberOfSphersPerSide = 5;
+					var sphereRadius = ( cubeWidth / numberOfSphersPerSide ) * 0.8 * 0.5;
+					var stepSize = 1.0 / numberOfSphersPerSide;
+					
+					var geometry = new THREE.SphereBufferGeometry( sphereRadius, 32, 16 );
 
-				for ( var alpha = 0, alphaIndex = 0; alpha <= 1.0; alpha += stepSize, alphaIndex ++ ) {
+					var index = 0;
 
-					for ( var beta = 0; beta <= 1.0; beta += stepSize ) {
+					for ( var alpha = 0; alpha <= 1.0; alpha += stepSize ) {
 
-						for ( var gamma = 0; gamma <= 1.0; gamma += stepSize ) {
+						for ( var beta = 0; beta <= 1.0; beta += stepSize ) {
 
-							// basic monochromatic energy preservation
-							var diffuseColor = new THREE.Color().setHSL( alpha, 0.5, gamma * 0.5 );
+							for ( var gamma = 0; gamma <= 1.0; gamma += stepSize ) {
 
-							var material = new THREE.MeshStandardMaterial( {
-								map: imgTexture,
-								bumpMap: imgTexture,
-								bumpScale: bumpScale,
-								color: diffuseColor,
-								metalness: beta,
-								roughness: 1.0 - alpha,
-								shading: THREE.SmoothShading,
-								envMap: alphaIndex % 2 === 0 ? null : reflectionCube
-							} );
+								// basic monochromatic energy preservation
+								var diffuseColor = new THREE.Color().setHSL( alpha, 0.5, gamma * 0.5 );
 
-							var mesh = new THREE.Mesh( geometry, material );
+								var material = new THREE.MeshStandardMaterial( {
+									map: imgTexture,
+									bumpMap: imgTexture,
+									bumpScale: bumpScale,
+									color: diffuseColor,
+									metalness: beta,
+									roughness: 1.0 - alpha,
+									shading: THREE.SmoothShading,
+									envMap: index % 2 === 0 ? null : hdrCubeRenderTarget.texture						
+								} );
 
-							mesh.position.x = alpha * 400 - 200;
-							mesh.position.y = beta * 400 - 200;
-							mesh.position.z = gamma * 400 - 200;
+								index ++;
 
-							scene.add( mesh );
+								var mesh = new THREE.Mesh( geometry, material );
 
+								mesh.position.x = alpha * 400 - 200;
+								mesh.position.y = beta * 400 - 200;
+								mesh.position.z = gamma * 400 - 200;
+
+								scene.add( mesh );
+
+							}
+							
 						}
+						
+						index ++;
 
 					}
-
-				}
+				});
 
 				function addLabel( name, location ) {
 
@@ -172,6 +204,8 @@
 
 				renderer.gammaInput = true;
 				renderer.gammaOutput = true;
+				renderer.toneMapping = THREE.Uncharted2ToneMapping;
+				renderer.toneMappingExposure = 0.75;
 
 				//
 

+ 45 - 62
examples/webgl_morphtargets.html

@@ -6,64 +6,41 @@
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<style>
 			body {
-				background:#000;
-				color:#fff;
-				padding:0;
-				margin:0;
-				font-weight: bold;
-				overflow:hidden;
+				font-family: Monospace;
+				background-color: #f0f0f0;
+				margin: 0px;
+				overflow: hidden;
 			}
 
 			#info {
 				position: absolute;
-				top: 0px; width: 100%;
-				color: #ffffff;
+				top: 0px;
+				width: 100%;
 				padding: 5px;
-				font-family: Monospace;
-				font-size: 13px;
-				text-align: center;
-				z-index:100;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+				color: #ffffff;
 			}
 
-			#ctrl {
-				position: absolute;
-				top: 0px;
-				left: 0px;
-				width: 200px;
+			a {
 				color: #ffffff;
-				padding: 5px;
-				font-family: Monospace;
-				font-size: 13px;
-				z-index:100;
 			}
 
-			a { color:red }
-
 		</style>
 	</head>
 
 	<body>
+		<div id="container"></div>
 		<div id="info">
 			<a href="http://threejs.org" target="_blank">three.js</a> - WebGL morph target example
 		</div>
 
-
-		<div id="ctrl">
-			Use controls to change morph target influences:<br/>
-			<input type="range" value="0" min="0" max="100" onchange="mesh.morphTargetInfluences[ 0 ] = this.value/100;" />
-			<input type="range" value="0" min="0" max="100" onchange="mesh.morphTargetInfluences[ 1 ] = this.value/100;" />
-			<input type="range" value="0" min="0" max="100" onchange="mesh.morphTargetInfluences[ 2 ] = this.value/100;" />
-			<input type="range" value="0" min="0" max="100" onchange="mesh.morphTargetInfluences[ 3 ] = this.value/100;" />
-			<input type="range" value="0" min="0" max="100" onchange="mesh.morphTargetInfluences[ 4 ] = this.value/100;" />
-			<input type="range" value="0" min="0" max="100" onchange="mesh.morphTargetInfluences[ 5 ] = this.value/100;" />
-			<input type="range" value="0" min="0" max="100" onchange="mesh.morphTargetInfluences[ 6 ] = this.value/100;" />
-			<input type="range" value="0" min="0" max="100" onchange="mesh.morphTargetInfluences[ 7 ] = this.value/100;" />
-		</div>
-
-
 		<script src="../build/three.js"></script>
 
+		<script src="js/controls/OrbitControls.js"></script>
 		<script src="js/Detector.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
 		<script src="js/libs/stats.min.js"></script>
 
 		<script>
@@ -76,21 +53,14 @@
 
 			var geometry, objects;
 
-			var mouseX = 0, mouseY = 0;
-
 			var mesh;
-			var windowHalfX = window.innerWidth / 2;
-			var windowHalfY = window.innerHeight / 2;
-
-			document.addEventListener( 'mousemove', onDocumentMouseMove, false );
 
 			init();
 			animate();
 
 			function init() {
 
-				container = document.createElement( 'div' );
-				document.body.appendChild( container );
+				container = document.getElementById( 'container' );
 
 				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 15000 );
 				camera.position.z = 500;
@@ -138,6 +108,32 @@
 
 				//
 
+				var params = {
+					influence1: 0,
+					influence2: 0,
+					influence3: 0,
+					influence4: 0,
+					influence5: 0,
+					influence6: 0,
+					influence7: 0,
+					influence8: 0
+				};
+
+				var gui = new dat.GUI();
+
+				var folder = gui.addFolder( 'Morph Targets' );
+				folder.add( params, 'influence1', 0, 1 ).step( 0.01 ).onChange( function( value ) { mesh.morphTargetInfluences[ 0 ] = value; } );
+				folder.add( params, 'influence2', 0, 1 ).step( 0.01 ).onChange( function( value ) { mesh.morphTargetInfluences[ 1 ] = value; } );
+				folder.add( params, 'influence3', 0, 1 ).step( 0.01 ).onChange( function( value ) { mesh.morphTargetInfluences[ 2 ] = value; } );
+				folder.add( params, 'influence4', 0, 1 ).step( 0.01 ).onChange( function( value ) { mesh.morphTargetInfluences[ 3 ] = value; } );
+				folder.add( params, 'influence5', 0, 1 ).step( 0.01 ).onChange( function( value ) { mesh.morphTargetInfluences[ 4 ] = value; } );
+				folder.add( params, 'influence6', 0, 1 ).step( 0.01 ).onChange( function( value ) { mesh.morphTargetInfluences[ 5 ] = value; } );
+				folder.add( params, 'influence7', 0, 1 ).step( 0.01 ).onChange( function( value ) { mesh.morphTargetInfluences[ 6 ] = value; } );
+				folder.add( params, 'influence8', 0, 1 ).step( 0.01 ).onChange( function( value ) { mesh.morphTargetInfluences[ 7 ] = value; } );
+				folder.open();
+
+				//
+
 				renderer = new THREE.WebGLRenderer();
 				renderer.setClearColor( 0x222222 );
 				renderer.setPixelRatio( window.devicePixelRatio );
@@ -147,15 +143,16 @@
 
 				//
 
+				controls = new THREE.OrbitControls( camera, renderer.domElement );
+
+				//
+
 				window.addEventListener( 'resize', onWindowResize, false );
 
 			}
 
 			function onWindowResize() {
 
-				windowHalfX = window.innerWidth / 2;
-				windowHalfY = window.innerHeight / 2;
-
 				camera.aspect = window.innerWidth / window.innerHeight;
 				camera.updateProjectionMatrix();
 
@@ -163,13 +160,6 @@
 
 			}
 
-			function onDocumentMouseMove(event) {
-
-				mouseX = ( event.clientX - windowHalfX );
-				mouseY = ( event.clientY - windowHalfY ) * 2;
-
-			}
-
 			function animate() {
 
 				requestAnimationFrame( animate );
@@ -181,13 +171,6 @@
 
 				mesh.rotation.y += 0.01;
 
-				//mesh.morphTargetInfluences[ 0 ] = Math.sin( mesh.rotation.y ) * 0.5 + 0.5;
-
-				//camera.position.x += ( mouseX - camera.position.x ) * .005;
-				camera.position.y += ( - mouseY - camera.position.y ) * .01;
-
-				camera.lookAt( scene.position );
-
 				renderer.render( scene, camera );
 
 			}

+ 40 - 13
examples/webgl_shaders_ocean.html

@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<title>three.js webgl - geometry - terrain</title>
+		<title>three.js webgl - shaders - ocean</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 {
 				color: #000;
 				font-family:Monospace;
@@ -20,21 +21,18 @@
 				padding: 5px;
 			}
 
-			a {
-
-				color: #a06851;
-			}
-
 		</style>
 	</head>
 	<body>
 
-		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> - webgl ocean demo</div>
+		<div id="container"></div>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank">three.js</a> - webgl ocean demo
+		</div>
 
 		<script src="../build/three.js"></script>
 
 		<script src="js/controls/OrbitControls.js"></script>
-		<script src="js/Mirror.js"></script>
 		<script src="js/WaterShader.js"></script>
 
 		<script src="js/Detector.js"></script>
@@ -70,20 +68,27 @@
 
 			function init() {
 
-				container = document.createElement( 'div' );
-				document.body.appendChild( container );
+				container = document.getElementById( 'container' );
+
+				//
 
 				renderer = new THREE.WebGLRenderer();
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				container.appendChild( renderer.domElement );
 
+				//
+
 				scene = new THREE.Scene();
-				scene.fog = new THREE.FogExp2(0xaabbbb, 0.0001);
+				scene.fog = new THREE.FogExp2( 0xaabbbb, 0.0001 );
+
+				//
 
 				camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.5, 3000000 );
 				camera.position.set( 2000, 750, 2000 );
 
+				//
+
 				controls = new THREE.OrbitControls( camera, renderer.domElement );
 				controls.enablePan = false;
 				controls.minDistance = 1000.0;
@@ -93,10 +98,13 @@
 
 				scene.add( new THREE.AmbientLight( 0x444444 ) );
 
+				//
+
 				var light = new THREE.DirectionalLight( 0xffffbb, 1 );
 				light.position.set( - 1, 1, - 1 );
 				scene.add( light );
 
+				//
 
 				waterNormals = new THREE.TextureLoader().load( 'textures/waternormals.jpg' );
 				waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping;
@@ -123,8 +131,7 @@
 				mirrorMesh.rotation.x = - Math.PI * 0.5;
 				scene.add( mirrorMesh );
 
-
-				// load skybox
+				// skybox
 
 				var cubeMap = new THREE.CubeTexture( [] );
 				cubeMap.format = THREE.RGBFormat;
@@ -175,6 +182,7 @@
 
 				scene.add( skyBox );
 
+				//
 
 				var geometry = new THREE.IcosahedronGeometry( 400, 4 );
 
@@ -193,6 +201,24 @@
 				sphere = new THREE.Mesh( geometry, material );
 				scene.add( sphere );
 
+				//
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
 			}
 
 			//
@@ -201,6 +227,7 @@
 
 				requestAnimationFrame( animate );
 				render();
+				stats.update();
 
 			}
 

+ 5 - 12
examples/webvr_cubes.html

@@ -4,6 +4,8 @@
 		<title>three.js webvr - cubes</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
+		<!-- Origin Trial Token, feature = WebVR, origin = https://threejs.org, expires = 2017-04-17 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR" data-expires="2017-04-17" content="Aj3fKurj0eOvRSbpJ0NaqkV/AE2Y2KJIepdCGWNmCB/iMgbDV/rhFn8zFu6c6zZZlRGZNR7Xv/BEX544EeqLuQAAAABKeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUiIsImV4cGlyeSI6MTQ5MjQ3MzYwMH0=">
 		<style>
 			body {
 				font-family: Monospace;
@@ -130,20 +132,11 @@
 				controls = new THREE.VRControls( camera );
 				effect = new THREE.VREffect( renderer );
 
-				if ( navigator.getVRDisplays ) {
+				WEBVR.getVRDisplay( function ( display ) {
 
-					navigator.getVRDisplays()
-						.then( function ( displays ) {
-							effect.setVRDisplay( displays[ 0 ] );
-							controls.setVRDisplay( displays[ 0 ] );
-						} )
-						.catch( function () {
-							// no displays
-						} );
+					document.body.appendChild( WEBVR.getButton( display, renderer.domElement ) );
 
-					document.body.appendChild( WEBVR.getButton( effect ) );
-
-				}
+				} );
 
 				renderer.domElement.addEventListener( 'mousedown', onMouseDown, false );
 				renderer.domElement.addEventListener( 'mouseup', onMouseUp, false );

+ 5 - 2
examples/webvr_daydream.html

@@ -123,9 +123,12 @@
 
 				WEBVR.getVRDisplay( function ( display ) {
 
-					vrdisplay = display;
+					if ( display !== undefined ) {
 
-					camera = new THREE.WebVRCamera( display, renderer );
+						vrdisplay = display;
+						camera = new THREE.WebVRCamera( display, renderer );
+
+					}
 
 					document.body.appendChild( WEBVR.getButton( display, renderer.domElement ) );
 

+ 5 - 3
examples/webvr_panorama.html

@@ -4,6 +4,8 @@
 		<title>three.js webvr - panorama</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
+		<!-- Origin Trial Token, feature = WebVR, origin = https://threejs.org, expires = 2017-04-17 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR" data-expires="2017-04-17" content="Aj3fKurj0eOvRSbpJ0NaqkV/AE2Y2KJIepdCGWNmCB/iMgbDV/rhFn8zFu6c6zZZlRGZNR7Xv/BEX544EeqLuQAAAABKeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUiIsImV4cGlyeSI6MTQ5MjQ3MzYwMH0=">
 		<style>
 			html, body {
 				background-color: #000;
@@ -54,11 +56,11 @@
 			controls = new THREE.VRControls( camera );
 			effect = new THREE.VREffect( renderer );
 
-			if ( WEBVR.isAvailable() === true ) {
+			WEBVR.getVRDisplay( function ( display ) {
 
-				document.body.appendChild( WEBVR.getButton( effect ) );
+				document.body.appendChild( WEBVR.getButton( display, renderer.domElement ) );
 
-			}
+			} );
 
 			var textures = getTexturesFromAtlasFile( "textures/cube/sun_temple_stripe_stereo.jpg", 12 );
 

+ 58 - 43
examples/webvr_rollercoaster.html

@@ -4,6 +4,8 @@
 		<title>three.js webvr - roller coaster</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
+		<!-- Origin Trial Token, feature = WebVR, origin = https://threejs.org, expires = 2017-04-17 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR" data-expires="2017-04-17" content="Aj3fKurj0eOvRSbpJ0NaqkV/AE2Y2KJIepdCGWNmCB/iMgbDV/rhFn8zFu6c6zZZlRGZNR7Xv/BEX544EeqLuQAAAABKeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUiIsImV4cGlyeSI6MTQ5MjQ3MzYwMH0=">
 		<style>
 			body {
 				margin: 0px;
@@ -52,36 +54,45 @@
 			var train = new THREE.Object3D();
 			scene.add( train );
 
-			var camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 5000 );
+			var camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 0.1, 500 );
 			camera.rotation.y = Math.PI;
 			train.add( camera );
 
 			// environment
 
-			var geometry = new THREE.PlaneGeometry( 5000, 5000, 15, 15 );
+			var geometry = new THREE.PlaneBufferGeometry( 500, 500, 15, 15 );
 			geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
-			var material = new THREE.MeshLambertMaterial( { color: 0x407000, shading: THREE.FlatShading } );
 
-			for ( var i = 0; i < geometry.vertices.length; i ++ ) {
+			var positions = geometry.attributes.position.array;
+			var vertex = new THREE.Vector3();
 
-				var vertex = geometry.vertices[ i ];
+			for ( var i = 0; i < positions.length; i += 3 ) {
 
-				vertex.x += Math.random() * 100 - 50;
-				vertex.z += Math.random() * 100 - 50;
+				vertex.fromArray( positions, i );
 
-				var distance = ( vertex.distanceTo( scene.position ) / 5 ) - 250;
+				vertex.x += Math.random() * 10 - 5;
+				vertex.z += Math.random() * 10 - 5;
 
+				var distance = ( vertex.distanceTo( scene.position ) / 5 ) - 25;
 				vertex.y = Math.random() * Math.max( 0, distance );
 
+				vertex.toArray( positions, i );
+
 			}
 
-			geometry.computeFaceNormals();
+			geometry.computeVertexNormals();
+
+			var material = new THREE.MeshLambertMaterial( {
+				color: 0x407000
+			} );
 
 			var mesh = new THREE.Mesh( geometry, material );
 			scene.add( mesh );
 
 			var geometry = new TreesGeometry( mesh );
-			var material = new THREE.MeshBasicMaterial( { side: THREE.DoubleSide, vertexColors: THREE.VertexColors } );
+			var material = new THREE.MeshBasicMaterial( {
+				side: THREE.DoubleSide, vertexColors: THREE.VertexColors
+			} );
 			var mesh = new THREE.Mesh( geometry, material );
 			scene.add( mesh );
 
@@ -103,13 +114,13 @@
 
 					getPointAt: function ( t ) {
 
-						t *= Math.PI;
+						t = t * PI2;
 
-						var x = Math.sin(t * 4) * Math.cos(t * 6) * 1000;
-						var y = Math.cos(t * 8) * 80 + Math.cos(t * 20 * Math.sin(t)) * 40 + 200;
-						var z = Math.sin(t * 5) * Math.sin(t * 3) * 1000;
+						var x = Math.sin( t * 3 ) * Math.cos( t * 4 ) * 50;
+						var y = Math.sin( t * 10 ) * 2 + Math.cos( t * 17 ) * 2 + 5;
+						var z = Math.sin( t ) * Math.sin( t * 4 ) * 50;
 
-						return vector.set( x, y, z );
+						return vector.set( x, y, z ).multiplyScalar( 2 );
 
 					},
 
@@ -119,7 +130,8 @@
 						var t1 = Math.max( 0, t - delta );
 						var t2 = Math.min( 1, t + delta );
 
-						return vector2.copy( this.getPointAt ( t2 ) ).sub( this.getPointAt( t1 ) ).normalize();
+						return vector2.copy( this.getPointAt ( t2 ) )
+							.sub( this.getPointAt( t1 ) ).normalize();
 
 					}
 
@@ -128,46 +140,47 @@
 			} )();
 
 			var geometry = new RollerCoasterGeometry( curve, 1500 );
-			var material = new THREE.MeshStandardMaterial( {
-				roughness: 0.1,
-				metalness: 0,
+			var material = new THREE.MeshPhongMaterial( {
 				vertexColors: THREE.VertexColors
 			} );
 			var mesh = new THREE.Mesh( geometry, material );
 			scene.add( mesh );
 
 			var geometry = new RollerCoasterLiftersGeometry( curve, 100 );
-			var material = new THREE.MeshStandardMaterial( {
-				roughness: 0.1,
-				metalness: 0
-			} );
+			var material = new THREE.MeshPhongMaterial();
 			var mesh = new THREE.Mesh( geometry, material );
-			mesh.position.y = 1;
+			mesh.position.y = 0.1;
 			scene.add( mesh );
 
 			var geometry = new RollerCoasterShadowGeometry( curve, 500 );
-			var material = new THREE.MeshBasicMaterial( { color: 0x000000, opacity: 0.1, depthWrite: false, transparent: true } );
+			var material = new THREE.MeshBasicMaterial( {
+				color: 0x305000, depthWrite: false, transparent: true
+			} );
 			var mesh = new THREE.Mesh( geometry, material );
-			mesh.position.y = 1;
+			mesh.position.y = 0.1;
 			scene.add( mesh );
 
 			var funfairs = [];
 
 			//
 
-			var geometry = new THREE.CylinderGeometry( 100, 100, 50, 15 );
-			var material = new THREE.MeshLambertMaterial( { color: 0xff8080, shading: THREE.FlatShading } );
+			var geometry = new THREE.CylinderBufferGeometry( 10, 10, 5, 15 );
+			var material = new THREE.MeshLambertMaterial( {
+				color: 0xff8080, shading: THREE.FlatShading
+			} );
 			var mesh = new THREE.Mesh( geometry, material );
-			mesh.position.set( - 800, 100, - 700 );
+			mesh.position.set( - 80, 10, - 70 );
 			mesh.rotation.x = Math.PI / 2;
 			scene.add( mesh );
 
 			funfairs.push( mesh );
 
-			var geometry = new THREE.CylinderGeometry( 50, 60, 40, 10 );
-			var material = new THREE.MeshLambertMaterial( { color: 0x8080ff, shading: THREE.FlatShading } );
+			var geometry = new THREE.CylinderBufferGeometry( 5, 6, 4, 10 );
+			var material = new THREE.MeshLambertMaterial( {
+				color: 0x8080ff, shading: THREE.FlatShading
+			} );
 			var mesh = new THREE.Mesh( geometry, material );
-			mesh.position.set( 500, 20, 300 );
+			mesh.position.set( 50, 2, 30 );
 			scene.add( mesh );
 
 			funfairs.push( mesh );
@@ -177,11 +190,11 @@
 			var controls = new THREE.VRControls( camera );
 			var effect = new THREE.VREffect( renderer );
 
-			if ( WEBVR.isAvailable() === true ) {
+			WEBVR.getVRDisplay( function ( display ) {
 
-				document.body.appendChild( WEBVR.getButton( effect ) );
+				document.body.appendChild( WEBVR.getButton( display, renderer.domElement ) );
 
-			}
+			} );
 
 			//
 
@@ -204,36 +217,36 @@
 			var velocity = 0;
 			var progress = 0;
 
-			var clock = new THREE.Clock();
+			var prevTime = performance.now();
 
 			function animate( time ) {
 
 				effect.requestAnimationFrame( animate );
 
-				var delta = clock.getDelta() * 60;
+				var delta = time - prevTime;
 
 				for ( var i = 0; i < funfairs.length; i ++ ) {
 
-					funfairs[ i ].rotation.y = time * 0.0002;
+					funfairs[ i ].rotation.y = time * 0.0004;
 
 				}
 
 				//
 
-				progress += velocity * delta;
+				progress += velocity;
 				progress = progress % 1;
 
 				position.copy( curve.getPointAt( progress ) );
-				position.y += 3;
+				position.y += 0.3;
 
 				train.position.copy( position );
 
 				tangent.copy( curve.getTangentAt( progress ) );
 
-				velocity -= tangent.y * 0.0000015 * delta;
-				velocity = Math.max( velocity, 0.00004 );
+				velocity -= tangent.y * 0.0000001 * delta;
+				velocity = Math.max( 0.00004, Math.min( 0.0002, velocity ) );
 
-				train.lookAt( lookAt.copy( position ).sub( tangent ) );
+				train.lookAt( lookAt.copy( position ).add( tangent ) );
 
 				//
 
@@ -241,6 +254,8 @@
 
 				effect.render( scene, camera );
 
+				prevTime = time;
+
 			}
 
 			effect.requestAnimationFrame( animate );

+ 6 - 4
examples/webvr_shadow.html → examples/webvr_sandbox.html

@@ -1,9 +1,11 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<title>three.js webgl - effects - cardboard</title>
+		<title>three.js webvr - sandbox</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
+		<!-- Origin Trial Token, feature = WebVR, origin = https://threejs.org, expires = 2017-04-17 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR" data-expires="2017-04-17" content="Aj3fKurj0eOvRSbpJ0NaqkV/AE2Y2KJIepdCGWNmCB/iMgbDV/rhFn8zFu6c6zZZlRGZNR7Xv/BEX544EeqLuQAAAABKeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUiIsImV4cGlyeSI6MTQ5MjQ3MzYwMH0=">
 		<style>
 			body {
 				margin: 0px;
@@ -117,11 +119,11 @@
 				controls = new THREE.VRControls( camera );
 				effect = new THREE.VREffect( renderer );
 
-				if ( WEBVR.isAvailable() === true ) {
+				WEBVR.getVRDisplay( function ( display ) {
 
-					document.body.appendChild( WEBVR.getButton( effect ) );
+					document.body.appendChild( WEBVR.getButton( display, renderer.domElement ) );
 
-				}
+				} );
 
 				//
 

+ 5 - 3
examples/webvr_video.html

@@ -4,6 +4,8 @@
 		<title>three.js webvr - video</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
+		<!-- Origin Trial Token, feature = WebVR, origin = https://threejs.org, expires = 2017-04-17 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR" data-expires="2017-04-17" content="Aj3fKurj0eOvRSbpJ0NaqkV/AE2Y2KJIepdCGWNmCB/iMgbDV/rhFn8zFu6c6zZZlRGZNR7Xv/BEX544EeqLuQAAAABKeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUiIsImV4cGlyeSI6MTQ5MjQ3MzYwMH0=">
 		<style>
 			body {
 				font-family: Monospace;
@@ -148,11 +150,11 @@
 				effect.scale = 0; // video doesn't need eye separation
 				effect.setSize( window.innerWidth, window.innerHeight );
 
-				if ( WEBVR.isAvailable() === true ) {
+				WEBVR.getVRDisplay( function ( display ) {
 
-					document.body.appendChild( WEBVR.getButton( effect ) );
+					document.body.appendChild( WEBVR.getButton( display, renderer.domElement ) );
 
-				}
+				} );
 
 
 				//

+ 3 - 3
examples/webvr_vive.html

@@ -196,11 +196,11 @@
 
 				effect = new THREE.VREffect( renderer );
 
-				if ( WEBVR.isAvailable() === true ) {
+				WEBVR.getVRDisplay( function ( display ) {
 
-					document.body.appendChild( WEBVR.getButton( effect ) );
+					document.body.appendChild( WEBVR.getButton( display, renderer.domElement ) );
 
-				}
+				} );
 
 				//
 

+ 3 - 3
examples/webvr_vive_camerarig.html

@@ -209,11 +209,11 @@
 
 				effect = new THREE.VREffect( renderer );
 
-				if ( WEBVR.isAvailable() === true ) {
+				WEBVR.getVRDisplay( function ( display ) {
 
-					document.body.appendChild( WEBVR.getButton( effect ) );
+					document.body.appendChild( WEBVR.getButton( display, renderer.domElement ) );
 
-				}
+				} );
 
 				//
 

+ 3 - 3
examples/webvr_vive_dragging.html

@@ -195,11 +195,11 @@
 
 				effect = new THREE.VREffect( renderer );
 
-				if ( WEBVR.isAvailable() === true ) {
+				WEBVR.getVRDisplay( function ( display ) {
 
-					document.body.appendChild( WEBVR.getButton( effect ) );
+					document.body.appendChild( WEBVR.getButton( display, renderer.domElement ) );
 
-				}
+				} );
 
 				//
 

+ 3 - 3
examples/webvr_vive_paint.html

@@ -190,11 +190,11 @@
 
 				effect = new THREE.VREffect( renderer );
 
-				if ( WEBVR.isAvailable() === true ) {
+				WEBVR.getVRDisplay( function ( display ) {
 
-					document.body.appendChild( WEBVR.getButton( effect ) );
+					document.body.appendChild( WEBVR.getButton( display, renderer.domElement ) );
 
-				}
+				} );
 
 				//
 

+ 3 - 3
examples/webvr_vive_sculpt.html

@@ -168,11 +168,11 @@
 
 				effect = new THREE.VREffect( renderer );
 
-				if ( WEBVR.isAvailable() === true ) {
+				WEBVR.getVRDisplay( function ( display ) {
 
-					document.body.appendChild( WEBVR.getButton( effect ) );
+					document.body.appendChild( WEBVR.getButton( display, renderer.domElement ) );
 
-				}
+				} );
 
 				//
 

+ 17 - 0
src/Three.Legacy.js

@@ -776,6 +776,23 @@ Object.defineProperty( Skeleton.prototype, 'useVertexTexture', {
 
 } );
 
+Object.defineProperty( Curve.prototype, '__arcLengthDivisions', {
+
+	get: function () {
+
+		console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' );
+		return this.arcLengthDivisions;
+
+	},
+	set: function ( value ) {
+
+		console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' );
+		this.arcLengthDivisions = value;
+
+	}
+
+} );
+
 //
 
 PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) {

+ 13 - 3
src/animation/AnimationAction.js

@@ -66,8 +66,8 @@ function AnimationAction( mixer, clip, localRoot ) {
 
 	this.repetitions = Infinity; 		// no. of repetitions when looping
 
-	this.paused = false;				// false -> zero effective time scale
-	this.enabled = true;				// true -> zero effective weight
+	this.paused = false;				// true -> zero effective time scale
+	this.enabled = true;				// false -> zero effective weight
 
 	this.clampWhenFinished 	= false;	// keep feeding the last frame?
 
@@ -220,7 +220,7 @@ Object.assign( AnimationAction.prototype, {
 
 	// Time Scale Control
 
-	// set the weight stopping any scheduled warping
+	// set the time scale stopping any scheduled warping
 	// although .paused = true yields an effective time scale of zero, this
 	// method does *not* change .paused, because it would be confusing
 	setEffectiveTimeScale: function( timeScale ) {
@@ -327,8 +327,18 @@ Object.assign( AnimationAction.prototype, {
 	// Interna
 
 	_update: function( time, deltaTime, timeDirection, accuIndex ) {
+
 		// called by the mixer
 
+		if ( ! this.enabled ) {
+
+			// call ._updateWeight() to update ._effectiveWeight
+
+			this._updateWeight( time );
+			return;
+
+		}
+
 		var startTime = this._startTime;
 
 		if ( startTime !== null ) {

+ 1 - 5
src/animation/AnimationMixer.js

@@ -610,11 +610,7 @@ Object.assign( AnimationMixer.prototype, EventDispatcher.prototype, {
 
 			var action = actions[ i ];
 
-			if ( action.enabled ) {
-
-				action._update( time, deltaTime, timeDirection, accuIndex );
-
-			}
+			action._update( time, deltaTime, timeDirection, accuIndex );
 
 		}
 

+ 1 - 1
src/core/BufferGeometry.js

@@ -881,7 +881,7 @@ Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, {
 
 		var data = {
 			metadata: {
-				version: 4.4,
+				version: 4.5,
 				type: 'BufferGeometry',
 				generator: 'BufferGeometry.toJSON'
 			}

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