1 // Copyright (C) 2002-2012 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 // This file was written by Jonas Petersen and modified by Nikolaus Gebhardt.
5 // See CLMTSMeshFileLoder.h for details.
6 /*
7 
8 CLMTSMeshFileLoader.cpp
9 
10 LMTSMeshFileLoader
11 Written by Jonas Petersen (a.k.a. jox)
12 
13 Version 1.5 - 15 March 2005
14 
15 Get the latest version here: http://development.mindfloaters.de/
16 
17 This class allows loading meshes with lightmaps (*.lmts + *.tga files) that were created
18 using Pulsar LMTools by Lord Trancos (http://www.geocities.com/dxlab/index_en.html)
19 
20 Notes:
21 - This version does not support user data in the *.lmts files, but still loads those files (by skipping the extra data).
22 
23 License:
24 --------
25 
26 It's free. You are encouraged to give me credit if you use it in your software.
27 
28 Version History:
29 ----------------
30 
31 Version 1.5 - 15 March 2005
32 - Did a better cleanup. No memory leaks in case of an loading error.
33 - Added "#include <stdio.h>" for sprintf.
34 
35 Version 1.4 - 12 March 2005
36 - Fixed bug in texture and subset loading code that would possibly cause crash.
37 - Fixed memory cleanup to avoid leak when loading more then one mesh
38 - Used the irrlicht Logger instead of cerr to output warnings and errors.
39   For this I had to change the constructor
40   from:
41 	CLMTSMeshFileLoader(io::IFileSystem* fs, video::IVideoDriver* driver)
42   to:
43 	CLMTSMeshFileLoader(IrrlichtDevice* device)
44 
45 Version 1.3 - 15 February 2005
46 - Fixed bug that prevented loading more than one different lmts files.
47 - Removed unnecessary "#include <os.h>".
48 - Added "std::" in front of "cerr". This was necessary for Visual Studio .NET,
49   I hope it's not disturbing other compilers.
50 - Added warning message when a texture can not be loaded.
51 - Changed the documentation a bit (minor).
52 
53 Version 1.2
54 - To avoid confusion I skipped version 1.2 because the website was offering
55 version 1.2 even though it was only version 1.1. Sorry about that.
56 
57 Version 1.1 - 29 July 2004
58 - Added setTexturePath() function
59 - Minor improvements
60 
61 Version 1.0 - 29 July 2004
62 - Initial release
63 
64 
65 */
66 //////////////////////////////////////////////////////////////////////
67 
68 #include "IrrCompileConfig.h"
69 #ifdef _IRR_COMPILE_WITH_LMTS_LOADER_
70 
71 #include "SMeshBufferLightMap.h"
72 #include "SAnimatedMesh.h"
73 #include "SMeshBuffer.h"
74 #include "irrString.h"
75 #include "IReadFile.h"
76 #include "IAttributes.h"
77 #include "ISceneManager.h"
78 #include "CLMTSMeshFileLoader.h"
79 #include "os.h"
80 
81 namespace irr
82 {
83 namespace scene
84 {
85 
CLMTSMeshFileLoader(io::IFileSystem * fs,video::IVideoDriver * driver,io::IAttributes * parameters)86 CLMTSMeshFileLoader::CLMTSMeshFileLoader(io::IFileSystem* fs,
87 		video::IVideoDriver* driver, io::IAttributes* parameters)
88 	: Textures(0), Subsets(0), Triangles(0),
89 	Parameters(parameters), Driver(driver), FileSystem(fs), FlipEndianess(false)
90 {
91 	#ifdef _DEBUG
92 	setDebugName("CLMTSMeshFileLoader");
93 	#endif
94 
95 	if (Driver)
96 		Driver->grab();
97 
98 	if (FileSystem)
99 		FileSystem->grab();
100 }
101 
102 
~CLMTSMeshFileLoader()103 CLMTSMeshFileLoader::~CLMTSMeshFileLoader()
104 {
105 	cleanup();
106 
107 	if (Driver)
108 		Driver->drop();
109 
110 	if (FileSystem)
111 		FileSystem->drop();
112 }
113 
114 
cleanup()115 void CLMTSMeshFileLoader::cleanup()
116 {
117 	delete [] Textures;
118 	Textures = 0;
119 	delete [] Subsets;
120 	Subsets = 0;
121 	delete [] Triangles;
122 	Triangles = 0;
123 }
124 
125 
isALoadableFileExtension(const io::path & filename) const126 bool CLMTSMeshFileLoader::isALoadableFileExtension(const io::path& filename) const
127 {
128 	return core::hasFileExtension ( filename, "lmts" );
129 }
130 
131 
createMesh(io::IReadFile * file)132 IAnimatedMesh* CLMTSMeshFileLoader::createMesh(io::IReadFile* file)
133 {
134 	u32 i;
135 	u32 id;
136 
137 	// HEADER
138 
139 	file->read(&Header, sizeof(SLMTSHeader));
140 	if (Header.MagicID == 0x4C4D5354)
141 	{
142 		FlipEndianess = true;
143 		Header.MagicID = os::Byteswap::byteswap(Header.MagicID);
144 		Header.Version = os::Byteswap::byteswap(Header.Version);
145 		Header.HeaderSize = os::Byteswap::byteswap(Header.HeaderSize);
146 		Header.TextureCount = os::Byteswap::byteswap(Header.TextureCount);
147 		Header.SubsetCount = os::Byteswap::byteswap(Header.SubsetCount);
148 		Header.TriangleCount = os::Byteswap::byteswap(Header.TriangleCount);
149 		Header.SubsetSize = os::Byteswap::byteswap(Header.SubsetSize);
150 		Header.VertexSize = os::Byteswap::byteswap(Header.VertexSize);
151 	}
152 	if (Header.MagicID != 0x53544D4C) { // "LMTS"
153 		os::Printer::log("LMTS ERROR: wrong header magic id!", ELL_ERROR);
154 		return 0;
155 	}
156 
157 	//Skip any User Data (arbitrary app specific data)
158 
159 	const s32 userSize = Header.HeaderSize - sizeof(SLMTSHeader);
160 	if (userSize>0)
161 		file->seek(userSize,true);
162 
163 	// TEXTURES
164 
165 	file->read(&id, sizeof(u32));
166 	if (FlipEndianess)
167 		id = os::Byteswap::byteswap(id);
168 	if (id != 0x54584554) { // "TEXT"
169 		os::Printer::log("LMTS ERROR: wrong texture magic id!", ELL_ERROR);
170 		return 0;
171 	}
172 
173 	Textures = new SLMTSTextureInfoEntry[Header.TextureCount];
174 
175 	file->read(Textures, sizeof(SLMTSTextureInfoEntry)*Header.TextureCount);
176 	if (FlipEndianess)
177 	{
178 		for (i=0; i<Header.TextureCount; ++i)
179 			Textures[i].Flags = os::Byteswap::byteswap(Textures[i].Flags);
180 	}
181 
182 	// SUBSETS
183 
184 	file->read(&id, sizeof(u32));
185 	if (FlipEndianess)
186 		id = os::Byteswap::byteswap(id);
187 	if (id != 0x53425553) // "SUBS"
188 	{
189 		os::Printer::log("LMTS ERROR: wrong subset magic id!", ELL_ERROR);
190 		cleanup();
191 		return 0;
192 	}
193 
194 	Subsets = new SLMTSSubsetInfoEntry[Header.SubsetCount];
195 	const s32 subsetUserSize = Header.SubsetSize - sizeof(SLMTSSubsetInfoEntry);
196 
197 	for (i=0; i<Header.SubsetCount; ++i)
198 	{
199 		file->read(&Subsets[i], sizeof(SLMTSSubsetInfoEntry));
200 		if (FlipEndianess)
201 		{
202 			Subsets[i].Offset = os::Byteswap::byteswap(Subsets[i].Offset);
203 			Subsets[i].Count = os::Byteswap::byteswap(Subsets[i].Count);
204 			Subsets[i].TextID1 = os::Byteswap::byteswap(Subsets[i].TextID1);
205 			Subsets[i].TextID2 = os::Byteswap::byteswap(Subsets[i].TextID2);
206 		}
207 		if (subsetUserSize>0)
208 			file->seek(subsetUserSize,true);
209 	}
210 
211 	// TRIANGLES
212 
213 	file->read(&id, sizeof(u32));
214 	if (FlipEndianess)
215 		id = os::Byteswap::byteswap(id);
216 	if (id != 0x53495254) // "TRIS"
217 	{
218 		os::Printer::log("LMTS ERROR: wrong triangle magic id!", ELL_ERROR);
219 		cleanup();
220 		return 0;
221 	}
222 
223 	Triangles = new SLMTSTriangleDataEntry[(Header.TriangleCount*3)];
224 	const s32 triUserSize = Header.VertexSize - sizeof(SLMTSTriangleDataEntry);
225 
226 	for (i=0; i<(Header.TriangleCount*3); ++i)
227 	{
228 		file->read(&Triangles[i], sizeof(SLMTSTriangleDataEntry));
229 		if (FlipEndianess)
230 		{
231 			Triangles[i].X = os::Byteswap::byteswap(Triangles[i].X);
232 			Triangles[i].Y = os::Byteswap::byteswap(Triangles[i].Y);
233 			Triangles[i].Z = os::Byteswap::byteswap(Triangles[i].Z);
234 			Triangles[i].U1 = os::Byteswap::byteswap(Triangles[i].U1);
235 			Triangles[i].V1 = os::Byteswap::byteswap(Triangles[i].U2);
236 			Triangles[i].U2 = os::Byteswap::byteswap(Triangles[i].V1);
237 			Triangles[i].V2 = os::Byteswap::byteswap(Triangles[i].V2);
238 		}
239 		if (triUserSize>0)
240 			file->seek(triUserSize,true);
241 	}
242 
243 	/////////////////////////////////////////////////////////////////
244 
245 	SMesh* mesh = new SMesh();
246 
247 	constructMesh(mesh);
248 
249 	loadTextures(mesh);
250 
251 	cleanup();
252 
253 	SAnimatedMesh* am = new SAnimatedMesh();
254 	am->Type = EAMT_LMTS; // not unknown to irrlicht anymore
255 
256 	am->addMesh(mesh);
257 	am->recalculateBoundingBox();
258 	mesh->drop();
259 	return am;
260 }
261 
262 
constructMesh(SMesh * mesh)263 void CLMTSMeshFileLoader::constructMesh(SMesh* mesh)
264 {
265 	for (s32 i=0; i<Header.SubsetCount; ++i)
266 	{
267 		scene::SMeshBufferLightMap* meshBuffer = new scene::SMeshBufferLightMap();
268 
269 		// EMT_LIGHTMAP_M2/EMT_LIGHTMAP_M4 also possible
270 		meshBuffer->Material.MaterialType = video::EMT_LIGHTMAP;
271 		meshBuffer->Material.Wireframe = false;
272 		meshBuffer->Material.Lighting = false;
273 
274 		mesh->addMeshBuffer(meshBuffer);
275 
276 		const u32 offs = Subsets[i].Offset * 3;
277 
278 		for (u32 sc=0; sc<Subsets[i].Count; sc++)
279 		{
280 			const u32 idx = meshBuffer->getVertexCount();
281 
282 			for (u32 vu=0; vu<3; ++vu)
283 			{
284 				const SLMTSTriangleDataEntry& v = Triangles[offs+(3*sc)+vu];
285 				meshBuffer->Vertices.push_back(
286 						video::S3DVertex2TCoords(
287 							v.X, v.Y, v.Z,
288 							video::SColor(255,255,255,255),
289 							v.U1, v.V1, v.U2, v.V2));
290 			}
291 			const core::vector3df normal = core::plane3df(
292 				meshBuffer->Vertices[idx].Pos,
293 				meshBuffer->Vertices[idx+1].Pos,
294 				meshBuffer->Vertices[idx+2].Pos).Normal;
295 
296 			meshBuffer->Vertices[idx].Normal = normal;
297 			meshBuffer->Vertices[idx+1].Normal = normal;
298 			meshBuffer->Vertices[idx+2].Normal = normal;
299 
300 			meshBuffer->Indices.push_back(idx);
301 			meshBuffer->Indices.push_back(idx+1);
302 			meshBuffer->Indices.push_back(idx+2);
303 		}
304 		meshBuffer->drop();
305 	}
306 
307 	for (u32 j=0; j<mesh->MeshBuffers.size(); ++j)
308 		mesh->MeshBuffers[j]->recalculateBoundingBox();
309 
310 	mesh->recalculateBoundingBox();
311 }
312 
313 
loadTextures(SMesh * mesh)314 void CLMTSMeshFileLoader::loadTextures(SMesh* mesh)
315 {
316 	if (!Driver || !FileSystem)
317 		return;
318 
319 	// load textures
320 
321 	// a little too much space, but won't matter here
322 	core::array<video::ITexture*> tex;
323 	tex.reallocate(Header.TextureCount);
324 	core::array<video::ITexture*> lig;
325 	lig.reallocate(Header.TextureCount);
326 	core::array<u32> id2id;
327 	id2id.reallocate(Header.TextureCount);
328 
329 	const core::stringc Path = Parameters->getAttributeAsString(LMTS_TEXTURE_PATH);
330 
331 	core::stringc s;
332 	for (u32 t=0; t<Header.TextureCount; ++t)
333 	{
334 		video::ITexture* tmptex = 0;
335 		s = Path;
336 		s.append(Textures[t].Filename);
337 
338 		if (FileSystem->existFile(s))
339 			tmptex = Driver->getTexture(s);
340 		else
341 			os::Printer::log("LMTS WARNING: Texture does not exist", s.c_str(), ELL_WARNING);
342 
343 		if (Textures[t].Flags & 0x01)
344 		{
345 			id2id.push_back(lig.size());
346 			lig.push_back(tmptex);
347 		}
348 		else
349 		{
350 			id2id.push_back(tex.size());
351 			tex.push_back(tmptex);
352 		}
353 	}
354 
355 	// attach textures to materials.
356 
357 	for (u32 i=0; i<Header.SubsetCount; ++i)
358 	{
359 		if (Subsets[i].TextID1 < Header.TextureCount && id2id[Subsets[i].TextID1] < tex.size())
360 			mesh->getMeshBuffer(i)->getMaterial().setTexture(0, tex[id2id[Subsets[i].TextID1]]);
361 		if (Subsets[i].TextID2 < Header.TextureCount && id2id[Subsets[i].TextID2] < lig.size())
362 			mesh->getMeshBuffer(i)->getMaterial().setTexture(1, lig[id2id[Subsets[i].TextID2]]);
363 
364 		if (!mesh->getMeshBuffer(i)->getMaterial().getTexture(1))
365 			mesh->getMeshBuffer(i)->getMaterial().MaterialType = video::EMT_SOLID;
366 	}
367 }
368 
369 
370 } // end namespace scene
371 } // end namespace irr
372 
373 #endif // _IRR_COMPILE_WITH_LMTS_LOADER_
374