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