API Docs for: 1.0.2
Show:

File: GIScene\Scene.js

/**
 * The Scene Object
 *
 * @namespace GIScene
 * @class Scene
 * @constructor
 * @extends THREE.EventDispatcher
 * @param {String} containerDivId The id of an HTMLDivElement which will be used as container for the webgl canvas.
 * @param {Object} [config] Allowed
 * values for config are:
 *
 * 	cameratype:	'perspective'|'ortho'
 *  near: {Number}
 *  far:  {Numver}
 *  fov:  {Number}
 * 
 *  width:	{Integer}
 *  height: {Integer}
 * 
 *  animate:{Boolean}
 *  
 *  center: 				{THREE.Vector3}
 *  positionFromCenter: 	{THREE.Vector3}
 *  projection: 			{String}
 *  units:					{String}
 *  offset:					{GIScene.Coordinate3}
 *  skyColor:				{THREE.Color}
 *  fog:					{THREE.Fog}
 */

GIScene.Scene = function(containerDivId, config) {

	/**
	 * The default config which is used when no config object is provided.
	 *
	 * @private
	 * @property defaults
	 * @type Object
	 **/
	var defaults = {

		//canvas options
		width : null,
		height : null,

		//render options
		animate : true, 
		fog : null,

		//scene options
		center : new THREE.Vector3(0, 0, 0),
		projection : 'EPSG:32616', //not yet used UTM16N???-->EPSG:32616
		units : 'm', //not yet used
		offset : new GIScene.Coordinate3(0,0,0), 
		//new THREE.Vector3(0,0,0), //new THREE.Vector3(269500, 550, -1641500), //not yet used offset is used to keep coordinates short.
		skyColor : new THREE.Color().setStyle('lightskyblue').getHex(),

		//camera options
		cameratype : 'perspective',
		near : 0.1,
		far : 1000,
		fov : 45, //for perspective camera only
		positionFromCenter : new THREE.Vector3(0, 0, 10),

		//data options
		// url : null,
		// format : null,
		// verticalAxis : "Y"

	};

	/**
	 * The config which is used to initialize the Scene. Merged from defaults and passed config Object.
	 *
	 * @property config
	 * @type Object
	 */
	this.config = GIScene.Utils.mergeObjects(defaults, config || {});


	//make this class an EventDispatcher
	//THREE.EventDispatcher.call( this );

	//canvas properties
	this.containerDiv = document.getElementById(containerDivId);
	this.canvas = null;
	//get size from config else from containerDiv-CSS Style else use default 500x500 px
	var width = null;
	width = (this.config.width) ? this.config.width :  this.containerDiv.clientWidth ;
	
	var height = null;
	height = (this.config.height) ?  this.config.height : (this.containerDiv.clientHeight > 0) ?  this.containerDiv.clientHeight :  500;

	//render properties
	var animationFrameId;

	//scene properties
	/**
	 * The scenegraph root node. Add {THREE.Object3D}-objects to this node. See THREE.js docs for further information
	 * @property root
	 * @type {THREE.Scene}
	 */
	this.root = null;
	/**
	 * The active camera. {THREE.CombinedCamera} can be switched from perspective to orthographic and vice versa
	 * @property camera
	 * @type {THREE.CombinedCamera}
	 */
	this.camera = null;
	//Camera gets a target object later in initScene()
	
	//THREE.Scene and THREE.Orthocamera to render Sprites in screen coordinate space
	this.spriteRoot = null;
	this.spriteCamera = null;
	
	this.lights = [];
	this.renderer = null;
	this.effectComposer = null;
	this.fog = null;
	this.layers = [];
	//Layers should add {THREE.Scene} objects to this.root
	this.center = new THREE.Vector3(0, 0, 0);

	//controls
	this.controls = [];

	//clock to be independent of framerates
	this.clock = new THREE.Clock();
	this.delta = null; //global, automatically updated in startAnimation() every frame

	this.init = function() {

		//initCanvas
		this.initCanvas();

		//initScene
		this.initScene();

		//animate: start renderLoop or render once
		(this.config.animate) ? this.startAnimation() : this.renderer.render(this.root, this.camera);

	};

	this.initCanvas = function() {
		//create canvas in div use div use size from config else from div element else
		this.canvas = document.createElement('canvas');
		this.canvas.className = "giscene_canvas";
		//remove line height from canvas containterDiv to fit the canvas exactly to its wrapper div
		this.containerDiv.style.lineHeight = 0;
		
		
		
		// this.canvas.style.width  = "100%";//width + "px";//
		// this.canvas.style.height = "100%";//height + "px";//
		this.containerDiv.appendChild(this.canvas);
		// this.containerDiv.style.width  = width + "px";
		// this.containerDiv.style.height = height + "px";
	};

	this.initScene = function(config) {
		//create THREE.Scene
		this.root = new THREE.Scene();
		this.root.name = 'root';
		
		//optionally add fog
		if(this.config.fog){this.root.fog = this.fog = this.config.fog.clone();}
		
		//initCamera
		//this.camera = (this.config.cameratype == 'ortho') ? new THREE.OrthographicCamera(width / -2, width / 2, height / 2, height / -2, this.config.near, this.config.far) : new THREE.PerspectiveCamera(this.config.fov, width / height, this.config.near, this.config.far);
		this.camera = new THREE.CombinedCamera(width, height, this.config.fov, this.config.near, this.config.far, this.config.near, this.config.far );
		this.camera.name = 'THREE.CombinedCamera';
		this.camera.target = new THREE.Object3D();
		this.camera.target.name = "Camera target";
		this.camera.add(this.camera.target);
		if(this.config.cameratype == 'ortho'){this.camera.toOrthographic();}

		/**
		 * Fires whenever camera position changes
		 *
		 * @event cameraChange
		 */
		var oldCamPos = new THREE.Vector3(0, 0, 0);
		var oldCamRot = new THREE.Vector3(0, 0, 0);
		var cameraHasChanged = function() {
			if (!oldCamPos.equals(this.camera.position) || !oldCamRot.equals(this.camera.rotation)) {
				
				this.dispatchEvent({type : 'cameraChange'});
				oldCamPos = this.camera.position.clone();
				oldCamRot = this.camera.rotation.clone();
			};
			
		}.bind(this);
		this.addEventListener('afterRender', cameraHasChanged);
		//this.addEventListener('beforeRender', cameraHasChanged);
		
		//init sprite scene and camera
		this.spriteRoot = new THREE.Scene();
		// this.spriteCamera = new THREE.OrthographicCamera(0,width,0,height,-100,100 ); //l,r,t,b,near,far
		var width1_2 = width/2;
		var heigh1_2 = height/2;
		this.spriteCamera = new THREE.OrthographicCamera(-width1_2,width1_2,heigh1_2,-heigh1_2,-100,100 ); //l,r,t,b,near,far
		// this.spriteRoot.add(this.spriteCamera);
		// this.camera.add(this.spriteCamera);
		
		//initLights
		this.directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);//new THREE.DirectionalLight(0xffffff, 0.5);
		this.directionalLight.name = "THREE.DirectionalLight";
		//color, intensity //position will be set by Control.CameraLight
		this.ambientLight = new THREE.AmbientLight(0xcccccc); //cc ^= 0.8   new THREE.AmbientLight(0xffffff);
		this.ambientLight.name = "THREE.AmbientLight";

		//experimental spot light
		this.headLight = new THREE.SpotLight(0xffffff,0.3,0,Math.PI/2,20);
		this.camera.add(this.headLight);
		this.headLight.target.position.setZ(-1000);
		this.camera.target.add(this.headLight.target);

		//initRenderer
		this.renderer = new THREE.WebGLRenderer({
			antialias : true,
			precision : "highp",
			canvas : this.canvas,
			devicePixelRatio: 1, //when browser is in zoom mode this ratio is changed and renderer.setSize uses it 
		});
		
		this.renderer.setSize(width, height);
		this.renderer.setClearColor(this.config.skyColor);
		this.renderer.autoClear = false;
		
		// init effectComposer
		this.effectComposer = new THREE.EffectComposer(this.renderer);
		this.effectComposer.setSize(width, height);
		
		// add objects to the scene
		this.root.add(this.camera);
		this.root.add(this.ambientLight);
		this.root.add(this.directionalLight);

		//setCenter
		// this.camera.position.add(this.config.positionFromCenter);
		// this.camera.target.position.setZ(-this.config.positionFromCenter.length());
		this.setCenter(this.config.center, this.config.positionFromCenter);
		
		//add Logo
		var gisceneLogoTexture = new THREE.Texture(null);
		var gisceneLogoImage = new Image();
		gisceneLogoImage.onload = function(e) {
			gisceneLogoTexture.image = gisceneLogoImage;
			gisceneLogoTexture.needsUpdate = true;
		};
		// dataURL from poweredbygiscenegiscience.png
		// gisceneLogoImage.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJ4AAAAUCAYAAAB8roTFAAAABmJLR0QAAAAAAAD5Q7t/AAAVfklEQVRo3uWaeXRUVb7vP+fUPCZVlbGADFBAgABhiIAMakQZG54+wG6hceBdEZet0t1eG1G7W+jrbfThclZeu7i0aLNaWO0FH6IgtEbTShoQIZGEQMicVFKpVKXmOlX7/ZFQEgbt1Q7d977fP7Xq7H32/p29v+f7+/2+Z0v8g0wIMRy4A7gNcF7YpigJ1GrV3zoOyaS46FoSSZK/9l6V6qv7KEoCWZaQZZn/X00JhQg3NGAZNepbHVf9PYMtA1gF/BgYA5BIJIlE4vT2RlGUJAaDBkVJYDJpMZv1qXvD4RiKkuzrH1WIRhMkkgK9ToVWM/AxkskkkiQhSdIVr8fjCUKROLIsoVHLGPQaNBoVarWMwaAhmRR4vSGEAEVJoiQF6VYdWq0KvV773x5wIpkkEYuRjMdJhELf+vjq7wFsKmAesBJYJITQhcNxAoEonZ4QPn8YISDNqifTYcJi0aFWywQCUdra/bR2BOgNRInFFIwGDVaLDrNJh8WsJZkURJUEPd4QkgTBsEJcSVzWD1mWMOjUqGSJNIsOs1GL3qABIBiM0t7ZN4+/N4pep0atlsnNsmAx69DpZCK9UerOdhOOxNEbNDjSDdhsRswm7dcy53850CkKweZmtHY7KqMRJRb71ueQvkPADQXuApYDg2MxhbZ2Px2dQTo6A0SiCdKtOgrzbOTmWDCZdHR3B2lo9tHQ3ENHVxCLSUuG3Uhhno2sTDMmowZfb5QTtZ00t/tJs+ixmLTYrHqG5FiQZZnzQTcWUwDQaFQp5ksmErR2huj2hfAHYnj9EWxWPcXDM8hzpiHLEqFQDJ8/wtkGL+6uIC3tfhw2IwVDbORm9fnQ0NRDa0cvsXiSDLsBZ7aV7CwTaWmGS1j2vxbiBEo4jBIIgBDIej0oCv5Tp8iYPv2fF3hCCCOwoj+UXg3IiUSShqYezp7rpt3dSyyWwGzSUjwqm8J8GwaDlkgkxtHP2/jr8TY6ugIYDRpmXJXHxLG5mEzaPkAJwduH6th9qA5nlpnZU/MZV5SFpb/9QjtZ6+aP+07hD8S4cXoB869xXeJrOBzjdIOXg582Un6kiRunF3LrgtFYzLpUn0gkzokvOvjkSDMt7b2YTVrGj86mtMSJLElU1bipb/CiUsmYTVqG5ttwDXVgsei/1U2K+v24T5zAYLfjKCpKgTsWDBLt6cHidMIFgE8qCq2VlZgyM0kfOhTpK3JUkUzSduQIuSUlRD0eVHo9kc5OtA4HWqsVSa3G/ec/k3Xttf98oVYIMb0/lN4CpH2ZnCscP9nBqdNuItG+EKjXqZkwNpfhrsxUcVB5rJX3PjhDOKqgVslMnTiYWdMKBrBHXUM3r/zxOLG4wgMrJzNmeOZlfUkmk+yvOMcf951CAN2+MFPGOXHYjAP6GQxaxhVlk5dr5fMaN7/beZzheTaum5qfmlev11A6YTAWk443366ixx+h/NMGgqEYi+cWMXH8IBQlSWOLjx5/hM9OtuPuClJSnEN2tvUbr6uvsZFDDz9Mw6FDJKJRAAZPn85NO3agMRj4YscODq5bx71nz6IxmwH4eMMGqnfuJOzxoEQizHv5ZUYtWXLFOb7YtYt9a9Yw77nnyJ85E+JxSCTQ2e0ogQCR9vY+BvxnyfGEEFn9VelKYPTl+nR1hag905UCHYDFrGXI4PQB/eJKYkBuplbLl4SsnAwzU8blcuhwIzX1Hobn29Bq1ZeLFkSiSup/KKJcUvVeWLUer3Hj8YUpKnSQ7TBdNlRqtSpkWUqNH40lUnlpbraF5lYfSQFJIWhp82O16MjIMH+j3C/s9bLzpptQabXMefZZcktLafzwQ/yNjWgMhv54NdDXmv/8Tz595hmu2bCBsStX0l1Tg83l+kq2c44bR+k995A9diwqnQ4BaBwOgmfOoLJYMLtc+Kqq/rHAE0KogZv6Q+kc4CvLO41GhUY9cPETCUEspqDXa/rXTmLS+EEkEkmqajpp7wzy1+OtaDUyJcW56HRqZFnGYtbxk+UTKS3O4VS9h9d2n6TAmUbhYBtZdgM6nRpJ6pNIrikdQltnAF8gyo1XF2JPN5BIJBFCkEgk6fCEOdfipbrOg8cXZs70QmZPK2BEoeMSYNY3eKn4axM+fwSrWceIoXamTBiM0dj36JFoHHEBriVJQqdVp4D699rhp58m1NXFivffT4Fn7IoVX3lPz9mz6G02Jq5ejSTL5EyceAm7dRw9iqxWM2zOHGSgeudOZK0Wg8OB1majq6KC3t5e2o4dI9rbS25pKZZkEoCKTZvIHj+eQFsbnVVVZBYVMWrpUrTWPnb31NZSt2cPvS0t2FwuJq1ZA5JEpKeHk9u301Nfj23YMFwLF/5twOvX3M7LIM6/dfHsdiMjhmVw8lQH4UgfC/n8EeobvIwZpU1pdWlWPTdcO5yJY51U1XRytrGbjyubqD3bTXaGibxBaQx2pmHQq7lxRiHXXpWHuzuMxxukvrmbz2tifVWrXo0jzUhOhon/9T/HAmA166mt99DZHaY3FCceV9BqZNKteqaOz6VgUBpWix5ZlonFFBQlibszQHObn5b2Xlo7etHr1JTNGMrIYQ6cORZ0ur6XpqOjl5a23lRBI0sSQwalMbTA9o2LjJZPPqGgrOwrGetiyxo3jojXy6F167ju3/4NSaVKhYG9a9ZQv38/rvnzEYkEnZ9/jikri5bDhxHAlHvvJeJ2U/P++xzbtg1naSmSLPPZq68y9c47GbxsGad376b6jTfImjABndnM+7/4BUo8zqS776bu3Xc58MAD2FwusoqLaaushHvuobO6mv+7ahUCcJaWUvHb39JUUXFl4AkhzMCt/ZXppL+rcpEkikdnY7XoqKvvxt0VJBKN83l1B4FgjOFDHdjtBtTqPjccDhMzpxmZVjoET3cId1eAto4AtWc91J71kEgIsjJMaDWCc2eO0tXVTjgcTrEZqJg2vQyraTAfHHwHj6eLRYtuIisrCxH303T6Uzo6OlCUOKr+TTEYTJRMmg2SBo83TDyeQKWS0WhUDMu3MW3yEDLsRvR6daqICYdjNDX7qDnTRVd3CFmSSE/TM6zAztACO+YLCpS/1wKtrQy++uov/7e3c/SVV/rSjpISRixefMk9hddfz9QHH+STp57C19zM4q1bkbVaTm7fTu2ePSx76y2cpaXEe3pIxuMgy1Tv3IlIJDAOHsyZP/2Jo7/7HZN+8hOm/+IX+OrreX32bHxtbX0pUTBI0bJlzHj4YQA6q6vpPHkSJRym/Fe/Ir+sjLnPPZcqZoQQHFq3DkmlYvm776IxmVBpNDSWlw8EnhBCAq7vZ7abAMs3XUBZlinIt+PMteLuDHDmXDfurhCnz3po7QiQlWFkyKB0HHYjFrMWSZLQaFTkZFvIybYwdrRACEEwGCMaTRAIRnjqfz/N9t+/QCgUHMAsBoORh9dbGVci8+r/eYXGxjPk5xdRUBjnlRd/y+7db6IoyoB78vIKefaFyQx3FeAqsKPVqjGb+7S58/2EEJzXHju6grS2+XF3BZEkyMkyU5hnY5AzDatF963JKRqzmfgFwm0iGsVXX09TRQVKKHRZ4AFc/dBD6NLS+Ojxxzn4yCPM3rSJmt27cc2di7O0FCUSIRGLpYqVnvp6XP1jnd67FyUWo+voUXYvXUrXmTNozGYKpk0j0NKCv7mZnJKSL/PQ7m4s2dnU79+P9/RpFm3bNqCCbj96lOaPPya7pIQPH38cb10drZWVTH/44T7gCSHy+guFHwEjvwuJSKtVM3hQOjnZFnoDMdzuXjo6g3h9YVraGtHrNNjS9aRb9WRmmNBp1aSnG1Cp+jbSYtFjsUBLSx0f/nk3Go2aO++8k8LCwhRj6nQ6rrtuJnZ7Jg6HmY4ODcMKHZjNCkeP/gWj0ciPf/xjBg8enPLLZrNxzYzRGI3G/hwwgRBJuruDRKMJ/IE43d4w3d4Q/kAUkUxgtegocjkYlJtGerqh/2uLgqIoA16486w6MG9U+tn5yn0A0vLzaT96FCEEkiSRlp/PD/7jP3h99uyvXetJd9+Nv6GB03v2IJ54An9zMyMWLEAoCkpPD4l4HLXBQCIex9vYiM3lInDuHD11dWSNGkXW5MnEgkFG33EH2SNH4jt2DPfJk0iSRMbYvhQm0NZGoKWF9OHD8be0oLfZcIwYcUnOCTBi0SKCHR0MX7iQ2U89RXphIWohxA7gfwA6vgfTaNQIIbClGxg2NIFGo6elpYsefwR3V5DWjl7qznkBSLf2uWQ0aMjKNGPQazh48CPOnDnDkiXL2LDhN1itlosYVoXf70sxj1arJhj0093djcvl4t57f0JurhMhBIFAjGg8Qbc3jrvTw9GjR/jkLx/R0dGM1xsCScuc+ctwODI4dPA93O3nUKti2O1m8vPzmDx5MmVlZXzwwQfs3LmTWL/Cr1aryczMZPz48cybNw+DwUBHRwf79u2jqqoKv9+PVqtlyJAhTJ06lRkzZlzClKNvuYW9d93FJ08+ybR//deUPpeMx6/8XTUaRa3TpVg6EYn0vbROJ+2ff060qwuVyYQIhdBlZHDq9ddJRqNo4nF6q6uxjR1L4wcfMGXtWmRNXx4bqK8HoPv0aSxOJ+lDhgDgPn4ckUxiGzaMoNtNxOultbISZ2lp3/yJBMbMPsnLlJND6X33pfw6X9Xe0hfeNKxatYqXXnrpexPK+4oLBaczDaczjaJkklhMIRCIk0gk8fdG8Hj7crj6hm5C4TifVlYRi8WwOYZReayTaLSJaDRyQbg1EQ4HaWvzEgpFqPi0HrVag6IkOXv2HL/61b9jMllQa3QgGRg+cgJ5+YUcO/Ixv9/6FB5PGzabDYDMzExcBUY+++wDtm/7LWazGV3/xvb09OB0OnnhhReora1l69atSJKE3W7vk3FCISRJYvPmzfzwhz9k8+bNPP/881itVjT9m+rxeBg1ahRbtmxh4kUVaNHNN9NUXs5fNm2i8cMPSSsooLmiAn9jI4U33HDJWnpqa/nTkiXkTp1KxOuluaKCcStXIqlUuObP59C6dbzzwAOotFpiPh+z1q6lp6EBo8OB65Zb8B45woiFC6nesYM//uAH5EyaROvhwyx85pk+4NXVDSh0vGfPojWbU6HXlJPDO6tXk3/99bRVVjLzl78kb+ZMMouLKX/8cVorKwm0tlJQVkbJqlWkAnJ1dTXbtm0DYOPGjVitVp544gny8/NZuXIlGo2GlStXkp+fn5r86aefxmKxsGrVqlQxMW3atMu23X///ZSVlV0WgGVlZdx///1MmjSJjz76kDfeeJXbb1/GcFcmUyYP4eop+VwzvZDrZhaSnalFpVIxdsxQhhbY+ezI+/xy/d38cv3d/PvGn9HSVEVWhjmlvaVbdZSML+LGG+fi83nZsWMrr776LK+8/CSvvPQ4f/1kF1dNyMDjPklHRxM//elP2bdvHwcOHOAPf/gDU6ZM5MSJE8iyzPr169m/fz979+5lwYIFNDQ0UFtb239CJsnixYt59913ee+997jvvvvw+XxUVVXh8/morKxEr9ezYcMG9u/fz1tvvcU111xDTU0N9f2scrHd8PTTzH3xRTJGj0YJhXDNm8fi7duZ8cgjfcXYyJGMu+MOZK0WrclE0bJlCCHILC5m8Wuvce1vfgNA8Y9+xKKXXsKYlobeaGTC6tWYCgqwFxczYfVqop2daDMyKLzxRhb9/vc4Ro0iEY0yZvlykv3pg2PkSIouEKKNOTlMvuceZI0GWaNh5aFDlPzLv5BUFEYtW0bOxInIajWLt29n1NKlKMEg2SUl5F93XaqgEIDQaDTirrvuEkIIkZ6eLsrLy0V6erpYsWKFUKvV4vbbbxdqtVqsWLFCnDe73S4AoVKpxPlxampqLttmsVhEeXm56J8yZYAoLy8XFotFPPvss2LBggUiMzNTVFVViYtNURTx0EMPCZ1OJ95++20hhBBbtmwRkydPFmazWVitVrFz507h8/lEWVmZyMnJEYcPHxZCCHH69Gnx8ssvi02bNoknn3xSrF27VmRkZIiioiJx6tQpcfPNN4ucnBxRUVExYM5wOCyWL18ujEajePTRR8U777wj9uzZI+bNmyfMZrN47bXXxIsvvij0er1YvXq1SCQSQggh3njjDaHVasXPf/5z4fF4RFlZmUhLSxMbN24U+/btE7t27RIzZswQdrtd7Nu3T3xXpgQCoqe6WoTb20XE7e57prY2EQ8GU306P/roivd7KitF05tvfut+para2BVOIMyaNYvXX3+ddevWsW3bNmbNmjWgvby8nBkzZqT+j7ggwby47ets+fLlPPjgg9x5552MHj36MufnVKkw6Ha7Abj11luZM2cOq1evpqKiYoCUcz6BB3C5XAwbNiyVY3i9Xg4fPkxbWxvxeDz1/Fqt9hJJSJZlQqEQW7ZsYevWrf3fcSPMnj2b0tJSDh48+LWykiRJ+Hw+nn/++VQxFIlEWLhwIcXFxd/ZKROhUmFwOpFkGak/xCuBAPqcnJTGJxKJK4/xHZxMueKXi5/97GfMnz+fdevWMWvWLMaMGcOIESMYM2bMAOBt3LiR+fPn09vbm9rQK7XddtttPPbYY5d14rHHHuP222+nu7sbg8HAr3/96ys67HK50Ol0HDx4kGuvvRaLxYJKpfpKGUMIgcfjGXCtvb1PAzwPTqvViqIotPVrVhebxWLhgQceYObMmSkg5eXl4XQ6vxZ4Xwrqdh566CGuuuqq1Lwul4uMjIzvZHOT0SjJeBxtWhpRjwedxUKgvh5TQcGX34OrqrCMvLKQkfgugXcxaB555BEe6c8jAE6cODHg97ytWbOGNWvWDNjgK7U999xzVwTFl8WGmkcffZTMzMwrOjxhwgQmTJjAm2++SW1tLTqdDiHEJb5daMePH2f9+vX09vZecDolzIkTJxg3bhxms5mSkhJ27drFM888Q2VlJbIsYzabWbp0KVarlWg0yvHjx4n2618ABQUFLPmKD/AXMnV6ejrhcJhjx47h8/lSbU1NTSxevPgSpv2mpgQCIMtozGYibjf67GySiQRCUZDUX/JNpK2NtMswrkgmSUQiKD093x/j/aPsQh3sSjZ06FDWrl3L5s2b+eKLL1LA1Wg0WK1WtNq+Y1J2ux2Hw4FGoyEUClFTUzNgwwGcTidLliwhNzeXpUuXUlNTw969e/nss88AyM7O5oYbbmDOnDlUVlZy4MABDhw4kLp/5syZzJ8/H71eT0ZGBhbLl9LOhdeMRiOLFi2itraWvXv3DvBh0aJFzJ0791sFXqCujmB9PYrfjxACpbcXSa0m3NaGrNXSEosholES0SiR9nZad+/uY8f+a8loFHHBXuTfdtu3vtf/D4zKlfzuTTcVAAAAAElFTkSuQmCC";	
		// dataURL from poweredbygiscenegiscience2.png
		gisceneLogoImage.src = "data:image/png;base64,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";
		var gisceneLogoMaterial = new THREE.SpriteMaterial({ map : gisceneLogoTexture, opacity:1  });
		var gisceneLogo = new THREE.Sprite(gisceneLogoMaterial);
		gisceneLogo.scale.set( 158, 20, 1 );
		gisceneLogo.position.set((this.canvas.width/2)-79,(-this.canvas.height/2)+10,0);
		this.spriteRoot.add(gisceneLogo);
		//will be called in this.onResize
		this.logoOnResize = function() {
			gisceneLogo.position.set((this.canvas.width/2)-79,(-this.canvas.height/2)+10,0);
		}.bind(this);
		

	};

	/**
	 * Start animation frame and render loop
	 *
	 * @method startAnimation
	 */
	this.startAnimation = function() {
		animationFrameId = requestAnimationFrame(this.startAnimation.bind(this));
		
		this.delta = this.clock.getDelta();
		
		TWEEN.update();
		
		this.renderer.clear(true,true,true); //color, depth, stencil; autoclear is false to render sprites on top of scene
		/**
		 * beforeRender Event will be executed directly before the render call in the render loop
		 * @event beforeRender
		 */
		this.dispatchEvent({
			type : 'beforeRender'
		});
		/**
		 * beforeRender2 Event will be executed directly before the render call in the render loop
		 * @event beforeRender
		 */
		this.dispatchEvent({
			type : 'beforeRender2'
		});
		
		//mcaTWEEN.update();	
		
		if(this.effectComposer.passes.length>0){
			this.effectComposer.render();
		}else{
			this.renderer.render(this.root, this.camera);
		}
		
		this.renderer.clear(false,true,false); //render sprites always on top, therefore clear depth buffer
		this.renderer.render(this.spriteRoot, this.spriteCamera);
		
		this.dispatchEvent({
			type : 'afterRender'
		});
	};

	/**
	 * Stop animation frame and render loop
	 *
	 * @method stopAnimation
	 */
	this.stopAnimation = function() {
		cancelAnimationFrame(animationFrameId);
	};

	this.setCenter_old = function(vector, positionFromCenter) {
		var translation = vector.clone().sub(this.center);
		//
		// //keep light direction but translate light.target
		// var lightWorld 		 = this.directionalLight.parent.localToWorld(this.directionalLight.position.clone());
		// var lightTargetWorld = this.directionalLight.target.parent.localToWorld(this.directionalLight.target.position.clone());
		// var lightWorldNew 	 = lightWorld.add(translation);
		// this.directionalLight.position = this.directionalLight.parent.worldToLocal(lightWorldNew);
		//
		// this.directionalLight.target.position = vector.clone();

		//scene center
		this.center = vector.clone();
		//focus camera on center
		if (positionFromCenter) {
			//translate and focus camera on center
			// console.log(positionFromCenter.length());
			this.camera.position = vector.clone().add(positionFromCenter);
			this.camera.lookAt(this.center);
			this.camera.target.position.setZ(-positionFromCenter.length());
			
		} else {
			//just translate
			this.camera.position.add(translation);
		}

		// var positionFromCenter = positionFromCenter || this.camera.position.clone().sub(this.camera.target.parent.localToWorld(this.camera.target.position.clone())); //new THREE.Vector3(0,0,0);
		// this.camera.position = vector.clone().add(positionFromCenter);
		// this.camera.target.position = positionFromCenter.clone().multiplyScalar(-1);
		//this.camera.lookAt(this.center);
		// //this.camera.target.position = this.camera.target.parent.worldToLocal(vector.clone());
		this.dispatchEvent({
			type : 'center',
			content : {
				center : vector.clone(),
				translation : translation,
				positionFromCenter : positionFromCenter
			}
		});
	};
	
	
	/**
	 * Jump to another place in the scene.
	 * 
	 * @method setCenter
	 * @param {THREE.Vector3} vector vector in scene coordinates
	 * @param {THREE.Vector3} positionFromCenter vector to place the camera at some distance from the point of interest (vector)
	 * @param {Number} duration duration in milliseconds. if undefined or > 0 an animation is perforemed from the current to the specified new center. Set this to 0 to jump immediately to the new location.
	 */
	var _setCenterActive=false;
	var _setCenterTween = null;
	this.setCenter = function(vector, positionFromCenter, duration) {
		// stop exisitng tween when a new one comes during animation time
		if(_setCenterActive && _setCenterTween){_setCenterTween.stop();_setCenterActive=false;}
		
		if(!_setCenterActive){
				_setCenterActive = true;
		//var active = "false"; // avoid multiple calls at a time
		var duration = (duration == null)?1000:duration;
		var oldCamTargetPos	= this.center;//this.camera.localToWorld(this.camera.target.position.clone());
		var newCamTargetPos	= vector.clone();
		var positionFromCenter = positionFromCenter || this.camera.position.clone().sub( oldCamTargetPos );
		var oldCamPos		= this.camera.position.clone();
		var newCamPos 		= vector.clone().add(positionFromCenter);
		var lookAtVector	= new THREE.Vector3();
		var numDecimals = 3;
		
		var tweenValues = {
			tx: oldCamTargetPos.x,
			ty: oldCamTargetPos.y,
			tz: oldCamTargetPos.z,
			cx: oldCamPos.x,
			cy: oldCamPos.y,
			cz: oldCamPos.z
		};
		var targetValues = {
			tx: newCamTargetPos.x,
			ty: newCamTargetPos.y,
			tz: newCamTargetPos.z,
			cx: newCamPos.x,
			cy: newCamPos.y,
			cz: newCamPos.z
		};
		//scene center
		this.center = vector.clone();
		// console.log("Scene.setCenter():duration: "+duration);
		if(duration == 0){
			this.camera.position.set(parseFloat(targetValues.cx.toFixed(numDecimals)), parseFloat(targetValues.cy.toFixed(numDecimals)), parseFloat(targetValues.cz.toFixed(numDecimals)));
			this.camera.lookAt(lookAtVector.set(parseFloat(targetValues.tx.toFixed(numDecimals)),parseFloat(targetValues.ty.toFixed(numDecimals)),parseFloat(targetValues.tz.toFixed(numDecimals))));
			this.camera.target.position.setZ(-positionFromCenter.length());
			/**
			 * Fires after setCenter() completed
			 * @event center
			 */
			this.dispatchEvent({
				type : 'center',
				content : {
					center : vector.clone(),
					translation : oldCamTargetPos.clone().sub(newCamTargetPos),
					positionFromCenter : positionFromCenter
				}
			});
			_setCenterActive = false;
		}
		else {
			
				//tween camera target position and camera position
				_setCenterTween = new TWEEN.Tween(tweenValues)
											.to(targetValues,duration)
											.easing( TWEEN.Easing.Quartic.Out )
											.onUpdate(function(){
												this.camera.position.set(parseFloat(tweenValues.cx.toFixed(numDecimals)), parseFloat(tweenValues.cy.toFixed(numDecimals)), parseFloat(tweenValues.cz.toFixed(numDecimals)));
												this.camera.lookAt(lookAtVector.set(parseFloat(tweenValues.tx.toFixed(numDecimals)),parseFloat(tweenValues.ty.toFixed(numDecimals)),parseFloat(tweenValues.tz.toFixed(numDecimals))));
											}.bind(this))
											.onComplete(function(){
												this.camera.target.position.setZ(-positionFromCenter.length());
												/**
												 * Fires after setCenter() completed
												 * @event center
												 */
												this.dispatchEvent({
													type : 'center',
													content : {
														center : vector.clone(),
														translation : oldCamTargetPos.clone().sub(newCamTargetPos),
														positionFromCenter : positionFromCenter
													}
												});
												
												_setCenterActive = false;
												
												}.bind(this))
											.start();		
				
			}
		

		//this is done every frame update by OrbitZoomPan-Control
		// if (this.camera.inOrthographicMode) {
			// this.camera.toOrthographic();
		// }
		
		}	
	};
	
	/**
	 * Function to move camera to predefined relative positions from Scene.center
	 * 
	 * @method viewFrom
	 * @param {String} relativePosition allowed values are 'left','right','front','back','top','bottom'
	 */
	this.viewFrom = function(relativePosition) {
		var distanceToCenter = this.camera.position.clone().sub( this.center ).length();
		var direction = new THREE.Vector3(0,0,1); //default 'front'
		switch (relativePosition) {
			case 'left': 
				direction = new THREE.Vector3(-1,0,0);
				break;
			case 'right':
				direction = new THREE.Vector3(1,0,0);
				break;
			case 'front':
				direction = new THREE.Vector3(0,0,1);
				break;
			case 'back':
				direction = new THREE.Vector3(0,0,-1);
				break;
			case 'top':
				direction = new THREE.Vector3(0,1,0.01);
				break;	
			case 'bottom':
				direction = new THREE.Vector3(0,-1,0.01);
				break;	
			default:
				console.error('Value "' + relativePosition + '" not supported!');	
		}
		
		this.setCenter(this.center, direction.clone().multiplyScalar(distanceToCenter));
	};

	/**
	 * Adds a control to the scene
	 *
	 * @method addControl
	 */
	this.addControl = function(control) {
		// add Control to controls array
		this.controls.push(control);
		control.setScene(this);

	};

	/**
	 * Removes a control from the scene
	 *
	 * @method removeControl
	 */
	this.removeControl = function(control) {
		control.deactivate();
		var x = 0;
		while (x < this.controls.length) {
			if (control === this.controls[x]) {
				this.controls.splice(x, 1);
			}
			x++;
		}
		control.setScene(null);
	};

	/**
	 * Adds a layer to the scene
	 *
	 * @method addLayer
	 */
	this.addLayer = function(layer) {
		this.layers.push(layer);
		layer.setScene(this);
		this.root.add(layer.root);
		/**
		 * @event addlayer 
		 */
		this.dispatchEvent({ type: 'addlayer', content: layer });
	};

	/**
	 * Removes a layer from the scene
	 *
	 * @method removeLayer
	 */
	this.removeLayer = function(layer) {
		/**
		 * @event removelayer 
		 */
		this.dispatchEvent({ type: 'beforeremovelayer', content: layer });
		this.root.remove(layer.root);
		var x = 0;
		while (x < this.layers.length) {
			if (layer === this.layers[x]) {
				this.layers.splice(x, 1);
			}
			x++;
		}
		layer.setScene(null);
		/**
		 * @event removelayer 
		 */
		this.dispatchEvent({ type: 'removelayer', content: layer });
	};
	
	/**
	 * Deletes all layer objects and the layer from memory
	 * 
	 * @method disposeLayer
	 * @param {GIScene.Layer} layer
	 * 
	 *  @TODO remove all working materials first
	 *  @TODO removeEventListeners
	 */
	this.disposeLayer = function(layer) {
		this.removeLayer(layer);
		
		var objects = layer.root.getDescendants(); 
		objects.push(layer.root);
		//get all geoms, materials, textures
		var materials = [], textures = [];
		
		objects.forEach(function(object){
			//@TODO Docs fire event to clear references in other objects, eg. selectables in Control.Select
			this.dispatchEvent( { type: 'beforeDisposeObject', content: object } );
			
			if(object.geometry){
				object.geometry.dispose();
				delete object.geometry;
			}
			
			//get materials
			if (object.material) {
				if (object.material instanceof THREE.MeshFaceMaterial) {
					object.material.materials.forEach(function(material) {
						if (! GIScene.Utils.arrayContains(materials, material)) {
							materials.push(material);
						};
					});
				} else {

					if (! GIScene.Utils.arrayContains(materials, object.material)) {
						materials.push(object.material);
					};
				}
			}
			delete object.material;
			
			
			// // get textures
			// materials.forEach(function(material) {
				// var maps = ["map", "lightMap", "bumpMap", "normalMap", "specularMap", "envMap"];
				// for (var i = 0, j = maps.length; i < j; i++) {
					// if (material[maps[i]] && material[maps[i]] != null && ! GIScene.Utils.arrayContains(textures, material[maps[i]])) {
						// textures.push(material[maps[i]]);
					// };
				// };
			// }); 
			
			//dispose objects
			if(object.dispose)object.dispose();
			object = null;
		}.bind(this)
		);
		
		//get further materials from styles
		layer.styles.forEach(function(style){
			if (style.material) {
				if (style.material instanceof THREE.MeshFaceMaterial) {
					style.material.materials.forEach(function(material) {
						if (! GIScene.Utils.arrayContains(materials, material)) {
							materials.push(material);
						};
					});
				} else {

					if (! GIScene.Utils.arrayContains(materials, style.material)) {
						materials.push(style.material);
					};
				}
			}
			
		});
		
		// get textures
			materials.forEach(function(material) {
				var maps = ["map", "lightMap", "bumpMap", "normalMap", "specularMap", "envMap", "texture"]; //texture exists in RasterOverlayMaterial
				for (var i = 0, j = maps.length; i < j; i++) {
					if (material[maps[i]] && material[maps[i]] != null && ! GIScene.Utils.arrayContains(textures, material[maps[i]])) {
						textures.push(material[maps[i]]);
					};
				};
			}); 
		
		//dispose textures, materials
		textures.forEach(function (texture) {
		  texture.dispose();
		});
		textures = null;
		
		materials.forEach(function (material) {
		  material.dispose();
		});
		materials = null;
		
		objects = null;
		
		//remove THREE.EventDispatcher listeners
		for (type in layer._listeners){
			delete layer._listeners[type];
		}
		for (type in layer.loader._listeners){
			delete layer.loader._listeners[type];
		}
		
		layer = null;
	};
	
	/**
	 * Set the wireframe property for the whole scene
	 * @method setWireframe
	 * @param {Mixed} wireframeMode can be {String} 'default' | {boolean} true | {boolean} false
	 */
	
	this.setWireframe = function(wireframeMode) {
		this.root.traverse(function(object) {
			
			GIScene.Utils.WorkingMaterial.setWireframe(object,wireframeMode);
			
			// if (object.material) {
				// if(!object.userData.originalMaterial){
					// // if no working material exists create one 
					// object.userData.originalMaterial = object.material;
					// object.material = object.userData.originalMaterial.clone();
				// }
				// if(object.userData.workingMaterialFlags && (object.userData.workingMaterialFlags & GIScene.WORKINGMATERIALFLAGS.WIRE) != 0){
					// //if wire flag exist remove and check if workingmaterial is still in use, and change state or material
					// object.userData.workingMaterialFlags ^= GIScene.WORKINGMATERIALFLAGS.WIRE;
					// if(object.userData.workingMaterialFlags == 0){
						// //change back to original material
						// object.material = object.userData.originalMaterial;
						// object.userData.originalMaterial = null;
						// delete object.userData.originalMaterial;
					// }
					// else{
						// // set original state
// 						
						// //for multimaterial objects
						// if("materials" in object.material){
							// object.material.materials.forEach(
								// function (e,i,a){
									// a[i].wireframe = isTrue;
								// }
							// );
						// }
// 						
						// // for single material objects
						// else {
							// object.material.wireframe = isTrue;
						// }
					// }
				// }
				// else{
					// //set wireflag and change wireframe state
					// //set wire flag
					// object.userData.workingMaterialFlags = ("workingMaterialFlags" in object.userData)? object.userData.workingMaterialFlags ^ GIScene.WORKINGMATERIALFLAGS.WIRE : GIScene.WORKINGMATERIALFLAGS.WIRE ;
					// //change wireframe state
// 					
					// //for multimaterial objects
						// if("materials" in object.material){
							// object.material.materials.forEach(
								// function (e,i,a){
									// a[i].wireframe = isTrue;
								// }
							// );
						// }
// 						
						// // for single material objects
						// else {
							// object.material.wireframe = isTrue;
						// }
				// }
// 				
			// }
		});
	}; 

	/**
	 * Sets texturing of on or off for the whole scene
	 * @method setTexturing
	 * @param {Mixed} texturingMode can be: {String} 'default' | {boolean} true | {boolean} false
	 */
	this.setTexturing = function(texturingMode) {
		this.root.traverse(function(object) {
			
			GIScene.Utils.WorkingMaterial.setTexturing(object, texturingMode);
			
			// if (object.material && !((object instanceof THREE.Sprite)||(object instanceof THREE.ParticleSystem))) {
				// if(!object.userData.originalMaterial){
					// // if no working material exists create one 
					// object.userData.originalMaterial = object.material;
					// object.material = object.userData.originalMaterial.clone();
				// }
				// if(object.userData.workingMaterialFlags && (object.userData.workingMaterialFlags & GIScene.WORKINGMATERIALFLAGS.MAP) != 0){
					// //if texture flag exist remove and check if workingmaterial is still in use, and change state or material
					// object.userData.workingMaterialFlags ^= GIScene.WORKINGMATERIALFLAGS.MAP;
					// if(object.userData.workingMaterialFlags == 0){
						// //change back to original material
						// object.material = object.userData.originalMaterial;
						// object.userData.originalMaterial = null;
						// delete object.userData.originalMaterial;
					// }
					// else{
						// // set original state
// 						
						// //for multimaterial objects
						// if("materials" in object.material){
							// object.material.materials.forEach(
								// function (e,i,a){
									// a[i].map = (object.userData.originalMaterial.materials[i].map && object.userData.originalMaterial.materials[i].map != null)? object.userData.originalMaterial.materials[i].map.clone() : null;
									// if(a[i].map != null)a[i].map.needsUpdate = true;
									// a[i].needsUpdate = true;
								// }
							// );
						// }
// 						
						// // for single material objects
						// else{
							// object.material.map = (object.userData.originalMaterial.map && object.userData.originalMaterial.map != null)? object.userData.originalMaterial.map.clone() : null;
							// if(object.material.map != null)object.material.map.needsUpdate = true;
							// object.material.needsUpdate = true;
						// }
// 						
					// }
				// }
				// else{
					// //set mapflag and change map state
					// //set map flag
					// object.userData.workingMaterialFlags = ("workingMaterialFlags" in object.userData)? object.userData.workingMaterialFlags ^ GIScene.WORKINGMATERIALFLAGS.MAP : GIScene.WORKINGMATERIALFLAGS.MAP ;
// 					
					// //change map state
// 					
					// //for multimaterial objects
					// if("materials" in object.material){
						// object.material.materials.forEach(
							// function (e,i,a){
								// a[i].map = null;
								// a[i].needsUpdate = true;
							// }
						// );
					// }
// 					
					// // for single material objects
					// else{
						// object.material.map = null;
						// object.material.needsUpdate =true;
					// }
				// }
			// }
		});
	};
	
	/**
	 * Sets the face culling mode for the whole scene
	 * 
	 * @method setFaceCulling
	 * @param {THREE.FrontSide || THREE.BackSide || THREE.DoubleSide || 'default'} faceCullingMode
	 */
	
	this.setFaceCulling = function(faceCullingMode) { //use THREE.FrontSide || THREE.BackSide || THREE.DoubleSide || 'default'
		this.root.traverse(function(object) {
			
			GIScene.Utils.WorkingMaterial.setFaceCulling(object,faceCullingMode);
			
			// if (object.material && !((object instanceof THREE.Sprite)||(object instanceof THREE.ParticleSystem))) {
				// if(!object.userData.originalMaterial){
					// // if no working material exists create one 
					// object.userData.originalMaterial = object.material;
					// object.material = object.userData.originalMaterial.clone();
					// //set flag
					// object.userData.workingMaterialFlags = GIScene.WORKINGMATERIALFLAGS.SIDE;
				// }
// 				
				// //no wm --> create and set flag and mode
// 					
				// //else wm exists --> check if new mode == original
				// /*else*/ if (faceCullingMode == object.userData.originalMaterial.side || faceCullingMode == 'default'){
					// //remove (toggle) flag
					// object.userData.workingMaterialFlags ^= GIScene.WORKINGMATERIALFLAGS.SIDE;	
				// }
// 				
				// //set property
// 				
				// function setFC(material, faceCullingMode){
					// material.side = faceCullingMode;
					// material.needsUpdate = true;
				// }
// 				
				// //for multimaterial objects
						// if("materials" in object.material){
							// object.material.materials.forEach(
								// function (e,i,a){
									// if(faceCullingMode == 'default'){
										// var FCValue = object.userData.originalMaterial.materials[i].side;
										// // a[i].side = object.userData.originalMaterial.materials[i].side;
										// // a[i].needsUpdate = true;
									// }
									// else{
										// var FCValue = faceCullingMode;
										// // a[i].side = faceCullingMode;
										// // a[i].needsUpdate = true;
									// }
									// setFC(a[i],FCValue);
								// }
							// );
						// }
// 						
						// // for single material objects
						// else {
							// if(faceCullingMode == 'default'){
								// var FCValue = object.userData.originalMaterial.side;
								// // object.material.side = object.userData.originalMaterial.side;
								// // object.material.needsUpdate = true;
							// }
							// else{
								// var FCValue = faceCullingMode;
								// // object.material.side = faceCullingMode;
								// // object.material.needsUpdate = true;
							// }
							// setFC(object.material, FCValue);
						// }
// 				
				// //check if wm still in use --> false remove wm and switch to orignal
				// if(object.userData.workingMaterialFlags == 0){
						// //change back to original material
						// object.material = object.userData.originalMaterial;
						// object.userData.originalMaterial = null;
						// delete object.userData.originalMaterial;
				// }
// 				
			// }
		}
		);
	};
	
	/**
	 * Sets the vertex color mode for the whole scene
	 * 
	 * @method setVertexColors
	 * @param {THREE.NoColors || THREE.FaceColors || THREE.VertexColors || 'default'} vertexColorMode
	 */
	
	this.setVertexColors = function(vertexColorMode) { 
		this.root.traverse(function(object) {
			
			GIScene.Utils.WorkingMaterial.setVertexColors(object,vertexColorMode);
			
			// console.log('VC',object.userData.workingMaterialFlags);
			// if (object.material && !((object instanceof THREE.Sprite)||(object instanceof THREE.ParticleSystem))) {
				// if(!object.userData.originalMaterial){
					// // if no working material exists create one 
					// object.userData.originalMaterial = object.material;
					// object.material = object.userData.originalMaterial.clone();
					// //set flag
					// // console.log("set workingmaterial vertexColors");
					// object.userData.workingMaterialFlags = 0;//GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS;
					// // console.log(object.userData.workingMaterialFlags);
				// }
// 				
				// //no wm --> create and set flag and mode
// 					
				// //else wm exists --> check if new mode == original
				// // /*else*/ if (vertexColorMode == object.userData.originalMaterial.vertexColors || vertexColorMode == 'default'){
// 					
// 							 
							 // //if default clear flag: object.userData.workingMaterialFlags &= ~GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS;
							 // if (vertexColorMode == 'default'){ object.userData.workingMaterialFlags &= ~GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS; }
							 // //else
							 // else{
								 // //if multi
								 // if ("materials" in object.material){
								 	// //check isHomogenous
								 	// var isHomogenous = null;
								 	// for (var i=1; i < object.userData.originalMaterial.materials.length; i++){
								 		// isHomogenous = (object.userData.originalMaterial.materials[0].vertexColors == object.userData.originalMaterial.materials[i].vertexColors);
								 		// if(!isHomogenous){break;}
								 	// }
								 	// //if isHomogenous
								 	// if(isHomogenous){
								 		// //if newVC != object.userData.originalMaterial.materials[0].vertexColors : set flag |=
								 		// if(vertexColorMode != object.userData.originalMaterial.materials[0].vertexColors){ object.userData.workingMaterialFlags |= GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS; }
								 		// //else clear flag &= ~
								 		// else{object.userData.workingMaterialFlags &= ~GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS;}
								 	// }
								 	// //else (not homogenous)
								 	// else{	
								 		// //set flag: set flag |=
								 		// object.userData.workingMaterialFlags |= GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS;
								 	// }
								 // }
								 // //else (single)
								 // else{
								 	// //if newVC != object.userData.originalMaterial.vertexColors : set flag |=
								 	// if(vertexColorMode != object.userData.originalMaterial.vertexColors){ object.userData.workingMaterialFlags |= GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS; }
							 		// //else clear flag &= ~
							 		// else{object.userData.workingMaterialFlags &= ~GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS;}
								 // }
							 // }
// 							 
// 							 
							 // //check multiIsHomogenous
							 // //if multiIsHomogenous == false: setMask : object.userData.workingMaterialFlags |= GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS;
							 // //else 
							 // //if newVCMode != 
// 							 
							 // // var newVCMode = (vertexColorMode == 'default')? object.userData.originalMaterial.vertexColors : vertexColorMode;
					// // /*else*/ if (newVCMode != object.userData.originalMaterial.vertexColors || vertexColorMode == 'default'){
					// //remove (toggle) flag
					// // console.log("remove workingmaterial vertexColors");
					// // object.userData.workingMaterialFlags ^= GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS;	
					// // console.log(object.userData.workingMaterialFlags);
				// // }
				// //set property
// 				
				// //for multimaterial objects
				// if("materials" in object.material){
					// object.material.materials.forEach(
								// function (e,i,a){
									// if(vertexColorMode == 'default'){
										// a[i].vertexColors = object.userData.originalMaterial.materials[i].vertexColors;
										// a[i].color = object.userData.originalMaterial.materials[i].color;
										// a[i].ambient = object.userData.originalMaterial.materials[i].ambient;
										// a[i].needsUpdate = true;
									// }
									// else{
										// if (object.geometry.faces[0].vertexColors.length > 0) {
// 											
											// a[i].vertexColors = vertexColorMode;
// 											
											// if(vertexColorMode == THREE.NoColors){
												// a[i].color = new THREE.Color(0xFFFFFF);
												// a[i].ambient = new THREE.Color(0x999999);
											// }else
											// {
												// a[i].color = new THREE.Color(0xFFFFFF);
												// a[i].ambient = new THREE.Color(0xFFFFFF);
											// }
// 											
											// a[i].needsUpdate = true;
// 											
										// }
									// }
								// }
					// )
				// }
				// else {
					// //for single material models
					// if(vertexColorMode == 'default'){
						// object.material.vertexColors = object.userData.originalMaterial.vertexColors;
						// object.material.color = object.userData.originalMaterial.color;
						// object.material.ambient = object.userData.originalMaterial.ambient;
						// object.material.needsUpdate = true;
					// }
					// else{
						// if (object.geometry.faces[0].vertexColors.length > 0) {
// 							
							// object.material.vertexColors = vertexColorMode;
// 							
							// if(vertexColorMode == THREE.NoColors){
								// object.material.color = new THREE.Color(0xFFFFFF);
								// object.material.ambient = new THREE.Color(0x999999);
							// }else
							// {
								// object.material.color = new THREE.Color(0xFFFFFF);
								// object.material.ambient = new THREE.Color(0xFFFFFF);
							// }
// 							
							// object.material.needsUpdate = true;
// 							
						// }
					// }
				// }
// 				
				// //check if wm still in use --> false remove wm and switch to orignal
				// if(object.userData.workingMaterialFlags == 0){
						// //change back to original material
						// object.material = object.userData.originalMaterial;
						// object.userData.originalMaterial = null;
						// delete object.userData.originalMaterial;
				// }
			// }
		}
		);
	};
	
	/**
	 * Sets the shading mode for the whole scene
	 * 
	 * @method setShading
	 * @param { THREE.FlatShading || THREE.SmoothShading || 'default' } shadingMode
	 */
	this.setShading = function(shadingMode) { //use THREE.FlatShading || THREE.SmoothShading || 'default'
		this.root.traverse(function(object) {
			
			GIScene.Utils.WorkingMaterial.setShading(object,shadingMode);
			
			// if (object.material && !((object instanceof THREE.Sprite)||(object instanceof THREE.ParticleSystem))) {
				// if(!object.userData.originalMaterial){
					// // if no working material exists create one 
					// object.userData.originalMaterial = object.material;
					// object.material = object.userData.originalMaterial.clone();
					// //set flag
					// object.userData.workingMaterialFlags = GIScene.WORKINGMATERIALFLAGS.SHADING;
				// }
// 				
				// //no wm --> create and set flag and mode
// 					
				// //else wm exists --> check if new mode == original
				// /*else*/ if (shadingMode == object.userData.originalMaterial.shading || shadingMode == 'default'){
					// //remove (toggle) flag
					// object.userData.workingMaterialFlags ^= GIScene.WORKINGMATERIALFLAGS.SHADING;	
				// }
// 				
				// //store originalVertexNormals the first time we are changing this property
				// if(!("originalVertexNormals" in object.userData) ) {
					// // console.log('store originalVertexNormals');
					// object.userData.originalVertexNormals = [];
					// for (var i=0,l=object.geometry.faces.length; i<l ;i++){
						// // object.userData.originalVertexNormals.push(object.geometry.faces[i].vertexNormals);
						// if(object.geometry.faces[i].vertexNormals.length != 0){
							// object.userData.originalVertexNormals[object.geometry.faces[i].a] = object.geometry.faces[i].vertexNormals[0].clone();
							// object.userData.originalVertexNormals[object.geometry.faces[i].b] = object.geometry.faces[i].vertexNormals[1].clone();
							// object.userData.originalVertexNormals[object.geometry.faces[i].c] = object.geometry.faces[i].vertexNormals[2].clone();	
						// }
					// }
				// }
// 				
				// //set property
				// if(shadingMode == 'default'){
					// //restore originalVertexNormals
					// for (var i=0,l=object.geometry.faces.length; i<l ;i++){
						// if(object.userData.originalVertexNormals[object.geometry.faces[i].a] == undefined){
							// object.geometry.faces[i].vertexNormals = [];
						// }
						// else{
							// object.geometry.faces[i].vertexNormals[0] = object.userData.originalVertexNormals[object.geometry.faces[i].a];
							// object.geometry.faces[i].vertexNormals[1] = object.userData.originalVertexNormals[object.geometry.faces[i].b];
							// object.geometry.faces[i].vertexNormals[2] = object.userData.originalVertexNormals[object.geometry.faces[i].c];	
						// }	
					// }
					// // console.log('deleting originalVertexNormals');
					// object.geometry.__tmpVertices = undefined; //has been set by Geometry.computeVertexNormals()
					// delete object.userData.originalVertexNormals;
// 					
						// //for multimaterial objects
						// if("materials" in object.material){
							// object.material.materials.forEach(
								// function (e,i,a){
									// a[i].shading = object.userData.originalMaterial.materials[i].shading;
									// object.geometry.normalsNeedUpdate = true;
									// a[i].needsUpdate = true;
								// }
							// )
						// }
// 						
						// //for single materials
						// else{
							// object.material.shading = object.userData.originalMaterial.shading;
							// object.geometry.normalsNeedUpdate = true;
							// object.material.needsUpdate = true;
						// }
				// }
// 				
				// // other than 'default'
				// else{
					// if( object.geometry.faces[0].vertexNormals.length == 0 ){
						// object.geometry.computeVertexNormals();
					// }
// 					
					// //for multimaterial objects
						// if("materials" in object.material){
							// object.material.materials.forEach(
								// function (e,i,a){
									// a[i].shading = shadingMode;
									// object.geometry.normalsNeedUpdate = true;
									// a[i].needsUpdate = true;
								// }
							// )
						// }
// 						
						// //for single materials
						// else{
							// object.material.shading = shadingMode;
							// object.geometry.normalsNeedUpdate = true;
							// object.material.needsUpdate = true;
						// }
				// }
// 					
				// //check if wm still in use --> false remove wm and switch to orignal
				// if(object.userData.workingMaterialFlags == 0){
						// //change back to original material
						// object.material = object.userData.originalMaterial;
						// object.userData.originalMaterial = null;
						// delete object.userData.originalMaterial;
				// }
// 				
			// }
		}
		);
	};
	
	/**
	 * get all descendants of the layers. Optionally the layers can be filtered by an array filter function.
	 * 
	 * @method getLayerDescendants
	 * @param {Function} filter An array filter function that is applied to each layer object of the scene. Should return true or false. 
	 * @example
	 * 	//get only descendants of layers that are not Helper Layers
	 * 	scene.getLayerDescendants(function(e,i,a){
	 * 		return !(e instanceof GIScene.Layer.Helper);
	 * 		}
	 *	);
	 *  
	 */
	this.getLayerDescendants = function(filter){
		var layerDescendants = [];
		var layers = this.layers;
		
		if(typeof filter == 'function'){
			layers = layers.filter(filter);
		}
		
		for(var i=0,j=layers.length; i<j; i++){
		  layers[i].root.getDescendants(layerDescendants);
		};
		
		return layerDescendants;
	};
	
	/**
	 * @method getLayerById
	 * @param {String} id
	 * @return {GIScene.Layer} result returns the layer found by id or null
	 */
	this.getLayerById = function(id) {
		var result = null;
		for (var i=0,j=this.layers.length;i<j;i++){
			if(this.layers[i].id.toString() === id.toString()) return this.layers[i];
		}
		return result;
	};
	
	/* experimental function */
	this.sortFacesFromCamera = function() {
		//sorting function to sort face arrays according to the distance from the camera to minimize opacity rendering problems
		var sortingFunction = function (elementA, elementB){
		    var distA = this.camera.position.distanceTo(elementA);
		    var distB = this.camera.position.distanceTo(elementB);
		    // return distA - distB;
		    return distB - distA; //sort from far to near
		}.bind(this);
		
		//merge all geoms
		var mergedGeom = new THREE.Geometry();
		this.root.traverse(function(object) {
			if (object.geometry && object instanceof THREE.Mesh){
				THREE.GeometryUtils.merge(mergedGeom, object.geometry);
			}
		}.bind(this));
		
		//sort triangles
		mergedGeom.faces.sort(sortingFunction);
		// this.root.traverse(function(object) {
			// if (object.geometry && object instanceof THREE.Mesh){
				// object.geometry.faces.sort(sortingFunction);
			// }
		// });
		mergedMat = new THREE.MeshLambertMaterial({
			color : 0xD2B48C, //0xFFFF66(gelb),
			ambient : 0x8B7355, //0x7B7B33,
			emissive : 0x000000,
			depthTest: false,
			depthWrite: true,
			opacity:0.5,
			transparent:true
		});
		
		mergedMesh = new THREE.Mesh(mergedGeom, mergedMat);
		
		mergedLayer = new GIScene.Layer();
		mergedLayer.root.add(mergedMesh);
		this.disposeLayer(this.layers[0]);
		this.addLayer(mergedLayer);
		
		
	};

	var debugView = false;
	this.toggleDebugView = function() {
		if(debugView){
			debugView = false;
			this.camera.target.remove(this.camera.target.children[1]);
		}
		else{
			debugView = true;
			var sphereGeom1 = new THREE.SphereGeometry(0.1,8,8);
			var camTargetDummy = new THREE.Mesh(sphereGeom1);
			this.camera.target.add(camTargetDummy);
			
			var sphereGeom2 = new THREE.SphereGeometry(0.2,10,10);
			var pivotLightDummy = new THREE.Mesh(sphereGeom2);
			cameraLightControl.pivotLight.add(pivotLightDummy);
		}
	};
	
	//start auto initialization
	this.init();
	
	//set viewport on resize
	this.onResize = function(event) {
		var w = this.containerDiv.clientWidth;
		var h = this.containerDiv.clientHeight;
		this.renderer.setSize( w, h );
		this.effectComposer.setSize( w, h );
		this.camera.setSize( w, h );
		this.camera.updateProjectionMatrix();
		
		this.spriteCamera.left = -w/2;
		this.spriteCamera.right = w/2;
		this.spriteCamera.top = h/2;
		this.spriteCamera.bottom = -h/2;
		this.spriteCamera.updateProjectionMatrix();
		
		//update logo position
		this.logoOnResize();
	}.bind(this);
	window.addEventListener('resize', this.onResize, false);
	
	this.onUnload = function(event) {
		//@TODO free memory before reload
		
		this.stopAnimation();
		
		//deactivate all controls to removeEventListeners on canvas etc
		for(var i=0,j=this.controls.length; i<j; i++){
		  if(this.controls[i].isActive)this.controls[i].deactivate();
		};
		
		//dispose all layers
		for(var i=0,j=this.layers.length; i<j; i++){
		  this.disposeLayer(this.layers[i]); 
		};
		
		this.containerDiv.innerHTML = "";
		
		for (props in this){
			delete this[props];
		}
		
		
		
	}.bind(this);
	window.addEventListener('unload', this.onUnload, false);
};

