function parse_params(light, xml)
		{
			for(var i = 0; i < xml.childNodes.length; i++)
			{
				var child = xml.childNodes.item(i);
				if( !child || child.nodeType != 1 ) //tag
					continue;

				switch( child.localName )
				{
					case "color": 
						light.color = Collada.readContentAsFloats( child ); break;
					case "falloff_angle": 
						light.angle_end = Collada.readContentAsFloats( child )[0]; 
						light.angle = light.angle_end - 10; 
					break;
				}
			}
		}
** OBJ parser adapted from SpiderGL implementation *****************
var parserOBJ = {
	extension: 'obj',
	type: 'mesh',
	resource: 'Mesh',
	format: 'text',
	dataType:'text',

	flipAxis: false,

	parse: function(text, options)
	{
		options = options || {};
		var support_uint = true;

		//unindexed containers
		var vertices = [];
		var normals = [];
		var uvs = [];

		//final containers
		var vertices_buffer_data = [];
		var normals_buffer_data = [];
		var uvs_buffer_data = [];

		//groups
		var group_id = 0;
		var groups = [];
		var current_group_materials = {};
		var last_group_name = null;
		var materials_found = {};
		var mtllib = null;
		var group = createGroup();

		var indices_map = new Map();
		var next_index = 0;

		var V_CODE = 1;
		var VT_CODE = 2;
		var VN_CODE = 3;
		var F_CODE = 4;
		var G_CODE = 5;
		var O_CODE = 6;
		var USEMTL_CODE = 7;
		var MTLLIB_CODE = 8;
		var codes = { v: V_CODE, vt: VT_CODE, vn: VN_CODE, f: F_CODE, g: G_CODE, o: O_CODE, usemtl: USEMTL_CODE, mtllib: MTLLIB_CODE };

		var x,y,z;

		var lines = text.split("\n");
		var length = lines.length;
		for (var lineIndex = 0;  lineIndex < length; ++lineIndex) {

			var line = lines[lineIndex];
			line = line.replace(/[ \t]+/g, " ").replace(/\s\s*$/, ""); //better than trim

			if(line[ line.length - 1 ] == "\\") //breakline support
			{
				lineIndex += 1;
				var next_line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, ""); //better than trim
				line = (line.substr(0,line.length - 1) + next_line).replace(/[ \t]+/g, " ").replace(/\s\s*$/, "");
			}
			
			if (line[0] == "#")
				continue;
			if(line == "")
				continue;

			var tokens = line.split(" ");
			var code = codes[ tokens[0] ];

			if( code <= VN_CODE ) //v,vt,vn
			{
				x = parseFloat(tokens[1]);
				y = parseFloat(tokens[2]);
				if( code != VT_CODE ) //not always present
					z = parseFloat(tokens[3]); 
			}
			
			switch(code)
			{
				case V_CODE: vertices.push(x,y,z);	break;
				case VT_CODE: uvs.push(x,y);	break;
				case VN_CODE: normals.push(x,y,z);	break;
				case F_CODE: 
					if (tokens.length < 4)
						continue; //faces with less that 3 vertices? nevermind
					//get the triangle indices
					var polygon_indices = [];
					for(var i = 1; i < tokens.length; ++i)
						polygon_indices.push( getIndex( tokens[i] ) );
					group.indices.push( polygon_indices[0], polygon_indices[1], polygon_indices[2] );
					//polygons are break intro triangles (assuming they are convex!)
					for(var i = 2; i < polygon_indices.length-1; ++i)
						group.indices.push( polygon_indices[0], polygon_indices[i], polygon_indices[i+1] );
					break;
				case G_CODE:  
				case O_CODE:  //whats the difference?
					var name = tokens[1];
					last_group_name = name;
					if(!group.name)
						group.name = name;
					else
					{
						current_group_materials = {};
						group = createGroup( name );
					}
					break;
				case USEMTL_CODE: 
					changeMaterial( tokens[1] );
					break;
				case MTLLIB_CODE:
					mtllib = tokens[1];
					break;
				default:
			}
		}

		//generate indices
		var indices = [];
		var group_index = 0;
		var final_groups = [];
		for(var i = 0; i < groups.length; ++i)
		{
			var group = groups[i];
			if(!group.indices) //already added?
				continue;
			group.start = group_index;
			group.length = group.indices.length;
			indices = indices.concat( group.indices );
			delete group.indices; //do not store indices in JSON format!
			group_index += group.length;
			final_groups.push( group );
		}
		groups = final_groups;

		//finish mesh
		var mesh = {};

		if(!vertices.length)
		{
			console.error("mesh without vertices");
			return null;
		}

		//create typed arrays
		mesh.vertices = new Float32Array( vertices_buffer_data );
		if ( normals_buffer_data.length )
			mesh.normals = new Float32Array( normals_buffer_data );
		if ( uvs_buffer_data.length )
			mesh.coords = new Float32Array( uvs_buffer_data );
		if ( indices && indices.length > 0 )
			mesh.triangles = new ( support_uint && group_index > 256*256 ? Uint32Array : Uint16Array )(indices);

		//extra info
		mesh.bounding = GL.Mesh.computeBoundingBox( mesh.vertices );
		var info = {};
		if(groups.length > 1)
		{
			info.groups = groups;
			//compute bounding of groups? //TODO: this is complicated, it is affected by indices, etc, better done afterwards
		}

		mesh.info = info;
		if( !mesh.bounding )
		{
			console.log("empty mesh");
			return null;
		}

		if( mesh.bounding.radius == 0 || isNaN(mesh.bounding.radius))
			console.log("no radius found in mesh");
		//console.log(mesh);
		return mesh;

		//this function helps reuse triplets that have been created before
		function getIndex( str )
		{
			var pos,tex,nor,f;
			var has_negative = false;

			//cannot use negative indices as keys, convert them to positive
			if(str.indexOf("-") == -1)
			{
				var index = indices_map.get(str);
				if(index !== undefined)
					return index;
			}
			else
				has_negative = true;

			if(!f) //maybe it was parsed before
				f = str.split("/");

			if (f.length == 1) { //unpacked
				pos = parseInt(f[0]);
				tex = pos;
				nor = pos;
			}
			else if (f.length == 2) { //no normals
				pos = parseInt(f[0]);
				tex = parseInt(f[1]);
				nor = pos;
			}
			else if (f.length == 3) { //all three indexed
				pos = parseInt(f[0]);
				tex = parseInt(f[1]);
				nor = parseInt(f[2]);
			}
			else {
				console.log("Problem parsing: unknown number of values per face");
				return -1;
			}

			//negative indices are relative to the end
			if(pos < 0) 
				pos = vertices.length / 3 + pos + 1;
			if(nor < 0)
				nor = normals.length / 2 + nor + 1;
			if(tex < 0)
				tex = uvs.length / 2 + tex + 1;

			//try again to see if we already have this
			if(has_negative)
			{
				str = pos + "/" + tex + "/" + nor;
				var index = indices_map.get(str);
				if(index !== undefined)
					return index;
			}

			//fill buffers
			pos -= 1; tex -= 1; nor -= 1; //indices in obj start in 1, buffers in 0
			vertices_buffer_data.push( vertices[pos*3+0], vertices[pos*3+1], vertices[pos*3+2] );
			if(uvs.length)
				uvs_buffer_data.push( uvs[tex*2+0], uvs[tex*2+1] );
			if(normals.length)
				normals_buffer_data.push( normals[nor*3+0], normals[nor*3+1], normals[nor*3+2] );

			//store index
			var index = next_index;
			indices_map.set( str, index );
			++next_index;
			return index;
		}

		function createGroup( name )
		{
			var g = {
				name: name || "",
				material: "",
				start: -1,
				length: -1,
				indices: []
			};
			groups.push(g);
			return g;
		}

		function changeMaterial( material_name )
		{
			if( !group.material )
			{
				group.material = material_name + ".json";
				current_group_materials[ material_name ] = group;
				return group;
			}

			var g = current_group_materials[ material_name ];
			if(!g)
			{
				g = createGroup( last_group_name + "_" + material_name );
				g.material = material_name + ".json";
				current_group_materials[ material_name ] = g;
			}
			group = g;
			return g;
		}
	}
};

