]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CColladaMeshWriter.cpp
Fix Windows, Android build
[irrlicht.git] / source / Irrlicht / CColladaMeshWriter.cpp
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt\r
2 // This file is part of the "Irrlicht Engine".\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h\r
4 \r
5 // TODO: second UV-coordinates currently ignored in textures\r
6 \r
7 #include "IrrCompileConfig.h"\r
8 \r
9 #ifdef _IRR_COMPILE_WITH_COLLADA_WRITER_\r
10 \r
11 #include "CColladaMeshWriter.h"\r
12 #include "os.h"\r
13 #include "IFileSystem.h"\r
14 #include "IWriteFile.h"\r
15 #include "IXMLWriter.h"\r
16 #include "IMesh.h"\r
17 #include "IAttributes.h"\r
18 #include "IAnimatedMeshSceneNode.h"\r
19 #include "IMeshSceneNode.h"\r
20 #include "ITerrainSceneNode.h"\r
21 #include "ILightSceneNode.h"\r
22 #include "ICameraSceneNode.h"\r
23 #include "ISceneManager.h"\r
24 \r
25 namespace irr\r
26 {\r
27 namespace scene\r
28 {\r
29 \r
30 //! Which lighting model should be used in the technique (FX) section when exporting effects (materials)\r
31 E_COLLADA_TECHNIQUE_FX CColladaMeshWriterProperties::getTechniqueFx(const video::SMaterial& material) const\r
32 {\r
33         return ECTF_BLINN;\r
34 }\r
35 \r
36 //! Which texture index should be used when writing the texture of the given sampler color.\r
37 s32 CColladaMeshWriterProperties::getTextureIdx(const video::SMaterial & material, E_COLLADA_COLOR_SAMPLER cs) const\r
38 {\r
39         // So far we just export in a way which is similar to how we import colladas.\r
40         // There might be better ways to do this, but I suppose it depends a lot for which target\r
41         // application we export, so in most cases it will have to be done in user-code anyway.\r
42         switch ( cs )\r
43         {\r
44                 case ECCS_DIFFUSE:\r
45                         return 2;\r
46                 case ECCS_AMBIENT:\r
47                         return 1;\r
48                 case ECCS_EMISSIVE:\r
49                         return 0;\r
50                 case ECCS_SPECULAR:\r
51                         return 3;\r
52                 case ECCS_TRANSPARENT:\r
53                         return -1;\r
54                 case ECCS_REFLECTIVE:\r
55                         return -1;\r
56         };\r
57         return -1;\r
58 }\r
59 \r
60 E_COLLADA_IRR_COLOR CColladaMeshWriterProperties::getColorMapping(const video::SMaterial & material, E_COLLADA_COLOR_SAMPLER cs) const\r
61 {\r
62         switch ( cs )\r
63         {\r
64                 case ECCS_DIFFUSE:\r
65                         return ECIC_DIFFUSE;\r
66                 case ECCS_AMBIENT:\r
67                         return ECIC_AMBIENT;\r
68                 case ECCS_EMISSIVE:\r
69                         return ECIC_EMISSIVE;\r
70                 case ECCS_SPECULAR:\r
71                         return ECIC_SPECULAR;\r
72                 case ECCS_TRANSPARENT:\r
73                         return ECIC_NONE;\r
74                 case ECCS_REFLECTIVE:\r
75                         return ECIC_CUSTOM;\r
76         };\r
77 \r
78         return ECIC_NONE;\r
79 }\r
80 \r
81 //! Return custom colors for certain color types requested by collada.\r
82 video::SColor CColladaMeshWriterProperties::getCustomColor(const video::SMaterial & material, E_COLLADA_COLOR_SAMPLER cs) const\r
83 {\r
84         return video::SColor(255, 0, 0, 0);\r
85 }\r
86 \r
87 \r
88 //! Return the settings for transparence\r
89 E_COLLADA_TRANSPARENT_FX CColladaMeshWriterProperties::getTransparentFx(const video::SMaterial& material) const\r
90 {\r
91         // TODO: figure out best default mapping\r
92         return ECOF_A_ONE;\r
93 }\r
94 \r
95 //! Transparency value for the material.\r
96 f32 CColladaMeshWriterProperties::getTransparency(const video::SMaterial& material) const\r
97 {\r
98         // TODO: figure out best default mapping\r
99         return -1.f;\r
100 }\r
101 \r
102 //! Reflectivity value for that material\r
103 f32 CColladaMeshWriterProperties::getReflectivity(const video::SMaterial& material) const\r
104 {\r
105         // TODO: figure out best default mapping\r
106         return 0.f;\r
107 }\r
108 \r
109 //! Return index of refraction for that material\r
110 f32 CColladaMeshWriterProperties::getIndexOfRefraction(const video::SMaterial& material) const\r
111 {\r
112         return -1.f;\r
113 }\r
114 \r
115 bool CColladaMeshWriterProperties::isExportable(const irr::scene::ISceneNode * node) const\r
116 {\r
117         return node && node->isVisible();\r
118 }\r
119 \r
120 IMesh* CColladaMeshWriterProperties::getMesh(irr::scene::ISceneNode * node)\r
121 {\r
122         if ( !node )\r
123                 return 0;\r
124         if ( node->getType() == ESNT_ANIMATED_MESH )\r
125                 return static_cast<IAnimatedMeshSceneNode*>(node)->getMesh()->getMesh(0);\r
126         // TODO: we need some ISceneNode::hasType() function to get rid of those checks\r
127         if (    node->getType() == ESNT_MESH\r
128                 ||      node->getType() == ESNT_CUBE\r
129                 ||      node->getType() == ESNT_SPHERE\r
130                 ||      node->getType() == ESNT_WATER_SURFACE\r
131                 ||      node->getType() == ESNT_Q3SHADER_SCENE_NODE\r
132                 )\r
133                 return static_cast<IMeshSceneNode*>(node)->getMesh();\r
134         if ( node->getType() == ESNT_TERRAIN )\r
135                 return static_cast<ITerrainSceneNode*>(node)->getMesh();\r
136         return 0;\r
137 }\r
138 \r
139 // Check if the node has it's own material overwriting the mesh-materials\r
140 bool CColladaMeshWriterProperties::useNodeMaterial(const scene::ISceneNode* node) const\r
141 {\r
142         if ( !node )\r
143                 return false;\r
144 \r
145         // TODO: we need some ISceneNode::hasType() function to get rid of those checks\r
146         bool useMeshMaterial =  (       (node->getType() == ESNT_MESH ||\r
147                                                                 node->getType() == ESNT_CUBE ||\r
148                                                                 node->getType() == ESNT_SPHERE ||\r
149                                                                 node->getType() == ESNT_WATER_SURFACE ||\r
150                                                                 node->getType() == ESNT_Q3SHADER_SCENE_NODE)\r
151                                                                 && static_cast<const IMeshSceneNode*>(node)->isReadOnlyMaterials())\r
152 \r
153                                                         ||      (node->getType() == ESNT_ANIMATED_MESH\r
154                                                                 && static_cast<const IAnimatedMeshSceneNode*>(node)->isReadOnlyMaterials() );\r
155 \r
156         return !useMeshMaterial;\r
157 }\r
158 \r
159 \r
160 \r
161 CColladaMeshWriterNames::CColladaMeshWriterNames(IColladaMeshWriter * writer)\r
162         : ColladaMeshWriter(writer)\r
163 {\r
164 }\r
165 \r
166 irr::core::stringc CColladaMeshWriterNames::nameForMesh(const scene::IMesh* mesh, int instance)\r
167 {\r
168         irr::core::stringc name("mesh");\r
169         name += nameForPtr(mesh);\r
170         if ( instance > 0 )\r
171         {\r
172                 name += "i";\r
173                 name += irr::core::stringc(instance);\r
174         }\r
175         return ColladaMeshWriter->toNCName(name);\r
176 }\r
177 \r
178 irr::core::stringc CColladaMeshWriterNames::nameForNode(const scene::ISceneNode* node)\r
179 {\r
180         irr::core::stringc name;\r
181         // Prefix, because xs:ID can't start with a number, also nicer name\r
182         if ( node && node->getType() == ESNT_LIGHT )\r
183                 name = "light";\r
184         else\r
185                 name = "node";\r
186         name += nameForPtr(node);\r
187         if ( node )\r
188         {\r
189                 name += irr::core::stringc(node->getName());\r
190         }\r
191         return ColladaMeshWriter->toNCName(name);\r
192 }\r
193 \r
194 irr::core::stringc CColladaMeshWriterNames::nameForMaterial(const video::SMaterial & material, int materialId, const scene::IMesh* mesh, const scene::ISceneNode* node)\r
195 {\r
196         core::stringc strMat("mat");\r
197 \r
198         bool nodeMaterial = ColladaMeshWriter->getProperties()->useNodeMaterial(node);\r
199         if ( nodeMaterial )\r
200         {\r
201                 strMat += "node";\r
202                 strMat += nameForPtr(node);\r
203                 strMat += irr::core::stringc(node->getName());\r
204         }\r
205         strMat += "mesh";\r
206         strMat += nameForPtr(mesh);\r
207         strMat += materialId;\r
208         return ColladaMeshWriter->toNCName(strMat);\r
209 }\r
210 \r
211 irr::core::stringc CColladaMeshWriterNames::nameForPtr(const void* ptr) const\r
212 {\r
213         c8 buf[32];\r
214         snprintf_irr(buf, 32, "%p", ptr);\r
215         return irr::core::stringc(buf);\r
216 }\r
217 \r
218 \r
219 \r
220 CColladaMeshWriter::CColladaMeshWriter( ISceneManager * smgr, video::IVideoDriver* driver,\r
221                                         io::IFileSystem* fs)\r
222         : FileSystem(fs), VideoDriver(driver), Writer(0)\r
223 {\r
224 \r
225         #ifdef _DEBUG\r
226         setDebugName("CColladaMeshWriter");\r
227         #endif\r
228 \r
229         if (VideoDriver)\r
230                 VideoDriver->grab();\r
231 \r
232         if (FileSystem)\r
233                 FileSystem->grab();\r
234 \r
235         if ( smgr )\r
236                 setAmbientLight( smgr->getAmbientLight() );\r
237 \r
238         // Escape some characters \r
239         // Slightly fuzzy definition for xs:anyURI.\r
240         // In theory not even spaces would need to be escaped, \r
241         // but it's strongly encouraged to do so and many Apps rely on it.\r
242         // If there are any apps out there which need more escapes we can add them.\r
243         // See https://www.w3schools.com/tags/ref_urlencode.asp for a list.\r
244         // NOTE: Never replace by empty characters (so not the place to delete chars!)\r
245         EscapeCharsAnyURI.push_back(EscapeCharacterURL(' ', "%20"));\r
246         EscapeCharsAnyURI.push_back(EscapeCharacterURL('#', "%23"));\r
247         EscapeCharsAnyURI.push_back(EscapeCharacterURL('%', "%25"));\r
248 \r
249         CColladaMeshWriterProperties * p = new CColladaMeshWriterProperties();\r
250         setDefaultProperties(p);\r
251         setProperties(p);\r
252         p->drop();\r
253 \r
254         CColladaMeshWriterNames * nameGenerator = new CColladaMeshWriterNames(this);\r
255         setDefaultNameGenerator(nameGenerator);\r
256         setNameGenerator(nameGenerator);\r
257         nameGenerator->drop();\r
258 }\r
259 \r
260 \r
261 CColladaMeshWriter::~CColladaMeshWriter()\r
262 {\r
263         if (VideoDriver)\r
264                 VideoDriver->drop();\r
265 \r
266         if (FileSystem)\r
267                 FileSystem->drop();\r
268 }\r
269 \r
270 \r
271 void CColladaMeshWriter::reset()\r
272 {\r
273         LibraryImages.clear();\r
274         Meshes.clear();\r
275         LightNodes.clear();\r
276         CameraNodes.clear();\r
277         MaterialsWritten.clear();\r
278         EffectsWritten.clear();\r
279         MaterialNameCache.clear();\r
280 }\r
281 \r
282 //! Returns the type of the mesh writer\r
283 EMESH_WRITER_TYPE CColladaMeshWriter::getType() const\r
284 {\r
285         return EMWT_COLLADA;\r
286 }\r
287 \r
288 //! writes a scene starting with the given node\r
289 bool CColladaMeshWriter::writeScene(io::IWriteFile* file, scene::ISceneNode* root, int writeRoot)\r
290 {\r
291         if (!file || !root)\r
292                 return false;\r
293 \r
294         reset();\r
295 \r
296         Writer = FileSystem->createXMLWriterUTF8(file);\r
297 \r
298         if (!Writer)\r
299         {\r
300                 os::Printer::log("Could not write file", file->getFileName());\r
301                 return false;\r
302         }\r
303 \r
304         Directory = FileSystem->getFileDir(FileSystem->getAbsolutePath( file->getFileName() ));\r
305 \r
306         // make names for all nodes with exportable meshes\r
307         makeMeshNames(root);\r
308 \r
309         os::Printer::log("Writing scene", file->getFileName());\r
310 \r
311         // write COLLADA header\r
312 \r
313         Writer->writeXMLHeader();\r
314 \r
315         Writer->writeElement("COLLADA", false,\r
316                 "xmlns", "http://www.collada.org/2005/11/COLLADASchema",\r
317                 "version", "1.4.1");\r
318         Writer->writeLineBreak();\r
319 \r
320         // write asset data\r
321         writeAsset();\r
322 \r
323         // write all materials\r
324         Writer->writeElement("library_materials", false);\r
325         Writer->writeLineBreak();\r
326         writeNodeMaterials(root);\r
327         Writer->writeClosingTag("library_materials");\r
328         Writer->writeLineBreak();\r
329 \r
330         Writer->writeElement("library_effects", false);\r
331         Writer->writeLineBreak();\r
332         writeNodeEffects(root);\r
333         Writer->writeClosingTag("library_effects");\r
334         Writer->writeLineBreak();\r
335 \r
336 \r
337         // images\r
338         writeLibraryImages();\r
339 \r
340         // lights\r
341         Writer->writeElement("library_lights", false);\r
342         Writer->writeLineBreak();\r
343 \r
344         writeAmbientLightElement( getAmbientLight() );\r
345         writeNodeLights(root);\r
346 \r
347         Writer->writeClosingTag("library_lights");\r
348         Writer->writeLineBreak();\r
349 \r
350         // cameras\r
351         Writer->writeElement("library_cameras", false);\r
352         Writer->writeLineBreak();\r
353         writeNodeCameras(root);\r
354         Writer->writeClosingTag("library_cameras");\r
355         Writer->writeLineBreak();\r
356 \r
357         // write meshes\r
358         Writer->writeElement("library_geometries", false);\r
359         Writer->writeLineBreak();\r
360         writeAllMeshGeometries();\r
361         Writer->writeClosingTag("library_geometries");\r
362         Writer->writeLineBreak();\r
363 \r
364         // write scene\r
365         Writer->writeElement("library_visual_scenes", false);\r
366         Writer->writeLineBreak();\r
367         Writer->writeElement("visual_scene", false, "id", "default_scene");\r
368         Writer->writeLineBreak();\r
369 \r
370                 // ambient light (instance_light also needs a node as parent so we have to create one)\r
371                 Writer->writeElement("node", false);\r
372                 Writer->writeLineBreak();\r
373                 Writer->writeElement("instance_light", true, "url", "#ambientlight");\r
374                 Writer->writeLineBreak();\r
375                 Writer->writeClosingTag("node");\r
376                 Writer->writeLineBreak();\r
377 \r
378                 // Write the scenegraph.\r
379                 if ( writeRoot == 2 || (writeRoot == 1 && root->getType() != ESNT_SCENE_MANAGER) )\r
380                 {\r
381                         // TODO: Not certain if we should really write the root or if we should just always only write the children.\r
382                         // For now writing root to keep backward compatibility for this case, but if anyone needs to _not_ write\r
383                         // that root-node we can add a parameter for this later on in writeScene.\r
384                         writeSceneNode(root);\r
385                 }\r
386                 else\r
387                 {\r
388                         // The visual_scene element is identical to our scenemanager and acts as root,\r
389                         // so we do not write the root itself if it points to the scenemanager.\r
390                         const core::list<ISceneNode*>& rootChildren = root->getChildren();\r
391                         for ( core::list<ISceneNode*>::ConstIterator it = rootChildren.begin();\r
392                                         it != rootChildren.end();\r
393                                         ++ it )\r
394                         {\r
395                                 writeSceneNode(*it);\r
396                         }\r
397                 }\r
398 \r
399 \r
400         Writer->writeClosingTag("visual_scene");\r
401         Writer->writeLineBreak();\r
402         Writer->writeClosingTag("library_visual_scenes");\r
403         Writer->writeLineBreak();\r
404 \r
405 \r
406         // instance scene\r
407         Writer->writeElement("scene", false);\r
408         Writer->writeLineBreak();\r
409 \r
410                 Writer->writeElement("instance_visual_scene", true, "url", "#default_scene");\r
411                 Writer->writeLineBreak();\r
412 \r
413         Writer->writeClosingTag("scene");\r
414         Writer->writeLineBreak();\r
415 \r
416 \r
417         // close everything\r
418 \r
419         Writer->writeClosingTag("COLLADA");\r
420         Writer->drop();\r
421 \r
422         return true;\r
423 }\r
424 \r
425 void CColladaMeshWriter::makeMeshNames(irr::scene::ISceneNode * node)\r
426 {\r
427         if ( !node || !getProperties() || !getProperties()->isExportable(node) || !getNameGenerator())\r
428                 return;\r
429 \r
430         IMesh* mesh = getProperties()->getMesh(node);\r
431         if ( mesh )\r
432         {\r
433                 if ( !Meshes.find(mesh) )\r
434                 {\r
435                         SColladaMesh cm;\r
436                         cm.Name = nameForMesh(mesh, 0);\r
437                         Meshes.insert(mesh, cm);\r
438                 }\r
439         }\r
440 \r
441         const core::list<ISceneNode*>& children = node->getChildren();\r
442         for ( core::list<ISceneNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )\r
443         {\r
444                 makeMeshNames(*it);\r
445         }\r
446 }\r
447 \r
448 void CColladaMeshWriter::writeNodeMaterials(irr::scene::ISceneNode * node)\r
449 {\r
450         if ( !node || !getProperties() || !getProperties()->isExportable(node) )\r
451                 return;\r
452 \r
453         core::array<irr::core::stringc> materialNames;\r
454 \r
455         IMesh* mesh = getProperties()->getMesh(node);\r
456         if ( mesh )\r
457         {\r
458                 MeshNode * n = Meshes.find(mesh);\r
459                 if ( !getProperties()->useNodeMaterial(node) )\r
460                 {\r
461                         // no material overrides - write mesh materials\r
462                         if ( n && !n->getValue().MaterialsWritten )\r
463                         {\r
464                                 writeMeshMaterials(mesh, getGeometryWriting() == ECGI_PER_MESH_AND_MATERIAL ? &materialNames : NULL);\r
465                                 n->getValue().MaterialsWritten = true;\r
466                         }\r
467                 }\r
468                 else\r
469                 {\r
470                         // write node materials\r
471                         for (u32 i=0; i<node->getMaterialCount(); ++i)\r
472                         {\r
473                                 video::SMaterial & material = node->getMaterial(i);\r
474                                 core::stringc strMat(nameForMaterial(material, i, mesh, node));\r
475                                 writeMaterial(strMat);\r
476                                 if ( getGeometryWriting() == ECGI_PER_MESH_AND_MATERIAL )\r
477                                         materialNames.push_back(strMat);\r
478                         }\r
479                 }\r
480 \r
481                 // When we write another mesh-geometry for each new material-list we have\r
482                 // to figure out here if we need another geometry copy and create a new name here.\r
483                 if ( n && getGeometryWriting() == ECGI_PER_MESH_AND_MATERIAL )\r
484                 {\r
485                         SGeometryMeshMaterials * geomMat = n->getValue().findGeometryMeshMaterials(materialNames);\r
486                         if ( geomMat )\r
487                                 geomMat->MaterialOwners.push_back(node);\r
488                         else\r
489                         {\r
490                                 SGeometryMeshMaterials gmm;\r
491                                 if ( n->getValue().GeometryMeshMaterials.empty() )\r
492                                         gmm.GeometryName = n->getValue().Name;  // first one can use the original name\r
493                                 else\r
494                                         gmm.GeometryName =      nameForMesh(mesh, n->getValue().GeometryMeshMaterials.size());\r
495                                 gmm.MaterialNames = materialNames;\r
496                                 gmm.MaterialOwners.push_back(node);\r
497                                 n->getValue().GeometryMeshMaterials.push_back(gmm);\r
498                         }\r
499                 }\r
500         }\r
501 \r
502         const core::list<ISceneNode*>& children = node->getChildren();\r
503         for ( core::list<ISceneNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )\r
504         {\r
505                 writeNodeMaterials( *it );\r
506         }\r
507 }\r
508 \r
509 void CColladaMeshWriter::writeMaterial(const irr::core::stringc& materialname)\r
510 {\r
511         if ( MaterialsWritten.find(materialname) )\r
512                 return;\r
513         MaterialsWritten.insert(materialname, true);\r
514 \r
515         Writer->writeElement("material", false,\r
516                 "id", materialname.c_str(),\r
517                 "name", materialname.c_str());\r
518         Writer->writeLineBreak();\r
519 \r
520         // We don't make a difference between material and effect on export.\r
521         // Every material is just using an instance of an effect.\r
522         core::stringc strFx(materialname);\r
523         strFx += "-fx";\r
524         Writer->writeElement("instance_effect", true,\r
525                 "url", (core::stringc("#") + strFx).c_str());\r
526         Writer->writeLineBreak();\r
527 \r
528         Writer->writeClosingTag("material");\r
529         Writer->writeLineBreak();\r
530 }\r
531 \r
532 void CColladaMeshWriter::writeNodeEffects(irr::scene::ISceneNode * node)\r
533 {\r
534         if ( !node || !getProperties() || !getProperties()->isExportable(node) || !getNameGenerator() )\r
535                 return;\r
536 \r
537         IMesh* mesh = getProperties()->getMesh(node);\r
538         if ( mesh )\r
539         {\r
540                 if ( !getProperties()->useNodeMaterial(node) )\r
541                 {\r
542                         // no material overrides - write mesh materials\r
543                         MeshNode * n = Meshes.find(mesh);\r
544                         if ( n  && !n->getValue().EffectsWritten )\r
545                         {\r
546                                 writeMeshEffects(mesh);\r
547                                 n->getValue().EffectsWritten = true;\r
548                         }\r
549                 }\r
550                 else\r
551                 {\r
552                         // write node materials\r
553                         for (u32 i=0; i<node->getMaterialCount(); ++i)\r
554                         {\r
555                                 video::SMaterial & material = node->getMaterial(i);\r
556                                 irr::core::stringc materialfxname(nameForMaterial(material, i, mesh, node));\r
557                                 materialfxname += "-fx";\r
558                                 writeMaterialEffect(materialfxname, material);\r
559                         }\r
560                 }\r
561         }\r
562 \r
563         const core::list<ISceneNode*>& children = node->getChildren();\r
564         for ( core::list<ISceneNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )\r
565         {\r
566                 writeNodeEffects( *it );\r
567         }\r
568 }\r
569 \r
570 void CColladaMeshWriter::writeNodeLights(irr::scene::ISceneNode * node)\r
571 {\r
572         if ( !node || !getProperties() || !getProperties()->isExportable(node))\r
573                 return;\r
574 \r
575         if ( node->getType() == ESNT_LIGHT )\r
576         {\r
577                 ILightSceneNode * lightNode = static_cast<ILightSceneNode*>(node);\r
578                 const video::SLight& lightData = lightNode->getLightData();\r
579 \r
580                 SColladaLight cLight;\r
581                 cLight.Name = nameForNode(node);\r
582                 LightNodes.insert(node, cLight);\r
583 \r
584                 Writer->writeElement("light", false, "id", cLight.Name.c_str());\r
585                 Writer->writeLineBreak();\r
586 \r
587                 Writer->writeElement("technique_common", false);\r
588                 Writer->writeLineBreak();\r
589 \r
590                 switch ( lightNode->getLightType() )\r
591                 {\r
592                         case video::ELT_POINT:\r
593                                 Writer->writeElement("point", false);\r
594                                 Writer->writeLineBreak();\r
595 \r
596                                 writeColorElement(lightData.DiffuseColor, false);\r
597                                 writeNode("constant_attenuation ", core::stringc(lightData.Attenuation.X).c_str());\r
598                                 writeNode("linear_attenuation  ", core::stringc(lightData.Attenuation.Y).c_str());\r
599                                 writeNode("quadratic_attenuation", core::stringc(lightData.Attenuation.Z).c_str());\r
600 \r
601                                 Writer->writeClosingTag("point");\r
602                                 Writer->writeLineBreak();\r
603                                 break;\r
604 \r
605                         case video::ELT_SPOT:\r
606                                 Writer->writeElement("spot", false);\r
607                                 Writer->writeLineBreak();\r
608 \r
609                                 writeColorElement(lightData.DiffuseColor, false);\r
610 \r
611                                 writeNode("constant_attenuation ", core::stringc(lightData.Attenuation.X).c_str());\r
612                                 writeNode("linear_attenuation  ", core::stringc(lightData.Attenuation.Y).c_str());\r
613                                 writeNode("quadratic_attenuation", core::stringc(lightData.Attenuation.Z).c_str());\r
614 \r
615                                 writeNode("falloff_angle", core::stringc(lightData.OuterCone * core::RADTODEG).c_str());\r
616                                 writeNode("falloff_exponent", core::stringc(lightData.Falloff).c_str());\r
617 \r
618                                 Writer->writeClosingTag("spot");\r
619                                 Writer->writeLineBreak();\r
620                                 break;\r
621 \r
622                         case video::ELT_DIRECTIONAL:\r
623                                 Writer->writeElement("directional", false);\r
624                                 Writer->writeLineBreak();\r
625 \r
626                                 writeColorElement(lightData.DiffuseColor, false);\r
627 \r
628                                 Writer->writeClosingTag("directional");\r
629                                 Writer->writeLineBreak();\r
630                                 break;\r
631                         default:\r
632                                 break;\r
633                 }\r
634 \r
635                 Writer->writeClosingTag("technique_common");\r
636                 Writer->writeLineBreak();\r
637 \r
638                 Writer->writeClosingTag("light");\r
639                 Writer->writeLineBreak();\r
640 \r
641         }\r
642 \r
643         const core::list<ISceneNode*>& children = node->getChildren();\r
644         for ( core::list<ISceneNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )\r
645         {\r
646                 writeNodeLights( *it );\r
647         }\r
648 }\r
649 \r
650 void CColladaMeshWriter::writeNodeCameras(irr::scene::ISceneNode * node)\r
651 {\r
652         if ( !node || !getProperties() || !getProperties()->isExportable(node) )\r
653                 return;\r
654 \r
655         if ( isCamera(node) )\r
656         {\r
657                 ICameraSceneNode * cameraNode = static_cast<ICameraSceneNode*>(node);\r
658                 irr::core::stringc name = nameForNode(node);\r
659                 CameraNodes.insert(cameraNode, name);\r
660 \r
661                 Writer->writeElement("camera", false, "id", name.c_str());\r
662                 Writer->writeLineBreak();\r
663 \r
664                 Writer->writeElement("optics", false);\r
665                 Writer->writeLineBreak();\r
666 \r
667                 Writer->writeElement("technique_common", false);\r
668                 Writer->writeLineBreak();\r
669 \r
670                 if ( cameraNode->isOrthogonal() )\r
671                 {\r
672                         Writer->writeElement("orthographic", false);\r
673                         Writer->writeLineBreak();\r
674 \r
675                         irr::core::matrix4 projMat( cameraNode->getProjectionMatrix() );\r
676                         irr::f32 xmag = 2.f/projMat[0];\r
677                         irr::f32 ymag = 2.f/projMat[5];\r
678 \r
679                         // Note that Irrlicht camera does not update near/far when setting the projection matrix,\r
680                         // so we have to calculate that here (at least currently - maybe camera code will be updated at some time).\r
681                         irr::f32 nearMinusFar = -1.f/projMat[10];\r
682                         irr::f32 zNear = projMat[14]*nearMinusFar;\r
683                         irr::f32 zFar = 1.f/projMat[10] + zNear;\r
684 \r
685                         writeNode("xmag", core::stringc(xmag).c_str());\r
686                         writeNode("ymag", core::stringc(ymag).c_str());\r
687                         writeNode("znear", core::stringc(zNear).c_str());\r
688                         writeNode("zfar", core::stringc(zFar).c_str());\r
689 \r
690                         Writer->writeClosingTag("orthographic");\r
691                         Writer->writeLineBreak();\r
692                 }\r
693                 else\r
694                 {\r
695                         Writer->writeElement("perspective", false);\r
696                         Writer->writeLineBreak();\r
697 \r
698                         writeNode("yfov", core::stringc(cameraNode->getFOV()*core::RADTODEG).c_str());\r
699                         writeNode("aspect_ratio", core::stringc(cameraNode->getAspectRatio()).c_str());\r
700                         writeNode("znear", core::stringc(cameraNode->getNearValue()).c_str());\r
701                         writeNode("zfar", core::stringc(cameraNode->getFarValue()).c_str());\r
702 \r
703                         Writer->writeClosingTag("perspective");\r
704                         Writer->writeLineBreak();\r
705                 }\r
706 \r
707                 Writer->writeClosingTag("technique_common");\r
708                 Writer->writeLineBreak();\r
709 \r
710                 Writer->writeClosingTag("optics");\r
711                 Writer->writeLineBreak();\r
712 \r
713                 Writer->writeClosingTag("camera");\r
714                 Writer->writeLineBreak();\r
715         }\r
716 \r
717         const core::list<ISceneNode*>& children = node->getChildren();\r
718         for ( core::list<ISceneNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )\r
719         {\r
720                 writeNodeCameras( *it );\r
721         }\r
722 }\r
723 \r
724 void CColladaMeshWriter::writeAllMeshGeometries()\r
725 {\r
726         core::map<IMesh*, SColladaMesh>::ConstIterator it = Meshes.getConstIterator();\r
727         for(; !it.atEnd(); it++ )\r
728         {\r
729                 IMesh* mesh = it->getKey();\r
730                 const SColladaMesh& colladaMesh = it->getValue();\r
731 \r
732                 if ( getGeometryWriting() == ECGI_PER_MESH_AND_MATERIAL && colladaMesh.GeometryMeshMaterials.size() > 1 )\r
733                 {\r
734                         for ( u32 i=0; i<colladaMesh.GeometryMeshMaterials.size(); ++i )\r
735                         {\r
736                                 writeMeshGeometry(colladaMesh.GeometryMeshMaterials[i].GeometryName, mesh);\r
737                         }\r
738                 }\r
739                 else\r
740                 {\r
741                         writeMeshGeometry(colladaMesh.Name, mesh);\r
742                 }\r
743         }\r
744 }\r
745 \r
746 void CColladaMeshWriter::writeSceneNode(irr::scene::ISceneNode * node )\r
747 {\r
748         if ( !node || !getProperties() || !getProperties()->isExportable(node) )\r
749                 return;\r
750 \r
751         // Collada doesn't require to set the id, but some other tools have problems if none exists, so we just add it.\r
752         irr::core::stringc nameId(nameForNode(node));\r
753         Writer->writeElement("node", false, "id", nameId.c_str());\r
754         Writer->writeLineBreak();\r
755 \r
756         // DummyTransformationSceneNode don't have rotation, position, scale information\r
757         // But also don't always export the transformation matrix as that forces us creating\r
758         // new DummyTransformationSceneNode's on import.\r
759         if ( node->getType() == ESNT_DUMMY_TRANSFORMATION )\r
760         {\r
761                 writeMatrixElement(node->getRelativeTransformation());\r
762         }\r
763         else if ( isCamera(node) )\r
764         {\r
765                 // TODO: We do not handle the case when ICameraSceneNode::getTargetAndRotationBinding() is false. Probably we would have to create a second\r
766                 // node to do that.\r
767 \r
768                 // Note: We can't use rotations for the camera as Irrlicht does not regard the up-vector in rotations so far.\r
769                 // We could maybe use projection matrices, but avoiding them might allow us to get rid of some DummyTransformationSceneNodes on\r
770                 // import in the future. So that's why we use the lookat element instead.\r
771 \r
772                 ICameraSceneNode * camNode = static_cast<ICameraSceneNode*>(node);\r
773                 writeLookAtElement(camNode->getPosition(), camNode->getTarget(), camNode->getUpVector());\r
774         }\r
775         else\r
776         {\r
777                 writeTranslateElement( node->getPosition() );\r
778 \r
779                 irr::core::vector3df rot(node->getRotation());\r
780                 core::quaternion quat(rot*core::DEGTORAD);\r
781                 f32 angle;\r
782                 core::vector3df axis;\r
783                 quat.toAngleAxis(angle, axis);\r
784                 writeRotateElement( axis, angle*core::RADTODEG );\r
785 \r
786                 writeScaleElement( node->getScale() );\r
787         }\r
788 \r
789         // instance geometry\r
790         IMesh* mesh = getProperties()->getMesh(node);\r
791         if ( mesh )\r
792         {\r
793                 MeshNode * n = Meshes.find(mesh);\r
794                 if ( n )\r
795                 {\r
796                         const SColladaMesh& colladaMesh = n->getValue();\r
797                         writeMeshInstanceGeometry(colladaMesh.findGeometryNameForNode(node), mesh, node);\r
798                 }\r
799         }\r
800 \r
801         // instance light\r
802         if ( node->getType() == ESNT_LIGHT )\r
803         {\r
804                 LightNode * n = LightNodes.find(node);\r
805                 if ( n )\r
806                         writeLightInstance(n->getValue().Name);\r
807         }\r
808 \r
809         // instance camera\r
810         if ( isCamera(node) )\r
811         {\r
812                 CameraNode * camNode = CameraNodes.find(node);\r
813                 if ( camNode )\r
814                         writeCameraInstance(camNode->getValue());\r
815         }\r
816 \r
817         const core::list<ISceneNode*>& children = node->getChildren();\r
818         for ( core::list<ISceneNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )\r
819         {\r
820                 writeSceneNode( *it );\r
821         }\r
822 \r
823         Writer->writeClosingTag("node");\r
824         Writer->writeLineBreak();\r
825 }\r
826 \r
827 //! writes a mesh\r
828 bool CColladaMeshWriter::writeMesh(io::IWriteFile* file, scene::IMesh* mesh, s32 flags)\r
829 {\r
830         if (!file)\r
831                 return false;\r
832 \r
833         reset();\r
834 \r
835         Writer = FileSystem->createXMLWriterUTF8(file);\r
836 \r
837         if (!Writer)\r
838         {\r
839                 os::Printer::log("Could not write file", file->getFileName());\r
840                 return false;\r
841         }\r
842 \r
843         Directory = FileSystem->getFileDir(FileSystem->getAbsolutePath( file->getFileName() ));\r
844 \r
845         os::Printer::log("Writing mesh", file->getFileName());\r
846 \r
847         // write COLLADA header\r
848 \r
849         Writer->writeXMLHeader();\r
850 \r
851         Writer->writeElement("COLLADA", false,\r
852                 "xmlns", "http://www.collada.org/2005/11/COLLADASchema",\r
853                 "version", "1.4.1");\r
854         Writer->writeLineBreak();\r
855 \r
856         // write asset data\r
857         writeAsset();\r
858 \r
859         // write all materials\r
860 \r
861         Writer->writeElement("library_materials", false);\r
862         Writer->writeLineBreak();\r
863 \r
864         writeMeshMaterials(mesh);\r
865 \r
866         Writer->writeClosingTag("library_materials");\r
867         Writer->writeLineBreak();\r
868 \r
869         Writer->writeElement("library_effects", false);\r
870         Writer->writeLineBreak();\r
871 \r
872         writeMeshEffects(mesh);\r
873 \r
874         Writer->writeClosingTag("library_effects");\r
875         Writer->writeLineBreak();\r
876 \r
877         // images\r
878         writeLibraryImages();\r
879 \r
880         // write mesh\r
881 \r
882         Writer->writeElement("library_geometries", false);\r
883         Writer->writeLineBreak();\r
884 \r
885         irr::core::stringc meshname(nameForMesh(mesh, 0));\r
886         writeMeshGeometry(meshname, mesh);\r
887 \r
888         Writer->writeClosingTag("library_geometries");\r
889         Writer->writeLineBreak();\r
890 \r
891         // write scene_library\r
892         if ( getWriteDefaultScene() )\r
893         {\r
894                 Writer->writeElement("library_visual_scenes", false);\r
895                 Writer->writeLineBreak();\r
896 \r
897                 Writer->writeElement("visual_scene", false, "id", "default_scene");\r
898                 Writer->writeLineBreak();\r
899 \r
900                         Writer->writeElement("node", false);\r
901                         Writer->writeLineBreak();\r
902 \r
903                                 writeMeshInstanceGeometry(meshname, mesh);\r
904 \r
905                         Writer->writeClosingTag("node");\r
906                         Writer->writeLineBreak();\r
907 \r
908                 Writer->writeClosingTag("visual_scene");\r
909                 Writer->writeLineBreak();\r
910 \r
911                 Writer->writeClosingTag("library_visual_scenes");\r
912                 Writer->writeLineBreak();\r
913 \r
914 \r
915                 // write scene\r
916                 Writer->writeElement("scene", false);\r
917                 Writer->writeLineBreak();\r
918 \r
919                         Writer->writeElement("instance_visual_scene", true, "url", "#default_scene");\r
920                         Writer->writeLineBreak();\r
921 \r
922                 Writer->writeClosingTag("scene");\r
923                 Writer->writeLineBreak();\r
924         }\r
925 \r
926 \r
927         // close everything\r
928 \r
929         Writer->writeClosingTag("COLLADA");\r
930         Writer->drop();\r
931 \r
932         return true;\r
933 }\r
934 \r
935 void CColladaMeshWriter::writeMeshInstanceGeometry(const irr::core::stringc& meshname, scene::IMesh* mesh, scene::ISceneNode* node)\r
936 {\r
937         //<instance_geometry url="#mesh">\r
938         Writer->writeElement("instance_geometry", false, "url", toRef(meshname).c_str());\r
939         Writer->writeLineBreak();\r
940 \r
941                 Writer->writeElement("bind_material", false);\r
942                 Writer->writeLineBreak();\r
943 \r
944                         Writer->writeElement("technique_common", false);\r
945                         Writer->writeLineBreak();\r
946 \r
947                         // instance materials\r
948                         // <instance_material symbol="leaf" target="#MidsummerLeaf01"/>\r
949                         bool useNodeMaterials = node && node->getMaterialCount() == mesh->getMeshBufferCount();\r
950                         for (u32 i=0; i<mesh->getMeshBufferCount(); ++i)\r
951                         {\r
952                                 irr::core::stringc strMatSymbol(nameForMaterialSymbol(mesh, i));\r
953                                 core::stringc strMatTarget = "#";\r
954                                 video::SMaterial & material = useNodeMaterials ? node->getMaterial(i) : mesh->getMeshBuffer(i)->getMaterial();\r
955                                 strMatTarget += nameForMaterial(material, i, mesh, node);\r
956                                 Writer->writeElement("instance_material", false, "symbol", strMatSymbol.c_str(), "target", strMatTarget.c_str());\r
957                                 Writer->writeLineBreak();\r
958 \r
959                                         // TODO: need to handle second UV-set\r
960                                         // <bind_vertex_input semantic="uv" input_semantic="TEXCOORD" input_set="0"/>\r
961                                         Writer->writeElement("bind_vertex_input", true, "semantic", "uv", "input_semantic", "TEXCOORD", "input_set", "0" );\r
962                                         Writer->writeLineBreak();\r
963 \r
964                                 Writer->writeClosingTag("instance_material");\r
965                                 Writer->writeLineBreak();\r
966                         }\r
967 \r
968                         Writer->writeClosingTag("technique_common");\r
969                         Writer->writeLineBreak();\r
970 \r
971                 Writer->writeClosingTag("bind_material");\r
972                 Writer->writeLineBreak();\r
973 \r
974         Writer->writeClosingTag("instance_geometry");\r
975         Writer->writeLineBreak();\r
976 }\r
977 \r
978 void CColladaMeshWriter::writeLightInstance(const irr::core::stringc& lightName)\r
979 {\r
980         Writer->writeElement("instance_light", true, "url", toRef(lightName).c_str());\r
981         Writer->writeLineBreak();\r
982 }\r
983 \r
984 void CColladaMeshWriter::writeCameraInstance(const irr::core::stringc& cameraName)\r
985 {\r
986         Writer->writeElement("instance_camera", true, "url", toRef(cameraName).c_str());\r
987         Writer->writeLineBreak();\r
988 }\r
989 \r
990 bool CColladaMeshWriter::hasSecondTextureCoordinates(video::E_VERTEX_TYPE type) const\r
991 {\r
992         return type == video::EVT_2TCOORDS;\r
993 }\r
994 \r
995 void CColladaMeshWriter::writeVector(const irr::core::vector3df& vec)\r
996 {\r
997         c8 tmpbuf[255];\r
998 \r
999         snprintf_irr(tmpbuf, 255, "%f", vec.X);\r
1000         WriteBuffer = tmpbuf;\r
1001         WriteBuffer.eraseTrailingFloatZeros();\r
1002 \r
1003         snprintf_irr(tmpbuf, 255, " %f", vec.Y);\r
1004         WriteBuffer.append(tmpbuf);\r
1005         WriteBuffer.eraseTrailingFloatZeros();\r
1006 \r
1007         snprintf_irr(tmpbuf, 255, " %f", vec.Z*-1.f);   //      change handedness\r
1008         WriteBuffer.append(tmpbuf);\r
1009         WriteBuffer.eraseTrailingFloatZeros();\r
1010 \r
1011         Writer->writeText(WriteBuffer.c_str());\r
1012 }\r
1013 \r
1014 void CColladaMeshWriter::writeUv(const irr::core::vector2df& vec)\r
1015 {\r
1016         c8 tmpbuf[255];\r
1017 \r
1018         snprintf_irr(tmpbuf, 255, "%f", vec.X);\r
1019         WriteBuffer = tmpbuf;\r
1020         WriteBuffer.eraseTrailingFloatZeros();\r
1021 \r
1022         snprintf_irr(tmpbuf, 255, " %f", 1.f-vec.Y);    //      change handedness\r
1023         WriteBuffer.append(tmpbuf);\r
1024         WriteBuffer.eraseTrailingFloatZeros();\r
1025 \r
1026         Writer->writeText(WriteBuffer.c_str());\r
1027 }\r
1028 \r
1029 void CColladaMeshWriter::writeColor(const irr::video::SColorf& colorf, bool writeAlpha)\r
1030 {\r
1031         c8 tmpbuf[255];\r
1032 \r
1033         snprintf_irr(tmpbuf, 255, "%f", colorf.getRed());\r
1034         WriteBuffer = tmpbuf;\r
1035         WriteBuffer.eraseTrailingFloatZeros();\r
1036 \r
1037         snprintf_irr(tmpbuf, 255, " %f", colorf.getGreen());\r
1038         WriteBuffer.append(tmpbuf);\r
1039         WriteBuffer.eraseTrailingFloatZeros();\r
1040 \r
1041         snprintf_irr(tmpbuf, 255, " %f", colorf.getBlue());\r
1042         WriteBuffer.append(tmpbuf);\r
1043         WriteBuffer.eraseTrailingFloatZeros();\r
1044 \r
1045         if ( writeAlpha )\r
1046         {\r
1047                 snprintf_irr(tmpbuf, 255, " %f", colorf.getAlpha());\r
1048                 WriteBuffer.append(tmpbuf);\r
1049                 WriteBuffer.eraseTrailingFloatZeros();\r
1050         }\r
1051 \r
1052         Writer->writeText(WriteBuffer.c_str());\r
1053 }\r
1054 \r
1055 irr::core::stringc CColladaMeshWriter::toString(const irr::video::ECOLOR_FORMAT format) const\r
1056 {\r
1057         switch ( format )\r
1058         {\r
1059                 case video::ECF_A1R5G5B5:       return irr::core::stringc("A1R5G5B5");\r
1060                 case video::ECF_R5G6B5:         return irr::core::stringc("R5G6B5");\r
1061                 case video::ECF_R8G8B8:         return irr::core::stringc("R8G8B8");\r
1062                 case video::ECF_A8R8G8B8:       return irr::core::stringc("A8R8G8B8");\r
1063                 default:                                        return irr::core::stringc("");\r
1064         }\r
1065 }\r
1066 \r
1067 irr::core::stringc CColladaMeshWriter::toString(const irr::video::E_TEXTURE_CLAMP clamp) const\r
1068 {\r
1069         switch ( clamp )\r
1070         {\r
1071                 case video::ETC_REPEAT:\r
1072                         return core::stringc("WRAP");\r
1073                 case video::ETC_CLAMP:\r
1074                 case video::ETC_CLAMP_TO_EDGE:\r
1075                         return core::stringc("CLAMP");\r
1076                 case video::ETC_CLAMP_TO_BORDER:\r
1077                         return core::stringc("BORDER");\r
1078                 case video::ETC_MIRROR:\r
1079                 case video::ETC_MIRROR_CLAMP:\r
1080                 case video::ETC_MIRROR_CLAMP_TO_EDGE:\r
1081                 case video::ETC_MIRROR_CLAMP_TO_BORDER:\r
1082                         return core::stringc("MIRROR");\r
1083         }\r
1084         return core::stringc("NONE");\r
1085 }\r
1086 \r
1087 irr::core::stringc CColladaMeshWriter::toString(const irr::scene::E_COLLADA_TRANSPARENT_FX transparent) const\r
1088 {\r
1089         if ( transparent & ECOF_RGB_ZERO )\r
1090                 return core::stringc("RGB_ZERO");\r
1091         else\r
1092                 return core::stringc("A_ONE");\r
1093 }\r
1094 \r
1095 irr::core::stringc CColladaMeshWriter::toRef(const irr::core::stringc& source) const\r
1096 {\r
1097         irr::core::stringc ref("#");\r
1098         ref += source;\r
1099         return ref;\r
1100 }\r
1101 \r
1102 bool CColladaMeshWriter::isCamera(const scene::ISceneNode* node) const\r
1103 {\r
1104         // TODO: we need some ISceneNode::hasType() function to get rid of those checks\r
1105         if (    node->getType() == ESNT_CAMERA\r
1106                 ||      node->getType() == ESNT_CAMERA_MAYA\r
1107                 ||      node->getType() == ESNT_CAMERA_FPS )\r
1108                 return true;\r
1109         return false;\r
1110 }\r
1111 \r
1112 irr::core::stringc CColladaMeshWriter::nameForMesh(const scene::IMesh* mesh, int instance) const\r
1113 {\r
1114         IColladaMeshWriterNames * nameGenerator = getNameGenerator();\r
1115         if ( nameGenerator )\r
1116         {\r
1117                 return nameGenerator->nameForMesh(mesh, instance);\r
1118         }\r
1119         return irr::core::stringc("missing_name_generator");\r
1120 }\r
1121 \r
1122 irr::core::stringc CColladaMeshWriter::nameForNode(const scene::ISceneNode* node) const\r
1123 {\r
1124         IColladaMeshWriterNames * nameGenerator = getNameGenerator();\r
1125         if ( nameGenerator )\r
1126         {\r
1127                 return nameGenerator->nameForNode(node);\r
1128         }\r
1129         return irr::core::stringc("missing_name_generator");\r
1130 }\r
1131 \r
1132 irr::core::stringc CColladaMeshWriter::nameForMaterial(const video::SMaterial & material, int materialId, const scene::IMesh* mesh, const scene::ISceneNode* node)\r
1133 {\r
1134         irr::core::stringc matName;\r
1135         if ( getExportSMaterialsOnlyOnce() )\r
1136         {\r
1137                 matName = findCachedMaterialName(material);\r
1138                 if ( !matName.empty() )\r
1139                         return matName;\r
1140         }\r
1141 \r
1142         IColladaMeshWriterNames * nameGenerator = getNameGenerator();\r
1143         if ( nameGenerator )\r
1144         {\r
1145                 matName = nameGenerator->nameForMaterial(material, materialId, mesh, node);\r
1146         }\r
1147         else\r
1148                 matName = irr::core::stringc("missing_name_generator");\r
1149 \r
1150         if ( getExportSMaterialsOnlyOnce() )\r
1151                 MaterialNameCache.push_back (MaterialName(material, matName));\r
1152         return matName;\r
1153 }\r
1154 \r
1155 // Each mesh-material has one symbol which is replaced on instantiation\r
1156 irr::core::stringc CColladaMeshWriter::nameForMaterialSymbol(const scene::IMesh* mesh, int materialId) const\r
1157 {\r
1158         c8 buf[100];\r
1159         snprintf_irr(buf, 100, "mat_symb_%p_%d", mesh, materialId);\r
1160         return irr::core::stringc(buf);\r
1161 }\r
1162 \r
1163 irr::core::stringc CColladaMeshWriter::findCachedMaterialName(const irr::video::SMaterial& material) const\r
1164 {\r
1165         for ( u32 i=0; i<MaterialNameCache.size(); ++i )\r
1166         {\r
1167                 if ( MaterialNameCache[i].Material == material )\r
1168                         return MaterialNameCache[i].Name;\r
1169         }\r
1170         return irr::core::stringc();\r
1171 }\r
1172 \r
1173 irr::core::stringc CColladaMeshWriter::minTexfilterToString(bool bilinear, bool trilinear) const\r
1174 {\r
1175         if ( trilinear )\r
1176                 return core::stringc("LINEAR_MIPMAP_LINEAR");\r
1177         else if ( bilinear )\r
1178                 return core::stringc("LINEAR_MIPMAP_NEAREST");\r
1179 \r
1180         return core::stringc("NONE");\r
1181 }\r
1182 \r
1183 inline irr::core::stringc CColladaMeshWriter::magTexfilterToString(bool bilinear, bool trilinear) const\r
1184 {\r
1185         if ( bilinear || trilinear )\r
1186                 return core::stringc("LINEAR");\r
1187 \r
1188         return core::stringc("NONE");\r
1189 }\r
1190 \r
1191 bool CColladaMeshWriter::isXmlNameStartChar(c8 c) const\r
1192 {\r
1193         return     (c >= 'A' && c <= 'Z')\r
1194                         ||      c == '_'\r
1195                         ||      (c >= 'a' && c <= 'z');\r
1196                         /*      Following would also be legal, but only when using real unicode.\r
1197                                 We do only check ansi codes as they are sufficient for us.\r
1198                         ||      (c >= 0xC0 && c <= 0xD6)\r
1199                         ||      (c >= 0xD8 && c <= 0xF6)\r
1200                         ||      (c >= 0xF8 && c <= 0x2FF)\r
1201                         ||      (c >= 0x370 && c <= 0x37D)\r
1202                         ||  (c >= 0x37F && c <= 0x1FFF)\r
1203                         ||  (c >= 0x200C && c <= 0x200D)\r
1204                         ||  (c >= 0x2070 && c <= 0x218F)\r
1205                         ||  (c >= 0x2C00 && c <= 0x2FEF)\r
1206                         ||  (c >= 0x3001 && c <= 0xD7FF)\r
1207                         ||  (c >= 0xF900 && c <= 0xFDCF)\r
1208                         ||  (c >= 0xFDF0 && c <= 0xFFFD)\r
1209                         ||  (c >= 0x10000 && c <=0xEFFFF)\r
1210 */\r
1211                         ;\r
1212 }\r
1213 \r
1214 bool CColladaMeshWriter::isXmlNameChar(c8 c) const\r
1215 {\r
1216         return isXmlNameStartChar(c)\r
1217                 ||      c == '-'\r
1218                 ||      c == '.'\r
1219                 ||      (c >= '0' && c <= '9');\r
1220                 /*      Following would also be legal, but only when using real unicode.\r
1221                         We do only check ansi codes for now as they are sufficient for us.\r
1222                  ||     c == 0xB7\r
1223                  ||     (c >= 9x0300 && c <= 0x036F)\r
1224                  ||     (c >= 0x203F && c <= 0x2040)\r
1225                  */\r
1226 }\r
1227 \r
1228 // Restrict the characters to a set of allowed characters in xs:NCName.\r
1229 irr::core::stringc CColladaMeshWriter::toNCName(const irr::core::stringc& oldString, const irr::core::stringc& prefix) const\r
1230 {\r
1231         irr::core::stringc result(prefix);      // help to ensure id starts with a valid char and reduce chance of name-conflicts\r
1232         if ( oldString.empty() )\r
1233                 return result;\r
1234 \r
1235         result.append( oldString );\r
1236 \r
1237         // We replace all characters not allowed by a replacement char\r
1238         const c8 REPLACMENT = '-';\r
1239         for ( irr::u32 i=1; i < result.size(); ++i )\r
1240         {\r
1241                 if ( result[i] == ':' || !isXmlNameChar(result[i]) )\r
1242                 {\r
1243                         result[i] = REPLACMENT;\r
1244                 }\r
1245         }\r
1246         return result;\r
1247 }\r
1248 \r
1249 const irr::core::stringc* CColladaMeshWriter::findGeometryNameForNode(ISceneNode* node)\r
1250 {\r
1251         IMesh* mesh = getProperties()->getMesh(node);\r
1252         if ( !mesh )\r
1253                 return NULL;\r
1254 \r
1255         MeshNode * n = Meshes.find(mesh);\r
1256         if ( !n )\r
1257                 return NULL;\r
1258 \r
1259         const SColladaMesh& colladaMesh = n->getValue();\r
1260         return &colladaMesh.findGeometryNameForNode(node);\r
1261 }\r
1262 \r
1263 // Restrict the characters to a set of allowed characters in xs:anyURI\r
1264 irr::core::stringc CColladaMeshWriter::pathToURI(const irr::io::path& path) const\r
1265 {\r
1266         irr::core::stringc result;\r
1267 \r
1268         // is this a relative path?\r
1269         if ( path.size() > 1\r
1270                 && path[0] != _IRR_TEXT('/')\r
1271                 && path[0] != _IRR_TEXT('\\')\r
1272                 && path[1] != _IRR_TEXT(':') )\r
1273         {\r
1274                 // not already starting with "./" ?\r
1275                 if (    path[0] != _IRR_TEXT('.')\r
1276                         ||      path[1] != _IRR_TEXT('/') )\r
1277                 {\r
1278                         result.append("./");\r
1279                 }\r
1280         }\r
1281         result.append(path);\r
1282 \r
1283         // Make correct URI (without whitespace)\r
1284         u32 len = result.size();\r
1285         for (u32 i=0; i<len; ++i)\r
1286         {\r
1287                 for (u32 e = 0; e < EscapeCharsAnyURI.size(); ++e)\r
1288                 {\r
1289                         if (result[i] == EscapeCharsAnyURI[e].Character)\r
1290                         {\r
1291                                 // escape characters should always be at least 3 characters\r
1292                                 const u32 addLen = EscapeCharsAnyURI[e].Escape.size() - 1;\r
1293                                 result[i] = EscapeCharsAnyURI[e].Escape[0];     // replace first one\r
1294                                 result.insert(i+1, &EscapeCharsAnyURI[e].Escape[1], addLen); // insert rest\r
1295                                 i += addLen;\r
1296                                 len += addLen;\r
1297                                 break;\r
1298                         }\r
1299                 }\r
1300         }\r
1301 \r
1302 \r
1303         return result;\r
1304 }\r
1305 \r
1306 void CColladaMeshWriter::writeAsset()\r
1307 {\r
1308         Writer->writeElement("asset", false);\r
1309         Writer->writeLineBreak();\r
1310 \r
1311         Writer->writeElement("contributor", false);\r
1312         Writer->writeLineBreak();\r
1313         Writer->writeElement("authoring_tool", false);\r
1314         Writer->writeText("Irrlicht Engine");\r
1315         Writer->writeClosingTag("authoring_tool");\r
1316         Writer->writeLineBreak();\r
1317         Writer->writeClosingTag("contributor");\r
1318         Writer->writeLineBreak();\r
1319 \r
1320         // The next two are required\r
1321         Writer->writeElement("created", false);\r
1322         Writer->writeText("2008-01-31T00:00:00Z");\r
1323         Writer->writeClosingTag("created");\r
1324         Writer->writeLineBreak();\r
1325 \r
1326         Writer->writeElement("modified", false);\r
1327         Writer->writeText("2008-01-31T00:00:00Z");\r
1328         Writer->writeClosingTag("modified");\r
1329         Writer->writeLineBreak();\r
1330 \r
1331         // Revision 2.0 changes (since 1.0):\r
1332         // - All coordinates are now written with right-handed coordinate system.\r
1333         //   Before only texture V of first textures was swapped and all other \r
1334         //   parameters where exported left-handed.\r
1335         //   For specific changes change svn revision 5708.\r
1336         // - authoring_tool no longer mentions IrrEdit (this code has originated \r
1337         //   from irrEdit 0.7) to avoid conflicts as the software is now\r
1338         //   independent of each other and we're not aware of irrEdit revision numbers.\r
1339         Writer->writeElement("revision", false);\r
1340         Writer->writeText("2.0");       \r
1341         Writer->writeClosingTag("revision");\r
1342         Writer->writeLineBreak();\r
1343 \r
1344         Writer->writeElement("unit", true, "meter", core::stringc(getUnitMeter()).eraseTrailingFloatZeros().c_str(), "name", getUnitName().c_str());\r
1345         Writer->writeLineBreak();\r
1346 \r
1347         Writer->writeClosingTag("asset");\r
1348         Writer->writeLineBreak();\r
1349 }\r
1350 \r
1351 void CColladaMeshWriter::writeMeshMaterials(scene::IMesh* mesh, irr::core::array<irr::core::stringc> * materialNamesOut)\r
1352 {\r
1353         u32 i;\r
1354         for (i=0; i<mesh->getMeshBufferCount(); ++i)\r
1355         {\r
1356                 video::SMaterial & material = mesh->getMeshBuffer(i)->getMaterial();\r
1357                 core::stringc strMat(nameForMaterial(material, i, mesh, NULL));\r
1358                 writeMaterial(strMat);\r
1359                 if ( materialNamesOut )\r
1360                         materialNamesOut->push_back(strMat);\r
1361         }\r
1362 }\r
1363 \r
1364 void CColladaMeshWriter::writeMaterialEffect(const irr::core::stringc& materialfxname, const video::SMaterial & material)\r
1365 {\r
1366         if ( EffectsWritten.find(materialfxname) )\r
1367                 return;\r
1368         EffectsWritten.insert(materialfxname, true);\r
1369 \r
1370         Writer->writeElement("effect", false,\r
1371                 "id", materialfxname.c_str(),\r
1372                 "name", materialfxname.c_str());\r
1373         Writer->writeLineBreak();\r
1374         Writer->writeElement("profile_COMMON", false);\r
1375         Writer->writeLineBreak();\r
1376 \r
1377         int numTextures = 0;\r
1378         if ( getWriteTextures() )\r
1379         {\r
1380                 // write texture surfaces and samplers and buffer all used imagess\r
1381                 for ( int t=0; t<4; ++t )\r
1382                 {\r
1383                         const video::SMaterialLayer& layer  = material.TextureLayer[t];\r
1384                         if ( !layer.Texture )\r
1385                                 break;\r
1386                         ++numTextures;\r
1387 \r
1388                         if ( LibraryImages.linear_search(layer.Texture) < 0 )\r
1389                                         LibraryImages.push_back( layer.Texture );\r
1390 \r
1391                         irr::core::stringc texName("tex");\r
1392                         texName += irr::core::stringc(t);\r
1393 \r
1394                         // write texture surface\r
1395                         //<newparam sid="tex0-surface">\r
1396                         irr::core::stringc texSurface(texName);\r
1397                         texSurface += "-surface";\r
1398                         Writer->writeElement("newparam", false, "sid", texSurface.c_str());\r
1399                         Writer->writeLineBreak();\r
1400                         //  <surface type="2D">\r
1401                                 Writer->writeElement("surface", false, "type", "2D");\r
1402                                 Writer->writeLineBreak();\r
1403 \r
1404                 //          <init_from>internal_texturename</init_from>\r
1405                                         Writer->writeElement("init_from", false);\r
1406                                         irr::io::path p(FileSystem->getRelativeFilename(layer.Texture->getName().getPath(), Directory));\r
1407                                         Writer->writeText(toNCName(irr::core::stringc(p)).c_str());     // same ID for internal name as in writeLibraryImages\r
1408                                         Writer->writeClosingTag("init_from");\r
1409                                         Writer->writeLineBreak();\r
1410 \r
1411                 //          <format>A8R8G8B8</format>\r
1412                                         Writer->writeElement("format", false);\r
1413                                         video::ECOLOR_FORMAT format = layer.Texture->getColorFormat();\r
1414                                         Writer->writeText(toString(format).c_str());\r
1415                                         Writer->writeClosingTag("format");\r
1416                                         Writer->writeLineBreak();\r
1417                 //      </surface>\r
1418                                 Writer->writeClosingTag("surface");\r
1419                                 Writer->writeLineBreak();\r
1420                 //  </newparam>\r
1421                         Writer->writeClosingTag("newparam");\r
1422                         Writer->writeLineBreak();\r
1423 \r
1424                         // write texture sampler\r
1425                 //  <newparam sid="tex0-sampler">\r
1426                         irr::core::stringc texSampler(texName);\r
1427                         texSampler += "-sampler";\r
1428                         Writer->writeElement("newparam", false, "sid", texSampler.c_str());\r
1429                         Writer->writeLineBreak();\r
1430                 //      <sampler2D>\r
1431                                 Writer->writeElement("sampler2D", false);\r
1432                                 Writer->writeLineBreak();\r
1433 \r
1434                 //          <source>tex0-surface</source>\r
1435                                         Writer->writeElement("source", false);\r
1436                                         Writer->writeText(texSurface.c_str());\r
1437                                         Writer->writeClosingTag("source");\r
1438                                         Writer->writeLineBreak();\r
1439 \r
1440                 //                      <wrap_s>WRAP</wrap_s>\r
1441                                         Writer->writeElement("wrap_s", false);\r
1442                                         Writer->writeText(toString((video::E_TEXTURE_CLAMP)layer.TextureWrapU).c_str());\r
1443                                         Writer->writeClosingTag("wrap_s");\r
1444                                         Writer->writeLineBreak();\r
1445 \r
1446                 //                      <wrap_t>WRAP</wrap_t>\r
1447                                         Writer->writeElement("wrap_t", false);\r
1448                                         Writer->writeText(toString((video::E_TEXTURE_CLAMP)layer.TextureWrapV).c_str());\r
1449                                         Writer->writeClosingTag("wrap_t");\r
1450                                         Writer->writeLineBreak();\r
1451 \r
1452                 //                      <wrap_p>WRAP</wrap_p>   // TODO: Should only be written in Collada 1.5\r
1453                                         Writer->writeElement("wrap_p", false);\r
1454                                         Writer->writeText(toString((video::E_TEXTURE_CLAMP)layer.TextureWrapW).c_str());\r
1455                                         Writer->writeClosingTag("wrap_p");\r
1456                                         Writer->writeLineBreak();\r
1457 \r
1458                 //                      <minfilter>LINEAR_MIPMAP_LINEAR</minfilter>\r
1459                                         Writer->writeElement("minfilter", false);\r
1460                                         Writer->writeText(minTexfilterToString(layer.BilinearFilter, layer.TrilinearFilter).c_str());\r
1461                                         Writer->writeClosingTag("minfilter");\r
1462                                         Writer->writeLineBreak();\r
1463 \r
1464                 //                      <magfilter>LINEAR</magfilter>\r
1465                                         Writer->writeElement("magfilter", false);\r
1466                                         Writer->writeText(magTexfilterToString(layer.BilinearFilter, layer.TrilinearFilter).c_str());\r
1467                                         Writer->writeClosingTag("magfilter");\r
1468                                         Writer->writeLineBreak();\r
1469 \r
1470                                         // TBD - actually not sure how anisotropic should be written, so for now it writes in a way\r
1471                                         // that works with the way the loader reads it again.\r
1472                                         if ( layer.AnisotropicFilter )\r
1473                                         {\r
1474                 //                      <mipfilter>LINEAR_MIPMAP_LINEAR</mipfilter>\r
1475                                                 Writer->writeElement("mipfilter", false);\r
1476                                                 Writer->writeText("LINEAR_MIPMAP_LINEAR");\r
1477                                                 Writer->writeClosingTag("mipfilter");\r
1478                                                 Writer->writeLineBreak();\r
1479                                         }\r
1480 \r
1481                 //     </sampler2D>\r
1482                                 Writer->writeClosingTag("sampler2D");\r
1483                                 Writer->writeLineBreak();\r
1484                 //  </newparam>\r
1485                         Writer->writeClosingTag("newparam");\r
1486                         Writer->writeLineBreak();\r
1487                 }\r
1488         }\r
1489 \r
1490         Writer->writeElement("technique", false, "sid", "common");\r
1491         Writer->writeLineBreak();\r
1492 \r
1493         E_COLLADA_TECHNIQUE_FX techFx = getProperties() ? getProperties()->getTechniqueFx(material) : ECTF_BLINN;\r
1494         writeFxElement(material, techFx);\r
1495 \r
1496         Writer->writeClosingTag("technique");\r
1497         Writer->writeLineBreak();\r
1498         Writer->writeClosingTag("profile_COMMON");\r
1499         Writer->writeLineBreak();\r
1500         Writer->writeClosingTag("effect");\r
1501         Writer->writeLineBreak();\r
1502 }\r
1503 \r
1504 void CColladaMeshWriter::writeMeshEffects(scene::IMesh* mesh)\r
1505 {\r
1506         for (u32 i=0; i<mesh->getMeshBufferCount(); ++i)\r
1507         {\r
1508                 video::SMaterial & material = mesh->getMeshBuffer(i)->getMaterial();\r
1509                 irr::core::stringc materialfxname(nameForMaterial(material, i, mesh, NULL));\r
1510                 materialfxname += "-fx";\r
1511                 writeMaterialEffect(materialfxname, material);\r
1512         }\r
1513 }\r
1514 \r
1515 void CColladaMeshWriter::writeMeshGeometry(const irr::core::stringc& meshname, scene::IMesh* mesh)\r
1516 {\r
1517         core::stringc meshId(meshname);\r
1518 \r
1519         Writer->writeElement("geometry", false, "id", meshId.c_str(), "name", meshId.c_str());\r
1520         Writer->writeLineBreak();\r
1521         Writer->writeElement("mesh");\r
1522         Writer->writeLineBreak();\r
1523 \r
1524         // do some statistics for the mesh to know which stuff needs to be saved into\r
1525         // the file:\r
1526         // - count vertices\r
1527         // - check for the need of a second texture coordinate\r
1528         // - count amount of second texture coordinates\r
1529         // - check for the need of tangents (TODO)\r
1530 \r
1531         u32 totalVertexCount = 0;\r
1532         u32 totalTCoords2Count = 0;\r
1533         bool needsTangents = false; // TODO: tangents not supported here yet\r
1534         u32 i=0;\r
1535         for (i=0; i<mesh->getMeshBufferCount(); ++i)\r
1536         {\r
1537                 totalVertexCount += mesh->getMeshBuffer(i)->getVertexCount();\r
1538 \r
1539                 if (hasSecondTextureCoordinates(mesh->getMeshBuffer(i)->getVertexType()))\r
1540                         totalTCoords2Count += mesh->getMeshBuffer(i)->getVertexCount();\r
1541 \r
1542                 if (!needsTangents)\r
1543                         needsTangents = mesh->getMeshBuffer(i)->getVertexType() == video::EVT_TANGENTS;\r
1544         }\r
1545 \r
1546         const irr::u32 mbCount = mesh->getMeshBufferCount();\r
1547         SComponentGlobalStartPos* globalIndices = new SComponentGlobalStartPos[mbCount];\r
1548 \r
1549         // write positions\r
1550         core::stringc meshPosId(meshId);\r
1551         meshPosId += "-Pos";\r
1552         Writer->writeElement("source", false, "id", meshPosId.c_str());\r
1553         Writer->writeLineBreak();\r
1554 \r
1555                 core::stringc vertexCountStr(totalVertexCount*3);\r
1556                 core::stringc meshPosArrayId(meshPosId);\r
1557                 meshPosArrayId += "-array";\r
1558                 Writer->writeElement("float_array", false, "id", meshPosArrayId.c_str(),\r
1559                                         "count", vertexCountStr.c_str());\r
1560                 Writer->writeLineBreak();\r
1561 \r
1562                 for (i=0; i<mbCount; ++i)\r
1563                 {\r
1564                         scene::IMeshBuffer* buffer = mesh->getMeshBuffer(i);\r
1565                         u32 vertexCount = buffer->getVertexCount();\r
1566 \r
1567                         if ( i == 0 )\r
1568                                 globalIndices[i].PosStartIndex = 0;\r
1569 \r
1570                         if (i+1 < mbCount)\r
1571                                 globalIndices[i+1].PosStartIndex = globalIndices[i].PosStartIndex + vertexCount;\r
1572 \r
1573                         u8* vertices = static_cast<u8*>(buffer->getVertices());\r
1574                         u32 vertexPitch = getVertexPitchFromType(buffer->getVertexType());\r
1575                         for (u32 j=0; j<vertexCount; ++j)\r
1576                         {\r
1577                                 writeVector( (*reinterpret_cast<const video::S3DVertex*>(&vertices[j*vertexPitch])).Pos );\r
1578                                 Writer->writeLineBreak();\r
1579                         }\r
1580                 }\r
1581 \r
1582                 Writer->writeClosingTag("float_array");\r
1583                 Writer->writeLineBreak();\r
1584 \r
1585                 Writer->writeElement("technique_common", false);\r
1586                 Writer->writeLineBreak();\r
1587 \r
1588                 vertexCountStr = core::stringc(totalVertexCount);\r
1589 \r
1590                         Writer->writeElement("accessor", false, "source", toRef(meshPosArrayId).c_str(),\r
1591                                                 "count", vertexCountStr.c_str(), "stride", "3");\r
1592                         Writer->writeLineBreak();\r
1593 \r
1594                                 Writer->writeElement("param", true, "name", "X", "type", "float");\r
1595                                 Writer->writeLineBreak();\r
1596                                 Writer->writeElement("param", true, "name", "Y", "type", "float");\r
1597                                 Writer->writeLineBreak();\r
1598                                 Writer->writeElement("param", true, "name", "Z", "type", "float");\r
1599                                 Writer->writeLineBreak();\r
1600 \r
1601                                 Writer->writeClosingTag("accessor");\r
1602                                 Writer->writeLineBreak();\r
1603 \r
1604                 Writer->writeClosingTag("technique_common");\r
1605                 Writer->writeLineBreak();\r
1606 \r
1607         Writer->writeClosingTag("source");\r
1608         Writer->writeLineBreak();\r
1609 \r
1610         // write texture coordinates\r
1611 \r
1612         core::stringc meshTexCoord0Id(meshId);\r
1613         meshTexCoord0Id += "-TexCoord0";\r
1614         Writer->writeElement("source", false, "id", meshTexCoord0Id.c_str());\r
1615         Writer->writeLineBreak();\r
1616 \r
1617                 vertexCountStr = core::stringc(totalVertexCount*2);\r
1618                 core::stringc meshTexCoordArrayId(meshTexCoord0Id);\r
1619                 meshTexCoordArrayId += "-array";\r
1620                 Writer->writeElement("float_array", false, "id", meshTexCoordArrayId.c_str(),\r
1621                                         "count", vertexCountStr.c_str());\r
1622                 Writer->writeLineBreak();\r
1623 \r
1624                 for (i=0; i<mbCount; ++i)\r
1625                 {\r
1626                         scene::IMeshBuffer* buffer = mesh->getMeshBuffer(i);\r
1627                         u32 vertexCount = buffer->getVertexCount();\r
1628 \r
1629                         if (i==0)\r
1630                                 globalIndices[i].TCoord0StartIndex = 0;\r
1631 \r
1632                         if (i+1 < mbCount)\r
1633                                 globalIndices[i+1].TCoord0StartIndex = globalIndices[i].TCoord0StartIndex + vertexCount;\r
1634 \r
1635                         u8* vertices = static_cast<u8*>(buffer->getVertices());\r
1636                         u32 vertexPitch = getVertexPitchFromType(buffer->getVertexType());\r
1637                         for (u32 j=0; j<vertexCount; ++j)\r
1638                         {\r
1639                                 writeUv( (*reinterpret_cast<const video::S3DVertex*>(&vertices[j*vertexPitch])).TCoords );\r
1640                                 Writer->writeLineBreak();\r
1641                         }\r
1642                 }\r
1643 \r
1644                 Writer->writeClosingTag("float_array");\r
1645                 Writer->writeLineBreak();\r
1646 \r
1647                 Writer->writeElement("technique_common", false);\r
1648                 Writer->writeLineBreak();\r
1649 \r
1650                 vertexCountStr = core::stringc(totalVertexCount);\r
1651 \r
1652                         Writer->writeElement("accessor", false, "source", toRef(meshTexCoordArrayId).c_str(),\r
1653                                                 "count", vertexCountStr.c_str(), "stride", "2");\r
1654                         Writer->writeLineBreak();\r
1655 \r
1656                                 Writer->writeElement("param", true, "name", ParamNamesUV[0].c_str(), "type", "float");\r
1657                                 Writer->writeLineBreak();\r
1658                                 Writer->writeElement("param", true, "name", ParamNamesUV[1].c_str(), "type", "float");\r
1659                                 Writer->writeLineBreak();\r
1660 \r
1661                         Writer->writeClosingTag("accessor");\r
1662                         Writer->writeLineBreak();\r
1663 \r
1664                 Writer->writeClosingTag("technique_common");\r
1665                 Writer->writeLineBreak();\r
1666 \r
1667         Writer->writeClosingTag("source");\r
1668         Writer->writeLineBreak();\r
1669 \r
1670         // write normals\r
1671         core::stringc meshNormalId(meshId);\r
1672         meshNormalId += "-Normal";\r
1673         Writer->writeElement("source", false, "id", meshNormalId.c_str());\r
1674         Writer->writeLineBreak();\r
1675 \r
1676                 vertexCountStr = core::stringc(totalVertexCount*3);\r
1677                 core::stringc meshNormalArrayId(meshNormalId);\r
1678                 meshNormalArrayId += "-array";\r
1679                 Writer->writeElement("float_array", false, "id", meshNormalArrayId.c_str(),\r
1680                                         "count", vertexCountStr.c_str());\r
1681                 Writer->writeLineBreak();\r
1682 \r
1683                 for (i=0; i<mbCount; ++i)\r
1684                 {\r
1685                         scene::IMeshBuffer* buffer = mesh->getMeshBuffer(i);\r
1686                         u32 vertexCount = buffer->getVertexCount();\r
1687 \r
1688                         if ( i==0 )\r
1689                                 globalIndices[i].NormalStartIndex = 0;\r
1690 \r
1691                         if (i+1 < mbCount)\r
1692                                 globalIndices[i+1].NormalStartIndex = globalIndices[i].NormalStartIndex + vertexCount;\r
1693 \r
1694                         u8* vertices = static_cast<u8*>(buffer->getVertices());\r
1695                         u32 vertexPitch = getVertexPitchFromType(buffer->getVertexType());\r
1696                         for (u32 j=0; j<vertexCount; ++j)\r
1697                         {\r
1698                                 writeVector( (*reinterpret_cast<const video::S3DVertex*>(&vertices[j*vertexPitch])).Normal );\r
1699                                 Writer->writeLineBreak();\r
1700                         }\r
1701                 }\r
1702 \r
1703                 Writer->writeClosingTag("float_array");\r
1704                 Writer->writeLineBreak();\r
1705 \r
1706                 Writer->writeElement("technique_common", false);\r
1707                 Writer->writeLineBreak();\r
1708 \r
1709                 vertexCountStr = core::stringc(totalVertexCount);\r
1710 \r
1711                 Writer->writeElement("accessor", false, "source", toRef(meshNormalArrayId).c_str(),\r
1712                                                                         "count", vertexCountStr.c_str(), "stride", "3");\r
1713                         Writer->writeLineBreak();\r
1714 \r
1715                                 Writer->writeElement("param", true, "name", "X", "type", "float");\r
1716                                 Writer->writeLineBreak();\r
1717                                 Writer->writeElement("param", true, "name", "Y", "type", "float");\r
1718                                 Writer->writeLineBreak();\r
1719                                 Writer->writeElement("param", true, "name", "Z", "type", "float");\r
1720                                 Writer->writeLineBreak();\r
1721 \r
1722                         Writer->writeClosingTag("accessor");\r
1723                         Writer->writeLineBreak();\r
1724 \r
1725                 Writer->writeClosingTag("technique_common");\r
1726                 Writer->writeLineBreak();\r
1727 \r
1728         Writer->writeClosingTag("source");\r
1729         Writer->writeLineBreak();\r
1730 \r
1731         // write second set of texture coordinates\r
1732         core::stringc meshTexCoord1Id(meshId);\r
1733         meshTexCoord1Id += "-TexCoord1";\r
1734         if (totalTCoords2Count)\r
1735         {\r
1736                 Writer->writeElement("source", false, "id", meshTexCoord1Id.c_str());\r
1737                 Writer->writeLineBreak();\r
1738 \r
1739                         vertexCountStr = core::stringc(totalTCoords2Count*2);\r
1740                         core::stringc meshTexCoord1ArrayId(meshTexCoord1Id);\r
1741                         meshTexCoord1ArrayId += "-array";\r
1742                         Writer->writeElement("float_array", false, "id", meshTexCoord1ArrayId.c_str(),\r
1743                                                                         "count", vertexCountStr.c_str());\r
1744                         Writer->writeLineBreak();\r
1745 \r
1746                         for (i=0; i<mbCount; ++i)\r
1747                         {\r
1748                                 scene::IMeshBuffer* buffer = mesh->getMeshBuffer(i);\r
1749                                 video::E_VERTEX_TYPE vtxType = buffer->getVertexType();\r
1750                                 u32 vertexCount = 0;\r
1751 \r
1752                                 if (hasSecondTextureCoordinates(vtxType))\r
1753                                 {\r
1754                                         vertexCount = buffer->getVertexCount();\r
1755                                         switch(vtxType)\r
1756                                         {\r
1757                                         case video::EVT_2TCOORDS:\r
1758                                                 {\r
1759                                                         video::S3DVertex2TCoords* vtx = (video::S3DVertex2TCoords*)buffer->getVertices();\r
1760                                                         for (u32 j=0; j<vertexCount; ++j)\r
1761                                                         {\r
1762                                                                 writeUv(vtx[j].TCoords2);\r
1763                                                                 Writer->writeLineBreak();\r
1764                                                         }\r
1765                                                 }\r
1766                                                 break;\r
1767                                         default:\r
1768                                                 break;\r
1769                                         }\r
1770                                 } // end this buffer has 2 texture coordinates\r
1771 \r
1772                                 if ( i == 0 )\r
1773                                         globalIndices[i].TCoord1StartIndex = 0;\r
1774 \r
1775                                 if (i+1 < mbCount)\r
1776                                         globalIndices[i+1].TCoord1StartIndex = globalIndices[i].TCoord1StartIndex + vertexCount;\r
1777                         }\r
1778 \r
1779                         Writer->writeClosingTag("float_array");\r
1780                         Writer->writeLineBreak();\r
1781 \r
1782                         Writer->writeElement("technique_common", false);\r
1783                         Writer->writeLineBreak();\r
1784 \r
1785                         vertexCountStr = core::stringc(totalTCoords2Count);\r
1786 \r
1787                                 Writer->writeElement("accessor", false, "source", toRef(meshTexCoord1ArrayId).c_str(),\r
1788                                                                                 "count", vertexCountStr.c_str(), "stride", "2");\r
1789                                 Writer->writeLineBreak();\r
1790 \r
1791                                         Writer->writeElement("param", true, "name", ParamNamesUV[0].c_str(), "type", "float");\r
1792                                         Writer->writeLineBreak();\r
1793                                         Writer->writeElement("param", true, "name", ParamNamesUV[1].c_str(), "type", "float");\r
1794                                         Writer->writeLineBreak();\r
1795 \r
1796                                 Writer->writeClosingTag("accessor");\r
1797                                 Writer->writeLineBreak();\r
1798 \r
1799                         Writer->writeClosingTag("technique_common");\r
1800                         Writer->writeLineBreak();\r
1801 \r
1802                 Writer->writeClosingTag("source");\r
1803                 Writer->writeLineBreak();\r
1804         }\r
1805 \r
1806         // write tangents\r
1807 \r
1808         // TODO\r
1809 \r
1810         // write vertices\r
1811         core::stringc meshVtxId(meshId);\r
1812         meshVtxId += "-Vtx";\r
1813         Writer->writeElement("vertices", false, "id", meshVtxId.c_str());\r
1814         Writer->writeLineBreak();\r
1815 \r
1816                 Writer->writeElement("input", true, "semantic", "POSITION", "source", toRef(meshPosId).c_str());\r
1817                 Writer->writeLineBreak();\r
1818 \r
1819         Writer->writeClosingTag("vertices");\r
1820         Writer->writeLineBreak();\r
1821 \r
1822         // write polygons\r
1823 \r
1824         for (i=0; i<mbCount; ++i)\r
1825         {\r
1826                 scene::IMeshBuffer* buffer = mesh->getMeshBuffer(i);\r
1827 \r
1828                 if ( buffer->getPrimitiveType() != EPT_TRIANGLES )\r
1829                 {\r
1830                         os::Printer::log("Collada writer does not support non-triangle meshbuffers. Mesh: ", meshname.c_str(), ELL_WARNING);\r
1831                         continue;\r
1832                 }\r
1833 \r
1834                 const u32 polyCount = buffer->getPrimitiveCount();\r
1835                 core::stringc strPolyCount(polyCount);\r
1836                 irr::core::stringc strMat(nameForMaterialSymbol(mesh, i));\r
1837 \r
1838                 Writer->writeElement("triangles", false, "count", strPolyCount.c_str(),\r
1839                                                                 "material", strMat.c_str());\r
1840                 Writer->writeLineBreak();\r
1841 \r
1842                 Writer->writeElement("input", true, "semantic", "VERTEX", "source", toRef(meshVtxId).c_str(), "offset", "0");\r
1843                 Writer->writeLineBreak();\r
1844                 Writer->writeElement("input", true, "semantic", "TEXCOORD", "source", toRef(meshTexCoord0Id).c_str(), "offset", "1", "set", "0");\r
1845                 Writer->writeLineBreak();\r
1846                 Writer->writeElement("input", true, "semantic", "NORMAL", "source", toRef(meshNormalId).c_str(), "offset", "2");\r
1847                 Writer->writeLineBreak();\r
1848 \r
1849                 bool has2ndTexCoords = hasSecondTextureCoordinates(buffer->getVertexType());\r
1850                 if (has2ndTexCoords)\r
1851                 {\r
1852                         // TODO: when working on second uv-set - my suspicion is that this one should be called "TEXCOORD2"\r
1853                         // to allow bind_vertex_input to differentiate the uv-sets.\r
1854                         Writer->writeElement("input", true, "semantic", "TEXCOORD", "source", toRef(meshTexCoord1Id).c_str(), "idx", "3");\r
1855                         Writer->writeLineBreak();\r
1856                 }\r
1857 \r
1858                 // write indices now\r
1859 \r
1860                 // In Collada we us a single global buffer for all vertices, so indices have this offset compared to Irrlicht\r
1861                 u32 posIdx = globalIndices[i].PosStartIndex;\r
1862                 u32 tCoordIdx = globalIndices[i].TCoord0StartIndex;\r
1863                 u32 normalIdx = globalIndices[i].NormalStartIndex;\r
1864                 u32 tCoord2Idx = globalIndices[i].TCoord1StartIndex;\r
1865 \r
1866                 Writer->writeElement("p", false);\r
1867 \r
1868                 core::stringc strP;\r
1869                 strP.reserve(100);\r
1870                 for (u32 p=0; p<polyCount; ++p)\r
1871                 {\r
1872                         // Irrlicht uses clockwise, Collada uses counter-clockwise to define front-face\r
1873                         u32 irrIdx = buffer->getIndices()[(p*3) + 2];\r
1874                         strP = "";\r
1875                         strP += irrIdx + posIdx;\r
1876                         strP += " ";\r
1877                         strP += irrIdx + tCoordIdx;\r
1878                         strP += " ";\r
1879                         strP += irrIdx + normalIdx;\r
1880                         strP += " ";\r
1881                         if (has2ndTexCoords)\r
1882                         {\r
1883                                 strP += irrIdx + tCoord2Idx;\r
1884                                 strP += " ";\r
1885                         }\r
1886 \r
1887                         irrIdx = buffer->getIndices()[(p*3) + 1];\r
1888                         strP += irrIdx + posIdx;\r
1889                         strP += " ";\r
1890                         strP += irrIdx + tCoordIdx;\r
1891                         strP += " ";\r
1892                         strP += irrIdx + normalIdx;\r
1893                         strP += " ";\r
1894                         if (has2ndTexCoords)\r
1895                         {\r
1896                                 strP += irrIdx + tCoord2Idx;\r
1897                                 strP += " ";\r
1898                         }\r
1899 \r
1900                         irrIdx = buffer->getIndices()[(p*3) + 0];\r
1901                         strP += irrIdx + posIdx;\r
1902                         strP += " ";\r
1903                         strP += irrIdx + tCoordIdx;\r
1904                         strP += " ";\r
1905                         strP += irrIdx + normalIdx;\r
1906                         if (has2ndTexCoords)\r
1907                         {\r
1908                                 strP += " ";\r
1909                                 strP += irrIdx + tCoord2Idx;\r
1910                         }\r
1911                         strP += " ";\r
1912 \r
1913                         Writer->writeText(strP.c_str());\r
1914                 }\r
1915 \r
1916                 Writer->writeClosingTag("p");\r
1917                 Writer->writeLineBreak();\r
1918 \r
1919                 // close index buffer section\r
1920 \r
1921                 Writer->writeClosingTag("triangles");\r
1922                 Writer->writeLineBreak();\r
1923         }\r
1924 \r
1925         // close mesh and geometry\r
1926         delete [] globalIndices;\r
1927         Writer->writeClosingTag("mesh");\r
1928         Writer->writeLineBreak();\r
1929         Writer->writeClosingTag("geometry");\r
1930         Writer->writeLineBreak();\r
1931 }\r
1932 \r
1933 void CColladaMeshWriter::writeLibraryImages()\r
1934 {\r
1935         if ( getWriteTextures() && !LibraryImages.empty() )\r
1936         {\r
1937                 Writer->writeElement("library_images", false);\r
1938                 Writer->writeLineBreak();\r
1939 \r
1940                 for ( irr::u32 i=0; i<LibraryImages.size(); ++i )\r
1941                 {\r
1942                         irr::io::path p(FileSystem->getRelativeFilename(LibraryImages[i]->getName().getPath(), Directory));\r
1943                         //<image name="rose01">\r
1944                         irr::core::stringc ncname( toNCName(irr::core::stringc(p)) );\r
1945                         Writer->writeElement("image", false, "id", ncname.c_str(), "name", ncname.c_str());\r
1946                         Writer->writeLineBreak();\r
1947                         //  <init_from>../flowers/rose01.jpg</init_from>\r
1948                                 Writer->writeElement("init_from", false);\r
1949                                 Writer->writeText(pathToURI(p).c_str());\r
1950                                 Writer->writeClosingTag("init_from");\r
1951                                 Writer->writeLineBreak();\r
1952                         //  </image>\r
1953                         Writer->writeClosingTag("image");\r
1954                         Writer->writeLineBreak();\r
1955                 }\r
1956 \r
1957                 Writer->writeClosingTag("library_images");\r
1958                 Writer->writeLineBreak();\r
1959         }\r
1960 }\r
1961 \r
1962 void CColladaMeshWriter::writeColorElement(const video::SColorf & col, bool writeAlpha)\r
1963 {\r
1964         Writer->writeElement("color", false);\r
1965 \r
1966         writeColor(col, writeAlpha);\r
1967 \r
1968         Writer->writeClosingTag("color");\r
1969         Writer->writeLineBreak();\r
1970 }\r
1971 \r
1972 void CColladaMeshWriter::writeColorElement(const video::SColor & col, bool writeAlpha)\r
1973 {\r
1974         writeColorElement( video::SColorf(col), writeAlpha );\r
1975 }\r
1976 \r
1977 void CColladaMeshWriter::writeAmbientLightElement(const video::SColorf & col)\r
1978 {\r
1979         Writer->writeElement("light", false, "id", "ambientlight");\r
1980         Writer->writeLineBreak();\r
1981 \r
1982                 Writer->writeElement("technique_common", false);\r
1983                 Writer->writeLineBreak();\r
1984 \r
1985                         Writer->writeElement("ambient", false);\r
1986                         Writer->writeLineBreak();\r
1987 \r
1988                                 writeColorElement(col, false);\r
1989 \r
1990                         Writer->writeClosingTag("ambient");\r
1991                         Writer->writeLineBreak();\r
1992 \r
1993                 Writer->writeClosingTag("technique_common");\r
1994                 Writer->writeLineBreak();\r
1995 \r
1996         Writer->writeClosingTag("light");\r
1997         Writer->writeLineBreak();\r
1998 }\r
1999 \r
2000 s32 CColladaMeshWriter::getCheckedTextureIdx(const video::SMaterial & material, E_COLLADA_COLOR_SAMPLER cs)\r
2001 {\r
2002         if (    !getWriteTextures()\r
2003                 ||      !getProperties() )\r
2004                 return -1;\r
2005 \r
2006         s32 idx = getProperties()->getTextureIdx(material, cs);\r
2007         if ( idx >= 0 && !material.TextureLayer[idx].Texture )\r
2008                 return -1;\r
2009 \r
2010         return idx;\r
2011 }\r
2012 \r
2013 video::SColor CColladaMeshWriter::getColorMapping(const video::SMaterial & material, E_COLLADA_COLOR_SAMPLER cs, E_COLLADA_IRR_COLOR colType)\r
2014 {\r
2015         switch ( colType )\r
2016         {\r
2017                 case ECIC_NONE:\r
2018                         return video::SColor(255, 0, 0, 0);\r
2019 \r
2020                 case ECIC_CUSTOM:\r
2021                         return getProperties()->getCustomColor(material, cs);\r
2022 \r
2023                 case ECIC_DIFFUSE:\r
2024                         return material.DiffuseColor;\r
2025 \r
2026                 case ECIC_AMBIENT:\r
2027                         return material.AmbientColor;\r
2028 \r
2029                 case ECIC_EMISSIVE:\r
2030                         return material.EmissiveColor;\r
2031 \r
2032                 case ECIC_SPECULAR:\r
2033                         return material.SpecularColor;\r
2034         }\r
2035         return video::SColor(255, 0, 0, 0);\r
2036 }\r
2037 \r
2038 void CColladaMeshWriter::writeTextureSampler(s32 textureIdx)\r
2039 {\r
2040         irr::core::stringc sampler("tex");\r
2041         sampler += irr::core::stringc(textureIdx);\r
2042         sampler += "-sampler";\r
2043 \r
2044         // <texture texture="sampler" texcoord="texCoordUv"/>\r
2045         Writer->writeElement("texture", true, "texture", sampler.c_str(), "texcoord", "uv" );\r
2046         Writer->writeLineBreak();\r
2047 }\r
2048 \r
2049 void CColladaMeshWriter::writeFxElement(const video::SMaterial & material, E_COLLADA_TECHNIQUE_FX techFx)\r
2050 {\r
2051         core::stringc fxLabel;\r
2052         bool writeEmission = true;\r
2053         bool writeAmbient = true;\r
2054         bool writeDiffuse = true;\r
2055         bool writeSpecular = true;\r
2056         bool writeShininess = true;\r
2057         bool writeReflective = true;\r
2058         bool writeReflectivity = true;\r
2059         bool writeTransparent = true;\r
2060         bool writeTransparency = true;\r
2061         bool writeIndexOfRefraction = true;\r
2062         switch ( techFx )\r
2063         {\r
2064                 case ECTF_BLINN:\r
2065                         fxLabel = "blinn";\r
2066                         break;\r
2067                 case ECTF_PHONG:\r
2068                         fxLabel = "phong";\r
2069                         break;\r
2070                 case ECTF_LAMBERT:\r
2071                         fxLabel = "lambert";\r
2072                         writeSpecular = false;\r
2073                         writeShininess = false;\r
2074                         break;\r
2075                 case ECTF_CONSTANT:\r
2076                         fxLabel = "constant";\r
2077                         writeAmbient = false;\r
2078                         writeDiffuse = false;\r
2079                         writeSpecular = false;\r
2080                         writeShininess = false;\r
2081                         break;\r
2082         }\r
2083 \r
2084         Writer->writeElement(fxLabel.c_str(), false);\r
2085         Writer->writeLineBreak();\r
2086 \r
2087         // write all interesting material parameters\r
2088         // attributes must be written in fixed order\r
2089         if ( getProperties() )\r
2090         {\r
2091                 if ( writeEmission )\r
2092                 {\r
2093                         writeColorFx(material, "emission", ECCS_EMISSIVE);\r
2094                 }\r
2095 \r
2096                 if ( writeAmbient )\r
2097                 {\r
2098                         writeColorFx(material, "ambient", ECCS_AMBIENT);\r
2099                 }\r
2100 \r
2101                 if ( writeDiffuse )\r
2102                 {\r
2103                         writeColorFx(material, "diffuse", ECCS_DIFFUSE);\r
2104                 }\r
2105 \r
2106                 if ( writeSpecular )\r
2107                 {\r
2108                         writeColorFx(material, "specular", ECCS_SPECULAR);\r
2109                 }\r
2110 \r
2111                 if ( writeShininess )\r
2112                 {\r
2113                         Writer->writeElement("shininess", false);\r
2114                         Writer->writeLineBreak();\r
2115                         writeFloatElement(material.Shininess);\r
2116                         Writer->writeClosingTag("shininess");\r
2117                         Writer->writeLineBreak();\r
2118                 }\r
2119 \r
2120                 if ( writeReflective )\r
2121                 {\r
2122                         writeColorFx(material, "reflective", ECCS_REFLECTIVE);\r
2123                 }\r
2124 \r
2125                 if ( writeReflectivity )\r
2126                 {\r
2127                         f32 t = getProperties()->getReflectivity(material);\r
2128                         if ( t >= 0.f )\r
2129                         {\r
2130                                 // <transparency>  <float>1.000000</float> </transparency>\r
2131                                 Writer->writeElement("reflectivity", false);\r
2132                                 Writer->writeLineBreak();\r
2133                                 writeFloatElement(t);\r
2134                                 Writer->writeClosingTag("reflectivity");\r
2135                                 Writer->writeLineBreak();\r
2136                         }\r
2137                 }\r
2138 \r
2139                 if ( writeTransparent )\r
2140                 {\r
2141                         E_COLLADA_TRANSPARENT_FX transparentFx = getProperties()->getTransparentFx(material);\r
2142                         writeColorFx(material, "transparent", ECCS_TRANSPARENT, "opaque", toString(transparentFx).c_str());\r
2143                 }\r
2144 \r
2145                 if ( writeTransparency  )\r
2146                 {\r
2147                         f32 t = getProperties()->getTransparency(material);\r
2148                         if ( t >= 0.f )\r
2149                         {\r
2150                                 // <transparency>  <float>1.000000</float> </transparency>\r
2151                                 Writer->writeElement("transparency", false);\r
2152                                 Writer->writeLineBreak();\r
2153                                 writeFloatElement(t);\r
2154                                 Writer->writeClosingTag("transparency");\r
2155                                 Writer->writeLineBreak();\r
2156                         }\r
2157                 }\r
2158 \r
2159                 if ( writeIndexOfRefraction )\r
2160                 {\r
2161                         f32 t = getProperties()->getIndexOfRefraction(material);\r
2162                         if ( t >= 0.f )\r
2163                         {\r
2164                                 Writer->writeElement("index_of_refraction", false);\r
2165                                 Writer->writeLineBreak();\r
2166                                 writeFloatElement(t);\r
2167                                 Writer->writeClosingTag("index_of_refraction");\r
2168                                 Writer->writeLineBreak();\r
2169                         }\r
2170                 }\r
2171         }\r
2172 \r
2173 \r
2174         Writer->writeClosingTag(fxLabel.c_str());\r
2175         Writer->writeLineBreak();\r
2176 }\r
2177 \r
2178 void CColladaMeshWriter::writeColorFx(const video::SMaterial & material, const c8 * colorname, E_COLLADA_COLOR_SAMPLER cs, const c8* attr1Name, const c8* attr1Value)\r
2179 {\r
2180         irr::s32 idx = getCheckedTextureIdx(material, cs);\r
2181         E_COLLADA_IRR_COLOR colType = idx < 0 ? getProperties()->getColorMapping(material, cs) : ECIC_NONE;\r
2182         if ( idx >= 0 || colType != ECIC_NONE )\r
2183         {\r
2184                 Writer->writeElement(colorname, false, attr1Name, attr1Value);\r
2185                 Writer->writeLineBreak();\r
2186                 if ( idx >= 0 )\r
2187                         writeTextureSampler(idx);\r
2188                 else\r
2189                         writeColorElement(getColorMapping(material, cs, colType));\r
2190                 Writer->writeClosingTag(colorname);\r
2191                 Writer->writeLineBreak();\r
2192         }\r
2193 }\r
2194 \r
2195 void CColladaMeshWriter::writeNode(const c8 * nodeName, const c8 * content)\r
2196 {\r
2197         Writer->writeElement(nodeName, false);\r
2198         Writer->writeText(content);\r
2199         Writer->writeClosingTag(nodeName);\r
2200         Writer->writeLineBreak();\r
2201 }\r
2202 \r
2203 void CColladaMeshWriter::writeFloatElement(irr::f32 value)\r
2204 {\r
2205         Writer->writeElement("float", false);\r
2206         Writer->writeText(core::stringc((double)value).eraseTrailingFloatZeros().c_str());\r
2207         Writer->writeClosingTag("float");\r
2208         Writer->writeLineBreak();\r
2209 }\r
2210 \r
2211 void CColladaMeshWriter::writeRotateElement(const irr::core::vector3df& axis, irr::f32 angle)\r
2212 {\r
2213         Writer->writeElement("rotate", false);\r
2214         irr::core::stringc txt(axis.X);\r
2215         txt.eraseTrailingFloatZeros();\r
2216         txt += " ";\r
2217         txt += irr::core::stringc(axis.Y);\r
2218         txt.eraseTrailingFloatZeros();\r
2219         txt += " ";\r
2220         txt += irr::core::stringc(axis.Z * -1.f);\r
2221         txt.eraseTrailingFloatZeros();\r
2222         txt += " ";\r
2223         txt += irr::core::stringc((double)angle * -1.f);\r
2224         txt.eraseTrailingFloatZeros();\r
2225         Writer->writeText(txt.c_str());\r
2226         Writer->writeClosingTag("rotate");\r
2227         Writer->writeLineBreak();\r
2228 }\r
2229 \r
2230 void CColladaMeshWriter::writeScaleElement(const irr::core::vector3df& scale)\r
2231 {\r
2232         Writer->writeElement("scale", false);\r
2233         irr::core::stringc txt(scale.X);\r
2234         txt.eraseTrailingFloatZeros();\r
2235         txt += " ";\r
2236         txt += irr::core::stringc(scale.Y);\r
2237         txt.eraseTrailingFloatZeros();\r
2238         txt += " ";\r
2239         txt += irr::core::stringc(scale.Z);\r
2240         txt.eraseTrailingFloatZeros();\r
2241         Writer->writeText(txt.c_str());\r
2242         Writer->writeClosingTag("scale");\r
2243         Writer->writeLineBreak();\r
2244 }\r
2245 \r
2246 void CColladaMeshWriter::writeTranslateElement(const irr::core::vector3df& translate)\r
2247 {\r
2248         Writer->writeElement("translate", false);\r
2249         irr::core::stringc txt(translate.X);\r
2250         txt.eraseTrailingFloatZeros();\r
2251         txt += " ";\r
2252         txt += irr::core::stringc(translate.Y);\r
2253         txt.eraseTrailingFloatZeros();\r
2254         txt += " ";\r
2255         txt += irr::core::stringc(translate.Z*-1.f);\r
2256         txt.eraseTrailingFloatZeros();\r
2257         Writer->writeText(txt.c_str());\r
2258         Writer->writeClosingTag("translate");\r
2259         Writer->writeLineBreak();\r
2260 }\r
2261 \r
2262 void CColladaMeshWriter::writeLookAtElement(const irr::core::vector3df& eyePos, const irr::core::vector3df& targetPos, const irr::core::vector3df& upVector)\r
2263 {\r
2264         Writer->writeElement("lookat", false);\r
2265 \r
2266         c8 tmpbuf[255];\r
2267         snprintf_irr(tmpbuf, 255, "%f %f %f %f %f %f %f %f %f", eyePos.X, eyePos.Y, eyePos.Z*-1.f, targetPos.X, targetPos.Y, targetPos.Z*-1.f, upVector.X, upVector.Y, upVector.Z*-1.f);\r
2268         Writer->writeText(tmpbuf);\r
2269 \r
2270         Writer->writeClosingTag("lookat");\r
2271         Writer->writeLineBreak();\r
2272 }\r
2273 \r
2274 void CColladaMeshWriter::writeMatrixElement(const irr::core::matrix4& matrixIrr)\r
2275 {\r
2276         irr::core::matrix4 matrix(matrixIrr.getTransposed());   // transposed because row/lines are written other way round in Collada\r
2277         // Convert to right-handed\r
2278         matrix[2] *= -1.f;\r
2279         matrix[6] *= -1.f;\r
2280         matrix[8] *= -1.f;\r
2281         matrix[9] *= -1.f;\r
2282         matrix[11] *= -1.f;\r
2283         matrix[14] *= -1.f;\r
2284 \r
2285         Writer->writeElement("matrix", false);\r
2286         Writer->writeLineBreak();\r
2287 \r
2288         for ( int a=0; a<4; ++a )\r
2289         {\r
2290                 irr::core::stringc txt;\r
2291                 for ( int b=0; b<4; ++b )\r
2292                 {\r
2293                         if ( b > 0 )\r
2294                                 txt += " ";\r
2295                         txt += irr::core::stringc(matrix[a*4+b]).eraseTrailingFloatZeros();\r
2296                 }\r
2297                 Writer->writeText(txt.c_str());\r
2298                 Writer->writeLineBreak();\r
2299         }\r
2300 \r
2301         Writer->writeClosingTag("matrix");\r
2302         Writer->writeLineBreak();\r
2303 }\r
2304 \r
2305 } // end namespace\r
2306 } // end namespace\r
2307 \r
2308 #endif\r
2309 \r