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 // orginally written by Christian Stehno, modified by Nikolaus Gebhardt
5 
6 #include "IrrCompileConfig.h"
7 #ifdef _IRR_COMPILE_WITH_OGRE_LOADER_
8 
9 #include "COgreMeshFileLoader.h"
10 #include "os.h"
11 #include "SMeshBuffer.h"
12 #include "SAnimatedMesh.h"
13 #include "IReadFile.h"
14 #include "fast_atof.h"
15 #include "coreutil.h"
16 
17 #ifdef _DEBUG
18 #define IRR_OGRE_LOADER_DEBUG
19 #endif
20 
21 namespace irr
22 {
23 namespace scene
24 {
25 
26 namespace
27 {
28 	enum OGRE_CHUNKS
29 	{
30 		// Main Chunks
31 		COGRE_HEADER= 0x1000,
32 		COGRE_SKELETON= 0x2000,
33 		COGRE_MESH= 0x3000,
34 
35 		// sub chunks of COGRE_MESH
36 		COGRE_SUBMESH= 0x4000,
37 		COGRE_GEOMETRY= 0x5000,
38 		COGRE_SKELETON_LINK= 0x6000,
39 		COGRE_BONE_ASSIGNMENT= 0x7000,
40 		COGRE_MESH_LOD= 0x8000,
41 		COGRE_MESH_BOUNDS= 0x9000,
42 		COGRE_MESH_SUBMESH_NAME_TABLE= 0xA000,
43 		COGRE_MESH_EDGE_LISTS= 0xB000,
44 
45 		// sub chunks of COGRE_SKELETON
46 		COGRE_BONE_PARENT= 0x3000,
47 		COGRE_ANIMATION= 0x4000,
48 		COGRE_ANIMATION_TRACK= 0x4100,
49 		COGRE_ANIMATION_KEYFRAME= 0x4110,
50 		COGRE_ANIMATION_LINK= 0x5000,
51 
52 		// sub chunks of COGRE_SUBMESH
53 		COGRE_SUBMESH_OPERATION= 0x4010,
54 		COGRE_SUBMESH_BONE_ASSIGNMENT= 0x4100,
55 		COGRE_SUBMESH_TEXTURE_ALIAS= 0x4200,
56 
57 		// sub chunks of COGRE_GEOMETRY
58 		COGRE_GEOMETRY_VERTEX_DECLARATION= 0x5100,
59 		COGRE_GEOMETRY_VERTEX_ELEMENT= 0x5110,
60 		COGRE_GEOMETRY_VERTEX_BUFFER= 0x5200,
61 		COGRE_GEOMETRY_VERTEX_BUFFER_DATA= 0x5210
62 	};
63 }
64 
65 //! Constructor
COgreMeshFileLoader(io::IFileSystem * fs,video::IVideoDriver * driver)66 COgreMeshFileLoader::COgreMeshFileLoader(io::IFileSystem* fs, video::IVideoDriver* driver)
67 : FileSystem(fs), Driver(driver), SwapEndian(false), Mesh(0), NumUV(0)
68 {
69 
70 	#ifdef _DEBUG
71 	setDebugName("COgreMeshFileLoader");
72 	#endif
73 
74 	if (FileSystem)
75 		FileSystem->grab();
76 
77 	if (Driver)
78 		Driver->grab();
79 }
80 
81 
82 //! destructor
~COgreMeshFileLoader()83 COgreMeshFileLoader::~COgreMeshFileLoader()
84 {
85 	clearMeshes();
86 
87 	if (FileSystem)
88 		FileSystem->drop();
89 
90 	if (Driver)
91 		Driver->drop();
92 
93 	if (Mesh)
94 		Mesh->drop();
95 }
96 
97 
98 //! returns true if the file maybe is able to be loaded by this class
99 //! based on the file extension (e.g. ".bsp")
isALoadableFileExtension(const io::path & filename) const100 bool COgreMeshFileLoader::isALoadableFileExtension(const io::path& filename) const
101 {
102 	return core::hasFileExtension ( filename, "mesh" );
103 }
104 
105 
106 //! creates/loads an animated mesh from the file.
107 //! \return Pointer to the created mesh. Returns 0 if loading failed.
108 //! If you no longer need the mesh, you should call IAnimatedMesh::drop().
109 //! See IReferenceCounted::drop() for more information.
createMesh(io::IReadFile * file)110 IAnimatedMesh* COgreMeshFileLoader::createMesh(io::IReadFile* file)
111 {
112 	s16 id;
113 
114 	file->read(&id, 2);
115 
116 	if (id == COGRE_HEADER)
117 		SwapEndian=false;
118 	else if (id == 0x0010)
119 		SwapEndian=true;
120 	else
121 		return 0;
122 	ChunkData data;
123 	readString(file, data, Version);
124 	if ((Version != "[MeshSerializer_v1.30]") && (Version != "[MeshSerializer_v1.40]") && (Version != "[MeshSerializer_v1.41]"))
125 		return 0;
126 
127 	clearMeshes();
128 	if (Mesh)
129 		Mesh->drop();
130 
131 	CurrentlyLoadingFromPath = FileSystem->getFileDir(file->getFileName());
132 	loadMaterials(file);
133 
134 	if (readChunk(file))
135 	{
136 		// delete data loaded from file
137 		clearMeshes();
138 
139 		if (Skeleton.Bones.size())
140 		{
141 			ISkinnedMesh* tmp = static_cast<CSkinnedMesh*>(Mesh);
142 			static_cast<CSkinnedMesh*>(Mesh)->updateBoundingBox();
143 			Skeleton.Animations.clear();
144 			Skeleton.Bones.clear();
145 			Mesh=0;
146 			return tmp;
147 		}
148 		else
149 		{
150 			for (u32 i=0; i<Mesh->getMeshBufferCount(); ++i)
151 				((SMeshBuffer*)Mesh->getMeshBuffer(i))->recalculateBoundingBox();
152 
153 			((SMesh*)Mesh)->recalculateBoundingBox();
154 			SAnimatedMesh* am = new SAnimatedMesh();
155 			am->Type = EAMT_3DS;
156 			am->addMesh(Mesh);
157 			am->recalculateBoundingBox();
158 			Mesh->drop();
159 			Mesh = 0;
160         	return am;
161 		}
162 	}
163 
164 	Mesh->drop();
165 	Mesh = 0;
166 
167 	return 0;
168 }
169 
170 
readChunk(io::IReadFile * file)171 bool COgreMeshFileLoader::readChunk(io::IReadFile* file)
172 {
173 	while(file->getPos() < file->getSize())
174 	{
175 		ChunkData data;
176 		readChunkData(file, data);
177 
178 		switch(data.header.id)
179 		{
180 		case COGRE_MESH:
181 			{
182 				Meshes.push_back(OgreMesh());
183 				readObjectChunk(file, data, Meshes.getLast());
184 				if (Skeleton.Bones.size())
185 					Mesh = new CSkinnedMesh();
186 				else
187 					Mesh = new SMesh();
188 				composeObject();
189 			}
190 			break;
191 		default:
192 			return true;
193 		}
194 	}
195 
196 	return true;
197 }
198 
199 
readObjectChunk(io::IReadFile * file,ChunkData & parent,OgreMesh & mesh)200 bool COgreMeshFileLoader::readObjectChunk(io::IReadFile* file, ChunkData& parent, OgreMesh& mesh)
201 {
202 #ifdef IRR_OGRE_LOADER_DEBUG
203 	os::Printer::log("Read Object Chunk", ELL_DEBUG);
204 #endif
205 	readBool(file, parent, mesh.SkeletalAnimation);
206 	bool skeleton_loaded=false;
207 	while ((parent.read < parent.header.length)&&(file->getPos() < file->getSize()))
208 	{
209 		ChunkData data;
210 		readChunkData(file, data);
211 
212 		switch(data.header.id)
213 		{
214 			case COGRE_GEOMETRY:
215 				readGeometry(file, data, mesh.Geometry);
216 			break;
217 			case COGRE_SUBMESH:
218 				mesh.SubMeshes.push_back(OgreSubMesh());
219 				readSubMesh(file, data, mesh.SubMeshes.getLast());
220 			break;
221 			case COGRE_MESH_BOUNDS:
222 			{
223 #ifdef IRR_OGRE_LOADER_DEBUG
224 				os::Printer::log("Read Mesh Bounds", ELL_DEBUG);
225 #endif
226 				readVector(file, data, mesh.BBoxMinEdge);
227 				readVector(file, data, mesh.BBoxMaxEdge);
228 				readFloat(file, data, &mesh.BBoxRadius);
229 			}
230 			break;
231 			case COGRE_SKELETON_LINK:
232 			{
233 #ifdef IRR_OGRE_LOADER_DEBUG
234 				os::Printer::log("Read Skeleton link", ELL_DEBUG);
235 #endif
236 				core::stringc name;
237 				readString(file, data, name);
238 				loadSkeleton(file, name);
239 				skeleton_loaded=true;
240 			}
241 			break;
242 			case COGRE_BONE_ASSIGNMENT:
243 			{
244 				mesh.BoneAssignments.push_back(OgreBoneAssignment());
245 				readInt(file, data, &mesh.BoneAssignments.getLast().VertexID);
246 				readShort(file, data, &mesh.BoneAssignments.getLast().BoneID);
247 				readFloat(file, data, &mesh.BoneAssignments.getLast().Weight);
248 			}
249 			break;
250 			case COGRE_MESH_LOD:
251 			case COGRE_MESH_SUBMESH_NAME_TABLE:
252 			case COGRE_MESH_EDGE_LISTS:
253 				// ignore chunk
254 				file->seek(data.header.length-data.read, true);
255 				data.read += data.header.length-data.read;
256 				break;
257 			default:
258 #ifdef IRR_OGRE_LOADER_DEBUG
259 				os::Printer::log("Skipping", core::stringc(data.header.id), ELL_DEBUG);
260 #endif
261 				// ignore chunk
262 				file->seek(data.header.length-data.read, true);
263 				data.read += data.header.length-data.read;
264 				break;
265 		}
266 		parent.read += data.read;
267 	}
268 	if (!skeleton_loaded)
269 		loadSkeleton(file, FileSystem->getFileBasename(file->getFileName(), false));
270 	return true;
271 }
272 
273 
readGeometry(io::IReadFile * file,ChunkData & parent,OgreGeometry & geometry)274 bool COgreMeshFileLoader::readGeometry(io::IReadFile* file, ChunkData& parent, OgreGeometry& geometry)
275 {
276 #ifdef IRR_OGRE_LOADER_DEBUG
277 	os::Printer::log("Read Geometry", ELL_DEBUG);
278 #endif
279 	readInt(file, parent, &geometry.NumVertex);
280 	while(parent.read < parent.header.length)
281 	{
282 		ChunkData data;
283 		readChunkData(file, data);
284 
285 		switch(data.header.id)
286 		{
287 		case COGRE_GEOMETRY_VERTEX_DECLARATION:
288 			readVertexDeclaration(file, data, geometry);
289 			break;
290 		case COGRE_GEOMETRY_VERTEX_BUFFER:
291 			readVertexBuffer(file, data, geometry);
292 			break;
293 		default:
294 			// ignore chunk
295 #ifdef IRR_OGRE_LOADER_DEBUG
296 			os::Printer::log("Skipping", core::stringc(data.header.id), ELL_DEBUG);
297 #endif
298 			file->seek(data.header.length-data.read, true);
299 			data.read += data.header.length-data.read;
300 		}
301 		parent.read += data.read;
302 	}
303 	if (parent.read != parent.header.length)
304 		os::Printer::log("Incorrect geometry length. File might be corrupted.");
305 	return true;
306 }
307 
308 
readVertexDeclaration(io::IReadFile * file,ChunkData & parent,OgreGeometry & geometry)309 bool COgreMeshFileLoader::readVertexDeclaration(io::IReadFile* file, ChunkData& parent, OgreGeometry& geometry)
310 {
311 #ifdef IRR_OGRE_LOADER_DEBUG
312 	os::Printer::log("Read Vertex Declaration", ELL_DEBUG);
313 #endif
314 	NumUV = 0;
315 	while(parent.read < parent.header.length)
316 	{
317 		ChunkData data;
318 		readChunkData(file, data);
319 
320 		switch(data.header.id)
321 		{
322 		case COGRE_GEOMETRY_VERTEX_ELEMENT:
323 		{
324 			geometry.Elements.push_back(OgreVertexElement());
325 			OgreVertexElement& elem = geometry.Elements.getLast();
326 			readShort(file, data, &elem.Source);
327 			readShort(file, data, &elem.Type);
328 			readShort(file, data, &elem.Semantic);
329 			if (elem.Semantic == 7) //Tex coords
330 			{
331 				++NumUV;
332 			}
333 			readShort(file, data, &elem.Offset);
334 			elem.Offset /= sizeof(f32);
335 			readShort(file, data, &elem.Index);
336 		}
337 			break;
338 		default:
339 			// ignore chunk
340 			file->seek(data.header.length-data.read, true);
341 			data.read += data.header.length-data.read;
342 		}
343 		parent.read += data.read;
344 	}
345 	if (parent.read != parent.header.length)
346 		os::Printer::log("Incorrect vertex declaration length. File might be corrupted.");
347 	return true;
348 }
349 
350 
readVertexBuffer(io::IReadFile * file,ChunkData & parent,OgreGeometry & geometry)351 bool COgreMeshFileLoader::readVertexBuffer(io::IReadFile* file, ChunkData& parent, OgreGeometry& geometry)
352 {
353 #ifdef IRR_OGRE_LOADER_DEBUG
354 	os::Printer::log("Read Vertex Buffer", ELL_DEBUG);
355 #endif
356 	OgreVertexBuffer buf;
357 	readShort(file, parent, &buf.BindIndex);
358 	readShort(file, parent, &buf.VertexSize);
359 	buf.VertexSize /= sizeof(f32);
360 	ChunkData data;
361 	readChunkData(file, data);
362 
363 	if (data.header.id == COGRE_GEOMETRY_VERTEX_BUFFER_DATA)
364 	{
365 		buf.Data.set_used(geometry.NumVertex*buf.VertexSize);
366 		readFloat(file, data, buf.Data.pointer(), geometry.NumVertex*buf.VertexSize);
367 	}
368 
369 	geometry.Buffers.push_back(buf);
370 	parent.read += data.read;
371 	if (parent.read != parent.header.length)
372 		os::Printer::log("Incorrect vertex buffer length. File might be corrupted.");
373 	return true;
374 }
375 
376 
readSubMesh(io::IReadFile * file,ChunkData & parent,OgreSubMesh & subMesh)377 bool COgreMeshFileLoader::readSubMesh(io::IReadFile* file, ChunkData& parent, OgreSubMesh& subMesh)
378 {
379 #ifdef IRR_OGRE_LOADER_DEBUG
380 	os::Printer::log("Read Submesh", ELL_DEBUG);
381 #endif
382 	readString(file, parent, subMesh.Material);
383 #ifdef IRR_OGRE_LOADER_DEBUG
384 	os::Printer::log("using material", subMesh.Material, ELL_DEBUG);
385 #endif
386 	readBool(file, parent, subMesh.SharedVertices);
387 
388 	s32 numIndices;
389 	readInt(file, parent, &numIndices);
390 	subMesh.Indices.set_used(numIndices);
391 
392 	readBool(file, parent, subMesh.Indices32Bit);
393 
394 	if (subMesh.Indices32Bit)
395 		readInt(file, parent, subMesh.Indices.pointer(), numIndices);
396 	else
397 	{
398 		for (s32 i=0; i<numIndices; ++i)
399 		{
400 			u16 num;
401 			readShort(file, parent, &num);
402 			subMesh.Indices[i]=num;
403 		}
404 	}
405 
406 	while(parent.read < parent.header.length)
407 	{
408 		ChunkData data;
409 		readChunkData(file, data);
410 
411 		switch(data.header.id)
412 		{
413 		case COGRE_GEOMETRY:
414 			readGeometry(file, data, subMesh.Geometry);
415 		break;
416 		case COGRE_SUBMESH_OPERATION:
417 			readShort(file, data, &subMesh.Operation);
418 #ifdef IRR_OGRE_LOADER_DEBUG
419 			os::Printer::log("Read Submesh Operation",core::stringc(subMesh.Operation), ELL_DEBUG);
420 #endif
421 			if (subMesh.Operation != 4)
422 				os::Printer::log("Primitive type != trilist not yet implemented", ELL_WARNING);
423 			break;
424 		case COGRE_SUBMESH_TEXTURE_ALIAS:
425 		{
426 #ifdef IRR_OGRE_LOADER_DEBUG
427 				os::Printer::log("Read Submesh Texture Alias", ELL_DEBUG);
428 #endif
429 			core::stringc texture, alias;
430 			readString(file, data, texture);
431 			readString(file, data, alias);
432 			subMesh.TextureAliases.push_back(OgreTextureAlias(texture,alias));
433 		}
434 			break;
435 		case COGRE_SUBMESH_BONE_ASSIGNMENT:
436 		{
437 			subMesh.BoneAssignments.push_back(OgreBoneAssignment());
438 			readInt(file, data, &subMesh.BoneAssignments.getLast().VertexID);
439 			readShort(file, data, &subMesh.BoneAssignments.getLast().BoneID);
440 			readFloat(file, data, &subMesh.BoneAssignments.getLast().Weight);
441 		}
442 			break;
443 		default:
444 #ifdef IRR_OGRE_LOADER_DEBUG
445 			os::Printer::log("Skipping", core::stringc(data.header.id), ELL_DEBUG);
446 #endif
447 			parent.read=parent.header.length;
448 			file->seek(-(long)sizeof(ChunkHeader), true);
449 			return true;
450 		}
451 		parent.read += data.read;
452 	}
453 	if (parent.read != parent.header.length)
454 		os::Printer::log("Incorrect submesh length. File might be corrupted.");
455 #ifdef IRR_OGRE_LOADER_DEBUG
456 	os::Printer::log("Done with submesh", ELL_DEBUG);
457 #endif
458 	return true;
459 }
460 
461 
composeMeshBufferMaterial(scene::IMeshBuffer * mb,const core::stringc & materialName)462 void COgreMeshFileLoader::composeMeshBufferMaterial(scene::IMeshBuffer* mb, const core::stringc& materialName)
463 {
464 	video::SMaterial& material=mb->getMaterial();
465 	for (u32 k=0; k<Materials.size(); ++k)
466 	{
467 		if ((materialName==Materials[k].Name)&&(Materials[k].Techniques.size())&&(Materials[k].Techniques[0].Passes.size()))
468 		{
469 			material=Materials[k].Techniques[0].Passes[0].Material;
470 			for (u32 i=0; i<Materials[k].Techniques[0].Passes[0].Texture.Filename.size(); ++i)
471 			{
472 				if (FileSystem->existFile(Materials[k].Techniques[0].Passes[0].Texture.Filename[i]))
473 					material.setTexture(i, Driver->getTexture(Materials[k].Techniques[0].Passes[0].Texture.Filename[i]));
474 				else
475 					material.setTexture(i, Driver->getTexture((CurrentlyLoadingFromPath+"/"+FileSystem->getFileBasename(Materials[k].Techniques[0].Passes[0].Texture.Filename[i]))));
476 			}
477 			break;
478 		}
479 	}
480 }
481 
482 
composeMeshBuffer(const core::array<s32> & indices,const OgreGeometry & geom)483 scene::SMeshBuffer* COgreMeshFileLoader::composeMeshBuffer(const core::array<s32>& indices, const OgreGeometry& geom)
484 {
485 	scene::SMeshBuffer *mb=new scene::SMeshBuffer();
486 
487 	u32 i;
488 	mb->Indices.set_used(indices.size());
489 	for (i=0; i<indices.size(); ++i)
490 		mb->Indices[i]=indices[i];
491 
492 	mb->Vertices.set_used(geom.NumVertex);
493 	for (i=0; i<geom.Elements.size(); ++i)
494 	{
495 		if (geom.Elements[i].Semantic==1) //Pos
496 		{
497 			for (u32 j=0; j<geom.Buffers.size(); ++j)
498 			{
499 				if (geom.Elements[i].Source==geom.Buffers[j].BindIndex)
500 				{
501 					u32 eSize=geom.Buffers[j].VertexSize;
502 					u32 ePos=geom.Elements[i].Offset;
503 					for (s32 k=0; k<geom.NumVertex; ++k)
504 					{
505 						mb->Vertices[k].Color=mb->Material.DiffuseColor;
506 						mb->Vertices[k].Pos.set(geom.Buffers[j].Data[ePos],geom.Buffers[j].Data[ePos+1],geom.Buffers[j].Data[ePos+2]);
507 						ePos += eSize;
508 					}
509 				}
510 			}
511 		}
512 
513 		if (geom.Elements[i].Semantic==4) //Normal
514 		{
515 			for (u32 j=0; j<geom.Buffers.size(); ++j)
516 			{
517 				if (geom.Elements[i].Source==geom.Buffers[j].BindIndex)
518 				{
519 					u32 eSize=geom.Buffers[j].VertexSize;
520 					u32 ePos=geom.Elements[i].Offset;
521 					for (s32 k=0; k<geom.NumVertex; ++k)
522 					{
523 						mb->Vertices[k].Normal.set(geom.Buffers[j].Data[ePos],geom.Buffers[j].Data[ePos+1],geom.Buffers[j].Data[ePos+2]);
524 						ePos += eSize;
525 					}
526 				}
527 			}
528 		}
529 
530 		if (geom.Elements[i].Semantic==7) //TexCoord
531 		{
532 			for (u32 j=0; j<geom.Buffers.size(); ++j)
533 			{
534 				if (geom.Elements[i].Source==geom.Buffers[j].BindIndex)
535 				{
536 					u32 eSize=geom.Buffers[j].VertexSize;
537 					u32 ePos=geom.Elements[i].Offset;
538 					for (s32 k=0; k<geom.NumVertex; ++k)
539 					{
540 						mb->Vertices[k].TCoords.set(geom.Buffers[j].Data[ePos],geom.Buffers[j].Data[ePos+1]);
541 						ePos += eSize;
542 					}
543 				}
544 			}
545 		}
546 	}
547 	return mb;
548 }
549 
550 
composeMeshBufferLightMap(const core::array<s32> & indices,const OgreGeometry & geom)551 scene::SMeshBufferLightMap* COgreMeshFileLoader::composeMeshBufferLightMap(const core::array<s32>& indices, const OgreGeometry& geom)
552 {
553 	scene::SMeshBufferLightMap *mb=new scene::SMeshBufferLightMap();
554 
555 	u32 i;
556 	mb->Indices.set_used(indices.size());
557 	for (i=0; i<indices.size(); ++i)
558 		mb->Indices[i]=indices[i];
559 
560 	mb->Vertices.set_used(geom.NumVertex);
561 
562 	for (i=0; i<geom.Elements.size(); ++i)
563 	{
564 		if (geom.Elements[i].Semantic==1) //Pos
565 		{
566 			for (u32 j=0; j<geom.Buffers.size(); ++j)
567 			{
568 				if (geom.Elements[i].Source==geom.Buffers[j].BindIndex)
569 				{
570 					u32 eSize=geom.Buffers[j].VertexSize;
571 					u32 ePos=geom.Elements[i].Offset;
572 					for (s32 k=0; k<geom.NumVertex; ++k)
573 					{
574 						mb->Vertices[k].Color=mb->Material.DiffuseColor;
575 						mb->Vertices[k].Pos.set(geom.Buffers[j].Data[ePos],geom.Buffers[j].Data[ePos+1],geom.Buffers[j].Data[ePos+2]);
576 						ePos += eSize;
577 					}
578 				}
579 			}
580 		}
581 
582 		if (geom.Elements[i].Semantic==4) //Normal
583 		{
584 			for (u32 j=0; j<geom.Buffers.size(); ++j)
585 			{
586 				if (geom.Elements[i].Source==geom.Buffers[j].BindIndex)
587 				{
588 					u32 eSize=geom.Buffers[j].VertexSize;
589 					u32 ePos=geom.Elements[i].Offset;
590 					for (s32 k=0; k<geom.NumVertex; ++k)
591 					{
592 						mb->Vertices[k].Normal.set(geom.Buffers[j].Data[ePos],geom.Buffers[j].Data[ePos+1],geom.Buffers[j].Data[ePos+2]);
593 						ePos += eSize;
594 					}
595 				}
596 			}
597 		}
598 
599 		if (geom.Elements[i].Semantic==7) //TexCoord
600 		{
601 			for (u32 j=0; j<geom.Buffers.size(); ++j)
602 			{
603 				if (geom.Elements[i].Source==geom.Buffers[j].BindIndex)
604 				{
605 					u32 eSize=geom.Buffers[j].VertexSize;
606 					u32 ePos=geom.Elements[i].Offset;
607 					// make sure we have data for a second texture coord
608 					const bool secondCoord = (eSize>ePos+3);
609 					for (s32 k=0; k<geom.NumVertex; ++k)
610 					{
611 						mb->Vertices[k].TCoords.set(geom.Buffers[j].Data[ePos], geom.Buffers[j].Data[ePos+1]);
612 						if (secondCoord)
613 							mb->Vertices[k].TCoords2.set(geom.Buffers[j].Data[ePos+2], geom.Buffers[j].Data[ePos+3]);
614 						else
615 							mb->Vertices[k].TCoords2.set(geom.Buffers[j].Data[ePos], geom.Buffers[j].Data[ePos+1]);
616 						ePos += eSize;
617 					}
618 				}
619 			}
620 		}
621 	}
622 
623 	return mb;
624 }
625 
626 
composeMeshBufferSkinned(scene::CSkinnedMesh & mesh,const core::array<s32> & indices,const OgreGeometry & geom)627 scene::IMeshBuffer* COgreMeshFileLoader::composeMeshBufferSkinned(scene::CSkinnedMesh& mesh, const core::array<s32>& indices, const OgreGeometry& geom)
628 {
629 	scene::SSkinMeshBuffer *mb=mesh.addMeshBuffer();
630 	if (NumUV>1)
631 	{
632 		mb->convertTo2TCoords();
633 		mb->Vertices_2TCoords.set_used(geom.NumVertex);
634 	}
635 	else
636 		mb->Vertices_Standard.set_used(geom.NumVertex);
637 
638 	u32 i;
639 	mb->Indices.set_used(indices.size());
640 	for (i=0; i<indices.size(); i+=3)
641 	{
642 		mb->Indices[i+0]=indices[i+2];
643 		mb->Indices[i+1]=indices[i+1];
644 		mb->Indices[i+2]=indices[i+0];
645 	}
646 
647 	for (i=0; i<geom.Elements.size(); ++i)
648 	{
649 		if (geom.Elements[i].Semantic==1) //Pos
650 		{
651 			for (u32 j=0; j<geom.Buffers.size(); ++j)
652 			{
653 				if (geom.Elements[i].Source==geom.Buffers[j].BindIndex)
654 				{
655 					u32 eSize=geom.Buffers[j].VertexSize;
656 					u32 ePos=geom.Elements[i].Offset;
657 					for (s32 k=0; k<geom.NumVertex; ++k)
658 					{
659 						if (NumUV>1)
660 							mb->Vertices_2TCoords[k].Color=mb->Material.DiffuseColor;
661 						else
662 							mb->Vertices_Standard[k].Color=mb->Material.DiffuseColor;
663 						mb->getPosition(k).set(-geom.Buffers[j].Data[ePos],geom.Buffers[j].Data[ePos+1],geom.Buffers[j].Data[ePos+2]);
664 						ePos += eSize;
665 					}
666 				}
667 			}
668 		}
669 
670 		if (geom.Elements[i].Semantic==4) //Normal
671 		{
672 			for (u32 j=0; j<geom.Buffers.size(); ++j)
673 			{
674 				if (geom.Elements[i].Source==geom.Buffers[j].BindIndex)
675 				{
676 					u32 eSize=geom.Buffers[j].VertexSize;
677 					u32 ePos=geom.Elements[i].Offset;
678 					for (s32 k=0; k<geom.NumVertex; ++k)
679 					{
680 						mb->getNormal(k).set(-geom.Buffers[j].Data[ePos],geom.Buffers[j].Data[ePos+1],geom.Buffers[j].Data[ePos+2]);
681 						ePos += eSize;
682 					}
683 				}
684 			}
685 		}
686 
687 		if (geom.Elements[i].Semantic==7) //TexCoord
688 		{
689 			for (u32 j=0; j<geom.Buffers.size(); ++j)
690 			{
691 				if (geom.Elements[i].Source==geom.Buffers[j].BindIndex)
692 				{
693 					u32 eSize=geom.Buffers[j].VertexSize;
694 					u32 ePos=geom.Elements[i].Offset;
695 					// make sure we have data for a second texture coord
696 					const bool secondCoord = (eSize>ePos+3);
697 					for (s32 k=0; k<geom.NumVertex; ++k)
698 					{
699 						mb->getTCoords(k).set(geom.Buffers[j].Data[ePos], geom.Buffers[j].Data[ePos+1]);
700 						if (NumUV>1)
701 						{
702 							if (secondCoord)
703 								mb->Vertices_2TCoords[k].TCoords2.set(geom.Buffers[j].Data[ePos+2], geom.Buffers[j].Data[ePos+3]);
704 							else
705 								mb->Vertices_2TCoords[k].TCoords2.set(geom.Buffers[j].Data[ePos], geom.Buffers[j].Data[ePos+1]);
706 						}
707 						ePos += eSize;
708 					}
709 				}
710 			}
711 		}
712 	}
713 
714 	return mb;
715 }
716 
717 
composeObject(void)718 void COgreMeshFileLoader::composeObject(void)
719 {
720 	for (u32 i=0; i<Meshes.size(); ++i)
721 	{
722 		for (u32 j=0; j<Meshes[i].SubMeshes.size(); ++j)
723 		{
724 			IMeshBuffer* mb;
725 			if (Meshes[i].SubMeshes[j].SharedVertices)
726 			{
727 				if (Skeleton.Bones.size())
728 				{
729 					mb = composeMeshBufferSkinned(*(CSkinnedMesh*)Mesh, Meshes[i].SubMeshes[j].Indices, Meshes[i].Geometry);
730 				}
731 				else if (NumUV < 2)
732 				{
733 					mb = composeMeshBuffer(Meshes[i].SubMeshes[j].Indices, Meshes[i].Geometry);
734 				}
735 				else
736 				{
737 					mb = composeMeshBufferLightMap(Meshes[i].SubMeshes[j].Indices, Meshes[i].Geometry);
738 				}
739 			}
740 			else
741 			{
742 				if (Skeleton.Bones.size())
743 				{
744 					mb = composeMeshBufferSkinned(*(CSkinnedMesh*)Mesh, Meshes[i].SubMeshes[j].Indices, Meshes[i].SubMeshes[j].Geometry);
745 				}
746 				else if (NumUV < 2)
747 				{
748 					mb = composeMeshBuffer(Meshes[i].SubMeshes[j].Indices, Meshes[i].SubMeshes[j].Geometry);
749 				}
750 				else
751 				{
752 					mb = composeMeshBufferLightMap(Meshes[i].SubMeshes[j].Indices, Meshes[i].SubMeshes[j].Geometry);
753 				}
754 			}
755 
756 			if (mb != 0)
757 			{
758 				composeMeshBufferMaterial(mb, Meshes[i].SubMeshes[j].Material);
759 				if (!Skeleton.Bones.size())
760 				{
761 					((SMesh*)Mesh)->addMeshBuffer(mb);
762 					mb->drop();
763 				}
764 			}
765 		}
766 	}
767 	if (Skeleton.Bones.size())
768 	{
769 		CSkinnedMesh* m = (CSkinnedMesh*)Mesh;
770 		// Create Joints
771 		for (u32 i=0; i<Skeleton.Bones.size(); ++i)
772 		{
773 			ISkinnedMesh::SJoint* joint = m->addJoint();
774 			joint->Name=Skeleton.Bones[i].Name;
775 
776 			// IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched to getMatrix_transposed instead of getMatrix for downward compatibility.
777 			//								   Not tested so far if this was correct or wrong before quaternion fix!
778 			Skeleton.Bones[i].Orientation.getMatrix_transposed(joint->LocalMatrix);
779 
780 			if (Skeleton.Bones[i].Scale != core::vector3df(1,1,1))
781 			{
782 				core::matrix4 scaleMatrix;
783 				scaleMatrix.setScale( Skeleton.Bones[i].Scale );
784 				joint->LocalMatrix *= scaleMatrix;
785 			}
786 			joint->LocalMatrix.setTranslation( Skeleton.Bones[i].Position );
787 		}
788 		// Joints hierarchy
789 		for (u32 i=0; i<Skeleton.Bones.size(); ++i)
790 		{
791 			if (Skeleton.Bones[i].Parent<m->getJointCount())
792 			{
793 				m->getAllJoints()[Skeleton.Bones[i].Parent]->Children.push_back(m->getAllJoints()[Skeleton.Bones[i].Handle]);
794 			}
795 		}
796 
797 		// Weights
798 		u32 bufCount=0;
799 		for (u32 i=0; i<Meshes.size(); ++i)
800 		{
801 			for (u32 j=0; j<Meshes[i].SubMeshes.size(); ++j)
802 			{
803 				for (u32 k=0; k<Meshes[i].SubMeshes[j].BoneAssignments.size(); ++k)
804 				{
805 					const OgreBoneAssignment& ba = Meshes[i].SubMeshes[j].BoneAssignments[k];
806 					if (ba.BoneID<m->getJointCount())
807 					{
808 						ISkinnedMesh::SWeight* w = m->addWeight(m->getAllJoints()[ba.BoneID]);
809 						w->strength=ba.Weight;
810 						w->vertex_id=ba.VertexID;
811 						w->buffer_id=bufCount;
812 					}
813 				}
814 				++bufCount;
815 			}
816 		}
817 
818 		for (u32 i=0; i<Skeleton.Animations.size(); ++i)
819 		{
820 			for (u32 j=0; j<Skeleton.Animations[i].Keyframes.size(); ++j)
821 			{
822 				OgreKeyframe& frame = Skeleton.Animations[i].Keyframes[j];
823 				ISkinnedMesh::SJoint* keyjoint = m->getAllJoints()[frame.BoneID];
824 				ISkinnedMesh::SPositionKey* poskey = m->addPositionKey(keyjoint);
825 				poskey->frame=frame.Time*25;
826 				poskey->position=keyjoint->LocalMatrix.getTranslation()+frame.Position;
827 				ISkinnedMesh::SRotationKey* rotkey = m->addRotationKey(keyjoint);
828 				rotkey->frame=frame.Time*25;
829 
830 				// IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched from keyjoint->LocalMatrix to keyjoint->LocalMatrix.getTransposed() for downward compatibility.
831 				//								   Not tested so far if this was correct or wrong before quaternion fix!
832 				rotkey->rotation=core::quaternion(keyjoint->LocalMatrix.getTransposed())*frame.Orientation;
833 
834 				ISkinnedMesh::SScaleKey* scalekey = m->addScaleKey(keyjoint);
835 				scalekey->frame=frame.Time*25;
836 				scalekey->scale=frame.Scale;
837 			}
838 		}
839 		m->finalize();
840 	}
841 }
842 
843 
getMaterialToken(io::IReadFile * file,core::stringc & token,bool noNewLine)844 void COgreMeshFileLoader::getMaterialToken(io::IReadFile* file, core::stringc& token, bool noNewLine)
845 {
846 	bool parseString=false;
847 	c8 c=0;
848 	token = "";
849 
850 	if (file->getPos() >= file->getSize())
851 		return;
852 
853 	file->read(&c, sizeof(c8));
854 	// search for word beginning
855 	while ( core::isspace(c) && (file->getPos() < file->getSize()))
856 	{
857 		if (noNewLine && c=='\n')
858 		{
859 			file->seek(-1, true);
860 			return;
861 		}
862 		file->read(&c, sizeof(c8));
863 	}
864 	// check if we read a string
865 	if (c=='"')
866 	{
867 		parseString = true;
868 		file->read(&c, sizeof(c8));
869 	}
870 	do
871 	{
872 		if (c=='/')
873 		{
874 			file->read(&c, sizeof(c8));
875 			// check for comments, cannot be part of strings
876 			if (!parseString && (c=='/'))
877 			{
878 				// skip comments
879 				while(c!='\n')
880 					file->read(&c, sizeof(c8));
881 				if (!token.size())
882 				{
883 					// if we start with a comment we need to skip
884 					// following whitespaces, so restart
885 					getMaterialToken(file, token, noNewLine);
886 					return;
887 				}
888 				else
889 				{
890 					// else continue with next character
891 					file->read(&c, sizeof(c8));
892 					continue;
893 				}
894 			}
895 			else
896 			{
897 				// else append first slash and check if second char
898 				// ends this token
899 				token.append('/');
900 				if ((!parseString && core::isspace(c)) ||
901 						(parseString && (c=='"')))
902 					return;
903 			}
904 		}
905 		token.append(c);
906 		file->read(&c, sizeof(c8));
907 		// read until a token delimiter is found
908 	}
909 	while (((!parseString && !core::isspace(c)) || (parseString && (c!='"'))) &&
910 			(file->getPos() < file->getSize()));
911 	// we want to skip the last quotes of a string , but other chars might be the next
912 	// token already.
913 	if (!parseString)
914 		file->seek(-1, true);
915 }
916 
917 
readColor(io::IReadFile * file,video::SColor & col)918 bool COgreMeshFileLoader::readColor(io::IReadFile* file, video::SColor& col)
919 {
920 	core::stringc token;
921 
922 	getMaterialToken(file, token);
923 	if (token!="vertexcolour")
924 	{
925 		video::SColorf col_f;
926 		col_f.r=core::fast_atof(token.c_str());
927 		getMaterialToken(file, token);
928 		col_f.g=core::fast_atof(token.c_str());
929 		getMaterialToken(file, token);
930 		col_f.b=core::fast_atof(token.c_str());
931 		getMaterialToken(file, token, true);
932 		if (token.size())
933 			col_f.a=core::fast_atof(token.c_str());
934 		else
935 			col_f.a=1.0f;
936 		if ((col_f.r==0.0f)&&(col_f.g==0.0f)&&(col_f.b==0.0f))
937 			col.set(255,255,255,255);
938 		else
939 			col=col_f.toSColor();
940 		return false;
941 	}
942 	return true;
943 }
944 
945 
readPass(io::IReadFile * file,OgreTechnique & technique)946 void COgreMeshFileLoader::readPass(io::IReadFile* file, OgreTechnique& technique)
947 {
948 #ifdef IRR_OGRE_LOADER_DEBUG
949 	os::Printer::log("Read Pass");
950 #endif
951 	core::stringc token;
952 	technique.Passes.push_back(OgrePass());
953 	OgrePass& pass=technique.Passes.getLast();
954 
955 	getMaterialToken(file, token); //open brace or name
956 	if (token != "{")
957 		getMaterialToken(file, token); //open brace
958 
959 	getMaterialToken(file, token);
960 	if (token == "}")
961 		return;
962 	u32 inBlocks=1;
963 	u32 textureUnit=0;
964 	while(inBlocks)
965 	{
966 		if (token=="ambient")
967 			pass.AmbientTokenColor=readColor(file, pass.Material.AmbientColor);
968 		else if (token=="diffuse")
969 			pass.DiffuseTokenColor=readColor(file, pass.Material.DiffuseColor);
970 		else if (token=="specular")
971 		{
972 			pass.SpecularTokenColor=readColor(file, pass.Material.SpecularColor);
973 			getMaterialToken(file, token);
974 			pass.Material.Shininess=core::fast_atof(token.c_str());
975 		}
976 		else if (token=="emissive")
977 			pass.EmissiveTokenColor=readColor(file, pass.Material.EmissiveColor);
978 		else if (token=="scene_blend")
979 		{ // TODO: Choose correct values
980 			getMaterialToken(file, token);
981 			if (token=="add")
982 				pass.Material.MaterialType=video::EMT_TRANSPARENT_ADD_COLOR;
983 			else if (token=="modulate")
984 				pass.Material.MaterialType=video::EMT_SOLID;
985 			else if (token=="alpha_blend")
986 				pass.Material.MaterialType=video::EMT_TRANSPARENT_ALPHA_CHANNEL;
987 			else if (token=="colour_blend")
988 				pass.Material.MaterialType=video::EMT_TRANSPARENT_VERTEX_ALPHA;
989 			else
990 				getMaterialToken(file, token);
991 		}
992 		else if (token=="depth_check")
993 		{
994 			getMaterialToken(file, token);
995 			if (token!="on")
996 				pass.Material.ZBuffer=video::ECFN_NEVER;
997 		}
998 		else if (token=="depth_write")
999 		{
1000 			getMaterialToken(file, token);
1001 			pass.Material.ZWriteEnable=(token=="on");
1002 		}
1003 		else if (token=="depth_func")
1004 		{
1005 			getMaterialToken(file, token); // Function name
1006 			if (token=="always_fail")
1007 				pass.Material.ZBuffer=video::ECFN_NEVER;
1008 			else if (token=="always_pass")
1009 				pass.Material.ZBuffer=video::ECFN_ALWAYS;
1010 			else if (token=="equal")
1011 				pass.Material.ZBuffer=video::ECFN_EQUAL;
1012 			else if (token=="greater")
1013 				pass.Material.ZBuffer=video::ECFN_GREATER;
1014 			else if (token=="greater_equal")
1015 				pass.Material.ZBuffer=video::ECFN_GREATEREQUAL;
1016 			else if (token=="less")
1017 				pass.Material.ZBuffer=video::ECFN_LESS;
1018 			else if (token=="less_equal")
1019 				pass.Material.ZBuffer=video::ECFN_LESSEQUAL;
1020 			else if (token=="not_equal")
1021 				pass.Material.ZBuffer=video::ECFN_NOTEQUAL;
1022 		}
1023 		else if (token=="normalise_normals")
1024 		{
1025 			getMaterialToken(file, token);
1026 			pass.Material.NormalizeNormals=(token=="on");
1027 		}
1028 		else if (token=="depth_bias")
1029 		{
1030 			getMaterialToken(file, token); // bias value
1031 		}
1032 		else if (token=="alpha_rejection")
1033 		{
1034 			getMaterialToken(file, token); // function name
1035 			getMaterialToken(file, token); // value
1036 			pass.Material.MaterialTypeParam=core::fast_atof(token.c_str());
1037 		}
1038 		else if (token=="alpha_to_coverage")
1039 		{
1040 			getMaterialToken(file, token);
1041 			if (token=="on")
1042 				pass.Material.AntiAliasing |= video::EAAM_ALPHA_TO_COVERAGE;
1043 		}
1044 		else if (token=="colour_write")
1045 		{
1046 			getMaterialToken(file, token);
1047 			pass.Material.ColorMask = (token=="on")?video::ECP_ALL:video::ECP_NONE;
1048 		}
1049 		else if (token=="cull_hardware")
1050 		{
1051 			getMaterialToken(file, token); // rotation name
1052 		}
1053 		else if (token=="cull_software")
1054 		{
1055 			getMaterialToken(file, token); // culling side
1056 		}
1057 		else if (token=="lighting")
1058 		{
1059 			getMaterialToken(file, token);
1060 			pass.Material.Lighting=(token=="on");
1061 		}
1062 		else if (token=="shading")
1063 		{
1064 			getMaterialToken(file, token);
1065 			// We take phong as gouraud
1066 			pass.Material.GouraudShading=(token!="flat");
1067 		}
1068 		else if (token=="polygon_mode")
1069 		{
1070 			getMaterialToken(file, token);
1071 			pass.Material.Wireframe=(token=="wireframe");
1072 			pass.Material.PointCloud=(token=="points");
1073 		}
1074 		else if (token=="max_lights")
1075 		{
1076 			getMaterialToken(file, token);
1077 			pass.MaxLights=core::strtoul10(token.c_str());
1078 		}
1079 		else if (token=="point_size")
1080 		{
1081 			getMaterialToken(file, token);
1082 			pass.PointSize=core::fast_atof(token.c_str());
1083 		}
1084 		else if (token=="point_sprites")
1085 		{
1086 			getMaterialToken(file, token);
1087 			pass.PointSprites=(token=="on");
1088 		}
1089 		else if (token=="point_size_min")
1090 		{
1091 			getMaterialToken(file, token);
1092 			pass.PointSizeMin=core::strtoul10(token.c_str());
1093 		}
1094 		else if (token=="point_size_max")
1095 		{
1096 			getMaterialToken(file, token);
1097 			pass.PointSizeMax=core::strtoul10(token.c_str());
1098 		}
1099 		else if (token=="texture_unit")
1100 		{
1101 #ifdef IRR_OGRE_LOADER_DEBUG
1102 			os::Printer::log("Read Texture unit", ELL_DEBUG);
1103 #endif
1104 			getMaterialToken(file, token); //open brace
1105 			getMaterialToken(file, token);
1106 			while(token != "}")
1107 			{
1108 				if (token=="texture")
1109 				{
1110 					getMaterialToken(file, token);
1111 					pass.Texture.Filename.push_back(token);
1112 #ifdef IRR_OGRE_LOADER_DEBUG
1113 					os::Printer::log("Read Texture", token, ELL_DEBUG);
1114 #endif
1115 					getMaterialToken(file, pass.Texture.CoordsType, true);
1116 					getMaterialToken(file, pass.Texture.MipMaps, true);
1117 					getMaterialToken(file, pass.Texture.Alpha, true);
1118 					// Hmm, we might need more hints for other material types using two textures...
1119 					if (textureUnit>0)
1120 						pass.Material.MaterialType=video::EMT_LIGHTMAP;
1121 				}
1122 				else if (token=="filtering")
1123 				{
1124 					getMaterialToken(file, token);
1125 					pass.Material.TextureLayer[textureUnit].AnisotropicFilter=0;
1126 					if (token=="point")
1127 					{
1128 						pass.Material.TextureLayer[textureUnit].BilinearFilter=false;
1129 						pass.Material.TextureLayer[textureUnit].TrilinearFilter=false;
1130 						getMaterialToken(file, token);
1131 						getMaterialToken(file, token);
1132 					}
1133 					else if (token=="linear")
1134 					{
1135 						getMaterialToken(file, token);
1136 						if (token=="point")
1137 						{
1138 							pass.Material.TextureLayer[textureUnit].BilinearFilter=false;
1139 							pass.Material.TextureLayer[textureUnit].TrilinearFilter=false;
1140 							getMaterialToken(file, token);
1141 						}
1142 						else
1143 						{
1144 							pass.Material.TextureLayer[textureUnit].BilinearFilter=true;
1145 							getMaterialToken(file, token);
1146 							pass.Material.TextureLayer[textureUnit].TrilinearFilter=(token=="linear");
1147 						}
1148 					}
1149 					else
1150 					{
1151 						pass.Material.TextureLayer[textureUnit].BilinearFilter=(token=="bilinear");
1152 						pass.Material.TextureLayer[textureUnit].TrilinearFilter=(token=="trilinear");
1153 						pass.Material.TextureLayer[textureUnit].AnisotropicFilter=(token=="anisotropic")?2:1;
1154 					}
1155 				}
1156 				else if (token=="max_anisotropy")
1157 				{
1158 					getMaterialToken(file, token);
1159 					pass.Material.TextureLayer[textureUnit].AnisotropicFilter=(u8)core::strtoul10(token.c_str());
1160 				}
1161 				else if (token=="texture_alias")
1162 				{
1163 					getMaterialToken(file, pass.Texture.Alias);
1164 				}
1165 				else if (token=="mipmap_bias")
1166 				{
1167 					getMaterialToken(file, token);
1168 					pass.Material.TextureLayer[textureUnit].LODBias=(s8)core::fast_atof(token.c_str());
1169 				}
1170 				else if (token=="colour_op")
1171 				{ // TODO: Choose correct values
1172 					getMaterialToken(file, token);
1173 					if (token=="add")
1174 						pass.Material.MaterialType=video::EMT_TRANSPARENT_ADD_COLOR;
1175 					else if (token=="modulate")
1176 						pass.Material.MaterialType=video::EMT_SOLID;
1177 					else if (token=="alpha_blend")
1178 						pass.Material.MaterialType=video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1179 					else if (token=="colour_blend")
1180 						pass.Material.MaterialType=video::EMT_TRANSPARENT_VERTEX_ALPHA;
1181 					else
1182 						getMaterialToken(file, token);
1183 				}
1184 				getMaterialToken(file, token);
1185 			}
1186 			++textureUnit;
1187 		}
1188 		else if (token=="shadow_caster_program_ref")
1189 		{
1190 			do
1191 			{
1192 				getMaterialToken(file, token);
1193 			} while (token != "}");
1194 		}
1195 		else if (token=="shadow_caster_vertex_program_ref")
1196 		{
1197 			do
1198 			{
1199 				getMaterialToken(file, token);
1200 			} while (token != "}");
1201 		}
1202 		else if (token=="vertex_program_ref")
1203 		{
1204 			do
1205 			{
1206 				getMaterialToken(file, token);
1207 			} while (token != "}");
1208 		}
1209 		//fog_override, iteration, point_size_attenuation
1210 		//not considered yet!
1211 		getMaterialToken(file, token);
1212 		if (token=="{")
1213 			++inBlocks;
1214 		else if (token=="}")
1215 			--inBlocks;
1216 	}
1217 }
1218 
1219 
readTechnique(io::IReadFile * file,OgreMaterial & mat)1220 void COgreMeshFileLoader::readTechnique(io::IReadFile* file, OgreMaterial& mat)
1221 {
1222 #ifdef IRR_OGRE_LOADER_DEBUG
1223 	os::Printer::log("Read Technique");
1224 #endif
1225 	core::stringc token;
1226 	mat.Techniques.push_back(OgreTechnique());
1227 	OgreTechnique& technique=mat.Techniques.getLast();
1228 
1229 	getMaterialToken(file, technique.Name); //open brace or name
1230 	if (technique.Name != "{")
1231 		getMaterialToken(file, token); //open brace
1232 	else
1233 		technique.Name=core::stringc((int)mat.Techniques.size());
1234 
1235 	getMaterialToken(file, token);
1236 	while (token != "}")
1237 	{
1238 		if (token == "pass")
1239 			readPass(file, technique);
1240 		else if (token == "scheme")
1241 			getMaterialToken(file, token);
1242 		else if (token == "lod_index")
1243 			getMaterialToken(file, token);
1244 		getMaterialToken(file, token);
1245 	}
1246 }
1247 
1248 
loadMaterials(io::IReadFile * meshFile)1249 void COgreMeshFileLoader::loadMaterials(io::IReadFile* meshFile)
1250 {
1251 #ifdef IRR_OGRE_LOADER_DEBUG
1252 	os::Printer::log("Load Materials", ELL_DEBUG);
1253 #endif
1254 	core::stringc token;
1255 	io::IReadFile* file = 0;
1256 	io::path filename = FileSystem->getFileBasename(meshFile->getFileName(), false) + ".material";
1257 	if (FileSystem->existFile(filename))
1258 		file = FileSystem->createAndOpenFile(filename);
1259 	else
1260 		file = FileSystem->createAndOpenFile(FileSystem->getFileDir(meshFile->getFileName())+"/"+filename);
1261 
1262 	if (!file)
1263 	{
1264 		os::Printer::log("Could not load OGRE material", filename);
1265 		return;
1266 	}
1267 
1268 	getMaterialToken(file, token);
1269 
1270 	while (file->getPos() < file->getSize())
1271 	{
1272 		if ((token == "fragment_program") || (token == "vertex_program"))
1273 		{
1274 			// skip whole block
1275 			u32 blocks=1;
1276 			do
1277 			{
1278 				getMaterialToken(file, token);
1279 			} while (token != "{");
1280 			do
1281 			{
1282 				getMaterialToken(file, token);
1283 				if (token == "{")
1284 					++blocks;
1285 				else if (token == "}")
1286 					--blocks;
1287 			} while (blocks);
1288 			getMaterialToken(file, token);
1289 			continue;
1290 		}
1291 		if (token != "material")
1292 		{
1293 			if (token.trim().size())
1294 				os::Printer::log("Unknown material group", token.c_str());
1295 			break;
1296 		}
1297 
1298 		Materials.push_back(OgreMaterial());
1299 		OgreMaterial& mat = Materials.getLast();
1300 
1301 		getMaterialToken(file, mat.Name);
1302 #ifdef IRR_OGRE_LOADER_DEBUG
1303 	os::Printer::log("Load Material", mat.Name.c_str(), ELL_DEBUG);
1304 #endif
1305 		getMaterialToken(file, token); //open brace
1306 		getMaterialToken(file, token);
1307 		while(token != "}")
1308 		{
1309 			if (token=="lod_distances") // can have several items
1310 				getMaterialToken(file, token);
1311 			else if (token=="receive_shadows")
1312 			{
1313 				getMaterialToken(file, token);
1314 				mat.ReceiveShadows=(token=="on");
1315 			}
1316 			else if (token=="transparency_casts_shadows")
1317 			{
1318 				getMaterialToken(file, token);
1319 				mat.TransparencyCastsShadows=(token=="on");
1320 			}
1321 			else if (token=="set_texture_alias")
1322 			{
1323 				getMaterialToken(file, token);
1324 				getMaterialToken(file, token);
1325 			}
1326 			else if (token=="technique")
1327 				readTechnique(file, mat);
1328 			getMaterialToken(file, token);
1329 		}
1330 		getMaterialToken(file, token);
1331 	}
1332 
1333 	file->drop();
1334 #ifdef IRR_OGRE_LOADER_DEBUG
1335 	os::Printer::log("Finished loading Materials", ELL_DEBUG);
1336 #endif
1337 }
1338 
1339 
loadSkeleton(io::IReadFile * meshFile,const core::stringc & name)1340 bool COgreMeshFileLoader::loadSkeleton(io::IReadFile* meshFile, const core::stringc& name)
1341 {
1342 #ifdef IRR_OGRE_LOADER_DEBUG
1343 	os::Printer::log("Load Skeleton", name, ELL_DEBUG);
1344 #endif
1345 	io::IReadFile* file = 0;
1346 	io::path filename;
1347 	if (FileSystem->existFile(name))
1348 		file = FileSystem->createAndOpenFile(name);
1349 	else if (FileSystem->existFile(filename = FileSystem->getFileDir(meshFile->getFileName())+"/"+name))
1350 		file = FileSystem->createAndOpenFile(filename);
1351 	else if (FileSystem->existFile(filename = FileSystem->getFileBasename(meshFile->getFileName(), false) + ".skeleton"))
1352 		file = FileSystem->createAndOpenFile(filename);
1353 	else
1354 		file = FileSystem->createAndOpenFile(FileSystem->getFileDir(meshFile->getFileName())+"/"+filename);
1355 	if (!file)
1356 	{
1357 		os::Printer::log("Could not load matching skeleton", name);
1358 		return false;
1359 	}
1360 
1361 	s16 id;
1362 	file->read(&id, 2);
1363 	if (SwapEndian)
1364 		id = os::Byteswap::byteswap(id);
1365 	if (id != COGRE_HEADER)
1366 	{
1367 		file->drop();
1368 		return false;
1369 	}
1370 
1371 	core::stringc skeletonVersion;
1372 	ChunkData head;
1373 	readString(file, head, skeletonVersion);
1374 	if (skeletonVersion != "[Serializer_v1.10]")
1375 	{
1376 		file->drop();
1377 		return false;
1378 	}
1379 
1380 	u16 bone=0;
1381 	f32 animationTotal=0.f;
1382 	while(file->getPos() < file->getSize())
1383 	{
1384 		ChunkData data;
1385 		readChunkData(file, data);
1386 
1387 		switch(data.header.id)
1388 		{
1389 		case COGRE_SKELETON:
1390 			{
1391 				Skeleton.Bones.push_back(OgreBone());
1392 				OgreBone& bone = Skeleton.Bones.getLast();
1393 				readString(file, data, bone.Name);
1394 				readShort(file, data, &bone.Handle);
1395 				readVector(file, data, bone.Position);
1396 				readQuaternion(file, data, bone.Orientation);
1397 #ifdef IRR_OGRE_LOADER_DEBUG
1398 				os::Printer::log("Bone", bone.Name+" ("+core::stringc(bone.Handle)+")", ELL_DEBUG);
1399 				os::Printer::log("Position", core::stringc(bone.Position.X)+" "+core::stringc(bone.Position.Y)+" "+core::stringc(bone.Position.Z), ELL_DEBUG);
1400 				os::Printer::log("Rotation quat", core::stringc(bone.Orientation.W)+" "+core::stringc(bone.Orientation.X)+" "+core::stringc(bone.Orientation.Y)+" "+core::stringc(bone.Orientation.Z), ELL_DEBUG);
1401 //				core::vector3df rot;
1402 //				bone.Orientation.toEuler(rot);
1403 //				rot *= core::RADTODEG;
1404 //				os::Printer::log("Rotation", core::stringc(rot.X)+" "+core::stringc(rot.Y)+" "+core::stringc(rot.Z));
1405 #endif
1406 				if (data.read<(data.header.length-bone.Name.size()))
1407 				{
1408 					readVector(file, data, bone.Scale);
1409 					bone.Scale.X *= -1.f;
1410 				}
1411 				else
1412 					bone.Scale=core::vector3df(1,1,1);
1413 				bone.Parent=0xffff;
1414 			}
1415 			break;
1416 		case COGRE_BONE_PARENT:
1417 			{
1418 				u16 parent;
1419 				readShort(file, data, &bone);
1420 				readShort(file, data, &parent);
1421 				if (bone<Skeleton.Bones.size() && parent<Skeleton.Bones.size())
1422 					Skeleton.Bones[bone].Parent=parent;
1423 			}
1424 			break;
1425 		case COGRE_ANIMATION:
1426 			{
1427 				if (Skeleton.Animations.size())
1428 					animationTotal+=Skeleton.Animations.getLast().Length;
1429 				Skeleton.Animations.push_back(OgreAnimation());
1430 				OgreAnimation& anim = Skeleton.Animations.getLast();
1431 				readString(file, data, anim.Name);
1432 				readFloat(file, data, &anim.Length);
1433 #ifdef IRR_OGRE_LOADER_DEBUG
1434 				os::Printer::log("Animation", anim.Name, ELL_DEBUG);
1435 				os::Printer::log("Length", core::stringc(anim.Length), ELL_DEBUG);
1436 #endif
1437 			}
1438 			break;
1439 		case COGRE_ANIMATION_TRACK:
1440 #ifdef IRR_OGRE_LOADER_DEBUG
1441 			os::Printer::log("for Bone ", core::stringc(bone), ELL_DEBUG);
1442 #endif
1443 				readShort(file, data, &bone); // store current bone
1444 			break;
1445 		case COGRE_ANIMATION_KEYFRAME:
1446 			{
1447 				Skeleton.Animations.getLast().Keyframes.push_back(OgreKeyframe());
1448 				OgreKeyframe& keyframe = Skeleton.Animations.getLast().Keyframes.getLast();
1449 				readFloat(file, data, &keyframe.Time);
1450 				keyframe.Time+=animationTotal;
1451 				readQuaternion(file, data, keyframe.Orientation);
1452 				readVector(file, data, keyframe.Position);
1453 				if (data.read<data.header.length)
1454 				{
1455 					readVector(file, data, keyframe.Scale);
1456 					keyframe.Scale.X *= -1.f;
1457 				}
1458 				else
1459 					keyframe.Scale=core::vector3df(1,1,1);
1460 				keyframe.BoneID=bone;
1461 			}
1462 			break;
1463 		case COGRE_ANIMATION_LINK:
1464 #ifdef IRR_OGRE_LOADER_DEBUG
1465 			os::Printer::log("Animation link", ELL_DEBUG);
1466 #endif
1467 			break;
1468 		default:
1469 			break;
1470 		}
1471 	}
1472 	file->drop();
1473 	return true;
1474 }
1475 
1476 
readChunkData(io::IReadFile * file,ChunkData & data)1477 void COgreMeshFileLoader::readChunkData(io::IReadFile* file, ChunkData& data)
1478 {
1479 	file->read(&data.header, sizeof(ChunkHeader));
1480 	if (SwapEndian)
1481 	{
1482 		data.header.id = os::Byteswap::byteswap(data.header.id);
1483 		data.header.length = os::Byteswap::byteswap(data.header.length);
1484 	}
1485 	data.read += sizeof(ChunkHeader);
1486 }
1487 
1488 
readString(io::IReadFile * file,ChunkData & data,core::stringc & out)1489 void COgreMeshFileLoader::readString(io::IReadFile* file, ChunkData& data, core::stringc& out)
1490 {
1491 	c8 c = 0;
1492 	out = "";
1493 
1494 	while (c!='\n')
1495 	{
1496 		file->read(&c, sizeof(c8));
1497 		if (c!='\n')
1498 			out.append(c);
1499 
1500 	}
1501 	data.read+=out.size()+1;
1502 }
1503 
1504 
readBool(io::IReadFile * file,ChunkData & data,bool & out)1505 void COgreMeshFileLoader::readBool(io::IReadFile* file, ChunkData& data, bool& out)
1506 {
1507 	// normal C type because we read a bit string
1508 	char c = 0;
1509 	file->read(&c, sizeof(char));
1510 	out=(c!=0);
1511 	++data.read;
1512 }
1513 
1514 
readInt(io::IReadFile * file,ChunkData & data,s32 * out,u32 num)1515 void COgreMeshFileLoader::readInt(io::IReadFile* file, ChunkData& data, s32* out, u32 num)
1516 {
1517 	// normal C type because we read a bit string
1518 	file->read(out, sizeof(int)*num);
1519 	if (SwapEndian)
1520 	{
1521 		for (u32 i=0; i<num; ++i)
1522 			out[i] = os::Byteswap::byteswap(out[i]);
1523 	}
1524 	data.read+=sizeof(int)*num;
1525 }
1526 
1527 
readShort(io::IReadFile * file,ChunkData & data,u16 * out,u32 num)1528 void COgreMeshFileLoader::readShort(io::IReadFile* file, ChunkData& data, u16* out, u32 num)
1529 {
1530 	// normal C type because we read a bit string
1531 	file->read(out, sizeof(short)*num);
1532 	if (SwapEndian)
1533 	{
1534 		for (u32 i=0; i<num; ++i)
1535 			out[i] = os::Byteswap::byteswap(out[i]);
1536 	}
1537 	data.read+=sizeof(short)*num;
1538 }
1539 
1540 
readFloat(io::IReadFile * file,ChunkData & data,f32 * out,u32 num)1541 void COgreMeshFileLoader::readFloat(io::IReadFile* file, ChunkData& data, f32* out, u32 num)
1542 {
1543 	// normal C type because we read a bit string
1544 	file->read(out, sizeof(float)*num);
1545 	if (SwapEndian)
1546 	{
1547 		for (u32 i=0; i<num; ++i)
1548 			out[i] = os::Byteswap::byteswap(out[i]);
1549 	}
1550 	data.read+=sizeof(float)*num;
1551 }
1552 
1553 
readVector(io::IReadFile * file,ChunkData & data,core::vector3df & out)1554 void COgreMeshFileLoader::readVector(io::IReadFile* file, ChunkData& data, core::vector3df& out)
1555 {
1556 	readFloat(file, data, &out.X);
1557 	readFloat(file, data, &out.Y);
1558 	readFloat(file, data, &out.Z);
1559 	out.X *= -1.f;
1560 }
1561 
1562 
readQuaternion(io::IReadFile * file,ChunkData & data,core::quaternion & out)1563 void COgreMeshFileLoader::readQuaternion(io::IReadFile* file, ChunkData& data, core::quaternion& out)
1564 {
1565 	readVector(file, data, *((core::vector3df*)&out.X));
1566 	readFloat(file, data, &out.W);
1567 }
1568 
1569 
clearMeshes()1570 void COgreMeshFileLoader::clearMeshes()
1571 {
1572 	for (u32 i=0; i<Meshes.size(); ++i)
1573 	{
1574 		for (int k=0; k<(int)Meshes[i].Geometry.Buffers.size(); ++k)
1575 			Meshes[i].Geometry.Buffers[k].Data.clear();
1576 
1577 		for (u32 j=0; j<Meshes[i].SubMeshes.size(); ++j)
1578 		{
1579 			for (int h=0; h<(int)Meshes[i].SubMeshes[j].Geometry.Buffers.size(); ++h)
1580 				Meshes[i].SubMeshes[j].Geometry.Buffers[h].Data.clear();
1581 		}
1582 	}
1583 
1584 	Meshes.clear();
1585 }
1586 
1587 
1588 } // end namespace scene
1589 } // end namespace irr
1590 
1591 #endif // _IRR_COMPILE_WITH_OGRE_LOADER_
1592 
1593