/Irrlicht/CDMFLoader.cpp
C++ | 437 lines | 324 code | 52 blank | 61 comment | 76 complexity | 316aa51d20b2330c248fe4a46158f189 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0
1// Copyright (C) 2002-2010 Nikolaus Gebhardt 2// This file is part of the "Irrlicht Engine". 3// For conditions of distribution and use, see copyright notice in irrlicht.h 4// 5// This file was originally written by Salvatore Russo. 6// I (Nikolaus Gebhardt) did some minor modifications and changes to it and 7// integrated it into Irrlicht. 8// Thanks a lot to Salvatore for his work on this and that he gave me 9// his permission to add it into Irrlicht using the zlib license. 10/* 11 CDMFLoader by Salvatore Russo (September 2005) 12 13 See the header file for additional information including use and distribution rights. 14*/ 15 16#include "IrrCompileConfig.h" 17#ifdef _IRR_COMPILE_WITH_DMF_LOADER_ 18 19#ifdef _DEBUG 20#define _IRR_DMF_DEBUG_ 21#include "os.h" 22#endif 23 24#include "CDMFLoader.h" 25#include "ISceneManager.h" 26#include "IAttributes.h" 27#include "SAnimatedMesh.h" 28#include "SSkinMeshBuffer.h" 29#include "irrString.h" 30#include "irrMath.h" 31#include "dmfsupport.h" 32#include "CImage.h" 33 34namespace irr 35{ 36namespace scene 37{ 38 39/** Constructor*/ 40CDMFLoader::CDMFLoader(ISceneManager* smgr, io::IFileSystem* filesys) 41: SceneMgr(smgr), FileSystem(filesys) 42{ 43 #ifdef _DEBUG 44 IReferenceCounted::setDebugName("CDMFLoader"); 45 #endif 46} 47 48 49void CDMFLoader::findFile(bool use_mat_dirs, const core::stringc& path, const core::stringc& matPath, core::stringc& filename) 50{ 51 // path + texpath + full name 52 if (use_mat_dirs && FileSystem->existFile(path+matPath+filename)) 53 filename = path+matPath+filename; 54 // path + full name 55 else if (FileSystem->existFile(path+filename)) 56 filename = path+filename; 57 // path + texpath + base name 58 else if (use_mat_dirs && FileSystem->existFile(path+matPath+FileSystem->getFileBasename(filename))) 59 filename = path+matPath+FileSystem->getFileBasename(filename); 60 // path + base name 61 else if (FileSystem->existFile(path+FileSystem->getFileBasename(filename))) 62 filename = path+FileSystem->getFileBasename(filename); 63 // texpath + full name 64 else if (use_mat_dirs && FileSystem->existFile(matPath+filename)) 65 filename = matPath+filename; 66 // texpath + base name 67 else if (use_mat_dirs && FileSystem->existFile(matPath+FileSystem->getFileBasename(filename))) 68 filename = matPath+FileSystem->getFileBasename(filename); 69 // base name 70 else if (FileSystem->existFile(FileSystem->getFileBasename(filename))) 71 filename = FileSystem->getFileBasename(filename); 72} 73 74 75/**Creates/loads an animated mesh from the file. 76 \return Pointer to the created mesh. Returns 0 if loading failed. 77 If you no longer need the mesh, you should call IAnimatedMesh::drop(). 78 See IReferenceCounted::drop() for more information.*/ 79IAnimatedMesh* CDMFLoader::createMesh(io::IReadFile* file) 80{ 81 if (!file) 82 return 0; 83 video::IVideoDriver* driver = SceneMgr->getVideoDriver(); 84 85 //Load stringlist 86 StringList dmfRawFile; 87 LoadFromFile(file, dmfRawFile); 88 89 if (dmfRawFile.size()==0) 90 return 0; 91 92 SMesh * mesh = new SMesh(); 93 94 u32 i; 95 96 dmfHeader header; 97 98 //load header 99 core::array<dmfMaterial> materiali; 100 if (GetDMFHeader(dmfRawFile, header)) 101 { 102 //let's set ambient light 103 SceneMgr->setAmbientLight(header.dmfAmbient); 104 105 //let's create the correct number of materials, vertices and faces 106 dmfVert *verts=new dmfVert[header.numVertices]; 107 dmfFace *faces=new dmfFace[header.numFaces]; 108 109 //let's get the materials 110#ifdef _IRR_DMF_DEBUG_ 111 os::Printer::log("Loading materials", core::stringc(header.numMaterials).c_str()); 112#endif 113 GetDMFMaterials(dmfRawFile, materiali, header.numMaterials); 114 115 //let's get vertices and faces 116#ifdef _IRR_DMF_DEBUG_ 117 os::Printer::log("Loading geometry"); 118#endif 119 GetDMFVerticesFaces(dmfRawFile, verts, faces); 120 121 //create a meshbuffer for each material, then we'll remove empty ones 122#ifdef _IRR_DMF_DEBUG_ 123 os::Printer::log("Creating meshbuffers."); 124#endif 125 for (i=0; i<header.numMaterials; i++) 126 { 127 //create a new SMeshBufferLightMap for each material 128 SSkinMeshBuffer* buffer = new SSkinMeshBuffer(); 129 buffer->Material.MaterialType = video::EMT_LIGHTMAP_LIGHTING; 130 buffer->Material.Wireframe = false; 131 buffer->Material.Lighting = true; 132 mesh->addMeshBuffer(buffer); 133 buffer->drop(); 134 } 135 136 // Build the mesh buffers 137#ifdef _IRR_DMF_DEBUG_ 138 os::Printer::log("Adding geometry to mesh."); 139#endif 140 for (i = 0; i < header.numFaces; i++) 141 { 142#ifdef _IRR_DMF_DEBUG_ 143 os::Printer::log("Polygon with #vertices", core::stringc(faces[i].numVerts).c_str()); 144#endif 145 if (faces[i].numVerts < 3) 146 continue; 147 148 const core::vector3df normal = 149 core::triangle3df(verts[faces[i].firstVert].pos, 150 verts[faces[i].firstVert+1].pos, 151 verts[faces[i].firstVert+2].pos).getNormal().normalize(); 152 153 SSkinMeshBuffer* meshBuffer = (SSkinMeshBuffer*)mesh->getMeshBuffer( 154 faces[i].materialID); 155 156 const bool use2TCoords = meshBuffer->Vertices_2TCoords.size() || 157 materiali[faces[i].materialID].lightmapName.size(); 158 if (use2TCoords && meshBuffer->Vertices_Standard.size()) 159 meshBuffer->convertTo2TCoords(); 160 const u32 base = meshBuffer->Vertices_2TCoords.size()?meshBuffer->Vertices_2TCoords.size():meshBuffer->Vertices_Standard.size(); 161 162 // Add this face's verts 163 if (use2TCoords) 164 { 165 // make sure we have the proper type set 166 meshBuffer->VertexType=video::EVT_2TCOORDS; 167 for (u32 v = 0; v < faces[i].numVerts; v++) 168 { 169 const dmfVert& vv = verts[faces[i].firstVert + v]; 170 video::S3DVertex2TCoords vert(vv.pos, 171 normal, video::SColor(255,255,255,255), vv.tc, vv.lc); 172 if (materiali[faces[i].materialID].textureBlend==4 && 173 SceneMgr->getParameters()->getAttributeAsBool(DMF_FLIP_ALPHA_TEXTURES)) 174 { 175 vert.TCoords.set(vv.tc.X,-vv.tc.Y); 176 } 177 meshBuffer->Vertices_2TCoords.push_back(vert); 178 } 179 } 180 else 181 { 182 for (u32 v = 0; v < faces[i].numVerts; v++) 183 { 184 const dmfVert& vv = verts[faces[i].firstVert + v]; 185 video::S3DVertex vert(vv.pos, 186 normal, video::SColor(255,255,255,255), vv.tc); 187 if (materiali[faces[i].materialID].textureBlend==4 && 188 SceneMgr->getParameters()->getAttributeAsBool(DMF_FLIP_ALPHA_TEXTURES)) 189 { 190 vert.TCoords.set(vv.tc.X,-vv.tc.Y); 191 } 192 meshBuffer->Vertices_Standard.push_back(vert); 193 } 194 } 195 196 // Now add the indices 197 // This weird loop turns convex polygons into triangle strips. 198 // I do it this way instead of a simple fan because it usually 199 // looks a lot better in wireframe, for example. 200 u32 h = faces[i].numVerts - 1, l = 0, c; // High, Low, Center 201 for (u32 v = 0; v < faces[i].numVerts - 2; v++) 202 { 203 if (v & 1) // odd 204 c = h - 1; 205 else // even 206 c = l + 1; 207 208 meshBuffer->Indices.push_back(base + h); 209 meshBuffer->Indices.push_back(base + l); 210 meshBuffer->Indices.push_back(base + c); 211 212 if (v & 1) // odd 213 h--; 214 else // even 215 l++; 216 } 217 } 218 219 delete [] verts; 220 delete [] faces; 221 } 222 223 // delete all buffers without geometry in it. 224#ifdef _IRR_DMF_DEBUG_ 225 os::Printer::log("Cleaning meshbuffers."); 226#endif 227 i = 0; 228 while(i < mesh->MeshBuffers.size()) 229 { 230 if (mesh->MeshBuffers[i]->getVertexCount() == 0 || 231 mesh->MeshBuffers[i]->getIndexCount() == 0) 232 { 233 // Meshbuffer is empty -- drop it 234 mesh->MeshBuffers[i]->drop(); 235 mesh->MeshBuffers.erase(i); 236 materiali.erase(i); 237 } 238 else 239 { 240 i++; 241 } 242 } 243 244 245 { 246 //load textures and lightmaps in materials. 247 //don't worry if you receive a could not load texture, cause if you don't need 248 //a particular material in your scene it will be loaded and then destroyed. 249#ifdef _IRR_DMF_DEBUG_ 250 os::Printer::log("Loading textures."); 251#endif 252 const bool use_mat_dirs=!SceneMgr->getParameters()->getAttributeAsBool(DMF_IGNORE_MATERIALS_DIRS); 253 254 core::stringc path; 255 if ( SceneMgr->getParameters()->existsAttribute(DMF_TEXTURE_PATH) ) 256 path = SceneMgr->getParameters()->getAttributeAsString(DMF_TEXTURE_PATH); 257 else 258 path = FileSystem->getFileDir(file->getFileName()); 259 path += ('/'); 260 261 for (i=0; i<mesh->getMeshBufferCount(); i++) 262 { 263 //texture and lightmap 264 video::ITexture *tex = 0; 265 video::ITexture *lig = 0; 266 267 //current buffer to apply material 268 video::SMaterial& mat = mesh->getMeshBuffer(i)->getMaterial(); 269 270 //Primary texture is normal 271 if (materiali[i].textureFlag==0) 272 { 273 if (materiali[i].textureBlend==4) 274 driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT,true); 275 findFile(use_mat_dirs, path, materiali[i].pathName, materiali[i].textureName); 276 tex = driver->getTexture(materiali[i].textureName); 277 } 278 //Primary texture is just a colour 279 else if(materiali[i].textureFlag==1) 280 { 281 video::SColor color(axtoi(materiali[i].textureName.c_str())); 282 283 //just for compatibility with older Irrlicht versions 284 //to support transparent materials 285 if (color.getAlpha()!=255 && materiali[i].textureBlend==4) 286 driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT,true); 287 288 video::CImage *immagine= new video::CImage(video::ECF_A8R8G8B8, 289 core::dimension2d<u32>(8,8)); 290 immagine->fill(color); 291 tex = driver->addTexture("", immagine); 292 immagine->drop(); 293 294 //to support transparent materials 295 if(color.getAlpha()!=255 && materiali[i].textureBlend==4) 296 { 297 mat.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; 298 mat.MaterialTypeParam =(((f32) (color.getAlpha()-1))/255.0f); 299 } 300 } 301 302 //Lightmap is present 303 if (materiali[i].lightmapFlag == 0) 304 { 305 findFile(use_mat_dirs, path, materiali[i].pathName, materiali[i].lightmapName); 306 lig = driver->getTexture(materiali[i].lightmapName); 307 } 308 else //no lightmap 309 { 310 mat.MaterialType = video::EMT_SOLID; 311 const f32 mult = 100.0f - header.dmfShadow; 312 mat.AmbientColor=header.dmfAmbient.getInterpolated(video::SColor(255,0,0,0),mult/100.f); 313 } 314 315 if (materiali[i].textureBlend==4) 316 { 317 mat.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; 318 mat.MaterialTypeParam = 319 SceneMgr->getParameters()->getAttributeAsFloat(DMF_ALPHA_CHANNEL_REF); 320 } 321 322 //if texture is present mirror vertically owing to DeleD representation 323 if (tex && header.dmfVersion<1.1) 324 { 325 const core::dimension2d<u32> texsize = tex->getSize(); 326 void* pp = tex->lock(); 327 if (pp) 328 { 329 const video::ECOLOR_FORMAT format = tex->getColorFormat(); 330 if (format == video::ECF_A1R5G5B5) 331 { 332 s16* p = (s16*)pp; 333 s16 tmp=0; 334 for (u32 x=0; x<texsize.Width; x++) 335 for (u32 y=0; y<texsize.Height/2; y++) 336 { 337 tmp=p[y*texsize.Width + x]; 338 p[y*texsize.Width + x] = p[(texsize.Height-y-1)*texsize.Width + x]; 339 p[(texsize.Height-y-1)*texsize.Width + x]=tmp; 340 } 341 } 342 else 343 if (format == video::ECF_A8R8G8B8) 344 { 345 s32* p = (s32*)pp; 346 s32 tmp=0; 347 for (u32 x=0; x<texsize.Width; x++) 348 for (u32 y=0; y<texsize.Height/2; y++) 349 { 350 tmp=p[y*texsize.Width + x]; 351 p[y*texsize.Width + x] = p[(texsize.Height-y-1)*texsize.Width + x]; 352 p[(texsize.Height-y-1)*texsize.Width + x]=tmp; 353 } 354 } 355 } 356 tex->unlock(); 357 tex->regenerateMipMapLevels(); 358 } 359 360 //if lightmap is present mirror vertically owing to DeleD rapresentation 361 if (lig && header.dmfVersion<1.1) 362 { 363 const core::dimension2d<u32> ligsize=lig->getSize(); 364 void* pp = lig->lock(); 365 if (pp) 366 { 367 video::ECOLOR_FORMAT format = lig->getColorFormat(); 368 if (format == video::ECF_A1R5G5B5) 369 { 370 s16* p = (s16*)pp; 371 s16 tmp=0; 372 for (u32 x=0; x<ligsize.Width; x++) 373 { 374 for (u32 y=0; y<ligsize.Height/2; y++) 375 { 376 tmp=p[y*ligsize.Width + x]; 377 p[y*ligsize.Width + x] = p[(ligsize.Height-y-1)*ligsize.Width + x]; 378 p[(ligsize.Height-y-1)*ligsize.Width + x]=tmp; 379 } 380 } 381 } 382 else if (format == video::ECF_A8R8G8B8) 383 { 384 s32* p = (s32*)pp; 385 s32 tmp=0; 386 for (u32 x=0; x<ligsize.Width; x++) 387 { 388 for (u32 y=0; y<ligsize.Height/2; y++) 389 { 390 tmp=p[y*ligsize.Width + x]; 391 p[y*ligsize.Width + x] = p[(ligsize.Height-y-1)*ligsize.Width + x]; 392 p[(ligsize.Height-y-1)*ligsize.Width + x]=tmp; 393 } 394 } 395 } 396 } 397 lig->unlock(); 398 lig->regenerateMipMapLevels(); 399 } 400 401 mat.setTexture(0, tex); 402 mat.setTexture(1, lig); 403 } 404 } 405 406 // create bounding box 407 for (i = 0; i < mesh->MeshBuffers.size(); ++i) 408 { 409 mesh->MeshBuffers[i]->recalculateBoundingBox(); 410 } 411 mesh->recalculateBoundingBox(); 412 413 // Set up an animated mesh to hold the mesh 414 SAnimatedMesh* AMesh = new SAnimatedMesh(); 415 AMesh->Type = EAMT_UNKNOWN; 416 AMesh->addMesh(mesh); 417 AMesh->recalculateBoundingBox(); 418 mesh->drop(); 419 420 return AMesh; 421} 422 423 424/** \brief Tell us if this file is able to be loaded by this class 425 based on the file extension (e.g. ".bsp") 426 \return true if file is loadable.*/ 427bool CDMFLoader::isALoadableFileExtension(const io::path& filename) const 428{ 429 return core::hasFileExtension ( filename, "dmf" ); 430} 431 432 433} // end namespace scene 434} // end namespace irr 435 436#endif // _IRR_COMPILE_WITH_DMF_LOADER_ 437