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
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"
19 #define _B3D_READER_DEBUG
28 CB3DMeshFileLoader::CB3DMeshFileLoader(scene::ISceneManager
*smgr
) :
29 AnimatedMesh(0), B3DFile(0), VerticesStart(0), NormalsInFile(false),
30 HasVertexColors(false), ShowWarning(true)
33 setDebugName("CB3DMeshFileLoader");
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
)
54 AnimatedMesh
= new scene::CSkinnedMesh();
55 ShowWarning
= true; // If true a warning is issued if too many textures are used
59 AnimatedMesh
->finalize();
68 bool CB3DMeshFileLoader::load()
72 NormalsInFile
= false;
73 HasVertexColors
= false;
75 //------ Get header ------
77 SB3dChunkHeader header
;
78 B3DFile
->read(&header
, sizeof(header
));
80 header
.size
= os::Byteswap::byteswap(header
.size
);
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
);
89 B3dStack
.push_back(SB3dChunk(header
, B3DFile
->getPos() - 8));
91 // Get file version, but ignore it, as it's not important with b3d files...
93 B3DFile
->read(&fileVersion
, sizeof(fileVersion
));
95 fileVersion
= os::Byteswap::byteswap(fileVersion
);
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
);
105 B3dStack
.push_back(SB3dChunk(header
, B3DFile
->getPos() - 8));
107 if (strncmp(B3dStack
.getLast().name
, "TEXS", 4) == 0) {
108 if (!readChunkTEXS())
110 } else if (strncmp(B3dStack
.getLast().name
, "BRUS", 4) == 0) {
111 if (!readChunkBRUS())
113 } else if (strncmp(B3dStack
.getLast().name
, "NODE", 4) == 0) {
114 if (!readChunkNODE((CSkinnedMesh::SJoint
*)0))
117 os::Printer::log("Unknown chunk found in mesh base - skipping");
118 if (!B3DFile
->seek(B3dStack
.getLast().startposition
+ B3dStack
.getLast().length
))
120 B3dStack
.erase(B3dStack
.size() - 1);
126 BaseVertices
.clear();
127 AnimatedVertices_VertexID
.clear();
128 AnimatedVertices_BufferID
.clear();
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
)
145 logStr
+= "read ChunkNODE";
146 os::Printer::log(logStr
.c_str(), joint
->Name
.value_or("").c_str(), ELL_DEBUG
);
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
;
171 joint
->GlobalMatrix
= inJoint
->GlobalMatrix
* joint
->LocalMatrix
;
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
);
183 B3dStack
.push_back(SB3dChunk(header
, B3DFile
->getPos() - 8));
185 if (strncmp(B3dStack
.getLast().name
, "NODE", 4) == 0) {
186 if (!readChunkNODE(joint
))
188 } else if (strncmp(B3dStack
.getLast().name
, "MESH", 4) == 0) {
189 VerticesStart
= BaseVertices
.size();
190 if (!readChunkMESH(joint
))
192 } else if (strncmp(B3dStack
.getLast().name
, "BONE", 4) == 0) {
193 if (!readChunkBONE(joint
))
195 } else if (strncmp(B3dStack
.getLast().name
, "KEYS", 4) == 0) {
196 if (!readChunkKEYS(joint
))
198 } else if (strncmp(B3dStack
.getLast().name
, "ANIM", 4) == 0) {
199 if (!readChunkANIM())
202 os::Printer::log("Unknown chunk found in node chunk - skipping");
203 if (!B3DFile
->seek(B3dStack
.getLast().startposition
+ B3dStack
.getLast().length
))
205 B3dStack
.erase(B3dStack
.size() - 1);
209 B3dStack
.erase(B3dStack
.size() - 1);
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
)
220 logStr
+= "read ChunkMESH";
221 os::Printer::log(logStr
.c_str(), ELL_DEBUG
);
225 B3DFile
->read(&brushID
, sizeof(brushID
));
226 #ifdef __BIG_ENDIAN__
227 brushID
= os::Byteswap::byteswap(brushID
);
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
);
241 B3dStack
.push_back(SB3dChunk(header
, B3DFile
->getPos() - 8));
243 if (strncmp(B3dStack
.getLast().name
, "VRTS", 4) == 0) {
244 if (!readChunkVRTS(inJoint
))
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
);
254 meshBuffer
->Material
= Materials
[brushID
].Material
;
257 if (readChunkTRIS(meshBuffer
, AnimatedMesh
->getMeshBuffers().size() - 1, VerticesStart
) == false)
260 if (!NormalsInFile
) {
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
;
280 os::Printer::log("Unknown chunk found in mesh - skipping");
281 if (!B3DFile
->seek(B3dStack
.getLast().startposition
+ B3dStack
.getLast().length
))
283 B3dStack
.erase(B3dStack
.size() - 1);
287 B3dStack
.erase(B3dStack
.size() - 1);
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
)
311 logStr
+= "ChunkVRTS";
312 os::Printer::log(logStr
.c_str(), ELL_DEBUG
);
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
);
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
);
334 //------ Allocate Memory, for speed -----------//
336 s32 numberOfReads
= 3;
339 NormalsInFile
= true;
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
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);
366 readFloats(normal
, 3);
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];
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(),
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
397 BaseVertices
.push_back(Vertex
);
399 AnimatedVertices_VertexID
.push_back(-1);
400 AnimatedVertices_BufferID
.push_back(-1);
403 B3dStack
.erase(B3dStack
.size() - 1);
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
)
414 logStr
+= "ChunkTRIS";
415 os::Printer::log(logStr
.c_str(), ELL_DEBUG
);
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
);
426 SB3dMaterial
*B3dMaterial
;
428 if (triangle_brush_id
== -1)
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
);
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
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]);
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
);
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
]]);
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
;
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");
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
)
530 logStr
+= "read ChunkBONE";
531 os::Printer::log(logStr
.c_str(), ELL_DEBUG
);
534 if (B3dStack
.getLast().length
> 8) {
535 while ((B3dStack
.getLast().startposition
+ B3dStack
.getLast().length
) > B3DFile
->getPos()) // this chunk repeats
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
);
545 globalVertexID
+= VerticesStart
;
547 if (globalVertexID
>= AnimatedVertices_VertexID
.size()) {
548 os::Printer::log("Illegal vertex index found", B3DFile
->getFileName(), ELL_ERROR
);
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);
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
)
576 logStr
+= "read ChunkKEYS";
577 os::Printer::log(logStr
.c_str(), ELL_DEBUG
);
582 B3DFile
->read(&flags
, sizeof(flags
));
583 #ifdef __BIG_ENDIAN__
584 flags
= os::Byteswap::byteswap(flags
);
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
598 B3DFile
->read(&frame
, sizeof(frame
));
599 #ifdef __BIG_ENDIAN__
600 frame
= os::Byteswap::byteswap(frame
);
603 // Add key frames, frames in Irrlicht are zero-based
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;
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]));
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]));
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;
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]));
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]));
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;
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();
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);
695 bool CB3DMeshFileLoader::readChunkANIM()
697 #ifdef _B3D_READER_DEBUG
698 core::stringc logStr
;
699 for (u32 i
= 1; i
< B3dStack
.size(); ++i
)
701 logStr
+= "read ChunkANIM";
702 os::Printer::log(logStr
.c_str(), ELL_DEBUG
);
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);
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
);
721 B3dStack
.erase(B3dStack
.size() - 1);
725 bool CB3DMeshFileLoader::readChunkTEXS()
727 #ifdef _B3D_READER_DEBUG
728 core::stringc logStr
;
729 for (u32 i
= 1; i
< B3dStack
.size(); ++i
)
731 logStr
+= "read ChunkTEXS";
732 os::Printer::log(logStr
.c_str(), ELL_DEBUG
);
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
);
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
);
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
);
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);
768 bool CB3DMeshFileLoader::readChunkBRUS()
770 #ifdef _B3D_READER_DEBUG
771 core::stringc logStr
;
772 for (u32 i
= 1; i
< B3dStack
.size(); ++i
)
774 logStr
+= "read ChunkBRUS";
775 os::Printer::log(logStr
.c_str(), ELL_DEBUG
);
779 B3DFile
->read(&n_texs
, sizeof(u32
));
780 #ifdef __BIG_ENDIAN__
781 n_texs
= os::Byteswap::byteswap(n_texs
);
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
);
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
);
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
);
818 for (i
= 0; i
< num_textures
; ++i
) {
820 B3DFile
->read(&texture_id
, sizeof(s32
));
821 #ifdef __BIG_ENDIAN__
822 texture_id
= os::Byteswap::byteswap(texture_id
);
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
);
832 B3dMaterial
.Textures
[i
] = 0;
834 // skip other texture ids
835 for (i
= 0; i
< n_texs_offset
; ++i
) {
837 B3DFile
->read(&texture_id
, sizeof(s32
));
838 #ifdef __BIG_ENDIAN__
839 texture_id
= os::Byteswap::byteswap(texture_id
);
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
);
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 -------
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
;
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
;
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);
912 std::string
CB3DMeshFileLoader::readString()
914 std::string newstring
= "";
917 if (B3DFile
->read(&character
, sizeof(character
)) == 0)
921 newstring
.push_back(character
);
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
]);
935 } // end namespace scene
936 } // end namespace irr