// Copyright (C) 2002-2012 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 "CMeshTextureLoader.h" #include "ISceneManager.h" #include "IAttributes.h" #include "SAnimatedMesh.h" #include "SSkinMeshBuffer.h" #include "irrString.h" #include "irrMath.h" #include "dmfsupport.h" namespace irr { namespace scene { /** Constructor*/ CDMFLoader::CDMFLoader(ISceneManager* smgr, io::IFileSystem* filesys) : SceneMgr(smgr), FileSystem(filesys) { #ifdef _DEBUG IReferenceCounted::setDebugName("CDMFLoader"); #endif TextureLoader = new CMeshTextureLoader( FileSystem, SceneMgr->getVideoDriver() ); } void CDMFLoader::addMaterialPath(core::stringc& filename, const core::stringc& matPath) { c8 last = matPath.lastChar(); if ( last == '/' || last == '\\' ) filename = matPath+filename; else { core::stringc matPathSlash(matPath); matPathSlash.append('/'); filename = matPathSlash+filename; } } /**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; if ( getMeshTextureLoader() ) { getMeshTextureLoader()->setMeshFile(file); if ( SceneMgr->getParameters()->existsAttribute(DMF_TEXTURE_PATH) ) getMeshTextureLoader()->setTexturePath( SceneMgr->getParameters()->getAttributeAsString(DMF_TEXTURE_PATH) ); } video::IVideoDriver* driver = SceneMgr->getVideoDriver(); //Load stringlist StringList dmfRawFile; LoadFromFile(file, dmfRawFile); if (dmfRawFile.size()==0) return 0; SMesh * mesh = new SMesh(); u32 i; dmfHeader header; //load header core::array materiali; if (GetDMFHeader(dmfRawFile, header)) { //let's set ambient light SceneMgr->setAmbientLight(header.dmfAmbient); //let's create the correct number of materials, vertices and faces dmfVert *verts=new dmfVert[header.numVertices]; dmfFace *faces=new dmfFace[header.numFaces]; //let's get the materials #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Loading materials", core::stringc(header.numMaterials).c_str()); #endif GetDMFMaterials(dmfRawFile, materiali, header.numMaterials); //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(); SSkinMeshBuffer* meshBuffer = (SSkinMeshBuffer*)mesh->getMeshBuffer( faces[i].materialID); const bool use2TCoords = meshBuffer->Vertices_2TCoords.size() || materiali[faces[i].materialID].lightmapName.size(); if (use2TCoords && meshBuffer->Vertices_Standard.size()) meshBuffer->convertTo2TCoords(); const u32 base = meshBuffer->Vertices_2TCoords.size()?meshBuffer->Vertices_2TCoords.size():meshBuffer->Vertices_Standard.size(); // Add this face's verts if (use2TCoords) { // make sure we have the proper type set meshBuffer->VertexType=video::EVT_2TCOORDS; for (u32 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_2TCoords.push_back(vert); } } else { for (u32 v = 0; v < faces[i].numVerts; v++) { const dmfVert& vv = verts[faces[i].firstVert + v]; video::S3DVertex vert(vv.pos, normal, video::SColor(255,255,255,255), vv.tc); 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_Standard.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 (u32 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++; } } 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); materiali.erase(i); } else { i++; } } { //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 const bool use_mat_dirs=!SceneMgr->getParameters()->getAttributeAsBool(DMF_IGNORE_MATERIALS_DIRS); for (i=0; igetMeshBufferCount(); i++) { //texture and lightmap video::ITexture *tex = 0; video::ITexture *lig = 0; //current buffer to apply material video::SMaterial& mat = mesh->getMeshBuffer(i)->getMaterial(); //Primary texture is normal if (materiali[i].textureFlag==0) { if (materiali[i].textureBlend==4) driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT,true); if ( use_mat_dirs ) addMaterialPath(materiali[i].textureName, materiali[i].pathName); tex = getMeshTextureLoader() ? getMeshTextureLoader()->getTexture( materiali[i].textureName ) : NULL; } //Primary texture is just a color else if(materiali[i].textureFlag==1) { video::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(video::ETCF_ALWAYS_32_BIT,true); video::IImage *immagine= driver->createImage(video::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) { mat.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; mat.MaterialTypeParam =(((f32) (color.getAlpha()-1))/255.0f); } } //Lightmap is present if (materiali[i].lightmapFlag == 0) { if ( use_mat_dirs ) addMaterialPath(materiali[i].lightmapName, materiali[i].pathName); lig = getMeshTextureLoader() ? getMeshTextureLoader()->getTexture(materiali[i].lightmapName) : NULL; } else //no lightmap { mat.MaterialType = video::EMT_SOLID; const f32 mult = 100.0f - header.dmfShadow; mat.AmbientColor=header.dmfAmbient.getInterpolated(video::SColor(255,0,0,0),mult/100.f); } if (materiali[i].textureBlend==4) { mat.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; mat.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(); } mat.setTexture(0, tex); mat.setTexture(1, lig); } } // 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 io::path& filename) const { return core::hasFileExtension ( filename, "dmf" ); } } // end namespace scene } // end namespace irr #endif // _IRR_COMPILE_WITH_DMF_LOADER_