Move SMaterial std::hash impl to its header
[minetest.git] / irr / src / CB3DMeshFileLoader.cpp
blob6cb40cb9554fb5b1be8982ede88ef2a59ad9fe84
1 // Copyright (C) 2006-2012 Luke Hoschke
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
5 // B3D Mesh loader
6 // File format designed by Mark Sibly for the Blitz3D engine and has been
7 // declared public domain
9 #include "CB3DMeshFileLoader.h"
11 #include "IVideoDriver.h"
12 #include "IFileSystem.h"
13 #include "coreutil.h"
14 #include "os.h"
16 #include <algorithm>
18 #ifdef _DEBUG
19 #define _B3D_READER_DEBUG
20 #endif
22 namespace irr
24 namespace scene
27 //! Constructor
28 CB3DMeshFileLoader::CB3DMeshFileLoader(scene::ISceneManager *smgr) :
29 AnimatedMesh(0), B3DFile(0), VerticesStart(0), NormalsInFile(false),
30 HasVertexColors(false), ShowWarning(true)
32 #ifdef _DEBUG
33 setDebugName("CB3DMeshFileLoader");
34 #endif
37 //! returns true if the file maybe is able to be loaded by this class
38 //! based on the file extension (e.g. ".bsp")
39 bool CB3DMeshFileLoader::isALoadableFileExtension(const io::path &filename) const
41 return core::hasFileExtension(filename, "b3d");
44 //! creates/loads an animated mesh from the file.
45 //! \return Pointer to the created mesh. Returns 0 if loading failed.
46 //! If you no longer need the mesh, you should call IAnimatedMesh::drop().
47 //! See IReferenceCounted::drop() for more information.
48 IAnimatedMesh *CB3DMeshFileLoader::createMesh(io::IReadFile *file)
50 if (!file)
51 return 0;
53 B3DFile = file;
54 AnimatedMesh = new scene::CSkinnedMesh();
55 ShowWarning = true; // If true a warning is issued if too many textures are used
56 VerticesStart = 0;
58 if (load()) {
59 AnimatedMesh->finalize();
60 } else {
61 AnimatedMesh->drop();
62 AnimatedMesh = 0;
65 return AnimatedMesh;
68 bool CB3DMeshFileLoader::load()
70 B3dStack.clear();
72 NormalsInFile = false;
73 HasVertexColors = false;
75 //------ Get header ------
77 SB3dChunkHeader header;
78 B3DFile->read(&header, sizeof(header));
79 #ifdef __BIG_ENDIAN__
80 header.size = os::Byteswap::byteswap(header.size);
81 #endif
83 if (strncmp(header.name, "BB3D", 4) != 0) {
84 os::Printer::log("File is not a b3d file. Loading failed (No header found)", B3DFile->getFileName(), ELL_ERROR);
85 return false;
88 // Add main chunk...
89 B3dStack.push_back(SB3dChunk(header, B3DFile->getPos() - 8));
91 // Get file version, but ignore it, as it's not important with b3d files...
92 s32 fileVersion;
93 B3DFile->read(&fileVersion, sizeof(fileVersion));
94 #ifdef __BIG_ENDIAN__
95 fileVersion = os::Byteswap::byteswap(fileVersion);
96 #endif
98 //------ Read main chunk ------
100 while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) {
101 B3DFile->read(&header, sizeof(header));
102 #ifdef __BIG_ENDIAN__
103 header.size = os::Byteswap::byteswap(header.size);
104 #endif
105 B3dStack.push_back(SB3dChunk(header, B3DFile->getPos() - 8));
107 if (strncmp(B3dStack.getLast().name, "TEXS", 4) == 0) {
108 if (!readChunkTEXS())
109 return false;
110 } else if (strncmp(B3dStack.getLast().name, "BRUS", 4) == 0) {
111 if (!readChunkBRUS())
112 return false;
113 } else if (strncmp(B3dStack.getLast().name, "NODE", 4) == 0) {
114 if (!readChunkNODE((CSkinnedMesh::SJoint *)0))
115 return false;
116 } else {
117 os::Printer::log("Unknown chunk found in mesh base - skipping");
118 if (!B3DFile->seek(B3dStack.getLast().startposition + B3dStack.getLast().length))
119 return false;
120 B3dStack.erase(B3dStack.size() - 1);
124 B3dStack.clear();
126 BaseVertices.clear();
127 AnimatedVertices_VertexID.clear();
128 AnimatedVertices_BufferID.clear();
130 Materials.clear();
131 Textures.clear();
133 return true;
136 bool CB3DMeshFileLoader::readChunkNODE(CSkinnedMesh::SJoint *inJoint)
138 CSkinnedMesh::SJoint *joint = AnimatedMesh->addJoint(inJoint);
139 joint->Name = readString();
141 #ifdef _B3D_READER_DEBUG
142 core::stringc logStr;
143 for (u32 i = 1; i < B3dStack.size(); ++i)
144 logStr += "-";
145 logStr += "read ChunkNODE";
146 os::Printer::log(logStr.c_str(), joint->Name.value_or("").c_str(), ELL_DEBUG);
147 #endif
149 f32 position[3], scale[3], rotation[4];
151 readFloats(position, 3);
152 readFloats(scale, 3);
153 readFloats(rotation, 4);
155 joint->Animatedposition = core::vector3df(position[0], position[1], position[2]);
156 joint->Animatedscale = core::vector3df(scale[0], scale[1], scale[2]);
157 joint->Animatedrotation = core::quaternion(rotation[1], rotation[2], rotation[3], rotation[0]);
159 // Build LocalMatrix:
161 core::matrix4 positionMatrix;
162 positionMatrix.setTranslation(joint->Animatedposition);
163 core::matrix4 scaleMatrix;
164 scaleMatrix.setScale(joint->Animatedscale);
165 core::matrix4 rotationMatrix;
166 joint->Animatedrotation.getMatrix_transposed(rotationMatrix);
168 joint->LocalMatrix = positionMatrix * rotationMatrix * scaleMatrix;
170 if (inJoint)
171 joint->GlobalMatrix = inJoint->GlobalMatrix * joint->LocalMatrix;
172 else
173 joint->GlobalMatrix = joint->LocalMatrix;
175 while (B3dStack.getLast().startposition + B3dStack.getLast().length > B3DFile->getPos()) // this chunk repeats
177 SB3dChunkHeader header;
178 B3DFile->read(&header, sizeof(header));
179 #ifdef __BIG_ENDIAN__
180 header.size = os::Byteswap::byteswap(header.size);
181 #endif
183 B3dStack.push_back(SB3dChunk(header, B3DFile->getPos() - 8));
185 if (strncmp(B3dStack.getLast().name, "NODE", 4) == 0) {
186 if (!readChunkNODE(joint))
187 return false;
188 } else if (strncmp(B3dStack.getLast().name, "MESH", 4) == 0) {
189 VerticesStart = BaseVertices.size();
190 if (!readChunkMESH(joint))
191 return false;
192 } else if (strncmp(B3dStack.getLast().name, "BONE", 4) == 0) {
193 if (!readChunkBONE(joint))
194 return false;
195 } else if (strncmp(B3dStack.getLast().name, "KEYS", 4) == 0) {
196 if (!readChunkKEYS(joint))
197 return false;
198 } else if (strncmp(B3dStack.getLast().name, "ANIM", 4) == 0) {
199 if (!readChunkANIM())
200 return false;
201 } else {
202 os::Printer::log("Unknown chunk found in node chunk - skipping");
203 if (!B3DFile->seek(B3dStack.getLast().startposition + B3dStack.getLast().length))
204 return false;
205 B3dStack.erase(B3dStack.size() - 1);
209 B3dStack.erase(B3dStack.size() - 1);
211 return true;
214 bool CB3DMeshFileLoader::readChunkMESH(CSkinnedMesh::SJoint *inJoint)
216 #ifdef _B3D_READER_DEBUG
217 core::stringc logStr;
218 for (u32 i = 1; i < B3dStack.size(); ++i)
219 logStr += "-";
220 logStr += "read ChunkMESH";
221 os::Printer::log(logStr.c_str(), ELL_DEBUG);
222 #endif
224 s32 brushID;
225 B3DFile->read(&brushID, sizeof(brushID));
226 #ifdef __BIG_ENDIAN__
227 brushID = os::Byteswap::byteswap(brushID);
228 #endif
230 NormalsInFile = false;
231 HasVertexColors = false;
233 while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
235 SB3dChunkHeader header;
236 B3DFile->read(&header, sizeof(header));
237 #ifdef __BIG_ENDIAN__
238 header.size = os::Byteswap::byteswap(header.size);
239 #endif
241 B3dStack.push_back(SB3dChunk(header, B3DFile->getPos() - 8));
243 if (strncmp(B3dStack.getLast().name, "VRTS", 4) == 0) {
244 if (!readChunkVRTS(inJoint))
245 return false;
246 } else if (strncmp(B3dStack.getLast().name, "TRIS", 4) == 0) {
247 scene::SSkinMeshBuffer *meshBuffer = AnimatedMesh->addMeshBuffer();
249 if (brushID == -1) { /* ok */
250 } else if (brushID < 0 || (u32)brushID >= Materials.size()) {
251 os::Printer::log("Illegal brush ID found", B3DFile->getFileName(), ELL_ERROR);
252 return false;
253 } else {
254 meshBuffer->Material = Materials[brushID].Material;
257 if (readChunkTRIS(meshBuffer, AnimatedMesh->getMeshBuffers().size() - 1, VerticesStart) == false)
258 return false;
260 if (!NormalsInFile) {
261 s32 i;
263 auto &indices = meshBuffer->Indices->Data;
264 for (i = 0; i < (s32)indices.size(); i += 3) {
265 core::plane3df p(meshBuffer->getVertex(indices[i + 0])->Pos,
266 meshBuffer->getVertex(indices[i + 1])->Pos,
267 meshBuffer->getVertex(indices[i + 2])->Pos);
269 meshBuffer->getVertex(indices[i + 0])->Normal += p.Normal;
270 meshBuffer->getVertex(indices[i + 1])->Normal += p.Normal;
271 meshBuffer->getVertex(indices[i + 2])->Normal += p.Normal;
274 for (i = 0; i < (s32)meshBuffer->getVertexCount(); ++i) {
275 meshBuffer->getVertex(i)->Normal.normalize();
276 BaseVertices[VerticesStart + i].Normal = meshBuffer->getVertex(i)->Normal;
279 } else {
280 os::Printer::log("Unknown chunk found in mesh - skipping");
281 if (!B3DFile->seek(B3dStack.getLast().startposition + B3dStack.getLast().length))
282 return false;
283 B3dStack.erase(B3dStack.size() - 1);
287 B3dStack.erase(B3dStack.size() - 1);
289 return true;
293 VRTS:
294 int flags ;1=normal values present, 2=rgba values present
295 int tex_coord_sets ;texture coords per vertex (eg: 1 for simple U/V) max=8
296 but we only support 3
297 int tex_coord_set_size ;components per set (eg: 2 for simple U/V) max=4
299 float x,y,z ;always present
300 float nx,ny,nz ;vertex normal: present if (flags&1)
301 float red,green,blue,alpha ;vertex color: present if (flags&2)
302 float tex_coords[tex_coord_sets][tex_coord_set_size] ;tex coords
305 bool CB3DMeshFileLoader::readChunkVRTS(CSkinnedMesh::SJoint *inJoint)
307 #ifdef _B3D_READER_DEBUG
308 core::stringc logStr;
309 for (u32 i = 1; i < B3dStack.size(); ++i)
310 logStr += "-";
311 logStr += "ChunkVRTS";
312 os::Printer::log(logStr.c_str(), ELL_DEBUG);
313 #endif
315 const s32 max_tex_coords = 3;
316 s32 flags, tex_coord_sets, tex_coord_set_size;
318 B3DFile->read(&flags, sizeof(flags));
319 B3DFile->read(&tex_coord_sets, sizeof(tex_coord_sets));
320 B3DFile->read(&tex_coord_set_size, sizeof(tex_coord_set_size));
321 #ifdef __BIG_ENDIAN__
322 flags = os::Byteswap::byteswap(flags);
323 tex_coord_sets = os::Byteswap::byteswap(tex_coord_sets);
324 tex_coord_set_size = os::Byteswap::byteswap(tex_coord_set_size);
325 #endif
327 if (tex_coord_sets < 0 || tex_coord_set_size < 0 ||
328 tex_coord_sets >= max_tex_coords || tex_coord_set_size >= 4) // Something is wrong
330 os::Printer::log("tex_coord_sets or tex_coord_set_size too big", B3DFile->getFileName(), ELL_ERROR);
331 return false;
334 //------ Allocate Memory, for speed -----------//
336 s32 numberOfReads = 3;
338 if (flags & 1) {
339 NormalsInFile = true;
340 numberOfReads += 3;
342 if (flags & 2) {
343 numberOfReads += 4;
344 HasVertexColors = true;
347 numberOfReads += tex_coord_sets * tex_coord_set_size;
349 const s32 memoryNeeded = (B3dStack.getLast().length / sizeof(f32)) / numberOfReads;
351 BaseVertices.reallocate(memoryNeeded + BaseVertices.size() + 1);
352 AnimatedVertices_VertexID.reallocate(memoryNeeded + AnimatedVertices_VertexID.size() + 1);
354 //--------------------------------------------//
356 while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
358 f32 position[3];
359 f32 normal[3] = {0.f, 0.f, 0.f};
360 f32 color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
361 f32 tex_coords[max_tex_coords][4];
363 readFloats(position, 3);
365 if (flags & 1)
366 readFloats(normal, 3);
367 if (flags & 2)
368 readFloats(color, 4);
370 for (s32 i = 0; i < tex_coord_sets; ++i)
371 readFloats(tex_coords[i], tex_coord_set_size);
373 f32 tu = 0.0f, tv = 0.0f;
374 if (tex_coord_sets >= 1 && tex_coord_set_size >= 2) {
375 tu = tex_coords[0][0];
376 tv = tex_coords[0][1];
379 f32 tu2 = 0.0f, tv2 = 0.0f;
380 if (tex_coord_sets > 1 && tex_coord_set_size > 1) {
381 tu2 = tex_coords[1][0];
382 tv2 = tex_coords[1][1];
385 // Create Vertex...
386 video::S3DVertex2TCoords Vertex(position[0], position[1], position[2],
387 normal[0], normal[1], normal[2],
388 video::SColorf(color[0], color[1], color[2], color[3]).toSColor(),
389 tu, tv, tu2, tv2);
391 // Transform the Vertex position by nested node...
392 inJoint->GlobalMatrix.transformVect(Vertex.Pos);
393 Vertex.Normal = inJoint->GlobalMatrix.rotateAndScaleVect(Vertex.Normal);
394 Vertex.Normal.normalize(); // renormalize: normal might have been skewed by scaling
396 // Add it...
397 BaseVertices.push_back(Vertex);
399 AnimatedVertices_VertexID.push_back(-1);
400 AnimatedVertices_BufferID.push_back(-1);
403 B3dStack.erase(B3dStack.size() - 1);
405 return true;
408 bool CB3DMeshFileLoader::readChunkTRIS(scene::SSkinMeshBuffer *meshBuffer, u32 meshBufferID, s32 vertices_Start)
410 #ifdef _B3D_READER_DEBUG
411 core::stringc logStr;
412 for (u32 i = 1; i < B3dStack.size(); ++i)
413 logStr += "-";
414 logStr += "ChunkTRIS";
415 os::Printer::log(logStr.c_str(), ELL_DEBUG);
416 #endif
418 bool showVertexWarning = false;
420 s32 triangle_brush_id; // Note: Irrlicht can't have different brushes for each triangle (using a workaround)
421 B3DFile->read(&triangle_brush_id, sizeof(triangle_brush_id));
422 #ifdef __BIG_ENDIAN__
423 triangle_brush_id = os::Byteswap::byteswap(triangle_brush_id);
424 #endif
426 SB3dMaterial *B3dMaterial;
428 if (triangle_brush_id == -1)
429 B3dMaterial = 0;
430 else if (triangle_brush_id < 0 || (u32)triangle_brush_id >= Materials.size()) {
431 os::Printer::log("Illegal material index found", B3DFile->getFileName(), ELL_ERROR);
432 return false;
433 } else {
434 B3dMaterial = &Materials[triangle_brush_id];
435 meshBuffer->Material = B3dMaterial->Material;
438 const s32 memoryNeeded = B3dStack.getLast().length / sizeof(s32);
439 meshBuffer->Indices->Data.reserve(memoryNeeded + meshBuffer->Indices->Data.size() + 1);
441 while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
443 s32 vertex_id[3];
445 B3DFile->read(vertex_id, 3 * sizeof(s32));
446 #ifdef __BIG_ENDIAN__
447 vertex_id[0] = os::Byteswap::byteswap(vertex_id[0]);
448 vertex_id[1] = os::Byteswap::byteswap(vertex_id[1]);
449 vertex_id[2] = os::Byteswap::byteswap(vertex_id[2]);
450 #endif
452 // Make Ids global:
453 vertex_id[0] += vertices_Start;
454 vertex_id[1] += vertices_Start;
455 vertex_id[2] += vertices_Start;
457 for (s32 i = 0; i < 3; ++i) {
458 if ((u32)vertex_id[i] >= AnimatedVertices_VertexID.size()) {
459 os::Printer::log("Illegal vertex index found", B3DFile->getFileName(), ELL_ERROR);
460 return false;
463 if (AnimatedVertices_VertexID[vertex_id[i]] != -1) {
464 if (AnimatedVertices_BufferID[vertex_id[i]] != (s32)meshBufferID) { // If this vertex is linked in a different meshbuffer
465 AnimatedVertices_VertexID[vertex_id[i]] = -1;
466 AnimatedVertices_BufferID[vertex_id[i]] = -1;
467 showVertexWarning = true;
470 if (AnimatedVertices_VertexID[vertex_id[i]] == -1) { // If this vertex is not in the meshbuffer
471 // Check for lightmapping:
472 if (BaseVertices[vertex_id[i]].TCoords2 != core::vector2df(0.f, 0.f))
473 meshBuffer->convertTo2TCoords(); // Will only affect the meshbuffer the first time this is called
475 // Add the vertex to the meshbuffer:
476 if (meshBuffer->VertexType == video::EVT_STANDARD)
477 meshBuffer->Vertices_Standard->Data.push_back(BaseVertices[vertex_id[i]]);
478 else
479 meshBuffer->Vertices_2TCoords->Data.push_back(BaseVertices[vertex_id[i]]);
481 // create vertex id to meshbuffer index link:
482 AnimatedVertices_VertexID[vertex_id[i]] = meshBuffer->getVertexCount() - 1;
483 AnimatedVertices_BufferID[vertex_id[i]] = meshBufferID;
485 if (B3dMaterial) {
486 // Apply Material/Color/etc...
487 video::S3DVertex *Vertex = meshBuffer->getVertex(meshBuffer->getVertexCount() - 1);
489 if (!HasVertexColors)
490 Vertex->Color = video::SColorf(B3dMaterial->red, B3dMaterial->green,
491 B3dMaterial->blue, B3dMaterial->alpha).toSColor();
492 else if (Vertex->Color.getAlpha() == 255)
493 Vertex->Color.setAlpha((s32)(B3dMaterial->alpha * 255.0f));
495 // Use texture's scale
496 if (B3dMaterial->Textures[0]) {
497 Vertex->TCoords.X *= B3dMaterial->Textures[0]->Xscale;
498 Vertex->TCoords.Y *= B3dMaterial->Textures[0]->Yscale;
501 if (B3dMaterial->Textures[1])
503 Vertex->TCoords2.X *=B3dMaterial->Textures[1]->Xscale;
504 Vertex->TCoords2.Y *=B3dMaterial->Textures[1]->Yscale;
511 meshBuffer->Indices->Data.push_back(AnimatedVertices_VertexID[vertex_id[0]]);
512 meshBuffer->Indices->Data.push_back(AnimatedVertices_VertexID[vertex_id[1]]);
513 meshBuffer->Indices->Data.push_back(AnimatedVertices_VertexID[vertex_id[2]]);
516 B3dStack.erase(B3dStack.size() - 1);
518 if (showVertexWarning)
519 os::Printer::log("B3dMeshLoader: Warning, different meshbuffers linking to the same vertex, this will cause problems with animated meshes");
521 return true;
524 bool CB3DMeshFileLoader::readChunkBONE(CSkinnedMesh::SJoint *inJoint)
526 #ifdef _B3D_READER_DEBUG
527 core::stringc logStr;
528 for (u32 i = 1; i < B3dStack.size(); ++i)
529 logStr += "-";
530 logStr += "read ChunkBONE";
531 os::Printer::log(logStr.c_str(), ELL_DEBUG);
532 #endif
534 if (B3dStack.getLast().length > 8) {
535 while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
537 u32 globalVertexID;
538 f32 strength;
539 B3DFile->read(&globalVertexID, sizeof(globalVertexID));
540 B3DFile->read(&strength, sizeof(strength));
541 #ifdef __BIG_ENDIAN__
542 globalVertexID = os::Byteswap::byteswap(globalVertexID);
543 strength = os::Byteswap::byteswap(strength);
544 #endif
545 globalVertexID += VerticesStart;
547 if (globalVertexID >= AnimatedVertices_VertexID.size()) {
548 os::Printer::log("Illegal vertex index found", B3DFile->getFileName(), ELL_ERROR);
549 return false;
552 if (AnimatedVertices_VertexID[globalVertexID] == -1) {
553 os::Printer::log("B3dMeshLoader: Weight has bad vertex id (no link to meshbuffer index found)");
554 } else if (strength > 0) {
555 CSkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(inJoint);
556 weight->strength = strength;
557 // Find the meshbuffer and Vertex index from the Global Vertex ID:
558 weight->vertex_id = AnimatedVertices_VertexID[globalVertexID];
559 weight->buffer_id = AnimatedVertices_BufferID[globalVertexID];
564 B3dStack.erase(B3dStack.size() - 1);
565 return true;
568 bool CB3DMeshFileLoader::readChunkKEYS(CSkinnedMesh::SJoint *inJoint)
570 #ifdef _B3D_READER_DEBUG
571 // Only print first, that's just too much output otherwise
572 if (!inJoint || (inJoint->PositionKeys.empty() && inJoint->ScaleKeys.empty() && inJoint->RotationKeys.empty())) {
573 core::stringc logStr;
574 for (u32 i = 1; i < B3dStack.size(); ++i)
575 logStr += "-";
576 logStr += "read ChunkKEYS";
577 os::Printer::log(logStr.c_str(), ELL_DEBUG);
579 #endif
581 s32 flags;
582 B3DFile->read(&flags, sizeof(flags));
583 #ifdef __BIG_ENDIAN__
584 flags = os::Byteswap::byteswap(flags);
585 #endif
587 CSkinnedMesh::SPositionKey *oldPosKey = 0;
588 core::vector3df oldPos[2];
589 CSkinnedMesh::SScaleKey *oldScaleKey = 0;
590 core::vector3df oldScale[2];
591 CSkinnedMesh::SRotationKey *oldRotKey = 0;
592 core::quaternion oldRot[2];
593 bool isFirst[3] = {true, true, true};
594 while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
596 s32 frame;
598 B3DFile->read(&frame, sizeof(frame));
599 #ifdef __BIG_ENDIAN__
600 frame = os::Byteswap::byteswap(frame);
601 #endif
603 // Add key frames, frames in Irrlicht are zero-based
604 f32 data[4];
605 if (flags & 1) {
606 readFloats(data, 3);
607 if ((oldPosKey != 0) && (oldPos[0] == oldPos[1])) {
608 const core::vector3df pos(data[0], data[1], data[2]);
609 if (oldPos[1] == pos)
610 oldPosKey->frame = (f32)frame - 1;
611 else {
612 oldPos[0] = oldPos[1];
613 oldPosKey = AnimatedMesh->addPositionKey(inJoint);
614 oldPosKey->frame = (f32)frame - 1;
615 oldPos[1].set(oldPosKey->position.set(pos));
617 } else if (oldPosKey == 0 && isFirst[0]) {
618 oldPosKey = AnimatedMesh->addPositionKey(inJoint);
619 oldPosKey->frame = (f32)frame - 1;
620 oldPos[0].set(oldPosKey->position.set(data[0], data[1], data[2]));
621 oldPosKey = 0;
622 isFirst[0] = false;
623 } else {
624 if (oldPosKey != 0)
625 oldPos[0] = oldPos[1];
626 oldPosKey = AnimatedMesh->addPositionKey(inJoint);
627 oldPosKey->frame = (f32)frame - 1;
628 oldPos[1].set(oldPosKey->position.set(data[0], data[1], data[2]));
631 if (flags & 2) {
632 readFloats(data, 3);
633 if ((oldScaleKey != 0) && (oldScale[0] == oldScale[1])) {
634 const core::vector3df scale(data[0], data[1], data[2]);
635 if (oldScale[1] == scale)
636 oldScaleKey->frame = (f32)frame - 1;
637 else {
638 oldScale[0] = oldScale[1];
639 oldScaleKey = AnimatedMesh->addScaleKey(inJoint);
640 oldScaleKey->frame = (f32)frame - 1;
641 oldScale[1].set(oldScaleKey->scale.set(scale));
643 } else if (oldScaleKey == 0 && isFirst[1]) {
644 oldScaleKey = AnimatedMesh->addScaleKey(inJoint);
645 oldScaleKey->frame = (f32)frame - 1;
646 oldScale[0].set(oldScaleKey->scale.set(data[0], data[1], data[2]));
647 oldScaleKey = 0;
648 isFirst[1] = false;
649 } else {
650 if (oldScaleKey != 0)
651 oldScale[0] = oldScale[1];
652 oldScaleKey = AnimatedMesh->addScaleKey(inJoint);
653 oldScaleKey->frame = (f32)frame - 1;
654 oldScale[1].set(oldScaleKey->scale.set(data[0], data[1], data[2]));
657 if (flags & 4) {
658 readFloats(data, 4);
659 if ((oldRotKey != 0) && (oldRot[0] == oldRot[1])) {
660 // meant to be in this order since b3d stores W first
661 const core::quaternion rot(data[1], data[2], data[3], data[0]);
662 if (oldRot[1] == rot)
663 oldRotKey->frame = (f32)frame - 1;
664 else {
665 oldRot[0] = oldRot[1];
666 oldRotKey = AnimatedMesh->addRotationKey(inJoint);
667 oldRotKey->frame = (f32)frame - 1;
668 oldRot[1].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0]));
669 oldRot[1].normalize();
671 } else if (oldRotKey == 0 && isFirst[2]) {
672 oldRotKey = AnimatedMesh->addRotationKey(inJoint);
673 oldRotKey->frame = (f32)frame - 1;
674 // meant to be in this order since b3d stores W first
675 oldRot[0].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0]));
676 oldRot[0].normalize();
677 oldRotKey = 0;
678 isFirst[2] = false;
679 } else {
680 if (oldRotKey != 0)
681 oldRot[0] = oldRot[1];
682 oldRotKey = AnimatedMesh->addRotationKey(inJoint);
683 oldRotKey->frame = (f32)frame - 1;
684 // meant to be in this order since b3d stores W first
685 oldRot[1].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0]));
686 oldRot[1].normalize();
691 B3dStack.erase(B3dStack.size() - 1);
692 return true;
695 bool CB3DMeshFileLoader::readChunkANIM()
697 #ifdef _B3D_READER_DEBUG
698 core::stringc logStr;
699 for (u32 i = 1; i < B3dStack.size(); ++i)
700 logStr += "-";
701 logStr += "read ChunkANIM";
702 os::Printer::log(logStr.c_str(), ELL_DEBUG);
703 #endif
705 s32 animFlags; // not stored\used
706 s32 animFrames; // not stored\used
707 f32 animFPS; // not stored\used
709 B3DFile->read(&animFlags, sizeof(s32));
710 B3DFile->read(&animFrames, sizeof(s32));
711 readFloats(&animFPS, 1);
712 if (animFPS > 0.f)
713 AnimatedMesh->setAnimationSpeed(animFPS);
714 os::Printer::log("FPS", io::path((double)animFPS), ELL_DEBUG);
716 #ifdef __BIG_ENDIAN__
717 animFlags = os::Byteswap::byteswap(animFlags);
718 animFrames = os::Byteswap::byteswap(animFrames);
719 #endif
721 B3dStack.erase(B3dStack.size() - 1);
722 return true;
725 bool CB3DMeshFileLoader::readChunkTEXS()
727 #ifdef _B3D_READER_DEBUG
728 core::stringc logStr;
729 for (u32 i = 1; i < B3dStack.size(); ++i)
730 logStr += "-";
731 logStr += "read ChunkTEXS";
732 os::Printer::log(logStr.c_str(), ELL_DEBUG);
733 #endif
735 while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
737 Textures.push_back(SB3dTexture());
738 SB3dTexture &B3dTexture = Textures.getLast();
740 B3dTexture.TextureName = readString();
741 std::replace(B3dTexture.TextureName.begin(), B3dTexture.TextureName.end(), '\\', '/');
742 #ifdef _B3D_READER_DEBUG
743 os::Printer::log("read Texture", B3dTexture.TextureName.c_str(), ELL_DEBUG);
744 #endif
746 B3DFile->read(&B3dTexture.Flags, sizeof(s32));
747 B3DFile->read(&B3dTexture.Blend, sizeof(s32));
748 #ifdef __BIG_ENDIAN__
749 B3dTexture.Flags = os::Byteswap::byteswap(B3dTexture.Flags);
750 B3dTexture.Blend = os::Byteswap::byteswap(B3dTexture.Blend);
751 #endif
752 #ifdef _B3D_READER_DEBUG
753 os::Printer::log("Flags", core::stringc(B3dTexture.Flags).c_str(), ELL_DEBUG);
754 os::Printer::log("Blend", core::stringc(B3dTexture.Blend).c_str(), ELL_DEBUG);
755 #endif
756 readFloats(&B3dTexture.Xpos, 1);
757 readFloats(&B3dTexture.Ypos, 1);
758 readFloats(&B3dTexture.Xscale, 1);
759 readFloats(&B3dTexture.Yscale, 1);
760 readFloats(&B3dTexture.Angle, 1);
763 B3dStack.erase(B3dStack.size() - 1);
765 return true;
768 bool CB3DMeshFileLoader::readChunkBRUS()
770 #ifdef _B3D_READER_DEBUG
771 core::stringc logStr;
772 for (u32 i = 1; i < B3dStack.size(); ++i)
773 logStr += "-";
774 logStr += "read ChunkBRUS";
775 os::Printer::log(logStr.c_str(), ELL_DEBUG);
776 #endif
778 u32 n_texs;
779 B3DFile->read(&n_texs, sizeof(u32));
780 #ifdef __BIG_ENDIAN__
781 n_texs = os::Byteswap::byteswap(n_texs);
782 #endif
784 // number of texture ids read for Irrlicht
785 const u32 num_textures = core::min_(n_texs, video::MATERIAL_MAX_TEXTURES);
786 // number of bytes to skip (for ignored texture ids)
787 const u32 n_texs_offset = (num_textures < n_texs) ? (n_texs - num_textures) : 0;
789 while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
791 // This is what blitz basic calls a brush, like a Irrlicht Material
793 auto name = readString();
794 #ifdef _B3D_READER_DEBUG
795 os::Printer::log("read Material", name.c_str(), ELL_DEBUG);
796 #endif
797 Materials.push_back(SB3dMaterial());
798 SB3dMaterial &B3dMaterial = Materials.getLast();
800 readFloats(&B3dMaterial.red, 1);
801 readFloats(&B3dMaterial.green, 1);
802 readFloats(&B3dMaterial.blue, 1);
803 readFloats(&B3dMaterial.alpha, 1);
804 readFloats(&B3dMaterial.shininess, 1);
806 B3DFile->read(&B3dMaterial.blend, sizeof(B3dMaterial.blend));
807 B3DFile->read(&B3dMaterial.fx, sizeof(B3dMaterial.fx));
808 #ifdef __BIG_ENDIAN__
809 B3dMaterial.blend = os::Byteswap::byteswap(B3dMaterial.blend);
810 B3dMaterial.fx = os::Byteswap::byteswap(B3dMaterial.fx);
811 #endif
812 #ifdef _B3D_READER_DEBUG
813 os::Printer::log("Blend", core::stringc(B3dMaterial.blend).c_str(), ELL_DEBUG);
814 os::Printer::log("FX", core::stringc(B3dMaterial.fx).c_str(), ELL_DEBUG);
815 #endif
817 u32 i;
818 for (i = 0; i < num_textures; ++i) {
819 s32 texture_id = -1;
820 B3DFile->read(&texture_id, sizeof(s32));
821 #ifdef __BIG_ENDIAN__
822 texture_id = os::Byteswap::byteswap(texture_id);
823 #endif
824 //--- Get pointers to the texture, based on the IDs ---
825 if ((u32)texture_id < Textures.size()) {
826 B3dMaterial.Textures[i] = &Textures[texture_id];
827 #ifdef _B3D_READER_DEBUG
828 os::Printer::log("Layer", core::stringc(i).c_str(), ELL_DEBUG);
829 os::Printer::log("using texture", Textures[texture_id].TextureName.c_str(), ELL_DEBUG);
830 #endif
831 } else
832 B3dMaterial.Textures[i] = 0;
834 // skip other texture ids
835 for (i = 0; i < n_texs_offset; ++i) {
836 s32 texture_id = -1;
837 B3DFile->read(&texture_id, sizeof(s32));
838 #ifdef __BIG_ENDIAN__
839 texture_id = os::Byteswap::byteswap(texture_id);
840 #endif
841 if (ShowWarning && (texture_id != -1) && (n_texs > video::MATERIAL_MAX_TEXTURES)) {
842 os::Printer::log("Too many textures used in one material", B3DFile->getFileName(), ELL_WARNING);
843 ShowWarning = false;
847 // Fixes problems when the lightmap is on the first texture:
848 if (B3dMaterial.Textures[0] != 0) {
849 if (B3dMaterial.Textures[0]->Flags & 65536) { // 65536 = secondary UV
850 SB3dTexture *TmpTexture;
851 TmpTexture = B3dMaterial.Textures[1];
852 B3dMaterial.Textures[1] = B3dMaterial.Textures[0];
853 B3dMaterial.Textures[0] = TmpTexture;
857 // If a preceeding texture slot is empty move the others down:
858 for (i = num_textures; i > 0; --i) {
859 for (u32 j = i - 1; j < num_textures - 1; ++j) {
860 if (B3dMaterial.Textures[j + 1] != 0 && B3dMaterial.Textures[j] == 0) {
861 B3dMaterial.Textures[j] = B3dMaterial.Textures[j + 1];
862 B3dMaterial.Textures[j + 1] = 0;
867 //------ Convert blitz flags/blend to irrlicht -------
869 // Two textures:
870 if (B3dMaterial.Textures[1]) {
871 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
872 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
873 } else if (B3dMaterial.Textures[0]) { // One texture:
874 // Flags & 0x1 is usual SOLID, 0x8 is mipmap (handled before)
875 if (B3dMaterial.Textures[0]->Flags & 0x2) { // (Alpha mapped)
876 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
877 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
878 } else if (B3dMaterial.Textures[0]->Flags & 0x4) //(Masked)
879 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; // TODO: create color key texture
880 else if (B3dMaterial.alpha == 1.f)
881 B3dMaterial.Material.MaterialType = video::EMT_SOLID;
882 else {
883 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
884 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
886 } else // No texture:
888 if (B3dMaterial.alpha == 1.f)
889 B3dMaterial.Material.MaterialType = video::EMT_SOLID;
890 else {
891 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
892 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
896 //------ Material fx ------
898 if (B3dMaterial.fx & 16) // disable backface culling
899 B3dMaterial.Material.BackfaceCulling = false;
901 if (B3dMaterial.fx & 32) { // force vertex alpha-blending
902 B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
903 B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
907 B3dStack.erase(B3dStack.size() - 1);
909 return true;
912 std::string CB3DMeshFileLoader::readString()
914 std::string newstring = "";
915 while (true) {
916 c8 character;
917 if (B3DFile->read(&character, sizeof(character)) == 0)
918 break; // eof
919 if (character == 0)
920 break;
921 newstring.push_back(character);
923 return newstring;
926 void CB3DMeshFileLoader::readFloats(f32 *vec, u32 count)
928 B3DFile->read(vec, count * sizeof(f32));
929 #ifdef __BIG_ENDIAN__
930 for (u32 n = 0; n < count; ++n)
931 vec[n] = os::Byteswap::byteswap(vec[n]);
932 #endif
935 } // end namespace scene
936 } // end namespace irr