diff options
Diffstat (limited to '')
-rw-r--r-- | libraries/irrlicht-1.8/source/Irrlicht/CColladaMeshWriter.cpp | 2245 |
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 | |||
25 | namespace irr | ||
26 | { | ||
27 | namespace scene | ||
28 | { | ||
29 | |||
30 | //! Which lighting model should be used in the technique (FX) section when exporting effects (materials) | ||
31 | E_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. | ||
37 | s32 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 | |||
60 | E_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. | ||
82 | video::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 | ||
89 | E_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. | ||
96 | f32 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 | ||
103 | f32 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 | ||
110 | f32 CColladaMeshWriterProperties::getIndexOfRefraction(const video::SMaterial& material) const | ||
111 | { | ||
112 | return -1.f; | ||
113 | } | ||
114 | |||
115 | bool CColladaMeshWriterProperties::isExportable(const irr::scene::ISceneNode * node) const | ||
116 | { | ||
117 | return node && node->isVisible(); | ||
118 | } | ||
119 | |||
120 | IMesh* 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 | ||
140 | bool 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 | |||
161 | CColladaMeshWriterNames::CColladaMeshWriterNames(IColladaMeshWriter * writer) | ||
162 | : ColladaMeshWriter(writer) | ||
163 | { | ||
164 | } | ||
165 | |||
166 | irr::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 | |||
178 | irr::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 | |||
194 | irr::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 | |||
211 | irr::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 | |||
220 | CColladaMeshWriter::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 | |||
250 | CColladaMeshWriter::~CColladaMeshWriter() | ||
251 | { | ||
252 | if (VideoDriver) | ||
253 | VideoDriver->drop(); | ||
254 | |||
255 | if (FileSystem) | ||
256 | FileSystem->drop(); | ||
257 | } | ||
258 | |||
259 | |||
260 | void 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 | ||
272 | EMESH_WRITER_TYPE CColladaMeshWriter::getType() const | ||
273 | { | ||
274 | return EMWT_COLLADA; | ||
275 | } | ||
276 | |||
277 | //! writes a scene starting with the given node | ||
278 | bool 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 | |||
414 | void 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 | |||
437 | void 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 | |||
498 | void 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 | |||
521 | void 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 | |||
559 | void 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 | |||
639 | void 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 | |||
704 | void 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 | |||
726 | void 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 | ||
799 | bool 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 | |||
906 | void 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 | |||
949 | void 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 | |||
955 | void 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 | |||
961 | bool CColladaMeshWriter::hasSecondTextureCoordinates(video::E_VERTEX_TYPE type) const | ||
962 | { | ||
963 | return type == video::EVT_2TCOORDS; | ||
964 | } | ||
965 | |||
966 | void 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 | |||
974 | void 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 | |||
983 | void 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 | |||
991 | void 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 | |||
1002 | irr::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 | |||
1014 | irr::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 | |||
1034 | irr::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 | |||
1042 | irr::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 | |||
1049 | bool 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 | |||
1059 | irr::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 | |||
1069 | irr::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 | |||
1079 | irr::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 | ||
1103 | irr::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 | |||
1110 | irr::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 | |||
1120 | irr::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 | |||
1130 | inline 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 | |||
1138 | bool 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 | |||
1160 | bool 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. | ||
1172 | irr::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. | ||
1193 | irr::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 | |||
1217 | void 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 | |||
1251 | void 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 | |||
1264 | void 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 | |||
1398 | void 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 | |||
1409 | void 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 | |||
1899 | void 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 | |||
1928 | void 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 | |||
1938 | void CColladaMeshWriter::writeColorElement(const video::SColor & col, bool writeAlpha) | ||
1939 | { | ||
1940 | writeColorElement( video::SColorf(col), writeAlpha ); | ||
1941 | } | ||
1942 | |||
1943 | void 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 | |||
1966 | s32 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 | |||
1979 | video::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 | |||
2004 | void 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 | |||
2015 | void 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 | |||
2144 | void 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 | |||
2161 | void 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 | |||
2169 | void 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 | |||
2177 | void 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 | |||
2192 | void 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 | |||
2205 | void 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 | |||
2218 | void 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 | |||