API Docs for: 1.0.2
Show:

File: GIScene\Utils.js

/**
 * Utility Class with convenience functions stored in a central place. 
 *
 * @namespace GIScene
 * @class Utils
 * @static 
 */
GIScene.Utils = {
	
	WorkingMaterial : {
		//set internal working material flag in a bitmask containing info about changed material propeties
		setFlag: function(object, materialPropertyName, propertyValue, FLAGCONSTANT) {
			
			//set or clear the flag
							 
			//if default clear flag: object.userData.workingMaterialFlags &= ~GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS;
			 if (propertyValue == 'default'){ object.userData.workingMaterialFlags &= ~FLAGCONSTANT; }
			 //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++){
				 		if(propertyValue instanceof THREE.Color){
				 			isHomogenous = (object.userData.originalMaterial.materials[0][materialPropertyName].equals(object.userData.originalMaterial.materials[i][materialPropertyName]));
				 		}
				 		else{
				 			isHomogenous = (object.userData.originalMaterial.materials[0][materialPropertyName] == object.userData.originalMaterial.materials[i][materialPropertyName]);
				 		}
				 		
				 		
				 		if(!isHomogenous){break;}
				 	}
				 	//if isHomogenous
				 	if(isHomogenous){
				 		//if newVC != object.userData.originalMaterial.materials[0].vertexColors : set flag |=
				 		if(propertyValue instanceof THREE.Color){
				 			if(!propertyValue.equals(object.userData.originalMaterial.materials[0][materialPropertyName])){ object.userData.workingMaterialFlags |= FLAGCONSTANT; }
					 		//else clear flag &= ~
					 		else{object.userData.workingMaterialFlags &= ~FLAGCONSTANT;}
				 		}
				 		else{
				 			if(propertyValue != object.userData.originalMaterial.materials[0][materialPropertyName]){ object.userData.workingMaterialFlags |= FLAGCONSTANT; }
					 		//else clear flag &= ~
					 		else{object.userData.workingMaterialFlags &= ~FLAGCONSTANT;}
				 		}
				 		
				 		
				 	}
				 	//else (not homogenous)
				 	else{	
				 		//set flag: set flag |=
				 		object.userData.workingMaterialFlags |= FLAGCONSTANT;
				 	}
				 }
				 //else (single)
				 else{
				 	//if newVC != object.userData.originalMaterial.vertexColors : set flag |=
				 	if(propertyValue instanceof THREE.Color){
				 		if(!propertyValue.equals(object.userData.originalMaterial[materialPropertyName])){ object.userData.workingMaterialFlags |= FLAGCONSTANT; }
				 		//else clear flag &= ~
			 			else{object.userData.workingMaterialFlags &= ~FLAGCONSTANT;}
				 	}
				 	else{
				 		if(propertyValue != object.userData.originalMaterial[materialPropertyName]){ object.userData.workingMaterialFlags |= FLAGCONSTANT; }
				 		//else clear flag &= ~
			 			else{object.userData.workingMaterialFlags &= ~FLAGCONSTANT;}
				 	}
				 	
			 		
			 		
				 }
			 }
			
		},
		
		/* find out if original material was changed in a specific way*/
		isSetFlag : function(object, workingMaterialFlagConstant) {
			
			if("workingMaterialFlags" in object.userData){
				return !!(object.userData.workingMaterialFlags & workingMaterialFlagConstant); //Bit operation
			}
			else{
				return false;
			}
			
		},
		
		
		getValues : function(object) {
			
			//collect values
			var values = {};
			
			if( GIScene.Utils.WorkingMaterial.isSetFlag( object, GIScene.WORKINGMATERIALFLAGS.SELECT		) ) { values["setSelectColor"]  = object.material.emissive.clone(); };
			if( GIScene.Utils.WorkingMaterial.isSetFlag( object, GIScene.WORKINGMATERIALFLAGS.WIRE   		) ) { values["setWireframe"]    = object.material.wireframe; };
			if( GIScene.Utils.WorkingMaterial.isSetFlag( object, GIScene.WORKINGMATERIALFLAGS.SHADING		) ) { values["setShading"]      = object.material.shading; };
			if( GIScene.Utils.WorkingMaterial.isSetFlag( object, GIScene.WORKINGMATERIALFLAGS.SIDE   		) ) { values["setFaceCulling"]  = object.material.side; };
			if( GIScene.Utils.WorkingMaterial.isSetFlag( object, GIScene.WORKINGMATERIALFLAGS.MAP    		) ) { values["setTexturing"]    = false; }; //if there is a bit flag it can only be false. Otherwise everything stays as default
			if( GIScene.Utils.WorkingMaterial.isSetFlag( object, GIScene.WORKINGMATERIALFLAGS.OPACITY		) ) { values["setOpacity"] 	    = object.material.opacity; };
			if( GIScene.Utils.WorkingMaterial.isSetFlag( object, GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS	) ) { values["setVertexColors"] = object.material.vertexColors; };
			if( GIScene.Utils.WorkingMaterial.isSetFlag( object, GIScene.WORKINGMATERIALFLAGS.DIFFUSE		) ) { values["setDiffuseColor"]	= object.material.color.clone(); };
			if( GIScene.Utils.WorkingMaterial.isSetFlag( object, GIScene.WORKINGMATERIALFLAGS.AMBIENT		) ) { values["setAmbientColor"]	= object.material.ambient.clone(); };
						
			
			return values;
			
		},
		
		setValues : function(object, values) { //values object created by getValues
			
			//setValues
			for (value in values){
				GIScene.Utils.WorkingMaterial[value](object, values[value]); 
			}
			
		},
		
		setWireframe : function (object, wireframeMode){ // 'default'|true|false
			
			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 = 0;//GIScene.WORKINGMATERIALFLAGS.WIRE;
				}
				
				//no wm --> create and set flag and mode
					
				//else wm exists --> check if new mode == original
				// /*else*/ if (isTrue != object.userData.originalMaterial.wireframe){
				// /*else*/ if (isTrue != object.material.wireframe){
					// //remove (toggle) flag
					// object.userData.workingMaterialFlags ^= GIScene.WORKINGMATERIALFLAGS.WIRE;	
				// }
				
				GIScene.Utils.WorkingMaterial.setFlag(object,'wireframe',wireframeMode,GIScene.WORKINGMATERIALFLAGS.WIRE);
				
				//set property
				
				//for multimaterial objects
						if("materials" in object.material){
							object.material.materials.forEach(
								function (e,i,a){
									if(wireframeMode == "default"){
										a[i].wireframe = object.userData.originalMaterial.materials[i].wireframe;
									}
									else {
										a[i].wireframe = wireframeMode;
									}
									
								}
							);
						}
						
						// for single material objects
						else {
							if(wireframeMode == "default"){
										object.material.wireframe = object.userData.originalMaterial.wireframe;
									}
									else {
										object.material.wireframe = wireframeMode;
									}
						}
				
				//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;
				}
			}
			
		},
		setTexturing : function (object, texturingMode){ // 'default', true, false
			
			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 = 0; //GIScene.WORKINGMATERIALFLAGS.MAP;
				}
				
				//no wm --> create and set flag and mode
					
				// //else wm exists --> check if new mode == original
				// /*else*/ if (isTrue == (!!object.userData.originalMaterial.map) ){ //!! converts object to true and null to false
					// //remove (toggle) flag
					// object.userData.workingMaterialFlags ^= GIScene.WORKINGMATERIALFLAGS.MAP;	
				// }
				
				var propertyValue;
				switch (texturingMode){
					case 'default'	: propertyValue = 'default';
										break;
					case true		: texturingMode = propertyValue = 'default'; //propertyValue = ('materials' in object.material)? object.userData.originalMaterial.materials[0].map : object.userData.originalMaterial.map; 
										break;
					case false		: propertyValue = null;
										break;
					default			: console.log("'setTexturing':  No such texturingMode: "+ texturingMode);
				}
				
				GIScene.Utils.WorkingMaterial.setFlag(object, 'map', propertyValue, GIScene.WORKINGMATERIALFLAGS.MAP);
				
				//set property
				
				//for multimaterial objects
						if("materials" in object.material){
							object.material.materials.forEach(
								function (e,i,a){
									if(texturingMode == 'default'){
										var isTrue = (!!object.userData.originalMaterial.materials[i].map);
									}
									else{
										var isTrue = texturingMode;
									}
									
									var mapValue = (isTrue)? ((!!object.userData.originalMaterial.materials[i].map)? object.userData.originalMaterial.materials[i].map.clone() : null) : null;
									a[i].map = mapValue;
									if(a[i].map != null)a[i].map.needsUpdate = true;
									a[i].needsUpdate = true;
									
								}
							);
						}
						
						// for single material objects
						else {
							if(texturingMode == 'default'){
								var isTrue = (!!object.userData.originalMaterial.map);
							}
							else{
								var isTrue = texturingMode;
							}
							var mapValue = (isTrue)? ((!!object.userData.originalMaterial.map)? object.userData.originalMaterial.map.clone() : null) : null;
							object.material.map = mapValue;
							if(object.material.map != null)object.material.map.needsUpdate = 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;
				}
			}
			
		},
		
		/**
		 * Sets the face culling mode for an object
		 * 
		 * @method setFaceCulling
		 * @param {THREE.FrontSide || THREE.BackSide || THREE.DoubleSide || 'default'} faceCullingMode
		 */
		setFaceCulling : function (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 = 0;//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 flag
				GIScene.Utils.WorkingMaterial.setFlag(object, 'side', faceCullingMode, 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 an object
		 * 
		 * @method setVertexColors
		 * @param {THREE.NoColors || THREE.FaceColors || THREE.VertexColors || 'default'} vertexColorMode
		 */
		setVertexColors : function (object, vertexColorMode){
			
			
			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 = 0;//GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS;
				}
				
				//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'){
					
							 //set or clear the flag
							 GIScene.Utils.WorkingMaterial.setFlag(object,'vertexColors',vertexColorMode,GIScene.WORKINGMATERIALFLAGS.VERTEXCOLORS);
							 
							 // //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;
				}
			}
			
		},
		setShading : function (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 = 0; //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;	
				// }
				
				GIScene.Utils.WorkingMaterial.setFlag(object,'shading',shadingMode,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;
				}
				
			}
			
		},
		
		setDiffuseColor : function(object, diffuseColor) { //{String} 'default' | {THREE.Color}
			
			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 = 0;
				}
				
				
				//set flag
				GIScene.Utils.WorkingMaterial.setFlag(object, 'color', diffuseColor, GIScene.WORKINGMATERIALFLAGS.DIFFUSE);
				
				//set property
				
				//for multimaterial objects
						if("materials" in object.material){
							object.material.materials.forEach(
								function (e,i,a){
									if(diffuseColor == 'default'){
										
										a[i].color = object.userData.originalMaterial.materials[i].color;
										
									}
									else{
										
										a[i].color = diffuseColor;
										
									}
									
								}
							);
						}
						
						// for single material objects
						else {
							if(diffuseColor == 'default'){
								
								object.material.color = object.userData.originalMaterial.color;
								
							}
							else{
								
								object.material.color = diffuseColor;
								
							}
							
						}
				
				//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;
				}
				
			}			
			
		},
		
		setAmbientColor : function(object, ambientColor) { //{String} 'default' | {THREE.Color}
			
			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 = 0;
				}
				
				
				//set flag
				GIScene.Utils.WorkingMaterial.setFlag(object, 'ambient', ambientColor, GIScene.WORKINGMATERIALFLAGS.AMBIENT);
				
				//set property
				
				//for multimaterial objects
						if("materials" in object.material){
							object.material.materials.forEach(
								function (e,i,a){
									if(ambientColor == 'default'){
										
										a[i].ambient = object.userData.originalMaterial.materials[i].ambient;
										
									}
									else{
										
										a[i].ambient = ambientColor;
										
									}
									
								}
							);
						}
						
						// for single material objects
						else {
							if(ambientColor == 'default'){
								
								object.material.ambient = object.userData.originalMaterial.ambient;
								
							}
							else{
								
								object.material.ambient = ambientColor;
								
							}
							
						}
				
				//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;
				}
				
			}			
			
		},
		
		setSelectColor : function(object, selectColor) { //{String} 'default' | {THREE.Color}
			
			
			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 = 0;
				}
				
				
				//set flag
				GIScene.Utils.WorkingMaterial.setFlag(object, 'emissive', selectColor, GIScene.WORKINGMATERIALFLAGS.SELECT);
				
				//set property
				
				//for multimaterial objects
						if("materials" in object.material){
							object.material.materials.forEach(
								function (e,i,a){
									
									//fails silently when emissive is not a property of material
									if("emissive" in a[i]){
									
										if(selectColor == 'default'){
											
											a[i].emissive = object.userData.originalMaterial.materials[i].emissive;
											
										}
										else{
											
											a[i].emissive = selectColor;
											
										}
									}
									
								}
							);
						}
						
						// for single material objects
						else {
							
							//fails silently when emissive is not a property of material
							if("emissive" in object.material){
							
								if(selectColor == 'default'){
									
									object.material.emissive = object.userData.originalMaterial.emissive;
									
								}
								else{
									
									object.material.emissive = selectColor;
									
								}
							}
							
						}
				
				//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;
				}
				
			}			
			
		},		
		
		setOpacity : function(object, opacity) { //{String} 'default' | {Number} 0..1
			
			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();
					//set flag
					object.userData.workingMaterialFlags = 0;
				}
				
				
				//set flag
				GIScene.Utils.WorkingMaterial.setFlag(object, 'opacity', opacity, GIScene.WORKINGMATERIALFLAGS.OPACITY);
				
				//set property
				
				//for multimaterial objects
						if("materials" in object.material){
							object.material.materials.forEach(
								function (e,i,a){
									if(opacity == 'default'){
										
										a[i].opacity = object.userData.originalMaterial.materials[i].opacity;
										// a[i].transparent = (a[i].opacity < 1 ) ? true : false;
										// a[i].depthTest = (a[i].opacity < 1 ) ? false : true;
										a[i].transparent = object.userData.originalMaterial.materials[i].transparent;
										a[i].depthTest = object.userData.originalMaterial.materials[i].depthTest;
									}
									else{
										
										a[i].opacity = opacity * object.userData.originalMaterial.materials[i].opacity;
										a[i].transparent = (a[i].opacity < 1 ) ? true : false;
										a[i].depthTest = (a[i].opacity < 1 ) ? false : true;
									}
									
								}
							);
						}
						
						// for single material objects
						else {
							//set property
							if(opacity == 'default'){
								object.material.opacity = object.userData.originalMaterial.opacity;
								// object.material.transparent = (object.material.opacity < 1 ) ? true : false;
								// object.material.depthTest = (object.material.opacity < 1 ) ? false : true;
								object.material.transparent = object.userData.originalMaterial.transparent;
								object.material.depthTest = object.userData.originalMaterial.depthTest;
							}
							else{
								object.material.opacity = opacity * object.userData.originalMaterial.opacity;
								object.material.transparent = (object.material.opacity < 1 ) ? true : false;
								object.material.depthTest = (object.material.opacity < 1 ) ? false : 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;
				}
				
			}			
			
		}
		//setColor, setDiffuseColor, setAmbientColor, setEmissiveColor ???
	},
	
	/** 
	 * Merges object properties from a abse object with properties of an extending object.
	 *
	 * @method mergeObjects
	 * @param {Object} base  
	 * @param {Object} extending
	 * @return {Object} merged object with base properties extended/overwritten by extending object.
	 **/	
	mergeObjects : function (base, extending){
		var mergedObject = {};
		
		for (var i in base){
			mergedObject[i] = base[i];
		};
		for (var i in extending){
			mergedObject[i] = extending[i];
		};
		return mergedObject;
	}
	,
	/**
	 * Transforms absolute screencoordinates from DOMEvents to relative screencoordinates inside a DOMElement
	 * 
	 * @method getRelativeScreenCoordsFromDOMEvent
	 * @param {DOMElement} domElement Usually the canvas Element
	 * @param {DOMEvent} event e.g. a mouse event
	 * @return {THREE.Vector2} A Vector which contains the event coordinates transformed into coordinates relative to the upper left corner of the DOMElement
	 */
	getRelativeScreenCoordsFromDOMEvent: function (domElement, event){
		var relativeScreenCoords = new THREE.Vector2();
		var currtopLeft = domElement.getBoundingClientRect();
		relativeScreenCoords.x = event.clientX - currtopLeft.left;
		relativeScreenCoords.y = event.clientY - currtopLeft.top;
		
		return relativeScreenCoords;
	}
	,
	/**
	 * Transforms screencoordinates from DOMEvents
	 * 
	 * @method getViewportCoordsFromDOMEvent
	 * @param {DOMElement} domElement Usually the canvas Element
	 * @param {DOMEvent} event e.g. a mouse event
	 * @return {THREE.Vector2} A Vector which contains the event coordinates transformed into viewport coordinates (x and y are between -1 and 1)
	 */
	getViewportCoordsFromDOMEvent: function (domElement, event){
		//calculate normalized viewport coords x[-1,1] y[-1,1]
		
		var relativeScreenCoords = GIScene.Utils.getRelativeScreenCoordsFromDOMEvent(domElement, event);
		var viewPortCoords = GIScene.Utils.transformRelativeScreenCoordsToViewportCoords(relativeScreenCoords, domElement.offsetWidth, domElement.offsetHeight);
		return viewPortCoords;
	},
	
	/**
	 * Transforms relative screen coordinates (e.g.from a canvas element) to normalized viewport coordinates
	 * 
	 * @method transformRelativeScreenCoordsToViewportCoords
	 * @param {THREE.Vector2} relativeScreenCoords
	 * @param {Number} width
	 * @param {Number} height
	 * @return {THREE.Vector2} A Vector which contains the relative screen coordinates transformed into viewport coordinates (x and y are between -1 and 1)
	 */
	transformRelativeScreenCoordsToViewportCoords: function (relativeScreenCoords, width, height){
		var viewPortCoords = new THREE.Vector2();
		viewPortCoords.x = (relativeScreenCoords.x / width ) * 2 - 1;
		viewPortCoords.y = -(relativeScreenCoords.y / height ) * 2 + 1;
		
		return viewPortCoords;
	},
	
	/**
	 * Transforms viewport coordinates to relative screen coordinates (e.g.from a canvas element) 
	 * 
	 * @method transformViewportCoordsToRelativeScreenCoords
	 * @param {THREE.Vector2} viewPortCoords
	 * @param {Number} width
	 * @param {Number} height
	 * @return {THREE.Vector2} A Vector which contains the viewport coordinates transformed into relative screen coordinates (x and y are between width and height)
	 */
	transformViewportCoordsToRelativeScreenCoords: function (viewPortCoords, width, height){
		var relativeScreenCoords = new THREE.Vector2();
		relativeScreenCoords.x = ((viewPortCoords.x + 1)/2)*width;
		relativeScreenCoords.y = ((viewPortCoords.y - 1)/-2)*height;
		
		return relativeScreenCoords;
	},
	
	/**
	 * Recursively computes the bounding box of a scenegraph
	 * 
	 * @method computeBoundingBoxRecursive
	 * @param {THREE.Object3D} node
	 * @return {THREE.Box3} The united bounding box of the node and all its descendants
	 */
	computeBoundingBoxRecursive: function (node){
		
		console.log("Deprecated: GIScene.Utils.computeBoundingBoxRecursive(). Use THREE.Box3().setFromObject(obj)");
		return new THREE.Box3().setFromObject(node);
		
		// var nodeBBOX;
		// if(node.geometry){
			// node.geometry.computeBoundingBox();
			// nodeBBOX = node.geometry.boundingBox.clone().translate(node.position);
		// } 
		// else{
			// nodeBBOX = new THREE.Box3();
		// }		
		// var descendants = node.getDescendants();
// 		
		// for (var i=0;i<descendants.length;i++){
			// if(descendants[i].geometry){
				// descendants[i].geometry.computeBoundingBox();
				// nodeBBOX.union(descendants[i].geometry.boundingBox.clone().translate(descendants[i].position));
			// }
		// }
// 		
		// return nodeBBOX;
		
	},
	
	/**
	 * @method computeVertexMeanRecursive 
	 * @param {THREE.Object3D} node node from which mean will be computed recursively including itself
	 * @return {THREE.Vector3} the point which represents the computed mean
	 * @TODO does NOT take care of MatrixRotations, e.g. if verticalAxis=Z
 	 */
	computeVertexMeanRecursive: function(node) {
			
		var allVertices = [];
		
		node.traverse(function(el) {
			if ("geometry" in el){ 
			allVertices.push.apply( allVertices, el.geometry.vertices);
			}
		});
		
		//alert(allVertices.length);
		
		var sum = new THREE.Vector3();
		for(var i = 0; i < allVertices.length; i++){
		    sum = sum.add(allVertices[0]);
		}

		var mean = sum.divideScalar(allVertices.length);
		
		
		return mean;
	},
	
	/**
	 * Performs a rotation of a point around a center
	 * 
	 * @method polarTransformationAddAngle
	 * @param {THREE.Vector3} center center of rotation
	 * @param {THREE.Vector3} point point to rotate around center
	 * @param {Number} deltaTheta horizontal angle in degrees to be rotated
	 * @param {Number} deltaPhi vertical angle in degrees to be rotated
	 * @return {THREE.Vector3} rotatedPoint
	 */
	polarTransformationAddAngle : function (center, point, deltaTheta, deltaPhi){
		var deltaTheta = THREE.Math.degToRad(deltaTheta);
		var deltaPhi = THREE.Math.degToRad(deltaPhi);
		var offset = point.clone().sub(center);
		// angle from z-axis around y-axis
		var theta = Math.atan2( offset.x, offset.z );
		// angle from y-axis
		var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
		//add deltas
		theta += deltaTheta;
		phi += deltaPhi;
		//transformation
		var radius = offset.length();
		offset.x = radius * Math.sin( phi ) * Math.sin( theta );
		offset.y = radius * Math.cos( phi );
		offset.z = radius * Math.sin( phi ) * Math.cos( theta );
		
		point.copy( center ).add( offset );
		return point;
	},
	
	/**
	 * sets the position property of an object to its boundingBoxCenter
	 * and changes vertex coordinates accordingly
	 * 
	 * @method translatePositionToBBoxCenter
	 * @param {THREE.Object3D} object
	 */
	translatePositionToBBoxCenter : function(object) {
		if(object.geometry && object.geometry.boundingBox){
			
			var translationVector = object.geometry.boundingBox.center().sub(object.position);
			object.position.add(translationVector);
			object.geometry.vertices.forEach(function(vertex) {vertex.sub(translationVector);});
			object.geometry.verticesNeedUpdate = true;
		}
	},
	
	/**
 	 * function to sort objects by object.renderDepth property.
 	 * Calculates distance from camera to object.position and subtracts the boundingSphere.radius.
 	 * Helps to obtain a better sorting of small objects inside bigger ones.
 	 * 
 	 * @method calcRenderDepthMinusBSphereRadius
 	 * @param {THREE.Object3D} object
 	 * @param {THREE.Camera} camera
 	 * @return {Number} distance
 	 */
 	calcRenderDepthMinusBSphereRadius : function(object, camera) {
 		var _vector3 = new THREE.Vector3();
 		var _projScreenMatrix = new THREE.Matrix4();
 		
 		_vector3.getPositionFromMatrix( object.matrixWorld );
 		
 		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
 		
		_vector3.applyProjection( _projScreenMatrix );

		return _vector3.z - object.geometry.boundingSphere.radius;
 	},

	/**
	 * function to calculate parameter for orthographic cameras based on params from a perspective camera and a distance at which the visible size of an object should be maintained
	 * 
	 * @method getOrthoParamsFromPerspectiveCam
	 * @param {HTMLCanvasElement} canvas
	 * @param {THREE.PerspectiveCamera} perspectiveCam 
	 * @param {Number} focusDistance
	 * @return {Object} parameters for THREE.OrthographicCamera
	 */
	getOrthoParamsFromPerspectiveCam : function(canvas, perspectiveCam, focusDistance) {
		

		var fov = perspectiveCam.fov;
		var focusDistance = (focusDistance)? focusDistance :( perspectiveCam.near + perspectiveCam.far ) / 2;
		var aspect = perspectiveCam.aspect; //canvas.width / canvas.height;
		
		var orthoparams = this.getOrthoSizeFromFovDistanceAspect(fov, focusDistance, aspect);
		
		orthoparams.near 	= perspectiveCam.near;
		orthoparams.far		= perspectiveCam.far;
		
		return orthoparams;
	},
	
	/**
	 * function to calculate the size parameters for an orthographic camera
	 * 
	 * @method getOrthoSizeFromFovDistanceAspect
	 * @param {Number} fov in degrees
	 * @param {Number} distance at which size is to be computed according to fov
	 * @param {Number} aspect ratio of canvas with / height
	 * @return {Object} orthosize Object which contains (top, bottom, left, right)-values for orthographic Cameras
	 */
	getOrthoSizeFromFovDistanceAspect : function (fov, distance, aspect){
		var orthosize = {};
		
		var halfHeight = Math.tan( THREE.Math.degToRad(fov) / 2 ) * distance;
		var planeHeight = 2 * halfHeight;
		var planeWidth = planeHeight * aspect;
		var halfWidth = planeWidth / 2;
		
		orthosize.left	= -halfWidth;
		orthosize.right	=  halfWidth;
		orthosize.top		=  halfHeight;
		orthosize.bottom	= -halfHeight;
		
		return orthosize;
	},
	
	/**
	 * function to calculate the distance a perspective camera needs from a target to maintain the visual size of objects
	 * 
	 * @method getPerspectiveDistanceFromFovHalfHeight
	 * @param {Number} fov field of view in degrees
	 * @param {Number} halfHeight half of the orthographic camera height or height at distance to be visible in camera in scene units
	 * @return {Number} distance a perspective camera with the given field of view (fov) should be placed from a target to completly see the given (half-)height
	 */
	getPerspectiveDistanceFromFovHalfHeight : function(fov, halfHeight) {
		
		var distance = halfHeight / Math.tan(THREE.Math.degToRad(fov) / 2);
		
		return distance;
	},
	
	/**
	 * check for item in array 
	 * 
	 * @method arrayContains
	 * @param {Array} array
	 * @param {item} item item to check for in array
	 */
	
	arrayContains : function(array, item) {
		for (var i = 0, j = array.length; i < j; i++) {
			if (array[i] === item)
				return true;
		}
	},
	
	/**
	 * Removes duplicates from an array, even different objects with equal properties
	 * 
	 * @method removeDuplicatesFromArray
	 * @param {Array} array
	 * @return {Array} filteredArray new filtered array
	 */
	removeDuplicatesFromArray : function(array) {
	    var seen = {};
	    return array.filter(function(elem) {
	        var k = JSON.stringify(elem);
	        return (seen[k] === 1) ? 0 : seen[k] = 1;
	    });
	},
	
	/**
	 * Flatten 3D Vectors horizontally to 2D
	 * Removes y from vector3 and puts z as y of vector2.
	 *
	 * @method vector3ToVector2
	 * @param {THREE.Vector3} v3  
	 * @return {THREE.Vector2}
	 */
	vector3ToVector2 : function(v3) {
		var v2 = new THREE.Vector2(v3.x, v3.z);
		return v2;
	},
	
	
	/**
	 * Transform a Vector2 to Vector3 by adding a y component
	 * @method vector2ToVector3
	 * @param {THREE.Vector2} v2 represents horizontal points
	 * @param {Number} y height to add to horizontal point
	 * @return {THREE.Vector3}
	 */
	vector2ToVector3 : function(v2, y) {
		var v3 = new THREE.Vector3(v2.x, y , v2.y);
		return v3;
	},
	
	/**
	 * Delete an object properly 
	 * @method disposeObject
	 * @param {THREE.Object3D} object the object to be disposed
	 * @param {Boolean} deleteGeometry should the geometry of the object be disposed 
	 * @param {Boolean} deleteMaterial should the material of the object be disposed also? Set to false if material is shared by other objects
	 * @param {Boolean} deleteTextures should the textures of the object be disposed also? Set to false if textures are shared by other objects
	 * @param {Boolean} recursive should all descendants of the object be disposed by the same criteria aswell?
	 */
	disposeObject : function(object, deleteGeometry, deleteMaterial, deleteTextures, recursive) {
		var materials = [], textures = [];
		
		if(recursive){
			var objects = object.getDescendants();
			objects.push(object);
			objects.forEach(function(object,i,a){
				GIScene.Utils.disposeObject(object, deleteGeometry, deleteMaterial, deleteTextures, false);
			}.bind(this));
		}
		
		if(deleteGeometry){
			if(object.geometry){
				object.geometry.dispose();
				delete object.geometry;
			}
		}
		
		
		if(deleteMaterial){	
			//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;
			
			
			if(deleteTextures){
				// 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]]);
							material[maps[i]] = null;
						};
					};
				}); 
			}
		}
		
		//dispose objects
		if(deleteGeometry){
			if(object.dispose)object.dispose();
			object = null;
		}
		
		//dispose textures, materials
		textures.forEach(function (texture) {
		  texture.dispose();
		});
		textures = null;
		
		materials.forEach(function (material) {
		  material.dispose();
		});
		materials = null;
	},
	
	/**
	 *isTypeOrClass 
	 * @method isTypeOrClass
	 * @param {String} typeOrClassName
	 */
	equalsTypeOrClass : function(valueOrObject, typeOrClassName) {
		
		var check;
		
		if ( typeof valueOrObject == "object") {
			console.log("object");
			try {
				check = valueOrObject instanceof  eval(typeOrClassName);
			} catch(e) {
				//primitive strings will fail
				check = (typeOrClassName.toLowerCase() == "object")? true : false;
			}
		} else {
			console.log("primitive");
			var check = typeof valueOrObject === typeOrClassName.toLowerCase();
		}

		return check;

		},
		
	/**
	 *get Objects by recursively calling a callback function which should return true or false to all children
	 * 
	 * @method getObjectsBy
	 * @param {THREE.Object3D} rootObject
	 * @param {Function} callback
	 */
	getObjectsBy: function(rootObject,callback) {
		var matches=[];
		var evalfunction = function(object) {
			if(callback(object)){
				matches.push(object);
			}
		};
		rootObject.traverse(evalfunction);
		
		return matches;
	}

}
;