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