]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CB3DMeshFileLoader.cpp
ca86a6a4326d70239336472b06ca2032e6c05c5a
[irrlicht.git] / source / Irrlicht / CB3DMeshFileLoader.cpp
1 // Copyright (C) 2006-2012 Luke Hoschke\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 // B3D Mesh loader\r
6 // File format designed by Mark Sibly for the Blitz3D engine and has been\r
7 // declared public domain\r
8 \r
9 #include "IrrCompileConfig.h"\r
10 #ifdef _IRR_COMPILE_WITH_B3D_LOADER_\r
11 \r
12 #include "CB3DMeshFileLoader.h"\r
13 \r
14 #include "IVideoDriver.h"\r
15 #include "IFileSystem.h"\r
16 #include "os.h"\r
17 \r
18 #ifdef _DEBUG\r
19 #define _B3D_READER_DEBUG\r
20 #endif\r
21 \r
22 namespace irr\r
23 {\r
24 namespace scene\r
25 {\r
26 \r
27 //! Constructor\r
28 CB3DMeshFileLoader::CB3DMeshFileLoader(scene::ISceneManager* smgr)\r
29 : AnimatedMesh(0), B3DFile(0), VerticesStart(0), NormalsInFile(false),\r
30         HasVertexColors(false), ShowWarning(true)\r
31 {\r
32         #ifdef _DEBUG\r
33         setDebugName("CB3DMeshFileLoader");\r
34         #endif\r
35 }\r
36 \r
37 \r
38 //! returns true if the file maybe is able to be loaded by this class\r
39 //! based on the file extension (e.g. ".bsp")\r
40 bool CB3DMeshFileLoader::isALoadableFileExtension(const io::path& filename) const\r
41 {\r
42         return core::hasFileExtension ( filename, "b3d" );\r
43 }\r
44 \r
45 \r
46 //! creates/loads an animated mesh from the file.\r
47 //! \return Pointer to the created mesh. Returns 0 if loading failed.\r
48 //! If you no longer need the mesh, you should call IAnimatedMesh::drop().\r
49 //! See IReferenceCounted::drop() for more information.\r
50 IAnimatedMesh* CB3DMeshFileLoader::createMesh(io::IReadFile* file)\r
51 {\r
52         if (!file)\r
53                 return 0;\r
54 \r
55         B3DFile = file;\r
56         AnimatedMesh = new scene::CSkinnedMesh();\r
57         ShowWarning = true; // If true a warning is issued if too many textures are used\r
58         VerticesStart=0;\r
59 \r
60         if ( load() )\r
61         {\r
62                 AnimatedMesh->finalize();\r
63         }\r
64         else\r
65         {\r
66                 AnimatedMesh->drop();\r
67                 AnimatedMesh = 0;\r
68         }\r
69 \r
70         return AnimatedMesh;\r
71 }\r
72 \r
73 \r
74 bool CB3DMeshFileLoader::load()\r
75 {\r
76         B3dStack.clear();\r
77 \r
78         NormalsInFile=false;\r
79         HasVertexColors=false;\r
80 \r
81         //------ Get header ------\r
82 \r
83         SB3dChunkHeader header;\r
84         B3DFile->read(&header, sizeof(header));\r
85 #ifdef __BIG_ENDIAN__\r
86         header.size = os::Byteswap::byteswap(header.size);\r
87 #endif\r
88 \r
89         if ( strncmp( header.name, "BB3D", 4 ) != 0 )\r
90         {\r
91                 os::Printer::log("File is not a b3d file. Loading failed (No header found)", B3DFile->getFileName(), ELL_ERROR);\r
92                 return false;\r
93         }\r
94 \r
95         // Add main chunk...\r
96         B3dStack.push_back(SB3dChunk(header, B3DFile->getPos()-8));\r
97 \r
98         // Get file version, but ignore it, as it's not important with b3d files...\r
99         s32 fileVersion;\r
100         B3DFile->read(&fileVersion, sizeof(fileVersion));\r
101 #ifdef __BIG_ENDIAN__\r
102         fileVersion = os::Byteswap::byteswap(fileVersion);\r
103 #endif\r
104 \r
105         //------ Read main chunk ------\r
106 \r
107         while ( (B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos() )\r
108         {\r
109                 B3DFile->read(&header, sizeof(header));\r
110 #ifdef __BIG_ENDIAN__\r
111                 header.size = os::Byteswap::byteswap(header.size);\r
112 #endif\r
113                 B3dStack.push_back(SB3dChunk(header, B3DFile->getPos()-8));\r
114 \r
115                 if ( strncmp( B3dStack.getLast().name, "TEXS", 4 ) == 0 )\r
116                 {\r
117                         if (!readChunkTEXS())\r
118                                 return false;\r
119                 }\r
120                 else if ( strncmp( B3dStack.getLast().name, "BRUS", 4 ) == 0 )\r
121                 {\r
122                         if (!readChunkBRUS())\r
123                                 return false;\r
124                 }\r
125                 else if ( strncmp( B3dStack.getLast().name, "NODE", 4 ) == 0 )\r
126                 {\r
127                         if (!readChunkNODE((CSkinnedMesh::SJoint*)0) )\r
128                                 return false;\r
129                 }\r
130                 else\r
131                 {\r
132                         os::Printer::log("Unknown chunk found in mesh base - skipping");\r
133                         if (!B3DFile->seek(B3dStack.getLast().startposition + B3dStack.getLast().length))\r
134                                 return false;\r
135                         B3dStack.erase(B3dStack.size()-1);\r
136                 }\r
137         }\r
138 \r
139         B3dStack.clear();\r
140 \r
141         BaseVertices.clear();\r
142         AnimatedVertices_VertexID.clear();\r
143         AnimatedVertices_BufferID.clear();\r
144 \r
145         Materials.clear();\r
146         Textures.clear();\r
147 \r
148         return true;\r
149 }\r
150 \r
151 \r
152 bool CB3DMeshFileLoader::readChunkNODE(CSkinnedMesh::SJoint *inJoint)\r
153 {\r
154         CSkinnedMesh::SJoint *joint = AnimatedMesh->addJoint(inJoint);\r
155         readString(joint->Name);\r
156 \r
157 #ifdef _B3D_READER_DEBUG\r
158         core::stringc logStr;\r
159         for ( u32 i=1; i < B3dStack.size(); ++i )\r
160                 logStr += "-";\r
161         logStr += "read ChunkNODE";\r
162         os::Printer::log(logStr.c_str(), joint->Name.c_str(), ELL_DEBUG);\r
163 #endif\r
164 \r
165         f32 position[3], scale[3], rotation[4];\r
166 \r
167         readFloats(position, 3);\r
168         readFloats(scale, 3);\r
169         readFloats(rotation, 4);\r
170 \r
171         joint->Animatedposition = core::vector3df(position[0],position[1],position[2]) ;\r
172         joint->Animatedscale = core::vector3df(scale[0],scale[1],scale[2]);\r
173         joint->Animatedrotation = core::quaternion(rotation[1], rotation[2], rotation[3], rotation[0]);\r
174 \r
175         //Build LocalMatrix:\r
176 \r
177         core::matrix4 positionMatrix;\r
178         positionMatrix.setTranslation( joint->Animatedposition );\r
179         core::matrix4 scaleMatrix;\r
180         scaleMatrix.setScale( joint->Animatedscale );\r
181         core::matrix4 rotationMatrix;\r
182         joint->Animatedrotation.getMatrix_transposed(rotationMatrix);\r
183 \r
184         joint->LocalMatrix = positionMatrix * rotationMatrix * scaleMatrix;\r
185 \r
186         if (inJoint)\r
187                 joint->GlobalMatrix = inJoint->GlobalMatrix * joint->LocalMatrix;\r
188         else\r
189                 joint->GlobalMatrix = joint->LocalMatrix;\r
190 \r
191         while(B3dStack.getLast().startposition + B3dStack.getLast().length > B3DFile->getPos()) // this chunk repeats\r
192         {\r
193                 SB3dChunkHeader header;\r
194                 B3DFile->read(&header, sizeof(header));\r
195 #ifdef __BIG_ENDIAN__\r
196                 header.size = os::Byteswap::byteswap(header.size);\r
197 #endif\r
198 \r
199                 B3dStack.push_back(SB3dChunk(header, B3DFile->getPos()-8));\r
200 \r
201                 if ( strncmp( B3dStack.getLast().name, "NODE", 4 ) == 0 )\r
202                 {\r
203                         if (!readChunkNODE(joint))\r
204                                 return false;\r
205                 }\r
206                 else if ( strncmp( B3dStack.getLast().name, "MESH", 4 ) == 0 )\r
207                 {\r
208                         VerticesStart=BaseVertices.size();\r
209                         if (!readChunkMESH(joint))\r
210                                 return false;\r
211                 }\r
212                 else if ( strncmp( B3dStack.getLast().name, "BONE", 4 ) == 0 )\r
213                 {\r
214                         if (!readChunkBONE(joint))\r
215                                 return false;\r
216                 }\r
217                 else if ( strncmp( B3dStack.getLast().name, "KEYS", 4 ) == 0 )\r
218                 {\r
219                         if(!readChunkKEYS(joint))\r
220                                 return false;\r
221                 }\r
222                 else if ( strncmp( B3dStack.getLast().name, "ANIM", 4 ) == 0 )\r
223                 {\r
224                         if (!readChunkANIM())\r
225                                 return false;\r
226                 }\r
227                 else\r
228                 {\r
229                         os::Printer::log("Unknown chunk found in node chunk - skipping");\r
230                         if (!B3DFile->seek(B3dStack.getLast().startposition + B3dStack.getLast().length))\r
231                                 return false;\r
232                         B3dStack.erase(B3dStack.size()-1);\r
233                 }\r
234         }\r
235 \r
236         B3dStack.erase(B3dStack.size()-1);\r
237 \r
238         return true;\r
239 }\r
240 \r
241 \r
242 bool CB3DMeshFileLoader::readChunkMESH(CSkinnedMesh::SJoint *inJoint)\r
243 {\r
244 #ifdef _B3D_READER_DEBUG\r
245         core::stringc logStr;\r
246         for ( u32 i=1; i < B3dStack.size(); ++i )\r
247                 logStr += "-";\r
248         logStr += "read ChunkMESH";\r
249         os::Printer::log(logStr.c_str(), ELL_DEBUG);\r
250 #endif\r
251 \r
252         s32 brushID;\r
253         B3DFile->read(&brushID, sizeof(brushID));\r
254 #ifdef __BIG_ENDIAN__\r
255         brushID = os::Byteswap::byteswap(brushID);\r
256 #endif\r
257 \r
258         NormalsInFile=false;\r
259         HasVertexColors=false;\r
260 \r
261         while((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) //this chunk repeats\r
262         {\r
263                 SB3dChunkHeader header;\r
264                 B3DFile->read(&header, sizeof(header));\r
265 #ifdef __BIG_ENDIAN__\r
266                 header.size = os::Byteswap::byteswap(header.size);\r
267 #endif\r
268 \r
269                 B3dStack.push_back(SB3dChunk(header, B3DFile->getPos()-8));\r
270 \r
271                 if ( strncmp( B3dStack.getLast().name, "VRTS", 4 ) == 0 )\r
272                 {\r
273                         if (!readChunkVRTS(inJoint))\r
274                                 return false;\r
275                 }\r
276                 else if ( strncmp( B3dStack.getLast().name, "TRIS", 4 ) == 0 )\r
277                 {\r
278                         scene::SSkinMeshBuffer *meshBuffer = AnimatedMesh->addMeshBuffer();\r
279 \r
280                         if (brushID!=-1)\r
281                         {\r
282                                 meshBuffer->Material=Materials[brushID].Material;\r
283                         }\r
284 \r
285                         if(readChunkTRIS(meshBuffer,AnimatedMesh->getMeshBuffers().size()-1, VerticesStart)==false)\r
286                                 return false;\r
287 \r
288                         if (!NormalsInFile)\r
289                         {\r
290                                 s32 i;\r
291 \r
292                                 for ( i=0; i<(s32)meshBuffer->Indices.size(); i+=3)\r
293                                 {\r
294                                         core::plane3df p(meshBuffer->getVertex(meshBuffer->Indices[i+0])->Pos,\r
295                                                         meshBuffer->getVertex(meshBuffer->Indices[i+1])->Pos,\r
296                                                         meshBuffer->getVertex(meshBuffer->Indices[i+2])->Pos);\r
297 \r
298                                         meshBuffer->getVertex(meshBuffer->Indices[i+0])->Normal += p.Normal;\r
299                                         meshBuffer->getVertex(meshBuffer->Indices[i+1])->Normal += p.Normal;\r
300                                         meshBuffer->getVertex(meshBuffer->Indices[i+2])->Normal += p.Normal;\r
301                                 }\r
302 \r
303                                 for ( i = 0; i<(s32)meshBuffer->getVertexCount(); ++i )\r
304                                 {\r
305                                         meshBuffer->getVertex(i)->Normal.normalize();\r
306                                         BaseVertices[VerticesStart+i].Normal=meshBuffer->getVertex(i)->Normal;\r
307                                 }\r
308                         }\r
309                 }\r
310                 else\r
311                 {\r
312                         os::Printer::log("Unknown chunk found in mesh - skipping");\r
313                         if (!B3DFile->seek(B3dStack.getLast().startposition + B3dStack.getLast().length))\r
314                                 return false;\r
315                         B3dStack.erase(B3dStack.size()-1);\r
316                 }\r
317         }\r
318 \r
319         B3dStack.erase(B3dStack.size()-1);\r
320 \r
321         return true;\r
322 }\r
323 \r
324 \r
325 /*\r
326 VRTS:\r
327   int flags                   ;1=normal values present, 2=rgba values present\r
328   int tex_coord_sets          ;texture coords per vertex (eg: 1 for simple U/V) max=8\r
329                                 but we only support 3\r
330   int tex_coord_set_size      ;components per set (eg: 2 for simple U/V) max=4\r
331   {\r
332   float x,y,z                 ;always present\r
333   float nx,ny,nz              ;vertex normal: present if (flags&1)\r
334   float red,green,blue,alpha  ;vertex color: present if (flags&2)\r
335   float tex_coords[tex_coord_sets][tex_coord_set_size]  ;tex coords\r
336   }\r
337 */\r
338 bool CB3DMeshFileLoader::readChunkVRTS(CSkinnedMesh::SJoint *inJoint)\r
339 {\r
340 #ifdef _B3D_READER_DEBUG\r
341         core::stringc logStr;\r
342         for ( u32 i=1; i < B3dStack.size(); ++i )\r
343                 logStr += "-";\r
344         logStr += "ChunkVRTS";\r
345         os::Printer::log(logStr.c_str(), ELL_DEBUG);\r
346 #endif\r
347 \r
348         const s32 max_tex_coords = 3;\r
349         s32 flags, tex_coord_sets, tex_coord_set_size;\r
350 \r
351         B3DFile->read(&flags, sizeof(flags));\r
352         B3DFile->read(&tex_coord_sets, sizeof(tex_coord_sets));\r
353         B3DFile->read(&tex_coord_set_size, sizeof(tex_coord_set_size));\r
354 #ifdef __BIG_ENDIAN__\r
355         flags = os::Byteswap::byteswap(flags);\r
356         tex_coord_sets = os::Byteswap::byteswap(tex_coord_sets);\r
357         tex_coord_set_size = os::Byteswap::byteswap(tex_coord_set_size);\r
358 #endif\r
359 \r
360         if (tex_coord_sets >= max_tex_coords || tex_coord_set_size >= 4) // Something is wrong\r
361         {\r
362                 os::Printer::log("tex_coord_sets or tex_coord_set_size too big", B3DFile->getFileName(), ELL_ERROR);\r
363                 return false;\r
364         }\r
365 \r
366         //------ Allocate Memory, for speed -----------//\r
367 \r
368         s32 numberOfReads = 3;\r
369 \r
370         if (flags & 1)\r
371         {\r
372                 NormalsInFile = true;\r
373                 numberOfReads += 3;\r
374         }\r
375         if (flags & 2)\r
376         {\r
377                 numberOfReads += 4;\r
378                 HasVertexColors=true;\r
379         }\r
380 \r
381         numberOfReads += tex_coord_sets*tex_coord_set_size;\r
382 \r
383         const s32 memoryNeeded = (B3dStack.getLast().length / sizeof(f32)) / numberOfReads;\r
384 \r
385         BaseVertices.reallocate(memoryNeeded + BaseVertices.size() + 1);\r
386         AnimatedVertices_VertexID.reallocate(memoryNeeded + AnimatedVertices_VertexID.size() + 1);\r
387 \r
388         //--------------------------------------------//\r
389 \r
390         while( (B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats\r
391         {\r
392                 f32 position[3];\r
393                 f32 normal[3]={0.f, 0.f, 0.f};\r
394                 f32 color[4]={1.0f, 1.0f, 1.0f, 1.0f};\r
395                 f32 tex_coords[max_tex_coords][4];\r
396 \r
397                 readFloats(position, 3);\r
398 \r
399                 if (flags & 1)\r
400                         readFloats(normal, 3);\r
401                 if (flags & 2)\r
402                         readFloats(color, 4);\r
403 \r
404                 for (s32 i=0; i<tex_coord_sets; ++i)\r
405                         readFloats(tex_coords[i], tex_coord_set_size);\r
406 \r
407                 f32 tu=0.0f, tv=0.0f;\r
408                 if (tex_coord_sets >= 1 && tex_coord_set_size >= 2)\r
409                 {\r
410                         tu=tex_coords[0][0];\r
411                         tv=tex_coords[0][1];\r
412                 }\r
413 \r
414                 f32 tu2=0.0f, tv2=0.0f;\r
415                 if (tex_coord_sets>1 && tex_coord_set_size>1)\r
416                 {\r
417                         tu2=tex_coords[1][0];\r
418                         tv2=tex_coords[1][1];\r
419                 }\r
420 \r
421                 // Create Vertex...\r
422                 video::S3DVertex2TCoords Vertex(position[0], position[1], position[2],\r
423                                 normal[0], normal[1], normal[2],\r
424                                 video::SColorf(color[0], color[1], color[2], color[3]).toSColor(),\r
425                                 tu, tv, tu2, tv2);\r
426 \r
427                 // Transform the Vertex position by nested node...\r
428                 inJoint->GlobalMatrix.transformVect(Vertex.Pos);\r
429                 inJoint->GlobalMatrix.rotateVect(Vertex.Normal);\r
430 \r
431                 //Add it...\r
432                 BaseVertices.push_back(Vertex);\r
433 \r
434                 AnimatedVertices_VertexID.push_back(-1);\r
435                 AnimatedVertices_BufferID.push_back(-1);\r
436         }\r
437 \r
438         B3dStack.erase(B3dStack.size()-1);\r
439 \r
440         return true;\r
441 }\r
442 \r
443 \r
444 bool CB3DMeshFileLoader::readChunkTRIS(scene::SSkinMeshBuffer *meshBuffer, u32 meshBufferID, s32 vertices_Start)\r
445 {\r
446 #ifdef _B3D_READER_DEBUG\r
447         core::stringc logStr;\r
448         for ( u32 i=1; i < B3dStack.size(); ++i )\r
449                 logStr += "-";\r
450         logStr += "ChunkTRIS";\r
451         os::Printer::log(logStr.c_str(), ELL_DEBUG);\r
452 #endif\r
453 \r
454         bool showVertexWarning=false;\r
455 \r
456         s32 triangle_brush_id; // Note: Irrlicht can't have different brushes for each triangle (using a workaround)\r
457         B3DFile->read(&triangle_brush_id, sizeof(triangle_brush_id));\r
458 #ifdef __BIG_ENDIAN__\r
459         triangle_brush_id = os::Byteswap::byteswap(triangle_brush_id);\r
460 #endif\r
461 \r
462         SB3dMaterial *B3dMaterial;\r
463 \r
464         if (triangle_brush_id != -1)\r
465         {\r
466                 B3dMaterial = &Materials[triangle_brush_id];\r
467                 meshBuffer->Material = B3dMaterial->Material;\r
468         }\r
469         else\r
470                 B3dMaterial = 0;\r
471 \r
472         const s32 memoryNeeded = B3dStack.getLast().length / sizeof(s32);\r
473         meshBuffer->Indices.reallocate(memoryNeeded + meshBuffer->Indices.size() + 1);\r
474 \r
475         while((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats\r
476         {\r
477                 s32 vertex_id[3];\r
478 \r
479                 B3DFile->read(vertex_id, 3*sizeof(s32));\r
480 #ifdef __BIG_ENDIAN__\r
481                 vertex_id[0] = os::Byteswap::byteswap(vertex_id[0]);\r
482                 vertex_id[1] = os::Byteswap::byteswap(vertex_id[1]);\r
483                 vertex_id[2] = os::Byteswap::byteswap(vertex_id[2]);\r
484 #endif\r
485 \r
486                 //Make Ids global:\r
487                 vertex_id[0] += vertices_Start;\r
488                 vertex_id[1] += vertices_Start;\r
489                 vertex_id[2] += vertices_Start;\r
490 \r
491                 for(s32 i=0; i<3; ++i)\r
492                 {\r
493                         if ((u32)vertex_id[i] >= AnimatedVertices_VertexID.size())\r
494                         {\r
495                                 os::Printer::log("Illegal vertex index found", B3DFile->getFileName(), ELL_ERROR);\r
496                                 return false;\r
497                         }\r
498 \r
499                         if (AnimatedVertices_VertexID[ vertex_id[i] ] != -1)\r
500                         {\r
501                                 if ( AnimatedVertices_BufferID[ vertex_id[i] ] != (s32)meshBufferID ) //If this vertex is linked in a different meshbuffer\r
502                                 {\r
503                                         AnimatedVertices_VertexID[ vertex_id[i] ] = -1;\r
504                                         AnimatedVertices_BufferID[ vertex_id[i] ] = -1;\r
505                                         showVertexWarning=true;\r
506                                 }\r
507                         }\r
508                         if (AnimatedVertices_VertexID[ vertex_id[i] ] == -1) //If this vertex is not in the meshbuffer\r
509                         {\r
510                                 //Check for lightmapping:\r
511                                 if (BaseVertices[ vertex_id[i] ].TCoords2 != core::vector2df(0.f,0.f))\r
512                                         meshBuffer->convertTo2TCoords(); //Will only affect the meshbuffer the first time this is called\r
513 \r
514                                 //Add the vertex to the meshbuffer:\r
515                                 if (meshBuffer->VertexType == video::EVT_STANDARD)\r
516                                         meshBuffer->Vertices_Standard.push_back( BaseVertices[ vertex_id[i] ] );\r
517                                 else\r
518                                         meshBuffer->Vertices_2TCoords.push_back(BaseVertices[ vertex_id[i] ] );\r
519 \r
520                                 //create vertex id to meshbuffer index link:\r
521                                 AnimatedVertices_VertexID[ vertex_id[i] ] = meshBuffer->getVertexCount()-1;\r
522                                 AnimatedVertices_BufferID[ vertex_id[i] ] = meshBufferID;\r
523 \r
524                                 if (B3dMaterial)\r
525                                 {\r
526                                         // Apply Material/Color/etc...\r
527                                         video::S3DVertex *Vertex=meshBuffer->getVertex(meshBuffer->getVertexCount()-1);\r
528 \r
529                                         if (!HasVertexColors)\r
530                                                 Vertex->Color=B3dMaterial->Material.DiffuseColor;\r
531                                         else if (Vertex->Color.getAlpha() == 255)\r
532                                                 Vertex->Color.setAlpha( (s32)(B3dMaterial->alpha * 255.0f) );\r
533 \r
534                                         // Use texture's scale\r
535                                         if (B3dMaterial->Textures[0])\r
536                                         {\r
537                                                 Vertex->TCoords.X *= B3dMaterial->Textures[0]->Xscale;\r
538                                                 Vertex->TCoords.Y *= B3dMaterial->Textures[0]->Yscale;\r
539                                         }\r
540                                         /*\r
541                                         if (B3dMaterial->Textures[1])\r
542                                         {\r
543                                                 Vertex->TCoords2.X *=B3dMaterial->Textures[1]->Xscale;\r
544                                                 Vertex->TCoords2.Y *=B3dMaterial->Textures[1]->Yscale;\r
545                                         }\r
546                                         */\r
547                                 }\r
548                         }\r
549                 }\r
550 \r
551                 meshBuffer->Indices.push_back( AnimatedVertices_VertexID[ vertex_id[0] ] );\r
552                 meshBuffer->Indices.push_back( AnimatedVertices_VertexID[ vertex_id[1] ] );\r
553                 meshBuffer->Indices.push_back( AnimatedVertices_VertexID[ vertex_id[2] ] );\r
554         }\r
555 \r
556         B3dStack.erase(B3dStack.size()-1);\r
557 \r
558         if (showVertexWarning)\r
559                 os::Printer::log("B3dMeshLoader: Warning, different meshbuffers linking to the same vertex, this will cause problems with animated meshes");\r
560 \r
561         return true;\r
562 }\r
563 \r
564 \r
565 bool CB3DMeshFileLoader::readChunkBONE(CSkinnedMesh::SJoint *inJoint)\r
566 {\r
567 #ifdef _B3D_READER_DEBUG\r
568         core::stringc logStr;\r
569         for ( u32 i=1; i < B3dStack.size(); ++i )\r
570                 logStr += "-";\r
571         logStr += "read ChunkBONE";\r
572         os::Printer::log(logStr.c_str(), ELL_DEBUG);\r
573 #endif\r
574 \r
575         if (B3dStack.getLast().length > 8)\r
576         {\r
577                 while((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats\r
578                 {\r
579                         u32 globalVertexID;\r
580                         f32 strength;\r
581                         B3DFile->read(&globalVertexID, sizeof(globalVertexID));\r
582                         B3DFile->read(&strength, sizeof(strength));\r
583 #ifdef __BIG_ENDIAN__\r
584                         globalVertexID = os::Byteswap::byteswap(globalVertexID);\r
585                         strength = os::Byteswap::byteswap(strength);\r
586 #endif\r
587                         globalVertexID += VerticesStart;\r
588 \r
589                         if (AnimatedVertices_VertexID[globalVertexID]==-1)\r
590                         {\r
591                                 os::Printer::log("B3dMeshLoader: Weight has bad vertex id (no link to meshbuffer index found)");\r
592                         }\r
593                         else if (strength >0)\r
594                         {\r
595                                 CSkinnedMesh::SWeight *weight=AnimatedMesh->addWeight(inJoint);\r
596                                 weight->strength=strength;\r
597                                 //Find the meshbuffer and Vertex index from the Global Vertex ID:\r
598                                 weight->vertex_id = AnimatedVertices_VertexID[globalVertexID];\r
599                                 weight->buffer_id = AnimatedVertices_BufferID[globalVertexID];\r
600                         }\r
601                 }\r
602         }\r
603 \r
604         B3dStack.erase(B3dStack.size()-1);\r
605         return true;\r
606 }\r
607 \r
608 \r
609 bool CB3DMeshFileLoader::readChunkKEYS(CSkinnedMesh::SJoint *inJoint)\r
610 {\r
611 #ifdef _B3D_READER_DEBUG\r
612         // Only print first, that's just too much output otherwise\r
613         if ( !inJoint || (inJoint->PositionKeys.empty() && inJoint->ScaleKeys.empty() && inJoint->RotationKeys.empty()) )\r
614         {\r
615                 core::stringc logStr;\r
616                 for ( u32 i=1; i < B3dStack.size(); ++i )\r
617                         logStr += "-";\r
618                 logStr += "read ChunkKEYS";\r
619                 os::Printer::log(logStr.c_str(), ELL_DEBUG);\r
620         }\r
621 #endif\r
622 \r
623         s32 flags;\r
624         B3DFile->read(&flags, sizeof(flags));\r
625 #ifdef __BIG_ENDIAN__\r
626         flags = os::Byteswap::byteswap(flags);\r
627 #endif\r
628 \r
629         CSkinnedMesh::SPositionKey *oldPosKey=0;\r
630         core::vector3df oldPos[2];\r
631         CSkinnedMesh::SScaleKey *oldScaleKey=0;\r
632         core::vector3df oldScale[2];\r
633         CSkinnedMesh::SRotationKey *oldRotKey=0;\r
634         core::quaternion oldRot[2];\r
635         bool isFirst[3]={true,true,true};\r
636         while((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) //this chunk repeats\r
637         {\r
638                 s32 frame;\r
639 \r
640                 B3DFile->read(&frame, sizeof(frame));\r
641                 #ifdef __BIG_ENDIAN__\r
642                 frame = os::Byteswap::byteswap(frame);\r
643                 #endif\r
644 \r
645                 // Add key frames, frames in Irrlicht are zero-based\r
646                 f32 data[4];\r
647                 if (flags & 1)\r
648                 {\r
649                         readFloats(data, 3);\r
650                         if ((oldPosKey!=0) && (oldPos[0]==oldPos[1]))\r
651                         {\r
652                                 const core::vector3df pos(data[0], data[1], data[2]);\r
653                                 if (oldPos[1]==pos)\r
654                                         oldPosKey->frame = (f32)frame-1;\r
655                                 else\r
656                                 {\r
657                                         oldPos[0]=oldPos[1];\r
658                                         oldPosKey=AnimatedMesh->addPositionKey(inJoint);\r
659                                         oldPosKey->frame = (f32)frame-1;\r
660                                         oldPos[1].set(oldPosKey->position.set(pos));\r
661                                 }\r
662                         }\r
663                         else if (oldPosKey==0 && isFirst[0])\r
664                         {\r
665                                 oldPosKey=AnimatedMesh->addPositionKey(inJoint);\r
666                                 oldPosKey->frame = (f32)frame-1;\r
667                                 oldPos[0].set(oldPosKey->position.set(data[0], data[1], data[2]));\r
668                                 oldPosKey=0;\r
669                                 isFirst[0]=false;\r
670                         }\r
671                         else\r
672                         {\r
673                                 if (oldPosKey!=0)\r
674                                         oldPos[0]=oldPos[1];\r
675                                 oldPosKey=AnimatedMesh->addPositionKey(inJoint);\r
676                                 oldPosKey->frame = (f32)frame-1;\r
677                                 oldPos[1].set(oldPosKey->position.set(data[0], data[1], data[2]));\r
678                         }\r
679                 }\r
680                 if (flags & 2)\r
681                 {\r
682                         readFloats(data, 3);\r
683                         if ((oldScaleKey!=0) && (oldScale[0]==oldScale[1]))\r
684                         {\r
685                                 const core::vector3df scale(data[0], data[1], data[2]);\r
686                                 if (oldScale[1]==scale)\r
687                                         oldScaleKey->frame = (f32)frame-1;\r
688                                 else\r
689                                 {\r
690                                         oldScale[0]=oldScale[1];\r
691                                         oldScaleKey=AnimatedMesh->addScaleKey(inJoint);\r
692                                         oldScaleKey->frame = (f32)frame-1;\r
693                                         oldScale[1].set(oldScaleKey->scale.set(scale));\r
694                                 }\r
695                         }\r
696                         else if (oldScaleKey==0 && isFirst[1])\r
697                         {\r
698                                 oldScaleKey=AnimatedMesh->addScaleKey(inJoint);\r
699                                 oldScaleKey->frame = (f32)frame-1;\r
700                                 oldScale[0].set(oldScaleKey->scale.set(data[0], data[1], data[2]));\r
701                                 oldScaleKey=0;\r
702                                 isFirst[1]=false;\r
703                         }\r
704                         else\r
705                         {\r
706                                 if (oldScaleKey!=0)\r
707                                         oldScale[0]=oldScale[1];\r
708                                 oldScaleKey=AnimatedMesh->addScaleKey(inJoint);\r
709                                 oldScaleKey->frame = (f32)frame-1;\r
710                                 oldScale[1].set(oldScaleKey->scale.set(data[0], data[1], data[2]));\r
711                         }\r
712                 }\r
713                 if (flags & 4)\r
714                 {\r
715                         readFloats(data, 4);\r
716                         if ((oldRotKey!=0) && (oldRot[0]==oldRot[1]))\r
717                         {\r
718                                 // meant to be in this order since b3d stores W first\r
719                                 const core::quaternion rot(data[1], data[2], data[3], data[0]);\r
720                                 if (oldRot[1]==rot)\r
721                                         oldRotKey->frame = (f32)frame-1;\r
722                                 else\r
723                                 {\r
724                                         oldRot[0]=oldRot[1];\r
725                                         oldRotKey=AnimatedMesh->addRotationKey(inJoint);\r
726                                         oldRotKey->frame = (f32)frame-1;\r
727                                         oldRot[1].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0]));\r
728                                         oldRot[1].normalize();\r
729                                 }\r
730                         }\r
731                         else if (oldRotKey==0 && isFirst[2])\r
732                         {\r
733                                 oldRotKey=AnimatedMesh->addRotationKey(inJoint);\r
734                                 oldRotKey->frame = (f32)frame-1;\r
735                                 // meant to be in this order since b3d stores W first\r
736                                 oldRot[0].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0]));\r
737                                 oldRot[0].normalize();\r
738                                 oldRotKey=0;\r
739                                 isFirst[2]=false;\r
740                         }\r
741                         else\r
742                         {\r
743                                 if (oldRotKey!=0)\r
744                                         oldRot[0]=oldRot[1];\r
745                                 oldRotKey=AnimatedMesh->addRotationKey(inJoint);\r
746                                 oldRotKey->frame = (f32)frame-1;\r
747                                 // meant to be in this order since b3d stores W first\r
748                                 oldRot[1].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0]));\r
749                                 oldRot[1].normalize();\r
750                         }\r
751                 }\r
752         }\r
753 \r
754         B3dStack.erase(B3dStack.size()-1);\r
755         return true;\r
756 }\r
757 \r
758 \r
759 bool CB3DMeshFileLoader::readChunkANIM()\r
760 {\r
761 #ifdef _B3D_READER_DEBUG\r
762         core::stringc logStr;\r
763         for ( u32 i=1; i < B3dStack.size(); ++i )\r
764                 logStr += "-";\r
765         logStr += "read ChunkANIM";\r
766         os::Printer::log(logStr.c_str(), ELL_DEBUG);\r
767 #endif\r
768 \r
769         s32 animFlags; //not stored\used\r
770         s32 animFrames;//not stored\used\r
771         f32 animFPS; //not stored\used\r
772 \r
773         B3DFile->read(&animFlags, sizeof(s32));\r
774         B3DFile->read(&animFrames, sizeof(s32));\r
775         readFloats(&animFPS, 1);\r
776         if (animFPS>0.f)\r
777                 AnimatedMesh->setAnimationSpeed(animFPS);\r
778         os::Printer::log("FPS", io::path((double)animFPS), ELL_DEBUG);\r
779 \r
780         #ifdef __BIG_ENDIAN__\r
781                 animFlags = os::Byteswap::byteswap(animFlags);\r
782                 animFrames = os::Byteswap::byteswap(animFrames);\r
783         #endif\r
784 \r
785         B3dStack.erase(B3dStack.size()-1);\r
786         return true;\r
787 }\r
788 \r
789 \r
790 bool CB3DMeshFileLoader::readChunkTEXS()\r
791 {\r
792 #ifdef _B3D_READER_DEBUG\r
793         core::stringc logStr;\r
794         for ( u32 i=1; i < B3dStack.size(); ++i )\r
795                 logStr += "-";\r
796         logStr += "read ChunkTEXS";\r
797         os::Printer::log(logStr.c_str(), ELL_DEBUG);\r
798 #endif\r
799 \r
800         while((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) //this chunk repeats\r
801         {\r
802                 Textures.push_back(SB3dTexture());\r
803                 SB3dTexture& B3dTexture = Textures.getLast();\r
804 \r
805                 readString(B3dTexture.TextureName);\r
806                 B3dTexture.TextureName.replace('\\','/');\r
807 #ifdef _B3D_READER_DEBUG\r
808                 os::Printer::log("read Texture", B3dTexture.TextureName.c_str(), ELL_DEBUG);\r
809 #endif\r
810 \r
811                 B3DFile->read(&B3dTexture.Flags, sizeof(s32));\r
812                 B3DFile->read(&B3dTexture.Blend, sizeof(s32));\r
813 #ifdef __BIG_ENDIAN__\r
814                 B3dTexture.Flags = os::Byteswap::byteswap(B3dTexture.Flags);\r
815                 B3dTexture.Blend = os::Byteswap::byteswap(B3dTexture.Blend);\r
816 #endif\r
817 #ifdef _B3D_READER_DEBUG\r
818                 os::Printer::log("Flags", core::stringc(B3dTexture.Flags).c_str(), ELL_DEBUG);\r
819                 os::Printer::log("Blend", core::stringc(B3dTexture.Blend).c_str(), ELL_DEBUG);\r
820 #endif\r
821                 readFloats(&B3dTexture.Xpos, 1);\r
822                 readFloats(&B3dTexture.Ypos, 1);\r
823                 readFloats(&B3dTexture.Xscale, 1);\r
824                 readFloats(&B3dTexture.Yscale, 1);\r
825                 readFloats(&B3dTexture.Angle, 1);\r
826         }\r
827 \r
828         B3dStack.erase(B3dStack.size()-1);\r
829 \r
830         return true;\r
831 }\r
832 \r
833 \r
834 bool CB3DMeshFileLoader::readChunkBRUS()\r
835 {\r
836 #ifdef _B3D_READER_DEBUG\r
837         core::stringc logStr;\r
838         for ( u32 i=1; i < B3dStack.size(); ++i )\r
839                 logStr += "-";\r
840         logStr += "read ChunkBRUS";\r
841         os::Printer::log(logStr.c_str(), ELL_DEBUG);\r
842 #endif\r
843 \r
844         u32 n_texs;\r
845         B3DFile->read(&n_texs, sizeof(u32));\r
846 #ifdef __BIG_ENDIAN__\r
847         n_texs = os::Byteswap::byteswap(n_texs);\r
848 #endif\r
849 \r
850         // number of texture ids read for Irrlicht\r
851         const u32 num_textures = core::min_(n_texs, video::MATERIAL_MAX_TEXTURES);\r
852         // number of bytes to skip (for ignored texture ids)\r
853         const u32 n_texs_offset = (num_textures<n_texs)?(n_texs-num_textures):0;\r
854 \r
855         while((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) //this chunk repeats\r
856         {\r
857                 // This is what blitz basic calls a brush, like a Irrlicht Material\r
858 \r
859                 core::stringc name;\r
860                 readString(name);\r
861 #ifdef _B3D_READER_DEBUG\r
862                 os::Printer::log("read Material", name, ELL_DEBUG);\r
863 #endif\r
864                 Materials.push_back(SB3dMaterial());\r
865                 SB3dMaterial& B3dMaterial=Materials.getLast();\r
866 \r
867                 readFloats(&B3dMaterial.red, 1);\r
868                 readFloats(&B3dMaterial.green, 1);\r
869                 readFloats(&B3dMaterial.blue, 1);\r
870                 readFloats(&B3dMaterial.alpha, 1);\r
871                 readFloats(&B3dMaterial.shininess, 1);\r
872 \r
873                 B3DFile->read(&B3dMaterial.blend, sizeof(B3dMaterial.blend));\r
874                 B3DFile->read(&B3dMaterial.fx, sizeof(B3dMaterial.fx));\r
875 #ifdef __BIG_ENDIAN__\r
876                 B3dMaterial.blend = os::Byteswap::byteswap(B3dMaterial.blend);\r
877                 B3dMaterial.fx = os::Byteswap::byteswap(B3dMaterial.fx);\r
878 #endif\r
879 #ifdef _B3D_READER_DEBUG\r
880                 os::Printer::log("Blend", core::stringc(B3dMaterial.blend).c_str(), ELL_DEBUG);\r
881                 os::Printer::log("FX", core::stringc(B3dMaterial.fx).c_str(), ELL_DEBUG);\r
882 #endif\r
883 \r
884                 u32 i;\r
885                 for (i=0; i<num_textures; ++i)\r
886                 {\r
887                         s32 texture_id=-1;\r
888                         B3DFile->read(&texture_id, sizeof(s32));\r
889 #ifdef __BIG_ENDIAN__\r
890                         texture_id = os::Byteswap::byteswap(texture_id);\r
891 #endif\r
892                         //--- Get pointers to the texture, based on the IDs ---\r
893                         if ((u32)texture_id < Textures.size())\r
894                         {\r
895                                 B3dMaterial.Textures[i]=&Textures[texture_id];\r
896 #ifdef _B3D_READER_DEBUG\r
897                                 os::Printer::log("Layer", core::stringc(i).c_str(), ELL_DEBUG);\r
898                                 os::Printer::log("using texture", Textures[texture_id].TextureName.c_str(), ELL_DEBUG);\r
899 #endif\r
900                         }\r
901                         else\r
902                                 B3dMaterial.Textures[i]=0;\r
903                 }\r
904                 // skip other texture ids\r
905                 for (i=0; i<n_texs_offset; ++i)\r
906                 {\r
907                         s32 texture_id=-1;\r
908                         B3DFile->read(&texture_id, sizeof(s32));\r
909 #ifdef __BIG_ENDIAN__\r
910                         texture_id = os::Byteswap::byteswap(texture_id);\r
911 #endif\r
912                         if (ShowWarning && (texture_id != -1) && (n_texs>video::MATERIAL_MAX_TEXTURES))\r
913                         {\r
914                                 os::Printer::log("Too many textures used in one material", B3DFile->getFileName(), ELL_WARNING);\r
915                                 ShowWarning = false;\r
916                         }\r
917                 }\r
918 \r
919                 //Fixes problems when the lightmap is on the first texture:\r
920                 if (B3dMaterial.Textures[0] != 0)\r
921                 {\r
922                         if (B3dMaterial.Textures[0]->Flags & 65536) // 65536 = secondary UV\r
923                         {\r
924                                 SB3dTexture *TmpTexture;\r
925                                 TmpTexture = B3dMaterial.Textures[1];\r
926                                 B3dMaterial.Textures[1] = B3dMaterial.Textures[0];\r
927                                 B3dMaterial.Textures[0] = TmpTexture;\r
928                         }\r
929                 }\r
930 \r
931                 //If a preceeding texture slot is empty move the others down:\r
932                 for (i=num_textures; i>0; --i)\r
933                 {\r
934                         for (u32 j=i-1; j<num_textures-1; ++j)\r
935                         {\r
936                                 if (B3dMaterial.Textures[j+1] != 0 && B3dMaterial.Textures[j] == 0)\r
937                                 {\r
938                                         B3dMaterial.Textures[j] = B3dMaterial.Textures[j+1];\r
939                                         B3dMaterial.Textures[j+1] = 0;\r
940                                 }\r
941                         }\r
942                 }\r
943 \r
944                 //------ Convert blitz flags/blend to irrlicht -------\r
945 \r
946                 //Two textures:\r
947                 if (B3dMaterial.Textures[1])\r
948                 {\r
949                         if (B3dMaterial.alpha==1.f)\r
950                         {\r
951                                 if (B3dMaterial.Textures[1]->Blend == 5) //(Multiply 2)\r
952                                         B3dMaterial.Material.MaterialType = video::EMT_LIGHTMAP_M2;\r
953                                 else\r
954                                         B3dMaterial.Material.MaterialType = video::EMT_LIGHTMAP;\r
955                                 B3dMaterial.Material.Lighting = false;\r
956                         }\r
957                         else\r
958                         {\r
959                                 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;\r
960                                 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;\r
961                         }\r
962                 }\r
963                 else if (B3dMaterial.Textures[0]) //One texture:\r
964                 {\r
965                         // Flags & 0x1 is usual SOLID, 0x8 is mipmap (handled before)\r
966                         if (B3dMaterial.Textures[0]->Flags & 0x2) //(Alpha mapped)\r
967                         {\r
968                                 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;\r
969                                 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;\r
970                         }\r
971                         else if (B3dMaterial.Textures[0]->Flags & 0x4) //(Masked)\r
972                                 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; // TODO: create color key texture\r
973                         else if (B3dMaterial.Textures[0]->Flags & 0x40)\r
974                                 B3dMaterial.Material.MaterialType = video::EMT_SPHERE_MAP;\r
975                         else if (B3dMaterial.Textures[0]->Flags & 0x80)\r
976                                 B3dMaterial.Material.MaterialType = video::EMT_SPHERE_MAP; // TODO: Should be cube map\r
977                         else if (B3dMaterial.alpha == 1.f)\r
978                                 B3dMaterial.Material.MaterialType = video::EMT_SOLID;\r
979                         else\r
980                         {\r
981                                 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;\r
982                                 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;\r
983                         }\r
984                 }\r
985                 else //No texture:\r
986                 {\r
987                         if (B3dMaterial.alpha == 1.f)\r
988                                 B3dMaterial.Material.MaterialType = video::EMT_SOLID;\r
989                         else\r
990                         {\r
991                                 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;\r
992                                 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;\r
993                         }\r
994                 }\r
995 \r
996                 B3dMaterial.Material.DiffuseColor = video::SColorf(B3dMaterial.red, B3dMaterial.green, B3dMaterial.blue, B3dMaterial.alpha).toSColor();\r
997                 B3dMaterial.Material.ColorMaterial=video::ECM_NONE;\r
998 \r
999                 //------ Material fx ------\r
1000 \r
1001                 if (B3dMaterial.fx & 1) //full-bright\r
1002                 {\r
1003                         B3dMaterial.Material.AmbientColor = video::SColor(255, 255, 255, 255);\r
1004                         B3dMaterial.Material.Lighting = false;\r
1005                 }\r
1006                 else\r
1007                         B3dMaterial.Material.AmbientColor = B3dMaterial.Material.DiffuseColor;\r
1008 \r
1009                 if (B3dMaterial.fx & 2) //use vertex colors instead of brush color\r
1010                         B3dMaterial.Material.ColorMaterial=video::ECM_DIFFUSE_AND_AMBIENT;\r
1011 \r
1012                 if (B3dMaterial.fx & 4) //flatshaded\r
1013                         B3dMaterial.Material.GouraudShading = false;\r
1014 \r
1015                 if (B3dMaterial.fx & 16) //disable backface culling\r
1016                         B3dMaterial.Material.BackfaceCulling = false;\r
1017 \r
1018                 if (B3dMaterial.fx & 32) //force vertex alpha-blending\r
1019                 {\r
1020                         B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;\r
1021                         B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;\r
1022                 }\r
1023 \r
1024                 B3dMaterial.Material.Shininess = B3dMaterial.shininess;\r
1025         }\r
1026 \r
1027         B3dStack.erase(B3dStack.size()-1);\r
1028 \r
1029         return true;\r
1030 }\r
1031 \r
1032 \r
1033 void CB3DMeshFileLoader::readString(core::stringc& newstring)\r
1034 {\r
1035         newstring="";\r
1036         while (B3DFile->getPos() <= B3DFile->getSize())\r
1037         {\r
1038                 c8 character;\r
1039                 B3DFile->read(&character, sizeof(character));\r
1040                 if (character==0)\r
1041                         return;\r
1042                 newstring.append(character);\r
1043         }\r
1044 }\r
1045 \r
1046 \r
1047 void CB3DMeshFileLoader::readFloats(f32* vec, u32 count)\r
1048 {\r
1049         B3DFile->read(vec, count*sizeof(f32));\r
1050         #ifdef __BIG_ENDIAN__\r
1051         for (u32 n=0; n<count; ++n)\r
1052                 vec[n] = os::Byteswap::byteswap(vec[n]);\r
1053         #endif\r
1054 }\r
1055 \r
1056 } // end namespace scene\r
1057 } // end namespace irr\r
1058 \r
1059 \r
1060 #endif // _IRR_COMPILE_WITH_B3D_LOADER_\r
1061 \r