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