]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CMS3DMeshFileLoader.cpp
Fix some more problems with OSX build file.
[irrlicht.git] / source / Irrlicht / CMS3DMeshFileLoader.cpp
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt\r
2 // 2019 additional alignment and big_endian fixes by Corto and Salas00\r
3 // This file is part of the "Irrlicht Engine".\r
4 // For conditions of distribution and use, see copyright notice in irrlicht.h\r
5 \r
6 #include "IrrCompileConfig.h"\r
7 #ifdef _IRR_COMPILE_WITH_MS3D_LOADER_\r
8 \r
9 #include "IReadFile.h"\r
10 #include "os.h"\r
11 #include "CMS3DMeshFileLoader.h"\r
12 #include "CSkinnedMesh.h"\r
13 \r
14 \r
15 namespace irr\r
16 {\r
17 namespace scene\r
18 {\r
19 \r
20 #ifdef _DEBUG\r
21 #define _IRR_DEBUG_MS3D_LOADER_\r
22 #endif\r
23 \r
24 // byte-align structures\r
25 #include "irrpack.h"\r
26 \r
27 namespace {\r
28 // File header\r
29 struct MS3DHeader\r
30 {\r
31         char ID[10];\r
32         int Version;\r
33 } PACK_STRUCT;\r
34 \r
35 // Vertex information\r
36 struct MS3DVertex\r
37 {\r
38         u8 pad1[3];\r
39         u8 Flags;\r
40         float Vertex[3];\r
41         char BoneID;\r
42         u8 RefCount;\r
43         u8 pad2[2];\r
44 } PACK_STRUCT;\r
45 \r
46 #define MS3DVERTEX_NUM_PAD_BYTES 5\r
47 \r
48 // Triangle information\r
49 struct MS3DTriangle\r
50 {\r
51         u16 Flags;\r
52         u16 VertexIndices[3];\r
53         float VertexNormals[3][3];\r
54         float S[3], T[3];\r
55         u8 SmoothingGroup;\r
56         u8 GroupIndex;\r
57         u8 pad1[2];\r
58 } PACK_STRUCT;\r
59 \r
60 #define MS3DTRIANGLE_NUM_PAD_BYTES 2\r
61 \r
62 // Material information\r
63 struct MS3DMaterial\r
64 {\r
65     char Name[32];\r
66     float Ambient[4];\r
67     float Diffuse[4];\r
68     float Specular[4];\r
69     float Emissive[4];\r
70     float Shininess;    // 0.0f - 128.0f\r
71     float Transparency; // 0.0f - 1.0f\r
72     u8 Mode;    // 0, 1, 2 is unused now\r
73     char Texture[128];\r
74     char Alphamap[128];\r
75     u8 pad1[3];\r
76 } PACK_STRUCT;\r
77 \r
78 #define MS3DMATERIAL_NUM_PAD_BYTES 3\r
79 \r
80 // Joint information\r
81 struct MS3DJoint\r
82 {\r
83         u8 pad[3];\r
84         u8 Flags;\r
85         char Name[32];\r
86         char ParentName[32];\r
87         float Rotation[3];\r
88         float Translation[3];\r
89         u16 NumRotationKeyframes;\r
90         u16 NumTranslationKeyframes;\r
91 } PACK_STRUCT;\r
92 \r
93 #define MS3DJOINT_NUM_PAD_BYTES 3\r
94 \r
95 // Keyframe data\r
96 struct MS3DKeyframe\r
97 {\r
98         float Time;\r
99         float Parameter[3];\r
100 } PACK_STRUCT;\r
101 \r
102 // vertex weights in 1.8.x\r
103 struct MS3DVertexWeights\r
104 {\r
105         char boneIds[3];\r
106         u8 weights[3];\r
107 } PACK_STRUCT;\r
108 \r
109 } // end namespace\r
110 \r
111 // Default alignment\r
112 #include "irrunpack.h"\r
113 \r
114 // Get float encoded in little endian in  way not causing troubles when floats have to be memory aligned.\r
115 static inline float get_unaligned_le_float(const u8 *ptr)\r
116 {\r
117     union {\r
118         u8 u[4];\r
119         float f;\r
120     } tmp;\r
121 #ifdef __BIG_ENDIAN__\r
122     tmp.u[0] = ptr[3];\r
123     tmp.u[1] = ptr[2];\r
124     tmp.u[2] = ptr[1];\r
125     tmp.u[3] = ptr[0];\r
126 #else\r
127     tmp.f = *(float*)ptr;\r
128 #endif\r
129     return tmp.f;\r
130 }\r
131 \r
132 \r
133 struct SGroup\r
134 {\r
135         core::stringc Name;\r
136         core::array<u16> VertexIds;\r
137         u16 MaterialIdx;\r
138 };\r
139 \r
140 //! Constructor\r
141 CMS3DMeshFileLoader::CMS3DMeshFileLoader(video::IVideoDriver *driver)\r
142 : Driver(driver), AnimatedMesh(0)\r
143 {\r
144         #ifdef _DEBUG\r
145         setDebugName("CMS3DMeshFileLoader");\r
146         #endif\r
147 }\r
148 \r
149 \r
150 //! returns true if the file maybe is able to be loaded by this class\r
151 //! based on the file extension (e.g. ".bsp")\r
152 bool CMS3DMeshFileLoader::isALoadableFileExtension(const io::path& filename) const\r
153 {\r
154         return core::hasFileExtension ( filename, "ms3d" );\r
155 }\r
156 \r
157 \r
158 //! creates/loads an animated mesh from the file.\r
159 //! \return Pointer to the created mesh. Returns 0 if loading failed.\r
160 //! If you no longer need the mesh, you should call IAnimatedMesh::drop().\r
161 //! See IReferenceCounted::drop() for more information.\r
162 IAnimatedMesh* CMS3DMeshFileLoader::createMesh(io::IReadFile* file)\r
163 {\r
164         if (!file)\r
165                 return 0;\r
166 \r
167         AnimatedMesh = new CSkinnedMesh();\r
168 \r
169         if ( load(file) )\r
170         {\r
171                 AnimatedMesh->finalize();\r
172         }\r
173         else\r
174         {\r
175                 AnimatedMesh->drop();\r
176                 AnimatedMesh = 0;\r
177         }\r
178 \r
179         return AnimatedMesh;\r
180 }\r
181 \r
182 \r
183 //! loads a milkshape file\r
184 bool CMS3DMeshFileLoader::load(io::IReadFile* file)\r
185 {\r
186         if (!file)\r
187                 return false;\r
188 \r
189         // find file size\r
190         const long fileSize = file->getSize();\r
191 \r
192         // read whole file\r
193 \r
194         u8* buffer = new u8[fileSize];\r
195         size_t read = file->read(buffer, fileSize);\r
196         if (read != (size_t)fileSize)\r
197         {\r
198                 delete [] buffer;\r
199                 os::Printer::log("Could not read full file. Loading failed", file->getFileName(), ELL_ERROR);\r
200                 return false;\r
201         }\r
202 \r
203         // read header\r
204 \r
205         const u8 *pPtr = (u8*)((void*)buffer);\r
206         MS3DHeader *pHeader = (MS3DHeader*)pPtr;\r
207         pPtr += sizeof(MS3DHeader);\r
208 \r
209         if ( strncmp( pHeader->ID, "MS3D000000", 10 ) != 0 )\r
210         {\r
211                 delete [] buffer;\r
212                 os::Printer::log("Not a valid Milkshape3D Model File. Loading failed", file->getFileName(), ELL_ERROR);\r
213                 return false;\r
214         }\r
215 \r
216 #ifdef __BIG_ENDIAN__\r
217         pHeader->Version = os::Byteswap::byteswap(pHeader->Version);\r
218 #endif\r
219         if ( pHeader->Version < 3 || pHeader->Version > 4 )\r
220         {\r
221                 delete [] buffer;\r
222                 os::Printer::log("Only Milkshape3D version 3 and 4 (1.3 to 1.8) is supported. Loading failed", file->getFileName(), ELL_ERROR);\r
223                 return false;\r
224         }\r
225 #ifdef _IRR_DEBUG_MS3D_LOADER_\r
226         os::Printer::log("Loaded header version", core::stringc(pHeader->Version).c_str());\r
227 #endif\r
228 \r
229         // get pointers to data\r
230 \r
231         // vertices\r
232         u16 numVertices = *(u16*)pPtr;\r
233 #ifdef __BIG_ENDIAN__\r
234         numVertices = os::Byteswap::byteswap(numVertices);\r
235 #endif\r
236 #ifdef _IRR_DEBUG_MS3D_LOADER_\r
237         os::Printer::log("Load vertices", core::stringc(numVertices).c_str());\r
238 #endif\r
239 \r
240         pPtr += sizeof(u16);\r
241         MS3DVertex *vertices = new MS3DVertex[numVertices];\r
242         if (pPtr + ((sizeof(MS3DVertex) - MS3DVERTEX_NUM_PAD_BYTES) * numVertices) > buffer+fileSize)\r
243         {\r
244                 delete [] buffer;\r
245                 os::Printer::log("Loading failed. Corrupted data found.", file->getFileName(), ELL_ERROR);\r
246                 return false;\r
247         }\r
248         for (u16 tmp=0; tmp<numVertices; ++tmp)\r
249         {\r
250                 //printf("&vertices[tmp].Vertex[0] = %p (%d)\n", &vertices[tmp].Vertex[0], (int)((long long)(&vertices[tmp].Vertex[0]) % 4));\r
251                 memcpy(&vertices[tmp].Flags, pPtr, sizeof(struct MS3DVertex) - MS3DVERTEX_NUM_PAD_BYTES);\r
252 #ifdef __BIG_ENDIAN__\r
253                 vertices[tmp].Vertex[0] = os::Byteswap::byteswap(vertices[tmp].Vertex[0]);\r
254                 vertices[tmp].Vertex[1] = os::Byteswap::byteswap(vertices[tmp].Vertex[1]);\r
255                 vertices[tmp].Vertex[2] = -os::Byteswap::byteswap(vertices[tmp].Vertex[2]);\r
256 #else\r
257                 vertices[tmp].Vertex[2] = -vertices[tmp].Vertex[2];\r
258 #endif\r
259                 // Go to the next vertex structure\r
260                 pPtr += sizeof(struct MS3DVertex) - MS3DVERTEX_NUM_PAD_BYTES;\r
261         }\r
262 \r
263         // triangles\r
264         u16 numTriangles = *(u16*)pPtr;\r
265 #ifdef __BIG_ENDIAN__\r
266         numTriangles = os::Byteswap::byteswap(numTriangles);\r
267 #endif\r
268 #ifdef _IRR_DEBUG_MS3D_LOADER_\r
269         os::Printer::log("Load Triangles", core::stringc(numTriangles).c_str());\r
270 #endif\r
271         pPtr += sizeof(u16);\r
272         MS3DTriangle *triangles = new MS3DTriangle[numTriangles];\r
273         if (pPtr + ((sizeof(MS3DTriangle) - MS3DTRIANGLE_NUM_PAD_BYTES) * numTriangles) > buffer+fileSize)\r
274         {\r
275                 delete [] buffer;\r
276                 os::Printer::log("Loading failed. Corrupted data found.", file->getFileName(), ELL_ERROR);\r
277                 return false;\r
278         }\r
279         for (u16 tmp=0; tmp<numTriangles; ++tmp)\r
280         {\r
281                 memcpy(&triangles[tmp].Flags, pPtr, sizeof(struct MS3DTriangle) - MS3DTRIANGLE_NUM_PAD_BYTES);\r
282 #ifdef __BIG_ENDIAN__\r
283                 triangles[tmp].Flags = os::Byteswap::byteswap(triangles[tmp].Flags);\r
284                 for (u16 j=0; j<3; ++j)\r
285                 {\r
286                         triangles[tmp].VertexIndices[j] = os::Byteswap::byteswap(triangles[tmp].VertexIndices[j]);\r
287                         triangles[tmp].VertexNormals[j][0] = os::Byteswap::byteswap(triangles[tmp].VertexNormals[j][0]);\r
288                         triangles[tmp].VertexNormals[j][1] = os::Byteswap::byteswap(triangles[tmp].VertexNormals[j][1]);\r
289                         triangles[tmp].VertexNormals[j][2] = -os::Byteswap::byteswap(triangles[tmp].VertexNormals[j][2]);\r
290                         triangles[tmp].S[j] = os::Byteswap::byteswap(triangles[tmp].S[j]);\r
291                         triangles[tmp].T[j] = os::Byteswap::byteswap(triangles[tmp].T[j]);\r
292                 }\r
293 #else\r
294                 triangles[tmp].VertexNormals[0][2] = -triangles[tmp].VertexNormals[0][2];\r
295                 triangles[tmp].VertexNormals[1][2] = -triangles[tmp].VertexNormals[1][2];\r
296                 triangles[tmp].VertexNormals[2][2] = -triangles[tmp].VertexNormals[2][2];\r
297 #endif\r
298                 // Go to the next triangle structure\r
299                 pPtr += sizeof(struct MS3DTriangle) - MS3DTRIANGLE_NUM_PAD_BYTES;\r
300         }\r
301 \r
302         // groups\r
303         u16 numGroups = *(u16*)pPtr;\r
304 #ifdef __BIG_ENDIAN__\r
305         numGroups = os::Byteswap::byteswap(numGroups);\r
306 #endif\r
307 #ifdef _IRR_DEBUG_MS3D_LOADER_\r
308         os::Printer::log("Load Groups", core::stringc(numGroups).c_str());\r
309 #endif\r
310         pPtr += sizeof(u16);\r
311 \r
312         core::array<SGroup> groups;\r
313         groups.reallocate(numGroups);\r
314 \r
315         //store groups\r
316         u32 i;\r
317         for (i=0; i<numGroups; ++i)\r
318         {\r
319                 groups.push_back(SGroup());\r
320                 SGroup& grp = groups.getLast();\r
321 \r
322                 // The byte flag is before the name, so add 1\r
323                 grp.Name = ((const c8*) pPtr) + 1;\r
324 \r
325                 pPtr += 33; // name and 1 byte flags\r
326                 u16 triangleCount = *(u16*)pPtr;\r
327 #ifdef __BIG_ENDIAN__\r
328                 triangleCount = os::Byteswap::byteswap(triangleCount);\r
329 #endif\r
330                 pPtr += sizeof(u16);\r
331                 grp.VertexIds.reallocate(triangleCount);\r
332 \r
333                 //pPtr += sizeof(u16) * triangleCount; // triangle indices\r
334                 for (u16 j=0; j<triangleCount; ++j)\r
335                 {\r
336 #ifdef __BIG_ENDIAN__\r
337                         grp.VertexIds.push_back(os::Byteswap::byteswap(*(u16*)pPtr));\r
338 #else\r
339                         grp.VertexIds.push_back(*(u16*)pPtr);\r
340 #endif\r
341                         pPtr += sizeof (u16);\r
342                 }\r
343 \r
344                 grp.MaterialIdx = *(u8*)pPtr;\r
345                 if (grp.MaterialIdx == 255)\r
346                         grp.MaterialIdx = 0;\r
347 \r
348                 pPtr += sizeof(c8); // material index\r
349                 if (pPtr > buffer+fileSize)\r
350                 {\r
351                         delete [] buffer;\r
352                         os::Printer::log("Loading failed. Corrupted data found.", file->getFileName(), ELL_ERROR);\r
353                         return false;\r
354                 }\r
355         }\r
356 \r
357         // load materials\r
358         u16 numMaterials = *(u16*)pPtr;\r
359 #ifdef __BIG_ENDIAN__\r
360         numMaterials = os::Byteswap::byteswap(numMaterials);\r
361 #endif\r
362 #ifdef _IRR_DEBUG_MS3D_LOADER_\r
363         os::Printer::log("Load Materials", core::stringc(numMaterials).c_str());\r
364 #endif\r
365         pPtr += sizeof(u16);\r
366 \r
367         if(numMaterials == 0)\r
368         {\r
369                 // if there are no materials, add at least one buffer\r
370                 AnimatedMesh->addMeshBuffer();\r
371         }\r
372 \r
373         MS3DMaterial *material = new MS3DMaterial;\r
374         for (i=0; i<numMaterials; ++i)\r
375         {\r
376                 memcpy(material, pPtr, sizeof(struct MS3DMaterial) - MS3DMATERIAL_NUM_PAD_BYTES);\r
377 #ifdef __BIG_ENDIAN__\r
378                 for (u16 j=0; j<4; ++j)\r
379                         material->Ambient[j] = os::Byteswap::byteswap(material->Ambient[j]);\r
380                 for (u16 j=0; j<4; ++j)\r
381                         material->Diffuse[j] = os::Byteswap::byteswap(material->Diffuse[j]);\r
382                 for (u16 j=0; j<4; ++j)\r
383                         material->Specular[j] = os::Byteswap::byteswap(material->Specular[j]);\r
384                 for (u16 j=0; j<4; ++j)\r
385                         material->Emissive[j] = os::Byteswap::byteswap(material->Emissive[j]);\r
386                 material->Shininess = os::Byteswap::byteswap(material->Shininess);\r
387                 material->Transparency = os::Byteswap::byteswap(material->Transparency);\r
388 #endif\r
389                 pPtr += (sizeof(MS3DMaterial) - MS3DMATERIAL_NUM_PAD_BYTES);\r
390                 if (pPtr > buffer+fileSize)\r
391                 {\r
392                         delete [] buffer;\r
393                         os::Printer::log("Loading failed. Corrupted data found.", file->getFileName(), ELL_ERROR);\r
394                         return false;\r
395                 }\r
396 \r
397                 scene::SSkinMeshBuffer *tmpBuffer = AnimatedMesh->addMeshBuffer();\r
398 \r
399                 tmpBuffer->Material.MaterialType = video::EMT_SOLID;\r
400 \r
401                 tmpBuffer->Material.AmbientColor = video::SColorf(material->Ambient[0], material->Ambient[1], material->Ambient[2], material->Ambient[3]).toSColor ();\r
402                 tmpBuffer->Material.DiffuseColor = video::SColorf(material->Diffuse[0], material->Diffuse[1], material->Diffuse[2], material->Diffuse[3]).toSColor ();\r
403                 tmpBuffer->Material.EmissiveColor = video::SColorf(material->Emissive[0], material->Emissive[1], material->Emissive[2], material->Emissive[3]).toSColor ();\r
404                 tmpBuffer->Material.SpecularColor = video::SColorf(material->Specular[0], material->Specular[1], material->Specular[2], material->Specular[3]).toSColor ();\r
405                 tmpBuffer->Material.Shininess = material->Shininess;\r
406 \r
407                 core::stringc TexturePath(material->Texture);\r
408                 if (TexturePath.trim()!="")\r
409                 {\r
410                         TexturePath=stripPathFromString(file->getFileName(),true) + stripPathFromString(TexturePath,false);\r
411                         tmpBuffer->Material.setTexture(0, Driver->getTexture(TexturePath));\r
412                 }\r
413 \r
414                 core::stringc AlphamapPath=(const c8*)material->Alphamap;\r
415                 if (AlphamapPath.trim()!="")\r
416                 {\r
417                         AlphamapPath=stripPathFromString(file->getFileName(),true) + stripPathFromString(AlphamapPath,false);\r
418                         tmpBuffer->Material.setTexture(2, Driver->getTexture(AlphamapPath));\r
419                 }\r
420         }\r
421         delete material;\r
422 \r
423         // animation time\r
424 \r
425 \r
426         f32 framesPerSecond = get_unaligned_le_float(pPtr);\r
427 \r
428 #ifdef _IRR_DEBUG_MS3D_LOADER_\r
429         os::Printer::log("FPS", core::stringc(framesPerSecond).c_str());\r
430 #endif\r
431         pPtr += sizeof(float) * 2; // fps and current time\r
432 \r
433         if (framesPerSecond<1.f)\r
434                 framesPerSecond=1.f;\r
435         AnimatedMesh->setAnimationSpeed(framesPerSecond);\r
436 \r
437 // ignore, calculated inside SkinnedMesh\r
438 //      s32 frameCount = *(int*)pPtr;\r
439 #ifdef __BIG_ENDIAN__\r
440 //      frameCount = os::Byteswap::byteswap(frameCount);\r
441 #endif\r
442         pPtr += sizeof(int);\r
443 \r
444         u16 jointCount = *(u16*)pPtr;\r
445 #ifdef __BIG_ENDIAN__\r
446         jointCount = os::Byteswap::byteswap(jointCount);\r
447 #endif\r
448 #ifdef _IRR_DEBUG_MS3D_LOADER_\r
449         os::Printer::log("Joints", core::stringc(jointCount).c_str());\r
450 #endif\r
451         pPtr += sizeof(u16);\r
452         if (pPtr > buffer+fileSize)\r
453         {\r
454                 delete [] buffer;\r
455                 os::Printer::log("Loading failed. Corrupted data found.", file->getFileName(), ELL_ERROR);\r
456                 return false;\r
457         }\r
458 \r
459         core::array<core::stringc> parentNames;\r
460         parentNames.reallocate(jointCount);\r
461 \r
462         // load joints\r
463         for (i=0; i<jointCount; ++i)\r
464         {\r
465                 u32 j;\r
466                 MS3DJoint *pJoint = new MS3DJoint;\r
467                 //printf("&pJoint->Rotation[0] = %p (%d)\n", &pJoint->Rotation[0], (int)((long long)(&pJoint->Rotation[0]) % 4));\r
468                 memcpy(&pJoint->Flags, pPtr, sizeof(MS3DJoint) - MS3DJOINT_NUM_PAD_BYTES);\r
469 \r
470 #ifdef __BIG_ENDIAN__\r
471                 for (j=0; j<3; ++j)\r
472                         pJoint->Rotation[j] = os::Byteswap::byteswap(pJoint->Rotation[j]);\r
473                 for (j=0; j<3; ++j)\r
474                         pJoint->Translation[j] = os::Byteswap::byteswap(pJoint->Translation[j]);\r
475                 pJoint->NumRotationKeyframes= os::Byteswap::byteswap(pJoint->NumRotationKeyframes);\r
476                 pJoint->NumTranslationKeyframes = os::Byteswap::byteswap(pJoint->NumTranslationKeyframes);\r
477 #endif\r
478                 pPtr = pPtr + sizeof(MS3DJoint) - MS3DJOINT_NUM_PAD_BYTES;\r
479                 if (pPtr > buffer+fileSize)\r
480                 {\r
481                         delete [] buffer;\r
482                         os::Printer::log("Loading failed. Corrupted data found.", file->getFileName(), ELL_ERROR);\r
483                         return false;\r
484                 }\r
485 \r
486                 ISkinnedMesh::SJoint *jnt = AnimatedMesh->addJoint();\r
487 \r
488                 jnt->Name = pJoint->Name;\r
489 #ifdef _IRR_DEBUG_MS3D_LOADER_\r
490                 os::Printer::log("Joint", jnt->Name.c_str());\r
491                 os::Printer::log("Rotation keyframes", core::stringc(pJoint->NumRotationKeyframes).c_str());\r
492                 os::Printer::log("Translation keyframes", core::stringc(pJoint->NumTranslationKeyframes).c_str());\r
493 #endif\r
494                 jnt->LocalMatrix.makeIdentity();\r
495                 jnt->LocalMatrix.setRotationRadians(\r
496                         core::vector3df(pJoint->Rotation[0], pJoint->Rotation[1], pJoint->Rotation[2]) );\r
497                 // convert right-handed to left-handed\r
498                 jnt->LocalMatrix[2]=-jnt->LocalMatrix[2];\r
499                 jnt->LocalMatrix[6]=-jnt->LocalMatrix[6];\r
500                 jnt->LocalMatrix[8]=-jnt->LocalMatrix[8];\r
501                 jnt->LocalMatrix[9]=-jnt->LocalMatrix[9];\r
502 \r
503                 jnt->LocalMatrix.setTranslation(\r
504                         core::vector3df(pJoint->Translation[0], pJoint->Translation[1], -pJoint->Translation[2]) );\r
505                 jnt->Animatedposition.set(jnt->LocalMatrix.getTranslation());\r
506                 jnt->Animatedrotation.set(jnt->LocalMatrix.getRotationDegrees());\r
507 \r
508                 parentNames.push_back( (c8*)pJoint->ParentName );\r
509 \r
510                 /*if (pJoint->NumRotationKeyframes ||\r
511                         pJoint->NumTranslationKeyframes)\r
512                         HasAnimation = true;\r
513                  */\r
514 \r
515                 MS3DKeyframe* kf = new MS3DKeyframe;\r
516 \r
517                 // get rotation keyframes\r
518                 const u16 numRotationKeyframes = pJoint->NumRotationKeyframes;\r
519                 for (j=0; j < numRotationKeyframes; ++j)\r
520                 {\r
521                         memcpy(kf, pPtr, sizeof(MS3DKeyframe));\r
522                         //printf("rotation kf = %p (%d)\n", kf, (int)((long long)kf % 4));\r
523 #ifdef __BIG_ENDIAN__\r
524                         kf->Time = os::Byteswap::byteswap(kf->Time);\r
525                         for (u32 l=0; l<3; ++l)\r
526                                 kf->Parameter[l] = os::Byteswap::byteswap(kf->Parameter[l]);\r
527 #endif\r
528                         pPtr += sizeof(MS3DKeyframe);\r
529                         if (pPtr > buffer+fileSize)\r
530                         {\r
531                                 delete [] buffer;\r
532                                 os::Printer::log("Loading failed. Corrupted data found.", file->getFileName(), ELL_ERROR);\r
533                                 return false;\r
534                         }\r
535 \r
536                         ISkinnedMesh::SRotationKey *k=AnimatedMesh->addRotationKey(jnt);\r
537                         k->frame = kf->Time * framesPerSecond-1;\r
538 \r
539                         core::matrix4 tmpMatrix;\r
540 \r
541                         tmpMatrix.setRotationRadians(\r
542                                 core::vector3df(kf->Parameter[0], kf->Parameter[1], kf->Parameter[2]) );\r
543                         // convert right-handed to left-handed\r
544                         tmpMatrix[2]=-tmpMatrix[2];\r
545                         tmpMatrix[6]=-tmpMatrix[6];\r
546                         tmpMatrix[8]=-tmpMatrix[8];\r
547                         tmpMatrix[9]=-tmpMatrix[9];\r
548 \r
549                         tmpMatrix=jnt->LocalMatrix*tmpMatrix;\r
550 \r
551                         // IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched from tmpMatrix to tmpMatrix.getTransposed() for downward compatibility.\r
552                         //                                                                 Not tested so far if this was correct or wrong before quaternion fix!\r
553                         k->rotation  = core::quaternion(tmpMatrix.getTransposed());\r
554                 }\r
555 \r
556                 // get translation keyframes\r
557                 const u16 numTranslationKeyframes = pJoint->NumTranslationKeyframes;\r
558                 for (j=0; j<numTranslationKeyframes; ++j)\r
559                 {\r
560                         memcpy(kf, pPtr, sizeof(MS3DKeyframe));\r
561                         //printf("translation kf = %p (%d)\n", kf, (int)((long long)kf % 4));\r
562 \r
563 #ifdef __BIG_ENDIAN__\r
564                         kf->Time = os::Byteswap::byteswap(kf->Time);\r
565                         for (u32 l=0; l<3; ++l)\r
566                                 kf->Parameter[l] = os::Byteswap::byteswap(kf->Parameter[l]);\r
567 #endif\r
568                         pPtr += sizeof(MS3DKeyframe);\r
569                         if (pPtr > buffer+fileSize)\r
570                         {\r
571                                 delete [] buffer;\r
572                                 os::Printer::log("Loading failed. Corrupted data found.", file->getFileName(), ELL_ERROR);\r
573                                 return false;\r
574                         }\r
575 \r
576                         ISkinnedMesh::SPositionKey *k=AnimatedMesh->addPositionKey(jnt);\r
577                         k->frame = kf->Time * framesPerSecond-1;\r
578 \r
579                         k->position = core::vector3df\r
580                                 (kf->Parameter[0]+pJoint->Translation[0],\r
581                                  kf->Parameter[1]+pJoint->Translation[1],\r
582                                  -kf->Parameter[2]-pJoint->Translation[2]);\r
583                 }\r
584 \r
585                 delete kf;\r
586                 delete pJoint;\r
587         }\r
588 \r
589         core::array<MS3DVertexWeights> vertexWeights;\r
590         f32 weightFactor=0;\r
591 \r
592         if (jointCount && (pHeader->Version == 4) && (pPtr < buffer+fileSize))\r
593         {\r
594                 s32 subVersion = *(s32*)pPtr; // comment subVersion, always 1\r
595 #ifdef __BIG_ENDIAN__\r
596                 subVersion = os::Byteswap::byteswap(subVersion);\r
597 #endif\r
598                 pPtr += sizeof(s32);\r
599 \r
600                 for (u32 j=0; j<4; ++j) // four comment groups\r
601                 {\r
602 #ifdef _IRR_DEBUG_MS3D_LOADER_\r
603                         os::Printer::log("Skipping comment group", core::stringc(j+1).c_str());\r
604 #endif\r
605                         u32 numComments = *(u32*)pPtr;\r
606 #ifdef __BIG_ENDIAN__\r
607                         numComments = os::Byteswap::byteswap(numComments);\r
608 #endif\r
609                         pPtr += sizeof(u32);\r
610                         for (i=0; i<numComments; ++i)\r
611                         {\r
612                                 // according to scorpiomidget this field does\r
613                                 // not exist for model comments. So avoid to\r
614                                 // read it\r
615                                 if (j!=3)\r
616                                         pPtr += sizeof(s32); // index\r
617                                 s32 commentLength = *(s32*)pPtr;\r
618 #ifdef __BIG_ENDIAN__\r
619                                 commentLength = os::Byteswap::byteswap(commentLength);\r
620 #endif\r
621                                 pPtr += sizeof(s32);\r
622                                 pPtr += commentLength;\r
623                         }\r
624 \r
625                         if (pPtr > buffer+fileSize)\r
626                         {\r
627                                 delete [] buffer;\r
628                                 os::Printer::log("Loading failed. Corrupted data found.", file->getFileName(), ELL_ERROR);\r
629                                 return false;\r
630                         }\r
631                 }\r
632 \r
633                 if (pPtr < buffer+fileSize)\r
634                 {\r
635                         subVersion = *(s32*)pPtr; // vertex subVersion, 1 or 2\r
636 #ifdef __BIG_ENDIAN__\r
637                         subVersion = os::Byteswap::byteswap(subVersion);\r
638 #endif\r
639                         if (subVersion==1)\r
640                                 weightFactor=1.f/255.f;\r
641                         else\r
642                                 weightFactor=1.f/100.f;\r
643                         pPtr += sizeof(s32);\r
644 \r
645 #ifdef _IRR_DEBUG_MS3D_LOADER_\r
646                         os::Printer::log("Reading vertex weights");\r
647 #endif\r
648                         // read vertex weights, ignoring data 'extra' from 1.8.2\r
649                         vertexWeights.reallocate(numVertices);\r
650                         const char offset = (subVersion==1)?6:10;\r
651                         for (i=0; i<numVertices; ++i)\r
652                         {\r
653                                 vertexWeights.push_back(*(MS3DVertexWeights*)pPtr);\r
654                                 pPtr += offset;\r
655                         }\r
656 \r
657                         if (pPtr > buffer+fileSize)\r
658                         {\r
659                                 delete [] buffer;\r
660                                 os::Printer::log("Loading failed. Corrupted data found.", file->getFileName(), ELL_ERROR);\r
661                                 return false;\r
662                         }\r
663                 }\r
664 \r
665                 if (pPtr < buffer+fileSize)\r
666                 {\r
667                         subVersion = *(s32*)pPtr; // joint subVersion, 1 or 2\r
668 #ifdef __BIG_ENDIAN__\r
669                         subVersion = os::Byteswap::byteswap(subVersion);\r
670 #endif\r
671                         pPtr += sizeof(s32);\r
672                         // skip joint colors\r
673 #ifdef _IRR_DEBUG_MS3D_LOADER_\r
674                         os::Printer::log("Skip joint color");\r
675 #endif\r
676                         pPtr += 3*sizeof(float)*jointCount;\r
677 \r
678                         if (pPtr > buffer+fileSize)\r
679                         {\r
680                                 delete [] buffer;\r
681                                 os::Printer::log("Loading failed. Corrupted data found", file->getFileName(), ELL_ERROR);\r
682                                 return false;\r
683                         }\r
684                 }\r
685 \r
686                 if (pPtr < buffer+fileSize)\r
687                 {\r
688                         subVersion = *(s32*)pPtr; // model subVersion, 1 or 2\r
689 #ifdef __BIG_ENDIAN__\r
690                         subVersion = os::Byteswap::byteswap(subVersion);\r
691 #endif\r
692                         pPtr += sizeof(s32);\r
693 #ifdef _IRR_DEBUG_MS3D_LOADER_\r
694                         os::Printer::log("Skip model extra information");\r
695 #endif\r
696                         // now the model extra information would follow\r
697                         // we also skip this for now\r
698                 }\r
699         }\r
700 \r
701         //find parent of every joint\r
702         for (u32 jointnum=0; jointnum<AnimatedMesh->getAllJoints().size(); ++jointnum)\r
703         {\r
704                 for (u32 j2=0; j2<AnimatedMesh->getAllJoints().size(); ++j2)\r
705                 {\r
706                         if (jointnum != j2 && parentNames[jointnum] == AnimatedMesh->getAllJoints()[j2]->Name )\r
707                         {\r
708                                 AnimatedMesh->getAllJoints()[j2]->Children.push_back(AnimatedMesh->getAllJoints()[jointnum]);\r
709                                 break;\r
710                         }\r
711                 }\r
712         }\r
713 \r
714         // create vertices and indices, attach them to the joints.\r
715         video::S3DVertex v;\r
716         core::array<video::S3DVertex> *Vertices;\r
717         core::array<u16> Indices;\r
718 \r
719         for (i=0; i<numTriangles; ++i)\r
720         {\r
721                 u32 tmp = groups[triangles[i].GroupIndex].MaterialIdx;\r
722                 Vertices = &AnimatedMesh->getMeshBuffers()[tmp]->Vertices_Standard;\r
723 \r
724                 for (s32 j = 2; j!=-1; --j)\r
725                 {\r
726                         const u32 vertidx = triangles[i].VertexIndices[j];\r
727 \r
728                         v.TCoords.X = triangles[i].S[j];\r
729                         v.TCoords.Y = triangles[i].T[j];\r
730 \r
731                         v.Normal.X = triangles[i].VertexNormals[j][0];\r
732                         v.Normal.Y = triangles[i].VertexNormals[j][1];\r
733                         v.Normal.Z = triangles[i].VertexNormals[j][2];\r
734 \r
735                         if(triangles[i].GroupIndex < groups.size() &&\r
736                                         groups[triangles[i].GroupIndex].MaterialIdx < AnimatedMesh->getMeshBuffers().size())\r
737                                 v.Color = AnimatedMesh->getMeshBuffers()[groups[triangles[i].GroupIndex].MaterialIdx]->Material.DiffuseColor;\r
738                         else\r
739                                 v.Color.set(255,255,255,255);\r
740 \r
741                         v.Pos.X = vertices[vertidx].Vertex[0];\r
742                         v.Pos.Y = vertices[vertidx].Vertex[1];\r
743                         v.Pos.Z = vertices[vertidx].Vertex[2];\r
744 \r
745                         // check if we already have this vertex in our vertex array\r
746                         s32 index = -1;\r
747                         for (u32 iV = 0; iV < Vertices->size(); ++iV)\r
748                         {\r
749                                 if (v == (*Vertices)[iV])\r
750                                 {\r
751                                         index = (s32)iV;\r
752                                         break;\r
753                                 }\r
754                         }\r
755 \r
756                         if (index == -1)\r
757                         {\r
758                                 index = Vertices->size();\r
759                                 const u32 matidx = groups[triangles[i].GroupIndex].MaterialIdx;\r
760                                 if (vertexWeights.size()==0)\r
761                                 {\r
762                                         const s32 boneid = vertices[vertidx].BoneID;\r
763                                         if ((u32)boneid < AnimatedMesh->getAllJoints().size())\r
764                                         {\r
765                                                 ISkinnedMesh::SWeight *w=AnimatedMesh->addWeight(AnimatedMesh->getAllJoints()[boneid]);\r
766                                                 w->buffer_id = matidx;\r
767                                                 w->strength = 1.0f;\r
768                                                 w->vertex_id = index;\r
769                                         }\r
770                                 }\r
771                                 else if (jointCount) // new weights from 1.8.x\r
772                                 {\r
773                                         f32 sum = 1.0f;\r
774                                         s32 boneid = vertices[vertidx].BoneID;\r
775                                         if (((u32)boneid < AnimatedMesh->getAllJoints().size()) && (vertexWeights[vertidx].weights[0] != 0))\r
776                                         {\r
777                                                 ISkinnedMesh::SWeight *w=AnimatedMesh->addWeight(AnimatedMesh->getAllJoints()[boneid]);\r
778                                                 w->buffer_id = matidx;\r
779                                                 sum -= (w->strength = vertexWeights[vertidx].weights[0]*weightFactor);\r
780                                                 w->vertex_id = index;\r
781                                         }\r
782                                         boneid = vertexWeights[vertidx].boneIds[0];\r
783                                         if (((u32)boneid < AnimatedMesh->getAllJoints().size()) && (vertexWeights[vertidx].weights[1] != 0))\r
784                                         {\r
785                                                 ISkinnedMesh::SWeight *w=AnimatedMesh->addWeight(AnimatedMesh->getAllJoints()[boneid]);\r
786                                                 w->buffer_id = matidx;\r
787                                                 sum -= (w->strength = vertexWeights[vertidx].weights[1]*weightFactor);\r
788                                                 w->vertex_id = index;\r
789                                         }\r
790                                         boneid = vertexWeights[vertidx].boneIds[1];\r
791                                         if (((u32)boneid < AnimatedMesh->getAllJoints().size()) && (vertexWeights[vertidx].weights[2] != 0))\r
792                                         {\r
793                                                 ISkinnedMesh::SWeight *w=AnimatedMesh->addWeight(AnimatedMesh->getAllJoints()[boneid]);\r
794                                                 w->buffer_id = matidx;\r
795                                                 sum -= (w->strength = vertexWeights[vertidx].weights[2]*weightFactor);\r
796                                                 w->vertex_id = index;\r
797                                         }\r
798                                         boneid = vertexWeights[vertidx].boneIds[2];\r
799                                         if (((u32)boneid < AnimatedMesh->getAllJoints().size()) && (sum > 0.f))\r
800                                         {\r
801                                                 ISkinnedMesh::SWeight *w=AnimatedMesh->addWeight(AnimatedMesh->getAllJoints()[boneid]);\r
802                                                 w->buffer_id = matidx;\r
803                                                 w->strength = sum;\r
804                                                 w->vertex_id = index;\r
805                                         }\r
806                                         // fallback, if no bone chosen. Seems to be an error in the specs\r
807                                         boneid = vertices[vertidx].BoneID;\r
808                                         if ((sum == 1.f) && ((u32)boneid < AnimatedMesh->getAllJoints().size()))\r
809                                         {\r
810                                                 ISkinnedMesh::SWeight *w=AnimatedMesh->addWeight(AnimatedMesh->getAllJoints()[boneid]);\r
811                                                 w->buffer_id = matidx;\r
812                                                 w->strength = 1.f;\r
813                                                 w->vertex_id = index;\r
814                                         }\r
815                                 }\r
816 \r
817                                 Vertices->push_back(v);\r
818                         }\r
819                         Indices.push_back(index);\r
820                 }\r
821         }\r
822 \r
823         //create groups\r
824         s32 iIndex = -1;\r
825         for (i=0; i<groups.size(); ++i)\r
826         {\r
827                 SGroup& grp = groups[i];\r
828 \r
829                 if (grp.MaterialIdx >= AnimatedMesh->getMeshBuffers().size())\r
830                         grp.MaterialIdx = 0;\r
831 \r
832                 core::array<u16>& indices = AnimatedMesh->getMeshBuffers()[grp.MaterialIdx]->Indices;\r
833 \r
834                 for (u32 k=0; k < grp.VertexIds.size(); ++k)\r
835                         for (u32 l=0; l<3; ++l)\r
836                                 indices.push_back(Indices[++iIndex]);\r
837         }\r
838 \r
839         delete [] buffer;\r
840         delete [] triangles;\r
841         delete [] vertices;\r
842 \r
843         return true;\r
844 }\r
845 \r
846 \r
847 core::stringc CMS3DMeshFileLoader::stripPathFromString(const core::stringc& inString, bool returnPath) const\r
848 {\r
849         s32 slashIndex=inString.findLast('/'); // forward slash\r
850         s32 backSlash=inString.findLast('\\'); // back slash\r
851 \r
852         if (backSlash>slashIndex) slashIndex=backSlash;\r
853 \r
854         if (slashIndex==-1)//no slashes found\r
855         {\r
856                 if (returnPath)\r
857                         return core::stringc(); //no path to return\r
858                 else\r
859                         return inString;\r
860         }\r
861 \r
862         if (returnPath)\r
863                 return inString.subString(0, slashIndex + 1);\r
864         else\r
865                 return inString.subString(slashIndex+1, inString.size() - (slashIndex+1));\r
866 }\r
867 \r
868 \r
869 } // end namespace scene\r
870 } // end namespace irr\r
871 \r
872 #endif\r