1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 #include "IrrCompileConfig.h"
6 #ifdef _IRR_COMPILE_WITH_COLLADA_LOADER_
7 
8 #include "CColladaFileLoader.h"
9 #include "os.h"
10 #include "IXMLReader.h"
11 #include "IDummyTransformationSceneNode.h"
12 #include "SAnimatedMesh.h"
13 #include "fast_atof.h"
14 #include "quaternion.h"
15 #include "ILightSceneNode.h"
16 #include "ICameraSceneNode.h"
17 #include "IMeshManipulator.h"
18 #include "IReadFile.h"
19 #include "IAttributes.h"
20 #include "IMeshCache.h"
21 #include "IMeshSceneNode.h"
22 #include "SMeshBufferLightMap.h"
23 #include "irrMap.h"
24 
25 #ifdef _DEBUG
26 #define COLLADA_READER_DEBUG
27 #endif
28 namespace irr
29 {
30 namespace scene
31 {
32 namespace
33 {
34 	// currently supported COLLADA tag names
35 	const core::stringc colladaSectionName =   "COLLADA";
36 	const core::stringc librarySectionName =   "library";
37 	const core::stringc libraryNodesSectionName = "library_nodes";
38 	const core::stringc libraryGeometriesSectionName = "library_geometries";
39 	const core::stringc libraryMaterialsSectionName = "library_materials";
40 	const core::stringc libraryImagesSectionName = "library_images";
41 	const core::stringc libraryVisualScenesSectionName = "library_visual_scenes";
42 	const core::stringc libraryCamerasSectionName = "library_cameras";
43 	const core::stringc libraryLightsSectionName = "library_lights";
44 	const core::stringc libraryEffectsSectionName = "library_effects";
45 	const core::stringc assetSectionName =     "asset";
46 	const core::stringc sceneSectionName =     "scene";
47 	const core::stringc visualSceneSectionName = "visual_scene";
48 
49 	const core::stringc lightPrefabName =      "light";
50 	const core::stringc cameraPrefabName =     "camera";
51 	const core::stringc materialSectionName =  "material";
52 	const core::stringc geometrySectionName =  "geometry";
53 	const core::stringc imageSectionName =     "image";
54 	const core::stringc textureSectionName =   "texture";
55 	const core::stringc effectSectionName =    "effect";
56 
57 	const core::stringc pointSectionName =     "point";
58 	const core::stringc directionalSectionName ="directional";
59 	const core::stringc spotSectionName =      "spot";
60 	const core::stringc ambientSectionName =   "ambient";
61 	const core::stringc meshSectionName =      "mesh";
62 	const core::stringc sourceSectionName =    "source";
63 	const core::stringc arraySectionName =     "array";
64 	const core::stringc floatArraySectionName ="float_array";
65 	const core::stringc intArraySectionName =  "int_array";
66 	const core::stringc techniqueCommonSectionName = "technique_common";
67 	const core::stringc accessorSectionName =  "accessor";
68 	const core::stringc verticesSectionName =  "vertices";
69 	const core::stringc inputTagName =         "input";
70 	const core::stringc polylistSectionName =  "polylist";
71 	const core::stringc trianglesSectionName = "triangles";
72 	const core::stringc polygonsSectionName =  "polygons";
73 	const core::stringc primitivesName =       "p";
74 	const core::stringc vcountName =           "vcount";
75 
76 	const core::stringc upAxisNodeName =       "up_axis";
77 	const core::stringc nodeSectionName =      "node";
78 	const core::stringc lookatNodeName =       "lookat";
79 	const core::stringc matrixNodeName =       "matrix";
80 	const core::stringc perspectiveNodeName =  "perspective";
81 	const core::stringc rotateNodeName =       "rotate";
82 	const core::stringc scaleNodeName =        "scale";
83 	const core::stringc translateNodeName =    "translate";
84 	const core::stringc skewNodeName =         "skew";
85 	const core::stringc bboxNodeName =         "boundingbox";
86 	const core::stringc minNodeName =          "min";
87 	const core::stringc maxNodeName =          "max";
88 	const core::stringc instanceName =         "instance";
89 	const core::stringc instanceGeometryName = "instance_geometry";
90 	const core::stringc instanceSceneName =    "instance_visual_scene";
91 	const core::stringc instanceEffectName =   "instance_effect";
92 	const core::stringc instanceMaterialName = "instance_material";
93 	const core::stringc instanceLightName =    "instance_light";
94 	const core::stringc instanceNodeName =     "instance_node";
95 	const core::stringc bindMaterialName =     "bind_material";
96 	const core::stringc extraNodeName =        "extra";
97 	const core::stringc techniqueNodeName =    "technique";
98 	const core::stringc colorNodeName =        "color";
99 	const core::stringc floatNodeName =        "float";
100 	const core::stringc float2NodeName =       "float2";
101 	const core::stringc float3NodeName =       "float3";
102 
103 	const core::stringc newParamName =         "newparam";
104 	const core::stringc paramTagName =         "param";
105 	const core::stringc initFromName =         "init_from";
106 	const core::stringc dataName =             "data";
107 	const core::stringc wrapsName =            "wrap_s";
108 	const core::stringc wraptName =            "wrap_t";
109 	const core::stringc minfilterName =        "minfilter";
110 	const core::stringc magfilterName =        "magfilter";
111 	const core::stringc mipfilterName =        "mipfilter";
112 
113 	const core::stringc textureNodeName =      "texture";
114 	const core::stringc doubleSidedNodeName =  "double_sided";
115 	const core::stringc constantAttenuationNodeName = "constant_attenuation";
116 	const core::stringc linearAttenuationNodeName = "linear_attenuation";
117 	const core::stringc quadraticAttenuationNodeName = "quadratic_attenuation";
118 	const core::stringc falloffAngleNodeName = "falloff_angle";
119 	const core::stringc falloffExponentNodeName = "falloff_exponent";
120 
121 	const core::stringc profileCOMMONSectionName = "profile_COMMON";
122 	const core::stringc profileCOMMONAttributeName = "COMMON";
123 
124 	const char* const inputSemanticNames[] = {"POSITION", "VERTEX", "NORMAL", "TEXCOORD",
125 		"UV", "TANGENT", "IMAGE", "TEXTURE", 0};
126 
127 	// We have to read ambient lights like other light types here, so we need a type for it
128 	const video::E_LIGHT_TYPE ELT_AMBIENT = video::E_LIGHT_TYPE(video::ELT_COUNT+1);
129 }
130 
131 	//! following class is for holding and creating instances of library
132 	//! objects, named prefabs in this loader.
133 	class CPrefab : public IColladaPrefab
134 	{
135 	public:
136 
CPrefab(const core::stringc & id)137 		CPrefab(const core::stringc& id) : Id(id)
138 		{
139 		}
140 
141 		//! creates an instance of this prefab
addInstance(scene::ISceneNode * parent,scene::ISceneManager * mgr)142 		virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
143 			scene::ISceneManager* mgr)
144 		{
145 			// empty implementation
146 			return 0;
147 		}
148 
149 		//! returns id of this prefab
getId()150 		virtual const core::stringc& getId()
151 		{
152 			return Id;
153 		}
154 
155 	protected:
156 
157 		core::stringc Id;
158 	};
159 
160 
161 	//! prefab for a light scene node
162 	class CLightPrefab : public CPrefab
163 	{
164 	public:
165 
CLightPrefab(const core::stringc & id)166 		CLightPrefab(const core::stringc& id) : CPrefab(id)
167 		{
168 			#ifdef COLLADA_READER_DEBUG
169 			os::Printer::log("COLLADA: loaded light prefab", Id.c_str(), ELL_DEBUG);
170 			#endif
171 		}
172 
173 		video::SLight LightData; // publically accessible
174 
175 		//! creates an instance of this prefab
addInstance(scene::ISceneNode * parent,scene::ISceneManager * mgr)176 		virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
177 			scene::ISceneManager* mgr)
178 		{
179 			#ifdef COLLADA_READER_DEBUG
180 			os::Printer::log("COLLADA: Constructing light instance", Id.c_str(), ELL_DEBUG);
181 			#endif
182 
183 			if ( LightData.Type == ELT_AMBIENT )
184 			{
185 				mgr->setAmbientLight( LightData.DiffuseColor );
186 				return 0;
187 			}
188 
189 			scene::ILightSceneNode* l = mgr->addLightSceneNode(parent);
190 			if (l)
191 			{
192 				l->setLightData ( LightData );
193 				l->setName(getId());
194 			}
195 			return l;
196 		}
197 	};
198 
199 
200 	//! prefab for a mesh scene node
201 	class CGeometryPrefab : public CPrefab
202 	{
203 	public:
204 
CGeometryPrefab(const core::stringc & id)205 		CGeometryPrefab(const core::stringc& id) : CPrefab(id)
206 		{
207 		}
208 
209 		scene::IMesh* Mesh;
210 
211 		//! creates an instance of this prefab
addInstance(scene::ISceneNode * parent,scene::ISceneManager * mgr)212 		virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
213 			scene::ISceneManager* mgr)
214 		{
215 			#ifdef COLLADA_READER_DEBUG
216 			os::Printer::log("COLLADA: Constructing mesh instance", Id.c_str(), ELL_DEBUG);
217 			#endif
218 
219 			scene::ISceneNode* m = mgr->addMeshSceneNode(Mesh, parent);
220 			if (m)
221 			{
222 				m->setName(getId());
223 //				m->setMaterialFlag(video::EMF_BACK_FACE_CULLING, false);
224 //				m->setDebugDataVisible(scene::EDS_FULL);
225 			}
226 			return m;
227 		}
228 	};
229 
230 
231 	//! prefab for a camera scene node
232 	class CCameraPrefab : public CPrefab
233 	{
234 	public:
235 
CCameraPrefab(const core::stringc & id)236 		CCameraPrefab(const core::stringc& id)
237 			: CPrefab(id), YFov(core::PI / 2.5f), ZNear(1.0f), ZFar(3000.0f)
238 		{
239 			#ifdef COLLADA_READER_DEBUG
240 			os::Printer::log("COLLADA: loaded camera prefab", Id.c_str(), ELL_DEBUG);
241 			#endif
242 		}
243 
244 		// publicly accessible data
245 		f32 YFov;
246 		f32 ZNear;
247 		f32 ZFar;
248 
249 		//! creates an instance of this prefab
addInstance(scene::ISceneNode * parent,scene::ISceneManager * mgr)250 		virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
251 			scene::ISceneManager* mgr)
252 		{
253 			#ifdef COLLADA_READER_DEBUG
254 			os::Printer::log("COLLADA: Constructing camera instance", Id.c_str(), ELL_DEBUG);
255 			#endif
256 
257 			scene::ICameraSceneNode* c = mgr->addCameraSceneNode(parent);
258 			if (c)
259 			{
260 				c->setFOV(YFov);
261 				c->setNearValue(ZNear);
262 				c->setFarValue(ZFar);
263 				c->setName(getId());
264 			}
265 			return c;
266 		}
267 	};
268 
269 
270 	//! prefab for a container scene node
271 	//! Collects other prefabs and instantiates them upon instantiation
272 	//! Uses a dummy scene node to return the children as one scene node
273 	class CScenePrefab : public CPrefab
274 	{
275 	public:
CScenePrefab(const core::stringc & id)276 		CScenePrefab(const core::stringc& id) : CPrefab(id)
277 		{
278 			#ifdef COLLADA_READER_DEBUG
279 			os::Printer::log("COLLADA: loaded scene prefab", Id.c_str(), ELL_DEBUG);
280 			#endif
281 		}
282 
283 		//! creates an instance of this prefab
addInstance(scene::ISceneNode * parent,scene::ISceneManager * mgr)284 		virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
285 			scene::ISceneManager* mgr)
286 		{
287 			#ifdef COLLADA_READER_DEBUG
288 			os::Printer::log("COLLADA: Constructing scene instance", Id.c_str(), ELL_DEBUG);
289 			#endif
290 
291 			if (Children.size()==0)
292 				return 0;
293 
294 			scene::IDummyTransformationSceneNode* s = mgr->addDummyTransformationSceneNode(parent);
295 			if (s)
296 			{
297 				s->setName(getId());
298 				s->getRelativeTransformationMatrix() = Transformation;
299 				s->updateAbsolutePosition();
300 				core::stringc t;
301 				for (u32 i=0; i<16; ++i)
302 				{
303 					t+=core::stringc((double)Transformation[i]);
304 					t+=" ";
305 				}
306 			#ifdef COLLADA_READER_DEBUG
307 			os::Printer::log("COLLADA: Transformation", t.c_str(), ELL_DEBUG);
308 			#endif
309 
310 				for (u32 i=0; i<Children.size(); ++i)
311 					Children[i]->addInstance(s, mgr);
312 			}
313 
314 			return s;
315 		}
316 
317 		core::array<IColladaPrefab*> Children;
318 		core::matrix4 Transformation;
319 	};
320 
321 
322 //! Constructor
CColladaFileLoader(scene::ISceneManager * smgr,io::IFileSystem * fs)323 CColladaFileLoader::CColladaFileLoader(scene::ISceneManager* smgr,
324 		io::IFileSystem* fs)
325 : SceneManager(smgr), FileSystem(fs), DummyMesh(0),
326 	FirstLoadedMesh(0), LoadedMeshCount(0), CreateInstances(false)
327 {
328 	#ifdef _DEBUG
329 	setDebugName("CColladaFileLoader");
330 	#endif
331 }
332 
333 
334 //! destructor
~CColladaFileLoader()335 CColladaFileLoader::~CColladaFileLoader()
336 {
337 	if (DummyMesh)
338 		DummyMesh->drop();
339 
340 	if (FirstLoadedMesh)
341 		FirstLoadedMesh->drop();
342 }
343 
344 
345 //! Returns true if the file maybe is able to be loaded by this class.
346 /** This decision should be based only on the file extension (e.g. ".cob") */
isALoadableFileExtension(const io::path & filename) const347 bool CColladaFileLoader::isALoadableFileExtension(const io::path& filename) const
348 {
349 	return core::hasFileExtension ( filename, "xml", "dae" );
350 }
351 
352 
353 //! creates/loads an animated mesh from the file.
354 //! \return Pointer to the created mesh. Returns 0 if loading failed.
355 //! If you no longer need the mesh, you should call IAnimatedMesh::drop().
356 //! See IReferenceCounted::drop() for more information.
createMesh(io::IReadFile * file)357 IAnimatedMesh* CColladaFileLoader::createMesh(io::IReadFile* file)
358 {
359 	io::IXMLReaderUTF8* reader = FileSystem->createXMLReaderUTF8(file);
360 	if (!reader)
361 		return 0;
362 
363 	CurrentlyLoadingMesh = file->getFileName();
364 	CreateInstances = SceneManager->getParameters()->getAttributeAsBool(
365 		scene::COLLADA_CREATE_SCENE_INSTANCES);
366 	Version = 0;
367 	FlipAxis = false;
368 
369 	// read until COLLADA section, skip other parts
370 
371 	while(reader->read())
372 	{
373 		if (reader->getNodeType() == io::EXN_ELEMENT)
374 		{
375 			if (colladaSectionName == reader->getNodeName())
376 				readColladaSection(reader);
377 			else
378 				skipSection(reader, true); // unknown section
379 		}
380 	}
381 
382 	reader->drop();
383 	if (!Version)
384 		return 0;
385 
386 	// because this loader loads and creates a complete scene instead of
387 	// a single mesh, return an empty dummy mesh to make the scene manager
388 	// know that everything went well.
389 	if (!DummyMesh)
390 		DummyMesh = new SAnimatedMesh();
391 	scene::IAnimatedMesh* returnMesh = DummyMesh;
392 
393 	if (Version < 10400)
394 		instantiateNode(SceneManager->getRootSceneNode());
395 
396 	// add the first loaded mesh into the mesh cache too, if more than one
397 	// meshes have been loaded from the file
398 	if (LoadedMeshCount>1 && FirstLoadedMesh)
399 	{
400 		os::Printer::log("Added COLLADA mesh", FirstLoadedMeshName.c_str());
401 		SceneManager->getMeshCache()->addMesh(FirstLoadedMeshName.c_str(), FirstLoadedMesh);
402 	}
403 
404 	// clean up temporary loaded data
405 	clearData();
406 
407 	returnMesh->grab(); // store until this loader is destroyed
408 
409 	DummyMesh->drop();
410 	DummyMesh = 0;
411 
412 	if (FirstLoadedMesh)
413 		FirstLoadedMesh->drop();
414 	FirstLoadedMesh = 0;
415 	LoadedMeshCount = 0;
416 
417 	return returnMesh;
418 }
419 
420 
421 //! skips an (unknown) section in the collada document
skipSection(io::IXMLReaderUTF8 * reader,bool reportSkipping)422 void CColladaFileLoader::skipSection(io::IXMLReaderUTF8* reader, bool reportSkipping)
423 {
424 	#ifndef COLLADA_READER_DEBUG
425 	if (reportSkipping) // always report in COLLADA_READER_DEBUG mode
426 	#endif
427 		os::Printer::log("COLLADA skipping section", core::stringc(reader->getNodeName()).c_str(), ELL_DEBUG);
428 
429 	// skip if this element is empty anyway.
430 	if (reader->isEmptyElement())
431 		return;
432 
433 	// read until we've reached the last element in this section
434 	u32 tagCounter = 1;
435 
436 	while(tagCounter && reader->read())
437 	{
438 		if (reader->getNodeType() == io::EXN_ELEMENT &&
439 			!reader->isEmptyElement())
440 		{
441 			#ifdef COLLADA_READER_DEBUG
442 			if (reportSkipping)
443 				os::Printer::log("Skipping COLLADA unknown element", core::stringc(reader->getNodeName()).c_str(), ELL_DEBUG);
444 			#endif
445 
446 			++tagCounter;
447 		}
448 		else
449 		if (reader->getNodeType() == io::EXN_ELEMENT_END)
450 			--tagCounter;
451 	}
452 }
453 
454 
455 //! reads the <COLLADA> section and its content
readColladaSection(io::IXMLReaderUTF8 * reader)456 void CColladaFileLoader::readColladaSection(io::IXMLReaderUTF8* reader)
457 {
458 	if (reader->isEmptyElement())
459 		return;
460 
461 	// todo: patch level needs to be handled
462 	const f32 version = core::fast_atof(core::stringc(reader->getAttributeValue("version")).c_str());
463 	Version = core::floor32(version)*10000+core::round32(core::fract(version)*1000.0f);
464 	// Version 1.4 can be checked for by if (Version >= 10400)
465 
466 	while(reader->read())
467 	if (reader->getNodeType() == io::EXN_ELEMENT)
468 	{
469 		if (assetSectionName == reader->getNodeName())
470 			readAssetSection(reader);
471 		else
472 		if (librarySectionName == reader->getNodeName())
473 			readLibrarySection(reader);
474 		else
475 		if (libraryNodesSectionName == reader->getNodeName())
476 			readLibrarySection(reader);
477 		else
478 		if (libraryGeometriesSectionName == reader->getNodeName())
479 			readLibrarySection(reader);
480 		else
481 		if (libraryMaterialsSectionName == reader->getNodeName())
482 			readLibrarySection(reader);
483 		else
484 		if (libraryEffectsSectionName == reader->getNodeName())
485 			readLibrarySection(reader);
486 		else
487 		if (libraryImagesSectionName == reader->getNodeName())
488 			readLibrarySection(reader);
489 		else
490 		if (libraryCamerasSectionName == reader->getNodeName())
491 			readLibrarySection(reader);
492 		else
493 		if (libraryLightsSectionName == reader->getNodeName())
494 			readLibrarySection(reader);
495 		else
496 		if (libraryVisualScenesSectionName == reader->getNodeName())
497 			readVisualScene(reader);
498 		else
499 		if (assetSectionName == reader->getNodeName())
500 			readAssetSection(reader);
501 		else
502 		if (sceneSectionName == reader->getNodeName())
503 			readSceneSection(reader);
504 		else
505 		{
506 			os::Printer::log("COLLADA loader warning: Wrong tag usage found", reader->getNodeName(), ELL_WARNING);
507 			skipSection(reader, true); // unknown section
508 		}
509 	}
510 }
511 
512 
513 //! reads a <library> section and its content
readLibrarySection(io::IXMLReaderUTF8 * reader)514 void CColladaFileLoader::readLibrarySection(io::IXMLReaderUTF8* reader)
515 {
516 	#ifdef COLLADA_READER_DEBUG
517 	os::Printer::log("COLLADA reading library", ELL_DEBUG);
518 	#endif
519 
520 	if (reader->isEmptyElement())
521 		return;
522 
523 	while(reader->read())
524 	{
525 		if (reader->getNodeType() == io::EXN_ELEMENT)
526 		{
527 			// animation section tbd
528 			if (cameraPrefabName == reader->getNodeName())
529 				readCameraPrefab(reader);
530 			else
531 			// code section tbd
532 			// controller section tbd
533 			if (geometrySectionName == reader->getNodeName())
534 				readGeometry(reader);
535 			else
536 			if (imageSectionName == reader->getNodeName())
537 				readImage(reader);
538 			else
539 			if (lightPrefabName == reader->getNodeName())
540 				readLightPrefab(reader);
541 			else
542 			if (materialSectionName == reader->getNodeName())
543 				readMaterial(reader);
544 			else
545 			if (nodeSectionName == reader->getNodeName())
546 			{
547 				CScenePrefab p("");
548 
549 				readNodeSection(reader, SceneManager->getRootSceneNode(), &p);
550 			}
551 			else
552 			if (effectSectionName == reader->getNodeName())
553 				readEffect(reader);
554 			else
555 			// program section tbd
556 			if (textureSectionName == reader->getNodeName())
557 				readTexture(reader);
558 			else
559 				skipSection(reader, true); // unknown section, not all allowed supported yet
560 		}
561 		else
562 		if (reader->getNodeType() == io::EXN_ELEMENT_END)
563 		{
564 			if (librarySectionName == reader->getNodeName())
565 				break; // end reading.
566 			if (libraryNodesSectionName == reader->getNodeName())
567 				break; // end reading.
568 			if (libraryGeometriesSectionName == reader->getNodeName())
569 				break; // end reading.
570 			if (libraryMaterialsSectionName == reader->getNodeName())
571 				break; // end reading.
572 			if (libraryEffectsSectionName == reader->getNodeName())
573 				break; // end reading.
574 			if (libraryImagesSectionName == reader->getNodeName())
575 				break; // end reading.
576 			if (libraryLightsSectionName == reader->getNodeName())
577 				break; // end reading.
578 			if (libraryCamerasSectionName == reader->getNodeName())
579 				break; // end reading.
580 		}
581 	}
582 }
583 
584 
585 //! reads a <visual_scene> element and stores it as a prefab
readVisualScene(io::IXMLReaderUTF8 * reader)586 void CColladaFileLoader::readVisualScene(io::IXMLReaderUTF8* reader)
587 {
588 	CScenePrefab* p = 0;
589 	while(reader->read())
590 	{
591 		if (reader->getNodeType() == io::EXN_ELEMENT)
592 		{
593 			if (visualSceneSectionName == reader->getNodeName())
594 				p = new CScenePrefab(readId(reader));
595 			else
596 			if (p && nodeSectionName == reader->getNodeName()) // as a child of visual_scene
597 				readNodeSection(reader, SceneManager->getRootSceneNode(), p);
598 			else
599 			if (assetSectionName == reader->getNodeName())
600 				readAssetSection(reader);
601 			else
602 			if (extraNodeName == reader->getNodeName())
603 				skipSection(reader, false); // ignore all other sections
604 			else
605 			{
606 				os::Printer::log("COLLADA loader warning: Wrong tag usage found", reader->getNodeName(), ELL_WARNING);
607 				skipSection(reader, true); // ignore all other sections
608 			}
609 		}
610 		else
611 		if (reader->getNodeType() == io::EXN_ELEMENT_END)
612 		{
613 			if (libraryVisualScenesSectionName == reader->getNodeName())
614 				return;
615 			else
616 			if ((visualSceneSectionName == reader->getNodeName()) && p)
617 			{
618 				Prefabs.push_back(p);
619 				p = 0;
620 			}
621 		}
622 	}
623 }
624 
625 
626 //! reads a <scene> section and its content
readSceneSection(io::IXMLReaderUTF8 * reader)627 void CColladaFileLoader::readSceneSection(io::IXMLReaderUTF8* reader)
628 {
629 	#ifdef COLLADA_READER_DEBUG
630 	os::Printer::log("COLLADA reading scene", ELL_DEBUG);
631 	#endif
632 
633 	if (reader->isEmptyElement())
634 		return;
635 
636 	// read the scene
637 
638 	core::matrix4 transform; // transformation of this node
639 	core::aabbox3df bbox;
640 	scene::IDummyTransformationSceneNode* node = 0;
641 
642 	while(reader->read())
643 	{
644 		if (reader->getNodeType() == io::EXN_ELEMENT)
645 		{
646 			if (lookatNodeName == reader->getNodeName())
647 				transform *= readLookAtNode(reader);
648 			else
649 			if (matrixNodeName == reader->getNodeName())
650 				transform *= readMatrixNode(reader);
651 			else
652 			if (perspectiveNodeName == reader->getNodeName())
653 				transform *= readPerspectiveNode(reader);
654 			else
655 			if (rotateNodeName == reader->getNodeName())
656 				transform *= readRotateNode(reader);
657 			else
658 			if (scaleNodeName == reader->getNodeName())
659 				transform *= readScaleNode(reader);
660 			else
661 			if (skewNodeName == reader->getNodeName())
662 				transform *= readSkewNode(reader);
663 			else
664 			if (translateNodeName == reader->getNodeName())
665 				transform *= readTranslateNode(reader);
666 			else
667 			if (bboxNodeName == reader->getNodeName())
668 				readBboxNode(reader, bbox);
669 			else
670 			if (nodeSectionName == reader->getNodeName())
671 			{
672 				// create dummy node if there is none yet.
673 				if (!node)
674 					node = SceneManager->addDummyTransformationSceneNode(SceneManager->getRootSceneNode());
675 
676 				readNodeSection(reader, node);
677 			}
678 			else
679 			if ((instanceSceneName == reader->getNodeName()))
680 				readInstanceNode(reader, SceneManager->getRootSceneNode(), 0, 0,instanceSceneName);
681 			else
682 			if (extraNodeName == reader->getNodeName())
683 				skipSection(reader, false);
684 			else
685 			{
686 				os::Printer::log("COLLADA loader warning: Wrong tag usage found", reader->getNodeName(), ELL_WARNING);
687 				skipSection(reader, true); // ignore all other sections
688 			}
689 		}
690 		else
691 		if ((reader->getNodeType() == io::EXN_ELEMENT_END) &&
692 			(sceneSectionName == reader->getNodeName()))
693 				return;
694 	}
695 	if (node)
696 		node->getRelativeTransformationMatrix() = transform;
697 }
698 
699 
700 //! reads a <asset> section and its content
readAssetSection(io::IXMLReaderUTF8 * reader)701 void CColladaFileLoader::readAssetSection(io::IXMLReaderUTF8* reader)
702 {
703 	#ifdef COLLADA_READER_DEBUG
704 	os::Printer::log("COLLADA reading asset", ELL_DEBUG);
705 	#endif
706 
707 	if (reader->isEmptyElement())
708 		return;
709 
710 	while(reader->read())
711 	{
712 		if (reader->getNodeType() == io::EXN_ELEMENT)
713 		{
714 			if (upAxisNodeName == reader->getNodeName())
715 			{
716 					reader->read();
717 					FlipAxis = (core::stringc("Z_UP") == reader->getNodeData());
718 			}
719 		}
720 		else
721 		if ((reader->getNodeType() == io::EXN_ELEMENT_END) &&
722 			(assetSectionName == reader->getNodeName()))
723 				return;
724 	}
725 }
726 
727 
728 //! reads a <node> section and its content
readNodeSection(io::IXMLReaderUTF8 * reader,scene::ISceneNode * parent,CScenePrefab * p)729 void CColladaFileLoader::readNodeSection(io::IXMLReaderUTF8* reader, scene::ISceneNode* parent, CScenePrefab* p)
730 {
731 	if (reader->isEmptyElement())
732 	{
733 		return;
734 		#ifdef COLLADA_READER_DEBUG
735 		os::Printer::log("COLLADA reading empty node", ELL_DEBUG);
736 		#endif
737 	}
738 
739 	core::stringc name = readId(reader);
740 	#ifdef COLLADA_READER_DEBUG
741 	os::Printer::log("COLLADA reading node", name, ELL_DEBUG);
742 	#endif
743 
744 	core::matrix4 transform; // transformation of this node
745 	core::aabbox3df bbox;
746 	scene::ISceneNode* node = 0; // instance
747 	CScenePrefab* nodeprefab = 0; // prefab for library_nodes usage
748 
749 	if (p)
750 	{
751 		nodeprefab = new CScenePrefab(readId(reader));
752 		p->Children.push_back(nodeprefab);
753 		Prefabs.push_back(nodeprefab); // in order to delete them later on
754 	}
755 
756 	// read the node
757 
758 	while(reader->read())
759 	{
760 		if (reader->getNodeType() == io::EXN_ELEMENT)
761 		{
762 			if (assetSectionName == reader->getNodeName())
763 				readAssetSection(reader);
764 			else
765 			if (lookatNodeName == reader->getNodeName())
766 				transform *= readLookAtNode(reader);
767 			else
768 			if (matrixNodeName == reader->getNodeName())
769 				transform *= readMatrixNode(reader);
770 			else
771 			if (perspectiveNodeName == reader->getNodeName())
772 				transform *= readPerspectiveNode(reader);
773 			else
774 			if (rotateNodeName == reader->getNodeName())
775 				transform *= readRotateNode(reader);
776 			else
777 			if (scaleNodeName == reader->getNodeName())
778 				transform *= readScaleNode(reader);
779 			else
780 			if (skewNodeName == reader->getNodeName())
781 				transform *= readSkewNode(reader);
782 			else
783 			if (translateNodeName == reader->getNodeName())
784 				transform *= readTranslateNode(reader);
785 			else
786 			if (bboxNodeName == reader->getNodeName())
787 				readBboxNode(reader, bbox);
788 			else
789 			if ((instanceName == reader->getNodeName()) ||
790 				(instanceNodeName == reader->getNodeName()) ||
791 				(instanceGeometryName == reader->getNodeName()) ||
792 				(instanceLightName == reader->getNodeName()))
793 			{
794 				scene::ISceneNode* newnode = 0;
795 				readInstanceNode(reader, parent, &newnode, nodeprefab, reader->getNodeName());
796 
797 				if (node && newnode)
798 				{
799 					// move children from dummy to new node
800 					ISceneNodeList::ConstIterator it = node->getChildren().begin();
801 					for (; it != node->getChildren().end(); it = node->getChildren().begin())
802 						(*it)->setParent(newnode);
803 
804 					// remove previous dummy node
805 					node->remove();
806 					node = newnode;
807 				}
808 			}
809 			else
810 			if (nodeSectionName == reader->getNodeName())
811 			{
812 				// create dummy node if there is none yet.
813 				if (CreateInstances && !node)
814 				{
815 					scene::IDummyTransformationSceneNode* dummy =
816 						SceneManager->addDummyTransformationSceneNode(parent);
817 					dummy->getRelativeTransformationMatrix() = transform;
818 					node = dummy;
819 				}
820 				else
821 					node = parent;
822 
823 				// read and add child
824 				readNodeSection(reader, node, nodeprefab);
825 			}
826 			else
827 			if (extraNodeName == reader->getNodeName())
828 				skipSection(reader, false);
829 			else
830 				skipSection(reader, true); // ignore all other sections
831 
832 		} // end if node
833 		else
834 		if (reader->getNodeType() == io::EXN_ELEMENT_END)
835 		{
836 			if (nodeSectionName == reader->getNodeName())
837 				break;
838 		}
839 	}
840 
841 	if (nodeprefab)
842 		nodeprefab->Transformation = transform;
843 	else
844 	if (node)
845 	{
846 		// set transformation correctly into node.
847 		node->setPosition(transform.getTranslation());
848 		node->setRotation(transform.getRotationDegrees());
849 		node->setScale(transform.getScale());
850 		node->updateAbsolutePosition();
851 
852 		node->setName(name);
853 	}
854 }
855 
856 
857 //! reads a <lookat> element and its content and creates a matrix from it
readLookAtNode(io::IXMLReaderUTF8 * reader)858 core::matrix4 CColladaFileLoader::readLookAtNode(io::IXMLReaderUTF8* reader)
859 {
860 	core::matrix4 mat;
861 	if (reader->isEmptyElement())
862 		return mat;
863 
864 	#ifdef COLLADA_READER_DEBUG
865 	os::Printer::log("COLLADA reading look at node", ELL_DEBUG);
866 	#endif
867 
868 	f32 floats[9];
869 	readFloatsInsideElement(reader, floats, 9);
870 
871 	mat.buildCameraLookAtMatrixLH(
872 		core::vector3df(floats[0], floats[1], floats[2]),
873 		core::vector3df(floats[3], floats[4], floats[5]),
874 		core::vector3df(floats[6], floats[7], floats[8]));
875 
876 	return mat;
877 }
878 
879 
880 //! reads a <skew> element and its content and creates a matrix from it
readSkewNode(io::IXMLReaderUTF8 * reader)881 core::matrix4 CColladaFileLoader::readSkewNode(io::IXMLReaderUTF8* reader)
882 {
883 	#ifdef COLLADA_READER_DEBUG
884 	os::Printer::log("COLLADA reading skew node", ELL_DEBUG);
885 	#endif
886 
887 	core::matrix4 mat;
888 	if (reader->isEmptyElement())
889 		return mat;
890 
891 	f32 floats[7]; // angle rotation-axis translation-axis
892 	readFloatsInsideElement(reader, floats, 7);
893 
894 	// build skew matrix from these 7 floats
895 	core::quaternion q;
896 	q.fromAngleAxis(floats[0]*core::DEGTORAD, core::vector3df(floats[1], floats[2], floats[3]));
897 	mat = q.getMatrix();
898 
899 	if (floats[4]==1.f) // along x-axis
900 	{
901 		mat[4]=0.f;
902 		mat[6]=0.f;
903 		mat[8]=0.f;
904 		mat[9]=0.f;
905 	}
906 	else
907 	if (floats[5]==1.f) // along y-axis
908 	{
909 		mat[1]=0.f;
910 		mat[2]=0.f;
911 		mat[8]=0.f;
912 		mat[9]=0.f;
913 	}
914 	else
915 	if (floats[6]==1.f) // along z-axis
916 	{
917 		mat[1]=0.f;
918 		mat[2]=0.f;
919 		mat[4]=0.f;
920 		mat[6]=0.f;
921 	}
922 
923 	return mat;
924 }
925 
926 
927 //! reads a <boundingbox> element and its content and stores it in bbox
readBboxNode(io::IXMLReaderUTF8 * reader,core::aabbox3df & bbox)928 void CColladaFileLoader::readBboxNode(io::IXMLReaderUTF8* reader,
929 		core::aabbox3df& bbox)
930 {
931 	#ifdef COLLADA_READER_DEBUG
932 	os::Printer::log("COLLADA reading boundingbox node", ELL_DEBUG);
933 	#endif
934 
935 	bbox.reset(core::aabbox3df());
936 
937 	if (reader->isEmptyElement())
938 		return;
939 
940 	f32 floats[3];
941 
942 	while(reader->read())
943 	{
944 		if (reader->getNodeType() == io::EXN_ELEMENT)
945 		{
946 			if (minNodeName == reader->getNodeName())
947 			{
948 				readFloatsInsideElement(reader, floats, 3);
949 				bbox.MinEdge.set(floats[0], floats[1], floats[2]);
950 			}
951 			else
952 			if (maxNodeName == reader->getNodeName())
953 			{
954 				readFloatsInsideElement(reader, floats, 3);
955 				bbox.MaxEdge.set(floats[0], floats[1], floats[2]);
956 			}
957 			else
958 				skipSection(reader, true); // ignore all other sections
959 		}
960 		else
961 		if (reader->getNodeType() == io::EXN_ELEMENT_END)
962 		{
963 			if (bboxNodeName == reader->getNodeName())
964 				break;
965 		}
966 	}
967 }
968 
969 
970 //! reads a <matrix> element and its content and creates a matrix from it
readMatrixNode(io::IXMLReaderUTF8 * reader)971 core::matrix4 CColladaFileLoader::readMatrixNode(io::IXMLReaderUTF8* reader)
972 {
973 	#ifdef COLLADA_READER_DEBUG
974 	os::Printer::log("COLLADA reading matrix node", ELL_DEBUG);
975 	#endif
976 
977 	core::matrix4 mat;
978 	if (reader->isEmptyElement())
979 		return mat;
980 
981 	readFloatsInsideElement(reader, mat.pointer(), 16);
982 	// put translation into the correct place
983 	if (FlipAxis)
984 	{
985 		core::matrix4 mat2(mat, core::matrix4::EM4CONST_TRANSPOSED);
986 		mat2[1]=mat[8];
987 		mat2[2]=mat[4];
988 		mat2[4]=mat[2];
989 		mat2[5]=mat[10];
990 		mat2[6]=mat[6];
991 		mat2[8]=mat[1];
992 		mat2[9]=mat[9];
993 		mat2[10]=mat[5];
994 		mat2[12]=mat[3];
995 		mat2[13]=mat[11];
996 		mat2[14]=mat[7];
997 		return mat2;
998 	}
999 	else
1000 		return core::matrix4(mat, core::matrix4::EM4CONST_TRANSPOSED);
1001 }
1002 
1003 
1004 //! reads a <perspective> element and its content and creates a matrix from it
readPerspectiveNode(io::IXMLReaderUTF8 * reader)1005 core::matrix4 CColladaFileLoader::readPerspectiveNode(io::IXMLReaderUTF8* reader)
1006 {
1007 	#ifdef COLLADA_READER_DEBUG
1008 	os::Printer::log("COLLADA reading perspective node", ELL_DEBUG);
1009 	#endif
1010 
1011 	core::matrix4 mat;
1012 	if (reader->isEmptyElement())
1013 		return mat;
1014 
1015 	f32 floats[1];
1016 	readFloatsInsideElement(reader, floats, 1);
1017 
1018 	// TODO: build perspecitve matrix from this float
1019 
1020 	os::Printer::log("COLLADA loader warning: <perspective> not implemented yet.", ELL_WARNING);
1021 
1022 	return mat;
1023 }
1024 
1025 
1026 //! reads a <rotate> element and its content and creates a matrix from it
readRotateNode(io::IXMLReaderUTF8 * reader)1027 core::matrix4 CColladaFileLoader::readRotateNode(io::IXMLReaderUTF8* reader)
1028 {
1029 	#ifdef COLLADA_READER_DEBUG
1030 	os::Printer::log("COLLADA reading rotate node", ELL_DEBUG);
1031 	#endif
1032 
1033 	core::matrix4 mat;
1034 	if (reader->isEmptyElement())
1035 		return mat;
1036 
1037 	f32 floats[4];
1038 	readFloatsInsideElement(reader, floats, 4);
1039 
1040 	if (!core::iszero(floats[3]))
1041 	{
1042 		core::quaternion q;
1043 		if (FlipAxis)
1044 			q.fromAngleAxis(floats[3]*core::DEGTORAD, core::vector3df(floats[0], floats[2], floats[1]));
1045 		else
1046 			q.fromAngleAxis(floats[3]*core::DEGTORAD, core::vector3df(floats[0], floats[1], floats[2]));
1047 		return q.getMatrix();
1048 	}
1049 	else
1050 		return core::IdentityMatrix;
1051 }
1052 
1053 
1054 //! reads a <scale> element and its content and creates a matrix from it
readScaleNode(io::IXMLReaderUTF8 * reader)1055 core::matrix4 CColladaFileLoader::readScaleNode(io::IXMLReaderUTF8* reader)
1056 {
1057 	#ifdef COLLADA_READER_DEBUG
1058 	os::Printer::log("COLLADA reading scale node", ELL_DEBUG);
1059 	#endif
1060 
1061 	core::matrix4 mat;
1062 	if (reader->isEmptyElement())
1063 		return mat;
1064 
1065 	f32 floats[3];
1066 	readFloatsInsideElement(reader, floats, 3);
1067 
1068 	if (FlipAxis)
1069 		mat.setScale(core::vector3df(floats[0], floats[2], floats[1]));
1070 	else
1071 		mat.setScale(core::vector3df(floats[0], floats[1], floats[2]));
1072 
1073 	return mat;
1074 }
1075 
1076 
1077 //! reads a <translate> element and its content and creates a matrix from it
readTranslateNode(io::IXMLReaderUTF8 * reader)1078 core::matrix4 CColladaFileLoader::readTranslateNode(io::IXMLReaderUTF8* reader)
1079 {
1080 	#ifdef COLLADA_READER_DEBUG
1081 	os::Printer::log("COLLADA reading translate node", ELL_DEBUG);
1082 	#endif
1083 
1084 	core::matrix4 mat;
1085 	if (reader->isEmptyElement())
1086 		return mat;
1087 
1088 	f32 floats[3];
1089 	readFloatsInsideElement(reader, floats, 3);
1090 
1091 	if (FlipAxis)
1092 		mat.setTranslation(core::vector3df(floats[0], floats[2], floats[1]));
1093 	else
1094 		mat.setTranslation(core::vector3df(floats[0], floats[1], floats[2]));
1095 
1096 	return mat;
1097 }
1098 
1099 
1100 //! reads any kind of <instance*> node
readInstanceNode(io::IXMLReaderUTF8 * reader,scene::ISceneNode * parent,scene::ISceneNode ** outNode,CScenePrefab * p,const core::stringc & type)1101 void CColladaFileLoader::readInstanceNode(io::IXMLReaderUTF8* reader,
1102 		scene::ISceneNode* parent, scene::ISceneNode** outNode,
1103 		CScenePrefab* p, const core::stringc& type)
1104 {
1105 	// find prefab of the specified id
1106 	core::stringc url = reader->getAttributeValue("url");
1107 	uriToId(url);
1108 
1109 	#ifdef COLLADA_READER_DEBUG
1110 	os::Printer::log("COLLADA reading instance", url, ELL_DEBUG);
1111 	#endif
1112 
1113 	if (!reader->isEmptyElement())
1114 	{
1115 		while(reader->read())
1116 		{
1117 			if (reader->getNodeType() == io::EXN_ELEMENT)
1118 			{
1119 				if (bindMaterialName == reader->getNodeName())
1120 					readBindMaterialSection(reader,url);
1121 				else
1122 				if (extraNodeName == reader->getNodeName())
1123 					skipSection(reader, false);
1124 			}
1125 			else
1126 			if (reader->getNodeType() == io::EXN_ELEMENT_END)
1127 				break;
1128 		}
1129 	}
1130 	instantiateNode(parent, outNode, p, url, type);
1131 }
1132 
1133 
instantiateNode(scene::ISceneNode * parent,scene::ISceneNode ** outNode,CScenePrefab * p,const core::stringc & url,const core::stringc & type)1134 void CColladaFileLoader::instantiateNode(scene::ISceneNode* parent,
1135 		scene::ISceneNode** outNode, CScenePrefab* p, const core::stringc& url,
1136 		const core::stringc& type)
1137 {
1138 	#ifdef COLLADA_READER_DEBUG
1139 	os::Printer::log("COLLADA instantiate node", ELL_DEBUG);
1140 	#endif
1141 
1142 	for (u32 i=0; i<Prefabs.size(); ++i)
1143 	{
1144 		if (url == "" || url == Prefabs[i]->getId())
1145 		{
1146 			if (p)
1147 				p->Children.push_back(Prefabs[i]);
1148 			else
1149 			if (CreateInstances)
1150 			{
1151 				scene::ISceneNode * newNode
1152 					= Prefabs[i]->addInstance(parent, SceneManager);
1153 				if (outNode)
1154 				{
1155 					*outNode = newNode;
1156 					if (*outNode)
1157 						(*outNode)->setName(url);
1158 				}
1159 			}
1160 			return;
1161 		}
1162 	}
1163 	if (p)
1164 	{
1165 		if (instanceGeometryName==type)
1166 		{
1167 			Prefabs.push_back(new CGeometryPrefab(url));
1168 			p->Children.push_back(Prefabs.getLast());
1169 		}
1170 	}
1171 }
1172 
1173 
1174 //! reads a <camera> element and stores it as prefab
readCameraPrefab(io::IXMLReaderUTF8 * reader)1175 void CColladaFileLoader::readCameraPrefab(io::IXMLReaderUTF8* reader)
1176 {
1177 	#ifdef COLLADA_READER_DEBUG
1178 	os::Printer::log("COLLADA reading camera prefab", ELL_DEBUG);
1179 	#endif
1180 
1181 	CCameraPrefab* prefab = new CCameraPrefab(readId(reader));
1182 
1183 	if (!reader->isEmptyElement())
1184 	{
1185 		// read techniques optics and imager (the latter is completely ignored, though)
1186 		readColladaParameters(reader, cameraPrefabName);
1187 
1188 		SColladaParam* p;
1189 
1190 		// XFOV not yet supported
1191 		p = getColladaParameter(ECPN_YFOV);
1192 		if (p && p->Type == ECPT_FLOAT)
1193 			prefab->YFov = p->Floats[0];
1194 
1195 		p = getColladaParameter(ECPN_ZNEAR);
1196 		if (p && p->Type == ECPT_FLOAT)
1197 			prefab->ZNear = p->Floats[0];
1198 
1199 		p = getColladaParameter(ECPN_ZFAR);
1200 		if (p && p->Type == ECPT_FLOAT)
1201 			prefab->ZFar = p->Floats[0];
1202 		// orthographic camera uses LEFT, RIGHT, TOP, and BOTTOM
1203 	}
1204 
1205 	Prefabs.push_back(prefab);
1206 }
1207 
1208 
1209 //! reads a <image> element and stores it in the image section
readImage(io::IXMLReaderUTF8 * reader)1210 void CColladaFileLoader::readImage(io::IXMLReaderUTF8* reader)
1211 {
1212 	// add image to list of loaded images.
1213 	Images.push_back(SColladaImage());
1214 	SColladaImage& image=Images.getLast();
1215 
1216 	image.Id = readId(reader);
1217 	#ifdef COLLADA_READER_DEBUG
1218 	os::Printer::log("COLLADA reading image", core::stringc(image.Id), ELL_DEBUG);
1219 	#endif
1220 	image.Dimension.Height = (u32)reader->getAttributeValueAsInt("height");
1221 	image.Dimension.Width = (u32)reader->getAttributeValueAsInt("width");
1222 
1223 	if (Version >= 10400) // start with 1.4
1224 	{
1225 		while(reader->read())
1226 		{
1227 			if (reader->getNodeType() == io::EXN_ELEMENT)
1228 			{
1229 				if (assetSectionName == reader->getNodeName())
1230 					skipSection(reader, false);
1231 				else
1232 				if (initFromName == reader->getNodeName())
1233 				{
1234 					reader->read();
1235 					image.Source = reader->getNodeData();
1236 					image.Source.trim();
1237 					image.SourceIsFilename=true;
1238 				}
1239 				else
1240 				if (dataName == reader->getNodeName())
1241 				{
1242 					reader->read();
1243 					image.Source = reader->getNodeData();
1244 					image.Source.trim();
1245 					image.SourceIsFilename=false;
1246 				}
1247 				else
1248 				if (extraNodeName == reader->getNodeName())
1249 					skipSection(reader, false);
1250 			}
1251 			else
1252 			if (reader->getNodeType() == io::EXN_ELEMENT_END)
1253 			{
1254 				if (initFromName == reader->getNodeName())
1255 					return;
1256 			}
1257 		}
1258 	}
1259 	else
1260 	{
1261 		image.Source = reader->getAttributeValue("source");
1262 		image.Source.trim();
1263 		image.SourceIsFilename=false;
1264 	}
1265 }
1266 
1267 
1268 //! reads a <texture> element and stores it in the texture section
readTexture(io::IXMLReaderUTF8 * reader)1269 void CColladaFileLoader::readTexture(io::IXMLReaderUTF8* reader)
1270 {
1271 	// add texture to list of loaded textures.
1272 	Textures.push_back(SColladaTexture());
1273 	SColladaTexture& texture=Textures.getLast();
1274 
1275 	texture.Id = readId(reader);
1276 	#ifdef COLLADA_READER_DEBUG
1277 	os::Printer::log("COLLADA reading texture", core::stringc(texture.Id), ELL_DEBUG);
1278 	#endif
1279 
1280 	if (!reader->isEmptyElement())
1281 	{
1282 		readColladaInputs(reader, textureSectionName);
1283 		SColladaInput* input = getColladaInput(ECIS_IMAGE);
1284 		if (input)
1285 		{
1286 			const core::stringc imageName = input->Source;
1287 			texture.Texture = getTextureFromImage(imageName, NULL);
1288 		}
1289 	}
1290 }
1291 
1292 
1293 //! reads a <material> element and stores it in the material section
readMaterial(io::IXMLReaderUTF8 * reader)1294 void CColladaFileLoader::readMaterial(io::IXMLReaderUTF8* reader)
1295 {
1296 	// add material to list of loaded materials.
1297 	Materials.push_back(SColladaMaterial());
1298 
1299 	SColladaMaterial& material = Materials.getLast();
1300 	material.Id = readId(reader);
1301 	#ifdef COLLADA_READER_DEBUG
1302 	os::Printer::log("COLLADA reading material", core::stringc(material.Id), ELL_DEBUG);
1303 	#endif
1304 
1305 	if (Version >= 10400)
1306 	{
1307 		while(reader->read())
1308 		{
1309 			if (reader->getNodeType() == io::EXN_ELEMENT &&
1310 				instanceEffectName == reader->getNodeName())
1311 			{
1312 				material.InstanceEffectId = reader->getAttributeValue("url");
1313 				uriToId(material.InstanceEffectId);
1314 			}
1315 			else
1316 			if (reader->getNodeType() == io::EXN_ELEMENT_END &&
1317 				materialSectionName == reader->getNodeName())
1318 			{
1319 				break;
1320 			}
1321 		} // end while reader->read();
1322 	}
1323 	else
1324 	{
1325 		if (!reader->isEmptyElement())
1326 		{
1327 			readColladaInputs(reader, materialSectionName);
1328 			SColladaInput* input = getColladaInput(ECIS_TEXTURE);
1329 			if (input)
1330 			{
1331 				core::stringc textureName = input->Source;
1332 				uriToId(textureName);
1333 				for (u32 i=0; i<Textures.size(); ++i)
1334 					if (textureName == Textures[i].Id)
1335 					{
1336 						material.Mat.setTexture(0, Textures[i].Texture);
1337 						break;
1338 					}
1339 			}
1340 
1341 			//does not work because the wrong start node is chosen due to reading of inputs before
1342 #if 0
1343 			readColladaParameters(reader, materialSectionName);
1344 
1345 			SColladaParam* p;
1346 
1347 			p = getColladaParameter(ECPN_AMBIENT);
1348 			if (p && p->Type == ECPT_FLOAT3)
1349 				material.Mat.AmbientColor = video::SColorf(p->Floats[0],p->Floats[1],p->Floats[2]).toSColor();
1350 			p = getColladaParameter(ECPN_DIFFUSE);
1351 			if (p && p->Type == ECPT_FLOAT3)
1352 				material.Mat.DiffuseColor = video::SColorf(p->Floats[0],p->Floats[1],p->Floats[2]).toSColor();
1353 			p = getColladaParameter(ECPN_SPECULAR);
1354 			if (p && p->Type == ECPT_FLOAT3)
1355 				material.Mat.DiffuseColor = video::SColorf(p->Floats[0],p->Floats[1],p->Floats[2]).toSColor();
1356 			p = getColladaParameter(ECPN_SHININESS);
1357 			if (p && p->Type == ECPT_FLOAT)
1358 				material.Mat.Shininess = p->Floats[0];
1359 #endif
1360 		}
1361 	}
1362 }
1363 
readEffect(io::IXMLReaderUTF8 * reader,SColladaEffect * effect)1364 void CColladaFileLoader::readEffect(io::IXMLReaderUTF8* reader, SColladaEffect * effect)
1365 {
1366 	static const core::stringc constantNode("constant");
1367 	static const core::stringc lambertNode("lambert");
1368 	static const core::stringc phongNode("phong");
1369 	static const core::stringc blinnNode("blinn");
1370 	static const core::stringc emissionNode("emission");
1371 	static const core::stringc ambientNode("ambient");
1372 	static const core::stringc diffuseNode("diffuse");
1373 	static const core::stringc specularNode("specular");
1374 	static const core::stringc shininessNode("shininess");
1375 	static const core::stringc reflectiveNode("reflective");
1376 	static const core::stringc reflectivityNode("reflectivity");
1377 	static const core::stringc transparentNode("transparent");
1378 	static const core::stringc transparencyNode("transparency");
1379 	static const core::stringc indexOfRefractionNode("index_of_refraction");
1380 
1381 	if (!effect)
1382 	{
1383 		Effects.push_back(SColladaEffect());
1384 		effect = &Effects.getLast();
1385 		effect->Parameters = new io::CAttributes();
1386 		effect->Id = readId(reader);
1387 		effect->Transparency = 1.f;
1388 		effect->Mat.Lighting=true;
1389 		effect->Mat.NormalizeNormals=true;
1390 		#ifdef COLLADA_READER_DEBUG
1391 		os::Printer::log("COLLADA reading effect", core::stringc(effect->Id), ELL_DEBUG);
1392 		#endif
1393 	}
1394 	while(reader->read())
1395 	{
1396 		if (reader->getNodeType() == io::EXN_ELEMENT)
1397 		{
1398 			// first come the tags we descend, but ignore the top-levels
1399 			if (!reader->isEmptyElement() && ((profileCOMMONSectionName == reader->getNodeName()) ||
1400 				(techniqueNodeName == reader->getNodeName())))
1401 				readEffect(reader,effect);
1402 			else
1403 			if (newParamName == reader->getNodeName())
1404 				readParameter(reader, effect->Parameters);
1405 			else
1406 			// these are the actual materials inside technique
1407 			if (constantNode == reader->getNodeName() ||
1408 				lambertNode == reader->getNodeName() ||
1409 				phongNode == reader->getNodeName() ||
1410 				blinnNode == reader->getNodeName())
1411 			{
1412 				#ifdef COLLADA_READER_DEBUG
1413 				os::Printer::log("COLLADA reading effect part", reader->getNodeName(), ELL_DEBUG);
1414 				#endif
1415 				effect->Mat.setFlag(irr::video::EMF_GOURAUD_SHADING,
1416 					phongNode == reader->getNodeName() ||
1417 					blinnNode == reader->getNodeName());
1418 				while(reader->read())
1419 				{
1420 					if (reader->getNodeType() == io::EXN_ELEMENT)
1421 					{
1422 						const core::stringc node = reader->getNodeName();
1423 						if (emissionNode == node || ambientNode == node ||
1424 							diffuseNode == node || specularNode == node ||
1425 							reflectiveNode == node || transparentNode == node )
1426 						{
1427 							// color or texture types
1428 							while(reader->read())
1429 							{
1430 								if (reader->getNodeType() == io::EXN_ELEMENT &&
1431 									colorNodeName == reader->getNodeName())
1432 								{
1433 									const video::SColorf colorf = readColorNode(reader);
1434 									const video::SColor color = colorf.toSColor();
1435 									if (emissionNode == node)
1436 										effect->Mat.EmissiveColor = color;
1437 									else
1438 									if (ambientNode == node)
1439 										effect->Mat.AmbientColor = color;
1440 									else
1441 									if (diffuseNode == node)
1442 										effect->Mat.DiffuseColor = color;
1443 									else
1444 									if (specularNode == node)
1445 										effect->Mat.SpecularColor = color;
1446 									else
1447 									if (transparentNode == node)
1448 										effect->Transparency = colorf.getAlpha();
1449 								}
1450 								else
1451 								if (reader->getNodeType() == io::EXN_ELEMENT &&
1452 									textureNodeName == reader->getNodeName())
1453 								{
1454 									effect->Textures.push_back(reader->getAttributeValue("texture"));
1455 									break;
1456 								}
1457 								else
1458 								if (reader->getNodeType() == io::EXN_ELEMENT)
1459 									skipSection(reader, false);
1460 								else
1461 								if (reader->getNodeType() == io::EXN_ELEMENT_END &&
1462 									node == reader->getNodeName())
1463 									break;
1464 							}
1465 						}
1466 						else
1467 						if (shininessNode == node || reflectivityNode == node ||
1468 							transparencyNode == node || indexOfRefractionNode == node )
1469 						{
1470 							// float or param types
1471 							while(reader->read())
1472 							{
1473 								if (reader->getNodeType() == io::EXN_ELEMENT &&
1474 									floatNodeName == reader->getNodeName())
1475 								{
1476 									f32 f = readFloatNode(reader);
1477 									if (shininessNode == node)
1478 										effect->Mat.Shininess = f;
1479 									else
1480 									if (transparencyNode == node)
1481 										effect->Transparency *= f;
1482 								}
1483 								else
1484 								if (reader->getNodeType() == io::EXN_ELEMENT)
1485 									skipSection(reader, false);
1486 								else
1487 								if (reader->getNodeType() == io::EXN_ELEMENT_END &&
1488 									node == reader->getNodeName())
1489 									break;
1490 							}
1491 						}
1492 						else
1493 							skipSection(reader, true); // ignore all other nodes
1494 					}
1495 					else
1496 					if (reader->getNodeType() == io::EXN_ELEMENT_END && (
1497 						constantNode == reader->getNodeName() ||
1498 						lambertNode == reader->getNodeName() ||
1499 						phongNode == reader->getNodeName() ||
1500 						blinnNode == reader->getNodeName()
1501 						))
1502 						break;
1503 				}
1504 			}
1505 			else
1506 			if (!reader->isEmptyElement() && (extraNodeName == reader->getNodeName()))
1507 				readEffect(reader,effect);
1508 			else
1509 			if (doubleSidedNodeName == reader->getNodeName())
1510 			{
1511 				// read the GoogleEarth extra flag for double sided polys
1512 				s32 doubleSided = 0;
1513 				readIntsInsideElement(reader,&doubleSided,1);
1514 				if (doubleSided)
1515 				{
1516 					#ifdef COLLADA_READER_DEBUG
1517 					os::Printer::log("Setting double sided flag for effect.", ELL_DEBUG);
1518 					#endif
1519 
1520 					effect->Mat.setFlag(irr::video::EMF_BACK_FACE_CULLING,false);
1521 				}
1522 			}
1523 			else
1524 				skipSection(reader, true); // ignore all other sections
1525 		}
1526 		else
1527 		if (reader->getNodeType() == io::EXN_ELEMENT_END)
1528 		{
1529 			if (effectSectionName == reader->getNodeName())
1530 				break;
1531 			else
1532 			if (profileCOMMONSectionName == reader->getNodeName())
1533 				break;
1534 			else
1535 			if (techniqueNodeName == reader->getNodeName())
1536 				break;
1537 			else
1538 			if (extraNodeName == reader->getNodeName())
1539 				break;
1540 		}
1541 	}
1542 
1543 	if (effect->Mat.AmbientColor == video::SColor(0) &&
1544 		effect->Mat.DiffuseColor != video::SColor(0))
1545 		effect->Mat.AmbientColor = effect->Mat.DiffuseColor;
1546 	if (effect->Mat.DiffuseColor == video::SColor(0) &&
1547 		effect->Mat.AmbientColor != video::SColor(0))
1548 		effect->Mat.DiffuseColor = effect->Mat.AmbientColor;
1549 	if ((effect->Transparency != 0.0f) && (effect->Transparency != 1.0f))
1550 	{
1551 		effect->Mat.MaterialType = irr::video::EMT_TRANSPARENT_VERTEX_ALPHA;
1552 		effect->Mat.ZWriteEnable = false;
1553 	}
1554 
1555 	video::E_TEXTURE_CLAMP twu = video::ETC_REPEAT;
1556 	s32 idx = effect->Parameters->findAttribute(wrapsName.c_str());
1557 	if ( idx >= 0 )
1558 		twu = (video::E_TEXTURE_CLAMP)(effect->Parameters->getAttributeAsInt(idx));
1559 	video::E_TEXTURE_CLAMP twv = video::ETC_REPEAT;
1560 	idx = effect->Parameters->findAttribute(wraptName.c_str());
1561 	if ( idx >= 0 )
1562 		twv = (video::E_TEXTURE_CLAMP)(effect->Parameters->getAttributeAsInt(idx));
1563 
1564 	for (u32 i=0; i<video::MATERIAL_MAX_TEXTURES; ++i)
1565 	{
1566 		effect->Mat.TextureLayer[i].TextureWrapU = twu;
1567 		effect->Mat.TextureLayer[i].TextureWrapV = twv;
1568 	}
1569 
1570 	effect->Mat.setFlag(video::EMF_BILINEAR_FILTER, effect->Parameters->getAttributeAsBool("bilinear"));
1571 	effect->Mat.setFlag(video::EMF_TRILINEAR_FILTER, effect->Parameters->getAttributeAsBool("trilinear"));
1572 	effect->Mat.setFlag(video::EMF_ANISOTROPIC_FILTER, effect->Parameters->getAttributeAsBool("anisotropic"));
1573 }
1574 
1575 
findMaterial(const core::stringc & materialName)1576 const SColladaMaterial* CColladaFileLoader::findMaterial(const core::stringc& materialName)
1577 {
1578 	#ifdef COLLADA_READER_DEBUG
1579 	os::Printer::log("COLLADA find material", materialName, ELL_DEBUG);
1580 	#endif
1581 
1582 	// do a quick lookup in the materials
1583 	SColladaMaterial matToFind;
1584 	matToFind.Id = materialName;
1585 	s32 mat = Materials.binary_search(matToFind);
1586 	if (mat == -1)
1587 		return 0;
1588 	// instantiate the material effect if needed
1589 	if (Materials[mat].InstanceEffectId.size() != 0)
1590 	{
1591 		// do a quick lookup in the effects
1592 		SColladaEffect effectToFind;
1593 		effectToFind.Id = Materials[mat].InstanceEffectId;
1594 		s32 effect = Effects.binary_search(effectToFind);
1595 		if (effect != -1)
1596 		{
1597 			// found the effect, instantiate by copying into the material
1598 			Materials[mat].Mat = Effects[effect].Mat;
1599 			if (Effects[effect].Textures.size())
1600 				Materials[mat].Mat.setTexture(0, getTextureFromImage(Effects[effect].Textures[0], &(Effects[effect])));
1601 			Materials[mat].Transparency = Effects[effect].Transparency;
1602 			// and indicate the material is instantiated by removing the effect ref
1603 			Materials[mat].InstanceEffectId = "";
1604 		}
1605 		else
1606 			return 0;
1607 	}
1608 	return &Materials[mat];
1609 }
1610 
1611 
readBindMaterialSection(io::IXMLReaderUTF8 * reader,const core::stringc & id)1612 void CColladaFileLoader::readBindMaterialSection(io::IXMLReaderUTF8* reader, const core::stringc & id)
1613 {
1614 	#ifdef COLLADA_READER_DEBUG
1615 	os::Printer::log("COLLADA reading bind material", ELL_DEBUG);
1616 	#endif
1617 
1618 	while(reader->read())
1619 	{
1620 		if (reader->getNodeType() == io::EXN_ELEMENT)
1621 		{
1622 			if (instanceMaterialName == reader->getNodeName())
1623 			{
1624 				// the symbol to retarget, and the target material
1625 				core::stringc meshbufferReference = reader->getAttributeValue("symbol");
1626 				if (meshbufferReference.size()==0)
1627 					continue;
1628 				core::stringc target = reader->getAttributeValue("target");
1629 				uriToId(target);
1630 				if (target.size()==0)
1631 					continue;
1632 				const SColladaMaterial * material = findMaterial(target);
1633 				if (!material)
1634 					continue;
1635 				// bind any pending materials for this node
1636 				meshbufferReference = id+"/"+meshbufferReference;
1637 #ifdef COLLADA_READER_DEBUG
1638 				os::Printer::log((core::stringc("Material binding: ")+meshbufferReference+" "+target).c_str(), ELL_DEBUG);
1639 #endif
1640 				if (MaterialsToBind.find(meshbufferReference))
1641 				{
1642 					core::array<irr::scene::IMeshBuffer*> & toBind
1643 						= MeshesToBind[MaterialsToBind[meshbufferReference]];
1644 #ifdef COLLADA_READER_DEBUG
1645 				os::Printer::log("Material binding now ",material->Id.c_str(), ELL_DEBUG);
1646 				os::Printer::log("#meshbuffers",core::stringc(toBind.size()).c_str(), ELL_DEBUG);
1647 #endif
1648 					SMesh tmpmesh;
1649 					for (u32 i = 0; i < toBind.size(); ++i)
1650 					{
1651 						toBind[i]->getMaterial() = material->Mat;
1652 						tmpmesh.addMeshBuffer(toBind[i]);
1653 
1654 						if ((material->Transparency!=0.0f) && (material->Transparency!=1.0f))
1655 						{
1656 							toBind[i]->getMaterial().MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
1657 							toBind[i]->getMaterial().ZWriteEnable = false;
1658 						}
1659 					}
1660 					SceneManager->getMeshManipulator()->setVertexColors(&tmpmesh,material->Mat.DiffuseColor);
1661 					if ((material->Transparency!=0.0f) && (material->Transparency!=1.0f))
1662 					{
1663 						#ifdef COLLADA_READER_DEBUG
1664 						os::Printer::log("COLLADA found transparency material", core::stringc(material->Transparency).c_str(), ELL_DEBUG);
1665 						#endif
1666 						SceneManager->getMeshManipulator()->setVertexColorAlpha(&tmpmesh, core::floor32(material->Transparency*255.0f));
1667 					}
1668 				}
1669 			}
1670 		}
1671 		else
1672 		if (reader->getNodeType() == io::EXN_ELEMENT_END &&
1673 			bindMaterialName == reader->getNodeName())
1674 			break;
1675 	}
1676 }
1677 
1678 
1679 //! reads a <geometry> element and stores it as mesh if possible
readGeometry(io::IXMLReaderUTF8 * reader)1680 void CColladaFileLoader::readGeometry(io::IXMLReaderUTF8* reader)
1681 {
1682 	core::stringc id = readId(reader);
1683 	#ifdef COLLADA_READER_DEBUG
1684 	os::Printer::log("COLLADA reading geometry", id, ELL_DEBUG);
1685 	#endif
1686 
1687 	SAnimatedMesh* amesh = new SAnimatedMesh();
1688 	scene::SMesh* mesh = new SMesh();
1689 	amesh->addMesh(mesh);
1690 	core::array<SSource> sources;
1691 	bool okToReadArray = false;
1692 
1693 	// handles geometry node and the mesh children in this loop
1694 	// read sources with arrays and accessor for each mesh
1695 	if (!reader->isEmptyElement())
1696 	while(reader->read())
1697 	{
1698 		if (reader->getNodeType() == io::EXN_ELEMENT)
1699 		{
1700 			const char* nodeName = reader->getNodeName();
1701 			if (meshSectionName == nodeName)
1702 			{
1703 				// inside a mesh section. Don't have to do anything here.
1704 			}
1705 			else
1706 			if (sourceSectionName == nodeName)
1707 			{
1708 				// create a new source
1709 				sources.push_back(SSource());
1710 				sources.getLast().Id = readId(reader);
1711 
1712 				#ifdef COLLADA_READER_DEBUG
1713 				os::Printer::log("Reading source", sources.getLast().Id.c_str(), ELL_DEBUG);
1714 				#endif
1715 			}
1716 			else
1717 			if (arraySectionName == nodeName || floatArraySectionName == nodeName || intArraySectionName == nodeName)
1718 			{
1719 				// create a new array and read it.
1720 				if (!sources.empty())
1721 				{
1722 					sources.getLast().Array.Name = readId(reader);
1723 
1724 					int count = reader->getAttributeValueAsInt("count");
1725 					sources.getLast().Array.Data.set_used(count); // pre allocate
1726 
1727 					// check if type of array is ok
1728 					const char* type = reader->getAttributeValue("type");
1729 					okToReadArray = (type && (!strcmp("float", type) || !strcmp("int", type))) || floatArraySectionName == nodeName || intArraySectionName == nodeName;
1730 
1731 					#ifdef COLLADA_READER_DEBUG
1732 					os::Printer::log("Read array", sources.getLast().Array.Name.c_str(), ELL_DEBUG);
1733 					#endif
1734 				}
1735 				#ifdef COLLADA_READER_DEBUG
1736 				else
1737 					os::Printer::log("Warning, array outside source found",
1738 						readId(reader).c_str(), ELL_DEBUG);
1739 				#endif
1740 
1741 			}
1742 			else
1743 			if (accessorSectionName == nodeName) // child of source (below a technique tag)
1744 			{
1745 				#ifdef COLLADA_READER_DEBUG
1746 				os::Printer::log("Reading accessor", ELL_DEBUG);
1747 				#endif
1748 				SAccessor accessor;
1749 				accessor.Count = reader->getAttributeValueAsInt("count");
1750 				accessor.Offset = reader->getAttributeValueAsInt("offset");
1751 				accessor.Stride = reader->getAttributeValueAsInt("stride");
1752 				if (accessor.Stride == 0)
1753 					accessor.Stride = 1;
1754 
1755 				// the accessor contains some information on how to access (boi!) the array,
1756 				// the info is stored in collada style parameters, so just read them.
1757 				readColladaParameters(reader, accessorSectionName);
1758 				if (!sources.empty())
1759 				{
1760 					sources.getLast().Accessors.push_back(accessor);
1761 					sources.getLast().Accessors.getLast().Parameters = ColladaParameters;
1762 				}
1763 			}
1764 			else
1765 			if (verticesSectionName == nodeName)
1766 			{
1767 				#ifdef COLLADA_READER_DEBUG
1768 				os::Printer::log("Reading vertices", ELL_DEBUG);
1769 				#endif
1770 				// read vertex input position source
1771 				readColladaInputs(reader, verticesSectionName);
1772 			}
1773 			else
1774 			// lines and linestrips missing
1775 			if (polygonsSectionName == nodeName ||
1776 				polylistSectionName == nodeName ||
1777 				trianglesSectionName == nodeName)
1778 			{
1779 				// read polygons section
1780 				readPolygonSection(reader, sources, mesh, id);
1781 			}
1782 			else
1783 			// trifans, and tristrips missing
1784 			if (doubleSidedNodeName == reader->getNodeName())
1785 			{
1786 				// read the extra flag for double sided polys
1787 				s32 doubleSided = 0;
1788 				readIntsInsideElement(reader,&doubleSided,1);
1789 				if (doubleSided)
1790 				{
1791 					#ifdef COLLADA_READER_DEBUG
1792 					os::Printer::log("Setting double sided flag for mesh.", ELL_DEBUG);
1793 					#endif
1794 					amesh->setMaterialFlag(irr::video::EMF_BACK_FACE_CULLING,false);
1795 				}
1796 			}
1797 			else
1798 			 // techniqueCommon or 'technique profile=common' must not be skipped
1799 			if ((techniqueCommonSectionName != nodeName) // Collada 1.2/1.3
1800 				&& (techniqueNodeName != nodeName) // Collada 1.4+
1801 				&& (extraNodeName != nodeName))
1802 			{
1803 				os::Printer::log("COLLADA loader warning: Wrong tag usage found in geometry", reader->getNodeName(), ELL_WARNING);
1804 				skipSection(reader, true); // ignore all other sections
1805 			}
1806 		} // end if node type is element
1807 		else
1808 		if (reader->getNodeType() == io::EXN_TEXT)
1809 		{
1810 			// read array data
1811 			if (okToReadArray && !sources.empty())
1812 			{
1813 				core::array<f32>& a = sources.getLast().Array.Data;
1814 				core::stringc data = reader->getNodeData();
1815 				data.trim();
1816 				const c8* p = &data[0];
1817 
1818 				for (u32 i=0; i<a.size(); ++i)
1819 				{
1820 					findNextNoneWhiteSpace(&p);
1821 					if (*p)
1822 						a[i] = readFloat(&p);
1823 					else
1824 						a[i] = 0.0f;
1825 				}
1826 			} // end reading array
1827 
1828 			okToReadArray = false;
1829 
1830 		} // end if node type is text
1831 		else
1832 		if (reader->getNodeType() == io::EXN_ELEMENT_END)
1833 		{
1834 			if (geometrySectionName == reader->getNodeName())
1835 			{
1836 				// end of geometry section reached, cancel out
1837 				break;
1838 			}
1839 		}
1840 	} // end while reader->read();
1841 
1842 	// add mesh as geometry
1843 
1844 	mesh->recalculateBoundingBox();
1845 	amesh->recalculateBoundingBox();
1846 
1847 	// create virtual file name
1848 	io::path filename = CurrentlyLoadingMesh;
1849 	filename += '#';
1850 	filename += id;
1851 
1852 	// add to scene manager
1853 	if (LoadedMeshCount)
1854 	{
1855 		SceneManager->getMeshCache()->addMesh(filename.c_str(), amesh);
1856 		os::Printer::log("Added COLLADA mesh", filename.c_str(), ELL_DEBUG);
1857 	}
1858 	else
1859 	{
1860 		FirstLoadedMeshName = filename;
1861 		FirstLoadedMesh = amesh;
1862 		FirstLoadedMesh->grab();
1863 	}
1864 
1865 	++LoadedMeshCount;
1866 	mesh->drop();
1867 	amesh->drop();
1868 
1869 	// create geometry prefab
1870 	u32 i;
1871 	for (i=0; i<Prefabs.size(); ++i)
1872 	{
1873 		if (Prefabs[i]->getId()==id)
1874 		{
1875 			((CGeometryPrefab*)Prefabs[i])->Mesh=mesh;
1876 			break;
1877 		}
1878 	}
1879 	if (i==Prefabs.size())
1880 	{
1881 		CGeometryPrefab* prefab = new CGeometryPrefab(id);
1882 		prefab->Mesh = mesh;
1883 		Prefabs.push_back(prefab);
1884 	}
1885 
1886 	// store as dummy mesh if no instances will be created
1887 	if (!CreateInstances && !DummyMesh)
1888 	{
1889 		DummyMesh = amesh;
1890 		DummyMesh->grab();
1891 	}
1892 }
1893 
1894 
1895 struct SPolygon
1896 {
1897 	core::array<s32> Indices;
1898 };
1899 
1900 //! reads a polygons section and creates a mesh from it
readPolygonSection(io::IXMLReaderUTF8 * reader,core::array<SSource> & sources,scene::SMesh * mesh,const core::stringc & geometryId)1901 void CColladaFileLoader::readPolygonSection(io::IXMLReaderUTF8* reader,
1902 		core::array<SSource>& sources, scene::SMesh* mesh,
1903 		const core::stringc& geometryId)
1904 {
1905 	#ifdef COLLADA_READER_DEBUG
1906 	os::Printer::log("COLLADA reading polygon section", ELL_DEBUG);
1907 	#endif
1908 
1909 	core::stringc materialName = reader->getAttributeValue("material");
1910 
1911 	core::stringc polygonType = reader->getNodeName();
1912 	const int polygonCount = reader->getAttributeValueAsInt("count"); // Not useful because it only determines the number of primitives, which have arbitrary vertices in case of polygon
1913 	core::array<SPolygon> polygons;
1914 	if (polygonType == polygonsSectionName)
1915 		polygons.reallocate(polygonCount);
1916 	core::array<int> vCounts;
1917 	bool parsePolygonOK = false;
1918 	bool parseVcountOK = false;
1919 	u32 inputSemanticCount = 0;
1920 	bool unresolvedInput=false;
1921 	u32 maxOffset = 0;
1922 	core::array<SColladaInput> localInputs;
1923 
1924 	// read all <input> and primitives
1925 	if (!reader->isEmptyElement())
1926 	while(reader->read())
1927 	{
1928 		const char* nodeName = reader->getNodeName();
1929 
1930 		if (reader->getNodeType() == io::EXN_ELEMENT)
1931 		{
1932 			// polygon node may contain params
1933 			if (inputTagName == nodeName)
1934 			{
1935 				// read input tag
1936 				readColladaInput(reader, localInputs);
1937 
1938 				// resolve input source
1939 				SColladaInput& inp = localInputs.getLast();
1940 
1941 				// get input source array id, if it is a vertex input, take
1942 				// the <vertex><input>-source attribute.
1943 				if (inp.Semantic == ECIS_VERTEX)
1944 				{
1945 					inp.Source = Inputs[0].Source;
1946 					for (u32 i=1; i<Inputs.size(); ++i)
1947 					{
1948 						localInputs.push_back(Inputs[i]);
1949 						uriToId(localInputs.getLast().Source);
1950 						maxOffset = core::max_(maxOffset,localInputs.getLast().Offset);
1951 						++inputSemanticCount;
1952 					}
1953 				}
1954 				uriToId(inp.Source);
1955 				maxOffset = core::max_(maxOffset,inp.Offset);
1956 				++inputSemanticCount;
1957 			}
1958 			else
1959 			if (primitivesName == nodeName)
1960 			{
1961 				parsePolygonOK = true;
1962 				polygons.push_back(SPolygon());
1963 			}
1964 			else
1965 			if (vcountName == nodeName)
1966 			{
1967 				parseVcountOK = true;
1968 			} // end  is polygon node
1969 		} // end is element node
1970 		else
1971 		if (reader->getNodeType() == io::EXN_ELEMENT_END)
1972 		{
1973 			if (primitivesName == nodeName)
1974 				parsePolygonOK = false; // end parsing a polygon
1975 			else
1976 			if (vcountName == nodeName)
1977 				parseVcountOK = false; // end parsing vcounts
1978 			else
1979 			if (polygonType == nodeName)
1980 				break; // cancel out and create mesh
1981 
1982 		} // end is element end
1983 		else
1984 		if (reader->getNodeType() == io::EXN_TEXT)
1985 		{
1986 			if (parseVcountOK)
1987 			{
1988 				core::stringc data = reader->getNodeData();
1989 				data.trim();
1990 				const c8* p = &data[0];
1991 				while(*p)
1992 				{
1993 					findNextNoneWhiteSpace(&p);
1994 					if (*p)
1995 						vCounts.push_back(readInt(&p));
1996 				}
1997 				parseVcountOK = false;
1998 			}
1999 			else
2000 			if (parsePolygonOK && polygons.size())
2001 			{
2002 				core::stringc data = reader->getNodeData();
2003 				data.trim();
2004 				const c8* p = &data[0];
2005 				SPolygon& poly = polygons.getLast();
2006 				if (polygonType == polygonsSectionName)
2007 					poly.Indices.reallocate((maxOffset+1)*3);
2008 				else
2009 					poly.Indices.reallocate(polygonCount*(maxOffset+1)*3);
2010 
2011 				if (vCounts.empty())
2012 				{
2013 					while(*p)
2014 					{
2015 						findNextNoneWhiteSpace(&p);
2016 						poly.Indices.push_back(readInt(&p));
2017 					}
2018 				}
2019 				else
2020 				{
2021 					for (u32 i = 0; i < vCounts.size(); i++)
2022 					{
2023 						const int polyVCount = vCounts[i];
2024 						core::array<int> polyCorners;
2025 
2026 						for (u32 j = 0; j < polyVCount * inputSemanticCount; j++)
2027 						{
2028 							if (!*p)
2029 								break;
2030 							findNextNoneWhiteSpace(&p);
2031 							polyCorners.push_back(readInt(&p));
2032 						}
2033 
2034 						while (polyCorners.size() >= 3 * inputSemanticCount)
2035 						{
2036 							// add one triangle's worth of indices
2037 							for (u32 k = 0; k < inputSemanticCount * 3; ++k)
2038 							{
2039 								poly.Indices.push_back(polyCorners[k]);
2040 							}
2041 
2042 							// remove one corner from our poly
2043 							polyCorners.erase(inputSemanticCount,inputSemanticCount);
2044 						}
2045 						polyCorners.clear();
2046 					}
2047 					vCounts.clear();
2048 				}
2049 				parsePolygonOK = false;
2050 			}
2051 		}
2052 	} // end while reader->read()
2053 
2054 	// find source array (we'll ignore accessors for this implementation)
2055 	for (u32 i=0; i<localInputs.size(); ++i)
2056 	{
2057 		SColladaInput& inp = localInputs[i];
2058 		u32 s;
2059 		for (s=0; s<sources.size(); ++s)
2060 		{
2061 			if (sources[s].Id == inp.Source)
2062 			{
2063 				// slot found
2064 				inp.Data = sources[s].Array.Data.pointer();
2065 				inp.Stride = sources[s].Accessors[0].Stride;
2066 				break;
2067 			}
2068 		}
2069 
2070 		if (s == sources.size())
2071 		{
2072 			os::Printer::log("COLLADA Warning, polygon input source not found",
2073 				inp.Source.c_str(), ELL_DEBUG);
2074 			inp.Semantic=ECIS_COUNT; // for unknown
2075 			unresolvedInput=true;
2076 		}
2077 		else
2078 		{
2079 			#ifdef COLLADA_READER_DEBUG
2080 			// print slot
2081 			core::stringc tmp = "Added slot ";
2082 			tmp += inputSemanticNames[inp.Semantic];
2083 			tmp += " sourceArray:";
2084 			tmp += inp.Source;
2085 			os::Printer::log(tmp.c_str(), ELL_DEBUG);
2086 			#endif
2087 		}
2088 	}
2089 
2090 	if ((inputSemanticCount == 0) || !polygons.size())
2091 		return; // cancel if there are no polygons anyway.
2092 
2093 	// analyze content of Inputs to create a fitting mesh buffer
2094 
2095 	u32 u;
2096 	u32 textureCoordSetCount = 0;
2097 	bool normalSlotCount = false;
2098 	u32 secondTexCoordSetIndex = 0xFFFFFFFF;
2099 
2100 	for (u=0; u<Inputs.size(); ++u)
2101 	{
2102 		if (Inputs[u].Semantic == ECIS_TEXCOORD || Inputs[u].Semantic == ECIS_UV )
2103 		{
2104 			++textureCoordSetCount;
2105 
2106 			if (textureCoordSetCount==2)
2107 				secondTexCoordSetIndex = u;
2108 		}
2109 		else
2110 		if (Inputs[u].Semantic == ECIS_NORMAL)
2111 			normalSlotCount=true;
2112 	}
2113 
2114 	// if there is more than one texture coordinate set, create a lightmap mesh buffer,
2115 	// otherwise use a standard mesh buffer
2116 
2117 	scene::IMeshBuffer* buffer = 0;
2118 	++maxOffset; // +1 to jump to the next value
2119 
2120 	if ( textureCoordSetCount < 2 )
2121 	{
2122 		// standard mesh buffer
2123 
2124 		scene::SMeshBuffer* mbuffer = new SMeshBuffer();
2125 		buffer = mbuffer;
2126 
2127 		core::map<video::S3DVertex, int> vertMap;
2128 
2129 		for (u32 i=0; i<polygons.size(); ++i)
2130 		{
2131 			core::array<u16> indices;
2132 			const u32 vertexCount = polygons[i].Indices.size() / maxOffset;
2133 			mbuffer->Vertices.reallocate(mbuffer->Vertices.size()+vertexCount);
2134 
2135 			// for all index/semantic groups
2136 			for (u32 v=0; v<polygons[i].Indices.size(); v+=maxOffset)
2137 			{
2138 				video::S3DVertex vtx;
2139 				vtx.Color.set(255,255,255,255);
2140 
2141 				// for all input semantics
2142 				for (u32 k=0; k<localInputs.size(); ++k)
2143 				{
2144 					if (!localInputs[k].Data)
2145 						continue;
2146 					// build vertex from input semantics.
2147 
2148 					const u32 idx = localInputs[k].Stride*polygons[i].Indices[v+localInputs[k].Offset];
2149 
2150 					switch(localInputs[k].Semantic)
2151 					{
2152 					case ECIS_POSITION:
2153 					case ECIS_VERTEX:
2154 						vtx.Pos.X = localInputs[k].Data[idx+0];
2155 						if (FlipAxis)
2156 						{
2157 							vtx.Pos.Z = localInputs[k].Data[idx+1];
2158 							vtx.Pos.Y = localInputs[k].Data[idx+2];
2159 						}
2160 						else
2161 						{
2162 							vtx.Pos.Y = localInputs[k].Data[idx+1];
2163 							vtx.Pos.Z = localInputs[k].Data[idx+2];
2164 						}
2165 						break;
2166 					case ECIS_NORMAL:
2167 						vtx.Normal.X = localInputs[k].Data[idx+0];
2168 						if (FlipAxis)
2169 						{
2170 							vtx.Normal.Z = localInputs[k].Data[idx+1];
2171 							vtx.Normal.Y = localInputs[k].Data[idx+2];
2172 						}
2173 						else
2174 						{
2175 							vtx.Normal.Y = localInputs[k].Data[idx+1];
2176 							vtx.Normal.Z = localInputs[k].Data[idx+2];
2177 						}
2178 						break;
2179 					case ECIS_TEXCOORD:
2180 					case ECIS_UV:
2181 						vtx.TCoords.X = localInputs[k].Data[idx+0];
2182 						vtx.TCoords.Y = 1-localInputs[k].Data[idx+1];
2183 						break;
2184 					case ECIS_TANGENT:
2185 						break;
2186 					default:
2187 						break;
2188 					}
2189 				}
2190 
2191 				//first, try to find this vertex in the mesh
2192 				core::map<video::S3DVertex, int>::Node* n = vertMap.find(vtx);
2193 				if (n)
2194 				{
2195 					indices.push_back(n->getValue());
2196 				}
2197 				else
2198 				{
2199 					indices.push_back(mbuffer->getVertexCount());
2200 					mbuffer->Vertices.push_back(vtx);
2201 					vertMap.insert(vtx, mbuffer->getVertexCount()-1);
2202 				}
2203 			} // end for all vertices
2204 
2205 			if (polygonsSectionName == polygonType &&
2206 				indices.size() > 3)
2207 			{
2208 				// need to tesselate for polygons of 4 or more vertices
2209 				// for now we naively turn interpret it as a triangle fan
2210 				// as full tesselation is problematic
2211 				if (FlipAxis)
2212 				{
2213 					for (u32 ind = indices.size()-3; ind>0 ; --ind)
2214 					{
2215 						mbuffer->Indices.push_back(indices[0]);
2216 						mbuffer->Indices.push_back(indices[ind+2]);
2217 						mbuffer->Indices.push_back(indices[ind+1]);
2218 					}
2219 				}
2220 				else
2221 				{
2222 					for (u32 ind = 0; ind+2 < indices.size(); ++ind)
2223 					{
2224 						mbuffer->Indices.push_back(indices[0]);
2225 						mbuffer->Indices.push_back(indices[ind+1]);
2226 						mbuffer->Indices.push_back(indices[ind+2]);
2227 					}
2228 				}
2229 			}
2230 			else
2231 			{
2232 				// it's just triangles
2233 				for (u32 ind = 0; ind < indices.size(); ind+=3)
2234 				{
2235 					if (FlipAxis)
2236 					{
2237 						mbuffer->Indices.push_back(indices[ind+2]);
2238 						mbuffer->Indices.push_back(indices[ind+1]);
2239 						mbuffer->Indices.push_back(indices[ind+0]);
2240 					}
2241 					else
2242 					{
2243 						mbuffer->Indices.push_back(indices[ind+0]);
2244 						mbuffer->Indices.push_back(indices[ind+1]);
2245 						mbuffer->Indices.push_back(indices[ind+2]);
2246 					}
2247 				}
2248 			}
2249 
2250 		} // end for all polygons
2251 	}
2252 	else
2253 	{
2254 		// lightmap mesh buffer
2255 
2256 		scene::SMeshBufferLightMap* mbuffer = new SMeshBufferLightMap();
2257 		buffer = mbuffer;
2258 
2259 		for (u32 i=0; i<polygons.size(); ++i)
2260 		{
2261 			const u32 vertexCount = polygons[i].Indices.size() / maxOffset;
2262 			mbuffer->Vertices.reallocate(mbuffer->Vertices.size()+vertexCount);
2263 			// for all vertices in array
2264 			for (u32 v=0; v<polygons[i].Indices.size(); v+=maxOffset)
2265 			{
2266 				video::S3DVertex2TCoords vtx;
2267 				vtx.Color.set(100,255,255,255);
2268 
2269 				// for all input semantics
2270 				for (u32 k=0; k<Inputs.size(); ++k)
2271 				{
2272 					// build vertex from input semantics.
2273 
2274 					const u32 idx = localInputs[k].Stride*polygons[i].Indices[v+Inputs[k].Offset];
2275 
2276 					switch(localInputs[k].Semantic)
2277 					{
2278 					case ECIS_POSITION:
2279 					case ECIS_VERTEX:
2280 						vtx.Pos.X = localInputs[k].Data[idx+0];
2281 						if (FlipAxis)
2282 						{
2283 							vtx.Pos.Z = localInputs[k].Data[idx+1];
2284 							vtx.Pos.Y = localInputs[k].Data[idx+2];
2285 						}
2286 						else
2287 						{
2288 							vtx.Pos.Y = localInputs[k].Data[idx+1];
2289 							vtx.Pos.Z = localInputs[k].Data[idx+2];
2290 						}
2291 						break;
2292 					case ECIS_NORMAL:
2293 						vtx.Normal.X = localInputs[k].Data[idx+0];
2294 						if (FlipAxis)
2295 						{
2296 							vtx.Normal.Z = localInputs[k].Data[idx+1];
2297 							vtx.Normal.Y = localInputs[k].Data[idx+2];
2298 						}
2299 						else
2300 						{
2301 							vtx.Normal.Y = localInputs[k].Data[idx+1];
2302 							vtx.Normal.Z = localInputs[k].Data[idx+2];
2303 						}
2304 						break;
2305 					case ECIS_TEXCOORD:
2306 					case ECIS_UV:
2307 						if (k==secondTexCoordSetIndex)
2308 						{
2309 							vtx.TCoords2.X = localInputs[k].Data[idx+0];
2310 							vtx.TCoords2.Y = 1-localInputs[k].Data[idx+1];
2311 						}
2312 						else
2313 						{
2314 							vtx.TCoords.X = localInputs[k].Data[idx+0];
2315 							vtx.TCoords.Y = 1-localInputs[k].Data[idx+1];
2316 						}
2317 						break;
2318 					case ECIS_TANGENT:
2319 						break;
2320 					default:
2321 						break;
2322 					}
2323 				}
2324 
2325 				mbuffer->Vertices.push_back(vtx);
2326 
2327 			} // end for all vertices
2328 
2329 			// add vertex indices
2330 			const u32 oldVertexCount = mbuffer->Vertices.size() - vertexCount;
2331 			for (u32 face=0; face<vertexCount-2; ++face)
2332 			{
2333 				mbuffer->Indices.push_back(oldVertexCount + 0);
2334 				mbuffer->Indices.push_back(oldVertexCount + 1 + face);
2335 				mbuffer->Indices.push_back(oldVertexCount + 2 + face);
2336 			}
2337 
2338 		} // end for all polygons
2339 	}
2340 
2341 	const SColladaMaterial* m = findMaterial(materialName);
2342 	if (m)
2343 	{
2344 		buffer->getMaterial() = m->Mat;
2345 		SMesh tmpmesh;
2346 		tmpmesh.addMeshBuffer(buffer);
2347 		SceneManager->getMeshManipulator()->setVertexColors(&tmpmesh,m->Mat.DiffuseColor);
2348 		if (m->Transparency != 1.0f)
2349 			SceneManager->getMeshManipulator()->setVertexColorAlpha(&tmpmesh,core::floor32(m->Transparency*255.0f));
2350 	}
2351 	// add future bind reference for the material
2352 	core::stringc meshbufferReference = geometryId+"/"+materialName;
2353 	if (!MaterialsToBind.find(meshbufferReference))
2354 	{
2355 		MaterialsToBind[meshbufferReference] = MeshesToBind.size();
2356 		MeshesToBind.push_back(core::array<irr::scene::IMeshBuffer*>());
2357 	}
2358 	MeshesToBind[MaterialsToBind[meshbufferReference]].push_back(buffer);
2359 
2360 	// calculate normals if there is no slot for it
2361 
2362 	if (!normalSlotCount)
2363 		SceneManager->getMeshManipulator()->recalculateNormals(buffer, true);
2364 
2365 	// recalculate bounding box
2366 	buffer->recalculateBoundingBox();
2367 
2368 	// add mesh buffer
2369 	mesh->addMeshBuffer(buffer);
2370 	#ifdef COLLADA_READER_DEBUG
2371 	os::Printer::log("COLLADA added meshbuffer", core::stringc(buffer->getVertexCount())+" vertices, "+core::stringc(buffer->getIndexCount())+" indices.", ELL_DEBUG);
2372 	#endif
2373 
2374 	buffer->drop();
2375 }
2376 
2377 
2378 //! reads a <light> element and stores it as prefab
readLightPrefab(io::IXMLReaderUTF8 * reader)2379 void CColladaFileLoader::readLightPrefab(io::IXMLReaderUTF8* reader)
2380 {
2381 	#ifdef COLLADA_READER_DEBUG
2382 	os::Printer::log("COLLADA reading light prefab", ELL_DEBUG);
2383 	#endif
2384 
2385 	CLightPrefab* prefab = new CLightPrefab(readId(reader));
2386 
2387 	if (!reader->isEmptyElement())
2388 	{
2389 		if (Version >= 10400) // start with 1.4
2390 		{
2391 			while(reader->read())
2392 			{
2393 				if (reader->getNodeType() == io::EXN_ELEMENT)
2394 				{
2395 					if (pointSectionName == reader->getNodeName())
2396 						prefab->LightData.Type=video::ELT_POINT;
2397 					else
2398 					if (directionalSectionName == reader->getNodeName())
2399 						prefab->LightData.Type=video::ELT_DIRECTIONAL;
2400 					else
2401 					if (spotSectionName == reader->getNodeName())
2402 						prefab->LightData.Type=video::ELT_SPOT;
2403 					else
2404 					if (ambientSectionName == reader->getNodeName())
2405 						prefab->LightData.Type=ELT_AMBIENT;
2406 					else
2407 					if (colorNodeName == reader->getNodeName())
2408 						prefab->LightData.DiffuseColor=readColorNode(reader);
2409 					else
2410 					if (constantAttenuationNodeName == reader->getNodeName())
2411 						readFloatsInsideElement(reader,&prefab->LightData.Attenuation.X,1);
2412 					else
2413 					if (linearAttenuationNodeName == reader->getNodeName())
2414 						readFloatsInsideElement(reader,&prefab->LightData.Attenuation.Y,1);
2415 					else
2416 					if (quadraticAttenuationNodeName == reader->getNodeName())
2417 						readFloatsInsideElement(reader,&prefab->LightData.Attenuation.Z,1);
2418 					else
2419 					if (falloffAngleNodeName == reader->getNodeName())
2420 					{
2421 						readFloatsInsideElement(reader,&prefab->LightData.OuterCone,1);
2422 						prefab->LightData.OuterCone *= core::DEGTORAD;
2423 					}
2424 					else
2425 					if (falloffExponentNodeName == reader->getNodeName())
2426 						readFloatsInsideElement(reader,&prefab->LightData.Falloff,1);
2427 				}
2428 				else
2429 				if (reader->getNodeType() == io::EXN_ELEMENT_END)
2430 				{
2431 					if ((pointSectionName == reader->getNodeName()) ||
2432 						(directionalSectionName == reader->getNodeName()) ||
2433 						(spotSectionName == reader->getNodeName()) ||
2434 						(ambientSectionName == reader->getNodeName()))
2435 						break;
2436 				}
2437 			}
2438 		}
2439 		else
2440 		{
2441 			readColladaParameters(reader, lightPrefabName);
2442 
2443 			SColladaParam* p = getColladaParameter(ECPN_COLOR);
2444 			if (p && p->Type == ECPT_FLOAT3)
2445 				prefab->LightData.DiffuseColor.set(p->Floats[0], p->Floats[1], p->Floats[2]);
2446 		}
2447 	}
2448 
2449 	Prefabs.push_back(prefab);
2450 }
2451 
2452 
2453 //! returns a collada parameter or none if not found
getColladaParameter(ECOLLADA_PARAM_NAME name)2454 SColladaParam* CColladaFileLoader::getColladaParameter(ECOLLADA_PARAM_NAME name)
2455 {
2456 	for (u32 i=0; i<ColladaParameters.size(); ++i)
2457 		if (ColladaParameters[i].Name == name)
2458 			return &ColladaParameters[i];
2459 
2460 	return 0;
2461 }
2462 
2463 //! returns a collada input or none if not found
getColladaInput(ECOLLADA_INPUT_SEMANTIC input)2464 SColladaInput* CColladaFileLoader::getColladaInput(ECOLLADA_INPUT_SEMANTIC input)
2465 {
2466 	for (u32 i=0; i<Inputs.size(); ++i)
2467 		if (Inputs[i].Semantic == input)
2468 			return &Inputs[i];
2469 
2470 	return 0;
2471 }
2472 
2473 
2474 //! reads a collada input tag and adds it to the input parameter
readColladaInput(io::IXMLReaderUTF8 * reader,core::array<SColladaInput> & inputs)2475 void CColladaFileLoader::readColladaInput(io::IXMLReaderUTF8* reader, core::array<SColladaInput>& inputs)
2476 {
2477 	// parse param
2478 	SColladaInput p;
2479 
2480 	// get type
2481 	core::stringc semanticName = reader->getAttributeValue("semantic");
2482 	for (u32 i=0; inputSemanticNames[i]; ++i)
2483 	{
2484 		if (semanticName == inputSemanticNames[i])
2485 		{
2486 			p.Semantic = (ECOLLADA_INPUT_SEMANTIC)i;
2487 			break;
2488 		}
2489 	}
2490 
2491 	// get source
2492 	p.Source = reader->getAttributeValue("source");
2493 	if (reader->getAttributeValue("offset")) // Collada 1.4+
2494 		p.Offset = (u32)reader->getAttributeValueAsInt("offset");
2495 	else // Collada 1.2/1.3
2496 		p.Offset = (u32)reader->getAttributeValueAsInt("idx");
2497 	p.Set = (u32)reader->getAttributeValueAsInt("set");
2498 
2499 	// add input
2500 	inputs.push_back(p);
2501 }
2502 
2503 //! parses all collada inputs inside an element and stores them in Inputs
readColladaInputs(io::IXMLReaderUTF8 * reader,const core::stringc & parentName)2504 void CColladaFileLoader::readColladaInputs(io::IXMLReaderUTF8* reader, const core::stringc& parentName)
2505 {
2506 	Inputs.clear();
2507 
2508 	while(reader->read())
2509 	{
2510 		if (reader->getNodeType() == io::EXN_ELEMENT &&
2511 			inputTagName == reader->getNodeName())
2512 		{
2513 			readColladaInput(reader, Inputs);
2514 		}
2515 		else
2516 		if (reader->getNodeType() == io::EXN_ELEMENT_END)
2517 		{
2518 			if (parentName == reader->getNodeName())
2519 				return; // end of parent reached
2520 		}
2521 
2522 	} // end while reader->read();
2523 }
2524 
2525 //! parses all collada parameters inside an element and stores them in ColladaParameters
readColladaParameters(io::IXMLReaderUTF8 * reader,const core::stringc & parentName)2526 void CColladaFileLoader::readColladaParameters(io::IXMLReaderUTF8* reader,
2527 		const core::stringc& parentName)
2528 {
2529 	ColladaParameters.clear();
2530 
2531 	const char* const paramNames[] = {"COLOR", "AMBIENT", "DIFFUSE",
2532 		"SPECULAR", "SHININESS", "YFOV", "ZNEAR", "ZFAR", 0};
2533 
2534 	const char* const typeNames[] = {"float", "float2", "float3", 0};
2535 
2536 	while(reader->read())
2537 	{
2538 		const char* nodeName = reader->getNodeName();
2539 		if (reader->getNodeType() == io::EXN_ELEMENT &&
2540 			paramTagName == nodeName)
2541 		{
2542 			// parse param
2543 			SColladaParam p;
2544 
2545 			// get type
2546 			u32 i;
2547 			core::stringc typeName = reader->getAttributeValue("type");
2548 			for (i=0; typeNames[i]; ++i)
2549 				if (typeName == typeNames[i])
2550 				{
2551 					p.Type = (ECOLLADA_PARAM_TYPE)i;
2552 					break;
2553 				}
2554 
2555 			// get name
2556 			core::stringc nameName = reader->getAttributeValue("name");
2557 			for (i=0; typeNames[i]; ++i)
2558 				if (nameName == paramNames[i])
2559 				{
2560 					p.Name = (ECOLLADA_PARAM_NAME)i;
2561 					break;
2562 				}
2563 
2564 			// read parameter data inside parameter tags
2565 			switch(p.Type)
2566 			{
2567 				case ECPT_FLOAT:
2568 				case ECPT_FLOAT2:
2569 				case ECPT_FLOAT3:
2570 				case ECPT_FLOAT4:
2571 					readFloatsInsideElement(reader, p.Floats, p.Type - ECPT_FLOAT + 1);
2572 					break;
2573 
2574 				// TODO: other types of data (ints, bools or whatever)
2575 				default:
2576 					break;
2577 			}
2578 
2579 			// add param
2580 			ColladaParameters.push_back(p);
2581 		}
2582 		else
2583 		if (reader->getNodeType() == io::EXN_ELEMENT_END)
2584 		{
2585 			if (parentName == reader->getNodeName())
2586 				return; // end of parent reached
2587 		}
2588 
2589 	} // end while reader->read();
2590 }
2591 
2592 
2593 //! parses a float from a char pointer and moves the pointer
2594 //! to the end of the parsed float
readFloat(const c8 ** p)2595 inline f32 CColladaFileLoader::readFloat(const c8** p)
2596 {
2597 	f32 ftmp;
2598 	*p = core::fast_atof_move(*p, ftmp);
2599 	return ftmp;
2600 }
2601 
2602 
2603 //! parses an int from a char pointer and moves the pointer to
2604 //! the end of the parsed float
readInt(const c8 ** p)2605 inline s32 CColladaFileLoader::readInt(const c8** p)
2606 {
2607 	return (s32)readFloat(p);
2608 }
2609 
2610 
2611 //! places pointer to next begin of a token
findNextNoneWhiteSpace(const c8 ** start)2612 void CColladaFileLoader::findNextNoneWhiteSpace(const c8** start)
2613 {
2614 	const c8* p = *start;
2615 
2616 	while(*p && (*p==' ' || *p=='\n' || *p=='\r' || *p=='\t'))
2617 		++p;
2618 
2619 	// TODO: skip comments <!-- -->
2620 
2621 	*start = p;
2622 }
2623 
2624 
2625 //! reads floats from inside of xml element until end of xml element
readFloatsInsideElement(io::IXMLReaderUTF8 * reader,f32 * floats,u32 count)2626 void CColladaFileLoader::readFloatsInsideElement(io::IXMLReaderUTF8* reader, f32* floats, u32 count)
2627 {
2628 	if (reader->isEmptyElement())
2629 		return;
2630 
2631 	while(reader->read())
2632 	{
2633 		// TODO: check for comments inside the element
2634 		// and ignore them.
2635 
2636 		if (reader->getNodeType() == io::EXN_TEXT)
2637 		{
2638 			// parse float data
2639 			core::stringc data = reader->getNodeData();
2640 			data.trim();
2641 			const c8* p = &data[0];
2642 
2643 			for (u32 i=0; i<count; ++i)
2644 			{
2645 				findNextNoneWhiteSpace(&p);
2646 				if (*p)
2647 					floats[i] = readFloat(&p);
2648 				else
2649 					floats[i] = 0.0f;
2650 			}
2651 		}
2652 		else
2653 		if (reader->getNodeType() == io::EXN_ELEMENT_END)
2654 			break; // end parsing text
2655 	}
2656 }
2657 
2658 
2659 //! reads ints from inside of xml element until end of xml element
readIntsInsideElement(io::IXMLReaderUTF8 * reader,s32 * ints,u32 count)2660 void CColladaFileLoader::readIntsInsideElement(io::IXMLReaderUTF8* reader, s32* ints, u32 count)
2661 {
2662 	if (reader->isEmptyElement())
2663 		return;
2664 
2665 	while(reader->read())
2666 	{
2667 		// TODO: check for comments inside the element
2668 		// and ignore them.
2669 
2670 		if (reader->getNodeType() == io::EXN_TEXT)
2671 		{
2672 			// parse float data
2673 			core::stringc data = reader->getNodeData();
2674 			data.trim();
2675 			const c8* p = &data[0];
2676 
2677 			for (u32 i=0; i<count; ++i)
2678 			{
2679 				findNextNoneWhiteSpace(&p);
2680 				if (*p)
2681 					ints[i] = readInt(&p);
2682 				else
2683 					ints[i] = 0;
2684 			}
2685 		}
2686 		else
2687 		if (reader->getNodeType() == io::EXN_ELEMENT_END)
2688 			break; // end parsing text
2689 	}
2690 }
2691 
2692 
readColorNode(io::IXMLReaderUTF8 * reader)2693 video::SColorf CColladaFileLoader::readColorNode(io::IXMLReaderUTF8* reader)
2694 {
2695 	if (reader->getNodeType() == io::EXN_ELEMENT &&
2696 		colorNodeName == reader->getNodeName())
2697 	{
2698 		f32 color[4];
2699 		readFloatsInsideElement(reader,color,4);
2700 		return video::SColorf(color[0], color[1], color[2], color[3]);
2701 	}
2702 
2703 	return video::SColorf();
2704 }
2705 
2706 
readFloatNode(io::IXMLReaderUTF8 * reader)2707 f32 CColladaFileLoader::readFloatNode(io::IXMLReaderUTF8* reader)
2708 {
2709 	#ifdef COLLADA_READER_DEBUG
2710 	os::Printer::log("COLLADA reading <float>", ELL_DEBUG);
2711 	#endif
2712 
2713 	f32 result = 0.0f;
2714 	if (reader->getNodeType() == io::EXN_ELEMENT &&
2715 		floatNodeName == reader->getNodeName())
2716 	{
2717 		readFloatsInsideElement(reader,&result,1);
2718 	}
2719 
2720 	return result;
2721 }
2722 
2723 
2724 //! clears all loaded data
clearData()2725 void CColladaFileLoader::clearData()
2726 {
2727 	// delete all prefabs
2728 
2729 	for (u32 i=0; i<Prefabs.size(); ++i)
2730 		Prefabs[i]->drop();
2731 
2732 	Prefabs.clear();
2733 
2734 	// clear all parameters
2735 	ColladaParameters.clear();
2736 
2737 	// clear all materials
2738 	Images.clear();
2739 
2740 	// clear all materials
2741 	Textures.clear();
2742 
2743 	// clear all materials
2744 	Materials.clear();
2745 
2746 	// clear all inputs
2747 	Inputs.clear();
2748 
2749 	// clear all effects
2750 	for ( u32 i=0; i<Effects.size(); ++i )
2751 		Effects[i].Parameters->drop();
2752 	Effects.clear();
2753 
2754 	// clear all the materials to bind
2755 	MaterialsToBind.clear();
2756 	MeshesToBind.clear();
2757 }
2758 
2759 
2760 //! changes the XML URI into an internal id
uriToId(core::stringc & str)2761 void CColladaFileLoader::uriToId(core::stringc& str)
2762 {
2763 	// currently, we only remove the # from the begin if there
2764 	// because we simply don't support referencing other files.
2765 	if (!str.size())
2766 		return;
2767 
2768 	if (str[0] == '#')
2769 		str.erase(0);
2770 }
2771 
2772 
2773 //! read Collada Id, uses id or name if id is missing
readId(io::IXMLReaderUTF8 * reader)2774 core::stringc CColladaFileLoader::readId(io::IXMLReaderUTF8* reader)
2775 {
2776 	core::stringc id = reader->getAttributeValue("id");
2777 	if (id.size()==0)
2778 		id = reader->getAttributeValue("name");
2779 	return id;
2780 }
2781 
2782 
2783 //! create an Irrlicht texture from the reference
getTextureFromImage(core::stringc uri,SColladaEffect * effect)2784 video::ITexture* CColladaFileLoader::getTextureFromImage(core::stringc uri, SColladaEffect * effect)
2785 {
2786 	#ifdef COLLADA_READER_DEBUG
2787 	os::Printer::log("COLLADA searching texture", uri, ELL_DEBUG);
2788 	#endif
2789 	video::IVideoDriver* driver = SceneManager->getVideoDriver();
2790 	for (;;)
2791 	{
2792 		uriToId(uri);
2793 		for (u32 i=0; i<Images.size(); ++i)
2794 		{
2795 			if (uri == Images[i].Id)
2796 			{
2797 				if (Images[i].Source.size() && Images[i].SourceIsFilename)
2798 				{
2799 					if (FileSystem->existFile(Images[i].Source))
2800 						return driver->getTexture(Images[i].Source);
2801 					return driver->getTexture((FileSystem->getFileDir(CurrentlyLoadingMesh)+"/"+Images[i].Source));
2802 				}
2803 				else
2804 				if (Images[i].Source.size())
2805 				{
2806 					//const u32 size = Images[i].Dimension.getArea();
2807 					const u32 size = Images[i].Dimension.Width * Images[i].Dimension.Height;;
2808 					u32* data = new u32[size]; // we assume RGBA
2809 					u32* ptrdest = data;
2810 					const c8* ptrsrc = Images[i].Source.c_str();
2811 					for (u32 j=0; j<size; ++j)
2812 					{
2813 						sscanf(ptrsrc, "%x", ptrdest);
2814 						++ptrdest;
2815 						ptrsrc += 4;
2816 					}
2817 					video::IImage* img = driver->createImageFromData(video::ECF_A8R8G8B8, Images[i].Dimension, data, true, true);
2818 					video::ITexture* tex = driver->addTexture((CurrentlyLoadingMesh+"#"+Images[i].Id).c_str(), img);
2819 					img->drop();
2820 					return tex;
2821 				}
2822 				break;
2823 			}
2824 		}
2825 		if (effect && effect->Parameters->getAttributeType(uri.c_str())==io::EAT_STRING)
2826 		{
2827 			uri = effect->Parameters->getAttributeAsString(uri.c_str());
2828 #ifdef COLLADA_READER_DEBUG
2829 			os::Printer::log("COLLADA now searching texture", uri.c_str(), ELL_DEBUG);
2830 #endif
2831 		}
2832 		else
2833 			break;
2834 	}
2835 	return 0;
2836 }
2837 
2838 
2839 //! read a parameter and value
readParameter(io::IXMLReaderUTF8 * reader,io::IAttributes * parameters)2840 void CColladaFileLoader::readParameter(io::IXMLReaderUTF8* reader, io::IAttributes* parameters)
2841 {
2842 	#ifdef COLLADA_READER_DEBUG
2843 	os::Printer::log("COLLADA reading parameter", ELL_DEBUG);
2844 	#endif
2845 
2846 	if ( !parameters )
2847 		return;
2848 
2849 	const core::stringc name = reader->getAttributeValue("sid");
2850 	if (!reader->isEmptyElement())
2851 	{
2852 		while(reader->read())
2853 		{
2854 			if (reader->getNodeType() == io::EXN_ELEMENT)
2855 			{
2856 				if (floatNodeName == reader->getNodeName())
2857 				{
2858 					const f32 f = readFloatNode(reader);
2859 					parameters->addFloat(name.c_str(), f);
2860 				}
2861 				else
2862 				if (float2NodeName == reader->getNodeName())
2863 				{
2864 					f32 f[2];
2865 					readFloatsInsideElement(reader, f, 2);
2866 //						Parameters.addVector2d(name.c_str(), core::vector2df(f[0],f[1]));
2867 				}
2868 				else
2869 				if (float3NodeName == reader->getNodeName())
2870 				{
2871 					f32 f[3];
2872 					readFloatsInsideElement(reader, f, 3);
2873 					parameters->addVector3d(name.c_str(), core::vector3df(f[0],f[1],f[2]));
2874 				}
2875 				else
2876 				if ((initFromName == reader->getNodeName()) ||
2877 					(sourceSectionName == reader->getNodeName()))
2878 				{
2879 					reader->read();
2880 					parameters->addString(name.c_str(), reader->getNodeData());
2881 				}
2882 				else
2883 				if (wrapsName == reader->getNodeName())
2884 				{
2885 					reader->read();
2886 					const core::stringc val = reader->getNodeData();
2887 					if (val == "WRAP")
2888 						parameters->addInt(wrapsName.c_str(), (int)video::ETC_REPEAT);
2889 					else if ( val== "MIRROR")
2890 						parameters->addInt(wrapsName.c_str(), (int)video::ETC_MIRROR);
2891 					else if ( val== "CLAMP")
2892 						parameters->addInt(wrapsName.c_str(), (int)video::ETC_CLAMP_TO_EDGE);
2893 					else if ( val== "BORDER")
2894 						parameters->addInt(wrapsName.c_str(), (int)video::ETC_CLAMP_TO_BORDER);
2895 					else if ( val== "NONE")
2896 						parameters->addInt(wrapsName.c_str(), (int)video::ETC_CLAMP_TO_BORDER);
2897 				}
2898 				else
2899 				if (wraptName == reader->getNodeName())
2900 				{
2901 					reader->read();
2902 					const core::stringc val = reader->getNodeData();
2903 					if (val == "WRAP")
2904 						parameters->addInt(wraptName.c_str(), (int)video::ETC_REPEAT);
2905 					else if ( val== "MIRROR")
2906 						parameters->addInt(wraptName.c_str(), (int)video::ETC_MIRROR);
2907 					else if ( val== "CLAMP")
2908 						parameters->addInt(wraptName.c_str(), (int)video::ETC_CLAMP_TO_EDGE);
2909 					else if ( val== "BORDER")
2910 						parameters->addInt(wraptName.c_str(), (int)video::ETC_CLAMP_TO_BORDER);
2911 					else if ( val== "NONE")
2912 						parameters->addInt(wraptName.c_str(), (int)video::ETC_CLAMP_TO_BORDER);
2913 				}
2914 				else
2915 				if (minfilterName == reader->getNodeName())
2916 				{
2917 					reader->read();
2918 					const core::stringc val = reader->getNodeData();
2919 					if (val == "LINEAR_MIPMAP_LINEAR")
2920 						parameters->addBool("trilinear", true);
2921 					else
2922 					if (val == "LINEAR_MIPMAP_NEAREST")
2923 						parameters->addBool("bilinear", true);
2924 				}
2925 				else
2926 				if (magfilterName == reader->getNodeName())
2927 				{
2928 					reader->read();
2929 					const core::stringc val = reader->getNodeData();
2930 					if (val != "LINEAR")
2931 					{
2932 						parameters->addBool("bilinear", false);
2933 						parameters->addBool("trilinear", false);
2934 					}
2935 				}
2936 				else
2937 				if (mipfilterName == reader->getNodeName())
2938 				{
2939 					parameters->addBool("anisotropic", true);
2940 				}
2941 			}
2942 			else
2943 			if(reader->getNodeType() == io::EXN_ELEMENT_END)
2944 			{
2945 				if (newParamName == reader->getNodeName())
2946 					break;
2947 			}
2948 		}
2949 	}
2950 }
2951 
2952 
2953 } // end namespace scene
2954 } // end namespace irr
2955 
2956 #endif // _IRR_COMPILE_WITH_COLLADA_LOADER_
2957 
2958