LS.Formats.addSupportedFormat( "obj", parserOBJ );


//***** MTL parser *****************
//info from: http://paulbourke.net/dataformats/mtl/
var parserMTL = {
	extension: 'mtl',
	type: 'material',
	resource: 'StandardMaterial',
	format: 'text',
	dataType:'text',

	parse: function( text, options )
	{
		var lines = text.split("\n");
		var length = lines.length;

		var materials = {};
		var current_material = null;

		for (var lineIndex = 0;  lineIndex < length; ++lineIndex)
		{
			var line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, ""); //trim
			line = line.trim();

			if (line[0] == "#" || line == "")
				continue;

			var tokens = line.split(" ");
			var c = tokens[0];

			switch(c)
			{
				case "newmtl":
					var filename = tokens[1] + ".json";
					current_material = { filename: filename, textures: {} };
					materials[ filename ] = current_material;
					break;
				case "Ka":
					current_material.ambient = readVector3(tokens);
					break;
				case "Kd":
					current_material.color = readVector3(tokens);
					break;
				case "Ks":
					current_material.specular_factor = parseFloat(tokens[1]); //readVector3(tokens);
					break;
				case "Ke":
					current_material.emissive = readVector3(tokens); //readVector3(tokens);
					break;
				case "Ns": //glossiness
					current_material.specular_gloss = parseFloat(tokens[1]);
					break;
				case "Tr": //reflection coefficient
					current_material.reflection = parseFloat( tokens[1] );
					break;
				case "map_Kd":
					current_material.textures["color"] = this.clearPath( tokens[1] );
					current_material.color = [1,1,1];
					break;
				case "map_Ka":
					current_material.textures["ambient"] = this.clearPath( tokens[1] );
					current_material.ambient = [1,1,1];
					break;
				case "map_Ks":
					current_material.textures["specular"] = this.clearPath( tokens[1] );
					current_material.specular_factor = 1;
					break;
				case "map_d":
					current_material.textures["opacity"] = this.clearPath( tokens[1] );
					current_material.blend_mode = LS.Blend.ALPHA;
					break;
				case "bump":
				case "map_bump":
					current_material.textures["bump"] = this.clearPath( tokens[1] );
					break;
				case "d": //disolve is like transparency
					current_material.opacity = parseFloat( tokens[1] );
					break;
				case "Tr": //reflection coefficient
					current_material.opacity = parseFloat( tokens[1] );
					break;
				//Not supported stuff
				case "illum": //illumination model (raytrace related)
				case "Tf": //reflection by components
				case "Ni": //refraction coefficient
					break;
				default:
					console.log("Unknown MTL info: ", c);
					break;
			}
		}

		for(var i in materials)
		{
			var material_info = materials[i];

			//hack, ambient must be 1,1,1
			material_info.ambient = [1,1,1];

			var material = new LS.StandardMaterial(material_info);
			LS.RM.registerResource( material_info.filename, material );
		}

		return null;

		function readVector3(v)
		{
			return [ parseFloat(v[1]), parseFloat(v[2]), parseFloat(v[3]) ];
		}
	},

	clearPath: function(path)
	{
		var pos = path.lastIndexOf("\\");
		if(pos != -1)
			path = path.substr(pos+1);
		var filename = LS.RM.getFilename(path);
		if( LS.RM.resources_renamed_recently[filename] )
			filename = LS.RM.resources_renamed_recently[filename];
		return filename.toLowerCase();
	}
};

