// Copyright (C) 2002-2009 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h // // This file was originally written by Salvatore Russo. // I (Nikolaus Gebhardt) did some minor modifications and changes to it and // integrated it into Irrlicht. // Thanks a lot to Salvatore for his work on this and that he gave me // his permission to add it into Irrlicht using the zlib license. /* CDMFLoader by Salvatore Russo (September 2005) See the header file for additional information including use and distribution rights. */ #include "IrrCompileConfig.h" #ifdef _IRR_COMPILE_WITH_DMF_LOADER_ #ifdef _DEBUG #define _IRR_DMF_DEBUG_ #include "os.h" #endif #include "CDMFLoader.h" #include "ISceneManager.h" #include "IAttributes.h" #include "SAnimatedMesh.h" #include "SMeshBufferLightMap.h" #include "irrString.h" #include "irrMath.h" #include "dmfsupport.h" #include "CImage.h" namespace irr { namespace scene { /** Constructor*/ CDMFLoader::CDMFLoader(ISceneManager* smgr, io::IFileSystem* filesys) : SceneMgr(smgr), FileSystem(filesys) { #ifdef _DEBUG IReferenceCounted::setDebugName("CDMFLoader"); #endif } /**Creates/loads an animated mesh from the file. \return Pointer to the created mesh. Returns 0 if loading failed. If you no longer need the mesh, you should call IAnimatedMesh::drop(). See IReferenceCounted::drop() for more information.*/ IAnimatedMesh* CDMFLoader::createMesh(io::IReadFile* file) { if (!file) return 0; video::IVideoDriver* driver = SceneMgr->getVideoDriver(); //Load stringlist StringList dmfRawFile(file); if (dmfRawFile.size()==0) return 0; SMesh * mesh = new SMesh(); u32 i; dmfHeader header; //load header if (GetDMFHeader(dmfRawFile, header)) { //let's set ambient light SceneMgr->setAmbientLight(header.dmfAmbient); //let's create the correct number of materials, vertices and faces core::array materiali; dmfVert *verts=new dmfVert[header.numVertices]; dmfFace *faces=new dmfFace[header.numFaces]; //let's get the materials const bool use_mat_dirs=SceneMgr->getParameters()->getAttributeAsBool(DMF_USE_MATERIALS_DIRS); #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Loading materials", core::stringc(header.numMaterials).c_str()); #endif GetDMFMaterials(dmfRawFile, materiali, header.numMaterials, use_mat_dirs); //let's get vertices and faces #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Loading geometry"); #endif GetDMFVerticesFaces(dmfRawFile, verts, faces); //create a meshbuffer for each material, then we'll remove empty ones #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Creating meshbuffers."); #endif for (i=0; iMaterial.MaterialType = video::EMT_LIGHTMAP_LIGHTING; buffer->Material.Wireframe = false; buffer->Material.Lighting = true; mesh->addMeshBuffer(buffer); buffer->drop(); } // Build the mesh buffers #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Adding geometry to mesh."); #endif for (i = 0; i < header.numFaces; i++) { #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Polygon with #vertices", core::stringc(faces[i].numVerts).c_str()); #endif if (faces[i].numVerts < 3) continue; const core::vector3df normal = core::triangle3df(verts[faces[i].firstVert].pos, verts[faces[i].firstVert+1].pos, verts[faces[i].firstVert+2].pos).getNormal().normalize(); SMeshBufferLightMap * meshBuffer = (SMeshBufferLightMap*)mesh->getMeshBuffer( faces[i].materialID); const u32 base = meshBuffer->Vertices.size(); // Add this face's verts u32 v; for (v = 0; v < faces[i].numVerts; v++) { const dmfVert& vv = verts[faces[i].firstVert + v]; video::S3DVertex2TCoords vert(vv.pos, normal, video::SColor(255,255,255,255), vv.tc, vv.lc); if (materiali[faces[i].materialID].textureBlend==4 && SceneMgr->getParameters()->getAttributeAsBool(DMF_FLIP_ALPHA_TEXTURES)) { vert.TCoords.set(vv.tc.X,-vv.tc.Y); } meshBuffer->Vertices.push_back(vert); } // Now add the indices // This weird loop turns convex polygons into triangle strips. // I do it this way instead of a simple fan because it usually // looks a lot better in wireframe, for example. u32 h = faces[i].numVerts - 1, l = 0, c; // High, Low, Center for (v = 0; v < faces[i].numVerts - 2; v++) { if (v & 1) // odd c = h - 1; else // even c = l + 1; meshBuffer->Indices.push_back(base + h); meshBuffer->Indices.push_back(base + l); meshBuffer->Indices.push_back(base + c); if (v & 1) // odd h--; else // even l++; } } //load textures and lightmaps in materials. //don't worry if you receive a could not load texture, cause if you don't need //a particular material in your scene it will be loaded and then destroyed. #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Loading textures."); #endif for (i=0; igetParameters()->existsAttribute(DMF_TEXTURE_PATH) ) path = SceneMgr->getParameters()->getAttributeAsString(DMF_TEXTURE_PATH); else path = FileSystem->getFileDir(file->getFileName()); path += ('/'); //texture and lightmap ITexture *tex = 0; ITexture *lig = 0; //current buffer to apply material SMeshBufferLightMap* buffer = (SMeshBufferLightMap*)mesh->getMeshBuffer(i); //Primary texture is normal if (materiali[i].textureFlag==0) { if (materiali[i].textureBlend==4) driver->setTextureCreationFlag(ETCF_ALWAYS_32_BIT,true); if (FileSystem->existFile(path+materiali[i].textureName)) tex = driver->getTexture((path+materiali[i].textureName).c_str()); else if (FileSystem->existFile(path+FileSystem->getFileBasename(materiali[i].textureName))) tex = driver->getTexture((path+FileSystem->getFileBasename(materiali[i].textureName)).c_str()); else if (FileSystem->existFile(materiali[i].textureName)) tex = driver->getTexture(materiali[i].textureName.c_str()); else if (FileSystem->existFile(FileSystem->getFileBasename(materiali[i].textureName))) tex = driver->getTexture(FileSystem->getFileBasename(materiali[i].textureName).c_str()); #ifdef _IRR_DMF_DEBUG_ else os::Printer::log("Could not load texture", materiali[i].textureName.c_str()); #endif // _IRR_DMF_DEBUG_ } //Primary texture is just a colour else if(materiali[i].textureFlag==1) { SColor color(axtoi(materiali[i].textureName.c_str())); //just for compatibility with older Irrlicht versions //to support transparent materials if (color.getAlpha()!=255 && materiali[i].textureBlend==4) driver->setTextureCreationFlag(ETCF_ALWAYS_32_BIT,true); CImage *immagine= new CImage(ECF_A8R8G8B8, core::dimension2d(8,8)); immagine->fill(color); tex = driver->addTexture("", immagine); immagine->drop(); //to support transparent materials if(color.getAlpha()!=255 && materiali[i].textureBlend==4) { buffer->Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; buffer->Material.MaterialTypeParam =(((f32) (color.getAlpha()-1))/255.0f); } } //Lightmap is present if (materiali[i].lightmapFlag == 0) lig = driver->getTexture((path+materiali[i].lightmapName).c_str()); else //no lightmap { buffer->Material.MaterialType = video::EMT_SOLID; const f32 mult = 100.0f - header.dmfShadow; buffer->Material.AmbientColor=header.dmfAmbient.getInterpolated(SColor(255,0,0,0),mult/100.f); } if (materiali[i].textureBlend==4) { buffer->Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; buffer->Material.MaterialTypeParam = SceneMgr->getParameters()->getAttributeAsFloat(DMF_ALPHA_CHANNEL_REF); } //if texture is present mirror vertically owing to DeleD representation if (tex && header.dmfVersion<1.1) { const core::dimension2d texsize = tex->getSize(); void* pp = tex->lock(); if (pp) { const video::ECOLOR_FORMAT format = tex->getColorFormat(); if (format == video::ECF_A1R5G5B5) { s16* p = (s16*)pp; s16 tmp=0; for (u32 x=0; xunlock(); tex->regenerateMipMapLevels(); } //if lightmap is present mirror vertically owing to DeleD rapresentation if (lig && header.dmfVersion<1.1) { const core::dimension2d ligsize=lig->getSize(); void* pp = lig->lock(); if (pp) { video::ECOLOR_FORMAT format = lig->getColorFormat(); if (format == video::ECF_A1R5G5B5) { s16* p = (s16*)pp; s16 tmp=0; for (u32 x=0; xunlock(); lig->regenerateMipMapLevels(); } buffer->Material.setTexture(0, tex); buffer->Material.setTexture(1, lig); } delete verts; delete faces; } // delete all buffers without geometry in it. #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Cleaning meshbuffers."); #endif i = 0; while(i < mesh->MeshBuffers.size()) { if (mesh->MeshBuffers[i]->getVertexCount() == 0 || mesh->MeshBuffers[i]->getIndexCount() == 0) { // Meshbuffer is empty -- drop it mesh->MeshBuffers[i]->drop(); mesh->MeshBuffers.erase(i); } else { i++; } } // create bounding box for (i = 0; i < mesh->MeshBuffers.size(); ++i) { mesh->MeshBuffers[i]->recalculateBoundingBox(); } mesh->recalculateBoundingBox(); // Set up an animated mesh to hold the mesh SAnimatedMesh* AMesh = new SAnimatedMesh(); AMesh->Type = EAMT_UNKNOWN; AMesh->addMesh(mesh); AMesh->recalculateBoundingBox(); mesh->drop(); return AMesh; } /** \brief Tell us if this file is able to be loaded by this class based on the file extension (e.g. ".bsp") \return true if file is loadable.*/ bool CDMFLoader::isALoadableFileExtension(const c8* filename) const { return strstr(filename, ".dmf") != 0; } } // end namespace scene } // end namespace irr #endif // _IRR_COMPILE_WITH_DMF_LOADER_