//Provide EventDispatcher Functions
GIScene.Scene.prototype = {

	constructor : GIScene.Scene,
	
	/**
	 * get Objects by a evaluation function which recursively tries to match the objects of the scene
	 * 
	 * @method getObjectsBy
	 * @param {Function} callback
	 * @return {Array} matches
	 */
	getObjectsBy : function(callback) {
		return GIScene.Utils.getObjectsBy(this.root,callback);
	},

	/**
	 * inherited from THREE.EventDispatcher
	 *
	 * @method addEventListener
	 * @param {String} eventType
	 * @param {Function} listener function, to be called when the event is dispatched
	 */
	addEventListener : THREE.EventDispatcher.prototype.addEventListener,
	
	/**
	 * inherited from THREE.EventDispatcher
	 *
	 * @method hasEventListener
	 * @param {String} eventType
	 * @param {Function} listener function, to be removed from the event
	 */
	hasEventListener : THREE.EventDispatcher.prototype.hasEventListener,
	
	/**
	 * inherited from THREE.EventDispatcher
	 *
	 * @method removeEventListener
	 * @param {String} eventType
	 * @param {Function} listener function, to be removed from the event
	 */
	removeEventListener : THREE.EventDispatcher.prototype.removeEventListener,
	
	/**
	 * inherited from THREE.EventDispatcher
	 *
	 * @method dispatchEvent
	 * @param {Object} event
	 * @example this.dispatchEvent( { type: 'beforeRender', content: anythingToBePassedToTheListeners } );
	 *
	 */
	dispatchEvent : THREE.EventDispatcher.prototype.dispatchEvent

};