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
6 // File format designed by Mark Sibly for the Blitz3D engine and has been
\r
7 // declared public domain
\r
9 #include "IrrCompileConfig.h"
\r
10 #ifdef _IRR_COMPILE_WITH_B3D_LOADER_
\r
12 #include "CB3DMeshFileLoader.h"
\r
13 #include "CMeshTextureLoader.h"
\r
15 #include "IVideoDriver.h"
\r
16 #include "IFileSystem.h"
\r
20 #define _B3D_READER_DEBUG
\r
29 CB3DMeshFileLoader::CB3DMeshFileLoader(scene::ISceneManager* smgr)
\r
30 : SceneManager(smgr), AnimatedMesh(0), B3DFile(0), NormalsInFile(false),
\r
31 HasVertexColors(false), ShowWarning(true)
\r
34 setDebugName("CB3DMeshFileLoader");
\r
37 TextureLoader = new CMeshTextureLoader( SceneManager->getFileSystem(), SceneManager->getVideoDriver() );
\r
41 //! returns true if the file maybe is able to be loaded by this class
\r
42 //! based on the file extension (e.g. ".bsp")
\r
43 bool CB3DMeshFileLoader::isALoadableFileExtension(const io::path& filename) const
\r
45 return core::hasFileExtension ( filename, "b3d" );
\r
49 //! creates/loads an animated mesh from the file.
\r
50 //! \return Pointer to the created mesh. Returns 0 if loading failed.
\r
51 //! If you no longer need the mesh, you should call IAnimatedMesh::drop().
\r
52 //! See IReferenceCounted::drop() for more information.
\r
53 IAnimatedMesh* CB3DMeshFileLoader::createMesh(io::IReadFile* file)
\r
58 if ( getMeshTextureLoader() )
\r
59 getMeshTextureLoader()->setMeshFile(file);
\r
62 AnimatedMesh = new scene::CSkinnedMesh();
\r
63 ShowWarning = true; // If true a warning is issued if too many textures are used
\r
68 AnimatedMesh->finalize();
\r
72 AnimatedMesh->drop();
\r
76 return AnimatedMesh;
\r
80 bool CB3DMeshFileLoader::load()
\r
84 NormalsInFile=false;
\r
85 HasVertexColors=false;
\r
87 //------ Get header ------
\r
89 SB3dChunkHeader header;
\r
90 B3DFile->read(&header, sizeof(header));
\r
91 #ifdef __BIG_ENDIAN__
\r
92 header.size = os::Byteswap::byteswap(header.size);
\r
95 if ( strncmp( header.name, "BB3D", 4 ) != 0 )
\r
97 os::Printer::log("File is not a b3d file. Loading failed (No header found)", B3DFile->getFileName(), ELL_ERROR);
\r
101 // Add main chunk...
\r
102 B3dStack.push_back(SB3dChunk(header, B3DFile->getPos()-8));
\r
104 // Get file version, but ignore it, as it's not important with b3d files...
\r
106 B3DFile->read(&fileVersion, sizeof(fileVersion));
\r
107 #ifdef __BIG_ENDIAN__
\r
108 fileVersion = os::Byteswap::byteswap(fileVersion);
\r
111 //------ Read main chunk ------
\r
113 while ( (B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos() )
\r
115 B3DFile->read(&header, sizeof(header));
\r
116 #ifdef __BIG_ENDIAN__
\r
117 header.size = os::Byteswap::byteswap(header.size);
\r
119 B3dStack.push_back(SB3dChunk(header, B3DFile->getPos()-8));
\r
121 if ( strncmp( B3dStack.getLast().name, "TEXS", 4 ) == 0 )
\r
123 if (!readChunkTEXS())
\r
126 else if ( strncmp( B3dStack.getLast().name, "BRUS", 4 ) == 0 )
\r
128 if (!readChunkBRUS())
\r
131 else if ( strncmp( B3dStack.getLast().name, "NODE", 4 ) == 0 )
\r
133 if (!readChunkNODE((CSkinnedMesh::SJoint*)0) )
\r
138 os::Printer::log("Unknown chunk found in mesh base - skipping");
\r
139 B3DFile->seek(B3dStack.getLast().startposition + B3dStack.getLast().length);
\r
140 B3dStack.erase(B3dStack.size()-1);
\r
146 BaseVertices.clear();
\r
147 AnimatedVertices_VertexID.clear();
\r
148 AnimatedVertices_BufferID.clear();
\r
157 bool CB3DMeshFileLoader::readChunkNODE(CSkinnedMesh::SJoint *inJoint)
\r
159 CSkinnedMesh::SJoint *joint = AnimatedMesh->addJoint(inJoint);
\r
160 readString(joint->Name);
\r
162 #ifdef _B3D_READER_DEBUG
\r
163 core::stringc logStr;
\r
164 for ( u32 i=1; i < B3dStack.size(); ++i )
\r
166 logStr += "read ChunkNODE";
\r
167 os::Printer::log(logStr.c_str(), joint->Name.c_str(), ELL_DEBUG);
\r
170 f32 position[3], scale[3], rotation[4];
\r
172 readFloats(position, 3);
\r
173 readFloats(scale, 3);
\r
174 readFloats(rotation, 4);
\r
176 joint->Animatedposition = core::vector3df(position[0],position[1],position[2]) ;
\r
177 joint->Animatedscale = core::vector3df(scale[0],scale[1],scale[2]);
\r
178 joint->Animatedrotation = core::quaternion(rotation[1], rotation[2], rotation[3], rotation[0]);
\r
180 //Build LocalMatrix:
\r
182 core::matrix4 positionMatrix;
\r
183 positionMatrix.setTranslation( joint->Animatedposition );
\r
184 core::matrix4 scaleMatrix;
\r
185 scaleMatrix.setScale( joint->Animatedscale );
\r
186 core::matrix4 rotationMatrix;
\r
187 joint->Animatedrotation.getMatrix_transposed(rotationMatrix);
\r
189 joint->LocalMatrix = positionMatrix * rotationMatrix * scaleMatrix;
\r
192 joint->GlobalMatrix = inJoint->GlobalMatrix * joint->LocalMatrix;
\r
194 joint->GlobalMatrix = joint->LocalMatrix;
\r
196 while(B3dStack.getLast().startposition + B3dStack.getLast().length > B3DFile->getPos()) // this chunk repeats
\r
198 SB3dChunkHeader header;
\r
199 B3DFile->read(&header, sizeof(header));
\r
200 #ifdef __BIG_ENDIAN__
\r
201 header.size = os::Byteswap::byteswap(header.size);
\r
204 B3dStack.push_back(SB3dChunk(header, B3DFile->getPos()-8));
\r
206 if ( strncmp( B3dStack.getLast().name, "NODE", 4 ) == 0 )
\r
208 if (!readChunkNODE(joint))
\r
211 else if ( strncmp( B3dStack.getLast().name, "MESH", 4 ) == 0 )
\r
213 VerticesStart=BaseVertices.size();
\r
214 if (!readChunkMESH(joint))
\r
217 else if ( strncmp( B3dStack.getLast().name, "BONE", 4 ) == 0 )
\r
219 if (!readChunkBONE(joint))
\r
222 else if ( strncmp( B3dStack.getLast().name, "KEYS", 4 ) == 0 )
\r
224 if(!readChunkKEYS(joint))
\r
227 else if ( strncmp( B3dStack.getLast().name, "ANIM", 4 ) == 0 )
\r
229 if (!readChunkANIM())
\r
234 os::Printer::log("Unknown chunk found in node chunk - skipping");
\r
235 B3DFile->seek(B3dStack.getLast().startposition + B3dStack.getLast().length);
\r
236 B3dStack.erase(B3dStack.size()-1);
\r
240 B3dStack.erase(B3dStack.size()-1);
\r
246 bool CB3DMeshFileLoader::readChunkMESH(CSkinnedMesh::SJoint *inJoint)
\r
248 #ifdef _B3D_READER_DEBUG
\r
249 core::stringc logStr;
\r
250 for ( u32 i=1; i < B3dStack.size(); ++i )
\r
252 logStr += "read ChunkMESH";
\r
253 os::Printer::log(logStr.c_str(), ELL_DEBUG);
\r
257 B3DFile->read(&brushID, sizeof(brushID));
\r
258 #ifdef __BIG_ENDIAN__
\r
259 brushID = os::Byteswap::byteswap(brushID);
\r
262 NormalsInFile=false;
\r
263 HasVertexColors=false;
\r
265 while((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) //this chunk repeats
\r
267 SB3dChunkHeader header;
\r
268 B3DFile->read(&header, sizeof(header));
\r
269 #ifdef __BIG_ENDIAN__
\r
270 header.size = os::Byteswap::byteswap(header.size);
\r
273 B3dStack.push_back(SB3dChunk(header, B3DFile->getPos()-8));
\r
275 if ( strncmp( B3dStack.getLast().name, "VRTS", 4 ) == 0 )
\r
277 if (!readChunkVRTS(inJoint))
\r
280 else if ( strncmp( B3dStack.getLast().name, "TRIS", 4 ) == 0 )
\r
282 scene::SSkinMeshBuffer *meshBuffer = AnimatedMesh->addMeshBuffer();
\r
286 loadTextures(Materials[brushID]);
\r
287 meshBuffer->Material=Materials[brushID].Material;
\r
290 if(readChunkTRIS(meshBuffer,AnimatedMesh->getMeshBuffers().size()-1, VerticesStart)==false)
\r
293 if (!NormalsInFile)
\r
297 for ( i=0; i<(s32)meshBuffer->Indices.size(); i+=3)
\r
299 core::plane3df p(meshBuffer->getVertex(meshBuffer->Indices[i+0])->Pos,
\r
300 meshBuffer->getVertex(meshBuffer->Indices[i+1])->Pos,
\r
301 meshBuffer->getVertex(meshBuffer->Indices[i+2])->Pos);
\r
303 meshBuffer->getVertex(meshBuffer->Indices[i+0])->Normal += p.Normal;
\r
304 meshBuffer->getVertex(meshBuffer->Indices[i+1])->Normal += p.Normal;
\r
305 meshBuffer->getVertex(meshBuffer->Indices[i+2])->Normal += p.Normal;
\r
308 for ( i = 0; i<(s32)meshBuffer->getVertexCount(); ++i )
\r
310 meshBuffer->getVertex(i)->Normal.normalize();
\r
311 BaseVertices[VerticesStart+i].Normal=meshBuffer->getVertex(i)->Normal;
\r
317 os::Printer::log("Unknown chunk found in mesh - skipping");
\r
318 B3DFile->seek(B3dStack.getLast().startposition + B3dStack.getLast().length);
\r
319 B3dStack.erase(B3dStack.size()-1);
\r
323 B3dStack.erase(B3dStack.size()-1);
\r
331 int flags ;1=normal values present, 2=rgba values present
\r
332 int tex_coord_sets ;texture coords per vertex (eg: 1 for simple U/V) max=8
\r
333 but we only support 3
\r
334 int tex_coord_set_size ;components per set (eg: 2 for simple U/V) max=4
\r
336 float x,y,z ;always present
\r
337 float nx,ny,nz ;vertex normal: present if (flags&1)
\r
338 float red,green,blue,alpha ;vertex color: present if (flags&2)
\r
339 float tex_coords[tex_coord_sets][tex_coord_set_size] ;tex coords
\r
342 bool CB3DMeshFileLoader::readChunkVRTS(CSkinnedMesh::SJoint *inJoint)
\r
344 #ifdef _B3D_READER_DEBUG
\r
345 core::stringc logStr;
\r
346 for ( u32 i=1; i < B3dStack.size(); ++i )
\r
348 logStr += "ChunkVRTS";
\r
349 os::Printer::log(logStr.c_str(), ELL_DEBUG);
\r
352 const s32 max_tex_coords = 3;
\r
353 s32 flags, tex_coord_sets, tex_coord_set_size;
\r
355 B3DFile->read(&flags, sizeof(flags));
\r
356 B3DFile->read(&tex_coord_sets, sizeof(tex_coord_sets));
\r
357 B3DFile->read(&tex_coord_set_size, sizeof(tex_coord_set_size));
\r
358 #ifdef __BIG_ENDIAN__
\r
359 flags = os::Byteswap::byteswap(flags);
\r
360 tex_coord_sets = os::Byteswap::byteswap(tex_coord_sets);
\r
361 tex_coord_set_size = os::Byteswap::byteswap(tex_coord_set_size);
\r
364 if (tex_coord_sets >= max_tex_coords || tex_coord_set_size >= 4) // Something is wrong
\r
366 os::Printer::log("tex_coord_sets or tex_coord_set_size too big", B3DFile->getFileName(), ELL_ERROR);
\r
370 //------ Allocate Memory, for speed -----------//
\r
372 s32 numberOfReads = 3;
\r
376 NormalsInFile = true;
\r
377 numberOfReads += 3;
\r
381 numberOfReads += 4;
\r
382 HasVertexColors=true;
\r
385 numberOfReads += tex_coord_sets*tex_coord_set_size;
\r
387 const s32 memoryNeeded = (B3dStack.getLast().length / sizeof(f32)) / numberOfReads;
\r
389 BaseVertices.reallocate(memoryNeeded + BaseVertices.size() + 1);
\r
390 AnimatedVertices_VertexID.reallocate(memoryNeeded + AnimatedVertices_VertexID.size() + 1);
\r
392 //--------------------------------------------//
\r
394 while( (B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
\r
397 f32 normal[3]={0.f, 0.f, 0.f};
\r
398 f32 color[4]={1.0f, 1.0f, 1.0f, 1.0f};
\r
399 f32 tex_coords[max_tex_coords][4];
\r
401 readFloats(position, 3);
\r
404 readFloats(normal, 3);
\r
406 readFloats(color, 4);
\r
408 for (s32 i=0; i<tex_coord_sets; ++i)
\r
409 readFloats(tex_coords[i], tex_coord_set_size);
\r
411 f32 tu=0.0f, tv=0.0f;
\r
412 if (tex_coord_sets >= 1 && tex_coord_set_size >= 2)
\r
414 tu=tex_coords[0][0];
\r
415 tv=tex_coords[0][1];
\r
418 f32 tu2=0.0f, tv2=0.0f;
\r
419 if (tex_coord_sets>1 && tex_coord_set_size>1)
\r
421 tu2=tex_coords[1][0];
\r
422 tv2=tex_coords[1][1];
\r
425 // Create Vertex...
\r
426 video::S3DVertex2TCoords Vertex(position[0], position[1], position[2],
\r
427 normal[0], normal[1], normal[2],
\r
428 video::SColorf(color[0], color[1], color[2], color[3]).toSColor(),
\r
431 // Transform the Vertex position by nested node...
\r
432 inJoint->GlobalMatrix.transformVect(Vertex.Pos);
\r
433 inJoint->GlobalMatrix.rotateVect(Vertex.Normal);
\r
436 BaseVertices.push_back(Vertex);
\r
438 AnimatedVertices_VertexID.push_back(-1);
\r
439 AnimatedVertices_BufferID.push_back(-1);
\r
442 B3dStack.erase(B3dStack.size()-1);
\r
448 bool CB3DMeshFileLoader::readChunkTRIS(scene::SSkinMeshBuffer *meshBuffer, u32 meshBufferID, s32 vertices_Start)
\r
450 #ifdef _B3D_READER_DEBUG
\r
451 core::stringc logStr;
\r
452 for ( u32 i=1; i < B3dStack.size(); ++i )
\r
454 logStr += "ChunkTRIS";
\r
455 os::Printer::log(logStr.c_str(), ELL_DEBUG);
\r
458 bool showVertexWarning=false;
\r
460 s32 triangle_brush_id; // Note: Irrlicht can't have different brushes for each triangle (using a workaround)
\r
461 B3DFile->read(&triangle_brush_id, sizeof(triangle_brush_id));
\r
462 #ifdef __BIG_ENDIAN__
\r
463 triangle_brush_id = os::Byteswap::byteswap(triangle_brush_id);
\r
466 SB3dMaterial *B3dMaterial;
\r
468 if (triangle_brush_id != -1)
\r
470 loadTextures(Materials[triangle_brush_id]);
\r
471 B3dMaterial = &Materials[triangle_brush_id];
\r
472 meshBuffer->Material = B3dMaterial->Material;
\r
477 const s32 memoryNeeded = B3dStack.getLast().length / sizeof(s32);
\r
478 meshBuffer->Indices.reallocate(memoryNeeded + meshBuffer->Indices.size() + 1);
\r
480 while((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
\r
484 B3DFile->read(vertex_id, 3*sizeof(s32));
\r
485 #ifdef __BIG_ENDIAN__
\r
486 vertex_id[0] = os::Byteswap::byteswap(vertex_id[0]);
\r
487 vertex_id[1] = os::Byteswap::byteswap(vertex_id[1]);
\r
488 vertex_id[2] = os::Byteswap::byteswap(vertex_id[2]);
\r
492 vertex_id[0] += vertices_Start;
\r
493 vertex_id[1] += vertices_Start;
\r
494 vertex_id[2] += vertices_Start;
\r
496 for(s32 i=0; i<3; ++i)
\r
498 if ((u32)vertex_id[i] >= AnimatedVertices_VertexID.size())
\r
500 os::Printer::log("Illegal vertex index found", B3DFile->getFileName(), ELL_ERROR);
\r
504 if (AnimatedVertices_VertexID[ vertex_id[i] ] != -1)
\r
506 if ( AnimatedVertices_BufferID[ vertex_id[i] ] != (s32)meshBufferID ) //If this vertex is linked in a different meshbuffer
\r
508 AnimatedVertices_VertexID[ vertex_id[i] ] = -1;
\r
509 AnimatedVertices_BufferID[ vertex_id[i] ] = -1;
\r
510 showVertexWarning=true;
\r
513 if (AnimatedVertices_VertexID[ vertex_id[i] ] == -1) //If this vertex is not in the meshbuffer
\r
515 //Check for lightmapping:
\r
516 if (BaseVertices[ vertex_id[i] ].TCoords2 != core::vector2df(0.f,0.f))
\r
517 meshBuffer->convertTo2TCoords(); //Will only affect the meshbuffer the first time this is called
\r
519 //Add the vertex to the meshbuffer:
\r
520 if (meshBuffer->VertexType == video::EVT_STANDARD)
\r
521 meshBuffer->Vertices_Standard.push_back( BaseVertices[ vertex_id[i] ] );
\r
523 meshBuffer->Vertices_2TCoords.push_back(BaseVertices[ vertex_id[i] ] );
\r
525 //create vertex id to meshbuffer index link:
\r
526 AnimatedVertices_VertexID[ vertex_id[i] ] = meshBuffer->getVertexCount()-1;
\r
527 AnimatedVertices_BufferID[ vertex_id[i] ] = meshBufferID;
\r
531 // Apply Material/Color/etc...
\r
532 video::S3DVertex *Vertex=meshBuffer->getVertex(meshBuffer->getVertexCount()-1);
\r
534 if (!HasVertexColors)
\r
535 Vertex->Color=B3dMaterial->Material.DiffuseColor;
\r
536 else if (Vertex->Color.getAlpha() == 255)
\r
537 Vertex->Color.setAlpha( (s32)(B3dMaterial->alpha * 255.0f) );
\r
539 // Use texture's scale
\r
540 if (B3dMaterial->Textures[0])
\r
542 Vertex->TCoords.X *= B3dMaterial->Textures[0]->Xscale;
\r
543 Vertex->TCoords.Y *= B3dMaterial->Textures[0]->Yscale;
\r
546 if (B3dMaterial->Textures[1])
\r
548 Vertex->TCoords2.X *=B3dMaterial->Textures[1]->Xscale;
\r
549 Vertex->TCoords2.Y *=B3dMaterial->Textures[1]->Yscale;
\r
556 meshBuffer->Indices.push_back( AnimatedVertices_VertexID[ vertex_id[0] ] );
\r
557 meshBuffer->Indices.push_back( AnimatedVertices_VertexID[ vertex_id[1] ] );
\r
558 meshBuffer->Indices.push_back( AnimatedVertices_VertexID[ vertex_id[2] ] );
\r
561 B3dStack.erase(B3dStack.size()-1);
\r
563 if (showVertexWarning)
\r
564 os::Printer::log("B3dMeshLoader: Warning, different meshbuffers linking to the same vertex, this will cause problems with animated meshes");
\r
570 bool CB3DMeshFileLoader::readChunkBONE(CSkinnedMesh::SJoint *inJoint)
\r
572 #ifdef _B3D_READER_DEBUG
\r
573 core::stringc logStr;
\r
574 for ( u32 i=1; i < B3dStack.size(); ++i )
\r
576 logStr += "read ChunkBONE";
\r
577 os::Printer::log(logStr.c_str(), ELL_DEBUG);
\r
580 if (B3dStack.getLast().length > 8)
\r
582 while((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
\r
584 u32 globalVertexID;
\r
586 B3DFile->read(&globalVertexID, sizeof(globalVertexID));
\r
587 B3DFile->read(&strength, sizeof(strength));
\r
588 #ifdef __BIG_ENDIAN__
\r
589 globalVertexID = os::Byteswap::byteswap(globalVertexID);
\r
590 strength = os::Byteswap::byteswap(strength);
\r
592 globalVertexID += VerticesStart;
\r
594 if (AnimatedVertices_VertexID[globalVertexID]==-1)
\r
596 os::Printer::log("B3dMeshLoader: Weight has bad vertex id (no link to meshbuffer index found)");
\r
598 else if (strength >0)
\r
600 CSkinnedMesh::SWeight *weight=AnimatedMesh->addWeight(inJoint);
\r
601 weight->strength=strength;
\r
602 //Find the meshbuffer and Vertex index from the Global Vertex ID:
\r
603 weight->vertex_id = AnimatedVertices_VertexID[globalVertexID];
\r
604 weight->buffer_id = AnimatedVertices_BufferID[globalVertexID];
\r
609 B3dStack.erase(B3dStack.size()-1);
\r
614 bool CB3DMeshFileLoader::readChunkKEYS(CSkinnedMesh::SJoint *inJoint)
\r
616 #ifdef _B3D_READER_DEBUG
\r
617 // Only print first, that's just too much output otherwise
\r
618 if ( !inJoint || (inJoint->PositionKeys.empty() && inJoint->ScaleKeys.empty() && inJoint->RotationKeys.empty()) )
\r
620 core::stringc logStr;
\r
621 for ( u32 i=1; i < B3dStack.size(); ++i )
\r
623 logStr += "read ChunkKEYS";
\r
624 os::Printer::log(logStr.c_str(), ELL_DEBUG);
\r
629 B3DFile->read(&flags, sizeof(flags));
\r
630 #ifdef __BIG_ENDIAN__
\r
631 flags = os::Byteswap::byteswap(flags);
\r
634 CSkinnedMesh::SPositionKey *oldPosKey=0;
\r
635 core::vector3df oldPos[2];
\r
636 CSkinnedMesh::SScaleKey *oldScaleKey=0;
\r
637 core::vector3df oldScale[2];
\r
638 CSkinnedMesh::SRotationKey *oldRotKey=0;
\r
639 core::quaternion oldRot[2];
\r
640 bool isFirst[3]={true,true,true};
\r
641 while((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) //this chunk repeats
\r
645 B3DFile->read(&frame, sizeof(frame));
\r
646 #ifdef __BIG_ENDIAN__
\r
647 frame = os::Byteswap::byteswap(frame);
\r
650 // Add key frames, frames in Irrlicht are zero-based
\r
654 readFloats(data, 3);
\r
655 if ((oldPosKey!=0) && (oldPos[0]==oldPos[1]))
\r
657 const core::vector3df pos(data[0], data[1], data[2]);
\r
658 if (oldPos[1]==pos)
\r
659 oldPosKey->frame = (f32)frame-1;
\r
662 oldPos[0]=oldPos[1];
\r
663 oldPosKey=AnimatedMesh->addPositionKey(inJoint);
\r
664 oldPosKey->frame = (f32)frame-1;
\r
665 oldPos[1].set(oldPosKey->position.set(pos));
\r
668 else if (oldPosKey==0 && isFirst[0])
\r
670 oldPosKey=AnimatedMesh->addPositionKey(inJoint);
\r
671 oldPosKey->frame = (f32)frame-1;
\r
672 oldPos[0].set(oldPosKey->position.set(data[0], data[1], data[2]));
\r
679 oldPos[0]=oldPos[1];
\r
680 oldPosKey=AnimatedMesh->addPositionKey(inJoint);
\r
681 oldPosKey->frame = (f32)frame-1;
\r
682 oldPos[1].set(oldPosKey->position.set(data[0], data[1], data[2]));
\r
687 readFloats(data, 3);
\r
688 if ((oldScaleKey!=0) && (oldScale[0]==oldScale[1]))
\r
690 const core::vector3df scale(data[0], data[1], data[2]);
\r
691 if (oldScale[1]==scale)
\r
692 oldScaleKey->frame = (f32)frame-1;
\r
695 oldScale[0]=oldScale[1];
\r
696 oldScaleKey=AnimatedMesh->addScaleKey(inJoint);
\r
697 oldScaleKey->frame = (f32)frame-1;
\r
698 oldScale[1].set(oldScaleKey->scale.set(scale));
\r
701 else if (oldScaleKey==0 && isFirst[1])
\r
703 oldScaleKey=AnimatedMesh->addScaleKey(inJoint);
\r
704 oldScaleKey->frame = (f32)frame-1;
\r
705 oldScale[0].set(oldScaleKey->scale.set(data[0], data[1], data[2]));
\r
711 if (oldScaleKey!=0)
\r
712 oldScale[0]=oldScale[1];
\r
713 oldScaleKey=AnimatedMesh->addScaleKey(inJoint);
\r
714 oldScaleKey->frame = (f32)frame-1;
\r
715 oldScale[1].set(oldScaleKey->scale.set(data[0], data[1], data[2]));
\r
720 readFloats(data, 4);
\r
721 if ((oldRotKey!=0) && (oldRot[0]==oldRot[1]))
\r
723 // meant to be in this order since b3d stores W first
\r
724 const core::quaternion rot(data[1], data[2], data[3], data[0]);
\r
725 if (oldRot[1]==rot)
\r
726 oldRotKey->frame = (f32)frame-1;
\r
729 oldRot[0]=oldRot[1];
\r
730 oldRotKey=AnimatedMesh->addRotationKey(inJoint);
\r
731 oldRotKey->frame = (f32)frame-1;
\r
732 oldRot[1].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0]));
\r
733 oldRot[1].normalize();
\r
736 else if (oldRotKey==0 && isFirst[2])
\r
738 oldRotKey=AnimatedMesh->addRotationKey(inJoint);
\r
739 oldRotKey->frame = (f32)frame-1;
\r
740 // meant to be in this order since b3d stores W first
\r
741 oldRot[0].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0]));
\r
742 oldRot[0].normalize();
\r
749 oldRot[0]=oldRot[1];
\r
750 oldRotKey=AnimatedMesh->addRotationKey(inJoint);
\r
751 oldRotKey->frame = (f32)frame-1;
\r
752 // meant to be in this order since b3d stores W first
\r
753 oldRot[1].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0]));
\r
754 oldRot[1].normalize();
\r
759 B3dStack.erase(B3dStack.size()-1);
\r
764 bool CB3DMeshFileLoader::readChunkANIM()
\r
766 #ifdef _B3D_READER_DEBUG
\r
767 core::stringc logStr;
\r
768 for ( u32 i=1; i < B3dStack.size(); ++i )
\r
770 logStr += "read ChunkANIM";
\r
771 os::Printer::log(logStr.c_str(), ELL_DEBUG);
\r
774 s32 animFlags; //not stored\used
\r
775 s32 animFrames;//not stored\used
\r
776 f32 animFPS; //not stored\used
\r
778 B3DFile->read(&animFlags, sizeof(s32));
\r
779 B3DFile->read(&animFrames, sizeof(s32));
\r
780 readFloats(&animFPS, 1);
\r
782 AnimatedMesh->setAnimationSpeed(animFPS);
\r
783 os::Printer::log("FPS", io::path((double)animFPS), ELL_DEBUG);
\r
785 #ifdef __BIG_ENDIAN__
\r
786 animFlags = os::Byteswap::byteswap(animFlags);
\r
787 animFrames = os::Byteswap::byteswap(animFrames);
\r
790 B3dStack.erase(B3dStack.size()-1);
\r
795 bool CB3DMeshFileLoader::readChunkTEXS()
\r
797 #ifdef _B3D_READER_DEBUG
\r
798 core::stringc logStr;
\r
799 for ( u32 i=1; i < B3dStack.size(); ++i )
\r
801 logStr += "read ChunkTEXS";
\r
802 os::Printer::log(logStr.c_str(), ELL_DEBUG);
\r
805 while((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) //this chunk repeats
\r
807 Textures.push_back(SB3dTexture());
\r
808 SB3dTexture& B3dTexture = Textures.getLast();
\r
810 readString(B3dTexture.TextureName);
\r
811 B3dTexture.TextureName.replace('\\','/');
\r
812 #ifdef _B3D_READER_DEBUG
\r
813 os::Printer::log("read Texture", B3dTexture.TextureName.c_str(), ELL_DEBUG);
\r
816 B3DFile->read(&B3dTexture.Flags, sizeof(s32));
\r
817 B3DFile->read(&B3dTexture.Blend, sizeof(s32));
\r
818 #ifdef __BIG_ENDIAN__
\r
819 B3dTexture.Flags = os::Byteswap::byteswap(B3dTexture.Flags);
\r
820 B3dTexture.Blend = os::Byteswap::byteswap(B3dTexture.Blend);
\r
822 #ifdef _B3D_READER_DEBUG
\r
823 os::Printer::log("Flags", core::stringc(B3dTexture.Flags).c_str(), ELL_DEBUG);
\r
824 os::Printer::log("Blend", core::stringc(B3dTexture.Blend).c_str(), ELL_DEBUG);
\r
826 readFloats(&B3dTexture.Xpos, 1);
\r
827 readFloats(&B3dTexture.Ypos, 1);
\r
828 readFloats(&B3dTexture.Xscale, 1);
\r
829 readFloats(&B3dTexture.Yscale, 1);
\r
830 readFloats(&B3dTexture.Angle, 1);
\r
833 B3dStack.erase(B3dStack.size()-1);
\r
839 bool CB3DMeshFileLoader::readChunkBRUS()
\r
841 #ifdef _B3D_READER_DEBUG
\r
842 core::stringc logStr;
\r
843 for ( u32 i=1; i < B3dStack.size(); ++i )
\r
845 logStr += "read ChunkBRUS";
\r
846 os::Printer::log(logStr.c_str(), ELL_DEBUG);
\r
850 B3DFile->read(&n_texs, sizeof(u32));
\r
851 #ifdef __BIG_ENDIAN__
\r
852 n_texs = os::Byteswap::byteswap(n_texs);
\r
855 // number of texture ids read for Irrlicht
\r
856 const u32 num_textures = core::min_(n_texs, video::MATERIAL_MAX_TEXTURES);
\r
857 // number of bytes to skip (for ignored texture ids)
\r
858 const u32 n_texs_offset = (num_textures<n_texs)?(n_texs-num_textures):0;
\r
860 while((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) //this chunk repeats
\r
862 // This is what blitz basic calls a brush, like a Irrlicht Material
\r
864 core::stringc name;
\r
866 #ifdef _B3D_READER_DEBUG
\r
867 os::Printer::log("read Material", name, ELL_DEBUG);
\r
869 Materials.push_back(SB3dMaterial());
\r
870 SB3dMaterial& B3dMaterial=Materials.getLast();
\r
872 readFloats(&B3dMaterial.red, 1);
\r
873 readFloats(&B3dMaterial.green, 1);
\r
874 readFloats(&B3dMaterial.blue, 1);
\r
875 readFloats(&B3dMaterial.alpha, 1);
\r
876 readFloats(&B3dMaterial.shininess, 1);
\r
878 B3DFile->read(&B3dMaterial.blend, sizeof(B3dMaterial.blend));
\r
879 B3DFile->read(&B3dMaterial.fx, sizeof(B3dMaterial.fx));
\r
880 #ifdef __BIG_ENDIAN__
\r
881 B3dMaterial.blend = os::Byteswap::byteswap(B3dMaterial.blend);
\r
882 B3dMaterial.fx = os::Byteswap::byteswap(B3dMaterial.fx);
\r
884 #ifdef _B3D_READER_DEBUG
\r
885 os::Printer::log("Blend", core::stringc(B3dMaterial.blend).c_str(), ELL_DEBUG);
\r
886 os::Printer::log("FX", core::stringc(B3dMaterial.fx).c_str(), ELL_DEBUG);
\r
890 for (i=0; i<num_textures; ++i)
\r
893 B3DFile->read(&texture_id, sizeof(s32));
\r
894 #ifdef __BIG_ENDIAN__
\r
895 texture_id = os::Byteswap::byteswap(texture_id);
\r
897 //--- Get pointers to the texture, based on the IDs ---
\r
898 if ((u32)texture_id < Textures.size())
\r
900 B3dMaterial.Textures[i]=&Textures[texture_id];
\r
901 #ifdef _B3D_READER_DEBUG
\r
902 os::Printer::log("Layer", core::stringc(i).c_str(), ELL_DEBUG);
\r
903 os::Printer::log("using texture", Textures[texture_id].TextureName.c_str(), ELL_DEBUG);
\r
907 B3dMaterial.Textures[i]=0;
\r
909 // skip other texture ids
\r
910 for (i=0; i<n_texs_offset; ++i)
\r
913 B3DFile->read(&texture_id, sizeof(s32));
\r
914 #ifdef __BIG_ENDIAN__
\r
915 texture_id = os::Byteswap::byteswap(texture_id);
\r
917 if (ShowWarning && (texture_id != -1) && (n_texs>video::MATERIAL_MAX_TEXTURES))
\r
919 os::Printer::log("Too many textures used in one material", B3DFile->getFileName(), ELL_WARNING);
\r
920 ShowWarning = false;
\r
924 //Fixes problems when the lightmap is on the first texture:
\r
925 if (B3dMaterial.Textures[0] != 0)
\r
927 if (B3dMaterial.Textures[0]->Flags & 65536) // 65536 = secondary UV
\r
929 SB3dTexture *TmpTexture;
\r
930 TmpTexture = B3dMaterial.Textures[1];
\r
931 B3dMaterial.Textures[1] = B3dMaterial.Textures[0];
\r
932 B3dMaterial.Textures[0] = TmpTexture;
\r
936 //If a preceeding texture slot is empty move the others down:
\r
937 for (i=num_textures; i>0; --i)
\r
939 for (u32 j=i-1; j<num_textures-1; ++j)
\r
941 if (B3dMaterial.Textures[j+1] != 0 && B3dMaterial.Textures[j] == 0)
\r
943 B3dMaterial.Textures[j] = B3dMaterial.Textures[j+1];
\r
944 B3dMaterial.Textures[j+1] = 0;
\r
949 //------ Convert blitz flags/blend to irrlicht -------
\r
952 if (B3dMaterial.Textures[1])
\r
954 if (B3dMaterial.alpha==1.f)
\r
956 if (B3dMaterial.Textures[1]->Blend == 5) //(Multiply 2)
\r
957 B3dMaterial.Material.MaterialType = video::EMT_LIGHTMAP_M2;
\r
959 B3dMaterial.Material.MaterialType = video::EMT_LIGHTMAP;
\r
960 B3dMaterial.Material.Lighting = false;
\r
964 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
\r
965 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
\r
968 else if (B3dMaterial.Textures[0]) //One texture:
\r
970 // Flags & 0x1 is usual SOLID, 0x8 is mipmap (handled before)
\r
971 if (B3dMaterial.Textures[0]->Flags & 0x2) //(Alpha mapped)
\r
973 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
\r
974 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
\r
976 else if (B3dMaterial.Textures[0]->Flags & 0x4) //(Masked)
\r
977 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; // TODO: create color key texture
\r
978 else if (B3dMaterial.Textures[0]->Flags & 0x40)
\r
979 B3dMaterial.Material.MaterialType = video::EMT_SPHERE_MAP;
\r
980 else if (B3dMaterial.Textures[0]->Flags & 0x80)
\r
981 B3dMaterial.Material.MaterialType = video::EMT_SPHERE_MAP; // TODO: Should be cube map
\r
982 else if (B3dMaterial.alpha == 1.f)
\r
983 B3dMaterial.Material.MaterialType = video::EMT_SOLID;
\r
986 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
\r
987 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
\r
992 if (B3dMaterial.alpha == 1.f)
\r
993 B3dMaterial.Material.MaterialType = video::EMT_SOLID;
\r
996 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
\r
997 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
\r
1001 B3dMaterial.Material.DiffuseColor = video::SColorf(B3dMaterial.red, B3dMaterial.green, B3dMaterial.blue, B3dMaterial.alpha).toSColor();
\r
1002 B3dMaterial.Material.ColorMaterial=video::ECM_NONE;
\r
1004 //------ Material fx ------
\r
1006 if (B3dMaterial.fx & 1) //full-bright
\r
1008 B3dMaterial.Material.AmbientColor = video::SColor(255, 255, 255, 255);
\r
1009 B3dMaterial.Material.Lighting = false;
\r
1012 B3dMaterial.Material.AmbientColor = B3dMaterial.Material.DiffuseColor;
\r
1014 if (B3dMaterial.fx & 2) //use vertex colors instead of brush color
\r
1015 B3dMaterial.Material.ColorMaterial=video::ECM_DIFFUSE_AND_AMBIENT;
\r
1017 if (B3dMaterial.fx & 4) //flatshaded
\r
1018 B3dMaterial.Material.GouraudShading = false;
\r
1020 if (B3dMaterial.fx & 16) //disable backface culling
\r
1021 B3dMaterial.Material.BackfaceCulling = false;
\r
1023 if (B3dMaterial.fx & 32) //force vertex alpha-blending
\r
1025 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
\r
1026 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
\r
1029 B3dMaterial.Material.Shininess = B3dMaterial.shininess;
\r
1032 B3dStack.erase(B3dStack.size()-1);
\r
1038 void CB3DMeshFileLoader::loadTextures(SB3dMaterial& material) const
\r
1040 if ( getMeshTextureLoader() )
\r
1042 if ( SceneManager->getParameters()->existsAttribute(B3D_TEXTURE_PATH) )
\r
1043 getMeshTextureLoader()->setTexturePath( SceneManager->getParameters()->getAttributeAsString(B3D_TEXTURE_PATH) );
\r
1046 const bool previous32BitTextureFlag = SceneManager->getVideoDriver()->getTextureCreationFlag(video::ETCF_ALWAYS_32_BIT);
\r
1047 SceneManager->getVideoDriver()->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
\r
1049 // read texture from disk
\r
1050 // note that mipmaps might be disabled by Flags & 0x8
\r
1051 const bool doMipMaps = SceneManager->getVideoDriver()->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
\r
1053 for (u32 i=0; i<video::MATERIAL_MAX_TEXTURES; ++i)
\r
1055 SB3dTexture* B3dTexture = material.Textures[i];
\r
1056 if (B3dTexture && B3dTexture->TextureName.size() && !material.Material.getTexture(i))
\r
1058 if (!SceneManager->getParameters()->getAttributeAsBool(B3D_LOADER_IGNORE_MIPMAP_FLAG))
\r
1059 SceneManager->getVideoDriver()->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, (B3dTexture->Flags & 0x8) ? true:false);
\r
1061 video::ITexture* tex = getMeshTextureLoader() ? getMeshTextureLoader()->getTexture(B3dTexture->TextureName) : NULL;
\r
1062 material.Material.setTexture(i, tex);
\r
1064 if (material.Textures[i]->Flags & 0x10) // Clamp U
\r
1065 material.Material.TextureLayer[i].TextureWrapU=video::ETC_CLAMP;
\r
1066 if (material.Textures[i]->Flags & 0x20) // Clamp V
\r
1067 material.Material.TextureLayer[i].TextureWrapV=video::ETC_CLAMP;
\r
1068 if (material.Textures[i]->Flags & 0x20) // Clamp R
\r
1069 material.Material.TextureLayer[i].TextureWrapW=video::ETC_CLAMP;
\r
1073 SceneManager->getVideoDriver()->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, doMipMaps);
\r
1074 SceneManager->getVideoDriver()->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, previous32BitTextureFlag);
\r
1078 void CB3DMeshFileLoader::readString(core::stringc& newstring)
\r
1081 while (B3DFile->getPos() <= B3DFile->getSize())
\r
1084 B3DFile->read(&character, sizeof(character));
\r
1087 newstring.append(character);
\r
1092 void CB3DMeshFileLoader::readFloats(f32* vec, u32 count)
\r
1094 B3DFile->read(vec, count*sizeof(f32));
\r
1095 #ifdef __BIG_ENDIAN__
\r
1096 for (u32 n=0; n<count; ++n)
\r
1097 vec[n] = os::Byteswap::byteswap(vec[n]);
\r
1101 } // end namespace scene
\r
1102 } // end namespace irr
\r
1105 #endif // _IRR_COMPILE_WITH_B3D_LOADER_
\r