瀏覽代碼

Merge branch 'dev' into adsk-dev

Conflicts:
	examples/js/controls/TransformControls.js
Daniel Taub 11 年之前
父節點
當前提交
3367742cb5
共有 100 個文件被更改,包括 2310 次插入1309 次删除
  1. 721 65
      build/three.js
  2. 140 143
      build/three.min.js
  3. 1 1
      docs/api/extras/helpers/ArrowHelper.html
  4. 2 2
      docs/api/materials/Material.html
  5. 1 1
      docs/api/renderers/WebGLRenderer.html
  6. 1 1
      docs/api/scenes/Scene.html
  7. 8 0
      editor/css/dark.css
  8. 8 0
      editor/css/light.css
  9. 9 8
      editor/index.html
  10. 16 6
      editor/js/Config.js
  11. 13 92
      editor/js/Editor.js
  12. 15 0
      editor/js/Loader.js
  13. 54 61
      editor/js/Menubar.File.js
  14. 34 0
      editor/js/Menubar.Play.js
  15. 1 0
      editor/js/Menubar.js
  16. 103 0
      editor/js/Player.js
  17. 7 1
      editor/js/Sidebar.Animation.js
  18. 7 1
      editor/js/Sidebar.Geometry.js
  19. 61 27
      editor/js/Sidebar.Material.js
  20. 10 15
      editor/js/Sidebar.Object3D.js
  21. 6 1
      editor/js/Sidebar.Renderer.js
  22. 29 24
      editor/js/Sidebar.Scene.js
  23. 82 0
      editor/js/Sidebar.Script.js
  24. 1 0
      editor/js/Sidebar.js
  25. 5 0
      editor/js/Toolbar.js
  26. 17 11
      editor/js/Viewport.js
  27. 46 12
      editor/js/libs/ui.js
  28. 1 1
      examples/css3d_periodictable.html
  29. 1 0
      examples/index.html
  30. 14 11
      examples/js/controls/EditorControls.js
  31. 8 0
      examples/js/controls/OrbitControls.js
  32. 10 7
      examples/js/controls/TransformControls.js
  33. 0 66
      examples/js/exporters/BufferGeometryExporter.js
  34. 0 192
      examples/js/exporters/GeometryExporter.js
  35. 0 17
      examples/js/exporters/HTMLExporter.js
  36. 0 113
      examples/js/exporters/MaterialExporter.js
  37. 0 268
      examples/js/exporters/ObjectExporter.js
  38. 18 31
      examples/js/libs/pnltri.min.js
  39. 3 1
      examples/js/loaders/AWDLoader.js
  40. 1 1
      examples/js/loaders/ColladaLoader.js
  41. 3 13
      examples/js/loaders/gltf/glTFLoader.js
  42. 14 5
      examples/js/renderers/WebGLDeferredRenderer.js
  43. 0 5
      examples/js/shaders/FXAAShader.js
  44. 1 1
      examples/misc_controls_transform.html
  45. 2 2
      examples/webgl_buffergeometry.html
  46. 2 2
      examples/webgl_buffergeometry_uint.html
  47. 2 1
      examples/webgl_decals.html
  48. 7 11
      examples/webgl_interactive_buffergeometry.html
  49. 0 56
      examples/webgl_loader_scene.html
  50. 1 4
      examples/webgl_materials_video.html
  51. 0 3
      examples/webgl_postprocessing_dof.html
  52. 32 7
      examples/webgl_shaders_ocean.html
  53. 1 0
      examples/webgl_shadowmap.html
  54. 2 1
      examples/webgl_shadowmap_performance.html
  55. 2 0
      src/cameras/Camera.js
  56. 2 0
      src/cameras/CubeCamera.js
  57. 2 0
      src/cameras/OrthographicCamera.js
  58. 2 0
      src/cameras/PerspectiveCamera.js
  59. 13 0
      src/core/BufferAttribute.js
  60. 60 0
      src/core/BufferGeometry.js
  61. 286 0
      src/core/Geometry.js
  62. 162 0
      src/core/Object3D.js
  63. 22 0
      src/core/Script.js
  64. 16 7
      src/extras/animation/Animation.js
  65. 2 0
      src/extras/geometries/BoxGeometry.js
  66. 2 0
      src/extras/geometries/CircleGeometry.js
  67. 2 0
      src/extras/geometries/CylinderGeometry.js
  68. 2 0
      src/extras/geometries/ExtrudeGeometry.js
  69. 6 5
      src/extras/geometries/IcosahedronGeometry.js
  70. 10 0
      src/extras/geometries/LatheGeometry.js
  71. 7 0
      src/extras/geometries/OctahedronGeometry.js
  72. 8 0
      src/extras/geometries/ParametricGeometry.js
  73. 2 0
      src/extras/geometries/PlaneGeometry.js
  74. 9 0
      src/extras/geometries/PolyhedronGeometry.js
  75. 11 0
      src/extras/geometries/RingGeometry.js
  76. 2 0
      src/extras/geometries/ShapeGeometry.js
  77. 2 0
      src/extras/geometries/SphereGeometry.js
  78. 7 0
      src/extras/geometries/TetrahedronGeometry.js
  79. 2 0
      src/extras/geometries/TextGeometry.js
  80. 2 0
      src/extras/geometries/TorusGeometry.js
  81. 2 0
      src/extras/geometries/TorusKnotGeometry.js
  82. 2 0
      src/extras/geometries/TubeGeometry.js
  83. 2 0
      src/lights/AmbientLight.js
  84. 2 0
      src/lights/AreaLight.js
  85. 2 0
      src/lights/DirectionalLight.js
  86. 2 0
      src/lights/HemisphereLight.js
  87. 2 0
      src/lights/Light.js
  88. 2 0
      src/lights/PointLight.js
  89. 2 0
      src/lights/SpotLight.js
  90. 1 0
      src/loaders/MaterialLoader.js
  91. 1 0
      src/loaders/ObjectLoader.js
  92. 2 0
      src/materials/LineBasicMaterial.js
  93. 2 0
      src/materials/LineDashedMaterial.js
  94. 73 0
      src/materials/Material.js
  95. 2 0
      src/materials/MeshBasicMaterial.js
  96. 2 0
      src/materials/MeshDepthMaterial.js
  97. 39 6
      src/materials/MeshFaceMaterial.js
  98. 2 0
      src/materials/MeshLambertMaterial.js
  99. 2 0
      src/materials/MeshNormalMaterial.js
  100. 2 0
      src/materials/MeshPhongMaterial.js

文件差異過大導致無法顯示
+ 721 - 65
build/three.js


文件差異過大導致無法顯示
+ 140 - 143
build/three.min.js


+ 1 - 1
docs/api/extras/helpers/ArrowHelper.html

@@ -32,7 +32,7 @@
 
 		<h3>[name]([page:Vector3 dir], [page:Vector3 origin], [page:Number length], [page:Number hex], [page:Number headLength], [page:Number headWidth] )</h3>
 		<div>
-		dir -- Vector3 -- direction from origin <br />
+		dir -- Vector3 -- direction from origin. Must be a unit vector. <br />
 		origin -- Vector3 <br />
 		length -- scalar <br />
 		hex -- hexadecimal value to define color ex:0xffff00<br />

+ 2 - 2
docs/api/materials/Material.html

@@ -106,9 +106,9 @@
 		Sets the alpha value to be used when running an alpha test. Default is *0*.
 		</div>
 
-		<h3>.[page:Boolean overdraw]</h3>
+		<h3>.[page:Float overdraw]</h3>
 		<div>
-		Enables/disables overdraw. If enabled, polygons are drawn slightly bigger in order to fix antialiasing gaps when using the [page:CanvasRenderer]. Default is *false*.
+		Amount of triangle expansion at draw time. This is a workaround for cases when gaps appear between triangles when using [page:CanvasRenderer]. *0.5* tends to give good results across browsers. Default is *0*.
 		</div>
 
 		<h3>.[page:Boolean visible]</h3>

+ 1 - 1
docs/api/renderers/WebGLRenderer.html

@@ -218,7 +218,7 @@
 
 		<h3>.clear( [page:Boolean color], [page:Boolean depth], [page:Boolean stencil] )</h3>
 		<div>Tells the renderer to clear its color, depth or stencil drawing buffer(s).</div>
-		<div>If no parameters are passed, no buffer will be cleared.</div>
+		<div>Arguments default to true.</div>
 
 		<h3>.addPostPlugin( plugin )</h3>
 		<div>Initialises the postprocessing plugin, and adds it to the renderPluginsPost array.</div>

+ 1 - 1
docs/api/scenes/Scene.html

@@ -34,7 +34,7 @@
 		
 		<h3>.[page:boolean autoUpdate]</h3>
 		<div>
-		Default is true. If set, then the renderer checks every frame if the scene and it's objects needs matrix updates. 
+		Default is true. If set, then the renderer checks every frame if the scene and its objects needs matrix updates. 
 		When it isn't, then you have to maintain all matrices in the scene yourself.  
 		</div> 
 

+ 8 - 0
editor/css/dark.css

@@ -117,6 +117,14 @@ input.Number {
 	bottom: 32px;
 }
 
+#player {
+	position: absolute;
+	top: 32px;
+	left: 0px;
+	right: 300px;
+	bottom: 32px;
+}
+
 #menubar {
 	position: absolute;
 	width: 100%;

+ 8 - 0
editor/css/light.css

@@ -91,6 +91,14 @@ input.Number {
 	bottom: 32px;
 }
 
+#player {
+	position: absolute;
+	top: 32px;
+	left: 0px;
+	right: 300px;
+	bottom: 32px;
+}
+
 #menubar {
 	position: absolute;
 	width: 100%;

+ 9 - 8
editor/index.html

@@ -14,6 +14,7 @@
 
 		<script src="../examples/js/controls/EditorControls.js"></script>
 		<script src="../examples/js/controls/TransformControls.js"></script>
+		<script src="../examples/js/loaders/AWDLoader.js"></script>
 		<script src="../examples/js/loaders/BabylonLoader.js"></script>
 		<script src="../examples/js/loaders/ColladaLoader.js"></script>
 		<script src="../examples/js/loaders/OBJLoader.js"></script>
@@ -26,7 +27,6 @@
 		<script src="../examples/js/loaders/ctm/lzma.js"></script>
 		<script src="../examples/js/loaders/ctm/ctm.js"></script>
 		<script src="../examples/js/loaders/ctm/CTMLoader.js"></script>
-		<script src="../examples/js/exporters/SceneExporter.js"></script>
 		<script src="../examples/js/exporters/OBJExporter.js"></script>
 		<script src="../examples/js/exporters/STLExporter.js"></script>
 		<script src="../examples/js/renderers/RaytracingRenderer.js"></script>
@@ -35,11 +35,6 @@
 
 		<!-- WIP -->
 
-		<script src="../examples/js/exporters/BufferGeometryExporter.js"></script>
-		<script src="../examples/js/exporters/TypedGeometryExporter.js"></script>
-		<script src="../examples/js/exporters/GeometryExporter.js"></script>
-		<script src="../examples/js/exporters/MaterialExporter.js"></script>
-		<script src="../examples/js/exporters/ObjectExporter.js"></script>
 		<script src="../examples/js/renderers/WebGLRenderer3.js"></script>
 
 		<script src="js/libs/signals.min.js"></script>
@@ -48,6 +43,8 @@
 
 		<script src="js/Storage.js"></script>
 
+		<script src="js/Player.js"></script>
+
 		<script src="js/Editor.js"></script>
 		<script src="js/Config.js"></script>
 		<script src="js/Loader.js"></script>
@@ -55,6 +52,7 @@
 		<script src="js/Menubar.File.js"></script>
 		<script src="js/Menubar.Edit.js"></script>
 		<script src="js/Menubar.Add.js"></script>
+		<script src="js/Menubar.Play.js"></script>
 		<script src="js/Menubar.View.js"></script>
 		<script src="js/Menubar.Help.js"></script>
 		<script src="js/Sidebar.js"></script>
@@ -74,6 +72,7 @@
 		<script src="js/Sidebar.Geometry.TorusGeometry.js"></script>
 		<script src="js/Sidebar.Geometry.TorusKnotGeometry.js"></script>
 		<script src="js/Sidebar.Material.js"></script>
+		<script src="js/Sidebar.Script.js"></script>
 		<script src="js/Toolbar.js"></script>
 		<script src="js/Viewport.js"></script>
 
@@ -87,6 +86,9 @@
 			var viewport = new Viewport( editor ).setId( 'viewport' );
 			document.body.appendChild( viewport.dom );
 
+			var player = new Player( editor ).setId( 'player' );
+			document.body.appendChild( player.dom );
+
 			var toolbar = new Toolbar( editor ).setId( 'toolbar' )
 			document.body.appendChild( toolbar.dom );
 
@@ -129,7 +131,6 @@
 				//
 
 				var timeout;
