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