1 //-----------------------------------------------------------------------------
2 // This is free and unencumbered software released into the public domain.
3 // For the full text of the Unlicense, see the file "docs/unlicense.html".
4 // Additional Unlicense information may be found at http://unlicense.org.
5 //-----------------------------------------------------------------------------
6 #include "CIrrBMeshWriter.h"
7 #include "IWriteFile.h"
8 #include "IXMLWriter.h"
9 #include "IMesh.h"
10 #include "IAttributes.h"
11 
12 namespace irr
13 {
14 	namespace scene
15 	{
16 		//! to be included EMESH_WRITER_TYPE enum
17 		u32 EMWT_IRRB_MESH     = MAKE_IRR_ID('i','r','r','b');
18 
get_endianess(void)19 		int get_endianess(void)  // 1-big, 0-lil
20 		{
21 			union {
22 				int i;
23 				char c[sizeof(int)];
24 			} t;
25 			t.i = 1;
26 			return t.c[0] == 0;
27 		}
28 
CIrrBMeshWriter(video::IVideoDriver * driver,io::IFileSystem * fs,core::array<SCustomMaterial> * customMaterials)29 		CIrrBMeshWriter::CIrrBMeshWriter(video::IVideoDriver* driver,
30 			io::IFileSystem* fs, core::array<SCustomMaterial>* customMaterials)
31 			: FileSystem(fs), VideoDriver(driver), CustomMaterials(customMaterials),
32 			Writer(0), Version(IRRB_VERSION),
33 			VMajor(IRRLICHT_VERSION_MAJOR), VMinor(IRRLICHT_VERSION_MINOR), Creator("unknown"),
34 			RelativeBase("")
35 		{
36 			if (VideoDriver)
37 				VideoDriver->grab();
38 
39 			if (FileSystem)
40 				FileSystem->grab();
41 		}
42 
~CIrrBMeshWriter()43 		CIrrBMeshWriter::~CIrrBMeshWriter()
44 		{
45 			if (VideoDriver)
46 				VideoDriver->drop();
47 
48 			if (FileSystem)
49 				FileSystem->drop();
50 		}
51 
52 		//! Returns the type of the mesh writer
getType() const53 		EMESH_WRITER_TYPE CIrrBMeshWriter::getType() const
54 		{
55 			return (irr::scene::EMESH_WRITER_TYPE)EMWT_IRRB_MESH;
56 		}
57 
58 		//! writes a mesh
writeMesh(io::IWriteFile * file,scene::IMesh * mesh,s32 flags)59 		bool CIrrBMeshWriter::writeMesh(io::IWriteFile* file, scene::IMesh* mesh, s32 flags)
60 		{
61 			bool rc = false;
62 
63 			if (!file)
64 				return false;
65 
66 			Writer = file;
67 
68 			if (!Writer)
69 			{
70 				//os::Printer::log("Could not write file", file->getFileName());
71 				return false;
72 			}
73 
74 
75 			//os::Printer::log("Writing mesh", file->getFileName());
76 
77 			// write IRRB MESH header
78 
79 			writeHeader(mesh);
80 
81 			// write mesh
82 			// todo: check the mesh type, cast it,
83 			// and add appropriate animation extensions...
84 
85 			rc = _writeMesh(mesh);
86 
87 			Writer->drop();
88 			return rc;
89 		}
90 
_writeStringChunk(irr::core::stringc value)91 		void CIrrBMeshWriter::_writeStringChunk(irr::core::stringc value)
92 		{
93 			struct IrrbChunkInfo ci;
94 			ci.iId = CID_STRING;
95 			ci.iSize = value.size() + 1;
96 			Writer->write(&ci,sizeof(ci));
97 			Writer->write(value.c_str(),ci.iSize);
98 		}
99 
_writeChunkInfo(u32 id,u32 size)100 		u32 CIrrBMeshWriter::_writeChunkInfo(u32 id, u32 size)
101 		{
102 			u32 offset;
103 			struct IrrbChunkInfo ci;
104 
105 			offset = Writer->getPos();
106 
107 			ci.iId = id;
108 			ci.iSize = size;
109 			Writer->write(&ci,sizeof(ci));
110 
111 			return offset;
112 		}
113 
_updateChunkSize(u32 id,u32 offset)114 		void CIrrBMeshWriter::_updateChunkSize(u32 id, u32 offset)
115 		{
116 			struct IrrbChunkInfo ci;
117 			u32 cpos=Writer->getPos();
118 
119 			ci.iId = id;
120 			ci.iSize = cpos - offset + sizeof(ci);
121 			Writer->seek(offset);
122 			Writer->write(&ci,sizeof(ci));
123 			Writer->seek(cpos);
124 		}
125 
addMaterial(irr::video::SMaterial & material)126 		bool CIrrBMeshWriter::addMaterial(irr::video::SMaterial& material)
127 		{
128 			irr::video::SMaterial amat;
129 
130 			for(u32 i=0; i<Materials.size(); i++)
131 			{
132 				if(material == Materials[i])
133 					return false;
134 			}
135 			Materials.push_back(material);
136 			return true;
137 		}
138 
getMaterialIndex(irr::video::SMaterial & material)139 		u32 CIrrBMeshWriter::getMaterialIndex(irr::video::SMaterial& material)
140 		{
141 			irr::video::SMaterial amat;
142 
143 			for(u32 i=0; i<Materials.size(); i++)
144 			{
145 				if(material == Materials[i])
146 					return i;
147 			}
148 			return 0;
149 		}
150 
getMaterialName(irr::video::SMaterial & material)151 		irr::core::stringc CIrrBMeshWriter::getMaterialName(irr::video::SMaterial& material)
152 		{
153 			irr::core::stringc result="solid";
154 
155 			if((u32)material.MaterialType > sizeof(irr::video::sBuiltInMaterialTypeNames))
156 			{
157 				result = irr::video::sBuiltInMaterialTypeNames[material.MaterialType];
158 			}
159 			else
160 			{
161 				for(u32 i=0; i < CustomMaterials->size(); i++)
162 				{
163 					if((*CustomMaterials)[i].Type == material.MaterialType)
164 					{
165 						result = (*CustomMaterials)[i].Name;
166 						break;
167 					}
168 				}
169 			}
170 
171 			return result;
172 		}
173 
_writeMesh(const scene::IMesh * mesh)174 		bool CIrrBMeshWriter::_writeMesh(const scene::IMesh* mesh)
175 		{
176 			bool rc = false;
177 			u32  offset;
178 			struct IrrbMeshInfo mi;
179 			u32  vcount=0;
180 			u32  icount=0;
181 			u32  mcount=0;
182 			u32* voffsets;
183 			u32* ioffsets;
184 			u32  bcount=0;
185 
186 
187 			offset = _writeChunkInfo(CID_MESH,0);
188 
189 			bcount = mesh->getMeshBufferCount();
190 
191 			voffsets = (u32*) malloc(bcount * sizeof(u32));
192 			ioffsets = (u32*) malloc(bcount * sizeof(u32));
193 
194 			//
195 			// count vertices, indices, & materials across all mesh buffers
196 			//
197 			core::aabbox3d<f32> mbb;
198 			mbb.reset(0.f,0.f,0.f);
199 			for (int i=0; i<(int)mesh->getMeshBufferCount(); ++i)
200 			{
201 				scene::IMeshBuffer* buffer = mesh->getMeshBuffer(i);
202 				buffer->recalculateBoundingBox();
203 				mbb.addInternalBox(buffer->getBoundingBox());
204 				voffsets[i] = vcount;
205 				ioffsets[i] = icount;
206 				vcount += buffer->getVertexCount();
207 				icount += buffer->getIndexCount();
208 
209 				irr::video::SMaterial& material = buffer->getMaterial();
210 				if(addMaterial(material))
211 					++mcount;
212 			}
213 
214 			//
215 			// write mesh info struct
216 			//
217 			mi.iMeshBufferCount = mesh->getMeshBufferCount();
218 			mi.iVertexCount = vcount;
219 			mi.iIndexCount = icount;
220 			mi.iMaterialCount = mcount;
221 			mi.ibbMin.x = mbb.MinEdge.X;
222 			mi.ibbMin.y = mbb.MinEdge.Y;
223 			mi.ibbMin.z = mbb.MinEdge.Z;
224 			mi.ibbMax.x = mbb.MaxEdge.X;
225 			mi.ibbMax.y = mbb.MaxEdge.Y;
226 			mi.ibbMax.z = mbb.MaxEdge.Z;
227 
228 			Writer->write(&mi,sizeof(mi));
229 
230 			//
231 			// build and write vertex & index buffers
232 			//
233 			u32 vbufsize,ibufsize;
234 			vbufsize = sizeof(struct IrrbVertex) * vcount;
235 			ibufsize = sizeof(u32) * icount;
236 
237 			VBuffer = (struct IrrbVertex*)malloc(vbufsize);
238 			IBuffer = (u32 *)malloc(ibufsize);
239 
240 			updateBuffers(mesh, VBuffer, IBuffer);
241 			Writer->write(VBuffer,vbufsize);
242 			Writer->write(IBuffer,ibufsize);
243 
244 			//
245 			// write materials
246 			//
247 			for(u32 i=0; i<Materials.size(); i++)
248 			{
249 				struct IrrbMaterial iMat;
250 
251 				struct IrrbMaterialLayer iLayer;
252 
253 				updateMaterial(Materials[i],iMat);
254 				u32 tCount=0;
255 
256 				for(tCount=0;tCount < 4; tCount++)
257 				{
258 					irr::video::ITexture* texture = Materials[i].getTexture(tCount);
259 					if(!texture)
260 						break;
261 				}
262 				iMat.mLayerCount = tCount;
263 				Writer->write(&iMat,sizeof(iMat));
264 
265 				for(u8 t=0; t<tCount; t++)
266 				{
267 					irr::core::stringc textureName;
268 					//if(VMajor == 1)
269 					{
270 						updateMaterialLayer(Materials[i],t,textureName,iLayer);
271 						_writeStringChunk(textureName);
272 						Writer->write(&iLayer,sizeof(iLayer));
273 					}
274 				}
275 			}
276 
277 			//
278 			// write mesh buffers info
279 			//
280 			_writeChunkInfo(CID_MESHBUF,0);
281 			for (int i=0; i<(int)mesh->getMeshBufferCount(); ++i)
282 			{
283 				scene::IMeshBuffer* buffer = mesh->getMeshBuffer(i);
284 				if (buffer)
285 				{
286 
287 					struct IrrbMeshBufInfo mbi;
288 
289 					// write meshbuffer info
290 
291 					memset(&mbi, 0, sizeof(mbi));
292 					mbi.iVertexType = buffer->getVertexType();
293 					mbi.iVertCount = buffer->getVertexCount();
294 					mbi.iVertStart = voffsets[i];
295 					mbi.iIndexCount = buffer->getIndexCount();
296 					mbi.iIndexStart = ioffsets[i];
297 					mbi.iFaceCount = buffer->getIndexCount() / 3;
298 					mbi.iMaterialIndex = getMaterialIndex(buffer->getMaterial());
299 					core::stringc mname = getMaterialName(buffer->getMaterial());
300 					strncpy(mbi.iMaterialName, mname.c_str(), sizeof(mbi.iMaterialName)-1);
301 
302 					//video::IMaterialRenderer* mr = VideoDriver->getMaterialRenderer(mbi.iMaterialIndex);
303 
304 					buffer->recalculateBoundingBox();
305 					mbb = buffer->getBoundingBox();
306 					mbi.ibbMin.x = mbb.MinEdge.X;
307 					mbi.ibbMin.y = mbb.MinEdge.Y;
308 					mbi.ibbMin.z = mbb.MinEdge.Z;
309 					mbi.ibbMax.x = mbb.MaxEdge.X;
310 					mbi.ibbMax.y = mbb.MaxEdge.Y;
311 					mbi.ibbMax.z = mbb.MaxEdge.Z;
312 
313 					Writer->write(&mbi,sizeof(mbi));
314 				}
315 			}
316 			_updateChunkSize(CID_MESHBUF,offset);
317 
318 			_updateChunkSize(CID_MESH,offset);
319 
320 			free(voffsets);
321 			free(VBuffer);
322 			free(ioffsets);
323 			free(IBuffer);
324 
325 			return rc;
326 		}
327 
328 
writeHeader(const scene::IMesh * mesh)329 		void CIrrBMeshWriter::writeHeader(const scene::IMesh* mesh)
330 		{
331 			struct IrrbHeader h;
332 			memset(&h,0,sizeof(h));
333 
334 			// include version is visible portion
335 			sprintf(h.hSig,"irrb %d.%d",Version >> 8,Version & 0xFF);
336 			*(h.hSig+strlen(h.hSig)) = 0x1a;
337 
338 			h.hSigCheck = MAKE_IRR_ID('i','r','r','b');
339 			h.hVersion = Version;
340 			h.hFlags = (get_endianess() << 16) | (sizeof(int) * 8);
341 			strcpy(h.hCreator,Creator.c_str());
342 			h.hMeshCount = 1;  // 1 mesh for now, todo: add ability to include lod mesh data...
343 			h.hMeshBufferCount = mesh->getMeshBufferCount();
344 			Writer->write(&h,sizeof(h));
345 		}
346 
updateBuffers(const scene::IMesh * mesh,struct IrrbVertex * VBuffer,u32 * IBuffer)347 		void CIrrBMeshWriter::updateBuffers(const scene::IMesh* mesh,
348 			struct IrrbVertex* VBuffer,u32* IBuffer)
349 		{
350 			u32 vidx=0,iidx=0;
351 
352 			for (int i=0; i<(int)mesh->getMeshBufferCount(); ++i)
353 			{
354 				scene::IMeshBuffer* buffer = mesh->getMeshBuffer(i);
355 				if (buffer)
356 				{
357 					u32 vertexCount = buffer->getVertexCount();
358 
359 					switch(buffer->getVertexType())
360 					{
361 					case video::EVT_STANDARD:
362 						{
363 							video::S3DVertex* vtx = (video::S3DVertex*)buffer->getVertices();
364 							for (u32 j=0; j<vertexCount; ++j)
365 							{
366 								VBuffer[vidx].vPos.x = vtx[j].Pos.X;
367 								VBuffer[vidx].vPos.y = vtx[j].Pos.Y;
368 								VBuffer[vidx].vPos.z = vtx[j].Pos.Z;
369 
370 								VBuffer[vidx].vNormal.x = vtx[j].Normal.X;
371 								VBuffer[vidx].vNormal.y = vtx[j].Normal.Y;
372 								VBuffer[vidx].vNormal.z = vtx[j].Normal.Z;
373 
374 								VBuffer[vidx].vColor = vtx[j].Color.color;
375 
376 								VBuffer[vidx].vUV1.x = vtx[j].TCoords.X;
377 								VBuffer[vidx].vUV1.y = vtx[j].TCoords.Y;
378 								++vidx;
379 							}
380 						}
381 						break;
382 					case video::EVT_2TCOORDS:
383 						{
384 							video::S3DVertex2TCoords* vtx = (video::S3DVertex2TCoords*)buffer->getVertices();
385 							for (u32 j=0; j<vertexCount; ++j)
386 							{
387 								VBuffer[vidx].vPos.x = vtx[j].Pos.X;
388 								VBuffer[vidx].vPos.y = vtx[j].Pos.Y;
389 								VBuffer[vidx].vPos.z = vtx[j].Pos.Z;
390 
391 								VBuffer[vidx].vNormal.x = vtx[j].Normal.X;
392 								VBuffer[vidx].vNormal.y = vtx[j].Normal.Y;
393 								VBuffer[vidx].vNormal.z = vtx[j].Normal.Z;
394 
395 								VBuffer[vidx].vColor = vtx[j].Color.color;
396 
397 								VBuffer[vidx].vUV1.x = vtx[j].TCoords.X;
398 								VBuffer[vidx].vUV1.y = vtx[j].TCoords.Y;
399 								VBuffer[vidx].vUV2.x = vtx[j].TCoords2.X;
400 								VBuffer[vidx].vUV2.y = vtx[j].TCoords2.Y;
401 								++vidx;
402 
403 							}
404 						}
405 						break;
406 					case video::EVT_TANGENTS:
407 						{
408 							video::S3DVertexTangents* vtx = (video::S3DVertexTangents*)buffer->getVertices();
409 							for (u32 j=0; j<vertexCount; ++j)
410 							{
411 								VBuffer[vidx].vPos.x = vtx[j].Pos.X;
412 								VBuffer[vidx].vPos.y = vtx[j].Pos.Y;
413 								VBuffer[vidx].vPos.z = vtx[j].Pos.Z;
414 
415 								VBuffer[vidx].vNormal.x = vtx[j].Normal.X;
416 								VBuffer[vidx].vNormal.y = vtx[j].Normal.Y;
417 								VBuffer[vidx].vNormal.z = vtx[j].Normal.Z;
418 
419 								VBuffer[vidx].vColor = vtx[j].Color.color;
420 
421 								VBuffer[vidx].vUV1.x = vtx[j].TCoords.X;
422 								VBuffer[vidx].vUV1.y = vtx[j].TCoords.Y;
423 
424 								VBuffer[vidx].vTangent.x = vtx[j].Tangent.X;
425 								VBuffer[vidx].vTangent.y = vtx[j].Tangent.Y;
426 								VBuffer[vidx].vTangent.z = vtx[j].Tangent.Z;
427 
428 								VBuffer[vidx].vBiNormal.x = vtx[j].Binormal.X;
429 								VBuffer[vidx].vBiNormal.y = vtx[j].Binormal.Y;
430 								VBuffer[vidx].vBiNormal.z = vtx[j].Binormal.Z;
431 								++vidx;
432 							}
433 						}
434 						break;
435 					}
436 
437 					// update indices
438 					u32 indexCount = buffer->getIndexCount();
439 					const u16* idx16 = buffer->getIndices();
440 					const u32* idx32 = (u32 *)buffer->getIndices();
441 
442 					video::E_INDEX_TYPE iType = buffer->getIndexType();
443 
444 					for(u32 j=0; j<indexCount; j++)
445 					{
446 						if(iType == video::EIT_16BIT)
447 							IBuffer[iidx++] = idx16[j];
448 						else
449 							IBuffer[iidx++] = idx32[j];
450 					}
451 				}
452 			}
453 		}
454 
updateMaterialLayer(const video::SMaterial & material,u8 layerNumber,irr::core::stringc & textureName,struct IrrbMaterialLayer & layer)455 		void CIrrBMeshWriter::updateMaterialLayer(const video::SMaterial& material,u8 layerNumber, irr::core::stringc& textureName, struct IrrbMaterialLayer& layer)
456 		{
457 			if(layerNumber > 3)
458 				return;
459 
460 			memset(&layer,0,sizeof(layer));
461 
462 			textureName = material.TextureLayer[layerNumber].Texture->getName().getPath().c_str();
463 
464 			if(RelativeBase.size())
465 			{
466 				// simple relative path calculation
467 				int blen = RelativeBase.size();
468 				int tlen = textureName.size();
469 				int idx = 0;
470 
471 				const char *bp = RelativeBase.c_str();
472 				const char *tp = textureName.c_str();
473 
474 				while( (idx < blen) & (idx < tlen))
475 				{
476 					if(bp[idx] != tp[idx])
477 						break;
478 					++idx;
479 				}
480 				textureName = textureName.subString(idx, textureName.size());
481 			}
482 
483 			layer.mBilinearFilter = material.TextureLayer[layerNumber].BilinearFilter;
484 			layer.mTrilinearFilter = material.TextureLayer[layerNumber].TrilinearFilter;
485 			layer.mAnisotropicFilter = material.TextureLayer[layerNumber].AnisotropicFilter;
486 			layer.mTextureWrapU = material.TextureLayer[layerNumber].TextureWrapU;
487 			layer.mTextureWrapV = material.TextureLayer[layerNumber].TextureWrapV;
488 			layer.mLODBias = material.TextureLayer[layerNumber].LODBias;
489 			memcpy(&layer.mMatrix,material.TextureLayer[layerNumber].getTextureMatrix().pointer(),sizeof(f32)*16);
490 		}
491 
updateMaterial(const video::SMaterial & material,struct IrrbMaterial & mat)492 		void CIrrBMeshWriter::updateMaterial(const video::SMaterial& material, struct IrrbMaterial& mat)
493 		{
494 
495 			memset(&mat,0,sizeof(mat));
496 
497 			mat.mType = material.MaterialType;
498 			mat.mAmbient = material.AmbientColor.color;
499 			mat.mDiffuse = material.DiffuseColor.color;
500 			mat.mEmissive = material.EmissiveColor.color;
501 			mat.mSpecular = material.SpecularColor.color;
502 			mat.mShininess = material.Shininess;
503 			mat.mParm1 = material.MaterialTypeParam;
504 			mat.mParm2 = material.MaterialTypeParam2;
505 			mat.mThickness = material.Thickness;
506 			mat.mZBuffer = material.ZBuffer;
507 			mat.mAntiAliasing = material.AntiAliasing;
508 			mat.mColorMask = material.ColorMask;
509 			mat.mColorMaterial = material.ColorMaterial;
510 			mat.mBlendOperation = material.BlendOperation;
511 			mat.mPolygonOffsetFactor = material.PolygonOffsetFactor;
512 			mat.mPolygonOffsetDirection = material.PolygonOffsetDirection;
513 
514 			mat.mWireframe = material.Wireframe;
515 			mat.mPointCloud = material.PointCloud;
516 			mat.mGrouraudShading = material.GouraudShading;
517 			mat.mLighting = material.Lighting;
518 			mat.mZWriteEnabled = material.ZWriteEnable;
519 			mat.mBackfaceCulling = material.BackfaceCulling;
520 			mat.mFrontfaceCulling = material.FrontfaceCulling;
521 			mat.mFogEnable = material.FogEnable;
522 			mat.mNormalizeNormals = material.NormalizeNormals;
523 			mat.mUseMipMaps = material.UseMipMaps;
524 		}
525 	} // end namespace
526 } // end namespace
527