-				var exporter = new THREE.ObjectExporter();
 
 				var saveState = function ( scene ) {
 
@@ -137,7 +138,7 @@
 
 					timeout = setTimeout( function () {
 
-						editor.storage.set( exporter.parse( editor.scene ) );
+						editor.storage.set( editor.scene.toJSON() );
 
 					}, 1000 );
 

+ 16 - 6
editor/js/Config.js

@@ -3,14 +3,24 @@ var Config = function () {
 	var name = 'threejs-editor';
 
 	var storage = {
-		theme: 'css/light.css',
-		camera: {
-			position: [ 500, 250, 500 ],
-			target: [ 0, 0, 0 ] 
-		}
+		'theme': 'css/light.css',
+		'camera/position': [ 500, 250, 500 ],
+		'camera/target': [ 0, 0, 0 ],
+
+		'ui/sidebar/animation/collapsed': true,
+		'ui/sidebar/geometry/collapsed': true,
+		'ui/sidebar/material/collapsed': true,
+		'ui/sidebar/object3d/collapsed': false,
+		'ui/sidebar/renderer/collapsed': true,
+		'ui/sidebar/scene/collapsed': false,
+		'ui/sidebar/script/collapsed': true
 	};
 
-	if ( window.localStorage[ name ] !== undefined ) {
+	if ( window.localStorage[ name ] === undefined ) {
+
+		window.localStorage[ name ] = JSON.stringify( storage );
+
+	} else {
 
 		var data = JSON.parse( window.localStorage[ name ] );
 

+ 13 - 92
editor/js/Editor.js

@@ -4,6 +4,11 @@ var Editor = function () {
 
 	this.signals = {
 
+		// player
+
+		startPlayer: new SIGNALS.Signal(),
+		stopPlayer: new SIGNALS.Signal(),
+
 		// actions
 
 		playAnimation: new SIGNALS.Signal(),
@@ -36,7 +41,9 @@ var Editor = function () {
 		fogTypeChanged: new SIGNALS.Signal(),
 		fogColorChanged: new SIGNALS.Signal(),
 		fogParametersChanged: new SIGNALS.Signal(),
-		windowResize: new SIGNALS.Signal()
+		windowResize: new SIGNALS.Signal(),
+
+		showGridChanged: new SIGNALS.Signal()
 
 	};
 	
@@ -44,13 +51,18 @@ var Editor = function () {
 	this.storage = new Storage();
 	this.loader = new Loader( this );
 
+	this.camera = new THREE.PerspectiveCamera( 50, 1, 1, 5000 );
 	this.scene = new THREE.Scene();
+	this.scene.name = 'Scene';
+	
 	this.sceneHelpers = new THREE.Scene();
 
 	this.object = {};
 	this.geometries = {};
 	this.materials = {};
 	this.textures = {};
+	
+	this.scripts = {};
 
 	this.selected = null;
 	this.helpers = {};
@@ -321,97 +333,6 @@ Editor.prototype = {
 
 		this.select( null );
 
-	},
-
-	// utils
-
-	getObjectType: function ( object ) {
-
-		var types = {
-
-			'Scene': THREE.Scene,
-			'PerspectiveCamera': THREE.PerspectiveCamera,
-			'AmbientLight': THREE.AmbientLight,
-			'DirectionalLight': THREE.DirectionalLight,
-			'HemisphereLight': THREE.HemisphereLight,
-			'PointLight': THREE.PointLight,
-			'SpotLight': THREE.SpotLight,
-			'SkinnedMesh': THREE.SkinnedMesh,
-			'Mesh': THREE.Mesh,
-			'Sprite': THREE.Sprite,
-			'Group': THREE.Group,
-			'Object3D': THREE.Object3D
-
-		};
-
-		for ( var type in types ) {
-
-			if ( object instanceof types[ type ] ) return type;
-
-		}
-
-	},
-
-	getGeometryType: function ( geometry ) {
-
-		var types = {
-
-			'BoxGeometry': THREE.BoxGeometry,
-			'CircleGeometry': THREE.CircleGeometry,
-			'CylinderGeometry': THREE.CylinderGeometry,
-			'ExtrudeGeometry': THREE.ExtrudeGeometry,
-			'IcosahedronGeometry': THREE.IcosahedronGeometry,
-			'LatheGeometry': THREE.LatheGeometry,
-			'OctahedronGeometry': THREE.OctahedronGeometry,
-			'ParametricGeometry': THREE.ParametricGeometry,
-			'PlaneGeometry': THREE.PlaneGeometry,
-			'PolyhedronGeometry': THREE.PolyhedronGeometry,
-			'ShapeGeometry': THREE.ShapeGeometry,
-			'SphereGeometry': THREE.SphereGeometry,
-			'TetrahedronGeometry': THREE.TetrahedronGeometry,
-			'TextGeometry': THREE.TextGeometry,
-			'TorusGeometry': THREE.TorusGeometry,
-			'TorusKnotGeometry': THREE.TorusKnotGeometry,
-			'TubeGeometry': THREE.TubeGeometry,
-			'Geometry': THREE.Geometry,
-			'BufferGeometry': THREE.BufferGeometry
-
-		};
-
-		for ( var type in types ) {
-
-			if ( geometry instanceof types[ type ] ) return type;
-
-		}
-
-	},
-
-	getMaterialType: function ( material ) {
-
-		var types = {
-
-			'LineBasicMaterial': THREE.LineBasicMaterial,
-			'LineDashedMaterial': THREE.LineDashedMaterial,
-			'MeshBasicMaterial': THREE.MeshBasicMaterial,
-			'MeshDepthMaterial': THREE.MeshDepthMaterial,
-			'MeshFaceMaterial': THREE.MeshFaceMaterial,
-			'MeshLambertMaterial': THREE.MeshLambertMaterial,
-			'MeshNormalMaterial': THREE.MeshNormalMaterial,
-			'MeshPhongMaterial': THREE.MeshPhongMaterial,
-			'PointCloudMaterial': THREE.PointCloudMaterial,
-			'ShaderMaterial': THREE.ShaderMaterial,
-			'SpriteCanvasMaterial': THREE.SpriteCanvasMaterial,
-			'SpriteMaterial': THREE.SpriteMaterial,
-			'Material': THREE.Material
-
-		};
-
-		for ( var type in types ) {
-
-			if ( material instanceof types[ type ] ) return type;
-
-		}
-
 	}
 
 }

+ 15 - 0
editor/js/Loader.js

@@ -10,6 +10,21 @@ var Loader = function ( editor ) {
 
 		switch ( extension ) {
 
+			case 'awd':
+
+				var reader = new FileReader();
+				reader.addEventListener( 'load', function ( event ) {
+
+					var loader = new THREE.AWDLoader();
+					var scene = loader.parse( event.target.result );
+
+					editor.setScene( scene );
+
+				}, false );
+				reader.readAsArrayBuffer( file );
+
+				break;
+
 			case 'babylon':
 
 				var reader = new FileReader();

+ 54 - 61
editor/js/Menubar.File.js

@@ -86,15 +86,11 @@ Menubar.File = function ( editor ) {
 
 		}
 
-		if ( geometry instanceof THREE.BufferGeometry ) {
-
-			exportGeometry( THREE.BufferGeometryExporter );
-
-		} else if ( geometry instanceof THREE.Geometry ) {
-
-			exportGeometry( THREE.GeometryExporter );
+		var output = geometry.toJSON();
+		output = JSON.stringify( output, null, '\t' );
+		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
 
-		}
+		exportString( output );
 
 	} );
 	options.add( option );
@@ -106,14 +102,20 @@ Menubar.File = function ( editor ) {
 	option.setTextContent( 'Export Object' );
 	option.onClick( function () {
 
-		if ( editor.selected === null ) {
+		var object = editor.selected;
+
+		if ( object === null ) {
 
 			alert( 'No object selected' );
 			return;
 
 		}
 
-		exportObject( THREE.ObjectExporter );
+		var output = object.toJSON();
+		output = JSON.stringify( output, null, '\t' );
+		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
+
+		exportString( output );
 
 	} );
 	options.add( option );
@@ -125,7 +127,11 @@ Menubar.File = function ( editor ) {
 	option.setTextContent( 'Export Scene' );
 	option.onClick( function () {
 
-		exportScene( THREE.ObjectExporter );
+		var output = editor.scene.toJSON();
+		output = JSON.stringify( output, null, '\t' );
+		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
+
+		exportString( output );
 
 	} );
 	options.add( option );
@@ -137,7 +143,27 @@ Menubar.File = function ( editor ) {
 	option.setTextContent( 'Export OBJ' );
 	option.onClick( function () {
 
-		exportGeometry( THREE.OBJExporter );
+		var object = editor.selected;
+
+		if ( object === null ) {
+
+			alert( 'No object selected.' );
+			return;
+
+		}
+
+		var geometry = object.geometry;
+
+		if ( geometry === undefined ) {
+
+			alert( 'The selected object doesn\'t have geometry.' );
+			return;
+
+		}
+
+		var exporter = new OBJExporter();
+
+		exportString( exporter.parse( geometry ) );
 
 	} );
 	options.add( option );
@@ -149,7 +175,9 @@ Menubar.File = function ( editor ) {
 	option.setTextContent( 'Export STL' );
 	option.onClick( function () {
 
-		exportScene( THREE.STLExporter );
+		var exporter = new STLExporter();
+
+		exportString( exporter.parse( editor.scene ) );
 
 	} );
 	options.add( option );
@@ -158,6 +186,8 @@ Menubar.File = function ( editor ) {
 
 	options.add( new UI.HorizontalRule() );
 
+	/*
+
 	// Test
 
 	var option = new UI.Panel();
@@ -170,61 +200,24 @@ Menubar.File = function ( editor ) {
 
 	} );
 	options.add( option );
+	*/
 
+	// Publish
 
-	//
-	
-	var exportGeometry = function ( exporterClass ) {
-
-		var object = editor.selected;
-		var exporter = new exporterClass();
-
-		var output = exporter.parse( object.geometry );
-
-		if ( exporter instanceof THREE.BufferGeometryExporter ||
-		     exporter instanceof THREE.GeometryExporter ) {
-
-			output = JSON.stringify( output, null, '\t' );
-			output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
-
-		}
-
-		var blob = new Blob( [ output ], { type: 'text/plain' } );
-		var objectURL = URL.createObjectURL( blob );
-
-		window.open( objectURL, '_blank' );
-		window.focus();
-
-	};
-
-	var exportObject = function ( exporterClass ) {
-
-		var object = editor.selected;
-		var exporter = new exporterClass();
-
-		var output = JSON.stringify( exporter.parse( object ), null, '\t' );
-		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
-
-		var blob = new Blob( [ output ], { type: 'text/plain' } );
-		var objectURL = URL.createObjectURL( blob );
-
-		window.open( objectURL, '_blank' );
-		window.focus();
-
-	};
-
-	var exportScene = function ( exporterClass ) {
+	var option = new UI.Panel();
+	option.setClass( 'option' );
+	option.setTextContent( 'Publish' );
+	option.onClick( function () {
 
-		var exporter = new exporterClass();
+		alert( 'Not yet...' );
 
-		var output = exporter.parse( editor.scene );
+	} );
+	options.add( option );
 
-		if ( exporter instanceof THREE.ObjectExporter ) {
 
-			output = JSON.stringify( output, null, '\t' );
-			output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
+	//
 
-		}
+	var exportString = function ( output ) {
 
 		var blob = new Blob( [ output ], { type: 'text/plain' } );
 		var objectURL = URL.createObjectURL( blob );

+ 34 - 0
editor/js/Menubar.Play.js

@@ -0,0 +1,34 @@
+Menubar.Play = function ( editor ) {
+
+	var signals = editor.signals;
+
+	var container = new UI.Panel();
+	container.setClass( 'menu' );
+
+	var isPlaying = false;
+
+	var title = new UI.Panel();
+	title.setClass( 'title' );
+	title.setTextContent( 'Play' );
+	title.onClick( function () {
+
+		if ( isPlaying === false ) {
+
+			isPlaying = true;
+			title.setTextContent( 'Stop' );
+			signals.startPlayer.dispatch( editor.scene.toJSON() );
+
+		} else {
+
+			isPlaying = false;
+			title.setTextContent( 'Play' );
+			signals.stopPlayer.dispatch();
+
+		}
+
+	} );
+	container.add( title );
+
+	return container;
+
+};

+ 1 - 0
editor/js/Menubar.js

@@ -5,6 +5,7 @@ var Menubar = function ( editor ) {
 	container.add( new Menubar.File( editor ) );
 	container.add( new Menubar.Edit( editor ) );
 	container.add( new Menubar.Add( editor ) );
+	container.add( new Menubar.Play( editor ) );
 	container.add( new Menubar.View( editor ) );
 	container.add( new Menubar.Help( editor ) );
 

+ 103 - 0
editor/js/Player.js

@@ -0,0 +1,103 @@
+var Player = function ( editor ) {
+
+	var signals = editor.signals;
+
+	var container = new UI.Panel();
+	container.setPosition( 'absolute' );
+	container.setDisplay( 'none' );
+
+	//
+
+	var camera, scene, renderer;
+	var scripts;
+
+	//
+
+	var load = function ( json ) {
+
+		if ( renderer !== undefined ) {
+
+			container.dom.removeChild( renderer.domElement );
+
+		}
+
+		renderer = new THREE.WebGLRenderer( { antialias: true } );
+		renderer.setSize( container.dom.offsetWidth, container.dom.offsetHeight );
+		container.dom.appendChild( renderer.domElement );
+
+		camera = editor.camera.clone();
+
+		scene = new THREE.ObjectLoader().parse( json );
+
+		//
+
+		scripts = [];
+
+		scene.traverse( function ( child ) {
+
+			if ( child.script !== undefined ) {
+
+				var script = new Function( 'scene', child.script.source ).bind( child );
+				scripts.push( script );
+
+			}
+
+		} );
+
+	};
+
+	var request;
+
+	var play = function () {
+
+		request = requestAnimationFrame( play );
+
+		update();
+		render();
+
+	};
+
+	var stop = function () {
+
+		cancelAnimationFrame( request );
+
+	};
+
+	var render = function () {
+
+		renderer.render( scene, camera );
+
+	};
+
+	var update = function () {
+
+		for ( var i = 0; i < scripts.length; i ++ ) {
+
+			scripts[ i ]( scene );
+
+		}
+
+		render();
+
+	};
+
+	signals.startPlayer.add( function ( json ) {
+
+		container.setDisplay( '' );
+
+		load( json );
+		play();
+
+	} );
+
+	signals.stopPlayer.add( function () {
+
+		container.setDisplay( 'none' );
+
+		stop();
+
+	} );
+
+	return container;
+
+};

+ 7 - 1
editor/js/Sidebar.Animation.js

@@ -6,9 +6,15 @@ Sidebar.Animation = function ( editor ) {
 	var possibleAnimations = {};
 
 	var container = new UI.CollapsiblePanel();
+	container.setCollapsed( editor.config.getKey( 'ui/sidebar/animation/collapsed' ) );
+	container.onCollapsedChange( function ( boolean ) {
+
+		editor.config.setKey( 'ui/sidebar/animation/collapsed', boolean );
+
+	} );
 	container.setDisplay( 'none' );
 
-	container.addStatic( new UI.Text( 'ANIMATION' ) );
+	container.addStatic( new UI.Text( 'Animation' ).setTextTransform( 'uppercase' ) );
 	container.add( new UI.Break() );
 
 	var animationsRow = new UI.Panel();

+ 7 - 1
editor/js/Sidebar.Geometry.js

@@ -3,6 +3,12 @@ Sidebar.Geometry = function ( editor ) {
 	var signals = editor.signals;
 
 	var container = new UI.CollapsiblePanel();
+	container.setCollapsed( editor.config.getKey( 'ui/sidebar/geometry/collapsed' ) );
+	container.onCollapsedChange( function ( boolean ) {
+
+		editor.config.setKey( 'ui/sidebar/geometry/collapsed', boolean );
+
+	} );
 	container.setDisplay( 'none' );
 
 	var geometryType = new UI.Text().setTextTransform( 'uppercase' );
@@ -66,7 +72,7 @@ Sidebar.Geometry = function ( editor ) {
 
 			container.setDisplay( 'block' );
 
-			geometryType.setValue( editor.getGeometryType( object.geometry ) );
+			geometryType.setValue( geometry.type );
 
 			geometryUUID.setValue( geometry.uuid );
 			geometryName.setValue( geometry.name );

+ 61 - 27
editor/js/Sidebar.Material.js

@@ -21,6 +21,12 @@ Sidebar.Material = function ( editor ) {
 	};
 
 	var container = new UI.CollapsiblePanel();
+	container.setCollapsed( editor.config.getKey( 'ui/sidebar/material/collapsed' ) );
+	container.onCollapsedChange( function ( boolean ) {
+
+		editor.config.setKey( 'ui/sidebar/material/collapsed', boolean );
+
+	} );
 	container.setDisplay( 'none' );
 	container.dom.classList.add( 'Material' );
 
@@ -278,6 +284,38 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialEnvMapRow );
 
+	// side
+
+	var materialSideRow = new UI.Panel();
+	var materialSide = new UI.Select().setOptions( {
+
+		0: 'Front',
+		1: 'Back',
+		2: 'Double'
+
+	} ).setWidth( '150px' ).setColor( '#444' ).setFontSize( '12px' ).onChange( update );
+
+	materialSideRow.add( new UI.Text( 'Side' ).setWidth( '90px' ) );
+	materialSideRow.add( materialSide );
+
+	container.add( materialSideRow );
+
+	// shading
+
+	var materialShadingRow = new UI.Panel();
+	var materialShading = new UI.Select().setOptions( {
+
+		0: 'No',
+		1: 'Flat',
+		2: 'Smooth'
+
+	} ).setWidth( '150px' ).setColor( '#444' ).setFontSize( '12px' ).onChange( update );
+
+	materialShadingRow.add( new UI.Text( 'Shading' ).setWidth( '90px' ) );
+	materialShadingRow.add( materialShading );
+
+	container.add( materialShadingRow );
+
 	// blending
 
 	var materialBlendingRow = new UI.Panel();
@@ -296,23 +334,6 @@ Sidebar.Material = function ( editor ) {
 	materialBlendingRow.add( materialBlending );
 
 	container.add( materialBlendingRow );
-
-	// side
-
-	var materialSideRow = new UI.Panel();
-	var materialSide = new UI.Select().setOptions( {
-
-		0: 'Front',
-		1: 'Back',
-		2: 'Double'
-
-	} ).setWidth( '150px' ).setColor( '#444' ).setFontSize( '12px' ).onChange( update );
-
-	materialSideRow.add( new UI.Text( 'Side' ).setWidth( '90px' ) );
-	materialSideRow.add( materialSide );
-
-	container.add( materialSideRow );
-
 	// opacity
 
 	var materialOpacityRow = new UI.Panel();
@@ -590,15 +611,21 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
-			if ( material.blending !== undefined ) {
+			if ( material.side !== undefined ) {
 
-				material.blending = parseInt( materialBlending.getValue() );
+				material.side = parseInt( materialSide.getValue() );
 
 			}
 
-			if ( material.side !== undefined ) {
+			if ( material.shading !== undefined ) {
 
-				material.side = parseInt( materialSide.getValue() );
+				material.shading = parseInt( materialShading.getValue() );
+
+			}
+
+			if ( material.blending !== undefined ) {
+
+				material.blending = parseInt( materialBlending.getValue() );
 
 			}
 
@@ -661,8 +688,9 @@ Sidebar.Material = function ( editor ) {
 			'normalMap': materialNormalMapRow,
 			'specularMap': materialSpecularMapRow,
 			'envMap': materialEnvMapRow,
-			'blending': materialBlendingRow,
 			'side': materialSideRow,
+			'shading': materialShadingRow,
+			'blending': materialBlendingRow,
 			'opacity': materialOpacityRow,
 			'transparent': materialTransparentRow,
 			'wireframe': materialWireframeRow
@@ -700,7 +728,7 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
-			materialClass.setValue( editor.getMaterialType( material ) );
+			materialClass.setValue( material.type );
 
 			if ( material.color !== undefined ) {
 
@@ -815,15 +843,21 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
-			if ( material.blending !== undefined ) {
+			if ( material.side !== undefined ) {
 
-				materialBlending.setValue( material.blending );
+				materialSide.setValue( material.side );
 
 			}
 
-			if ( material.side !== undefined ) {
+			if ( material.shading !== undefined ) {
 
-				materialSide.setValue( material.side );
+				materialShading.setValue( material.shading );
+
+			}
+
+			if ( material.blending !== undefined ) {
+
+				materialBlending.setValue( material.blending );
 
 			}
 

+ 10 - 15
editor/js/Sidebar.Object3D.js

@@ -3,6 +3,12 @@ Sidebar.Object3D = function ( editor ) {
 	var signals = editor.signals;
 
 	var container = new UI.CollapsiblePanel();
+	container.setCollapsed( editor.config.getKey( 'ui/sidebar/object3d/collapsed' ) );
+	container.onCollapsedChange( function ( boolean ) {
+
+		editor.config.setKey( 'ui/sidebar/object3d/collapsed', boolean );
+
+	} );
 	container.setDisplay( 'none' );
 
 	var objectType = new UI.Text().setTextTransform( 'uppercase' );
@@ -434,24 +440,13 @@ Sidebar.Object3D = function ( editor ) {
 	signals.sceneGraphChanged.add( function () {
 
 		var scene = editor.scene;
-
 		var options = {};
 
-		options[ scene.id ] = 'Scene';
-
-		( function addObjects( objects ) {
+		scene.traverse( function ( object ) {
 
-			for ( var i = 0, l = objects.length; i < l; i ++ ) {
-
-				var object = objects[ i ];
-
-				options[ object.id ] = object.name;
-
-				addObjects( object.children );
-
-			}
+			options[ object.id ] = object.name;
 
-		} )( scene.children );
+		} );
 
 		objectParent.setOptions( options );
 
@@ -467,7 +462,7 @@ Sidebar.Object3D = function ( editor ) {
 
 	function updateUI( object ) {
 
-		objectType.setValue( editor.getObjectType( object ) );
+		objectType.setValue( object.type );
 
 		objectUUID.setValue( object.uuid );
 		objectName.setValue( object.name );

+ 6 - 1
editor/js/Sidebar.Renderer.js

@@ -14,7 +14,12 @@ Sidebar.Renderer = function ( editor ) {
 	};
 
 	var container = new UI.CollapsiblePanel();
-	container.setCollapsed( true );
+	container.setCollapsed( editor.config.getKey( 'ui/sidebar/renderer/collapsed' ) );
+	container.onCollapsedChange( function ( boolean ) {
+
+		editor.config.setKey( 'ui/sidebar/renderer/collapsed', boolean );
+
+	} );
 
 	container.addStatic( new UI.Text( 'RENDERER' ) );
 	container.add( new UI.Break() );

+ 29 - 24
editor/js/Sidebar.Scene.js

@@ -3,6 +3,12 @@ Sidebar.Scene = function ( editor ) {
 	var signals = editor.signals;
 
 	var container = new UI.CollapsiblePanel();
+	container.setCollapsed( editor.config.getKey( 'ui/sidebar/scene/collapsed' ) );
+	container.onCollapsedChange( function ( boolean ) {
+
+		editor.config.setKey( 'ui/sidebar/scene/collapsed', boolean );
+
+	} );
 
 	container.addStatic( new UI.Text( 'SCENE' ) );
 	container.add( new UI.Break() );
@@ -111,47 +117,29 @@ Sidebar.Scene = function ( editor ) {
 
 	//
 
-	var refreshFogUI = function () {
-
-		var type = fogType.getValue();
-
-		fogColorRow.setDisplay( type === 'None' ? 'none' : '' );
-		fogNearRow.setDisplay( type === 'Fog' ? '' : 'none' );
-		fogFarRow.setDisplay( type === 'Fog' ? '' : 'none' );
-		fogDensityRow.setDisplay( type === 'FogExp2' ? '' : 'none' );
-
-	};
-
-	// events
-
-	signals.sceneGraphChanged.add( function () {
+	var refreshUI = function () {
 
 		var scene = editor.scene;
-		var sceneType = editor.getObjectType( scene );
 
 		var options = [];
 
-		options.push( { value: scene.id, html: '<span class="type ' + sceneType + '"></span> ' + scene.name } );
+		options.push( { value: scene.id, html: '<span class="type ' + scene.type + '"></span> ' + scene.name } );
 
 		( function addObjects( objects, pad ) {
 
 			for ( var i = 0, l = objects.length; i < l; i ++ ) {
 
 				var object = objects[ i ];
-				var objectType = editor.getObjectType( object );
 
-				var html = pad + '<span class="type ' + objectType + '"></span> ' + object.name;
+				var html = pad + '<span class="type ' + object.type + '"></span> ' + object.name;
 
 				if ( object instanceof THREE.Mesh ) {
 
 					var geometry = object.geometry;
 					var material = object.material;
 
-					var geometryType = editor.getGeometryType( geometry );
-					var materialType = editor.getMaterialType( material );
-
-					html += ' <span class="type ' + geometryType + '"></span> ' + geometry.name;
-					html += ' <span class="type ' + materialType + '"></span> ' + material.name;
+					html += ' <span class="type ' + geometry.type + '"></span> ' + geometry.name;
+					html += ' <span class="type ' + material.type + '"></span> ' + material.name;
 
 				}
 
@@ -196,7 +184,24 @@ Sidebar.Scene = function ( editor ) {
 
 		refreshFogUI();
 
-	} );
+	};
+
+	var refreshFogUI = function () {
+
+		var type = fogType.getValue();
+
+		fogColorRow.setDisplay( type === 'None' ? 'none' : '' );
+		fogNearRow.setDisplay( type === 'Fog' ? '' : 'none' );
+		fogFarRow.setDisplay( type === 'Fog' ? '' : 'none' );
+		fogDensityRow.setDisplay( type === 'FogExp2' ? '' : 'none' );
+
+	};
+
+	refreshUI();
+
+	// events
+
+	signals.sceneGraphChanged.add( refreshUI );
 
 	signals.objectSelected.add( function ( object ) {
 

+ 82 - 0
editor/js/Sidebar.Script.js

@@ -0,0 +1,82 @@
+Sidebar.Script = function ( editor ) {
+
+	var signals = editor.signals;
+
+	var container = new UI.CollapsiblePanel();
+	container.setCollapsed( editor.config.getKey( 'ui/sidebar/script/collapsed' ) );
+	container.onCollapsedChange( function ( boolean ) {
+
+		editor.config.setKey( 'ui/sidebar/script/collapsed', boolean );
+
+	} );
+	container.setDisplay( 'none' );
+
+	container.addStatic( new UI.Text( 'Script' ).setTextTransform( 'uppercase' ) );
+	container.add( new UI.Break() );
+
+	var scriptsRow = new UI.Panel();
+	container.add( scriptsRow );
+
+	// source
+
+	var scriptSourceRow = new UI.Panel();
+	var scriptSource = new UI.TextArea().setWidth( '240px' ).setHeight( '180px' ).setColor( '#444' ).setFontSize( '12px' );
+	scriptSource.onChange( function () {
+
+		var object = editor.selected;
+		var source = scriptSource.getValue();
+
+		try {
+
+			var script = new Function( 'scene', source ).bind( object.clone() );
+			script( new THREE.Scene() );
+
+			scriptSource.setBorderColor( '#ccc' );
+			scriptSource.setBackgroundColor( '' );
+
+		} catch ( error ) {
+
+			scriptSource.setBorderColor( '#f00' );
+			scriptSource.setBackgroundColor( 'rgba(255,0,0,0.25)' );
+
+		}
+
+		object.script = new THREE.Script( source );
+
+		editor.signals.objectChanged.dispatch( object );
+
+	} );
+
+	scriptSourceRow.add( scriptSource );
+
+	container.add( scriptSourceRow );
+
+	//
+
+	signals.objectSelected.add( function ( object ) {
+
+		if ( object !== null ) {
+
+			container.setDisplay( 'block' );
+
+			if ( object.script !== undefined ) {
+
+				scriptSource.setValue( object.script.source );
+
+			} else {
+
+				scriptSource.setValue( '' );
+
+			}
+
+		} else {
+
+			container.setDisplay( 'none' );
+
+		}
+
+	} );
+
+	return container;
+
+}

+ 1 - 0
editor/js/Sidebar.js

@@ -8,6 +8,7 @@ var Sidebar = function ( editor ) {
 	container.add( new Sidebar.Geometry( editor ) );
 	container.add( new Sidebar.Material( editor ) );
 	container.add( new Sidebar.Animation( editor ) );
+	container.add( new Sidebar.Script( editor ) );
 
 	return container;
 

+ 5 - 0
editor/js/Toolbar.js

@@ -45,10 +45,15 @@ var Toolbar = function ( editor ) {
 	buttons.add( local );
 	buttons.add( new UI.Text( 'local' ) );
 
+	var showGrid = new UI.Checkbox().onChange( update ).setValue( true );
+	buttons.add( showGrid );
+	buttons.add( new UI.Text( 'show' ) );
+
 	function update() {
 
 		signals.snapChanged.dispatch( snap.getValue() === true ? grid.getValue() : null );
 		signals.spaceChanged.dispatch( local.getValue() === true ? "local" : "world" );
+		signals.showGridChanged.dispatch( showGrid.getValue() );
 
 	}
 

+ 17 - 11
editor/js/Viewport.js

@@ -26,9 +26,9 @@ var Viewport = function ( editor ) {
 
 	//
 
-	var camera = new THREE.PerspectiveCamera( 50, 1, 1, 5000 );
-	camera.position.fromArray( editor.config.getKey( 'camera' ).position );
-	camera.lookAt( new THREE.Vector3().fromArray( editor.config.getKey( 'camera' ).target ) );
+	var camera = editor.camera;
+	camera.position.fromArray( editor.config.getKey( 'camera/position' ) );
+	camera.lookAt( new THREE.Vector3().fromArray( editor.config.getKey( 'camera/target' ) ) );
 
 	//
 
@@ -49,11 +49,12 @@ var Viewport = function ( editor ) {
 
 		}
 
-		if ( editor.selected !== null ) {
+		render();
 
-			signals.objectChanged.dispatch( editor.selected );
+	} );
+	transformControls.addEventListener( 'objectChange', function () {
 
-		}
+		signals.objectChanged.dispatch( transformControls.object );
 
 	} );
 	sceneHelpers.add( transformControls );
@@ -170,7 +171,7 @@ var Viewport = function ( editor ) {
 	// otherwise controls.enabled doesn't work.
 
 	var controls = new THREE.EditorControls( camera, container.dom );
-	controls.center.fromArray( editor.config.getKey( 'camera' ).target )
+	controls.center.fromArray( editor.config.getKey( 'camera/target' ) )
 	controls.addEventListener( 'change', function () {
 
 		transformControls.update();
@@ -254,10 +255,8 @@ var Viewport = function ( editor ) {
 
 		saveTimeout = setTimeout( function () {
 
-			editor.config.setKey( 'camera', {
-				position: camera.position.toArray(),
-				target: controls.center.toArray()
-			} );
+			editor.config.setKey( 'camera/position', camera.position.toArray() );
+			editor.config.setKey( 'camera/target', controls.center.toArray() );
 
 		}, 1000 );
 
@@ -429,6 +428,13 @@ var Viewport = function ( editor ) {
 
 	} );
 
+	signals.showGridChanged.add( function ( showGrid ) {
+
+		grid.visible = showGrid;
+		render();
+
+	} );
+
 	var animations = [];
 
 	signals.playAnimation.add( function ( animation ) {

+ 46 - 12
editor/js/libs/ui.js

@@ -52,7 +52,7 @@ UI.Element.prototype = {
 
 var properties = [ 'position', 'left', 'top', 'right', 'bottom', 'width', 'height', 'border', 'borderLeft',
 'borderTop', 'borderRight', 'borderBottom', 'borderColor', 'display', 'overflow', 'margin', 'marginLeft', 'marginTop', 'marginRight', 'marginBottom', 'padding', 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom', 'color',
-'backgroundColor', 'opacity', 'fontSize', 'fontWeight', 'textTransform', 'cursor' ];
+'backgroundColor', 'opacity', 'fontSize', 'fontWeight', 'textAlign', 'textTransform', 'cursor' ];
 
 properties.forEach( function ( property ) {
 
@@ -269,19 +269,31 @@ UI.CollapsiblePanel.prototype.expand = function() {
 
 };
 
-UI.CollapsiblePanel.prototype.setCollapsed = function( setCollapsed ) {
+UI.CollapsiblePanel.prototype.setCollapsed = function( boolean ) {
 
-	if ( setCollapsed ) {
+	if ( boolean ) {
 
-		this.dom.classList.add('collapsed');
+		this.dom.classList.add( 'collapsed' );
 
 	} else {
 
-		this.dom.classList.remove('collapsed');
+		this.dom.classList.remove( 'collapsed' );
 
 	}
 
-	this.isCollapsed = setCollapsed;
+	this.isCollapsed = boolean;
+
+	if ( this.onCollapsedChangeCallback !== undefined ) {
+
+		this.onCollapsedChangeCallback( boolean );
+
+	}
+
+};
+
+UI.CollapsiblePanel.prototype.onCollapsedChange = function ( callback ) {
+
+	this.onCollapsedChangeCallback = callback;
 
 };
 
@@ -480,7 +492,13 @@ UI.Select.prototype.getValue = function () {
 
 UI.Select.prototype.setValue = function ( value ) {
 
-	this.dom.value = value;
+	value = String( value );
+
+	if ( this.dom.value !== value ) {
+
+		this.dom.value = value;
+
+	}
 
 	return this;
 
@@ -825,9 +843,19 @@ UI.Number = function ( number ) {
 
 	var onChange = function ( event ) {
 
-		var number = parseFloat( dom.value );
+		var value = 0;
+
+		try {
 
-		dom.value = isNaN( number ) === false ? number : 0;
+			value = eval( dom.value );
+
+		} catch ( error ) {
+
+			console.error( error.message );
+
+		}
+
+		dom.value = parseFloat( value );
 
 	};
 
@@ -978,14 +1006,20 @@ UI.Integer = function ( number ) {
 
 	var onChange = function ( event ) {
 
-		var number = parseInt( dom.value );
+		var value = 0;
+
+		try {
 
-		if ( isNaN( number ) === false ) {
+			value = eval( dom.value );
 
-			dom.value = number;
+		} catch ( error ) {
+
+			console.error( error.message );
 
 		}
 
+		dom.value = parseInt( value );
+
 	};
 
 	var onFocus = function ( event ) {

+ 1 - 1
examples/css3d_periodictable.html

@@ -397,7 +397,7 @@
 
 				}, false );
 
-				transform( targets.table, 5000 );
+				transform( targets.table, 2000 );
 
 				//
 

+ 1 - 0
examples/index.html

@@ -152,6 +152,7 @@
 				"webgl_geometry_terrain_raycast",
 				"webgl_geometry_tessellation",
 				"webgl_geometry_text",
+				"webgl_geometry_text2",
 				"webgl_gpgpu_birds",
 				"webgl_hdr",
 				"webgl_helpers",

+ 14 - 11
examples/js/controls/EditorControls.js

@@ -52,28 +52,31 @@ THREE.EditorControls = function ( object, domElement ) {
 
 	};
 
-	this.pan = function ( distance ) {
+	this.pan = function ( delta ) {
 
-		normalMatrix.getNormalMatrix( object.matrix );
+		var distance = object.position.distanceTo( center );
 
-		distance.applyMatrix3( normalMatrix );
-		distance.multiplyScalar( vector.copy( center ).sub( object.position ).length() * 0.001 );
+		delta.multiplyScalar( distance * 0.001 );
+		delta.applyMatrix3( normalMatrix.getNormalMatrix( object.matrix ) );
 
-		object.position.add( distance );
-		center.add( distance );
+		object.position.add( delta );
+		center.add( delta );
 
 		scope.dispatchEvent( changeEvent );
 
 	};
 
-	this.zoom = function ( distance ) {
+	this.zoom = function ( delta ) {
 
-		normalMatrix.getNormalMatrix( object.matrix );
+		var distance = object.position.distanceTo( center );
 
-		distance.applyMatrix3( normalMatrix );
-		distance.multiplyScalar( vector.copy( center ).sub( object.position ).length() * 0.001 );
+		delta.multiplyScalar( distance * 0.001 );
 
-		object.position.add( distance );
+		if ( delta.length() > distance ) return;
+
+		delta.applyMatrix3( normalMatrix.getNormalMatrix( object.matrix ) );
+
+		object.position.add( delta );
 
 		scope.dispatchEvent( changeEvent );
 

+ 8 - 0
examples/js/controls/OrbitControls.js

@@ -64,6 +64,11 @@ THREE.OrbitControls = function ( object, domElement ) {
 	this.minPolarAngle = 0; // radians
 	this.maxPolarAngle = Math.PI; // radians
 
+	// How far you can orbit horizontally, upper and lower limits.
+	// If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
+	this.minAzimuthAngle = - Infinity; // radians
+	this.maxAzimuthAngle = Infinity; // radians
+
 	// Set to true to disable use of the keys
 	this.noKeys = false;
 
@@ -255,6 +260,9 @@ THREE.OrbitControls = function ( object, domElement ) {
 		theta += thetaDelta;
 		phi += phiDelta;
 
+		// restrict theta to be between desired limits
+		theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) );
+
 		// restrict phi to be between desired limits
 		phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
 

+ 10 - 7
examples/js/controls/TransformControls.js

@@ -569,6 +569,7 @@
 
 		var changeEvent = { type: "change" };
 		var gizmoSelectEvent = { type: "gizmoSelect" };
+		var objectChangeEvent = { type: "objectChange" };
 
 		var ray = new THREE.Raycaster();
 		var projector = new THREE.Projector();
@@ -725,15 +726,17 @@
 
 			var intersect = intersectObjects( pointer, scope.gizmo[_mode].pickers.children );
 
+			var axis = null;
+
 			if ( intersect ) {
 
-				scope.axis = intersect.object.name;
-				scope.update();
-				scope.dispatchEvent( changeEvent );
+				axis = intersect.object.name;
 
-			} else if ( scope.axis !== null ) {
+			}
+
+			if ( scope.axis !== axis ) {
 
-				scope.axis = null;
+				scope.axis = axis;
 				scope.update();
 				scope.dispatchEvent( changeEvent );
 
@@ -951,7 +954,7 @@
 			}
 
 			scope.update();
-			scope.dispatchEvent( changeEvent );
+			scope.dispatchEvent( objectChangeEvent );
 
 		}
 
@@ -981,4 +984,4 @@
 
 	THREE.TransformControls.prototype = Object.create( THREE.Object3D.prototype );
 
-}());
+}());

+ 0 - 66
examples/js/exporters/BufferGeometryExporter.js

@@ -1,66 +0,0 @@
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.BufferGeometryExporter = function () {};
-
-THREE.BufferGeometryExporter.prototype = {
-
-	constructor: THREE.BufferGeometryExporter,
-
-	parse: function ( geometry ) {
-
-		var output = {
-			metadata: {
-				version: 4.0,
-				type: 'BufferGeometry',
-				generator: 'BufferGeometryExporter'
-			},
-			attributes: {
-			}
-		};
-
-		var attributes = geometry.attributes;
-		var offsets = geometry.offsets;
-		var boundingSphere = geometry.boundingSphere;
-
-		for ( var key in attributes ) {
-
-			var attribute = attributes[ key ];
-
-			var array = [], typeArray = attribute.array;
-
-			for ( var i = 0, l = typeArray.length; i < l; i ++ ) {
-
-				array[ i ] = typeArray[ i ];
-
-			}
-
-			output.attributes[ key ] = {
-				itemSize: attribute.itemSize,
-				type: attribute.array.constructor.name,
-				array: array
-			}
-
-		}
-
-		if ( offsets.length > 0 ) {
-
-			output.offsets = JSON.parse( JSON.stringify( offsets ) );
-
-		}
-
-		if ( boundingSphere !== null ) {
-
-			output.boundingSphere = {
-				center: boundingSphere.center.toArray(),
-				radius: boundingSphere.radius
-			}
-
-		}
-
-		return output;
-
-	}
-
-};

+ 0 - 192
examples/js/exporters/GeometryExporter.js

@@ -1,192 +0,0 @@
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.GeometryExporter = function () {};
-
-THREE.GeometryExporter.prototype = {
-
-	constructor: THREE.GeometryExporter,
-
-	parse: function ( geometry ) {
-
-		var output = {
-			metadata: {
-				version: 4.0,
-				type: 'geometry',
-				generator: 'GeometryExporter'
-			}
-		};
-
-		var vertices = [];
-
-		for ( var i = 0; i < geometry.vertices.length; i ++ ) {
-
-			var vertex = geometry.vertices[ i ];
-			vertices.push( vertex.x, vertex.y, vertex.z );
-
-		}
-
-		var faces = [];
-		var normals = [];
-		var normalsHash = {};
-		var colors = [];
-		var colorsHash = {};
-		var uvs = [];
-		var uvsHash = {};
-
-		for ( var i = 0; i < geometry.faces.length; i ++ ) {
-
-			var face = geometry.faces[ i ];
-
-			var hasMaterial = false; // face.materialIndex !== undefined;
-			var hasFaceUv = false; // deprecated
-			var hasFaceVertexUv = geometry.faceVertexUvs[ 0 ][ i ] !== undefined;
-			var hasFaceNormal = face.normal.length() > 0;
-			var hasFaceVertexNormal = face.vertexNormals.length > 0;
-			var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1;
-			var hasFaceVertexColor = face.vertexColors.length > 0;
-
-			var faceType = 0;
-
-			faceType = setBit( faceType, 0, 0 );
-			faceType = setBit( faceType, 1, hasMaterial );
-			faceType = setBit( faceType, 2, hasFaceUv );
-			faceType = setBit( faceType, 3, hasFaceVertexUv );
-			faceType = setBit( faceType, 4, hasFaceNormal );
-			faceType = setBit( faceType, 5, hasFaceVertexNormal );
-			faceType = setBit( faceType, 6, hasFaceColor );
-			faceType = setBit( faceType, 7, hasFaceVertexColor );
-
-			faces.push( faceType );
-			faces.push( face.a, face.b, face.c );
-
-
-			/*
-			if ( hasMaterial ) {
-
-				faces.push( face.materialIndex );
-
-			}
-			*/
-
-			if ( hasFaceVertexUv ) {
-
-				var faceVertexUvs = geometry.faceVertexUvs[ 0 ][ i ];
-
-				faces.push(
-					getUvIndex( faceVertexUvs[ 0 ] ),
-					getUvIndex( faceVertexUvs[ 1 ] ),
-					getUvIndex( faceVertexUvs[ 2 ] )
-				);
-
-			}
-
-			if ( hasFaceNormal ) {
-
-				faces.push( getNormalIndex( face.normal ) );
-
-			}
-
-			if ( hasFaceVertexNormal ) {
-
-				var vertexNormals = face.vertexNormals;
-
-				faces.push(
-					getNormalIndex( vertexNormals[ 0 ] ),
-					getNormalIndex( vertexNormals[ 1 ] ),
-					getNormalIndex( vertexNormals[ 2 ] )
-				);
-
-			}
-
-			if ( hasFaceColor ) {
-
-				faces.push( getColorIndex( face.color ) );
-
-			}
-
-			if ( hasFaceVertexColor ) {
-
-				var vertexColors = face.vertexColors;
-
-				faces.push(
-					getColorIndex( vertexColors[ 0 ] ),
-					getColorIndex( vertexColors[ 1 ] ),
-					getColorIndex( vertexColors[ 2 ] )
-				);
-
-			}
-
-		}
-
-		function setBit( value, position, enabled ) {
-
-			return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position) );
-
-		}
-
-		function getNormalIndex( normal ) {
-
-			var hash = normal.x.toString() + normal.y.toString() + normal.z.toString();
-
-			if ( normalsHash[ hash ] !== undefined ) {
-
-				return normalsHash[ hash ];
-
-			}
-
-			normalsHash[ hash ] = normals.length / 3;
-			normals.push( normal.x, normal.y, normal.z );
-
-			return normalsHash[ hash ];
-
-		}
-
-		function getColorIndex( color ) {
-
-			var hash = color.r.toString() + color.g.toString() + color.b.toString();
-
-			if ( colorsHash[ hash ] !== undefined ) {
-
-				return colorsHash[ hash ];
-
-			}
-
-			colorsHash[ hash ] = colors.length;
-			colors.push( color.getHex() );
-
-			return colorsHash[ hash ];
-
-		}
-
-		function getUvIndex( uv ) {
-
-			var hash = uv.x.toString() + uv.y.toString();
-
-			if ( uvsHash[ hash ] !== undefined ) {
-
-				return uvsHash[ hash ];
-
-			}
-
-			uvsHash[ hash ] = uvs.length / 2;
-			uvs.push( uv.x, uv.y );
-
-			return uvsHash[ hash ];
-
-		}
-
-		output.vertices = vertices;
-		output.normals = normals;
-		if ( colors.length > 0 ) output.colors = colors;
-		if ( uvs.length > 0 ) output.uvs = [ uvs ]; // temporal backward compatibility
-		output.faces = faces;
-
-		//
-
-		return output;
-
-	}
-
-};

+ 0 - 17
examples/js/exporters/HTMLExporter.js

@@ -1,17 +0,0 @@
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.HTMLExporter = function () {};
-
-THREE.HTMLExporter.prototype = {
-
-	constructor: THREE.HTMLExporter,
-
-	parse: function ( scene ) {
-
-		return output;
-
-	}
-
-}

+ 0 - 113
examples/js/exporters/MaterialExporter.js

@@ -1,113 +0,0 @@
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.MaterialExporter = function () {};
-
-THREE.MaterialExporter.prototype = {
-
-	constructor: THREE.MaterialExporter,
-
-	parse: function ( material ) {
-
-		var output = {
-			metadata: {
-				version: 4.2,
-				type: 'material',
-				generator: 'MaterialExporter'
-			}
-		};
-
-		output.uuid = material.uuid;
-
-		if ( material.name !== "" ) output.name = material.name;
-
-		if ( material instanceof THREE.MeshBasicMaterial ) {
-
-			output.type = 'MeshBasicMaterial';
-			output.color = material.color.getHex();
-			if ( material.vertexColors !== THREE.NoColors ) output.vertexColors = material.vertexColors;
-			if ( material.blending !== THREE.NormalBlending ) output.blending = material.blending;
-			if ( material.side !== THREE.FrontSide ) output.side = material.side;
-			output.opacity = material.opacity;
-			output.transparent = material.transparent;
-			output.wireframe = material.wireframe;
-
-		} else if ( material instanceof THREE.MeshLambertMaterial ) {
-
-			output.type = 'MeshLambertMaterial';
-			output.color = material.color.getHex();
-			output.ambient = material.ambient.getHex();
-			output.emissive = material.emissive.getHex();
-			if ( material.vertexColors !== THREE.NoColors ) output.vertexColors = material.vertexColors;
-			if ( material.blending !== THREE.NormalBlending ) output.blending = material.blending;
-			if ( material.side !== THREE.FrontSide ) output.side = material.side;
-			output.opacity = material.opacity;
-			output.transparent = material.transparent;
-			output.wireframe = material.wireframe;
-
-		} else if ( material instanceof THREE.MeshPhongMaterial ) {
-
-			output.type = 'MeshPhongMaterial';
-			output.color = material.color.getHex();
-			output.ambient = material.ambient.getHex();
-			output.emissive = material.emissive.getHex();
-			output.specular = material.specular.getHex();
-			output.shininess = material.shininess;
-			if ( material.vertexColors !== THREE.NoColors ) output.vertexColors = material.vertexColors;
-			if ( material.blending !== THREE.NormalBlending ) output.blending = material.blending;
-			if ( material.side !== THREE.FrontSide ) output.side = material.side;
-			output.opacity = material.opacity;
-			output.transparent = material.transparent;
-			output.wireframe = material.wireframe;
-
-		} else if ( material instanceof THREE.MeshNormalMaterial ) {
-
-			output.type = 'MeshNormalMaterial';
-			if ( material.blending !== THREE.NormalBlending ) output.blending = material.blending;
-			if ( material.side !== THREE.FrontSide ) output.side = material.side;
-			output.opacity = material.opacity;
-			output.transparent = material.transparent;
-			output.wireframe = material.wireframe;
-
-		} else if ( material instanceof THREE.MeshDepthMaterial ) {
-
-			output.type = 'MeshDepthMaterial';
-			if ( material.blending !== THREE.NormalBlending ) output.blending = material.blending;
-			if ( material.side !== THREE.FrontSide ) output.side = material.side;
-			output.opacity = material.opacity;
-			output.transparent = material.transparent;
-			output.wireframe = material.wireframe;
-
-		} else if ( material instanceof THREE.MeshFaceMaterial ) {
-
-			output.type = 'MeshFaceMaterial';
-			output.materials = [];
-
-			for ( var i = 0, l = material.materials.length; i < l; i ++ ) {
-
-				output.materials.push( this.parse( material.materials[ i ] ) );
-
-			}
-
-		} else if ( material instanceof THREE.ShaderMaterial ) {
-
-			output.type = 'ShaderMaterial';
-			output.uniforms = material.uniforms;
-			output.vertexShader = material.vertexShader;
-			output.fragmentShader = material.fragmentShader;
-
-		} else if ( material instanceof THREE.SpriteMaterial ) {
-
-			output.type = 'SpriteMaterial';
-			output.color = material.color.getHex();
-			output.opacity = material.opacity;
-			output.transparent = material.transparent;
-
-		}
-
-		return output;
-
-	}
-
-};

+ 0 - 268
examples/js/exporters/ObjectExporter.js

@@ -1,268 +0,0 @@
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.ObjectExporter = function () {};
-
-THREE.ObjectExporter.prototype = {
-
-	constructor: THREE.ObjectExporter,
-
-	parse: function ( object ) {
-
-		// console.log( object );
-
-		var output = {
-			metadata: {
-				version: 4.3,
-				type: 'Object',
-				generator: 'ObjectExporter'
-			}
-		};
-
-		//
-
-		var geometries = {};
-		var geometryExporter = new THREE.GeometryExporter();
-		var bufferGeometryExporter = new THREE.BufferGeometryExporter();
-
-		var parseGeometry = function ( geometry ) {
-
-			if ( output.geometries === undefined ) {
-
-				output.geometries = [];
-
-			}
-
-			if ( geometries[ geometry.uuid ] === undefined ) {
-
-				var data = {};
-
-				data.uuid = geometry.uuid;
-
-				if ( geometry.name !== "" ) data.name = geometry.name;
-
-				var handleParameters = function ( parameters ) {
-
-					for ( var i = 0; i < parameters.length; i ++ ) {
-
-						var parameter = parameters[ i ];
-
-						if ( geometry.parameters[ parameter ] !== undefined ) {
-
-							data[ parameter ] = geometry.parameters[ parameter ];
-
-						}
-
-					}
-
-				};
-
-				if ( geometry instanceof THREE.PlaneGeometry ) {
-
-					data.type = 'PlaneGeometry';
-					handleParameters( [ 'width', 'height', 'widthSegments', 'heightSegments' ] );
-
-				} else if ( geometry instanceof THREE.BoxGeometry ) {
-
-					data.type = 'BoxGeometry';
-					handleParameters( [ 'width', 'height', 'depth', 'widthSegments', 'heightSegments', 'depthSegments' ] );
-
-				} else if ( geometry instanceof THREE.CircleGeometry ) {
-
-					data.type = 'CircleGeometry';
-					handleParameters( [ 'radius', 'segments' ] );
-
-				} else if ( geometry instanceof THREE.CylinderGeometry ) {
-
-					data.type = 'CylinderGeometry';
-					handleParameters( [ 'radiusTop', 'radiusBottom', 'height', 'radialSegments', 'heightSegments', 'openEnded' ] );
-
-				} else if ( geometry instanceof THREE.SphereGeometry ) {
-
-					data.type = 'SphereGeometry';
-					handleParameters( [ 'radius', 'widthSegments', 'heightSegments', 'phiStart', 'phiLength', 'thetaStart', 'thetaLength' ] );
-
-				} else if ( geometry instanceof THREE.IcosahedronGeometry ) {
-
-					data.type = 'IcosahedronGeometry';
-					handleParameters( [ 'radius', 'detail' ] );
-
-				} else if ( geometry instanceof THREE.TorusGeometry ) {
-
-					data.type = 'TorusGeometry';
-					handleParameters( [ 'radius', 'tube', 'radialSegments', 'tubularSegments', 'arc' ] );
-
-				} else if ( geometry instanceof THREE.TorusKnotGeometry ) {
-
-					data.type = 'TorusKnotGeometry';
-					handleParameters( [ 'radius', 'tube', 'radialSegments', 'tubularSegments', 'p', 'q', 'heightScale' ] );
-
-				} else if ( geometry instanceof THREE.BufferGeometry ) {
-
-					data.type = 'BufferGeometry';
-					data.data = bufferGeometryExporter.parse( geometry );
-
-					delete data.data.metadata;
-
-				} else if ( geometry instanceof THREE.Geometry ) {
-
-					data.type = 'Geometry';
-					data.data = geometryExporter.parse( geometry );
-
-					delete data.data.metadata;
-
-				}
-
-				geometries[ geometry.uuid ] = data;
-
-				output.geometries.push( data );
-
-			}
-
-			return geometry.uuid;
-
-		};
-
-		//
-
-		var materials = {};
-		var materialExporter = new THREE.MaterialExporter();
-
-		var parseMaterial = function ( material ) {
-
-			if ( output.materials === undefined ) {
-
-				output.materials = [];
-
-			}
-
-			if ( materials[ material.uuid ] === undefined ) {
-
-				var data = materialExporter.parse( material );
-
-				delete data.metadata;
-
-				materials[ material.uuid ] = data;
-
-				output.materials.push( data );
-
-			}
-
-			return material.uuid;
-
-		};
-
-		//
-
-		var parseObject = function ( object ) {
-
-			var data = {};
-
-			data.uuid = object.uuid;
-
-			if ( object.name !== '' ) data.name = object.name;
-			if ( JSON.stringify( object.userData ) !== '{}' ) data.userData = object.userData;
-			if ( object.visible !== true ) data.visible = object.visible;
-
-			if ( object instanceof THREE.Scene ) {
-
-				data.type = 'Scene';
-
-			} else if ( object instanceof THREE.PerspectiveCamera ) {
-
-				data.type = 'PerspectiveCamera';
-				data.fov = object.fov;
-				data.aspect = object.aspect;
-				data.near = object.near;
-				data.far = object.far;
-
-			} else if ( object instanceof THREE.OrthographicCamera ) {
-
-				data.type = 'OrthographicCamera';
-				data.left = object.left;
-				data.right = object.right;
-				data.top = object.top;
-				data.bottom = object.bottom;
-				data.near = object.near;
-				data.far = object.far;
-
-			} else if ( object instanceof THREE.AmbientLight ) {
-
-				data.type = 'AmbientLight';
-				data.color = object.color.getHex();
-
-			} else if ( object instanceof THREE.DirectionalLight ) {
-
-				data.type = 'DirectionalLight';
-				data.color = object.color.getHex();
-				data.intensity = object.intensity;
-
-			} else if ( object instanceof THREE.PointLight ) {
-
-				data.type = 'PointLight';
-				data.color = object.color.getHex();
-				data.intensity = object.intensity;
-				data.distance = object.distance;
-
-			} else if ( object instanceof THREE.SpotLight ) {
-
-				data.type = 'SpotLight';
-				data.color = object.color.getHex();
-				data.intensity = object.intensity;
-				data.distance = object.distance;
-				data.angle = object.angle;
-				data.exponent = object.exponent;
-
-			} else if ( object instanceof THREE.HemisphereLight ) {
-
-				data.type = 'HemisphereLight';
-				data.color = object.color.getHex();
-				data.groundColor = object.groundColor.getHex();
-
-			} else if ( object instanceof THREE.Mesh ) {
-
-				data.type = 'Mesh';
-				data.geometry = parseGeometry( object.geometry );
-				data.material = parseMaterial( object.material );
-
-			} else if ( object instanceof THREE.Sprite ) {
-
-				data.type = 'Sprite';
-				data.material = parseMaterial( object.material );
-
-			} else if ( object instanceof THREE.Group ) {
-
-				data.type = 'Group';
-
-			} else {
-
-				data.type = 'Object3D';
-
-			}
-
-			data.matrix = object.matrix.toArray();
-
-			if ( object.children.length > 0 ) {
-
-				data.children = [];
-
-				for ( var i = 0; i < object.children.length; i ++ ) {
-
-					data.children.push( parseObject( object.children[ i ] ) );
-
-				}
-
-			}
-
-			return data;
-
-		}
-
-		output.object = parseObject( object );
-
-		return output;
-
-	}
-
-}

+ 18 - 31
examples/js/libs/pnltri.min.js

@@ -1,32 +1,19 @@
 // pnltri.js / raw.github.com/jahting/pnltri.js/master/LICENSE
-'use strict';var PNLTRI={REVISION:"1.4"};PNLTRI.Math={log2:function(a){return Math.log(a)/Math.LN2},random:Math.random,array_shuffle:function(a){for(var c=a.length-1;0<c;c--){var b=Math.floor(PNLTRI.Math.random()*(c+1)),d=a[c];a[c]=a[b];a[b]=d}return a},ptsCrossProd:function(a,c,b){return(c.x-a.x)*(b.y-a.y)-(c.y-a.y)*(b.x-a.x)}};PNLTRI.Math.EPSILON_P=Math.pow(2,-43);PNLTRI.Math.EPSILON_N=-PNLTRI.Math.EPSILON_P;PNLTRI.PolygonData=function(a){this.vertices=[];this.segments=[];this.idNextPolyChain=0;this.PolyLeftArr=[];this.monoSubPolyChains=[];this.triangles=[];if(a)for(var c=0,b=a.length;c<b;c++)this.addPolygonChain(a[c])};
-PNLTRI.PolygonData.prototype={constructor:PNLTRI.PolygonData,getSegments:function(){return this.segments},getFirstSegment:function(){return this.segments[0]},getMonoSubPolys:function(){return this.monoSubPolyChains},getTriangles:function(){return this.triangles.concat()},nbPolyChains:function(){return this.idNextPolyChain},get_PolyLeftArr:function(){return this.PolyLeftArr.concat()},set_PolyLeft_wrong:function(a){this.PolyLeftArr[a]=!1},compare_pts_yx:function(a,c){var b=a.y-c.y;if(b<PNLTRI.Math.EPSILON_N)return-1;
-if(b>PNLTRI.Math.EPSILON_P)return 1;b=a.x-c.x;return b<PNLTRI.Math.EPSILON_N?-1:b>PNLTRI.Math.EPSILON_P?1:0},isClockWise:function(a){var c=a,b=0;do b+=(c.vFrom.x-c.vTo.x)*(c.vFrom.y+c.vTo.y),c=c.snext;while(c!=a);return 0>b},appendVertexEntry:function(a,c){var b={id:this.vertices.length,x:a,y:c,outSegs:[]};this.vertices.push(b);return b},createSegmentEntry:function(a,c){return{chainId:this.idNextPolyChain,vFrom:a,vTo:c,upward:1==this.compare_pts_yx(c,a),sprev:null,snext:null,trLeft:null,trRight:null,
-mprev:null,mnext:null,marked:!1}},appendSegmentEntry:function(a){this.segments.push(a);return a},addVertexChain:function(a){function c(a,b){return Math.abs(a.x-b.x)<PNLTRI.Math.EPSILON_P&&Math.abs(a.y-b.y)<PNLTRI.Math.EPSILON_P}for(var b=[],d,e,k,f=0;f<a.length;f++)d=this.appendVertexEntry(a[f].x,a[f].y),e=!0,k=b.length-1,0<=k&&c(d,b[k])&&(e=!1),e&&b.push(d);1<b.length&&c(b[b.length-1],b[0])&&b.pop();return b},addPolygonChain:function(a){a=this.addVertexChain(a);if(3>a.length)return console.log("Polygon has < 3 vertices!",
-a),0;for(var c=this.segments.length,b,d,e,k=0;k<a.length-1;k++)b=this.createSegmentEntry(a[k],a[k+1]),e?(b.sprev=e,e.snext=b):d=b,e=b,this.appendSegmentEntry(b);b=this.createSegmentEntry(a[a.length-1],a[0]);b.sprev=e;e.snext=b;this.appendSegmentEntry(b);d.sprev=b;b.snext=d;this.PolyLeftArr[this.idNextPolyChain++]=!0;return this.segments.length-c},initMonoChains:function(){for(var a,c=0;c<this.segments.length;c++)a=this.segments[c],this.PolyLeftArr[a.chainId]?(a.mprev=a.sprev,a.mnext=a.snext,a.vFrom.outSegs.push({segOut:a,
-vertTo:a.vTo})):(a=a.snext,a.mprev=a.snext,a.mnext=a.sprev,a.vFrom.outSegs.push({segOut:a,vertTo:this.segments[c].vFrom}))},createMonoSegment:function(a){this.appendSegmentEntry(a);a.vFrom.outSegs.push({segOut:a,vertTo:a.vTo});return a},newMonoChain:function(a){var c=this.monoSubPolyChains.length;this.monoSubPolyChains[c]=a;return c},splitPolygonChain:function(a,c,b,d){function e(a,b){for(var c,d,e=null,k=4,h=0;h<a.outSegs.length;h++){c=a.outSegs[h];var f=c.vertTo;d=f.x-a.x;var f=f.y-a.y,l=b.x-a.x,
-t=b.y-a.y,v=(d*l+f*t)/Math.sqrt(d*d+f*f)/Math.sqrt(l*l+t*t);(d=0<=d*t-l*f?1-v:3+v)<k&&(k=d,e=c)}return e}var k,f;k=e(c,b);f=e(b,c);k=k.segOut;var l=f.segOut;f=this.createMonoSegment({vFrom:c,vTo:b,upward:!0,mprev:k.mprev,mnext:l});c=this.createMonoSegment({vFrom:b,vTo:c,upward:!1,mprev:l.mprev,mnext:k});k.mprev.mnext=f;l.mprev.mnext=c;k.mprev=c;l.mprev=f;b=this.monoSubPolyChains.length;d?(this.monoSubPolyChains[a]=f,this.monoSubPolyChains[b]=c):(this.monoSubPolyChains[a]=c,this.monoSubPolyChains[b]=
-f);return b},unique_monotone_chains_max:function(){for(var a,c,b,d,e,k=[],f=0;f<this.monoSubPolyChains.length;f++){a=c=this.monoSubPolyChains[f];d=e=a.vFrom;a.marked=!0;a=a.mnext;for(var l=!1;(b=a.vFrom)!=d;){if(a.marked){l=!0;break}else a.marked=!0;1==this.compare_pts_yx(b,e)&&(e=b,c=a);a=a.mnext}l||k.push(c)}return k},normalize_monotone_chains:function(){this.monoSubPolyChains=this.unique_monotone_chains_max();return this.monoSubPolyChains.length},clearTriangles:function(){this.triangles=[]},addTriangle:function(a,
-c,b){this.triangles.push([a.id,c.id,b.id])}};PNLTRI.EarClipTriangulator=function(a){this.polyData=a};
-PNLTRI.EarClipTriangulator.prototype={constructor:PNLTRI.EarClipTriangulator,triangulate_polygon_no_holes:function(){var a=this.polyData,c=a.getFirstSegment(),b=c;if(a.isClockWise(c)){do b.mprev=b.snext,b=b.mnext=b.sprev;while(b!=c);a.set_PolyLeft_wrong(0)}else{do b.mprev=b.sprev,b=b.mnext=b.snext;while(b!=c)}for(c=a=c;a.mnext!=a.mprev;){a:{var b=a.mprev.vFrom.x,d=a.mprev.vFrom.y,e=a.vFrom.x,k=a.vFrom.y,f=a.mnext.vFrom.x,l=a.mnext.vFrom.y,n=f-e,p=l-k,q=b-f,u=d-l,g=e-b,m=k-d;if(PNLTRI.Math.EPSILON_P>
-g*p-n*m)b=!1;else{for(var h=a.mprev.mprev,s=a.mnext;s!=h;){var s=s.mnext,r=s.vFrom.x,t=s.vFrom.y,v=r-b,w=t-d;if(0!=v||0!=w){var x=r-e,y=t-k;if(0!=x||0!=y)if(r-=f,t-=l,(0!=r||0!=t)&&n*y-p*x>=PNLTRI.Math.EPSILON_N&&g*w-m*v>=PNLTRI.Math.EPSILON_N&&q*t-u*r>=PNLTRI.Math.EPSILON_N){b=!1;break a}}}b=!0}}if(b)this.polyData.addTriangle(a.mprev.vFrom,a.vFrom,a.mnext.vFrom),a.mprev.mnext=a.mnext,a.mnext.mprev=a.mprev,c=a=a.mnext;else if(a=a.mnext,a==c)return!1}return!0}};PNLTRI.Trapezoid=function(a,c,b,d){this.vHigh=a?a:{x:Number.POSITIVE_INFINITY,y:Number.POSITIVE_INFINITY};this.vLow=c?c:{x:Number.NEGATIVE_INFINITY,y:Number.NEGATIVE_INFINITY};this.lseg=b;this.rseg=d;this.depth=-1;this.monoDone=!1};
-PNLTRI.Trapezoid.prototype={constructor:PNLTRI.Trapezoid,clone:function(){var a=new PNLTRI.Trapezoid(this.vHigh,this.vLow,this.lseg,this.rseg);a.uL=this.uL;a.uR=this.uR;a.dL=this.dL;a.dR=this.dR;a.sink=this.sink;return a},splitOffLower:function(a){var c=this.clone();this.vLow=c.vHigh=a;this.dL=c;c.uL=this;this.dR=c.uR=null;c.dL&&(c.dL.uL=c);c.dR&&(c.dR.uR=c);return c}};PNLTRI.QsNode=function(a){this.trap=a;a.sink=this};PNLTRI.QsNode.prototype={constructor:PNLTRI.QsNode};
-PNLTRI.QueryStructure=function(a){var c=new PNLTRI.Trapezoid(null,null,null,null);this.trapArray=[];this.appendTrapEntry(c);this.root=new PNLTRI.QsNode(c);if(a){for(var c=a.getSegments(),b=0;b<c.length;b++)c[b].rootFrom=c[b].rootTo=this.root,c[b].is_inserted=!1;this.compare_pts_yx=a.compare_pts_yx}else this.compare_pts_yx=PNLTRI.PolygonData.prototype.compare_pts_yx};
-PNLTRI.QueryStructure.prototype={constructor:PNLTRI.QueryStructure,getRoot:function(){return this.root},appendTrapEntry:function(a){a.trapID=this.trapArray.length;this.trapArray.push(a)},cloneTrap:function(a){a=a.clone();this.appendTrapEntry(a);return a},splitNodeAtPoint:function(a,c,b){var d=a.trap;if(d.vHigh==c||d.vLow==c)return a;var e=d.splitOffLower(c);this.appendTrapEntry(e);a.yval=c;a.trap=null;a.right=new PNLTRI.QsNode(d);a.left=new PNLTRI.QsNode(e);return b?d.sink:e.sink},fpEqual:function(a,
-c){return Math.abs(a-c)<PNLTRI.Math.EPSILON_P},is_left_of:function(a,c,b){b=a.vFrom.x-c.x;var d=a.vTo.x-c.x;if(Math.abs(a.vTo.y-c.y)<PNLTRI.Math.EPSILON_P)a=d,c=b;else if(Math.abs(a.vFrom.y-c.y)<PNLTRI.Math.EPSILON_P)a=b,c=d;else return a.upward?PNLTRI.Math.ptsCrossProd(a.vFrom,a.vTo,c):PNLTRI.Math.ptsCrossProd(a.vTo,a.vFrom,c);return Math.abs(a)<PNLTRI.Math.EPSILON_P?Math.abs(c)<PNLTRI.Math.EPSILON_P?0:c:a},ptNode:function(a,c,b){for(var d;b;)if(b.yval)d=a==b.yval?c:a,d=this.compare_pts_yx(d,b.yval),
-b=-1==d?b.left:b.right;else if(b.seg)a==b.seg.vFrom||a==b.seg.vTo?this.fpEqual(a.y,c.y)?b=c.x<a.x?b.left:b.right:(d=this.is_left_of(b.seg,c,!1),b=0<d?b.left:0>d?b.right:a==b.seg.vFrom?b.right:b.left):(d=a,d=this.is_left_of(b.seg,d,!0),b=0<d?b.left:b.right);else return b.trap||console.log("ptNode: unknown type",b),b},add_segment:function(a){function c(){var a=g.uL||g.uR;a.dL&&a.dR?g==a.dL?(h.uL=null,a.dL=m):(m.uR=null,a.dR=h):(h.uL=null,h.uR=a,a.dR=h)}function b(a){g.vLow==u.vLow?(q?g.dL?(a.uL=m,m.dL=
-a,h.dR=null):(a.uR=h,m.dL=null,h.dR=a):(a.uL=m,a.uR=h,m.dL=h.dR=a),m.dR=h.dL=null):(a.uL&&a.uR&&(a.uL==g?(a.usave=a.uR,a.uleft=!0):(a.usave=a.uL,a.uleft=!1)),a.uL=m,a.uR=h,m.dR=h.dL=a,m.dL=h.dR=null)}function d(){var b;g.vLow==u.vLow&&q?(g.dL.uL=m,g.dR.uR=h,m.dL=g.dL,h.dR=g.dR,b=m.dR=h.dL=null):(g.dL.uL=m,g.dR.uR=h,0<e.is_left_of(a,g.vLow,!0)?(b=g.dR,g.dR.uL=m,m.dL=g.dL,h.dR=null):(b=g.dL,g.dL.uR=h,h.dR=g.dR,m.dL=null),m.dR=h.dL=b);return b}var e=this,k,f,l,n,p,q;a.upward?(n=a.vFrom,k=a.vTo,p=a.rootFrom,
-f=a.rootTo,q=a.sprev.is_inserted,l=a.snext.is_inserted):(n=a.vTo,k=a.vFrom,p=a.rootTo,f=a.rootFrom,q=a.snext.is_inserted,l=a.sprev.is_inserted);f=this.ptNode(k,n,f);l||(f=this.splitNodeAtPoint(f,k,!1));l=f.trap;if(l.uL||l.uR){f=this.ptNode(n,k,p);q||(f=this.splitNodeAtPoint(f,n,!0));var u=f.trap,g=l,m,h,s,r;for(k=this.trapArray.length+2;g;){if(0>--k){console.log("ERR add_segment: infinite loop",g,a,this);return}if(!g.dL&&!g.dR){console.log("ERR add_segment: missing successors",g,a,this);return}n=
-g.sink;n.seg=a;n.trap=null;r&&r.rseg==g.rseg?(m=g,h=r,h.vLow=g.vLow,n.left=new PNLTRI.QsNode(m),n.right=r.sink):(s&&s.lseg==g.lseg?(h=g,m=s,m.vLow=g.vLow,n.left=s.sink):(m=g,h=this.cloneTrap(g),n.left=new PNLTRI.QsNode(m)),n.right=new PNLTRI.QsNode(h));g.uL&&g.uR?g.usave?(g.uleft?(h.uL=g.uR,h.uR=g.usave,h.uL.dL=h,h.uR.dR=h):(m.uR=g.uL,m.uL=g.usave,m.uL.dL=m,m.uR.dR=m),m.usave=h.usave=null):g.vHigh==l.vHigh?(h.uR.dR=h,m.uR=h.uL=null):h==g?(h.uL=h.uR,h.uR=null,h.uL.dL=h):(m.uR=m.uL,m.uL=null):c();g.dL&&
-g.dR?n=d():(n=g.dL?g.dL:g.dR,b(n));m.rseg&&(m.rseg.trLeft=h);h.lseg&&(h.lseg.trRight=m);m.rseg=h.lseg=a;a.trLeft=m;a.trRight=h;g.vLow!=u.vLow?(s=m,r=h,g=n):g=null}a.is_inserted=!0}else console.log("ERR add_segment: missing trFirst.uX: ",l)},assignDepths:function(a){var c=[this.trapArray[0]],b=[],d,e,k=0;do{for(var f=1==k%2;d=c.pop();)-1==d.depth&&(d.depth=k,d.uL&&c.push(d.uL),d.uR&&c.push(d.uR),d.dL&&c.push(d.dL),d.dR&&c.push(d.dR),(e=d.lseg)&&-1==e.trLeft.depth&&b.push(e.trLeft),e=d.rseg)&&(-1==
-e.trRight.depth&&b.push(e.trRight),e.upward!=f&&a.set_PolyLeft_wrong(e.chainId));c=b;b=[];k++}while(0<c.length)},find_first_inside:function(){for(var a,c=0,b=this.trapArray.length;c<b;c++)if(a=this.trapArray[c],1==a.depth%2&&!a.monoDone&&(!a.uL&&!a.uR||!a.dL&&!a.dR)&&a.lseg)return a;return null}};PNLTRI.Trapezoider=function(a){this.polyData=a;this.queryStructure=new PNLTRI.QueryStructure(this.polyData)};
-PNLTRI.Trapezoider.prototype={constructor:PNLTRI.Trapezoider,find_first_inside:function(){return this.queryStructure.find_first_inside()},math_logstar_n:function(a){var c;for(c=0;1<=a;c++)a=PNLTRI.Math.log2(a);return c-1},math_NH:function(a,c){var b,d;b=0;for(d=a;b<c;b++)d=PNLTRI.Math.log2(d);return Math.ceil(1*a/d)-1},optimise_randomlist:function(a){var c=0,b=this.polyData.nbPolyChains();if(1!=b)for(var d=Array(b),e=a.concat(),k=0;k<e.length;k++){var f=e[k].chainId;d[f]?a[b++]=e[k]:(a[c++]=e[k],
-d[f]=!0)}},trapezoide_polygon:function(){var a=this.polyData.getSegments().concat();PNLTRI.Math.array_shuffle(a);this.optimise_randomlist(a);var c=a.length,b=this.queryStructure,d,e,k,f=this.math_logstar_n(c);for(k=1;k<=f;k++){e=this.math_NH(c,k);for(d=this.math_NH(c,k-1);d<e;d++)b.add_segment(a[d]);for(d=e;d<c;d++)e=a[d],e.rootFrom=this.queryStructure.ptNode(e.vFrom,e.vTo,e.rootFrom),e.rootTo=this.queryStructure.ptNode(e.vTo,e.vFrom,e.rootTo)}for(d=this.math_NH(c,f);d<c;d++)b.add_segment(a[d]);b.assignDepths(this.polyData);
-for(d=0;d<c;d++)a[d].trLeft=null,a[d].trRight=null}};PNLTRI.MonoSplitter=function(a){this.polyData=a;this.startTrap=this.trapezoider=null};
-PNLTRI.MonoSplitter.prototype={constructor:PNLTRI.MonoSplitter,monotonate_trapezoids:function(){this.trapezoider=new PNLTRI.Trapezoider(this.polyData);this.trapezoider.trapezoide_polygon();this.startTrap=this.trapezoider.find_first_inside();this.polyData.initMonoChains();for(var a=this.startTrap;a;)this.alyTrap(this.polyData.newMonoChain(a.lseg),a,null,null,null),a=this.trapezoider.find_first_inside();return this.polyData.normalize_monotone_chains()},doSplit:function(a,c,b,d){return this.polyData.splitPolygonChain(a,
-c,b,d)},alyTrap:function(a,c,b,d,e){function k(){var a;return(a=f.pop())?(l=a[0],n=a[1],p=a[2],q=a[3],!0):!1}var f=[],l,n,p,q,u;null==b&&(d=!0,c.uL?b=!0:c.dL?b=!1:(d=!1,b=c.uR?!0:!1));for(c&&f.push([c,b,d,a]);k();)if(!l.monoDone){l.monoDone=!0;if(!l.lseg||!l.rseg)return console.log("ERR alyTrap: lseg/rseg missing",l),f;n?p?(a=l.uL,c=l.uR,b=l.dL,d=l.dR):(a=l.uR,c=l.uL,b=l.dR,d=l.dL):p?(a=l.dL,c=l.dR,b=l.uL,d=l.uR):(a=l.dR,c=l.dL,b=l.uR,d=l.uL);if(c||d)u=this.doSplit(q,l.vLow,l.vHigh,p);d&&f.push([d,
-n,!p,u]);c&&f.push([c,!n,!p,u]);b&&f.push([b,n,p,q]);b||d||a&&f.push([a,!n,p,q]);if(e)return f}return[]}};PNLTRI.MonoTriangulator=function(a){this.polyData=a};
-PNLTRI.MonoTriangulator.prototype={constructor:PNLTRI.MonoTriangulator,triangulate_all_polygons:function(){var a=this.polyData.getMonoSubPolys();this.polyData.clearTriangles();for(var c=0;c<a.length;c++){var b=a[c],d=b.mprev,e=b.mnext;e.mnext==d?this.polyData.addTriangle(b.vFrom,e.vFrom,d.vFrom):this.triangulate_monotone_polygon(b)}},triangulate_monotone_polygon:function(a){var c=a.mnext;a=a.vFrom;var b=[c.vFrom],d=0,c=c.mnext,e=c.vFrom;if(e!=a){for(;e!=a||1<d;)if(0<d)if(0<PNLTRI.Math.ptsCrossProd(e,
-b[d-1],b[d]))this.polyData.addTriangle(b[d-1],b[d],e),d--;else if(b[++d]=e,e==a)for(console.log("ERR uni-y-monotone: only concave angles left",b);1<d;)d--,this.polyData.addTriangle(b[d-1],b[d],b[d+1]);else c=c.mnext,e=c.vFrom;else b[++d]=e,c=c.mnext,e=c.vFrom;this.polyData.addTriangle(b[d-1],b[d],e)}}};PNLTRI.Triangulator=function(){this.lastPolyData=null};
-PNLTRI.Triangulator.prototype={constructor:PNLTRI.Triangulator,clear_lastData:function(){this.lastPolyData=null},get_PolyLeftArr:function(){return this.lastPolyData?this.lastPolyData.get_PolyLeftArr():null},triangulate_polygon:function(a,c){this.clear_lastData();if(!a||0==a.length)return[];var b=new PNLTRI.PolygonData(a),d;d=c?!1:1==b.nbPolyChains();d&&(d=new PNLTRI.EarClipTriangulator(b),d=d.triangulate_polygon_no_holes());if(!d){(new PNLTRI.MonoSplitter(b)).monotonate_trapezoids();d=new PNLTRI.MonoTriangulator(b);
-d.triangulate_all_polygons();d=b.getSegments();for(var e=0;e<d.length;e++)d[e].vFrom.outSegs=null}this.lastPolyData=b;return b.getTriangles()}};
+'use strict';var r={ca:"2.0"};r.c={random:Math.random,da:function(b){for(var c=b.length-1;0<c;c--){var l=Math.floor(r.c.random()*(c+1)),k=b[c];b[c]=b[l];b[l]=k}return b},G:function(b,c){var l=b.y-c.y;if(l<r.c.D)return-1;if(l>r.c.n)return 1;l=b.x-c.x;return l<r.c.D?-1:l>r.c.n?1:0},N:function(b,c,l){return(c.x-b.x)*(l.y-b.y)-(c.y-b.y)*(l.x-b.x)}};r.c.n=Math.pow(2,-43);r.c.D=-r.c.n;function u(b){this.U=[];this.r=[];this.X=[];this.L=0;this.F=[];this.R=[];this.T=[];if(b)for(var c=0,l=b.length;c<l;c++){var k=w(this,b[c]);if(3>k.length)console.log("Polygon has < 3 vertices!",k);else{for(var a=void 0,e=void 0,d=void 0,f=0;f<k.length-1;f++)a=x(this,k[f],k[f+1]),d?(a.o=d,d.m=a):e=a,d=a,this.r.push(a);a=x(this,k[k.length-1],k[0]);a.o=d;d.m=a;this.r.push(a);e.o=a;a.m=e;this.F[this.L++]=!0}}}u.prototype={Q:function(){return this.F.concat()}};
+function B(b,c,l,k){b.T.push([c.id,l.id,k.id])}
+function w(b,c){function l(a,b){return Math.abs(a.x-b.x)<r.c.n&&Math.abs(a.y-b.y)<r.c.n}function k(a,b,c){if(Math.abs(r.c.N(b,a,c))>r.c.n)return!1;var d;Math.abs(a.y-b.y)<r.c.n?(d=b.x,a.x<c.x?(b=a.x,a=c.x):(b=c.x,a=a.x)):(d=b.y,a.y<c.y?(b=a.y,a=c.y):(b=c.y,a=a.y));return b-d<r.c.n&&d-a<r.c.n}for(var a=[],e,d,f,h=0;h<c.length;h++)e=D(b,c[h].x,c[h].y),d=!0,f=a.length-1,0<=f&&(l(e,a[f])?d=!1:0<f&&k(a[f-1],a[f],e)&&a.pop()),d&&a.push(e);f=a.length-1;0<f&&l(a[f],a[0])&&(a.pop(),f--);1<f&&(k(a[f-1],a[f],
+a[0])&&(a.pop(),f--),1<f&&k(a[f],a[0],a[1])&&a.shift());return a}function x(b,c,l){return{O:b.L,a:c,i:l,p:1==r.c.G(l,c),o:null,m:null,H:null,I:null,v:!1,J:null,K:null,j:null,g:null,w:!1}}function D(b,c,l){c={id:b.U.length,x:c,y:l};b.U.push(c);return c};function E(b){this.k=b}E.prototype={};function F(b,c,l,k){this.q=b?b:{x:Number.POSITIVE_INFINITY,y:Number.POSITIVE_INFINITY};this.l=c?c:{x:Number.NEGATIVE_INFINITY,y:Number.NEGATIVE_INFINITY};this.t=l;this.s=k;this.depth=-1}F.prototype={};function G(b){var c=new F(b.q,b.l,b.t,b.s);c.e=b.e;c.f=b.f;c.b=b.b;c.d=b.d;c.u=b.u;return c}function H(b){this.A=b;b.u=this}H.prototype={};function I(b){var c=new F(null,null,null,null);this.B=[];J(this,c);this.root=new H(c);if(b)for(b=b.r,c=0;c<b.length;c++)b[c].H=b[c].I=this.root,b[c].v=!1}
+I.prototype={P:function(b){var c,l,k=b.U.length,a=Array(k);for(c=0;c<k;c++)a[c]=Array(8);var e=Array(k);c=0;for(l=this.B.length;c<l;c++){var d=this.B[c],f=d.e?d.f?5:7:d.f?4:6,h=d.b?d.d?1:0:d.d?3:2;if(1==d.depth%2){if(5==f||1==h||7==f&&3==h||4==f&&0==h){var g;g={a:d.l,i:d.q,j:null,g:null,w:!1};b.X.push(g);var p;p={a:d.q,i:d.l,aa:g,j:null,g:null,w:!1};b.X.push(p);g.aa=p;a[d.l.id][h]=g;a[d.q.id][f]=p}}else null!=d.q.id&&(e[d.q.id]=f),null!=d.l.id&&(e[d.l.id]=h)}var t;for(c=0;c<k;c++)if(d=a[c],f=e[c],
+null!=f){l=f;h=null;do if(7<l++&&(l=0),b=d[l])h?(b.j=h,h.g=b):(t=b.a,t.Y=b),h=b.aa;while(l!=f);h&&(t.Z=h)}}};
+function L(b,c){function l(){var a=m.e||m.f;a.b&&a.d?m==a.b?(n.e=null,a.b=q):(q.f=null,a.d=n):(n.e=null,n.f=a,a.d=n)}function k(a){m.l==t.l?(f?m.b?(a.e=q,q.b=a,n.d=null):(a.f=n,q.b=null,n.d=a):(a.e=q,a.f=n,q.b=n.d=a),q.d=n.b=null):(a.e&&a.f&&(a.e==m?(a.C=a.f,a.ba=!0):(a.C=a.e,a.ba=!1)),a.e=q,a.f=n,q.d=n.b=a,q.b=n.d=null)}function a(){var a;if(m.l==t.l&&f)m.b.e=q,m.d.f=n,q.b=m.b,n.d=m.d,a=q.d=n.b=null;else{m.b.e=q;m.d.f=n;var b=M(c,m.l);if(0<b)a=!0;else if(0>b)a=!1;else{a=m.b.s;var d=a.p,b=d?a.a:a.i,
+b=M(c,b);0<b?a=!0:0>b?a=!1:(a=d?a.m:a.o,b=d?a.i:a.a,b=M(c,b),a=0<b?!0:!1)}a?(a=m.d,m.d.e=q,q.b=m.b,n.d=null):(a=m.b,m.b.f=n,n.d=m.d,q.b=null);q.d=n.b=a}return a}N(c);var e,d,f,h,g,p;c.p?(e=c.a,h=c.i,d=c.H,g=c.I,f=c.o.v,p=c.m.v):(e=c.i,h=c.a,d=c.I,g=c.H,f=c.m.v,p=c.o.v);p||(p=O(b,g,h,!1),g==d&&(d=p),g=p);g=g.A;if(g.e||g.f)if(g.q!=h)console.log("ERR add_segment: trFirstHigh != segHigh: ",g);else{f||(d=O(b,d,e,!0));var t=d.A,m=g,q,n,y,v;for(e=b.B.length+2;m;){if(0>--e){console.log("ERR add_segment: infinite loop",
+m,c,b);return}if(!m.b&&!m.d){console.log("ERR add_segment: missing successors",m,c,b);return}d=m.u;d.h=c;d.A=null;v&&v.s==m.s?(q=m,n=v,n.l=m.l,d.left=new H(q),d.right=v.u):(y&&y.t==m.t?(n=m,q=y,q.l=m.l,d.left=y.u):(q=m,n=P(b,m),d.left=new H(q)),d.right=new H(n));m.e&&m.f?m.C?(m.ba?(n.e=m.f,n.f=m.C,n.e.b=n,n.f.d=n):(q.f=m.e,q.e=m.C,q.e.b=q,q.f.d=q),q.C=n.C=null):m.q==g.q?(n.f.d=n,q.f=n.e=null):n==m?(n.e=n.f,n.f=null,n.e.b=n):(q.f=q.e,q.e=null):l();m.b&&m.d?d=a():(d=m.b?m.b:m.d,k(d));q.s&&(q.s.J=n);
+n.t&&(n.t.K=q);q.s=n.t=c;c.J=q;c.K=n;m.l!=t.l?(y=q,v=n,m=d):m=null}c.v=!0}else console.log("ERR add_segment: missing trFirst.uX: ",g)}
+function Q(b,c){if(c)var l=b.a,k=b.i,a=b.H;else l=b.i,k=b.a,a=b.I;for(var e;a;)if(a.V)a=-1==r.c.G(l==a.V?k:l,a.V)?a.left:a.right;else if(a.h){if(l==a.h.a||l==a.h.i)if(Math.abs(l.y-k.y)<r.c.n){a=Math.abs(a.h.a.y-a.h.i.y)<r.c.n?l==a.h.a?((e=b.p?k.x>=a.h.i.x:k.x<a.h.i.x)?b.o.p:a.h.m.p)?a.right:a.left:((e=b.p?k.x<a.h.a.x:k.x>=a.h.a.x)?b.m.p:a.h.o.p)?a.left:a.right:k.x<l.x?a.left:a.right;continue}else e=M(a.h,k),0==e&&(e=l==a.h.a?(e=b.p?k.y>=a.h.i.y:k.y<a.h.i.y)?M(a.h,b.o.a):-M(a.h,a.h.m.i):(e=b.p?k.y<
+a.h.a.y:k.y>=a.h.a.y)?M(a.h,b.m.i):-M(a.h,a.h.o.a));else e=M(a.h,l),0==e&&(e=M(a.h,k),0==e&&(e=M(a.h,c?b.o.a:b.m.i)));if(0<e)a=a.left;else if(0>e)a=a.right;else break}else{a.A||console.log("ptNode: unknown type",a);c?b.H=a:b.I=a;break}}function N(b){Q(b,!0);Q(b,!1)}function M(b,c){var l;l=b.a.x-c.x;var k=b.i.x-c.x,a=Math.abs(b.a.y-c.y)<r.c.n;if(Math.abs(b.i.y-c.y)<r.c.n){if(a)return 0;l=k}else if(!a)return b.p?r.c.N(b.a,b.i,c):r.c.N(b.i,b.a,c);return Math.abs(l)<r.c.n?0:l}
+function O(b,c,l,k){var a=c.A;if(a.q==l||a.l==l)return c;var e=G(a);a.l=e.q=l;a.b=e;e.e=a;a.d=e.f=null;e.b&&(e.b.e=e);e.d&&(e.d.f=e);J(b,e);c.V=l;c.A=null;c.right=new H(a);c.left=new H(e);return k?a.u:e.u}function P(b,c){var l=G(c);J(b,l);return l}function J(b,c){c.fa=b.B.length;b.B.push(c)}function V(b){this.k=b;this.$=new I(this.k)}V.prototype={P:function(){return this.$.P(this.k)}};function W(b){this.k=b;this.S=null}W.prototype={};function X(b){this.k=b}X.prototype={};function Z(){this.M=null}
+Z.prototype={W:function(){this.M=null},Q:function(){return this.M?this.M.Q():null},ea:function(b,c){this.W();if(!b||0==b.length)return[];var l=new u(b),k=c?!1:1==l.L;if(k){k=new E(l);a:{var a=k.k,e=a.r[0],d=e,f=e,h=0;do h+=(f.a.x-f.i.x)*(f.a.y+f.i.y),f=f.m;while(f!=e);if(0>h){do d.j=d.m,d=d.g=d.o;while(d!=e);a.F[0]=!1}else{do d.j=d.o,d=d.g=d.m;while(d!=e)}for(e=a=e;a.g!=a.j;){b:{var d=a.j.a.x,f=a.j.a.y,h=a.a.x,g=a.a.y,p=a.g.a.x,t=a.g.a.y,m=p-h,q=t-g,n=d-p,y=f-t,v=h-d,K=g-f;if(r.c.n>v*q-m*K)d=!1;else{for(var Y=
+a.j.j,C=a.g;C!=Y;){var C=C.g,z=C.a.x,A=C.a.y,R=z-d,S=A-f;if(0!=R||0!=S){var T=z-h,U=A-g;if(0!=T||0!=U)if(z-=p,A-=t,(0!=z||0!=A)&&m*U-q*T>=r.c.D&&v*S-K*R>=r.c.D&&n*A-y*z>=r.c.D){d=!1;break b}}}d=!0}}if(d)B(k.k,a.j.a,a.a,a.g.a),a.j.g=a.g,a.g.j=a.j,e=a=a.g;else if(a=a.g,a==e){k=!1;break a}}k=!0}}if(!k){k=new W(l);k.S=new V(k.k);e=k.S;a=e.k.r.concat();r.c.da(a);d=0;f=e.k.L;if(1!=f)for(h=Array(f),g=a.concat(),p=0;p<g.length;p++)t=g[p].O,h[t]?a[f++]=g[p]:(a[d++]=g[p],h[t]=!0);d=a.length;f=e.$;h=0;for(g=
+d;h<d;){g=Math.log(g)/Math.LN2;for(p=1<g?Math.floor(d/g):d;h<p;h++)L(f,a[h]);for(p=h;p<d;p++)N(a[p])}var e=e.k,f=[f.B[0]],h=[],s,p=0;do{for(t=1==p%2;g=f.pop();)-1==g.depth&&(g.depth=p,g.e&&f.push(g.e),g.f&&f.push(g.f),g.b&&f.push(g.b),g.d&&f.push(g.d),(s=g.t)&&-1==s.J.depth&&h.push(s.J),s=g.s)&&(-1==s.K.depth&&h.push(s.K),s.p!=t&&(e.F[s.O]=!1));f=h;h=[];p++}while(0<f.length);for(p=0;p<d;p++)a[p].J=a[p].K=null;k.S.P();s=k.k;d=0;for(f=s.r.length;d<f;d++){a=s.r[d];s.F[a.O]?(e=a.i,a.j=a.o,a.g=a.m):(e=
+a.a,a=a.m,a.j=a.m,a.g=a.o);if(h=a.a.Z)h.g=a,a.j=h,a.a.Z=null;if(h=e.Y)h.j=a,a.g=h,e.Y=null}s=k.k;s.R=[];k=0;for(a=s.r.length;k<a;k++)if(e=s.r[k],!e.w){a:{h=f=d=void 0;g=e;f=h=e.a;e.w=!0;for(e=e.g;d=e.a;){if(e.w){if(d==f)break;console.log("ERR unique_monotone: segment in two chains",f,e);e=null;break a}e.w=!0;1==r.c.G(d,h)&&(h=d,g=e);e=e.g}e=g}e&&s.R.push(e)}s=k=new X(l);k=s.k.R;s.k.T=[];for(a=0;a<k.length;a++)if(f=k[a],e=f.j,d=f.g,d.g==e)B(s.k,f.a,d.a,e.a);else if(e=s,d=f.g,f=f.a,h=[d.a],g=0,d=d.g,
+p=d.a,p!=f){for(;p!=f||1<g;)if(0<g)if(t=r.c.N(h[g],p,h[g-1]),Math.abs(t)<=r.c.n&&(p==f||r.c.G(h[g],p)==r.c.G(h[g],h[g-1]))&&(t=1),0<t)B(e.k,h[g-1],h[g],p),g--;else if(h[++g]=p,p==f)for(console.log("ERR uni-y-monotone: only concave angles left",h);1<g;)g--,B(e.k,h[g-1],h[g],h[g+1]);else d=d.g,p=d.a;else h[++g]=p,d=d.g,p=d.a;B(e.k,h[g-1],h[g],p)}}this.M=l;return l.T.concat()}};window.PNLTRI=r;r.REVISION=r.ca;r.Math=r.c;r.Triangulator=Z;Z.prototype.clear_lastData=Z.prototype.W;Z.prototype.get_PolyLeftArr=Z.prototype.Q;Z.prototype.triangulate_polygon=Z.prototype.ea;

+ 3 - 1
examples/js/loaders/AWDLoader.js

@@ -94,7 +94,7 @@ THREE.AWDLoader = (function (){
     this.materialFactory = undefined;
 
     this._resourceLoader = null;
-    this._url = null;
+    this._url = '';
 
     this._data;
     this._ptr = 0;
@@ -171,6 +171,8 @@ THREE.AWDLoader = (function (){
       this.parseNextBlock();
     }
 
+    return this.trunk;
+
   }
 
 

+ 1 - 1
examples/js/loaders/ColladaLoader.js

@@ -4886,7 +4886,7 @@ THREE.ColladaLoader = function () {
 
 		}
 
-		return new THREE.Matrix4(
+		return new THREE.Matrix4().set(
 			data[0], data[1], data[2], data[3],
 			data[4], data[5], data[6], data[7],
 			data[8], data[9], data[10], data[11],

+ 3 - 13
examples/js/loaders/gltf/glTFLoader.js

@@ -1043,12 +1043,7 @@ THREE.glTFLoader.prototype.load = function( url, callback ) {
 
                 var m = description.matrix;
                 if(m) {
-                    threeNode.applyMatrix(new THREE.Matrix4(
-                        m[0],  m[4],  m[8],  m[12],
-                        m[1],  m[5],  m[9],  m[13],
-                        m[2],  m[6],  m[10], m[14],
-                        m[3],  m[7],  m[11], m[15]
-                    ));
+                    threeNode.applyMatrix(new THREE.Matrix4().fromArray( m ));
                     threeNode.matrixAutoUpdate = false;
                     threeNode.matrixWorldNeedsUpdate = true;
                 }
@@ -1229,7 +1224,7 @@ THREE.glTFLoader.prototype.load = function( url, callback ) {
         	                                    bones.push(joint);
         	                                    
         	                                    var m = skin.inverseBindMatrices;
-        	                    	            var mat = new THREE.Matrix4(
+        	                    	            var mat = new THREE.Matrix4().set(
         	                                            m[i * 16 + 0],  m[i * 16 + 4],  m[i * 16 + 8],  m[i * 16 + 12],
         	                                            m[i * 16 + 1],  m[i * 16 + 5],  m[i * 16 + 9],  m[i * 16 + 13],
         	                                            m[i * 16 + 2],  m[i * 16 + 6],  m[i * 16 + 10], m[i * 16 + 14],
@@ -1458,12 +1453,7 @@ THREE.glTFLoader.prototype.load = function( url, callback ) {
         		};
         		
                 var m = description.bindShapeMatrix;
-	            skin.bindShapeMatrix = new THREE.Matrix4(
-                        m[0],  m[4],  m[8],  m[12],
-                        m[1],  m[5],  m[9],  m[13],
-                        m[2],  m[6],  m[10], m[14],
-                        m[3],  m[7],  m[11], m[15]
-                    );
+	            skin.bindShapeMatrix = new THREE.Matrix4().fromArray( m );
 	            
 	            skin.jointsIds = description.joints;
 	            var inverseBindMatricesDescription = description.inverseBindMatrices;

+ 14 - 5
examples/js/renderers/WebGLDeferredRenderer.js

@@ -7,13 +7,22 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	var _this = this;
 
-	var fullWidth = parameters.width !== undefined ? parameters.width : 800;
-	var fullHeight = parameters.height !== undefined ? parameters.height : 600;
+	var pixelWidth = parameters.width !== undefined ? parameters.width : 800;
+	var pixelHeight = parameters.height !== undefined ? parameters.height : 600;
 	var currentScale = parameters.scale !== undefined ? parameters.scale : 1;
-
+    
+	var devicePixelRatio = parameters.devicePixelRatio !== undefined
+		? parameters.devicePixelRatio
+			: self.devicePixelRatio !== undefined
+				? self.devicePixelRatio
+				: 1;
+
+	var fullWidth = pixelWidth * devicePixelRatio;
+	var fullHeight = pixelHeight * devicePixelRatio;
+	
 	var scaledWidth = Math.floor( currentScale * fullWidth );
 	var scaledHeight = Math.floor( currentScale * fullHeight );
-
+	
 	var brightness = parameters.brightness !== undefined ? parameters.brightness : 1;
 	var tonemapping = parameters.tonemapping !== undefined ? parameters.tonemapping : THREE.SimpleOperator;
 	var antialias = parameters.antialias !== undefined ? parameters.antialias : false;
@@ -22,7 +31,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	if ( this.renderer === undefined ) {
 
-		this.renderer = new THREE.WebGLRenderer( { antialias: false } );
+		this.renderer = new THREE.WebGLRenderer( { antialias: false, devicePixelRatio : devicePixelRatio } );
 		this.renderer.setSize( fullWidth, fullHeight );
 		this.renderer.setClearColor( 0x000000, 0 );
 

+ 0 - 5
examples/js/shaders/FXAAShader.js

@@ -19,11 +19,8 @@ THREE.FXAAShader = {
 
 	vertexShader: [
 
-		"varying vec2 vUv;",
-
 		"void main() {",
 
-			"vUv = uv;",
 			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
 
 		"}"
@@ -35,8 +32,6 @@ THREE.FXAAShader = {
 		"uniform sampler2D tDiffuse;",
 		"uniform vec2 resolution;",
 
-		"varying vec2 vUv;",
-
 		"#define FXAA_REDUCE_MIN   (1.0/128.0)",
 		"#define FXAA_REDUCE_MUL   (1.0/8.0)",
 		"#define FXAA_SPAN_MAX     8.0",

+ 1 - 1
examples/misc_controls_transform.html

@@ -67,8 +67,8 @@
 				var material = new THREE.MeshLambertMaterial( { map: texture } );
 
 				control = new THREE.TransformControls( camera, renderer.domElement );
-
 				control.addEventListener( 'change', render );
+				control.addEventListener( 'objectChange', render );
 
 				var mesh = new THREE.Mesh( geometry, material );
 				scene.add( mesh );

+ 2 - 2
examples/webgl_buffergeometry.html

@@ -217,8 +217,8 @@
 				geometry.computeBoundingSphere();
 
 				var material = new THREE.MeshPhongMaterial( {
-						color: 0xaaaaaa, ambient: 0xaaaaaa, specular: 0xffffff, shininess: 250,
-						side: THREE.DoubleSide, vertexColors: THREE.VertexColors
+					color: 0xaaaaaa, ambient: 0xaaaaaa, specular: 0xffffff, shininess: 250,
+					side: THREE.DoubleSide, vertexColors: THREE.VertexColors
 				} );
 
 				mesh = new THREE.Mesh( geometry, material );

+ 2 - 2
examples/webgl_buffergeometry_uint.html

@@ -198,8 +198,8 @@
 				geometry.computeBoundingSphere();
 
 				var material = new THREE.MeshPhongMaterial( {
-						color: 0xaaaaaa, ambient: 0xaaaaaa, specular: 0xffffff, shininess: 250,
-						side: THREE.DoubleSide, vertexColors: THREE.VertexColors
+					color: 0xaaaaaa, ambient: 0xaaaaaa, specular: 0xffffff, shininess: 250,
+					side: THREE.DoubleSide, vertexColors: THREE.VertexColors
 				} );
 
 				mesh = new THREE.Mesh( geometry, material );

+ 2 - 1
examples/webgl_decals.html

@@ -109,7 +109,7 @@
 	
 			scene.add( camera );
 
-			var light = new THREE.HemisphereLight( 0xffbbbb, 0x000011 );
+			var light = new THREE.HemisphereLight( 0xffddcc, 0x111122 );
 			light.position.set( 1, 0.75, 0.5 );
 			scene.add( light );
 
@@ -289,6 +289,7 @@
 			material.color.setHex( Math.random() * 0xffffff );
 
 			var m = new THREE.Mesh( new THREE.DecalGeometry( mesh, p, r, s, check ), material );
+			m.renderDepth = - scene.children.length;
 			decals.push( m );
 			scene.add( m );
 

+ 7 - 11
examples/webgl_interactive_buffergeometry.html

@@ -279,19 +279,15 @@
 				if ( intersects.length > 0 ) {
 
 					var intersect = intersects[ 0 ];
+					var face = intersect.face;
 
-					var object = intersect.object;
-					var positions = object.geometry.attributes.position.array;
+					var linePosition = line.geometry.attributes.position;
+					var meshPosition = mesh.geometry.attributes.position;
 
-					for ( var i = 0, j = 0; i < 4; i ++, j += 3 ) {
-
-						var index = intersect.indices[ i % 3 ] * 3;
-
-						line.geometry.attributes.position.array[ j     ] = positions[ index     ];
-						line.geometry.attributes.position.array[ j + 1 ] = positions[ index + 1 ];
-						line.geometry.attributes.position.array[ j + 2 ] = positions[ index + 2 ];
-
-					}
+					linePosition.copyAt( 0, meshPosition, face.a );
+					linePosition.copyAt( 1, meshPosition, face.b );
+					linePosition.copyAt( 2, meshPosition, face.c );
+					linePosition.copyAt( 3, meshPosition, face.a );
 
 					mesh.updateMatrix();
 					

+ 0 - 56
examples/webgl_loader_scene.html

@@ -160,36 +160,6 @@
 
 			}
 
-			function handle_update( result, pieces ) {
-
-				var m, material, count = 0;
-
-				for ( m in result.materials ) {
-
-					material = result.materials[ m ];
-					if ( ! ( material instanceof THREE.MeshFaceMaterial || material instanceof THREE.ShaderMaterial || material.morphTargets ) ) {
-
-						if( !material.program ) {
-
-							renderer.initMaterial( material, result.scene.__lights, result.scene.fog );
-
-							count += 1;
-
-							if( count > pieces ) {
-
-								//console.log("xxxxxxxxx");
-								break;
-
-							}
-
-						}
-
-					}
-
-				}
-
-			}
-
 			function init() {
 
 				container = document.createElement( 'div' );
@@ -228,29 +198,6 @@
 
 					$( "bar" ).style.width = bar + "px";
 
-					count = 0;
-					for ( var m in result.materials ) count++;
-
-					handle_update( result, Math.floor( count/total ) );
-
-				}
-
-				var callbackSync = function( result ) {
-
-					/*
-
-					// uncomment to see progressive scene loading
-
-					scene = result.scene;
-					camera = result.currentCamera;
-
-					camera.aspect = window.innerWidth / window.innerHeight;
-					camera.updateProjectionMatrix();
-
-					*/
-
-					//handle_update( result, 1 );
-
 				}
 
 				var callbackFinished = function ( result ) {
@@ -262,8 +209,6 @@
 					$( "start" ).style.display = "block";
 					$( "start" ).className = "enabled";
 
-					handle_update( result, 1 );
-
 					result.scene.traverse( function ( object ) {
 
 						if ( object.userData.rotating === true ) {
@@ -308,7 +253,6 @@
 				loader.addHierarchyHandler( "dae", THREE.ColladaLoader );
 				loader.addHierarchyHandler( "utf8", THREE.UTF8Loader );
 
-				loader.callbackSync = callbackSync;
 				loader.callbackProgress = callbackProgress;
 
 				loader.load( "scenes/test_scene.js", callbackFinished );

+ 1 - 4
examples/webgl_materials_video.html

@@ -123,10 +123,7 @@
 				xsize = 480 / xgrid;
 				ysize = 204 / ygrid;
 
-				var parameters = { color: 0xffffff, map: texture },
-					material_base = new THREE.MeshLambertMaterial( parameters );
-
-				renderer.initMaterial( material_base, scene.__lights, scene.fog );
+				var parameters = { color: 0xffffff, map: texture };
 
 				cube_count = 0;
 

+ 0 - 3
examples/webgl_postprocessing_dof.html

@@ -117,8 +117,6 @@
 
 				var start = Date.now();
 
-				renderer.initMaterial( cubeMaterial, scene.__lights, scene.fog );
-
 				var xgrid = 14,
 					ygrid = 9,
 					zgrid = 14;
@@ -142,7 +140,6 @@
 
 						materials[ c ] = new THREE.MeshBasicMaterial( parameters );
 						mesh = new THREE.Mesh( geo, materials[ c ] );
-						renderer.initMaterial( materials[ c ], scene.__lights, scene.fog, mesh );
 
 					}
 

+ 32 - 7
examples/webgl_shaders_ocean.html

@@ -51,6 +51,7 @@
 
 			var container, stats;
 			var camera, scene, renderer;
+			var sphere;
 
 			var parameters = {
 				width: 2000,
@@ -77,20 +78,20 @@
 				container.appendChild( renderer.domElement );
 
 				scene = new THREE.Scene();
-				camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.5, 3000000 );
 
-				camera.position.set( 0, Math.max( parameters.width * 1.5, parameters.height ) / 8, parameters.height );
-				camera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
+				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.userPan = false;
 				controls.userPanSpeed = 0.0;
 				controls.maxDistance = 5000.0;
 				controls.maxPolarAngle = Math.PI * 0.495;
+				controls.center.set( 0, 500, 0 );
 
-				directionalLight = new THREE.DirectionalLight( 0xffff55, 1 );
-				directionalLight.position.set( - 1, 0.4, - 1 );
-				scene.add( directionalLight );
+				var light = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 );
+				light.position.set( - 1, 1, - 1 );
+				scene.add( light );
 
 				
 				waterNormals = new THREE.ImageUtils.loadTexture( 'textures/waternormals.jpg' );
@@ -101,7 +102,7 @@
 					textureHeight: 512,
 					waterNormals: waterNormals,
 					alpha: 	1.0,
-					sunDirection: directionalLight.position.normalize(),
+					sunDirection: light.position.clone().normalize(),
 					sunColor: 0xffffff,
 					waterColor: 0x001e0f,
 					distortionScale: 50.0,
@@ -171,6 +172,24 @@
 				
 				scene.add( skyBox );
 
+
+				var geometry = new THREE.IcosahedronGeometry( 400, 4 );
+
+				for ( var i = 0, j = geometry.faces.length; i < j; i ++ ) {
+
+					geometry.faces[ i ].color.setHex( Math.random() * 0xffffff );
+
+				}
+
+				var material = new THREE.MeshPhongMaterial( {
+					vertexColors: THREE.FaceColors,
+					shininess: 100,
+					envMap: cubeMap
+				} );
+				
+				sphere = new THREE.Mesh( geometry, material );
+				scene.add( sphere );
+
 			}
 
 			//
@@ -184,6 +203,12 @@
 
 			function render() {
 
+				var time = performance.now() * 0.001;
+
+				sphere.position.y = Math.sin( time ) * 500 + 250;
+				sphere.rotation.x = time * 0.5;
+				sphere.rotation.z = time * 0.51;
+
 				water.material.uniforms.time.value += 1.0 / 60.0;
 				controls.update();
 				water.render();

+ 1 - 0
examples/webgl_shadowmap.html

@@ -95,6 +95,7 @@
 				controls.verticalMax = 2.0;
 
 				controls.lon = 250;
+				controls.lat = 30;
 
 				// SCENE
 

+ 2 - 1
examples/webgl_shadowmap_performance.html

@@ -89,7 +89,8 @@
 				controls.verticalMin = 1.5;
 				controls.verticalMax = 2.0;
 
-				controls.lon = -110;
+				controls.lon = 250;
+				controls.lat = 30;
 
 				// SCENE
 

+ 2 - 0
src/cameras/Camera.js

@@ -8,6 +8,8 @@ THREE.Camera = function () {
 
 	THREE.Object3D.call( this );
 
+	this.type = 'Camera';
+
 	this.matrixWorldInverse = new THREE.Matrix4();
 	this.projectionMatrix = new THREE.Matrix4();
 

+ 2 - 0
src/cameras/CubeCamera.js

@@ -9,6 +9,8 @@ THREE.CubeCamera = function ( near, far, cubeResolution ) {
 
 	THREE.Object3D.call( this );
 
+	this.type = 'CubeCamera';
+
 	var fov = 90, aspect = 1;
 
 	var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far );

+ 2 - 0
src/cameras/OrthographicCamera.js

@@ -6,6 +6,8 @@ THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) {
 
 	THREE.Camera.call( this );
 
+	this.type = 'OrthographicCamera';
+
 	this.left = left;
 	this.right = right;
 	this.top = top;

+ 2 - 0
src/cameras/PerspectiveCamera.js

@@ -8,6 +8,8 @@ THREE.PerspectiveCamera = function ( fov, aspect, near, far ) {
 
 	THREE.Camera.call( this );
 
+	this.type = 'PerspectiveCamera';
+
 	this.fov = fov !== undefined ? fov : 50;
 	this.aspect = aspect !== undefined ? aspect : 1;
 	this.near = near !== undefined ? near : 0.1;

+ 13 - 0
src/core/BufferAttribute.js

@@ -19,6 +19,19 @@ THREE.BufferAttribute.prototype = {
 
 	},
 
+	copyAt: function ( index1, attribute, index2 ) {
+
+		index1 *= this.itemSize;
+		index2 *= attribute.itemSize;
+
+		for ( var i = 0, l = this.itemSize; i < l; i ++ ) {
+
+			this.array[ index1 + i ] = attribute.array[ index2 + i ];
+
+		}
+
+	},
+
 	set: function ( value ) {
 
 		this.array.set( value );

+ 60 - 0
src/core/BufferGeometry.js

@@ -1,5 +1,6 @@
 /**
  * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
  */
 
 THREE.BufferGeometry = function () {
@@ -8,6 +9,7 @@ THREE.BufferGeometry = function () {
 	this.uuid = THREE.Math.generateUUID();
 
 	this.name = '';
+	this.type = 'BufferGeometry';
 
 	this.attributes = {};
 	this.drawcalls = [];
@@ -868,6 +870,64 @@ THREE.BufferGeometry.prototype = {
 		}
 	},
 
+	toJSON: function () {
+
+		var output = {
+			metadata: {
+				version: 4.0,
+				type: 'BufferGeometry',
+				generator: 'BufferGeometryExporter'
+			},
+			uuid: this.uuid,
+			type: this.type,
+			data: {
+				attributes: {}
+			}
+		};
+
+		var attributes = this.attributes;
+		var offsets = this.offsets;
+		var boundingSphere = this.boundingSphere;
+
+		for ( var key in attributes ) {
+
+			var attribute = attributes[ key ];
+
+			var array = [], typeArray = attribute.array;
+
+			for ( var i = 0, l = typeArray.length; i < l; i ++ ) {
+
+				array[ i ] = typeArray[ i ];
+
+			}
+
+			output.data.attributes[ key ] = {
+				itemSize: attribute.itemSize,
+				type: attribute.array.constructor.name,
+				array: array
+			}
+
+		}
+
+		if ( offsets.length > 0 ) {
+
+			output.data.offsets = JSON.parse( JSON.stringify( offsets ) );
+
+		}
+
+		if ( boundingSphere !== null ) {
+
+			output.data.boundingSphere = {
+				center: boundingSphere.center.toArray(),
+				radius: boundingSphere.radius
+			}
+
+		}
+
+		return output;
+
+	},
+
 	clone: function () {
 
 		var geometry = new THREE.BufferGeometry();

+ 286 - 0
src/core/Geometry.js

@@ -13,6 +13,7 @@ THREE.Geometry = function () {
 	this.uuid = THREE.Math.generateUUID();
 
 	this.name = '';
+	this.type = 'Geometry';
 
 	this.vertices = [];
 	this.colors = [];  // one-to-one vertex colors, used in Points and Line
@@ -94,6 +95,90 @@ THREE.Geometry.prototype = {
 
 	},
 
+	fromBufferGeometry: function ( geometry ) {
+
+		var scope = this;
+
+		var attributes = geometry.attributes;
+
+		var indices = attributes.index !== undefined && attributes.index.array;
+		var normals = attributes.normal !== undefined && attributes.normal.array;
+		var colors = attributes.color !== undefined && attributes.color.array;
+		var uvs = attributes.uv !== undefined && attributes.uv.array;
+
+		var vertices = attributes.position.array;
+
+		var tempNormals = [];
+		var tempUVs = [];
+
+		for ( var i = 0, j = 0; i < vertices.length; i += 3, j += 2 ) {
+
+			scope.vertices.push( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) );
+
+			if ( normals !== undefined ) {
+
+				tempNormals.push( new THREE.Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) );
+
+			}
+
+			if ( colors !== undefined ) {
+
+				scope.colors.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) );
+
+			}
+
+			if ( uvs !== undefined ) {
+
+				tempUVs.push( new THREE.Vector2( uvs[ j ], uvs[ j + 1 ] ) );
+
+			}
+
+		}
+
+		var addFace = function ( a, b, c ) {
+
+			var vertexNormals = normals !== undefined ? [ tempNormals[ a ], tempNormals[ b ], tempNormals[ c ] ] : [];
+			var vertexColors = colors !== undefined ? [ scope.colors[ a ], scope.colors[ b ], scope.colors[ c ] ] : [];
+
+			scope.faces.push( new THREE.Face3( a, b, c, vertexNormals, vertexColors ) );
+			scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ], tempUVs[ b ], tempUVs[ c ] ] );
+
+		};
+
+		if ( indices !== undefined ) {
+
+			for ( var i = 0; i < indices.length; i += 3 ) {
+
+				addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );
+
+			}
+
+		} else {
+
+			for ( var i = 0; i < vertices.length / 3; i += 3 ) {
+
+				addFace( i, i + 1, i + 2 );
+
+			}
+
+		}
+
+		if ( geometry.boundingBox !== null ) {
+
+			this.boundingBox = geometry.boundingBox.clone();
+
+		}
+
+		if ( geometry.boundingSphere !== null ) {
+
+			this.boundingSphere = geometry.boundingSphere.clone();
+
+		}
+
+		return this;
+
+	},
+
 	center: function () {
 
 		this.computeBoundingBox();
@@ -731,6 +816,207 @@ THREE.Geometry.prototype = {
 
 	} )(),
 
+	toJSON: function () {
+
+		var output = {
+			metadata: {
+				version: 4.0,
+				type: 'BufferGeometry',
+				generator: 'BufferGeometryExporter'
+			},
+			uuid: this.uuid,
+			type: this.type
+		};
+
+		if ( this.name !== "" ) output.name = this.name;
+
+		if ( this.parameters !== undefined ) {
+
+			var parameters = this.parameters;
+
+			for ( var key in parameters ) {
+
+				if ( parameters[ key ] !== undefined ) output[ key ] = parameters[ key ];
+
+			}
+
+			return output;
+
+		}
+
+		var vertices = [];
+
+		for ( var i = 0; i < this.vertices.length; i ++ ) {
+
+			var vertex = this.vertices[ i ];
+			vertices.push( vertex.x, vertex.y, vertex.z );
+
+		}
+
+		var faces = [];
+		var normals = [];
+		var normalsHash = {};
+		var colors = [];
+		var colorsHash = {};
+		var uvs = [];
+		var uvsHash = {};
+
+		for ( var i = 0; i < this.faces.length; i ++ ) {
+
+			var face = this.faces[ i ];
+
+			var hasMaterial = false; // face.materialIndex !== undefined;
+			var hasFaceUv = false; // deprecated
+			var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined;
+			var hasFaceNormal = face.normal.length() > 0;
+			var hasFaceVertexNormal = face.vertexNormals.length > 0;
+			var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1;
+			var hasFaceVertexColor = face.vertexColors.length > 0;
+
+			var faceType = 0;
+
+			faceType = setBit( faceType, 0, 0 );
+			faceType = setBit( faceType, 1, hasMaterial );
+			faceType = setBit( faceType, 2, hasFaceUv );
+			faceType = setBit( faceType, 3, hasFaceVertexUv );
+			faceType = setBit( faceType, 4, hasFaceNormal );
+			faceType = setBit( faceType, 5, hasFaceVertexNormal );
+			faceType = setBit( faceType, 6, hasFaceColor );
+			faceType = setBit( faceType, 7, hasFaceVertexColor );
+
+			faces.push( faceType );
+			faces.push( face.a, face.b, face.c );
+
+
+			/*
+			if ( hasMaterial ) {
+
+				faces.push( face.materialIndex );
+
+			}
+			*/
+
+			if ( hasFaceVertexUv ) {
+
+				var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ];
+
+				faces.push(
+					getUvIndex( faceVertexUvs[ 0 ] ),
+					getUvIndex( faceVertexUvs[ 1 ] ),
+					getUvIndex( faceVertexUvs[ 2 ] )
+				);
+
+			}
+
+			if ( hasFaceNormal ) {
+
+				faces.push( getNormalIndex( face.normal ) );
+
+			}
+
+			if ( hasFaceVertexNormal ) {
+
+				var vertexNormals = face.vertexNormals;
+
+				faces.push(
+					getNormalIndex( vertexNormals[ 0 ] ),
+					getNormalIndex( vertexNormals[ 1 ] ),
+					getNormalIndex( vertexNormals[ 2 ] )
+				);
+
+			}
+
+			if ( hasFaceColor ) {
+
+				faces.push( getColorIndex( face.color ) );
+
+			}
+
+			if ( hasFaceVertexColor ) {
+
+				var vertexColors = face.vertexColors;
+
+				faces.push(
+					getColorIndex( vertexColors[ 0 ] ),
+					getColorIndex( vertexColors[ 1 ] ),
+					getColorIndex( vertexColors[ 2 ] )
+				);
+
+			}
+
+		}
+
+		function setBit( value, position, enabled ) {
+
+			return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position) );
+
+		}
+
+		function getNormalIndex( normal ) {
+
+			var hash = normal.x.toString() + normal.y.toString() + normal.z.toString();
+
+			if ( normalsHash[ hash ] !== undefined ) {
+
+				return normalsHash[ hash ];
+
+			}
+
+			normalsHash[ hash ] = normals.length / 3;
+			normals.push( normal.x, normal.y, normal.z );
+
+			return normalsHash[ hash ];
+
+		}
+
+		function getColorIndex( color ) {
+
+			var hash = color.r.toString() + color.g.toString() + color.b.toString();
+
+			if ( colorsHash[ hash ] !== undefined ) {
+
+				return colorsHash[ hash ];
+
+			}
+
+			colorsHash[ hash ] = colors.length;
+			colors.push( color.getHex() );
+
+			return colorsHash[ hash ];
+
+		}
+
+		function getUvIndex( uv ) {
+
+			var hash = uv.x.toString() + uv.y.toString();
+
+			if ( uvsHash[ hash ] !== undefined ) {
+
+				return uvsHash[ hash ];
+
+			}
+
+			uvsHash[ hash ] = uvs.length / 2;
+			uvs.push( uv.x, uv.y );
+
+			return uvsHash[ hash ];
+
+		}
+
+		output.data = {};
+
+		output.data.vertices = vertices;
+		output.data.normals = normals;
+		if ( colors.length > 0 ) output.data.colors = colors;
+		if ( uvs.length > 0 ) output.data.uvs = [ uvs ]; // temporal backward compatibility
+		output.data.faces = faces;
+
+		//
+
+		return output;
+
+	},
+
 	clone: function () {
 
 		var geometry = new THREE.Geometry();

+ 162 - 0
src/core/Object3D.js

@@ -11,6 +11,7 @@ THREE.Object3D = function () {
 	this.uuid = THREE.Math.generateUUID();
 
 	this.name = '';
+	this.type = 'Object3D';
 
 	this.parent = undefined;
 	this.children = [];
@@ -499,6 +500,165 @@ THREE.Object3D.prototype = {
 
 	},
 
+	toJSON: function () {
+
+		var output = {
+			metadata: {
+				version: 4.3,
+				type: 'Object',
+				generator: 'ObjectExporter'
+			}
+		};
+
+		//
+
+		var geometries = {};
+
+		var parseGeometry = function ( geometry ) {
+
+			if ( output.geometries === undefined ) {
+
+				output.geometries = [];
+
+			}
+
+			if ( geometries[ geometry.uuid ] === undefined ) {
+
+				var json = geometry.toJSON();
+
+				delete json.metadata;
+
+				geometries[ geometry.uuid ] = json;
+
+				output.geometries.push( json );
+
+			}
+
+			return geometry.uuid;
+
+		};
+
+		//
+
+		var materials = {};
+
+		var parseMaterial = function ( material ) {
+
+			if ( output.materials === undefined ) {
+
+				output.materials = [];
+
+			}
+
+			if ( materials[ material.uuid ] === undefined ) {
+
+				var json = material.toJSON();
+
+				delete json.metadata;
+
+				materials[ material.uuid ] = json;
+
+				output.materials.push( json );
+
+			}
+
+			return material.uuid;
+
+		};
+
+		//
+
+		var parseObject = function ( object ) {
+
+			var data = {};
+
+			data.uuid = object.uuid;
+			data.type = object.type;
+
+			if ( object.name !== '' ) data.name = object.name;
+			if ( JSON.stringify( object.userData ) !== '{}' ) data.userData = object.userData;
+			if ( object.script !== undefined ) data.script = object.script.source;
+			if ( object.visible !== true ) data.visible = object.visible;
+
+			if ( object instanceof THREE.PerspectiveCamera ) {
+
+				data.fov = object.fov;
+				data.aspect = object.aspect;
+				data.near = object.near;
+				data.far = object.far;
+
+			} else if ( object instanceof THREE.OrthographicCamera ) {
+
+				data.left = object.left;
+				data.right = object.right;
+				data.top = object.top;
+				data.bottom = object.bottom;
+				data.near = object.near;
+				data.far = object.far;
+
+			} else if ( object instanceof THREE.AmbientLight ) {
+
+				data.color = object.color.getHex();
+
+			} else if ( object instanceof THREE.DirectionalLight ) {
+
+				data.color = object.color.getHex();
+				data.intensity = object.intensity;
+
+			} else if ( object instanceof THREE.PointLight ) {
+
+				data.color = object.color.getHex();
+				data.intensity = object.intensity;
+				data.distance = object.distance;
+
+			} else if ( object instanceof THREE.SpotLight ) {
+
+				data.color = object.color.getHex();
+				data.intensity = object.intensity;
+				data.distance = object.distance;
+				data.angle = object.angle;
+				data.exponent = object.exponent;
+
+			} else if ( object instanceof THREE.HemisphereLight ) {
+
+				data.color = object.color.getHex();
+				data.groundColor = object.groundColor.getHex();
+
+			} else if ( object instanceof THREE.Mesh ) {
+
+				data.geometry = parseGeometry( object.geometry );
+				data.material = parseMaterial( object.material );
+
+			} else if ( object instanceof THREE.Sprite ) {
+
+				data.material = parseMaterial( object.material );
+
+			}
+
+			data.matrix = object.matrix.toArray();
+
+			if ( object.children.length > 0 ) {
+
+				data.children = [];
+
+				for ( var i = 0; i < object.children.length; i ++ ) {
+
+					data.children.push( parseObject( object.children[ i ] ) );
+
+				}
+
+			}
+
+			return data;
+
+		}
+
+		output.object = parseObject( this );
+
+		return output;
+
+	},
+
 	clone: function ( object, recursive ) {
 
 		if ( object === undefined ) object = new THREE.Object3D();
@@ -531,6 +691,8 @@ THREE.Object3D.prototype = {
 
 		object.userData = JSON.parse( JSON.stringify( this.userData ) );
 
+		if ( this.script !== undefined ) object.script = this.script.clone();
+
 		if ( recursive === true ) {
 
 			for ( var i = 0; i < this.children.length; i ++ ) {

+ 22 - 0
src/core/Script.js

@@ -0,0 +1,22 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Script = function ( source ) {
+
+	this.uuid = THREE.Math.generateUUID();
+	this.source = source;
+
+};
+
+THREE.Script.prototype = {
+
+	constructor: THREE.Script,
+
+	clone: function () {
+
+		return new THREE.Script( this.source );
+
+	}
+
+};

+ 16 - 7
src/extras/animation/Animation.js

@@ -183,15 +183,23 @@ THREE.Animation.prototype.update = (function(){
 
 		var duration = this.data.length;
 
-		if ( this.loop === true && this.currentTime > duration ) {
+		if ( this.currentTime > duration || this.currentTime < 0 ) {
 
-			this.currentTime %= duration;
-			this.reset();
+			if ( this.loop ) {
 
-		} else if ( this.loop === false && this.currentTime > duration ) {
+				this.currentTime %= duration;
 
-			this.stop();
-			return;
+				if ( this.currentTime < 0 )
+					this.currentTime += duration;
+
+				this.reset();
+
+			} else {
+
+				this.stop();
+				return;
+
+			}
 
 		}
 
@@ -211,7 +219,8 @@ THREE.Animation.prototype.update = (function(){
 				var prevKey = animationCache.prevKey[ type ];
 				var nextKey = animationCache.nextKey[ type ];
 
-				if ( nextKey.time <= this.currentTime ) {
+				if ( ( this.timeScale > 0 && nextKey.time <= this.currentTime ) ||
+					( this.timeScale < 0 && prevKey.time >= this.currentTime ) ) {
 
 					prevKey = this.data.hierarchy[ h ].keys[ 0 ];
 					nextKey = this.getNextKeyWith( type, h, 1 );

+ 2 - 0
src/extras/geometries/BoxGeometry.js

@@ -7,6 +7,8 @@ THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegmen
 
 	THREE.Geometry.call( this );
 
+	this.type = 'BoxGeometry';
+
 	this.parameters = {
 		width: width,
 		height: height,

+ 2 - 0
src/extras/geometries/CircleGeometry.js

@@ -6,6 +6,8 @@ THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) {
 
 	THREE.Geometry.call( this );
 
+	this.type = 'CircleGeometry';
+
 	this.parameters = {
 		radius: radius,
 		segments: segments,

+ 2 - 0
src/extras/geometries/CylinderGeometry.js

@@ -6,6 +6,8 @@ THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegme
 
 	THREE.Geometry.call( this );
 
+	this.type = 'CylinderGeometry';
+
 	this.parameters = {
 		radiusTop: radiusTop,
 		radiusBottom: radiusBottom,

+ 2 - 0
src/extras/geometries/ExtrudeGeometry.js

@@ -33,6 +33,8 @@ THREE.ExtrudeGeometry = function ( shapes, options ) {
 
 	THREE.Geometry.call( this );
 
+	this.type = 'ExtrudeGeometry';
+
 	shapes = shapes instanceof Array ? shapes : [ shapes ];
 
 	this.addShapeList( shapes, options );

+ 6 - 5
src/extras/geometries/IcosahedronGeometry.js

@@ -4,11 +4,6 @@
 
 THREE.IcosahedronGeometry = function ( radius, detail ) {
 
-	this.parameters = {
-		radius: radius,
-		detail: detail
-	};
-
 	var t = ( 1 + Math.sqrt( 5 ) ) / 2;
 
 	var vertices = [
@@ -26,6 +21,12 @@ THREE.IcosahedronGeometry = function ( radius, detail ) {
 
 	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
 
+	this.type = 'IcosahedronGeometry';
+
+	this.parameters = {
+		radius: radius,
+		detail: detail
+	};
 };
 
 THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );

+ 10 - 0
src/extras/geometries/LatheGeometry.js

@@ -10,10 +10,20 @@
 // phiStart - the starting radian
 // phiLength - the radian (0 to 2*PI) range of the lathed section
 //    2*pi is a closed lathe, less than 2PI is a portion.
+
 THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) {
 
 	THREE.Geometry.call( this );
 
+	this.type = 'LatheGeometry';
+
+	this.parameters = {
+		points: points,
+		segments: segments,
+		phiStart: phiStart,
+		phiLength: phiLength
+	};
+
 	segments = segments || 12;
 	phiStart = phiStart || 0;
 	phiLength = phiLength || 2 * Math.PI;

+ 7 - 0
src/extras/geometries/OctahedronGeometry.js

@@ -18,6 +18,13 @@ THREE.OctahedronGeometry = function ( radius, detail ) {
 	];
 
 	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
+
+	this.type = 'OctahedronGeometry';
+
+	this.parameters = {
+		radius: radius,
+		detail: detail
+	};
 };
 
 THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );

+ 8 - 0
src/extras/geometries/ParametricGeometry.js

@@ -11,6 +11,14 @@ THREE.ParametricGeometry = function ( func, slices, stacks ) {
 
 	THREE.Geometry.call( this );
 
+	this.type = 'ParametricGeometry';
+
+	this.parameters = {
+		func: func,
+		slices: slices,
+		stacks: stacks
+	};
+
 	var verts = this.vertices;
 	var faces = this.faces;
 	var uvs = this.faceVertexUvs[ 0 ];

+ 2 - 0
src/extras/geometries/PlaneGeometry.js

@@ -7,6 +7,8 @@ THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments )
 
 	THREE.Geometry.call( this );
 
+	this.type = 'PlaneGeometry';
+	
 	this.parameters = {
 		width: width,
 		height: height,

+ 9 - 0
src/extras/geometries/PolyhedronGeometry.js

@@ -8,6 +8,15 @@ THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) {
 
 	THREE.Geometry.call( this );
 
+	this.type = 'PolyhedronGeometry';
+
+	this.parameters = {
+		vertices: vertices,
+		indices: indices,
+		radius: radius,
+		detail: detail
+	};
+
 	radius = radius || 1;
 	detail = detail || 0;
 

+ 11 - 0
src/extras/geometries/RingGeometry.js

@@ -6,6 +6,17 @@ THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegm
 
 	THREE.Geometry.call( this );
 
+	this.type = 'RingGeometry';
+
+	this.parameters = {
+		innerRadius: innerRadius,
+		outerRadius: outerRadius,
+		thetaSegments: thetaSegments,
+		phiSegments: phiSegments,
+		thetaStart: thetaStart,
+		thetaLength: thetaLength
+	};
+
 	innerRadius = innerRadius || 0;
 	outerRadius = outerRadius || 50;
 

+ 2 - 0
src/extras/geometries/ShapeGeometry.js

@@ -18,6 +18,8 @@ THREE.ShapeGeometry = function ( shapes, options ) {
 
 	THREE.Geometry.call( this );
 
+	this.type = 'ShapeGeometry';
+
 	if ( shapes instanceof Array === false ) shapes = [ shapes ];
 
 	this.addShapeList( shapes, options );

+ 2 - 0
src/extras/geometries/SphereGeometry.js

@@ -6,6 +6,8 @@ THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStar
 
 	THREE.Geometry.call( this );
 
+	this.type = 'SphereGeometry';
+
 	this.parameters = {
 		radius: radius,
 		widthSegments: widthSegments,

+ 7 - 0
src/extras/geometries/TetrahedronGeometry.js

@@ -14,6 +14,13 @@ THREE.TetrahedronGeometry = function ( radius, detail ) {
 
 	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
 
+	this.type = 'TetrahedronGeometry';
+
+	this.parameters = {
+		radius: radius,
+		detail: detail
+	};
+
 };
 
 THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );

+ 2 - 0
src/extras/geometries/TextGeometry.js

@@ -54,6 +54,8 @@ THREE.TextGeometry = function ( text, parameters ) {
 
 	THREE.ExtrudeGeometry.call( this, textShapes, parameters );
 
+	this.type = 'TextGeometry';
+
 };
 
 THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype );

+ 2 - 0
src/extras/geometries/TorusGeometry.js

@@ -8,6 +8,8 @@ THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments,
 
 	THREE.Geometry.call( this );
 
+	this.type = 'TorusGeometry';
+
 	this.parameters = {
 		radius: radius,
 		tube: tube,

+ 2 - 0
src/extras/geometries/TorusKnotGeometry.js

@@ -7,6 +7,8 @@ THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegmen
 
 	THREE.Geometry.call( this );
 
+	this.type = 'TorusKnotGeometry';
+
 	this.parameters = {
 		radius: radius,
 		tube: tube,

+ 2 - 0
src/extras/geometries/TubeGeometry.js

@@ -15,6 +15,8 @@ THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed )
 
 	THREE.Geometry.call( this );
 
+	this.type = 'TubeGeometry';
+
 	this.parameters = {
 		path: path,
 		segments: segments,

+ 2 - 0
src/lights/AmbientLight.js

@@ -6,6 +6,8 @@ THREE.AmbientLight = function ( color ) {
 
 	THREE.Light.call( this, color );
 
+	this.type = 'AmbientLight';
+
 };
 
 THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype );

+ 2 - 0
src/lights/AreaLight.js

@@ -7,6 +7,8 @@ THREE.AreaLight = function ( color, intensity ) {
 
 	THREE.Light.call( this, color );
 
+	this.type = 'AreaLight';
+
 	this.normal = new THREE.Vector3( 0, - 1, 0 );
 	this.right = new THREE.Vector3( 1, 0, 0 );
 

+ 2 - 0
src/lights/DirectionalLight.js

@@ -7,6 +7,8 @@ THREE.DirectionalLight = function ( color, intensity ) {
 
 	THREE.Light.call( this, color );
 
+	this.type = 'DirectionalLight';
+
 	this.position.set( 0, 1, 0 );
 	this.target = new THREE.Object3D();
 

+ 2 - 0
src/lights/HemisphereLight.js

@@ -6,6 +6,8 @@ THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) {
 
 	THREE.Light.call( this, skyColor );
 
+	this.type = 'HemisphereLight';
+
 	this.position.set( 0, 100, 0 );
 
 	this.groundColor = new THREE.Color( groundColor );

+ 2 - 0
src/lights/Light.js

@@ -7,6 +7,8 @@ THREE.Light = function ( color ) {
 
 	THREE.Object3D.call( this );
 
+	this.type = 'Light';
+	
 	this.color = new THREE.Color( color );
 
 };

+ 2 - 0
src/lights/PointLight.js

@@ -6,6 +6,8 @@ THREE.PointLight = function ( color, intensity, distance ) {
 
 	THREE.Light.call( this, color );
 
+	this.type = 'PointLight';
+
 	this.intensity = ( intensity !== undefined ) ? intensity : 1;
 	this.distance = ( distance !== undefined ) ? distance : 0;
 

+ 2 - 0
src/lights/SpotLight.js

@@ -6,6 +6,8 @@ THREE.SpotLight = function ( color, intensity, distance, angle, exponent ) {
 
 	THREE.Light.call( this, color );
 
+	this.type = 'SpotLight';
+
 	this.position.set( 0, 1, 0 );
 	this.target = new THREE.Object3D();
 

+ 1 - 0
src/loaders/MaterialLoader.js

@@ -45,6 +45,7 @@ THREE.MaterialLoader.prototype = {
 		if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader;
 		if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader;		
 		if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors;
+		if ( json.shading !== undefined ) material.shading = json.shading;
 		if ( json.blending !== undefined ) material.blending = json.blending;
 		if ( json.side !== undefined ) material.side = json.side;
 		if ( json.opacity !== undefined ) material.opacity = json.opacity;

+ 1 - 0
src/loaders/ObjectLoader.js

@@ -332,6 +332,7 @@ THREE.ObjectLoader.prototype = {
 
 			if ( data.visible !== undefined ) object.visible = data.visible;
 			if ( data.userData !== undefined ) object.userData = data.userData;
+			if ( data.script !== undefined ) object.script = new THREE.Script( data.script );
 
 			if ( data.children !== undefined ) {
 

+ 2 - 0
src/materials/LineBasicMaterial.js

@@ -24,6 +24,8 @@ THREE.LineBasicMaterial = function ( parameters ) {
 
 	THREE.Material.call( this );
 
+	this.type = 'LineBasicMaterial';
+
 	this.color = new THREE.Color( 0xffffff );
 
 	this.linewidth = 1;

+ 2 - 0
src/materials/LineDashedMaterial.js

@@ -25,6 +25,8 @@ THREE.LineDashedMaterial = function ( parameters ) {
 
 	THREE.Material.call( this );
 
+	this.type = 'LineDashedMaterial';
+
 	this.color = new THREE.Color( 0xffffff );
 
 	this.linewidth = 1;

+ 73 - 0
src/materials/Material.js

@@ -9,6 +9,7 @@ THREE.Material = function () {
 	this.uuid = THREE.Math.generateUUID();
 
 	this.name = '';
+	this.type = 'Material';
 
 	this.side = THREE.FrontSide;
 
@@ -84,6 +85,78 @@ THREE.Material.prototype = {
 
 	},
 
+	toJSON: function () {
+
+		var output = {
+			metadata: {
+				version: 4.2,
+				type: 'material',
+				generator: 'MaterialExporter'
+			},
+			uuid: this.uuid,
+			type: this.type
+		};
+
+		if ( this.name !== "" ) output.name = this.name;
+
+		if ( this instanceof THREE.MeshBasicMaterial ) {
+
+			output.color = this.color.getHex();
+			if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors;
+			if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending;
+			if ( this.side !== THREE.FrontSide ) output.side = this.side;
+
+		} else if ( this instanceof THREE.MeshLambertMaterial ) {
+
+			output.color = this.color.getHex();
+			output.ambient = this.ambient.getHex();
+			output.emissive = this.emissive.getHex();
+			if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors;
+			if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending;
+			if ( this.side !== THREE.FrontSide ) output.side = this.side;
+
+		} else if ( this instanceof THREE.MeshPhongMaterial ) {
+
+			output.color = this.color.getHex();
+			output.ambient = this.ambient.getHex();
+			output.emissive = this.emissive.getHex();
+			output.specular = this.specular.getHex();
+			output.shininess = this.shininess;
+			if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors;
+			if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending;
+			if ( this.side !== THREE.FrontSide ) output.side = this.side;
+
+		} else if ( this instanceof THREE.MeshNormalMaterial ) {
+
+			if ( this.shading !== THREE.FlatShading ) output.shading = this.shading;
+			if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending;
+			if ( this.side !== THREE.FrontSide ) output.side = this.side;
+
+		} else if ( this instanceof THREE.MeshDepthMaterial ) {
+
+			if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending;
+			if ( this.side !== THREE.FrontSide ) output.side = this.side;
+
+		} else if ( this instanceof THREE.ShaderMaterial ) {
+
+			output.uniforms = this.uniforms;
+			output.vertexShader = this.vertexShader;
+			output.fragmentShader = this.fragmentShader;
+
+		} else if ( this instanceof THREE.SpriteMaterial ) {
+
+			output.color = this.color.getHex();
+
+		}
+
+		if ( this.opacity < 1 ) output.opacity = this.opacity;
+		if ( this.transparent !== false ) output.transparent = this.transparent;
+		if ( this.wireframe !== false ) output.wireframe = this.wireframe;
+
+		return output;
+
+	},
+
 	clone: function ( material ) {
 
 		if ( material === undefined ) material = new THREE.Material();

+ 2 - 0
src/materials/MeshBasicMaterial.js

@@ -39,6 +39,8 @@ THREE.MeshBasicMaterial = function ( parameters ) {
 
 	THREE.Material.call( this );
 
+	this.type = 'MeshBasicMaterial';
+
 	this.color = new THREE.Color( 0xffffff ); // emissive
 
 	this.map = null;

+ 2 - 0
src/materials/MeshDepthMaterial.js

@@ -18,6 +18,8 @@ THREE.MeshDepthMaterial = function ( parameters ) {
 
 	THREE.Material.call( this );
 
+	this.type = 'MeshDepthMaterial';
+
 	this.morphTargets = false;
 	this.wireframe = false;
 	this.wireframeLinewidth = 1;

+ 39 - 6
src/materials/MeshFaceMaterial.js

@@ -4,20 +4,53 @@
 
 THREE.MeshFaceMaterial = function ( materials ) {
 
+	this.uuid = THREE.Math.generateUUID();
+
+	this.type = 'MeshFaceMaterial';
+	
 	this.materials = materials instanceof Array ? materials : [];
 
 };
 
-THREE.MeshFaceMaterial.prototype.clone = function () {
+THREE.MeshFaceMaterial.prototype = {
 
-	var material = new THREE.MeshFaceMaterial();
+	constructor: THREE.MeshFaceMaterial,
 
-	for ( var i = 0; i < this.materials.length; i ++ ) {
+	toJSON: function () {
 
-		material.materials.push( this.materials[ i ].clone() );
+		var output = {
+			metadata: {
+				version: 4.2,
+				type: 'material',
+				generator: 'MaterialExporter'
+			},
+			uuid: this.uuid,
+			type: this.type,
+			materials: []
+		};
 
-	}
+		for ( var i = 0, l = this.materials.length; i < l; i ++ ) {
+
+			output.materials.push( this.materials[ i ].toJSON() );
+
+		}
+
+		return output;
+
+	},
+
+	clone: function () {
 
-	return material;
+		var material = new THREE.MeshFaceMaterial();
+
+		for ( var i = 0; i < this.materials.length; i ++ ) {
+
+			material.materials.push( this.materials[ i ].clone() );
+
+		}
+
+		return material;
+
+	}
 
 };

+ 2 - 0
src/materials/MeshLambertMaterial.js

@@ -43,6 +43,8 @@ THREE.MeshLambertMaterial = function ( parameters ) {
 
 	THREE.Material.call( this );
 
+	this.type = 'MeshLambertMaterial';
+
 	this.color = new THREE.Color( 0xffffff ); // diffuse
 	this.ambient = new THREE.Color( 0xffffff );
 	this.emissive = new THREE.Color( 0x000000 );

+ 2 - 0
src/materials/MeshNormalMaterial.js

@@ -18,6 +18,8 @@ THREE.MeshNormalMaterial = function ( parameters ) {
 
 	THREE.Material.call( this, parameters );
 
+	this.type = 'MeshNormalMaterial';
+
 	this.shading = THREE.FlatShading;
 
 	this.wireframe = false;

+ 2 - 0
src/materials/MeshPhongMaterial.js

@@ -51,6 +51,8 @@ THREE.MeshPhongMaterial = function ( parameters ) {
 
 	THREE.Material.call( this );
 
+	this.type = 'MeshPhongMaterial';
+
 	this.color = new THREE.Color( 0xffffff ); // diffuse
 	this.ambient = new THREE.Color( 0xffffff );
 	this.emissive = new THREE.Color( 0x000000 );

部分文件因文件數量過多而無法顯示