1 // Copyright (C) 2014 Lauri Kasanen
\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: replace printf's by logging messages
\r
7 #include "IrrCompileConfig.h"
\r
9 #ifdef _IRR_COMPILE_WITH_B3D_WRITER_
\r
11 #include "CB3DMeshWriter.h"
\r
13 #include "ISkinnedMesh.h"
\r
14 #include "IMeshBuffer.h"
\r
15 #include "IWriteFile.h"
\r
16 #include "ITexture.h"
\r
25 using namespace core;
\r
26 using namespace video;
\r
28 CB3DMeshWriter::CB3DMeshWriter()
\r
31 setDebugName("CB3DMeshWriter");
\r
36 //! Returns the type of the mesh writer
\r
37 EMESH_WRITER_TYPE CB3DMeshWriter::getType() const
\r
44 bool CB3DMeshWriter::writeMesh(io::IWriteFile* file, IMesh* const mesh, s32 flags)
\r
48 #ifdef __BIG_ENDIAN__
\r
49 os::Printer::log("B3D export does not support big-endian systems.", ELL_ERROR);
\r
54 file->write("BB3D", 4);
\r
55 file->write(&Size, sizeof(u32)); // Updated later once known.
\r
58 write(file, &version, sizeof(int));
\r
62 const u32 numBeshBuffers = mesh->getMeshBufferCount();
\r
63 array<SB3dTexture> texs;
\r
64 map<ITexture *, u32> tex2id; // TODO: texture pointer as key not sufficient as same texture can have several id's
\r
66 for (u32 i = 0; i < numBeshBuffers; i++)
\r
68 const IMeshBuffer * const mb = mesh->getMeshBuffer(i);
\r
69 const SMaterial &mat = mb->getMaterial();
\r
71 for (u32 j = 0; j < MATERIAL_MAX_TEXTURES; j++)
\r
73 if (mat.getTexture(j))
\r
76 t.TextureName = core::stringc(mat.getTexture(j)->getName().getPath());
\r
78 // TODO: need some description of Blitz3D texture-flags to figure this out. But Blend should likely depend on material-type.
\r
79 t.Flags = j == 2 ? 65536 : 1;
\r
82 // TODO: evaluate texture matrix
\r
90 texsizes += 7*4 + t.TextureName.size() + 1;
\r
91 tex2id[mat.getTexture(j)] = texs.size() - 1;
\r
96 write(file, "TEXS", 4);
\r
97 write(file, &texsizes, 4);
\r
99 u32 numTexture = texs.size();
\r
100 for (u32 i = 0; i < numTexture; i++)
\r
102 write(file, texs[i].TextureName.c_str(), texs[i].TextureName.size() + 1);
\r
103 write(file, &texs[i].Flags, 7*4);
\r
108 const u32 brushsize = (7 * 4 + 1) * numBeshBuffers + numBeshBuffers * 4 * MATERIAL_MAX_TEXTURES + 4;
\r
109 write(file, "BRUS", 4);
\r
110 write(file, &brushsize, 4);
\r
111 u32 brushcheck = Size;
\r
112 const u32 usedtex = MATERIAL_MAX_TEXTURES;
\r
113 write(file, &usedtex, 4);
\r
115 for (u32 i = 0; i < numBeshBuffers; i++)
\r
117 const IMeshBuffer * const mb = mesh->getMeshBuffer(i);
\r
118 const SMaterial &mat = mb->getMaterial();
\r
120 write(file, "", 1);
\r
123 write(file, &f, 4);
\r
124 write(file, &f, 4);
\r
125 write(file, &f, 4);
\r
126 write(file, &f, 4);
\r
129 write(file, &f, 4);
\r
132 write(file, &tmp, 4);
\r
134 write(file, &tmp, 4);
\r
136 for (u32 j = 0; j < MATERIAL_MAX_TEXTURES; j++)
\r
138 if (mat.getTexture(j))
\r
140 const u32 id = tex2id[mat.getTexture(j)];
\r
141 write(file, &id, 4);
\r
146 write(file, &id, 4);
\r
152 brushcheck = Size - brushcheck;
\r
153 if (brushcheck != brushsize)
\r
155 printf("Failed in brush size calculation, size %u advanced %u\n",
\r
156 brushsize, brushcheck);
\r
159 write(file, "NODE", 4);
\r
161 // Calculate node size
\r
162 u32 nodesize = 41 + 8 + 4 + 8;
\r
165 if(ISkinnedMesh *skinnedMesh = getSkinned(mesh))
\r
167 if (!skinnedMesh->isStatic())
\r
172 const core::array<ISkinnedMesh::SJoint*> rootJoints = getRootJoints(skinnedMesh);
\r
173 for (u32 i = 0; i < rootJoints.size(); i++)
\r
175 bonesSize += getJointChunkSize(skinnedMesh, rootJoints[i]);
\r
177 nodesize += bonesSize;
\r
179 // -------------------
\r
186 const u32 texcoords = getUVlayerCount(mesh);
\r
187 for (u32 i = 0; i < numBeshBuffers; i++)
\r
190 const IMeshBuffer * const mb = mesh->getMeshBuffer(i);
\r
192 nodesize += mb->getVertexCount() * 10 * 4;
\r
194 nodesize += mb->getVertexCount() * texcoords * 2 * 4;
\r
195 nodesize += mb->getIndexCount() * 4;
\r
197 write(file, &nodesize, 4);
\r
198 u32 nodecheck = Size;
\r
201 write(file, "", 1);
\r
203 write(file, &f, 4);
\r
204 write(file, &f, 4);
\r
205 write(file, &f, 4);
\r
208 write(file, &f, 4);
\r
209 write(file, &f, 4);
\r
210 write(file, &f, 4);
\r
212 write(file, &f, 4);
\r
214 write(file, &f, 4);
\r
215 write(file, &f, 4);
\r
216 write(file, &f, 4);
\r
219 write(file, "MESH", 4);
\r
220 const u32 meshsize = nodesize - 41 - 8 - bonesSize;
\r
221 write(file, &meshsize, 4);
\r
223 write(file, &brushID, 4);
\r
228 write(file, "VRTS", 4);
\r
231 for (u32 i = 0; i < numBeshBuffers; i++)
\r
233 const IMeshBuffer * const mb = mesh->getMeshBuffer(i);
\r
235 vertsize += mb->getVertexCount() * 10 * 4 +
\r
236 mb->getVertexCount() * texcoords * 2 * 4;
\r
238 write(file, &vertsize, 4);
\r
239 u32 vertcheck = Size;
\r
242 write(file, &flagsB3D, 4);
\r
244 write(file, &texcoords, 4);
\r
246 write(file, &flagsB3D, 4);
\r
248 for (u32 i = 0; i < numBeshBuffers; i++)
\r
250 const IMeshBuffer * const mb = mesh->getMeshBuffer(i);
\r
251 irr::u32 numVertices = mb->getVertexCount();
\r
252 for (u32 j = 0; j < numVertices; j++)
\r
254 const vector3df &pos = mb->getPosition(j);
\r
255 write(file, &pos.X, 4);
\r
256 write(file, &pos.Y, 4);
\r
257 write(file, &pos.Z, 4);
\r
259 const vector3df &n = mb->getNormal(j);
\r
260 write(file, &n.X, 4);
\r
261 write(file, &n.Y, 4);
\r
262 write(file, &n.Z, 4);
\r
264 const u32 zero = 0;
\r
265 switch (mb->getVertexType())
\r
269 S3DVertex *v = (S3DVertex *) mb->getVertices();
\r
270 const SColorf col(v[j].Color);
\r
271 write(file, &col.r, 4);
\r
272 write(file, &col.g, 4);
\r
273 write(file, &col.b, 4);
\r
274 write(file, &col.a, 4);
\r
276 write(file, &v[j].TCoords.X, 4);
\r
277 write(file, &v[j].TCoords.Y, 4);
\r
278 if (texcoords == 2)
\r
280 write(file, &zero, 4);
\r
281 write(file, &zero, 4);
\r
287 S3DVertex2TCoords *v = (S3DVertex2TCoords *) mb->getVertices();
\r
288 const SColorf col(v[j].Color);
\r
289 write(file, &col.r, 4);
\r
290 write(file, &col.g, 4);
\r
291 write(file, &col.b, 4);
\r
292 write(file, &col.a, 4);
\r
294 write(file, &v[j].TCoords.X, 4);
\r
295 write(file, &v[j].TCoords.Y, 4);
\r
296 write(file, &v[j].TCoords2.X, 4);
\r
297 write(file, &v[j].TCoords2.Y, 4);
\r
302 S3DVertexTangents *v = (S3DVertexTangents *) mb->getVertices();
\r
303 const SColorf col(v[j].Color);
\r
304 write(file, &col.r, 4);
\r
305 write(file, &col.g, 4);
\r
306 write(file, &col.b, 4);
\r
307 write(file, &col.a, 4);
\r
309 write(file, &v[j].TCoords.X, 4);
\r
310 write(file, &v[j].TCoords.Y, 4);
\r
311 if (texcoords == 2)
\r
313 write(file, &zero, 4);
\r
314 write(file, &zero, 4);
\r
322 vertcheck = Size - vertcheck;
\r
323 if (vertcheck != vertsize)
\r
325 printf("Failed in vertex size calculation, size %u advanced %u\n",
\r
326 vertsize, vertcheck);
\r
329 u32 currentMeshBufferIndex = 0;
\r
331 for (u32 i = 0; i < numBeshBuffers; i++)
\r
333 const IMeshBuffer * const mb = mesh->getMeshBuffer(i);
\r
334 write(file, "TRIS", 4);
\r
335 const u32 trisize = 4 + mb->getIndexCount() * 4;
\r
336 write(file, &trisize, 4);
\r
338 u32 tricheck = Size;
\r
340 write(file, &i, 4);
\r
342 u32 numIndices = mb->getIndexCount();
\r
343 const u16 * const idx = (u16 *) mb->getIndices();
\r
344 for (u32 j = 0; j < numIndices; j += 3)
\r
346 u32 tmp = idx[j] + currentMeshBufferIndex;
\r
347 write(file, &tmp, sizeof(u32));
\r
349 tmp = idx[j + 1] + currentMeshBufferIndex;
\r
350 write(file, &tmp, sizeof(u32));
\r
352 tmp = idx[j + 2] + currentMeshBufferIndex;
\r
353 write(file, &tmp, sizeof(u32));
\r
356 // Check that tris calculation was ok
\r
357 tricheck = Size - tricheck;
\r
358 if (tricheck != trisize)
\r
360 printf("Failed in tris size calculation, size %u advanced %u\n",
\r
361 trisize, tricheck);
\r
364 currentMeshBufferIndex += mb->getVertexCount();
\r
367 if(ISkinnedMesh *skinnedMesh = getSkinned(mesh))
\r
369 // Write animation data
\r
370 if (!skinnedMesh->isStatic())
\r
372 write(file, "ANIM", 4);
\r
374 const u32 animsize = 12;
\r
375 write(file, &animsize, 4);
\r
377 const u32 flags = 0;
\r
378 const u32 frames = skinnedMesh->getFrameCount();
\r
379 const f32 fps = skinnedMesh->getAnimationSpeed();
\r
381 write(file, &flags, 4);
\r
382 write(file, &frames, 4);
\r
383 write(file, &fps, 4);
\r
387 core::array<ISkinnedMesh::SJoint*> rootJoints = getRootJoints(skinnedMesh);
\r
389 for (u32 i = 0; i < rootJoints.size(); i++)
\r
391 writeJointChunk(file, skinnedMesh, rootJoints[i]);
\r
395 // Check that node calculation was ok
\r
396 nodecheck = Size - nodecheck;
\r
397 if (nodecheck != nodesize)
\r
399 printf("Failed in node size calculation, size %u advanced %u\n",
\r
400 nodesize, nodecheck);
\r
403 file->write(&Size, 4);
\r
410 void CB3DMeshWriter::writeJointChunk(io::IWriteFile* file, ISkinnedMesh* mesh , ISkinnedMesh::SJoint* joint)
\r
413 write(file, "NODE", 4);
\r
415 // Calculate node size
\r
416 u32 nodesize = getJointChunkSize(mesh, joint);
\r
417 nodesize -= 8; // The declaration + size of THIS chunk shouldn't be added to the size
\r
419 write(file, &nodesize, 4);
\r
422 core::stringc name = joint->Name;
\r
423 write(file, name.c_str(), name.size());
\r
424 write(file, "", 1);
\r
426 core::vector3df pos = joint->Animatedposition;
\r
428 write(file, &pos.X, 4);
\r
429 write(file, &pos.Y, 4);
\r
430 write(file, &pos.Z, 4);
\r
433 core::vector3df scale = joint->Animatedscale;
\r
434 if (scale == core::vector3df(0, 0, 0))
\r
435 scale = core::vector3df(1, 1, 1);
\r
437 write(file, &scale.X, 4);
\r
438 write(file, &scale.Y, 4);
\r
439 write(file, &scale.Z, 4);
\r
442 core::quaternion quat = joint->Animatedrotation;
\r
443 write(file, &quat.W, 4);
\r
444 write(file, &quat.X, 4);
\r
445 write(file, &quat.Y, 4);
\r
446 write(file, &quat.Z, 4);
\r
449 write(file, "BONE", 4);
\r
450 u32 bonesize = 8 * joint->Weights.size();
\r
451 write(file, &bonesize, 4);
\r
453 // Skinning ------------------
\r
454 for (u32 i = 0; i < joint->Weights.size(); i++)
\r
456 const u32 vertexID = joint->Weights[i].vertex_id;
\r
457 const u32 bufferID = joint->Weights[i].buffer_id;
\r
458 const f32 weight = joint->Weights[i].strength;
\r
460 u32 b3dVertexID = vertexID;
\r
461 for (u32 j = 0; j < bufferID; j++)
\r
463 b3dVertexID += mesh->getMeshBuffer(j)->getVertexCount();
\r
466 write(file, &b3dVertexID, 4);
\r
467 write(file, &weight, 4);
\r
469 // ---------------------------
\r
472 if (joint->PositionKeys.size())
\r
474 write(file, "KEYS", 4);
\r
475 u32 keysSize = 4 * joint->PositionKeys.size() * 4; // X, Y and Z pos + frame
\r
476 keysSize += 4; // Flag to define the type of the key
\r
477 write(file, &keysSize, 4);
\r
479 u32 flag = 1; // 1 = flag for position keys
\r
480 write(file, &flag, 4);
\r
482 for (u32 i = 0; i < joint->PositionKeys.size(); i++)
\r
484 const s32 frame = static_cast<s32>(joint->PositionKeys[i].frame);
\r
485 const core::vector3df pos = joint->PositionKeys[i].position;
\r
487 write (file, &frame, 4);
\r
489 write (file, &pos.X, 4);
\r
490 write (file, &pos.Y, 4);
\r
491 write (file, &pos.Z, 4);
\r
495 if (joint->RotationKeys.size())
\r
497 write(file, "KEYS", 4);
\r
498 u32 keysSize = 4 * joint->RotationKeys.size() * 5; // W, X, Y and Z rot + frame
\r
499 keysSize += 4; // Flag
\r
500 write(file, &keysSize, 4);
\r
503 write(file, &flag, 4);
\r
505 for (u32 i = 0; i < joint->RotationKeys.size(); i++)
\r
507 const s32 frame = static_cast<s32>(joint->RotationKeys[i].frame);
\r
508 const core::quaternion rot = joint->RotationKeys[i].rotation;
\r
510 write (file, &frame, 4);
\r
512 write (file, &rot.W, 4);
\r
513 write (file, &rot.X, 4);
\r
514 write (file, &rot.Y, 4);
\r
515 write (file, &rot.Z, 4);
\r
518 if (joint->ScaleKeys.size())
\r
520 write(file, "KEYS", 4);
\r
521 u32 keysSize = 4 * joint->ScaleKeys.size() * 4; // X, Y and Z scale + frame
\r
522 keysSize += 4; // Flag
\r
523 write(file, &keysSize, 4);
\r
526 write(file, &flag, 4);
\r
528 for (u32 i = 0; i < joint->ScaleKeys.size(); i++)
\r
530 const s32 frame = static_cast<s32>(joint->ScaleKeys[i].frame);
\r
531 const core::vector3df scale = joint->ScaleKeys[i].scale;
\r
533 write (file, &frame, 4);
\r
535 write (file, &scale.X, 4);
\r
536 write (file, &scale.Y, 4);
\r
537 write (file, &scale.Z, 4);
\r
541 for (u32 i = 0; i < joint->Children.size(); i++)
\r
543 writeJointChunk(file, mesh, joint->Children[i]);
\r
548 ISkinnedMesh* CB3DMeshWriter::getSkinned (IMesh *mesh)
\r
550 if (mesh->getMeshType() == EAMT_SKINNED)
\r
552 return static_cast<ISkinnedMesh*>(mesh);
\r
557 u32 CB3DMeshWriter::getJointChunkSize(const ISkinnedMesh* mesh, ISkinnedMesh::SJoint* joint)
\r
559 u32 chunkSize = 8 + 40; // Chunk declaration + chunk data
\r
560 chunkSize += joint->Name.size() + 1; // the NULL character at the end of the string
\r
562 u32 boneSize = joint->Weights.size() * 8; // vertex_id + weight = 8 bits per weight block
\r
563 boneSize += 8; // declaration + size of he BONE chunk
\r
566 if (joint->PositionKeys.size() != 0)
\r
568 keysSize += 8; // KEYS + chunk size
\r
569 keysSize += 4; // flags
\r
571 keysSize += (joint->PositionKeys.size() * 16);
\r
573 if (joint->RotationKeys.size() != 0)
\r
575 keysSize += 8; // KEYS + chunk size
\r
576 keysSize += 4; // flags
\r
578 keysSize += (joint->RotationKeys.size() * 20);
\r
580 if (joint->ScaleKeys.size() != 0)
\r
582 keysSize += 8; // KEYS + chunk size
\r
583 keysSize += 4; // flags
\r
585 keysSize += (joint->ScaleKeys.size() * 16);
\r
588 chunkSize += boneSize;
\r
589 chunkSize += keysSize;
\r
591 for (u32 i = 0; i < joint->Children.size(); ++i)
\r
593 chunkSize += (getJointChunkSize(mesh, joint->Children[i]));
\r
598 core::array<ISkinnedMesh::SJoint*> CB3DMeshWriter::getRootJoints(const ISkinnedMesh* mesh)
\r
600 core::array<ISkinnedMesh::SJoint*> roots;
\r
602 core::array<ISkinnedMesh::SJoint*> allJoints = mesh->getAllJoints();
\r
603 for (u32 i = 0; i < allJoints.size(); i++)
\r
605 bool isRoot = true;
\r
606 ISkinnedMesh::SJoint* testedJoint = allJoints[i];
\r
607 for (u32 j = 0; j < allJoints.size(); j++)
\r
609 ISkinnedMesh::SJoint* testedJoint2 = allJoints[j];
\r
610 for (u32 k = 0; k < testedJoint2->Children.size(); k++)
\r
612 if (testedJoint == testedJoint2->Children[k])
\r
617 roots.push_back(testedJoint);
\r
623 u32 CB3DMeshWriter::getUVlayerCount(IMesh* mesh)
\r
625 const u32 numBeshBuffers = mesh->getMeshBufferCount();
\r
626 for (u32 i = 0; i < numBeshBuffers; i++)
\r
628 const IMeshBuffer * const mb = mesh->getMeshBuffer(i);
\r
630 if (mb->getVertexType() == EVT_2TCOORDS)
\r
638 void CB3DMeshWriter::write(io::IWriteFile* file, const void *ptr, const u32 bytes)
\r
640 file->write(ptr, bytes);
\r
647 #endif // _IRR_COMPILE_WITH_B3D_WRITER_
\r