LS.Formats.addSupportedFormat( "mtl", parserMTL );
///@FILE:../src/parsers/parserSTL.js
///@INFO: PARSER
//***** STL Parser *****************
//based on https://github.com/tonylukasavage/jsstl
var parserSTL = {
	extension: 'stl',
	type: 'mesh',
	format: 'binary',
	dataType:'arraybuffer',
	
	parse: function( data, options )
	{
		options = options || {};

		var positionsArray = [];
		var normalsArray = [];
		var indicesArray = [];

		var dv = new DataView(data, 80); // 80 == unused header
		var isLittleEndian = true;
		var triangles = dv.getUint32(0, isLittleEndian); 
		// console.log('arraybuffer length:  ' + stl.byteLength);
		console.log('number of triangles: ' + triangles);
		var offset = 4;

		var tempA = vec3.create();
		var tempB = vec3.create();
		var tempC = vec3.create();
		var tempN = vec3.create();

		for (var i = 0; i < triangles; i++) {
			// Get the normal for this triangle
			var nx = dv.getFloat32(offset, isLittleEndian);
			var ny = dv.getFloat32(offset+4, isLittleEndian);
			var nz = dv.getFloat32(offset+8, isLittleEndian);

			offset += 12;
			// Get all 3 vertices for this triangle
			for (var j = 0; j < 3; j++) {
				var x = dv.getFloat32(offset, isLittleEndian);
				var y = dv.getFloat32(offset+4, isLittleEndian);
				var z = dv.getFloat32(offset+8, isLittleEndian);
				//positionsArray.push(x,y,z);
				positionsArray.push(x,z,-y); //flipped
				offset += 12
			}
			
			if(nx == 0 && ny == 0 && nz == 0) //compute normal
			{
				var l = positionsArray.length;
				tempA.set( positionsArray.slice(l-9,l-6) );
				tempB.set( positionsArray.slice(l-6,l-3) );
				tempC.set( positionsArray.slice(l-3,l) );
				vec3.sub( tempB, tempB, tempA );
				vec3.sub( tempC, tempC, tempA );
				vec3.cross( tempN, tempC, tempB );
				nx = tempN[0]; ny = tempN[1]; nz = tempN[2];
			}

			//normalsArray.push(nx,ny,nz,nx,ny,nz,nx,ny,nz);
			normalsArray.push(nx,nz,-ny,nx,nz,-ny,nx,nz,-ny); //flipped

			// there's also a Uint16 "attribute byte count" that we
			// don't need, it should always be zero.
			offset += 2;   
			// Create a new face for from the vertices and the normal             
			//indicesArray.push( i*3, i*3+1, i*3+2 );
		}
		// The binary STL I'm testing with seems to have all
		// zeroes for the normals, unlike its ASCII counterpart.
		// We can use three.js to compute the normals for us, though,
		// once we've assembled our geometry. This is a relatively 
		// expensive operation, but only needs to be done once.

		var mesh = { info: {} };

		mesh.vertices = new Float32Array(positionsArray);
		if (normalsArray.length > 0)
			mesh.normals = new Float32Array(normalsArray);
		if (indicesArray && indicesArray.length > 0)
			mesh.triangles = new Uint16Array(indicesArray);

		//extra info
		mesh.bounding = LS.Formats.computeMeshBounding( mesh.vertices );
		return mesh;
	}
};

