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
5 // TODO: second UV-coordinates currently ignored in textures
\r
7 #include "IrrCompileConfig.h"
\r
9 #ifdef _IRR_COMPILE_WITH_COLLADA_WRITER_
\r
11 #include "CColladaMeshWriter.h"
\r
13 #include "IFileSystem.h"
\r
14 #include "IWriteFile.h"
\r
15 #include "IXMLWriter.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
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
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
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
52 case ECCS_TRANSPARENT:
\r
54 case ECCS_REFLECTIVE:
\r
60 E_COLLADA_IRR_COLOR CColladaMeshWriterProperties::getColorMapping(const video::SMaterial & material, E_COLLADA_COLOR_SAMPLER cs) const
\r
65 return ECIC_DIFFUSE;
\r
67 return ECIC_AMBIENT;
\r
69 return ECIC_EMISSIVE;
\r
71 return ECIC_SPECULAR;
\r
72 case ECCS_TRANSPARENT:
\r
74 case ECCS_REFLECTIVE:
\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
84 return video::SColor(255, 0, 0, 0);
\r
88 //! Return the settings for transparence
\r
89 E_COLLADA_TRANSPARENT_FX CColladaMeshWriterProperties::getTransparentFx(const video::SMaterial& material) const
\r
91 // TODO: figure out best default mapping
\r
95 //! Transparency value for the material.
\r
96 f32 CColladaMeshWriterProperties::getTransparency(const video::SMaterial& material) const
\r
98 // TODO: figure out best default mapping
\r
102 //! Reflectivity value for that material
\r
103 f32 CColladaMeshWriterProperties::getReflectivity(const video::SMaterial& material) const
\r
105 // TODO: figure out best default mapping
\r
109 //! Return index of refraction for that material
\r
110 f32 CColladaMeshWriterProperties::getIndexOfRefraction(const video::SMaterial& material) const
\r
115 bool CColladaMeshWriterProperties::isExportable(const irr::scene::ISceneNode * node) const
\r
117 return node && node->isVisible();
\r
120 IMesh* CColladaMeshWriterProperties::getMesh(irr::scene::ISceneNode * node)
\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
133 return static_cast<IMeshSceneNode*>(node)->getMesh();
\r
134 if ( node->getType() == ESNT_TERRAIN )
\r
135 return static_cast<ITerrainSceneNode*>(node)->getMesh();
\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
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
153 || (node->getType() == ESNT_ANIMATED_MESH
\r
154 && static_cast<const IAnimatedMeshSceneNode*>(node)->isReadOnlyMaterials() );
\r
156 return !useMeshMaterial;
\r
161 CColladaMeshWriterNames::CColladaMeshWriterNames(IColladaMeshWriter * writer)
\r
162 : ColladaMeshWriter(writer)
\r
166 irr::core::stringc CColladaMeshWriterNames::nameForMesh(const scene::IMesh* mesh, int instance)
\r
168 irr::core::stringc name("mesh");
\r
169 name += nameForPtr(mesh);
\r
170 if ( instance > 0 )
\r
173 name += irr::core::stringc(instance);
\r
175 return ColladaMeshWriter->toNCName(name);
\r
178 irr::core::stringc CColladaMeshWriterNames::nameForNode(const scene::ISceneNode* node)
\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
186 name += nameForPtr(node);
\r
189 name += irr::core::stringc(node->getName());
\r
191 return ColladaMeshWriter->toNCName(name);
\r
194 irr::core::stringc CColladaMeshWriterNames::nameForMaterial(const video::SMaterial & material, int materialId, const scene::IMesh* mesh, const scene::ISceneNode* node)
\r
196 core::stringc strMat("mat");
\r
198 bool nodeMaterial = ColladaMeshWriter->getProperties()->useNodeMaterial(node);
\r
199 if ( nodeMaterial )
\r
202 strMat += nameForPtr(node);
\r
203 strMat += irr::core::stringc(node->getName());
\r
206 strMat += nameForPtr(mesh);
\r
207 strMat += materialId;
\r
208 return ColladaMeshWriter->toNCName(strMat);
\r
211 irr::core::stringc CColladaMeshWriterNames::nameForPtr(const void* ptr) const
\r
214 snprintf_irr(buf, 32, "%p", ptr);
\r
215 return irr::core::stringc(buf);
\r
220 CColladaMeshWriter::CColladaMeshWriter( ISceneManager * smgr, video::IVideoDriver* driver,
\r
221 io::IFileSystem* fs)
\r
222 : FileSystem(fs), VideoDriver(driver), Writer(0)
\r
226 setDebugName("CColladaMeshWriter");
\r
230 VideoDriver->grab();
\r
233 FileSystem->grab();
\r
236 setAmbientLight( smgr->getAmbientLight() );
\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
249 CColladaMeshWriterProperties * p = new CColladaMeshWriterProperties();
\r
250 setDefaultProperties(p);
\r
254 CColladaMeshWriterNames * nameGenerator = new CColladaMeshWriterNames(this);
\r
255 setDefaultNameGenerator(nameGenerator);
\r
256 setNameGenerator(nameGenerator);
\r
257 nameGenerator->drop();
\r
261 CColladaMeshWriter::~CColladaMeshWriter()
\r
264 VideoDriver->drop();
\r
267 FileSystem->drop();
\r
271 void CColladaMeshWriter::reset()
\r
273 LibraryImages.clear();
\r
275 LightNodes.clear();
\r
276 CameraNodes.clear();
\r
277 MaterialsWritten.clear();
\r
278 EffectsWritten.clear();
\r
279 MaterialNameCache.clear();
\r
282 //! Returns the type of the mesh writer
\r
283 EMESH_WRITER_TYPE CColladaMeshWriter::getType() const
\r
285 return EMWT_COLLADA;
\r
288 //! writes a scene starting with the given node
\r
289 bool CColladaMeshWriter::writeScene(io::IWriteFile* file, scene::ISceneNode* root, int writeRoot)
\r
291 if (!file || !root)
\r
296 Writer = FileSystem->createXMLWriterUTF8(file);
\r
300 os::Printer::log("Could not write file", file->getFileName());
\r
304 Directory = FileSystem->getFileDir(FileSystem->getAbsolutePath( file->getFileName() ));
\r
306 // make names for all nodes with exportable meshes
\r
307 makeMeshNames(root);
\r
309 os::Printer::log("Writing scene", file->getFileName());
\r
311 // write COLLADA header
\r
313 Writer->writeXMLHeader();
\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
320 // write asset data
\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
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
338 writeLibraryImages();
\r
341 Writer->writeElement("library_lights", false);
\r
342 Writer->writeLineBreak();
\r
344 writeAmbientLightElement( getAmbientLight() );
\r
345 writeNodeLights(root);
\r
347 Writer->writeClosingTag("library_lights");
\r
348 Writer->writeLineBreak();
\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
358 Writer->writeElement("library_geometries", false);
\r
359 Writer->writeLineBreak();
\r
360 writeAllMeshGeometries();
\r
361 Writer->writeClosingTag("library_geometries");
\r
362 Writer->writeLineBreak();
\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
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
378 // Write the scenegraph.
\r
379 if ( writeRoot == 2 || (writeRoot == 1 && root->getType() != ESNT_SCENE_MANAGER) )
\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
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
395 writeSceneNode(*it);
\r
400 Writer->writeClosingTag("visual_scene");
\r
401 Writer->writeLineBreak();
\r
402 Writer->writeClosingTag("library_visual_scenes");
\r
403 Writer->writeLineBreak();
\r
407 Writer->writeElement("scene", false);
\r
408 Writer->writeLineBreak();
\r
410 Writer->writeElement("instance_visual_scene", true, "url", "#default_scene");
\r
411 Writer->writeLineBreak();
\r
413 Writer->writeClosingTag("scene");
\r
414 Writer->writeLineBreak();
\r
417 // close everything
\r
419 Writer->writeClosingTag("COLLADA");
\r
425 void CColladaMeshWriter::makeMeshNames(irr::scene::ISceneNode * node)
\r
427 if ( !node || !getProperties() || !getProperties()->isExportable(node) || !getNameGenerator())
\r
430 IMesh* mesh = getProperties()->getMesh(node);
\r
433 if ( !Meshes.find(mesh) )
\r
436 cm.Name = nameForMesh(mesh, 0);
\r
437 Meshes.insert(mesh, cm);
\r
441 const core::list<ISceneNode*>& children = node->getChildren();
\r
442 for ( core::list<ISceneNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )
\r
444 makeMeshNames(*it);
\r
448 void CColladaMeshWriter::writeNodeMaterials(irr::scene::ISceneNode * node)
\r
450 if ( !node || !getProperties() || !getProperties()->isExportable(node) )
\r
453 core::array<irr::core::stringc> materialNames;
\r
455 IMesh* mesh = getProperties()->getMesh(node);
\r
458 MeshNode * n = Meshes.find(mesh);
\r
459 if ( !getProperties()->useNodeMaterial(node) )
\r
461 // no material overrides - write mesh materials
\r
462 if ( n && !n->getValue().MaterialsWritten )
\r
464 writeMeshMaterials(mesh, getGeometryWriting() == ECGI_PER_MESH_AND_MATERIAL ? &materialNames : NULL);
\r
465 n->getValue().MaterialsWritten = true;
\r
470 // write node materials
\r
471 for (u32 i=0; i<node->getMaterialCount(); ++i)
\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
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
485 SGeometryMeshMaterials * geomMat = n->getValue().findGeometryMeshMaterials(materialNames);
\r
487 geomMat->MaterialOwners.push_back(node);
\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
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
502 const core::list<ISceneNode*>& children = node->getChildren();
\r
503 for ( core::list<ISceneNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )
\r
505 writeNodeMaterials( *it );
\r
509 void CColladaMeshWriter::writeMaterial(const irr::core::stringc& materialname)
\r
511 if ( MaterialsWritten.find(materialname) )
\r
513 MaterialsWritten.insert(materialname, true);
\r
515 Writer->writeElement("material", false,
\r
516 "id", materialname.c_str(),
\r
517 "name", materialname.c_str());
\r
518 Writer->writeLineBreak();
\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
524 Writer->writeElement("instance_effect", true,
\r
525 "url", (core::stringc("#") + strFx).c_str());
\r
526 Writer->writeLineBreak();
\r
528 Writer->writeClosingTag("material");
\r
529 Writer->writeLineBreak();
\r
532 void CColladaMeshWriter::writeNodeEffects(irr::scene::ISceneNode * node)
\r
534 if ( !node || !getProperties() || !getProperties()->isExportable(node) || !getNameGenerator() )
\r
537 IMesh* mesh = getProperties()->getMesh(node);
\r
540 if ( !getProperties()->useNodeMaterial(node) )
\r
542 // no material overrides - write mesh materials
\r
543 MeshNode * n = Meshes.find(mesh);
\r
544 if ( n && !n->getValue().EffectsWritten )
\r
546 writeMeshEffects(mesh);
\r
547 n->getValue().EffectsWritten = true;
\r
552 // write node materials
\r
553 for (u32 i=0; i<node->getMaterialCount(); ++i)
\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
563 const core::list<ISceneNode*>& children = node->getChildren();
\r
564 for ( core::list<ISceneNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )
\r
566 writeNodeEffects( *it );
\r
570 void CColladaMeshWriter::writeNodeLights(irr::scene::ISceneNode * node)
\r
572 if ( !node || !getProperties() || !getProperties()->isExportable(node))
\r
575 if ( node->getType() == ESNT_LIGHT )
\r
577 ILightSceneNode * lightNode = static_cast<ILightSceneNode*>(node);
\r
578 const video::SLight& lightData = lightNode->getLightData();
\r
580 SColladaLight cLight;
\r
581 cLight.Name = nameForNode(node);
\r
582 LightNodes.insert(node, cLight);
\r
584 Writer->writeElement("light", false, "id", cLight.Name.c_str());
\r
585 Writer->writeLineBreak();
\r
587 Writer->writeElement("technique_common", false);
\r
588 Writer->writeLineBreak();
\r
590 switch ( lightNode->getLightType() )
\r
592 case video::ELT_POINT:
\r
593 Writer->writeElement("point", false);
\r
594 Writer->writeLineBreak();
\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
601 Writer->writeClosingTag("point");
\r
602 Writer->writeLineBreak();
\r
605 case video::ELT_SPOT:
\r
606 Writer->writeElement("spot", false);
\r
607 Writer->writeLineBreak();
\r
609 writeColorElement(lightData.DiffuseColor, false);
\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
615 writeNode("falloff_angle", core::stringc(lightData.OuterCone * core::RADTODEG).c_str());
\r
616 writeNode("falloff_exponent", core::stringc(lightData.Falloff).c_str());
\r
618 Writer->writeClosingTag("spot");
\r
619 Writer->writeLineBreak();
\r
622 case video::ELT_DIRECTIONAL:
\r
623 Writer->writeElement("directional", false);
\r
624 Writer->writeLineBreak();
\r
626 writeColorElement(lightData.DiffuseColor, false);
\r
628 Writer->writeClosingTag("directional");
\r
629 Writer->writeLineBreak();
\r
635 Writer->writeClosingTag("technique_common");
\r
636 Writer->writeLineBreak();
\r
638 Writer->writeClosingTag("light");
\r
639 Writer->writeLineBreak();
\r
643 const core::list<ISceneNode*>& children = node->getChildren();
\r
644 for ( core::list<ISceneNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )
\r
646 writeNodeLights( *it );
\r
650 void CColladaMeshWriter::writeNodeCameras(irr::scene::ISceneNode * node)
\r
652 if ( !node || !getProperties() || !getProperties()->isExportable(node) )
\r
655 if ( isCamera(node) )
\r
657 ICameraSceneNode * cameraNode = static_cast<ICameraSceneNode*>(node);
\r
658 irr::core::stringc name = nameForNode(node);
\r
659 CameraNodes.insert(cameraNode, name);
\r
661 Writer->writeElement("camera", false, "id", name.c_str());
\r
662 Writer->writeLineBreak();
\r
664 Writer->writeElement("optics", false);
\r
665 Writer->writeLineBreak();
\r
667 Writer->writeElement("technique_common", false);
\r
668 Writer->writeLineBreak();
\r
670 if ( cameraNode->isOrthogonal() )
\r
672 Writer->writeElement("orthographic", false);
\r
673 Writer->writeLineBreak();
\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
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
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
690 Writer->writeClosingTag("orthographic");
\r
691 Writer->writeLineBreak();
\r
695 Writer->writeElement("perspective", false);
\r
696 Writer->writeLineBreak();
\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
703 Writer->writeClosingTag("perspective");
\r
704 Writer->writeLineBreak();
\r
707 Writer->writeClosingTag("technique_common");
\r
708 Writer->writeLineBreak();
\r
710 Writer->writeClosingTag("optics");
\r
711 Writer->writeLineBreak();
\r
713 Writer->writeClosingTag("camera");
\r
714 Writer->writeLineBreak();
\r
717 const core::list<ISceneNode*>& children = node->getChildren();
\r
718 for ( core::list<ISceneNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )
\r
720 writeNodeCameras( *it );
\r
724 void CColladaMeshWriter::writeAllMeshGeometries()
\r
726 core::map<IMesh*, SColladaMesh>::ConstIterator it = Meshes.getConstIterator();
\r
727 for(; !it.atEnd(); it++ )
\r
729 IMesh* mesh = it->getKey();
\r
730 const SColladaMesh& colladaMesh = it->getValue();
\r
732 if ( getGeometryWriting() == ECGI_PER_MESH_AND_MATERIAL && colladaMesh.GeometryMeshMaterials.size() > 1 )
\r
734 for ( u32 i=0; i<colladaMesh.GeometryMeshMaterials.size(); ++i )
\r
736 writeMeshGeometry(colladaMesh.GeometryMeshMaterials[i].GeometryName, mesh);
\r
741 writeMeshGeometry(colladaMesh.Name, mesh);
\r
746 void CColladaMeshWriter::writeSceneNode(irr::scene::ISceneNode * node )
\r
748 if ( !node || !getProperties() || !getProperties()->isExportable(node) )
\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
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
761 writeMatrixElement(node->getRelativeTransformation());
\r
763 else if ( isCamera(node) )
\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
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
772 ICameraSceneNode * camNode = static_cast<ICameraSceneNode*>(node);
\r
773 writeLookAtElement(camNode->getPosition(), camNode->getTarget(), camNode->getUpVector());
\r
777 writeTranslateElement( node->getPosition() );
\r
779 irr::core::vector3df rot(node->getRotation());
\r
780 core::quaternion quat(rot*core::DEGTORAD);
\r
782 core::vector3df axis;
\r
783 quat.toAngleAxis(angle, axis);
\r
784 writeRotateElement( axis, angle*core::RADTODEG );
\r
786 writeScaleElement( node->getScale() );
\r
789 // instance geometry
\r
790 IMesh* mesh = getProperties()->getMesh(node);
\r
793 MeshNode * n = Meshes.find(mesh);
\r
796 const SColladaMesh& colladaMesh = n->getValue();
\r
797 writeMeshInstanceGeometry(colladaMesh.findGeometryNameForNode(node), mesh, node);
\r
802 if ( node->getType() == ESNT_LIGHT )
\r
804 LightNode * n = LightNodes.find(node);
\r
806 writeLightInstance(n->getValue().Name);
\r
810 if ( isCamera(node) )
\r
812 CameraNode * camNode = CameraNodes.find(node);
\r
814 writeCameraInstance(camNode->getValue());
\r
817 const core::list<ISceneNode*>& children = node->getChildren();
\r
818 for ( core::list<ISceneNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )
\r
820 writeSceneNode( *it );
\r
823 Writer->writeClosingTag("node");
\r
824 Writer->writeLineBreak();
\r
828 bool CColladaMeshWriter::writeMesh(io::IWriteFile* file, scene::IMesh* mesh, s32 flags)
\r
835 Writer = FileSystem->createXMLWriterUTF8(file);
\r
839 os::Printer::log("Could not write file", file->getFileName());
\r
843 Directory = FileSystem->getFileDir(FileSystem->getAbsolutePath( file->getFileName() ));
\r
845 os::Printer::log("Writing mesh", file->getFileName());
\r
847 // write COLLADA header
\r
849 Writer->writeXMLHeader();
\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
856 // write asset data
\r
859 // write all materials
\r
861 Writer->writeElement("library_materials", false);
\r
862 Writer->writeLineBreak();
\r
864 writeMeshMaterials(mesh);
\r
866 Writer->writeClosingTag("library_materials");
\r
867 Writer->writeLineBreak();
\r
869 Writer->writeElement("library_effects", false);
\r
870 Writer->writeLineBreak();
\r
872 writeMeshEffects(mesh);
\r
874 Writer->writeClosingTag("library_effects");
\r
875 Writer->writeLineBreak();
\r
878 writeLibraryImages();
\r
882 Writer->writeElement("library_geometries", false);
\r
883 Writer->writeLineBreak();
\r
885 irr::core::stringc meshname(nameForMesh(mesh, 0));
\r
886 writeMeshGeometry(meshname, mesh);
\r
888 Writer->writeClosingTag("library_geometries");
\r
889 Writer->writeLineBreak();
\r
891 // write scene_library
\r
892 if ( getWriteDefaultScene() )
\r
894 Writer->writeElement("library_visual_scenes", false);
\r
895 Writer->writeLineBreak();
\r
897 Writer->writeElement("visual_scene", false, "id", "default_scene");
\r
898 Writer->writeLineBreak();
\r
900 Writer->writeElement("node", false);
\r
901 Writer->writeLineBreak();
\r
903 writeMeshInstanceGeometry(meshname, mesh);
\r
905 Writer->writeClosingTag("node");
\r
906 Writer->writeLineBreak();
\r
908 Writer->writeClosingTag("visual_scene");
\r
909 Writer->writeLineBreak();
\r
911 Writer->writeClosingTag("library_visual_scenes");
\r
912 Writer->writeLineBreak();
\r
916 Writer->writeElement("scene", false);
\r
917 Writer->writeLineBreak();
\r
919 Writer->writeElement("instance_visual_scene", true, "url", "#default_scene");
\r
920 Writer->writeLineBreak();
\r
922 Writer->writeClosingTag("scene");
\r
923 Writer->writeLineBreak();
\r
927 // close everything
\r
929 Writer->writeClosingTag("COLLADA");
\r
935 void CColladaMeshWriter::writeMeshInstanceGeometry(const irr::core::stringc& meshname, scene::IMesh* mesh, scene::ISceneNode* node)
\r
937 //<instance_geometry url="#mesh">
\r
938 Writer->writeElement("instance_geometry", false, "url", toRef(meshname).c_str());
\r
939 Writer->writeLineBreak();
\r
941 Writer->writeElement("bind_material", false);
\r
942 Writer->writeLineBreak();
\r
944 Writer->writeElement("technique_common", false);
\r
945 Writer->writeLineBreak();
\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
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
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
964 Writer->writeClosingTag("instance_material");
\r
965 Writer->writeLineBreak();
\r
968 Writer->writeClosingTag("technique_common");
\r
969 Writer->writeLineBreak();
\r
971 Writer->writeClosingTag("bind_material");
\r
972 Writer->writeLineBreak();
\r
974 Writer->writeClosingTag("instance_geometry");
\r
975 Writer->writeLineBreak();
\r
978 void CColladaMeshWriter::writeLightInstance(const irr::core::stringc& lightName)
\r
980 Writer->writeElement("instance_light", true, "url", toRef(lightName).c_str());
\r
981 Writer->writeLineBreak();
\r
984 void CColladaMeshWriter::writeCameraInstance(const irr::core::stringc& cameraName)
\r
986 Writer->writeElement("instance_camera", true, "url", toRef(cameraName).c_str());
\r
987 Writer->writeLineBreak();
\r
990 bool CColladaMeshWriter::hasSecondTextureCoordinates(video::E_VERTEX_TYPE type) const
\r
992 return type == video::EVT_2TCOORDS;
\r
995 void CColladaMeshWriter::writeVector(const irr::core::vector3df& vec)
\r
999 snprintf_irr(tmpbuf, 255, "%f", vec.X);
\r
1000 WriteBuffer = tmpbuf;
\r
1001 WriteBuffer.eraseTrailingFloatZeros();
\r
1003 snprintf_irr(tmpbuf, 255, " %f", vec.Y);
\r
1004 WriteBuffer.append(tmpbuf);
\r
1005 WriteBuffer.eraseTrailingFloatZeros();
\r
1007 snprintf_irr(tmpbuf, 255, " %f", vec.Z*-1.f); // change handedness
\r
1008 WriteBuffer.append(tmpbuf);
\r
1009 WriteBuffer.eraseTrailingFloatZeros();
\r
1011 Writer->writeText(WriteBuffer.c_str());
\r
1014 void CColladaMeshWriter::writeUv(const irr::core::vector2df& vec)
\r
1018 snprintf_irr(tmpbuf, 255, "%f", vec.X);
\r
1019 WriteBuffer = tmpbuf;
\r
1020 WriteBuffer.eraseTrailingFloatZeros();
\r
1022 snprintf_irr(tmpbuf, 255, " %f", 1.f-vec.Y); // change handedness
\r
1023 WriteBuffer.append(tmpbuf);
\r
1024 WriteBuffer.eraseTrailingFloatZeros();
\r
1026 Writer->writeText(WriteBuffer.c_str());
\r
1029 void CColladaMeshWriter::writeColor(const irr::video::SColorf& colorf, bool writeAlpha)
\r
1033 snprintf_irr(tmpbuf, 255, "%f", colorf.getRed());
\r
1034 WriteBuffer = tmpbuf;
\r
1035 WriteBuffer.eraseTrailingFloatZeros();
\r
1037 snprintf_irr(tmpbuf, 255, " %f", colorf.getGreen());
\r
1038 WriteBuffer.append(tmpbuf);
\r
1039 WriteBuffer.eraseTrailingFloatZeros();
\r
1041 snprintf_irr(tmpbuf, 255, " %f", colorf.getBlue());
\r
1042 WriteBuffer.append(tmpbuf);
\r
1043 WriteBuffer.eraseTrailingFloatZeros();
\r
1047 snprintf_irr(tmpbuf, 255, " %f", colorf.getAlpha());
\r
1048 WriteBuffer.append(tmpbuf);
\r
1049 WriteBuffer.eraseTrailingFloatZeros();
\r
1052 Writer->writeText(WriteBuffer.c_str());
\r
1055 irr::core::stringc CColladaMeshWriter::toString(const irr::video::ECOLOR_FORMAT format) const
\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
1067 irr::core::stringc CColladaMeshWriter::toString(const irr::video::E_TEXTURE_CLAMP clamp) const
\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
1084 return core::stringc("NONE");
\r
1087 irr::core::stringc CColladaMeshWriter::toString(const irr::scene::E_COLLADA_TRANSPARENT_FX transparent) const
\r
1089 if ( transparent & ECOF_RGB_ZERO )
\r
1090 return core::stringc("RGB_ZERO");
\r
1092 return core::stringc("A_ONE");
\r
1095 irr::core::stringc CColladaMeshWriter::toRef(const irr::core::stringc& source) const
\r
1097 irr::core::stringc ref("#");
\r
1102 bool CColladaMeshWriter::isCamera(const scene::ISceneNode* node) const
\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
1112 irr::core::stringc CColladaMeshWriter::nameForMesh(const scene::IMesh* mesh, int instance) const
\r
1114 IColladaMeshWriterNames * nameGenerator = getNameGenerator();
\r
1115 if ( nameGenerator )
\r
1117 return nameGenerator->nameForMesh(mesh, instance);
\r
1119 return irr::core::stringc("missing_name_generator");
\r
1122 irr::core::stringc CColladaMeshWriter::nameForNode(const scene::ISceneNode* node) const
\r
1124 IColladaMeshWriterNames * nameGenerator = getNameGenerator();
\r
1125 if ( nameGenerator )
\r
1127 return nameGenerator->nameForNode(node);
\r
1129 return irr::core::stringc("missing_name_generator");
\r
1132 irr::core::stringc CColladaMeshWriter::nameForMaterial(const video::SMaterial & material, int materialId, const scene::IMesh* mesh, const scene::ISceneNode* node)
\r
1134 irr::core::stringc matName;
\r
1135 if ( getExportSMaterialsOnlyOnce() )
\r
1137 matName = findCachedMaterialName(material);
\r
1138 if ( !matName.empty() )
\r
1142 IColladaMeshWriterNames * nameGenerator = getNameGenerator();
\r
1143 if ( nameGenerator )
\r
1145 matName = nameGenerator->nameForMaterial(material, materialId, mesh, node);
\r
1148 matName = irr::core::stringc("missing_name_generator");
\r
1150 if ( getExportSMaterialsOnlyOnce() )
\r
1151 MaterialNameCache.push_back (MaterialName(material, matName));
\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
1159 snprintf_irr(buf, 100, "mat_symb_%p_%d", mesh, materialId);
\r
1160 return irr::core::stringc(buf);
\r
1163 irr::core::stringc CColladaMeshWriter::findCachedMaterialName(const irr::video::SMaterial& material) const
\r
1165 for ( u32 i=0; i<MaterialNameCache.size(); ++i )
\r
1167 if ( MaterialNameCache[i].Material == material )
\r
1168 return MaterialNameCache[i].Name;
\r
1170 return irr::core::stringc();
\r
1173 irr::core::stringc CColladaMeshWriter::minTexfilterToString(bool bilinear, bool trilinear) const
\r
1176 return core::stringc("LINEAR_MIPMAP_LINEAR");
\r
1177 else if ( bilinear )
\r
1178 return core::stringc("LINEAR_MIPMAP_NEAREST");
\r
1180 return core::stringc("NONE");
\r
1183 inline irr::core::stringc CColladaMeshWriter::magTexfilterToString(bool bilinear, bool trilinear) const
\r
1185 if ( bilinear || trilinear )
\r
1186 return core::stringc("LINEAR");
\r
1188 return core::stringc("NONE");
\r
1191 bool CColladaMeshWriter::isXmlNameStartChar(c8 c) const
\r
1193 return (c >= 'A' && c <= 'Z')
\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
1214 bool CColladaMeshWriter::isXmlNameChar(c8 c) const
\r
1216 return isXmlNameStartChar(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
1223 || (c >= 9x0300 && c <= 0x036F)
\r
1224 || (c >= 0x203F && c <= 0x2040)
\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
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
1235 result.append( oldString );
\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
1241 if ( result[i] == ':' || !isXmlNameChar(result[i]) )
\r
1243 result[i] = REPLACMENT;
\r
1249 const irr::core::stringc* CColladaMeshWriter::findGeometryNameForNode(ISceneNode* node)
\r
1251 IMesh* mesh = getProperties()->getMesh(node);
\r
1255 MeshNode * n = Meshes.find(mesh);
\r
1259 const SColladaMesh& colladaMesh = n->getValue();
\r
1260 return &colladaMesh.findGeometryNameForNode(node);
\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
1266 irr::core::stringc result;
\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
1274 // not already starting with "./" ?
\r
1275 if ( path[0] != _IRR_TEXT('.')
\r
1276 || path[1] != _IRR_TEXT('/') )
\r
1278 result.append("./");
\r
1281 result.append(path);
\r
1283 // Make correct URI (without whitespace)
\r
1284 u32 len = result.size();
\r
1285 for (u32 i=0; i<len; ++i)
\r
1287 for (u32 e = 0; e < EscapeCharsAnyURI.size(); ++e)
\r
1289 if (result[i] == EscapeCharsAnyURI[e].Character)
\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
1306 void CColladaMeshWriter::writeAsset()
\r
1308 Writer->writeElement("asset", false);
\r
1309 Writer->writeLineBreak();
\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
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
1326 Writer->writeElement("modified", false);
\r
1327 Writer->writeText("2008-01-31T00:00:00Z");
\r
1328 Writer->writeClosingTag("modified");
\r
1329 Writer->writeLineBreak();
\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
1344 Writer->writeElement("unit", true, "meter", core::stringc(getUnitMeter()).eraseTrailingFloatZeros().c_str(), "name", getUnitName().c_str());
\r
1345 Writer->writeLineBreak();
\r
1347 Writer->writeClosingTag("asset");
\r
1348 Writer->writeLineBreak();
\r
1351 void CColladaMeshWriter::writeMeshMaterials(scene::IMesh* mesh, irr::core::array<irr::core::stringc> * materialNamesOut)
\r
1354 for (i=0; i<mesh->getMeshBufferCount(); ++i)
\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
1364 void CColladaMeshWriter::writeMaterialEffect(const irr::core::stringc& materialfxname, const video::SMaterial & material)
\r
1366 if ( EffectsWritten.find(materialfxname) )
\r
1368 EffectsWritten.insert(materialfxname, true);
\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
1377 int numTextures = 0;
\r
1378 if ( getWriteTextures() )
\r
1380 // write texture surfaces and samplers and buffer all used imagess
\r
1381 for ( int t=0; t<4; ++t )
\r
1383 const video::SMaterialLayer& layer = material.TextureLayer[t];
\r
1384 if ( !layer.Texture )
\r
1388 if ( LibraryImages.linear_search(layer.Texture) < 0 )
\r
1389 LibraryImages.push_back( layer.Texture );
\r
1391 irr::core::stringc texName("tex");
\r
1392 texName += irr::core::stringc(t);
\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
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
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
1418 Writer->writeClosingTag("surface");
\r
1419 Writer->writeLineBreak();
\r
1421 Writer->writeClosingTag("newparam");
\r
1422 Writer->writeLineBreak();
\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
1431 Writer->writeElement("sampler2D", false);
\r
1432 Writer->writeLineBreak();
\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
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
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
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
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
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
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
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
1482 Writer->writeClosingTag("sampler2D");
\r
1483 Writer->writeLineBreak();
\r
1485 Writer->writeClosingTag("newparam");
\r
1486 Writer->writeLineBreak();
\r
1490 Writer->writeElement("technique", false, "sid", "common");
\r
1491 Writer->writeLineBreak();
\r
1493 E_COLLADA_TECHNIQUE_FX techFx = getProperties() ? getProperties()->getTechniqueFx(material) : ECTF_BLINN;
\r
1494 writeFxElement(material, techFx);
\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
1504 void CColladaMeshWriter::writeMeshEffects(scene::IMesh* mesh)
\r
1506 for (u32 i=0; i<mesh->getMeshBufferCount(); ++i)
\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
1515 void CColladaMeshWriter::writeMeshGeometry(const irr::core::stringc& meshname, scene::IMesh* mesh)
\r
1517 core::stringc meshId(meshname);
\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
1524 // do some statistics for the mesh to know which stuff needs to be saved into
\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
1531 u32 totalVertexCount = 0;
\r
1532 u32 totalTCoords2Count = 0;
\r
1533 bool needsTangents = false; // TODO: tangents not supported here yet
\r
1535 for (i=0; i<mesh->getMeshBufferCount(); ++i)
\r
1537 totalVertexCount += mesh->getMeshBuffer(i)->getVertexCount();
\r
1539 if (hasSecondTextureCoordinates(mesh->getMeshBuffer(i)->getVertexType()))
\r
1540 totalTCoords2Count += mesh->getMeshBuffer(i)->getVertexCount();
\r
1542 if (!needsTangents)
\r
1543 needsTangents = mesh->getMeshBuffer(i)->getVertexType() == video::EVT_TANGENTS;
\r
1546 const irr::u32 mbCount = mesh->getMeshBufferCount();
\r
1547 SComponentGlobalStartPos* globalIndices = new SComponentGlobalStartPos[mbCount];
\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
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
1562 for (i=0; i<mbCount; ++i)
\r
1564 scene::IMeshBuffer* buffer = mesh->getMeshBuffer(i);
\r
1565 u32 vertexCount = buffer->getVertexCount();
\r
1568 globalIndices[i].PosStartIndex = 0;
\r
1570 if (i+1 < mbCount)
\r
1571 globalIndices[i+1].PosStartIndex = globalIndices[i].PosStartIndex + vertexCount;
\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
1577 writeVector( (*reinterpret_cast<const video::S3DVertex*>(&vertices[j*vertexPitch])).Pos );
\r
1578 Writer->writeLineBreak();
\r
1582 Writer->writeClosingTag("float_array");
\r
1583 Writer->writeLineBreak();
\r
1585 Writer->writeElement("technique_common", false);
\r
1586 Writer->writeLineBreak();
\r
1588 vertexCountStr = core::stringc(totalVertexCount);
\r
1590 Writer->writeElement("accessor", false, "source", toRef(meshPosArrayId).c_str(),
\r
1591 "count", vertexCountStr.c_str(), "stride", "3");
\r
1592 Writer->writeLineBreak();
\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
1601 Writer->writeClosingTag("accessor");
\r
1602 Writer->writeLineBreak();
\r
1604 Writer->writeClosingTag("technique_common");
\r
1605 Writer->writeLineBreak();
\r
1607 Writer->writeClosingTag("source");
\r
1608 Writer->writeLineBreak();
\r
1610 // write texture coordinates
\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
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
1624 for (i=0; i<mbCount; ++i)
\r
1626 scene::IMeshBuffer* buffer = mesh->getMeshBuffer(i);
\r
1627 u32 vertexCount = buffer->getVertexCount();
\r
1630 globalIndices[i].TCoord0StartIndex = 0;
\r
1632 if (i+1 < mbCount)
\r
1633 globalIndices[i+1].TCoord0StartIndex = globalIndices[i].TCoord0StartIndex + vertexCount;
\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
1639 writeUv( (*reinterpret_cast<const video::S3DVertex*>(&vertices[j*vertexPitch])).TCoords );
\r
1640 Writer->writeLineBreak();
\r
1644 Writer->writeClosingTag("float_array");
\r
1645 Writer->writeLineBreak();
\r
1647 Writer->writeElement("technique_common", false);
\r
1648 Writer->writeLineBreak();
\r
1650 vertexCountStr = core::stringc(totalVertexCount);
\r
1652 Writer->writeElement("accessor", false, "source", toRef(meshTexCoordArrayId).c_str(),
\r
1653 "count", vertexCountStr.c_str(), "stride", "2");
\r
1654 Writer->writeLineBreak();
\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
1661 Writer->writeClosingTag("accessor");
\r
1662 Writer->writeLineBreak();
\r
1664 Writer->writeClosingTag("technique_common");
\r
1665 Writer->writeLineBreak();
\r
1667 Writer->writeClosingTag("source");
\r
1668 Writer->writeLineBreak();
\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
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
1683 for (i=0; i<mbCount; ++i)
\r
1685 scene::IMeshBuffer* buffer = mesh->getMeshBuffer(i);
\r
1686 u32 vertexCount = buffer->getVertexCount();
\r
1689 globalIndices[i].NormalStartIndex = 0;
\r
1691 if (i+1 < mbCount)
\r
1692 globalIndices[i+1].NormalStartIndex = globalIndices[i].NormalStartIndex + vertexCount;
\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
1698 writeVector( (*reinterpret_cast<const video::S3DVertex*>(&vertices[j*vertexPitch])).Normal );
\r
1699 Writer->writeLineBreak();
\r
1703 Writer->writeClosingTag("float_array");
\r
1704 Writer->writeLineBreak();
\r
1706 Writer->writeElement("technique_common", false);
\r
1707 Writer->writeLineBreak();
\r
1709 vertexCountStr = core::stringc(totalVertexCount);
\r
1711 Writer->writeElement("accessor", false, "source", toRef(meshNormalArrayId).c_str(),
\r
1712 "count", vertexCountStr.c_str(), "stride", "3");
\r
1713 Writer->writeLineBreak();
\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
1722 Writer->writeClosingTag("accessor");
\r
1723 Writer->writeLineBreak();
\r
1725 Writer->writeClosingTag("technique_common");
\r
1726 Writer->writeLineBreak();
\r
1728 Writer->writeClosingTag("source");
\r
1729 Writer->writeLineBreak();
\r
1731 // write second set of texture coordinates
\r
1732 core::stringc meshTexCoord1Id(meshId);
\r
1733 meshTexCoord1Id += "-TexCoord1";
\r
1734 if (totalTCoords2Count)
\r
1736 Writer->writeElement("source", false, "id", meshTexCoord1Id.c_str());
\r
1737 Writer->writeLineBreak();
\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
1746 for (i=0; i<mbCount; ++i)
\r
1748 scene::IMeshBuffer* buffer = mesh->getMeshBuffer(i);
\r
1749 video::E_VERTEX_TYPE vtxType = buffer->getVertexType();
\r
1750 u32 vertexCount = 0;
\r
1752 if (hasSecondTextureCoordinates(vtxType))
\r
1754 vertexCount = buffer->getVertexCount();
\r
1757 case video::EVT_2TCOORDS:
\r
1759 video::S3DVertex2TCoords* vtx = (video::S3DVertex2TCoords*)buffer->getVertices();
\r
1760 for (u32 j=0; j<vertexCount; ++j)
\r
1762 writeUv(vtx[j].TCoords2);
\r
1763 Writer->writeLineBreak();
\r
1770 } // end this buffer has 2 texture coordinates
\r
1773 globalIndices[i].TCoord1StartIndex = 0;
\r
1775 if (i+1 < mbCount)
\r
1776 globalIndices[i+1].TCoord1StartIndex = globalIndices[i].TCoord1StartIndex + vertexCount;
\r
1779 Writer->writeClosingTag("float_array");
\r
1780 Writer->writeLineBreak();
\r
1782 Writer->writeElement("technique_common", false);
\r
1783 Writer->writeLineBreak();
\r
1785 vertexCountStr = core::stringc(totalTCoords2Count);
\r
1787 Writer->writeElement("accessor", false, "source", toRef(meshTexCoord1ArrayId).c_str(),
\r
1788 "count", vertexCountStr.c_str(), "stride", "2");
\r
1789 Writer->writeLineBreak();
\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
1796 Writer->writeClosingTag("accessor");
\r
1797 Writer->writeLineBreak();
\r
1799 Writer->writeClosingTag("technique_common");
\r
1800 Writer->writeLineBreak();
\r
1802 Writer->writeClosingTag("source");
\r
1803 Writer->writeLineBreak();
\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
1816 Writer->writeElement("input", true, "semantic", "POSITION", "source", toRef(meshPosId).c_str());
\r
1817 Writer->writeLineBreak();
\r
1819 Writer->writeClosingTag("vertices");
\r
1820 Writer->writeLineBreak();
\r
1824 for (i=0; i<mbCount; ++i)
\r
1826 scene::IMeshBuffer* buffer = mesh->getMeshBuffer(i);
\r
1828 if ( buffer->getPrimitiveType() != EPT_TRIANGLES )
\r
1830 os::Printer::log("Collada writer does not support non-triangle meshbuffers. Mesh: ", meshname.c_str(), ELL_WARNING);
\r
1834 const u32 polyCount = buffer->getPrimitiveCount();
\r
1835 core::stringc strPolyCount(polyCount);
\r
1836 irr::core::stringc strMat(nameForMaterialSymbol(mesh, i));
\r
1838 Writer->writeElement("triangles", false, "count", strPolyCount.c_str(),
\r
1839 "material", strMat.c_str());
\r
1840 Writer->writeLineBreak();
\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
1849 bool has2ndTexCoords = hasSecondTextureCoordinates(buffer->getVertexType());
\r
1850 if (has2ndTexCoords)
\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
1858 // write indices now
\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
1866 Writer->writeElement("p", false);
\r
1868 core::stringc strP;
\r
1869 strP.reserve(100);
\r
1870 for (u32 p=0; p<polyCount; ++p)
\r
1872 // Irrlicht uses clockwise, Collada uses counter-clockwise to define front-face
\r
1873 u32 irrIdx = buffer->getIndices()[(p*3) + 2];
\r
1875 strP += irrIdx + posIdx;
\r
1877 strP += irrIdx + tCoordIdx;
\r
1879 strP += irrIdx + normalIdx;
\r
1881 if (has2ndTexCoords)
\r
1883 strP += irrIdx + tCoord2Idx;
\r
1887 irrIdx = buffer->getIndices()[(p*3) + 1];
\r
1888 strP += irrIdx + posIdx;
\r
1890 strP += irrIdx + tCoordIdx;
\r
1892 strP += irrIdx + normalIdx;
\r
1894 if (has2ndTexCoords)
\r
1896 strP += irrIdx + tCoord2Idx;
\r
1900 irrIdx = buffer->getIndices()[(p*3) + 0];
\r
1901 strP += irrIdx + posIdx;
\r
1903 strP += irrIdx + tCoordIdx;
\r
1905 strP += irrIdx + normalIdx;
\r
1906 if (has2ndTexCoords)
\r
1909 strP += irrIdx + tCoord2Idx;
\r
1913 Writer->writeText(strP.c_str());
\r
1916 Writer->writeClosingTag("p");
\r
1917 Writer->writeLineBreak();
\r
1919 // close index buffer section
\r
1921 Writer->writeClosingTag("triangles");
\r
1922 Writer->writeLineBreak();
\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
1933 void CColladaMeshWriter::writeLibraryImages()
\r
1935 if ( getWriteTextures() && !LibraryImages.empty() )
\r
1937 Writer->writeElement("library_images", false);
\r
1938 Writer->writeLineBreak();
\r
1940 for ( irr::u32 i=0; i<LibraryImages.size(); ++i )
\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
1953 Writer->writeClosingTag("image");
\r
1954 Writer->writeLineBreak();
\r
1957 Writer->writeClosingTag("library_images");
\r
1958 Writer->writeLineBreak();
\r
1962 void CColladaMeshWriter::writeColorElement(const video::SColorf & col, bool writeAlpha)
\r
1964 Writer->writeElement("color", false);
\r
1966 writeColor(col, writeAlpha);
\r
1968 Writer->writeClosingTag("color");
\r
1969 Writer->writeLineBreak();
\r
1972 void CColladaMeshWriter::writeColorElement(const video::SColor & col, bool writeAlpha)
\r
1974 writeColorElement( video::SColorf(col), writeAlpha );
\r
1977 void CColladaMeshWriter::writeAmbientLightElement(const video::SColorf & col)
\r
1979 Writer->writeElement("light", false, "id", "ambientlight");
\r
1980 Writer->writeLineBreak();
\r
1982 Writer->writeElement("technique_common", false);
\r
1983 Writer->writeLineBreak();
\r
1985 Writer->writeElement("ambient", false);
\r
1986 Writer->writeLineBreak();
\r
1988 writeColorElement(col, false);
\r
1990 Writer->writeClosingTag("ambient");
\r
1991 Writer->writeLineBreak();
\r
1993 Writer->writeClosingTag("technique_common");
\r
1994 Writer->writeLineBreak();
\r
1996 Writer->writeClosingTag("light");
\r
1997 Writer->writeLineBreak();
\r
2000 s32 CColladaMeshWriter::getCheckedTextureIdx(const video::SMaterial & material, E_COLLADA_COLOR_SAMPLER cs)
\r
2002 if ( !getWriteTextures()
\r
2003 || !getProperties() )
\r
2006 s32 idx = getProperties()->getTextureIdx(material, cs);
\r
2007 if ( idx >= 0 && !material.TextureLayer[idx].Texture )
\r
2013 video::SColor CColladaMeshWriter::getColorMapping(const video::SMaterial & material, E_COLLADA_COLOR_SAMPLER cs, E_COLLADA_IRR_COLOR colType)
\r
2015 switch ( colType )
\r
2018 return video::SColor(255, 0, 0, 0);
\r
2021 return getProperties()->getCustomColor(material, cs);
\r
2023 case ECIC_DIFFUSE:
\r
2024 return material.DiffuseColor;
\r
2026 case ECIC_AMBIENT:
\r
2027 return material.AmbientColor;
\r
2029 case ECIC_EMISSIVE:
\r
2030 return material.EmissiveColor;
\r
2032 case ECIC_SPECULAR:
\r
2033 return material.SpecularColor;
\r
2035 return video::SColor(255, 0, 0, 0);
\r
2038 void CColladaMeshWriter::writeTextureSampler(s32 textureIdx)
\r
2040 irr::core::stringc sampler("tex");
\r
2041 sampler += irr::core::stringc(textureIdx);
\r
2042 sampler += "-sampler";
\r
2044 // <texture texture="sampler" texcoord="texCoordUv"/>
\r
2045 Writer->writeElement("texture", true, "texture", sampler.c_str(), "texcoord", "uv" );
\r
2046 Writer->writeLineBreak();
\r
2049 void CColladaMeshWriter::writeFxElement(const video::SMaterial & material, E_COLLADA_TECHNIQUE_FX techFx)
\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
2065 fxLabel = "blinn";
\r
2068 fxLabel = "phong";
\r
2070 case ECTF_LAMBERT:
\r
2071 fxLabel = "lambert";
\r
2072 writeSpecular = false;
\r
2073 writeShininess = false;
\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
2084 Writer->writeElement(fxLabel.c_str(), false);
\r
2085 Writer->writeLineBreak();
\r
2087 // write all interesting material parameters
\r
2088 // attributes must be written in fixed order
\r
2089 if ( getProperties() )
\r
2091 if ( writeEmission )
\r
2093 writeColorFx(material, "emission", ECCS_EMISSIVE);
\r
2096 if ( writeAmbient )
\r
2098 writeColorFx(material, "ambient", ECCS_AMBIENT);
\r
2101 if ( writeDiffuse )
\r
2103 writeColorFx(material, "diffuse", ECCS_DIFFUSE);
\r
2106 if ( writeSpecular )
\r
2108 writeColorFx(material, "specular", ECCS_SPECULAR);
\r
2111 if ( writeShininess )
\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
2120 if ( writeReflective )
\r
2122 writeColorFx(material, "reflective", ECCS_REFLECTIVE);
\r
2125 if ( writeReflectivity )
\r
2127 f32 t = getProperties()->getReflectivity(material);
\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
2139 if ( writeTransparent )
\r
2141 E_COLLADA_TRANSPARENT_FX transparentFx = getProperties()->getTransparentFx(material);
\r
2142 writeColorFx(material, "transparent", ECCS_TRANSPARENT, "opaque", toString(transparentFx).c_str());
\r
2145 if ( writeTransparency )
\r
2147 f32 t = getProperties()->getTransparency(material);
\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
2159 if ( writeIndexOfRefraction )
\r
2161 f32 t = getProperties()->getIndexOfRefraction(material);
\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
2174 Writer->writeClosingTag(fxLabel.c_str());
\r
2175 Writer->writeLineBreak();
\r
2178 void CColladaMeshWriter::writeColorFx(const video::SMaterial & material, const c8 * colorname, E_COLLADA_COLOR_SAMPLER cs, const c8* attr1Name, const c8* attr1Value)
\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
2184 Writer->writeElement(colorname, false, attr1Name, attr1Value);
\r
2185 Writer->writeLineBreak();
\r
2187 writeTextureSampler(idx);
\r
2189 writeColorElement(getColorMapping(material, cs, colType));
\r
2190 Writer->writeClosingTag(colorname);
\r
2191 Writer->writeLineBreak();
\r
2195 void CColladaMeshWriter::writeNode(const c8 * nodeName, const c8 * content)
\r
2197 Writer->writeElement(nodeName, false);
\r
2198 Writer->writeText(content);
\r
2199 Writer->writeClosingTag(nodeName);
\r
2200 Writer->writeLineBreak();
\r
2203 void CColladaMeshWriter::writeFloatElement(irr::f32 value)
\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
2211 void CColladaMeshWriter::writeRotateElement(const irr::core::vector3df& axis, irr::f32 angle)
\r
2213 Writer->writeElement("rotate", false);
\r
2214 irr::core::stringc txt(axis.X);
\r
2215 txt.eraseTrailingFloatZeros();
\r
2217 txt += irr::core::stringc(axis.Y);
\r
2218 txt.eraseTrailingFloatZeros();
\r
2220 txt += irr::core::stringc(axis.Z * -1.f);
\r
2221 txt.eraseTrailingFloatZeros();
\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
2230 void CColladaMeshWriter::writeScaleElement(const irr::core::vector3df& scale)
\r
2232 Writer->writeElement("scale", false);
\r
2233 irr::core::stringc txt(scale.X);
\r
2234 txt.eraseTrailingFloatZeros();
\r
2236 txt += irr::core::stringc(scale.Y);
\r
2237 txt.eraseTrailingFloatZeros();
\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
2246 void CColladaMeshWriter::writeTranslateElement(const irr::core::vector3df& translate)
\r
2248 Writer->writeElement("translate", false);
\r
2249 irr::core::stringc txt(translate.X);
\r
2250 txt.eraseTrailingFloatZeros();
\r
2252 txt += irr::core::stringc(translate.Y);
\r
2253 txt.eraseTrailingFloatZeros();
\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
2262 void CColladaMeshWriter::writeLookAtElement(const irr::core::vector3df& eyePos, const irr::core::vector3df& targetPos, const irr::core::vector3df& upVector)
\r
2264 Writer->writeElement("lookat", false);
\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
2270 Writer->writeClosingTag("lookat");
\r
2271 Writer->writeLineBreak();
\r
2274 void CColladaMeshWriter::writeMatrixElement(const irr::core::matrix4& matrixIrr)
\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
2285 Writer->writeElement("matrix", false);
\r
2286 Writer->writeLineBreak();
\r
2288 for ( int a=0; a<4; ++a )
\r
2290 irr::core::stringc txt;
\r
2291 for ( int b=0; b<4; ++b )
\r
2295 txt += irr::core::stringc(matrix[a*4+b]).eraseTrailingFloatZeros();
\r
2297 Writer->writeText(txt.c_str());
\r
2298 Writer->writeLineBreak();
\r
2301 Writer->writeClosingTag("matrix");
\r
2302 Writer->writeLineBreak();
\r
2305 } // end namespace
\r
2306 } // end namespace
\r