]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CB3DMeshWriter.cpp
Move import/export macros into CMake
[irrlicht.git] / source / Irrlicht / CB3DMeshWriter.cpp
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
4 \r
5 // TODO: replace printf's by logging messages\r
6 \r
7 #include "IrrCompileConfig.h"\r
8 \r
9 #ifdef _IRR_COMPILE_WITH_B3D_WRITER_\r
10 \r
11 #include "CB3DMeshWriter.h"\r
12 #include "os.h"\r
13 #include "ISkinnedMesh.h"\r
14 #include "IMeshBuffer.h"\r
15 #include "IWriteFile.h"\r
16 #include "ITexture.h"\r
17 \r
18 \r
19 namespace irr\r
20 {\r
21 namespace scene\r
22 {\r
23 \r
24 using namespace core;\r
25 using namespace video;\r
26 \r
27 CB3DMeshWriter::CB3DMeshWriter()\r
28 {\r
29         #ifdef _DEBUG\r
30         setDebugName("CB3DMeshWriter");\r
31         #endif\r
32 }\r
33 \r
34 \r
35 //! Returns the type of the mesh writer\r
36 EMESH_WRITER_TYPE CB3DMeshWriter::getType() const\r
37 {\r
38     return EMWT_B3D;\r
39 }\r
40 \r
41 \r
42 //! writes a mesh\r
43 bool CB3DMeshWriter::writeMesh(io::IWriteFile* file, IMesh* const mesh, s32 flags)\r
44 {\r
45     if (!file || !mesh)\r
46         return false;\r
47 #ifdef __BIG_ENDIAN__\r
48     os::Printer::log("B3D export does not support big-endian systems.", ELL_ERROR);\r
49     return false;\r
50 #endif\r
51 \r
52     file->write("BB3D", 4);\r
53     file->write("size", 4); // BB3D chunk size, updated later\r
54 \r
55     const u32 version = 1;\r
56     file->write(&version, 4);\r
57 \r
58     //\r
59 \r
60     const u32 numMeshBuffers = mesh->getMeshBufferCount();\r
61     array<SB3dTexture> texs;\r
62     std::map<ITexture *, u32> tex2id;   // TODO: texture pointer as key not sufficient as same texture can have several id's\r
63     u32 texsizes = 0;\r
64     for (u32 i = 0; i < numMeshBuffers; i++)\r
65         {\r
66         const IMeshBuffer * const mb = mesh->getMeshBuffer(i);\r
67         const SMaterial &mat = mb->getMaterial();\r
68 \r
69         for (u32 j = 0; j < MATERIAL_MAX_TEXTURES; j++)\r
70                 {\r
71             if (mat.getTexture(j))\r
72                         {\r
73                 SB3dTexture t;\r
74                                 t.TextureName = core::stringc(mat.getTexture(j)->getName().getPath());\r
75 \r
76                                 // TODO: need some description of Blitz3D texture-flags to figure this out. But Blend should likely depend on material-type.\r
77                                 t.Flags = j == 2 ? 65536 : 1;\r
78                                 t.Blend = 2;\r
79 \r
80                                 // TODO: evaluate texture matrix\r
81                                 t.Xpos = 0;\r
82                                 t.Ypos = 0;\r
83                                 t.Xscale = 1;\r
84                                 t.Yscale = 1;\r
85                                 t.Angle = 0;\r
86 \r
87                 texs.push_back(t);\r
88                 texsizes += 7*4 + t.TextureName.size() + 1;\r
89                 tex2id[mat.getTexture(j)] = texs.size() - 1;\r
90             }\r
91         }\r
92     }\r
93 \r
94     file->write("TEXS", 4);\r
95     file->write(&texsizes, 4);\r
96 \r
97     u32 numTexture = texs.size();\r
98     for (u32 i = 0; i < numTexture; i++)\r
99         {\r
100         file->write(texs[i].TextureName.c_str(), texs[i].TextureName.size() + 1);\r
101         file->write(&texs[i].Flags, 7*4);\r
102     }\r
103 \r
104     //\r
105 \r
106     file->write("BRUS", 4);\r
107     const u32 brushSizeAdress = file->getPos();\r
108     file->write(&brushSizeAdress, 4); // BRUSH chunk size, updated later\r
109 \r
110     const u32 usedtex = MATERIAL_MAX_TEXTURES;\r
111     file->write(&usedtex, 4);\r
112 \r
113     for (u32 i = 0; i < numMeshBuffers; i++)\r
114         {\r
115         const IMeshBuffer * const mb = mesh->getMeshBuffer(i);\r
116         const SMaterial &mat = mb->getMaterial();\r
117 \r
118         file->write("", 1);\r
119 \r
120         float f = 1;\r
121         file->write(&f, 4);\r
122         file->write(&f, 4);\r
123         file->write(&f, 4);\r
124         file->write(&f, 4);\r
125 \r
126         f = 0;\r
127         file->write(&f, 4);\r
128 \r
129         u32 tmp = 1;\r
130         file->write(&tmp, 4);\r
131         tmp = 0;\r
132         file->write(&tmp, 4);\r
133 \r
134         for (u32 j = 0; j < MATERIAL_MAX_TEXTURES; j++)\r
135                 {\r
136                     s32 id = -1;\r
137             if (mat.getTexture(j))\r
138                         {\r
139                 id = tex2id[mat.getTexture(j)];\r
140             }\r
141             file->write(&id, 4);\r
142         }\r
143     }\r
144     writeSizeFrom(file, brushSizeAdress+4, brushSizeAdress); // BRUSH chunk size\r
145 \r
146     file->write("NODE", 4);\r
147     u32 nodeSizeAdress = file->getPos();\r
148     file->write(&nodeSizeAdress, 4); // NODE chunk size, updated later\r
149 \r
150     // Node\r
151     file->write("", 1);\r
152 \r
153     // position\r
154     writeVector3(file, core::vector3df(0.f, 0.f, 0.f));\r
155 \r
156     // scale\r
157     writeVector3(file, core::vector3df(1.f, 1.f, 1.f));\r
158 \r
159     // rotation\r
160     writeQuaternion(file, core::quaternion(0.f, 0.f, 0.f, 1.f));\r
161 \r
162     // Mesh\r
163     file->write("MESH", 4);\r
164     const u32 meshSizeAdress = file->getPos();\r
165     file->write(&meshSizeAdress, 4); // MESH chunk size, updated later\r
166 \r
167         s32 brushID = -1;\r
168     file->write(&brushID, 4);\r
169 \r
170 \r
171 \r
172     // Verts\r
173     file->write("VRTS", 4);\r
174     const u32 verticesSizeAdress = file->getPos();\r
175     file->write(&verticesSizeAdress, 4);\r
176 \r
177     u32 flagsB3D = 3; // 1=normal values present, 2=rgba values present\r
178     file->write(&flagsB3D, 4);\r
179 \r
180     const u32 texcoordsCount = getUVlayerCount(mesh);\r
181     file->write(&texcoordsCount, 4);\r
182     flagsB3D = 2;\r
183     file->write(&flagsB3D, 4);\r
184 \r
185     for (u32 i = 0; i < numMeshBuffers; i++)\r
186     {\r
187         const IMeshBuffer * const mb = mesh->getMeshBuffer(i);\r
188         const u32 numVertices = mb->getVertexCount();\r
189         for (u32 j = 0; j < numVertices; j++)\r
190                 {\r
191             const vector3df &pos = mb->getPosition(j);\r
192             writeVector3(file, pos);\r
193 \r
194             const vector3df &n = mb->getNormal(j);\r
195             writeVector3(file, n);\r
196 \r
197             switch (mb->getVertexType())\r
198                         {\r
199                 case EVT_STANDARD:\r
200                 {\r
201                     S3DVertex *v = (S3DVertex *) mb->getVertices();\r
202                     const SColorf col(v[j].Color);\r
203                     writeColor(file, col);\r
204 \r
205                     const core::vector2df uv1 = v[j].TCoords;\r
206                     writeVector2(file, uv1);\r
207                     if (texcoordsCount == 2)\r
208                     {\r
209                         writeVector2(file, core::vector2df(0.f, 0.f));\r
210                     }\r
211                 }\r
212                 break;\r
213                 case EVT_2TCOORDS:\r
214                 {\r
215                     S3DVertex2TCoords *v = (S3DVertex2TCoords *) mb->getVertices();\r
216                     const SColorf col(v[j].Color);\r
217                     writeColor(file, col);\r
218 \r
219                     const core::vector2df uv1 = v[j].TCoords;\r
220                     writeVector2(file, uv1);\r
221                     const core::vector2df uv2 = v[j].TCoords;\r
222                     writeVector2(file, uv2);\r
223                 }\r
224                 break;\r
225                 case EVT_TANGENTS:\r
226                 {\r
227                     S3DVertexTangents *v = (S3DVertexTangents *) mb->getVertices();\r
228                     const SColorf col(v[j].Color);\r
229                     writeColor(file, col);\r
230 \r
231                     const core::vector2df uv1 = v[j].TCoords;\r
232                     writeVector2(file, uv1);\r
233                     if (texcoordsCount == 2)\r
234                     {\r
235                         writeVector2(file, core::vector2df(0.f, 0.f));\r
236                     }\r
237                 }\r
238                 break;\r
239             }\r
240         }\r
241     }\r
242     writeSizeFrom(file, verticesSizeAdress+4, verticesSizeAdress); // VERT chunk size\r
243 \r
244 \r
245     u32 currentMeshBufferIndex = 0;\r
246     // Tris\r
247     for (u32 i = 0; i < numMeshBuffers; i++)\r
248     {\r
249         const IMeshBuffer * const mb = mesh->getMeshBuffer(i);\r
250         file->write("TRIS", 4);\r
251         const u32 trisSizeAdress = file->getPos();\r
252         file->write(&trisSizeAdress, 4); // TRIS chunk size, updated later\r
253 \r
254         file->write(&i, 4);\r
255 \r
256         u32 numIndices = mb->getIndexCount();\r
257         const u16 * const idx = (u16 *) mb->getIndices();\r
258         for (u32 j = 0; j < numIndices; j += 3)\r
259                 {\r
260             u32 tmp = idx[j] + currentMeshBufferIndex;\r
261             file->write(&tmp, sizeof(u32));\r
262 \r
263             tmp = idx[j + 1] + currentMeshBufferIndex;\r
264             file->write(&tmp, sizeof(u32));\r
265 \r
266             tmp = idx[j + 2] + currentMeshBufferIndex;\r
267             file->write(&tmp, sizeof(u32));\r
268         }\r
269         writeSizeFrom(file, trisSizeAdress+4, trisSizeAdress);  // TRIS chunk size\r
270 \r
271         currentMeshBufferIndex += mb->getVertexCount();\r
272     }\r
273     writeSizeFrom(file, meshSizeAdress+4, meshSizeAdress); // MESH chunk size\r
274 \r
275 \r
276     if(ISkinnedMesh *skinnedMesh = getSkinned(mesh))\r
277     {\r
278         // Write animation data\r
279         f32 animationSpeedMultiplier = 1.f;\r
280         if (!skinnedMesh->isStatic())\r
281         {\r
282             file->write("ANIM", 4);\r
283 \r
284             const u32 animsize = 12;\r
285             file->write(&animsize, 4);\r
286 \r
287             const u32 flags = 0;\r
288             f32 fps = skinnedMesh->getAnimationSpeed();\r
289 \r
290             /* B3D file format use integer as keyframe, so there is some potential issues if the model use float as keyframe (Irrlicht use float) with a low animation FPS value\r
291             So we define a minimum animation FPS value to multiply the frame and FPS value if the FPS of the animation is too low to store the keyframe with integers */\r
292             const int minimumAnimationFPS = 60;\r
293 \r
294             if (fps < minimumAnimationFPS)\r
295             {\r
296                 animationSpeedMultiplier = minimumAnimationFPS / fps;\r
297                 fps = minimumAnimationFPS;\r
298             }\r
299             const u32 frames = static_cast<u32>(skinnedMesh->getFrameCount() * animationSpeedMultiplier);\r
300 \r
301             file->write(&flags, 4);\r
302             file->write(&frames, 4);\r
303             file->write(&fps, 4);\r
304         }\r
305 \r
306         // Write joints\r
307         core::array<ISkinnedMesh::SJoint*> rootJoints = getRootJoints(skinnedMesh);\r
308 \r
309         for (u32 i = 0; i < rootJoints.size(); i++)\r
310         {\r
311             writeJointChunk(file, skinnedMesh, rootJoints[i], animationSpeedMultiplier);\r
312         }\r
313     }\r
314 \r
315     writeSizeFrom(file, nodeSizeAdress+4, nodeSizeAdress); // Node chunk size\r
316         writeSizeFrom(file, 8, 4); // BB3D chunk size\r
317 \r
318     return true;\r
319 }\r
320 \r
321 \r
322 \r
323 void CB3DMeshWriter::writeJointChunk(io::IWriteFile* file, ISkinnedMesh* mesh, ISkinnedMesh::SJoint* joint, f32 animationSpeedMultiplier)\r
324 {\r
325     // Node\r
326     file->write("NODE", 4);\r
327     const u32 nodeSizeAdress = file->getPos();\r
328     file->write(&nodeSizeAdress, 4);\r
329 \r
330 \r
331     core::stringc name = joint->Name;\r
332     file->write(name.c_str(), name.size());\r
333     file->write("", 1);\r
334 \r
335     // Position\r
336     const core::vector3df pos = joint->Animatedposition;\r
337     writeVector3(file, pos);\r
338 \r
339     // Scale\r
340     core::vector3df scale = joint->Animatedscale;\r
341     if (scale == core::vector3df(0, 0, 0))\r
342         scale = core::vector3df(1, 1, 1);\r
343 \r
344     writeVector3(file, scale);\r
345 \r
346     // Rotation\r
347     const core::quaternion quat = joint->Animatedrotation;\r
348     writeQuaternion(file, quat);\r
349 \r
350     // Bone\r
351     file->write("BONE", 4);\r
352     u32 bonesize = 8 * joint->Weights.size();\r
353     file->write(&bonesize, 4);\r
354 \r
355     // Skinning ------------------\r
356     for (u32 i = 0; i < joint->Weights.size(); i++)\r
357     {\r
358         const u32 vertexID = joint->Weights[i].vertex_id;\r
359         const u32 bufferID = joint->Weights[i].buffer_id;\r
360         const f32 weight = joint->Weights[i].strength;\r
361 \r
362         u32 b3dVertexID = vertexID;\r
363         for (u32 j = 0; j < bufferID; j++)\r
364         {\r
365             b3dVertexID += mesh->getMeshBuffer(j)->getVertexCount();\r
366         }\r
367 \r
368         file->write(&b3dVertexID, 4);\r
369         file->write(&weight, 4);\r
370     }\r
371     // ---------------------------\r
372 \r
373     f32 floatBuffer[5];\r
374     // Animation keys\r
375     if (joint->PositionKeys.size())\r
376     {\r
377         file->write("KEYS", 4);\r
378         u32 keysSize = 4 * joint->PositionKeys.size() * 4; // X, Y and Z pos + frame\r
379         keysSize += 4;  // Flag to define the type of the key\r
380         file->write(&keysSize, 4);\r
381 \r
382         u32 flag = 1; // 1 = flag for position keys\r
383         file->write(&flag, 4);\r
384 \r
385         for (u32 i = 0; i < joint->PositionKeys.size(); i++)\r
386         {\r
387             const s32 frame = static_cast<s32>(joint->PositionKeys[i].frame * animationSpeedMultiplier);\r
388             file->write(&frame, 4);\r
389 \r
390             const core::vector3df pos = joint->PositionKeys[i].position;\r
391             pos.getAs3Values(floatBuffer);\r
392             file->write(floatBuffer, 12);\r
393         }\r
394     }\r
395     if (joint->RotationKeys.size())\r
396     {\r
397         file->write("KEYS", 4);\r
398         u32 keysSize = 4 * joint->RotationKeys.size() * 5; // W, X, Y and Z rot + frame\r
399         keysSize += 4; // Flag\r
400         file->write(&keysSize, 4);\r
401 \r
402         u32 flag = 4;\r
403         file->write(&flag, 4);\r
404 \r
405         for (u32 i = 0; i < joint->RotationKeys.size(); i++)\r
406         {\r
407             const s32 frame = static_cast<s32>(joint->RotationKeys[i].frame * animationSpeedMultiplier);\r
408             const core::quaternion rot = joint->RotationKeys[i].rotation;\r
409 \r
410             memcpy(floatBuffer, &frame, 4);\r
411             floatBuffer[1] = rot.W;\r
412             floatBuffer[2] = rot.X;\r
413             floatBuffer[3] = rot.Y;\r
414             floatBuffer[4] = rot.Z;\r
415             file->write(floatBuffer, 20);\r
416         }\r
417     }\r
418     if (joint->ScaleKeys.size())\r
419     {\r
420         file->write("KEYS", 4);\r
421         u32 keysSize = 4 * joint->ScaleKeys.size() * 4; // X, Y and Z scale + frame\r
422         keysSize += 4; // Flag\r
423         file->write(&keysSize, 4);\r
424 \r
425         u32 flag = 2;\r
426         file->write(&flag, 4);\r
427 \r
428         for (u32 i = 0; i < joint->ScaleKeys.size(); i++)\r
429         {\r
430             const s32 frame = static_cast<s32>(joint->ScaleKeys[i].frame * animationSpeedMultiplier);\r
431             file->write(&frame, 4);\r
432 \r
433             const core::vector3df scale = joint->ScaleKeys[i].scale;\r
434             scale.getAs3Values(floatBuffer);\r
435             file->write(floatBuffer, 12);\r
436         }\r
437     }\r
438 \r
439     for (u32 i = 0; i < joint->Children.size(); i++)\r
440     {\r
441         writeJointChunk(file, mesh, joint->Children[i], animationSpeedMultiplier);\r
442     }\r
443 \r
444     writeSizeFrom(file, nodeSizeAdress+4, nodeSizeAdress); // NODE chunk size\r
445 }\r
446 \r
447 \r
448 ISkinnedMesh* CB3DMeshWriter::getSkinned (IMesh *mesh)\r
449 {\r
450         if (mesh->getMeshType() == EAMT_SKINNED)\r
451     {\r
452                 return static_cast<ISkinnedMesh*>(mesh);\r
453     }\r
454     return 0;\r
455 }\r
456 \r
457 core::array<ISkinnedMesh::SJoint*> CB3DMeshWriter::getRootJoints(const ISkinnedMesh* mesh)\r
458 {\r
459     core::array<ISkinnedMesh::SJoint*> roots;\r
460 \r
461     core::array<ISkinnedMesh::SJoint*> allJoints = mesh->getAllJoints();\r
462     for (u32 i = 0; i < allJoints.size(); i++)\r
463     {\r
464         bool isRoot = true;\r
465         ISkinnedMesh::SJoint* testedJoint = allJoints[i];\r
466         for (u32 j = 0; j < allJoints.size(); j++)\r
467         {\r
468            ISkinnedMesh::SJoint* testedJoint2 = allJoints[j];\r
469            for (u32 k = 0; k < testedJoint2->Children.size(); k++)\r
470            {\r
471                if (testedJoint == testedJoint2->Children[k])\r
472                     isRoot = false;\r
473            }\r
474         }\r
475         if (isRoot)\r
476             roots.push_back(testedJoint);\r
477     }\r
478 \r
479     return roots;\r
480 }\r
481 \r
482 u32 CB3DMeshWriter::getUVlayerCount(IMesh* mesh)\r
483 {\r
484     const u32 numBeshBuffers = mesh->getMeshBufferCount();\r
485     for (u32 i = 0; i < numBeshBuffers; i++)\r
486     {\r
487         const IMeshBuffer * const mb = mesh->getMeshBuffer(i);\r
488 \r
489         if (mb->getVertexType() == EVT_2TCOORDS)\r
490         {\r
491             return 2;\r
492         }\r
493     }\r
494     return 1;\r
495 }\r
496 \r
497 void CB3DMeshWriter::writeVector2(io::IWriteFile* file, const core::vector2df& vec2)\r
498 {\r
499     f32 buffer[2] = {vec2.X, vec2.Y};\r
500     file->write(buffer, 8);\r
501 }\r
502 \r
503 void CB3DMeshWriter::writeVector3(io::IWriteFile* file, const core::vector3df& vec3)\r
504 {\r
505     f32 buffer[3];\r
506     vec3.getAs3Values(buffer);\r
507     file->write(buffer, 12);\r
508 }\r
509 \r
510 void CB3DMeshWriter::writeQuaternion(io::IWriteFile* file, const core::quaternion& quat)\r
511 {\r
512     f32 buffer[4] = {quat.W, quat.X, quat.Y, quat.Z};\r
513     file->write(buffer, 16);\r
514 }\r
515 \r
516 void CB3DMeshWriter::writeColor(io::IWriteFile* file, const video::SColorf& color)\r
517 {\r
518     f32 buffer[4] = {color.r, color.g, color.b, color.a};\r
519     file->write(buffer, 16);\r
520 }\r
521 \r
522 // Write the size from a given position to current position at a specific position in the file\r
523 void CB3DMeshWriter::writeSizeFrom(io::IWriteFile* file, const u32 from, const u32 adressToWrite)\r
524 {\r
525     const long back = file->getPos();\r
526     file->seek(adressToWrite);\r
527     const u32 sizeToWrite = back - from;\r
528     file->write(&sizeToWrite, 4);\r
529     file->seek(back);\r
530 }\r
531 \r
532 } // end namespace\r
533 } // end namespace\r
534 \r
535 #endif // _IRR_COMPILE_WITH_B3D_WRITER_\r
536 \r