LS.Formats.addSupportedFormat( "stl", parserSTL );

///@FILE:../src/parsers/parserTGA.js
///@INFO: PARSER
var parserTGA = { 
	extension: 'tga',
	type: 'image',
	dataType:"arraybuffer",
	format: 'binary',
	skip_conversion: true,

	parse: function(data, options)
	{
		if(!data || data.constructor !== ArrayBuffer)
			throw( "ParserTGA: data must be ArrayBuffer");

		data = new Uint8Array(data);
		var TGAheader = new Uint8Array( [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0] );
		var TGAcompare = data.subarray(0,12);
		for(var i = 0; i < TGAcompare.length; i++)
			if(TGAheader[i] != TGAcompare[i])
			{
				console.error("TGA header is not valid");
				return null; //not a TGA
			}

		var header = data.subarray(12,18);

		var img = {};
		img.width = header[1] * 256 + header[0];
		img.height = header[3] * 256 + header[2];
		img.bpp = header[4];
		img.bytesPerPixel = img.bpp / 8;
		img.imageSize = img.width * img.height * img.bytesPerPixel;
		img.pixels = data.subarray(18,18+img.imageSize);
		img.pixels = new Uint8Array( img.pixels ); 	//clone
		var pixels = img.pixels;

		//TGA comes in BGR format so we swap it, this is slooooow
		for(var i = 0, l = img.imageSize, d = img.bytesPerPixel; i < l; i+= d )
		{
			var temp = pixels[i];
			pixels[i] = pixels[i+2];
			pixels[i+2] = temp;
		}
		img.format = img.bpp == 32 ? "RGBA" : "RGB";

		//if(	(header[5] & (1<<4)) == 0) //hack, needs swap
		//{
			//header[5] |= 1<<5; //mark as Y swaped
		//}
		//else
		//	img.format = img.bpp == 32 ? "RGBA" : "RGB";

		//some extra bytes to avoid alignment problems
		//img.pixels = new Uint8Array( img.imageSize + 14);
		//img.pixels.set( data.subarray(18,18+img.imageSize), 0);

		img.flipY = true;
		//img.format = img.bpp == 32 ? "BGRA" : "BGR";
		//trace("TGA info: " + img.width + "x" + img.height );
		return img;
	}
};

LS.Formats.addSupportedFormat( "tga", parserTGA );
///@FILE:../src/parsers/collada.js
// Collada.js  https://github.com/jagenjo/collada.js
// Javi Agenjo 2015 
// Specification from https://www.khronos.org/collada/wiki
var _collada;

