1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
\r
2 // This file is part of the "Irrlicht Engine".
\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
\r
5 // This file was originally written by Salvatore Russo.
\r
6 // I (Nikolaus Gebhardt) did some minor modifications and changes to it and
\r
7 // integrated it into Irrlicht.
\r
8 // Thanks a lot to Salvatore for his work on this and that he gave me
\r
9 // his permission to add it into Irrlicht using the zlib license.
\r
11 CDMFLoader by Salvatore Russo (September 2005)
\r
13 See the header file for additional information including use and distribution rights.
\r
16 #include "IrrCompileConfig.h"
\r
17 #ifdef _IRR_COMPILE_WITH_DMF_LOADER_
\r
20 #define _IRR_DMF_DEBUG_
\r
24 #include "CDMFLoader.h"
\r
25 #include "CMeshTextureLoader.h"
\r
26 #include "ISceneManager.h"
\r
27 #include "IAttributes.h"
\r
28 #include "SAnimatedMesh.h"
\r
29 #include "SSkinMeshBuffer.h"
\r
30 #include "irrString.h"
\r
31 #include "irrMath.h"
\r
32 #include "dmfsupport.h"
\r
40 CDMFLoader::CDMFLoader(ISceneManager* smgr, io::IFileSystem* filesys)
\r
41 : SceneMgr(smgr), FileSystem(filesys)
\r
44 IReferenceCounted::setDebugName("CDMFLoader");
\r
47 TextureLoader = new CMeshTextureLoader( FileSystem, SceneMgr->getVideoDriver() );
\r
50 void CDMFLoader::addMaterialPath(core::stringc& filename, const core::stringc& matPath)
\r
52 c8 last = matPath.lastChar();
\r
53 if ( last == '/' || last == '\\' )
\r
54 filename = matPath+filename;
\r
57 core::stringc matPathSlash(matPath);
\r
58 matPathSlash.append('/');
\r
59 filename = matPathSlash+filename;
\r
64 /**Creates/loads an animated mesh from the file.
\r
65 \return Pointer to the created mesh. Returns 0 if loading failed.
\r
66 If you no longer need the mesh, you should call IAnimatedMesh::drop().
\r
67 See IReferenceCounted::drop() for more information.*/
\r
68 IAnimatedMesh* CDMFLoader::createMesh(io::IReadFile* file)
\r
73 if ( getMeshTextureLoader() )
\r
75 getMeshTextureLoader()->setMeshFile(file);
\r
77 if ( SceneMgr->getParameters()->existsAttribute(DMF_TEXTURE_PATH) )
\r
78 getMeshTextureLoader()->setTexturePath( SceneMgr->getParameters()->getAttributeAsString(DMF_TEXTURE_PATH) );
\r
81 video::IVideoDriver* driver = SceneMgr->getVideoDriver();
\r
84 StringList dmfRawFile;
\r
85 LoadFromFile(file, dmfRawFile);
\r
87 if (dmfRawFile.size()==0)
\r
90 SMesh * mesh = new SMesh();
\r
97 core::array<dmfMaterial> materiali;
\r
98 if (GetDMFHeader(dmfRawFile, header))
\r
100 //let's set ambient light
\r
101 SceneMgr->setAmbientLight(header.dmfAmbient);
\r
103 //let's create the correct number of materials, vertices and faces
\r
104 dmfVert *verts=new dmfVert[header.numVertices];
\r
105 dmfFace *faces=new dmfFace[header.numFaces];
\r
107 //let's get the materials
\r
108 #ifdef _IRR_DMF_DEBUG_
\r
109 os::Printer::log("Loading materials", core::stringc(header.numMaterials).c_str());
\r
111 GetDMFMaterials(dmfRawFile, materiali, header.numMaterials);
\r
113 //let's get vertices and faces
\r
114 #ifdef _IRR_DMF_DEBUG_
\r
115 os::Printer::log("Loading geometry");
\r
117 GetDMFVerticesFaces(dmfRawFile, verts, faces);
\r
119 //create a meshbuffer for each material, then we'll remove empty ones
\r
120 #ifdef _IRR_DMF_DEBUG_
\r
121 os::Printer::log("Creating meshbuffers.");
\r
123 for (i=0; i<header.numMaterials; i++)
\r
125 //create a new SMeshBufferLightMap for each material
\r
126 SSkinMeshBuffer* buffer = new SSkinMeshBuffer();
\r
127 buffer->Material.MaterialType = video::EMT_LIGHTMAP_LIGHTING;
\r
128 buffer->Material.Wireframe = false;
\r
129 buffer->Material.Lighting = true;
\r
130 mesh->addMeshBuffer(buffer);
\r
134 // Build the mesh buffers
\r
135 #ifdef _IRR_DMF_DEBUG_
\r
136 os::Printer::log("Adding geometry to mesh.");
\r
138 for (i = 0; i < header.numFaces; i++)
\r
140 #ifdef _IRR_DMF_DEBUG_
\r
141 os::Printer::log("Polygon with #vertices", core::stringc(faces[i].numVerts).c_str());
\r
143 if (faces[i].numVerts < 3)
\r
146 const core::vector3df normal =
\r
147 core::triangle3df(verts[faces[i].firstVert].pos,
\r
148 verts[faces[i].firstVert+1].pos,
\r
149 verts[faces[i].firstVert+2].pos).getNormal().normalize();
\r
151 SSkinMeshBuffer* meshBuffer = (SSkinMeshBuffer*)mesh->getMeshBuffer(
\r
152 faces[i].materialID);
\r
154 const bool use2TCoords = meshBuffer->Vertices_2TCoords.size() ||
\r
155 materiali[faces[i].materialID].lightmapName.size();
\r
156 if (use2TCoords && meshBuffer->Vertices_Standard.size())
\r
157 meshBuffer->convertTo2TCoords();
\r
158 const u32 base = meshBuffer->Vertices_2TCoords.size()?meshBuffer->Vertices_2TCoords.size():meshBuffer->Vertices_Standard.size();
\r
160 // Add this face's verts
\r
163 // make sure we have the proper type set
\r
164 meshBuffer->VertexType=video::EVT_2TCOORDS;
\r
165 for (u32 v = 0; v < faces[i].numVerts; v++)
\r
167 const dmfVert& vv = verts[faces[i].firstVert + v];
\r
168 video::S3DVertex2TCoords vert(vv.pos,
\r
169 normal, video::SColor(255,255,255,255), vv.tc, vv.lc);
\r
170 if (materiali[faces[i].materialID].textureBlend==4 &&
\r
171 SceneMgr->getParameters()->getAttributeAsBool(DMF_FLIP_ALPHA_TEXTURES))
\r
173 vert.TCoords.set(vv.tc.X,-vv.tc.Y);
\r
175 meshBuffer->Vertices_2TCoords.push_back(vert);
\r
180 for (u32 v = 0; v < faces[i].numVerts; v++)
\r
182 const dmfVert& vv = verts[faces[i].firstVert + v];
\r
183 video::S3DVertex vert(vv.pos,
\r
184 normal, video::SColor(255,255,255,255), vv.tc);
\r
185 if (materiali[faces[i].materialID].textureBlend==4 &&
\r
186 SceneMgr->getParameters()->getAttributeAsBool(DMF_FLIP_ALPHA_TEXTURES))
\r
188 vert.TCoords.set(vv.tc.X,-vv.tc.Y);
\r
190 meshBuffer->Vertices_Standard.push_back(vert);
\r
194 // Now add the indices
\r
195 // This weird loop turns convex polygons into triangle strips.
\r
196 // I do it this way instead of a simple fan because it usually
\r
197 // looks a lot better in wireframe, for example.
\r
198 u32 h = faces[i].numVerts - 1, l = 0, c; // High, Low, Center
\r
199 for (u32 v = 0; v < faces[i].numVerts - 2; v++)
\r
206 meshBuffer->Indices.push_back(base + h);
\r
207 meshBuffer->Indices.push_back(base + l);
\r
208 meshBuffer->Indices.push_back(base + c);
\r
221 // delete all buffers without geometry in it.
\r
222 #ifdef _IRR_DMF_DEBUG_
\r
223 os::Printer::log("Cleaning meshbuffers.");
\r
226 while(i < mesh->MeshBuffers.size())
\r
228 if (mesh->MeshBuffers[i]->getVertexCount() == 0 ||
\r
229 mesh->MeshBuffers[i]->getIndexCount() == 0)
\r
231 // Meshbuffer is empty -- drop it
\r
232 mesh->MeshBuffers[i]->drop();
\r
233 mesh->MeshBuffers.erase(i);
\r
234 materiali.erase(i);
\r
244 //load textures and lightmaps in materials.
\r
245 //don't worry if you receive a could not load texture, cause if you don't need
\r
246 //a particular material in your scene it will be loaded and then destroyed.
\r
247 #ifdef _IRR_DMF_DEBUG_
\r
248 os::Printer::log("Loading textures.");
\r
250 const bool use_mat_dirs=!SceneMgr->getParameters()->getAttributeAsBool(DMF_IGNORE_MATERIALS_DIRS);
\r
252 for (i=0; i<mesh->getMeshBufferCount(); i++)
\r
254 //texture and lightmap
\r
255 video::ITexture *tex = 0;
\r
256 video::ITexture *lig = 0;
\r
258 //current buffer to apply material
\r
259 video::SMaterial& mat = mesh->getMeshBuffer(i)->getMaterial();
\r
261 //Primary texture is normal
\r
262 if (materiali[i].textureFlag==0)
\r
264 if (materiali[i].textureBlend==4)
\r
265 driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT,true);
\r
267 if ( use_mat_dirs )
\r
268 addMaterialPath(materiali[i].textureName, materiali[i].pathName);
\r
269 tex = getMeshTextureLoader() ? getMeshTextureLoader()->getTexture( materiali[i].textureName ) : NULL;
\r
271 //Primary texture is just a color
\r
272 else if(materiali[i].textureFlag==1)
\r
274 video::SColor color(axtoi(materiali[i].textureName.c_str()));
\r
276 //just for compatibility with older Irrlicht versions
\r
277 //to support transparent materials
\r
278 if (color.getAlpha()!=255 && materiali[i].textureBlend==4)
\r
279 driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT,true);
\r
281 video::IImage *immagine= driver->createImage(video::ECF_A8R8G8B8,
\r
282 core::dimension2d<u32>(8,8));
\r
283 immagine->fill(color);
\r
284 tex = driver->addTexture("", immagine);
\r
287 //to support transparent materials
\r
288 if (color.getAlpha()!=255 && materiali[i].textureBlend==4)
\r
290 mat.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
\r
291 mat.MaterialTypeParam =(((f32) (color.getAlpha()-1))/255.0f);
\r
295 //Lightmap is present
\r
296 if (materiali[i].lightmapFlag == 0)
\r
298 if ( use_mat_dirs )
\r
299 addMaterialPath(materiali[i].lightmapName, materiali[i].pathName);
\r
300 lig = getMeshTextureLoader() ? getMeshTextureLoader()->getTexture(materiali[i].lightmapName) : NULL;
\r
304 mat.MaterialType = video::EMT_SOLID;
\r
305 const f32 mult = 100.0f - header.dmfShadow;
\r
306 mat.AmbientColor=header.dmfAmbient.getInterpolated(video::SColor(255,0,0,0),mult/100.f);
\r
309 if (materiali[i].textureBlend==4)
\r
311 mat.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
\r
312 mat.MaterialTypeParam =
\r
313 SceneMgr->getParameters()->getAttributeAsFloat(DMF_ALPHA_CHANNEL_REF);
\r
316 //if texture is present mirror vertically owing to DeleD representation
\r
317 if (tex && header.dmfVersion<1.1)
\r
319 const core::dimension2d<u32> texsize = tex->getSize();
\r
320 void* pp = tex->lock();
\r
323 const video::ECOLOR_FORMAT format = tex->getColorFormat();
\r
324 if (format == video::ECF_A1R5G5B5)
\r
328 for (u32 x=0; x<texsize.Width; x++)
\r
329 for (u32 y=0; y<texsize.Height/2; y++)
\r
331 tmp=p[y*texsize.Width + x];
\r
332 p[y*texsize.Width + x] = p[(texsize.Height-y-1)*texsize.Width + x];
\r
333 p[(texsize.Height-y-1)*texsize.Width + x]=tmp;
\r
337 if (format == video::ECF_A8R8G8B8)
\r
341 for (u32 x=0; x<texsize.Width; x++)
\r
342 for (u32 y=0; y<texsize.Height/2; y++)
\r
344 tmp=p[y*texsize.Width + x];
\r
345 p[y*texsize.Width + x] = p[(texsize.Height-y-1)*texsize.Width + x];
\r
346 p[(texsize.Height-y-1)*texsize.Width + x]=tmp;
\r
351 tex->regenerateMipMapLevels();
\r
354 //if lightmap is present mirror vertically owing to DeleD rapresentation
\r
355 if (lig && header.dmfVersion<1.1)
\r
357 const core::dimension2d<u32> ligsize=lig->getSize();
\r
358 void* pp = lig->lock();
\r
361 video::ECOLOR_FORMAT format = lig->getColorFormat();
\r
362 if (format == video::ECF_A1R5G5B5)
\r
366 for (u32 x=0; x<ligsize.Width; x++)
\r
368 for (u32 y=0; y<ligsize.Height/2; y++)
\r
370 tmp=p[y*ligsize.Width + x];
\r
371 p[y*ligsize.Width + x] = p[(ligsize.Height-y-1)*ligsize.Width + x];
\r
372 p[(ligsize.Height-y-1)*ligsize.Width + x]=tmp;
\r
376 else if (format == video::ECF_A8R8G8B8)
\r
380 for (u32 x=0; x<ligsize.Width; x++)
\r
382 for (u32 y=0; y<ligsize.Height/2; y++)
\r
384 tmp=p[y*ligsize.Width + x];
\r
385 p[y*ligsize.Width + x] = p[(ligsize.Height-y-1)*ligsize.Width + x];
\r
386 p[(ligsize.Height-y-1)*ligsize.Width + x]=tmp;
\r
392 lig->regenerateMipMapLevels();
\r
395 mat.setTexture(0, tex);
\r
396 mat.setTexture(1, lig);
\r
400 // create bounding box
\r
401 for (i = 0; i < mesh->MeshBuffers.size(); ++i)
\r
403 mesh->MeshBuffers[i]->recalculateBoundingBox();
\r
405 mesh->recalculateBoundingBox();
\r
407 // Set up an animated mesh to hold the mesh
\r
408 SAnimatedMesh* AMesh = new SAnimatedMesh();
\r
409 AMesh->Type = EAMT_UNKNOWN;
\r
410 AMesh->addMesh(mesh);
\r
411 AMesh->recalculateBoundingBox();
\r
418 /** \brief Tell us if this file is able to be loaded by this class
\r
419 based on the file extension (e.g. ".bsp")
\r
420 \return true if file is loadable.*/
\r
421 bool CDMFLoader::isALoadableFileExtension(const io::path& filename) const
\r
423 return core::hasFileExtension ( filename, "dmf" );
\r
427 } // end namespace scene
\r
428 } // end namespace irr
\r
430 #endif // _IRR_COMPILE_WITH_DMF_LOADER_
\r