(function(global){

var isWorker = typeof(window) == "undefined" && typeof(exports) == "undefined";
var DEG2RAD = Math.PI * 2 / 360;

//global temporal variables
var temp_mat4 = null;
var temp_vec2 = null;
var temp_vec3 = null;
var temp_vec4 = null;
var temp_quat = null;

if( isWorker )
{
	global.console = {
		log: function(msg) { 
			var args = Array.prototype.slice.call(arguments, 0);
			self.postMessage({action:"log", params: args});
		},
		warn: function(msg) { 
			var args = Array.prototype.slice.call(arguments, 0);
			self.postMessage({action:"warn", params: args});
		},
		error: function(msg) { 
			var args = Array.prototype.slice.call(arguments, 0);
			self.postMessage({action:"error", params: args});
		}
	};

	global.alert = console.error;
}

//Collada parser
global.Collada = _collada = {

	libsPath: "./",
	workerPath: "./",
	no_flip: true,
	use_transferables: true, //for workers
	onerror: null,
	verbose: false,
	config: { forceParser:false },

	init: function (config)
	{
		config = config || {}
		for(var i in config)
			this[i] = config[i];
		this.config = config;

		if( isWorker )
		{
			try
			{
				importScripts( this.libsPath + "gl-matrix-min.js", this.libsPath + "tinyxml.js" );
			}
			catch (err)
			{
				Collada.throwException( Collada.LIBMISSING_ERROR );
			}
		}

		//init glMatrix
		temp_mat4 = mat4.create();
		temp_vec2 = vec3.create();
		temp_vec3 = vec3.create();
		temp_vec4 = vec3.create();
		temp_quat = quat.create();

		if( isWorker )
			console.log("Collada worker ready");
	},

	load: function(url, callback)
	{
		request(url, function(data)
		{
			if(!data)
				callback( null );
			else
				callback( Collada.parse( data ) );
		});
	},

	_xmlroot: null,
	_nodes_by_id: null,
	_nodes_by_sid: null,
	_transferables: null,
	_controllers_found: null,
	_geometries_found: null,

	safeString: function (str) { 
		if(!str)
			return "";

		if(this.convertID)
			return this.convertID(str);

		return str.replace(/ /g,"_"); 
	},

	LIBMISSING_ERROR: "Libraries loading error, when using workers remember to pass the URL to the tinyxml.js in the options.libsPath",
	NOXMLPARSER_ERROR: "TinyXML not found, when using workers remember to pass the URL to the tinyxml.js in the options.libsPath (Workers do not allow to access the native XML DOMParser)",
	throwException: function(msg)
	{
		if(isWorker)
			self.postMessage({action:"exception", msg: msg});
		else
			if(Collada.onerror)
				Collada.onerror(msg);
		throw(msg);
	},

	getFilename: function(filename)
	{
		var pos = filename.lastIndexOf("\\");
		if(pos != -1)
			filename = filename.substr(pos+1);
		//strip unix slashes
		pos = filename.lastIndexOf("/");
		if(pos != -1)
			filename = filename.substr(pos+1);
		return filename;
	},

	last_name: 0,

	generateName: function(v)
	{
		v = v || "name_";
		var name = v + this.last_name;
		this.last_name++;
		return name;
	},

	parse: function(data, options, filename)
	{
		options = options || {};
		filename = filename || "_dae_" + Date.now() + ".dae";

		//console.log("Parsing collada");
		var flip = false;

		var xmlparser = null;
		var root = null;
		this._transferables = [];
		
		if(this.verbose)
			console.log(" - XML parsing...");

		if(global["DOMParser"] && !this.config.forceParser )
		{
			xmlparser = new DOMParser();
			root = xmlparser.parseFromString(data,"text/xml");
			if(this.verbose)
				console.log(" - XML parsed");			
		}
		else //USING JS XML PARSER IMPLEMENTATION (much slower)
		{
			if(!global["DOMImplementation"] )
				return Collada.throwException( Collada.NOXMLPARSER_ERROR );
			//use tinyxmlparser
			try
			{
				xmlparser = new DOMImplementation();
			}
			catch (err)
			{
				return Collada.throwException( Collada.NOXMLPARSER_ERROR );
			}

			root = xmlparser.loadXML(data);
			if(this.verbose)
				console.log(" - XML parsed");

			//for every node...
			var by_ids = root._nodes_by_id = {};
			for(var i = 0, l = root.all.length; i < l; ++i)
			{
				var node = root.all[i];
				by_ids[ node.id ] = node;
				if(node.getAttribute("sid"))
					by_ids[ node.getAttribute("sid") ] = node;
			}

			if(!this.extra_functions)
			{
				this.extra_functions = true;
				//these methods are missing so here is a lousy implementation
				DOMDocument.prototype.querySelector = DOMElement.prototype.querySelector = function(selector)
				{
					var tags = selector.split(" ");
					var current_element = this;

					while(tags.length)
					{
						var current = tags.shift();
						var tokens = current.split("#");
						var tagname = tokens[0];
						var id = tokens[1];
						var elements = tagname ? current_element.getElementsByTagName(tagname) : current_element.childNodes;
						if(!id) //no id filter
						{
							if(tags.length == 0)
								return elements.item(0);
							current_element = elements.item(0);
							continue;
						}

						//has id? check for all to see if one matches the id
						for(var i = 0; i < elements.length; i++)
							if( elements.item(i).getAttribute("id") == id)
							{
								if(tags.length == 0)
									return elements.item(i);
								current_element = elements.item(i);
								break;
							}
					}
					return null;
				}

				DOMDocument.prototype.querySelectorAll = DOMElement.prototype.querySelectorAll = function( selector )
				{
					var tags = selector.split(" ");
					if(tags.length == 1)
						return this.getElementsByTagName( selector );

					var current_element = this;
					var result = [];

					inner(this, tags);

					function inner(root, tags )
					{
						if(!tags)
							return;

						var current = tags.shift();
						var elements = root.getElementsByTagName( current );
						if(tags.length == 0)
						{
							for(var i = 0; i < elements.length; i++)
								result.push( elements.item(i) );
							return;
						}

						for(var i = 0; i < elements.length; i++)
							inner( elements.item(i), tags.concat() );
					}

					var list = new DOMNodeList(this.documentElement);
					list._nodes = result;
					list.length = result.length;

					return list;
				}

				Object.defineProperty( DOMElement.prototype, "textContent", { 
					get: function() { 
						var nodes = this.getChildNodes();
						return nodes.item(0).toString(); 
					},
					set: function() {} 
				});
			}
		}
		this._xmlroot = root;
		var xmlcollada = root.querySelector("COLLADA");
		if(xmlcollada)
		{
			this._current_DAE_version = xmlcollada.getAttribute("version");
			console.log("DAE Version:" + this._current_DAE_version);
		}

		//var xmlvisual_scene = root.querySelector("visual_scene");
		var xmlvisual_scene = root.getElementsByTagName("visual_scene").item(0);
		if(!xmlvisual_scene)
			throw("visual_scene XML node not found in DAE");

		//hack to avoid problems with bones with spaces in names
		this._nodes_by_id = {}; //clear
		this._nodes_by_sid = {}; //clear
		this._controllers_found = {};//we need to check what controllers had been found, in case we miss one at the end
		this._geometries_found = {};

		//Create a scene tree
		var scene = { 
			object_class:"Scene", 
			light: null,
			materials: {},
			meshes: {},
			resources: {}, //used to store animation tracks
			root:{ children:[] },
			external_files: {} //store info about external files mentioned in this 
		};

		//scene metadata (like author, tool, up vector, dates, etc)
		var xmlasset = root.getElementsByTagName("asset")[0];
		if(xmlasset)
			scene.metadata = this.readAsset( xmlasset );

		//parse nodes tree to extract names and ierarchy only
		var xmlnodes = xmlvisual_scene.childNodes;
		for(var i = 0; i < xmlnodes.length; i++)
		{
			var item = xmlnodes.item(i);
			if(item.nodeType != 1 ) //not tag
				continue;

			var node = null;
			if(item.localName == "node")
				node = this.readNodeTree( item, scene, 0, flip );
			else 
				console.warn("DAE contains unknown node type: " + item.localName );

			if(node)
				scene.root.children.push(node);
		}

		//parse nodes content (two steps so we have first all the scene tree info)
		for(var i = 0; i < xmlnodes.length; i++)
		{
			if(xmlnodes.item(i).localName != "node")
				continue;
			this.readNodeInfo( xmlnodes.item(i), scene, 0, flip );
		}

		//read remaining controllers (in some cases some controllers are not linked from the nodes or the geometries)
		this.readLibraryControllers( scene );

		//read animations
		var animations = this.readAnimations( root, scene );
		if(animations)
		{
			var animations_name = "animations_" + filename.substr(0,filename.indexOf("."));
			scene.resources[ animations_name ] = animations;
			scene.root.animation = animations_name;
		}

		//read external files (images)
		scene.images = this.readImages(root);

		//clear memory
		this._nodes_by_id = {};
		this._nodes_by_sid = {};
		this._controllers_found = {};
		this._geometries_found = {};
		this._xmlroot = null;

		//console.log(scene);
		return scene;
	},

	/* Collect node ids, in case there is bones (with spaces in name) I need to know the nodenames in advance