Retour
Version Originale

./aip/1.8aipmod/source/Irrlicht/CQ3LevelMesh.cpp :


// Copyright (C) 2002-2011 Nikolaus Gebhardt

// This file is part of the "Irrlicht Engine".

// For conditions of distribution and use, see copyright notice in irrlicht.h


#include "IrrCompileConfig.h"
#ifdef _IRR_COMPILE_WITH_BSP_LOADER_

#include "CQ3LevelMesh.h"
#include "ISceneManager.h"
#include "os.h"
#include "SMeshBufferLightMap.h"
#include "irrString.h"
#include "ILightSceneNode.h"
#include "IQ3Shader.h"
#include "IFileList.h"

//#define TJUNCTION_SOLVER_ROUND

//#define TJUNCTION_SOLVER_0125


namespace irr
{
namespace scene
{

	using namespace quake3;

//! constructor

CQ3LevelMesh::CQ3LevelMesh(io::IFileSystem* fs, scene::ISceneManager* smgr,
				const Q3LevelLoadParameter &loadParam)
	: LoadParam(loadParam), Textures(0), NumTextures(0), LightMaps(0), NumLightMaps(0),
	Vertices(0), NumVertices(0), Faces(0), NumFaces(0),	Planes(0), NumPlanes(0),
	Nodes(0), NumNodes(0), Leafs(0), NumLeafs(0), LeafFaces(0), NumLeafFaces(0),
	MeshVerts(0), NumMeshVerts(0), Brushes(0), NumBrushes(0), FileSystem(fs), SceneManager(smgr)
{
	#ifdef _DEBUG
	IReferenceCounted::setDebugName("CQ3LevelMesh");
	#endif

	for ( s32 i = 0; i!= E_Q3_MESH_SIZE; ++i )
	{
		Mesh[i] = 0;
	}

	Driver = smgr ? smgr->getVideoDriver() : 0;
	if (Driver)
		Driver->grab();

	if (FileSystem)
		FileSystem->grab();

	// load default shaders

	InitShader();
}


//! destructor

CQ3LevelMesh::~CQ3LevelMesh()
{
	cleanLoader ();

	if (Driver)
		Driver->drop();

	if (FileSystem)
		FileSystem->drop();

	for ( s32 i = 0; i!= E_Q3_MESH_SIZE; ++i )
	{
		if ( Mesh[i] )
		{
			Mesh[i]->drop();
			Mesh[i] = 0;
		}
	}

	ReleaseShader();
	ReleaseEntity();
}


//! loads a level from a .bsp-File. Also tries to load all needed textures. Returns true if successful.

bool CQ3LevelMesh::loadFile(io::IReadFile* file)
{
	if (!file)
		return false;

	LevelName = file->getFileName();

	file->read(&header, sizeof(tBSPHeader));

	#ifdef __BIG_ENDIAN__
		header.strID = os::Byteswap::byteswap(header.strID);
		header.version = os::Byteswap::byteswap(header.version);
	#endif

	if (	(header.strID != 0x50534249 ||			// IBSP

				(	header.version != 0x2e			// quake3

				&& header.version != 0x2f			// rtcw

				)
			)
			&&
			( header.strID != 0x50534252 || header.version != 1 ) // RBSP, starwars jedi, sof

		)
	{
		os::Printer::log("Could not load .bsp file, unknown header.", file->getFileName(), ELL_ERROR);
		return false;
	}

#if 0
	if ( header.strID == 0x50534252 )	// RBSP Raven

	{
		LoadParam.swapHeader = 1;
	}
#endif

	// now read lumps

	file->read(&Lumps[0], sizeof(tBSPLump)*kMaxLumps);

	s32 i;
	if ( LoadParam.swapHeader )
	{
		for ( i=0; i< kMaxLumps;++i)
		{
			Lumps[i].offset = os::Byteswap::byteswap(Lumps[i].offset);
			Lumps[i].length = os::Byteswap::byteswap(Lumps[i].length);
		}
	}

	for ( i = 0; i!= E_Q3_MESH_SIZE; ++i )
	{
		Mesh[i] = new SMesh();
	}

	ReleaseEntity();

	// load everything

	loadEntities(&Lumps[kEntities], file);			// load the entities

	loadTextures(&Lumps[kShaders], file);			// Load the textures

	loadLightmaps(&Lumps[kLightmaps], file);		// Load the lightmaps

	loadVerts(&Lumps[kVertices], file);				// Load the vertices

	loadFaces(&Lumps[kFaces], file);				// Load the faces

	loadPlanes(&Lumps[kPlanes], file);				// Load the Planes of the BSP

	loadNodes(&Lumps[kNodes], file);				// load the Nodes of the BSP

	loadLeafs(&Lumps[kLeafs], file);				// load the Leafs of the BSP

	loadLeafFaces(&Lumps[kLeafFaces], file);		// load the Faces of the Leafs of the BSP

	loadVisData(&Lumps[kVisData], file);			// load the visibility data of the clusters

	loadModels(&Lumps[kModels], file);				// load the models

	loadMeshVerts(&Lumps[kMeshVerts], file);		// load the mesh vertices

	loadBrushes(&Lumps[kBrushes], file);			// load the brushes of the BSP

	loadBrushSides(&Lumps[kBrushSides], file);		// load the brushsides of the BSP

	loadLeafBrushes(&Lumps[kLeafBrushes], file);	// load the brushes of the leaf

	loadFogs(&Lumps[kFogs], file );					// load the fogs


	loadTextures();
	constructMesh();
	solveTJunction();

	cleanMeshes();
	calcBoundingBoxes();
	cleanLoader();

	return true;
}

/*!
*/
void CQ3LevelMesh::cleanLoader ()
{
	delete [] Textures; Textures = 0;
	delete [] LightMaps; LightMaps = 0;
	delete [] Vertices; Vertices = 0;
	delete [] Faces; Faces = 0;
	delete [] Planes; Planes = 0;
	delete [] Nodes; Nodes = 0;
	delete [] Leafs; Leafs = 0;
	delete [] LeafFaces; LeafFaces = 0;
	delete [] MeshVerts; MeshVerts = 0;
	delete [] Brushes; Brushes = 0;

	Lightmap.clear();
	Tex.clear();
}

//! returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh.

u32 CQ3LevelMesh::getFrameCount() const
{
	return 1;
}


//! returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail. Note, that some Meshes will ignore the detail level.

IMesh* CQ3LevelMesh::getMesh(s32 frameInMs, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)
{
	return Mesh[frameInMs];
}


void CQ3LevelMesh::loadTextures(tBSPLump* l, io::IReadFile* file)
{
	NumTextures = l->length / sizeof(tBSPTexture);
	if ( !NumTextures )
		return;
	Textures = new tBSPTexture[NumTextures];

	file->seek(l->offset);
	file->read(Textures, l->length);

	if ( LoadParam.swapHeader )
	{
		for (s32 i=0;i<NumTextures;++i)
		{
			Textures[i].flags = os::Byteswap::byteswap(Textures[i].flags);
			Textures[i].contents = os::Byteswap::byteswap(Textures[i].contents);
			//os::Printer::log("Loaded texture", Textures[i].strName, ELL_INFORMATION);

		}
	}
}


void CQ3LevelMesh::loadLightmaps(tBSPLump* l, io::IReadFile* file)
{
	NumLightMaps = l->length / sizeof(tBSPLightmap);
	if ( !NumLightMaps )
		return;
	LightMaps = new tBSPLightmap[NumLightMaps];

	file->seek(l->offset);
	file->read(LightMaps, l->length);
}

/*!
*/
void CQ3LevelMesh::loadVerts(tBSPLump* l, io::IReadFile* file)
{
	NumVertices = l->length / sizeof(tBSPVertex);
	if ( !NumVertices )
		return;
	Vertices = new tBSPVertex[NumVertices];

	file->seek(l->offset);
	file->read(Vertices, l->length);

	if ( LoadParam.swapHeader )
	for (s32 i=0;i<NumVertices;i++)
	{
		Vertices[i].vPosition[0] = os::Byteswap::byteswap(Vertices[i].vPosition[0]);
		Vertices[i].vPosition[1] = os::Byteswap::byteswap(Vertices[i].vPosition[1]);
		Vertices[i].vPosition[2] = os::Byteswap::byteswap(Vertices[i].vPosition[2]);
		Vertices[i].vTextureCoord[0] = os::Byteswap::byteswap(Vertices[i].vTextureCoord[0]);
		Vertices[i].vTextureCoord[1] = os::Byteswap::byteswap(Vertices[i].vTextureCoord[1]);
		Vertices[i].vLightmapCoord[0] = os::Byteswap::byteswap(Vertices[i].vLightmapCoord[0]);
		Vertices[i].vLightmapCoord[1] = os::Byteswap::byteswap(Vertices[i].vLightmapCoord[1]);
		Vertices[i].vNormal[0] = os::Byteswap::byteswap(Vertices[i].vNormal[0]);
		Vertices[i].vNormal[1] = os::Byteswap::byteswap(Vertices[i].vNormal[1]);
		Vertices[i].vNormal[2] = os::Byteswap::byteswap(Vertices[i].vNormal[2]);
	}
}


/*!
*/
void CQ3LevelMesh::loadFaces(tBSPLump* l, io::IReadFile* file)
{
	NumFaces = l->length / sizeof(tBSPFace);
	if (!NumFaces)
		return;
	Faces = new tBSPFace[NumFaces];

	file->seek(l->offset);
	file->read(Faces, l->length);

	if ( LoadParam.swapHeader )
	{
		for ( s32 i=0;i<NumFaces;i++)
		{
			Faces[i].textureID = os::Byteswap::byteswap(Faces[i].textureID);
			Faces[i].fogNum = os::Byteswap::byteswap(Faces[i].fogNum);
			Faces[i].type = os::Byteswap::byteswap(Faces[i].type);
			Faces[i].vertexIndex = os::Byteswap::byteswap(Faces[i].vertexIndex);
			Faces[i].numOfVerts = os::Byteswap::byteswap(Faces[i].numOfVerts);
			Faces[i].meshVertIndex = os::Byteswap::byteswap(Faces[i].meshVertIndex);
			Faces[i].numMeshVerts = os::Byteswap::byteswap(Faces[i].numMeshVerts);
			Faces[i].lightmapID = os::Byteswap::byteswap(Faces[i].lightmapID);
			Faces[i].lMapCorner[0] = os::Byteswap::byteswap(Faces[i].lMapCorner[0]);
			Faces[i].lMapCorner[1] = os::Byteswap::byteswap(Faces[i].lMapCorner[1]);
			Faces[i].lMapSize[0] = os::Byteswap::byteswap(Faces[i].lMapSize[0]);
			Faces[i].lMapSize[1] = os::Byteswap::byteswap(Faces[i].lMapSize[1]);
			Faces[i].lMapPos[0] = os::Byteswap::byteswap(Faces[i].lMapPos[0]);
			Faces[i].lMapPos[1] = os::Byteswap::byteswap(Faces[i].lMapPos[1]);
			Faces[i].lMapPos[2] = os::Byteswap::byteswap(Faces[i].lMapPos[2]);
			Faces[i].lMapBitsets[0][0] = os::Byteswap::byteswap(Faces[i].lMapBitsets[0][0]);
			Faces[i].lMapBitsets[0][1] = os::Byteswap::byteswap(Faces[i].lMapBitsets[0][1]);
			Faces[i].lMapBitsets[0][2] = os::Byteswap::byteswap(Faces[i].lMapBitsets[0][2]);
			Faces[i].lMapBitsets[1][0] = os::Byteswap::byteswap(Faces[i].lMapBitsets[1][0]);
			Faces[i].lMapBitsets[1][1] = os::Byteswap::byteswap(Faces[i].lMapBitsets[1][1]);
			Faces[i].lMapBitsets[1][2] = os::Byteswap::byteswap(Faces[i].lMapBitsets[1][2]);
			Faces[i].vNormal[0] = os::Byteswap::byteswap(Faces[i].vNormal[0]);
			Faces[i].vNormal[1] = os::Byteswap::byteswap(Faces[i].vNormal[1]);
			Faces[i].vNormal[2] = os::Byteswap::byteswap(Faces[i].vNormal[2]);
			Faces[i].size[0] = os::Byteswap::byteswap(Faces[i].size[0]);
			Faces[i].size[1] = os::Byteswap::byteswap(Faces[i].size[1]);
		}
	}
}


/*!
*/
void CQ3LevelMesh::loadPlanes(tBSPLump* l, io::IReadFile* file)
{
	// ignore

}


/*!
*/
void CQ3LevelMesh::loadNodes(tBSPLump* l, io::IReadFile* file)
{
	// ignore

}


/*!
*/
void CQ3LevelMesh::loadLeafs(tBSPLump* l, io::IReadFile* file)
{
	// ignore

}


/*!
*/
void CQ3LevelMesh::loadLeafFaces(tBSPLump* l, io::IReadFile* file)
{
	// ignore

}


/*!
*/
void CQ3LevelMesh::loadVisData(tBSPLump* l, io::IReadFile* file)
{
	// ignore

}


/*!
*/
void CQ3LevelMesh::loadEntities(tBSPLump* l, io::IReadFile* file)
{
	core::array<u8> entity;
	entity.set_used( l->length + 2 );
	entity[l->length + 1 ] = 0;

	file->seek(l->offset);
	file->read( entity.pointer(), l->length);

	parser_parse( entity.pointer(), l->length, &CQ3LevelMesh::scriptcallback_entity );
}


/*!
	load fog brushes
*/
void CQ3LevelMesh::loadFogs(tBSPLump* l, io::IReadFile* file)
{
	u32 files = l->length / sizeof(tBSPFog);

	file->seek( l->offset );
	tBSPFog fog;
	const IShader *shader;
	STexShader t;
	for ( u32 i = 0; i!= files; ++i )
	{
		file->read( &fog, sizeof( fog ) );

		shader = getShader( fog.shader );
		t.Texture = 0;
		t.ShaderID = shader ? shader->ID : -1;

		FogMap.push_back ( t );
	}
}


/*!
	load models named in bsp
*/
void CQ3LevelMesh::loadModels(tBSPLump* l, io::IReadFile* file)
{
	u32 files = l->length / sizeof(tBSPModel);
	file->seek( l->offset );

	tBSPModel def;
	for ( u32 i = 0; i!= files; ++i )
	{
		file->read( &def, sizeof( def ) );
	}

}

/*!
*/
void CQ3LevelMesh::loadMeshVerts(tBSPLump* l, io::IReadFile* file)
{
	NumMeshVerts = l->length / sizeof(s32);
	if (!NumMeshVerts)
		return;
	MeshVerts = new s32[NumMeshVerts];

	file->seek(l->offset);
	file->read(MeshVerts, l->length);

	if ( LoadParam.swapHeader )
	{
		for (int i=0;i<NumMeshVerts;i++)
			MeshVerts[i] = os::Byteswap::byteswap(MeshVerts[i]);
	}
}

/*!
*/
void CQ3LevelMesh::loadBrushes(tBSPLump* l, io::IReadFile* file)
{
	// ignore

}

/*!
*/
void CQ3LevelMesh::loadBrushSides(tBSPLump* l, io::IReadFile* file)
{
	// ignore

}

/*!
*/
void CQ3LevelMesh::loadLeafBrushes(tBSPLump* l, io::IReadFile* file)
{
	// ignore

}

/*!
*/
inline bool isQ3WhiteSpace( const u8 symbol )
{
	return symbol == ' ' || symbol == '\t' || symbol == '\r';
}

/*!
*/
inline bool isQ3ValidName( const u8 symbol )
{
	return	(symbol >= 'a' && symbol <= 'z' ) ||
			(symbol >= 'A' && symbol <= 'Z' ) ||
			(symbol >= '0' && symbol <= '9' ) ||
			(symbol == '/' || symbol == '_' || symbol == '.' );
}

/*!
*/
void CQ3LevelMesh::parser_nextToken()
{
	u8 symbol;

	Parser.token = "";
	Parser.tokenresult = Q3_TOKEN_UNRESOLVED;

	// skip white space

	do
	{
		if ( Parser.index >= Parser.sourcesize )
		{
			Parser.tokenresult = Q3_TOKEN_EOF;
			return;
		}

		symbol = Parser.source [ Parser.index ];
		Parser.index += 1;
	} while ( isQ3WhiteSpace( symbol ) );

	// first symbol, one symbol

	switch ( symbol )
	{
		case 0:
			Parser.tokenresult = Q3_TOKEN_EOF;
			return;

		case '/':
			// comment or divide

			if ( Parser.index >= Parser.sourcesize )
			{
				Parser.tokenresult = Q3_TOKEN_EOF;
				return;
			}
			symbol = Parser.source [ Parser.index ];
			Parser.index += 1;
			if ( isQ3WhiteSpace( symbol ) )
			{
				Parser.tokenresult = Q3_TOKEN_MATH_DIVIDE;
				return;
			}
			else
			if ( symbol == '*' )
			{
				// C-style comment in quake?

			}
			else
			if ( symbol == '/' )
			{
				// skip to eol

				do
				{
					if ( Parser.index >= Parser.sourcesize )
					{
						Parser.tokenresult = Q3_TOKEN_EOF;
						return;
					}
					symbol = Parser.source [ Parser.index ];
					Parser.index += 1;
				} while ( symbol != '\n' );
				Parser.tokenresult = Q3_TOKEN_COMMENT;
				return;
			}
			// take /[name] as valid token..?!?!?. mhmm, maybe

			break;

		case '\n':
			Parser.tokenresult = Q3_TOKEN_EOL;
			return;
		case '{':
			Parser.tokenresult = Q3_TOKEN_START_LIST;
			return;
		case '}':
			Parser.tokenresult = Q3_TOKEN_END_LIST;
			return;

		case '"':
			// string literal

			do
			{
				if ( Parser.index >= Parser.sourcesize )
				{
					Parser.tokenresult = Q3_TOKEN_EOF;
					return;
				}
				symbol = Parser.source [ Parser.index ];
				Parser.index += 1;
				if ( symbol != '"' )
					Parser.token.append( symbol );
			} while ( symbol != '"' );
			Parser.tokenresult = Q3_TOKEN_ENTITY;
			return;
	}

	// user identity

	Parser.token.append( symbol );

	// continue till whitespace

	bool validName = true;
	do
	{
		if ( Parser.index >= Parser.sourcesize )
		{
			Parser.tokenresult = Q3_TOKEN_EOF;
			return;
		}
		symbol = Parser.source [ Parser.index ];

		validName = isQ3ValidName( symbol );
		if ( validName )
		{
			Parser.token.append( symbol );
			Parser.index += 1;
		}
	} while ( validName );

	Parser.tokenresult = Q3_TOKEN_TOKEN;
	return;
}


/*
	parse entity & shader
	calls callback on content in {}
*/
void CQ3LevelMesh::parser_parse( const void * data, const u32 size, CQ3LevelMesh::tParserCallback callback )
{
	Parser.source = static_cast<const c8*>(data);
	Parser.sourcesize = size;
	Parser.index = 0;

	SVarGroupList *groupList;

	s32 active;
	s32 last;

	SVariable entity ( "" );

	groupList = new SVarGroupList();

	groupList->VariableGroup.push_back( SVarGroup() );
	active = last = 0;

	do
	{
		parser_nextToken();

		switch ( Parser.tokenresult )
		{
			case Q3_TOKEN_START_LIST:
			{
				//stack = core::min_( stack + 1, 7 );


				groupList->VariableGroup.push_back( SVarGroup() );
				last = active;
				active = groupList->VariableGroup.size() - 1;
				entity.clear();
			}  break;

			// a unregisterd variable is finished

			case Q3_TOKEN_EOL:
			{
				if ( entity.isValid() )
				{
					groupList->VariableGroup[active].Variable.push_back( entity );
					entity.clear();
				}
			} break;

			case Q3_TOKEN_TOKEN:
			case Q3_TOKEN_ENTITY:
			{
				Parser.token.make_lower();

				// store content based on line-delemiter

				if ( 0 == entity.isValid() )
				{
					entity.name = Parser.token;
					entity.content = "";

				}
				else
				{
					if ( entity.content.size() )
					{
						entity.content += " ";
					}
					entity.content += Parser.token;
				}
			} break;

			case Q3_TOKEN_END_LIST:
			{
				//stack = core::max_( stack - 1, 0 );


				// close tag for first

				if ( active == 1 )
				{
					(this->*callback)( groupList, Q3_TOKEN_END_LIST );

					// new group

					groupList->drop();
					groupList = new SVarGroupList();
					groupList->VariableGroup.push_back( SVarGroup() );
					last = 0;
				}

				active = last;
				entity.clear();

			} break;

			default:
			break;
		}

	} while ( Parser.tokenresult != Q3_TOKEN_EOF );

	(this->*callback)( groupList, Q3_TOKEN_EOF );

	groupList->drop();
}


/*
	this loader applies only textures for stage 1 & 2
*/
s32 CQ3LevelMesh::setShaderFogMaterial( video::SMaterial &material, const tBSPFace * face ) const
{
	material.MaterialType = video::EMT_SOLID;
	material.Wireframe = false;
	material.Lighting = false;
	material.BackfaceCulling = false;
	material.setTexture(0, 0);
	material.setTexture(1, 0);
	material.setTexture(2, 0);
	material.setTexture(3, 0);
	material.ZBuffer = video::ECFN_LESSEQUAL;
	material.ZWriteEnable = false;
	material.MaterialTypeParam = 0.f;

	s32 shaderState = -1;

	if ( (u32) face->fogNum < FogMap.size() )
	{
		material.setTexture(0, FogMap [ face->fogNum ].Texture);
		shaderState = FogMap [ face->fogNum ].ShaderID;
	}

	return shaderState;

}
/*
	this loader applies only textures for stage 1 & 2
*/
s32 CQ3LevelMesh::setShaderMaterial( video::SMaterial &material, const tBSPFace * face ) const
{
	material.MaterialType = video::EMT_SOLID;
	material.Wireframe = false;
	material.Lighting = false;
	material.BackfaceCulling = true;
	material.setTexture(0, 0);
	material.setTexture(1, 0);
	material.setTexture(2, 0);
	material.setTexture(3, 0);
	material.ZBuffer = video::ECFN_LESSEQUAL;
	material.ZWriteEnable = true;
	material.MaterialTypeParam = 0.f;

	s32 shaderState = -1;

	if ( face->textureID >= 0 && face->textureID < (s32)Tex.size() )
	{
		material.setTexture(0, Tex [ face->textureID ].Texture);
		shaderState = Tex [ face->textureID ].ShaderID;
	}

	if ( face->lightmapID >= 0 && face->lightmapID < (s32)Lightmap.size() )
	{
		material.setTexture(1, Lightmap [ face->lightmapID ]);
		material.MaterialType = LoadParam.defaultLightMapMaterial;
	}

	// store shader ID

	material.MaterialTypeParam2 = (f32) shaderState;

	const IShader *shader = getShader(shaderState);
	if ( 0 == shader )
		return shaderState;

	return shaderState;

#if 0
	const SVarGroup *group;


	// generic

	group = shader->getGroup( 1 );
	if ( group )
	{
		material.BackfaceCulling = getCullingFunction( group->get( "cull" ) );

		if ( group->isDefined( "surfaceparm", "nolightmap" ) )
		{
			material.MaterialType = video::EMT_SOLID;
			material.setTexture(1, 0);
		}

	}

	// try to get the best of the 8 texture stages..


	// texture 1, texture 2

	u32 startPos;
	for ( s32 g = 2; g <= 3; ++g )
	{
		group = shader->getGroup( g );
		if ( 0 == group )
			continue;

		startPos = 0;

		if ( group->isDefined( "depthwrite" ) )
		{
			material.ZWriteEnable = true;
		}

		SBlendFunc blendfunc ( LoadParam.defaultModulate );
		getBlendFunc( group->get( "blendfunc" ), blendfunc );
		getBlendFunc( group->get( "alphafunc" ), blendfunc );

		if ( 0 == LoadParam.alpharef &&
			(	blendfunc.type == video::EMT_TRANSPARENT_ALPHA_CHANNEL ||
				blendfunc.type == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF
			)
			)
		{
			blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
			blendfunc.param0 = 0.f;
		}

		material.MaterialType = blendfunc.type;
		material.MaterialTypeParam = blendfunc.param0;

		// try if we can match better

		shaderState |= (material.MaterialType == video::EMT_SOLID ) ? 0x00020000 : 0;
	}

	//material.BackfaceCulling = false;


	if ( shader->VarGroup->VariableGroup.size() <= 4 )
	{
		shaderState |= 0x00010000;
	}

	material.MaterialTypeParam2 = (f32) shaderState;
	return shaderState;
#endif
}


/*!
*/
void CQ3LevelMesh::solveTJunction()
{
}

/*!
	constructs a mesh from the quake 3 level file.
*/
void CQ3LevelMesh::constructMesh()
{
	if ( LoadParam.verbose > 0 )
	{
		LoadParam.startTime = os::Timer::getRealTime();

		if ( LoadParam.verbose > 1 )
		{
			snprintf( buf, sizeof ( buf ),
				"quake3::constructMesh start to create %d faces, %d vertices,%d mesh vertices",
				NumFaces,
				NumVertices,
				NumMeshVerts
				);
			os::Printer::log(buf, ELL_INFORMATION);
		}

	}

	s32 i, j, k,s;

	s32 *index;

	video::S3DVertex2TCoords temp[3];
	video::SMaterial material;
	video::SMaterial material2;

	SToBuffer item [ E_Q3_MESH_SIZE ];
	u32 itemSize;

	for ( i=0; i < NumFaces; ++i)
	{
		const tBSPFace * face = Faces + i;

		s32 shaderState = setShaderMaterial( material, face );
		itemSize = 0;

		const IShader *shader = getShader(shaderState);

		if ( face->fogNum >= 0 )
		{
			setShaderFogMaterial ( material2, face );
			item[itemSize].index = E_Q3_MESH_FOG;
			item[itemSize].takeVertexColor = 1;
			itemSize += 1;
		}

		switch( face->type )
		{
			case 1: // normal polygons

			case 2: // patches

			case 3: // meshes

				{
					if ( 0 == shader )
					{
						if ( LoadParam.cleanUnResolvedMeshes || material.getTexture(0) )
						{
							item[itemSize].takeVertexColor = 1;
							item[itemSize].index = E_Q3_MESH_GEOMETRY;
							itemSize += 1;
						}
						else
						{
							item[itemSize].takeVertexColor = 1;
							item[itemSize].index = E_Q3_MESH_UNRESOLVED;
							itemSize += 1;
						}
					}
					else
					{
						item[itemSize].takeVertexColor = 1;
						item[itemSize].index = E_Q3_MESH_ITEMS;
						itemSize += 1;
					}

				} break;

			case 4: // billboards

				//item[itemSize].takeVertexColor = 1;

				//item[itemSize].index = E_Q3_MESH_ITEMS;

				//itemSize += 1;

				break;

		}

		for ( u32 g = 0; g != itemSize; ++g )
		{
			scene::SMeshBufferLightMap* buffer = 0;

			if ( item[g].index == E_Q3_MESH_GEOMETRY )
			{
				if ( 0 == item[g].takeVertexColor )
				{
					item[g].takeVertexColor = material.getTexture(0) == 0 || material.getTexture(1) == 0;
				}

				if (Faces[i].lightmapID < -1 || Faces[i].lightmapID > NumLightMaps-1)
				{
					Faces[i].lightmapID = -1;
				}

#if 0
				// there are lightmapsids and textureid with -1

				const s32 tmp_index = ((Faces[i].lightmapID+1) * (NumTextures+1)) + (Faces[i].textureID+1);
				buffer = (SMeshBufferLightMap*) Mesh[E_Q3_MESH_GEOMETRY]->getMeshBuffer(tmp_index);
				buffer->setHardwareMappingHint ( EHM_STATIC );
				buffer->getMaterial() = material;
#endif
			}

			// Construct a unique mesh for each shader or combine meshbuffers for same shader

			if ( 0 == buffer )
			{

				if ( LoadParam.mergeShaderBuffer == 1 )
				{
					// combine

					buffer = (SMeshBufferLightMap*) Mesh[ item[g].index ]->getMeshBuffer(
						item[g].index != E_Q3_MESH_FOG ? material : material2 );
				}

				// create a seperate mesh buffer

				if ( 0 == buffer )
				{
					buffer = new scene::SMeshBufferLightMap();
					Mesh[ item[g].index ]->addMeshBuffer( buffer );
					buffer->drop();
					buffer->getMaterial() = item[g].index != E_Q3_MESH_FOG ? material : material2;
					if ( item[g].index == E_Q3_MESH_GEOMETRY )
						buffer->setHardwareMappingHint ( EHM_STATIC );
				}
			}


			switch(Faces[i].type)
			{
				case 4: // billboards

					break;
				case 2: // patches

					createCurvedSurface_bezier(	buffer, i,
												LoadParam.patchTesselation,
												item[g].takeVertexColor
											);
					break;

				case 1: // normal polygons

				case 3: // mesh vertices

					index = MeshVerts + face->meshVertIndex;
					k = buffer->getVertexCount();

					// reallocate better if many small meshes are used

					s = buffer->getIndexCount()+face->numMeshVerts;
					if ( buffer->Indices.allocated_size () < (u32) s )
					{
						if (	buffer->Indices.allocated_size () > 0 &&
								face->numMeshVerts < 20 && NumFaces > 1000
							)
						{
							s = buffer->getIndexCount() + (NumFaces >> 3 * face->numMeshVerts );
						}
						buffer->Indices.reallocate( s);
					}

					for ( j = 0; j < face->numMeshVerts; ++j )
					{
						buffer->Indices.push_back( k + index [j] );
					}

					s = k+face->numOfVerts;
					if ( buffer->Vertices.allocated_size () < (u32) s )
					{
						if (	buffer->Indices.allocated_size () > 0 &&
								face->numOfVerts < 20 && NumFaces > 1000
							)
						{
							s = buffer->getIndexCount() + (NumFaces >> 3 * face->numOfVerts );
						}
						buffer->Vertices.reallocate( s);
					}
					for ( j = 0; j != face->numOfVerts; ++j )
					{
						copy( &temp[0], &Vertices[ j + face->vertexIndex ], item[g].takeVertexColor );
						buffer->Vertices.push_back( temp[0] );
					}
					break;

			} // end switch

		}
	}

	if ( LoadParam.verbose > 0 )
	{
		LoadParam.endTime = os::Timer::getRealTime();

		snprintf( buf, sizeof ( buf ),
			"quake3::constructMesh needed %04d ms to create %d faces, %d vertices,%d mesh vertices",
			LoadParam.endTime - LoadParam.startTime,
			NumFaces,
			NumVertices,
			NumMeshVerts
			);
		os::Printer::log(buf, ELL_INFORMATION);
	}

}


void CQ3LevelMesh::S3DVertex2TCoords_64::copy( video::S3DVertex2TCoords &dest ) const
{
#if defined (TJUNCTION_SOLVER_ROUND)
	dest.Pos.X = core::round_( (f32) Pos.X );
	dest.Pos.Y = core::round_( (f32) Pos.Y );
	dest.Pos.Z = core::round_( (f32) Pos.Z );
#elif defined (TJUNCTION_SOLVER_0125)
	dest.Pos.X = (f32) ( floor ( Pos.X * 8.f + 0.5 ) * 0.125 );
	dest.Pos.Y = (f32) ( floor ( Pos.Y * 8.f + 0.5 ) * 0.125 );
	dest.Pos.Z = (f32) ( floor ( Pos.Z * 8.f + 0.5 ) * 0.125 );
#else
	dest.Pos.X = (f32) Pos.X;
	dest.Pos.Y = (f32) Pos.Y;
	dest.Pos.Z = (f32) Pos.Z;
#endif

	dest.Normal.X = (f32) Normal.X;
	dest.Normal.Y = (f32) Normal.Y;
	dest.Normal.Z = (f32) Normal.Z;
	dest.Normal.normalize();

	dest.Color = Color.toSColor();

	dest.TCoords.X = (f32) TCoords.X;
	dest.TCoords.Y = (f32) TCoords.Y;

	dest.TCoords2.X = (f32) TCoords2.X;
	dest.TCoords2.Y = (f32) TCoords2.Y;
}


void CQ3LevelMesh::copy( S3DVertex2TCoords_64 * dest, const tBSPVertex * source, s32 vertexcolor ) const
{
#if defined (TJUNCTION_SOLVER_ROUND)
	dest->Pos.X = core::round_( source->vPosition[0] );
	dest->Pos.Y = core::round_( source->vPosition[2] );
	dest->Pos.Z = core::round_( source->vPosition[1] );
#elif defined (TJUNCTION_SOLVER_0125)
	dest->Pos.X = (f32) ( floor ( source->vPosition[0] * 8.f + 0.5 ) * 0.125 );
	dest->Pos.Y = (f32) ( floor ( source->vPosition[2] * 8.f + 0.5 ) * 0.125 );
	dest->Pos.Z = (f32) ( floor ( source->vPosition[1] * 8.f + 0.5 ) * 0.125 );
#else
	dest->Pos.X = source->vPosition[0];
	dest->Pos.Y = source->vPosition[2];
	dest->Pos.Z = source->vPosition[1];
#endif

	dest->Normal.X = source->vNormal[0];
	dest->Normal.Y = source->vNormal[2];
	dest->Normal.Z = source->vNormal[1];
	dest->Normal.normalize();

	dest->TCoords.X = source->vTextureCoord[0];
	dest->TCoords.Y = source->vTextureCoord[1];
	dest->TCoords2.X = source->vLightmapCoord[0];
	dest->TCoords2.Y = source->vLightmapCoord[1];

	if ( vertexcolor )
	{
		//u32 a = core::s32_min( source->color[3] * LoadParam.defaultModulate, 255 );

		u32 a = source->color[3];
		u32 r = core::s32_min( source->color[0] * LoadParam.defaultModulate, 255 );
		u32 g = core::s32_min( source->color[1] * LoadParam.defaultModulate, 255 );
		u32 b = core::s32_min( source->color[2] * LoadParam.defaultModulate, 255 );

		dest->Color.set(a * 1.f/255.f, r * 1.f/255.f,
				g * 1.f/255.f, b * 1.f/255.f);
	}
	else
	{
		dest->Color.set( 1.f, 1.f, 1.f, 1.f );
	}
}


inline void CQ3LevelMesh::copy( video::S3DVertex2TCoords * dest, const tBSPVertex * source, s32 vertexcolor ) const
{
#if defined (TJUNCTION_SOLVER_ROUND)
	dest->Pos.X = core::round_( source->vPosition[0] );
	dest->Pos.Y = core::round_( source->vPosition[2] );
	dest->Pos.Z = core::round_( source->vPosition[1] );
#elif defined (TJUNCTION_SOLVER_0125)
	dest->Pos.X = (f32) ( floor ( source->vPosition[0] * 8.f + 0.5 ) * 0.125 );
	dest->Pos.Y = (f32) ( floor ( source->vPosition[2] * 8.f + 0.5 ) * 0.125 );
	dest->Pos.Z = (f32) ( floor ( source->vPosition[1] * 8.f + 0.5 ) * 0.125 );
#else
	dest->Pos.X = source->vPosition[0];
	dest->Pos.Y = source->vPosition[2];
	dest->Pos.Z = source->vPosition[1];
#endif

	dest->Normal.X = source->vNormal[0];
	dest->Normal.Y = source->vNormal[2];
	dest->Normal.Z = source->vNormal[1];
	dest->Normal.normalize();

	dest->TCoords.X = source->vTextureCoord[0];
	dest->TCoords.Y = source->vTextureCoord[1];
	dest->TCoords2.X = source->vLightmapCoord[0];
	dest->TCoords2.Y = source->vLightmapCoord[1];

	if ( vertexcolor )
	{
		//u32 a = core::s32_min( source->color[3] * LoadParam.defaultModulate, 255 );

		u32 a = source->color[3];
		u32 r = core::s32_min( source->color[0] * LoadParam.defaultModulate, 255 );
		u32 g = core::s32_min( source->color[1] * LoadParam.defaultModulate, 255 );
		u32 b = core::s32_min( source->color[2] * LoadParam.defaultModulate, 255 );

		dest->Color.set(a << 24 | r << 16 | g << 8 | b);
	}
	else
	{
		dest->Color.set(0xFFFFFFFF);
	}
}


void CQ3LevelMesh::SBezier::tesselate( s32 level )
{
	//Calculate how many vertices across/down there are

	s32 j, k;

	column[0].set_used( level + 1 );
	column[1].set_used( level + 1 );
	column[2].set_used( level + 1 );

	const f64 w = 0.0 + (1.0 / (f64) level );

	//Tesselate along the columns

	for( j = 0; j <= level; ++j)
	{
		const f64 f = w * (f64) j;

		column[0][j] = control[0].getInterpolated_quadratic(control[3], control[6], f );
		column[1][j] = control[1].getInterpolated_quadratic(control[4], control[7], f );
		column[2][j] = control[2].getInterpolated_quadratic(control[5], control[8], f );
	}

	const u32 idx = Patch->Vertices.size();
	Patch->Vertices.reallocate(idx+level*level);
	//Tesselate across the rows to get final vertices

	video::S3DVertex2TCoords v;
	S3DVertex2TCoords_64 f;
	for( j = 0; j <= level; ++j)
	{
		for( k = 0; k <= level; ++k)
		{
			f = column[0][j].getInterpolated_quadratic(column[1][j], column[2][j], w * (f64) k);
			f.copy( v );
			Patch->Vertices.push_back( v );
		}
	}

	Patch->Indices.reallocate(Patch->Indices.size()+6*level*level);
	// connect

	for( j = 0; j < level; ++j)
	{
		for( k = 0; k < level; ++k)
		{
			const s32 inx = idx + ( k * ( level + 1 ) ) + j;

			Patch->Indices.push_back( inx + 0 );
			Patch->Indices.push_back( inx + (level + 1 ) + 0 );
			Patch->Indices.push_back( inx + (level + 1 ) + 1 );

			Patch->Indices.push_back( inx + 0 );
			Patch->Indices.push_back( inx + (level + 1 ) + 1 );
			Patch->Indices.push_back( inx + 1 );
		}
	}
}


/*!
	no subdivision
*/
void CQ3LevelMesh::createCurvedSurface_nosubdivision(SMeshBufferLightMap* meshBuffer,
					s32 faceIndex,
					s32 patchTesselation,
					s32 storevertexcolor)
{
	tBSPFace * face = &Faces[faceIndex];
	u32 j,k,m;

	// number of control points across & up

	const u32 controlWidth = face->size[0];
	const u32 controlHeight = face->size[1];
	if ( 0 == controlWidth || 0 == controlHeight )
		return;

	video::S3DVertex2TCoords v;

	m = meshBuffer->Vertices.size();
	meshBuffer->Vertices.reallocate(m+controlHeight * controlWidth);
	for ( j = 0; j!= controlHeight * controlWidth; ++j )
	{
		copy( &v, &Vertices [ face->vertexIndex + j ], storevertexcolor );
		meshBuffer->Vertices.push_back( v );
	}

	meshBuffer->Indices.reallocate(meshBuffer->Indices.size()+6*(controlHeight-1) * (controlWidth-1));
	for ( j = 0; j!= controlHeight - 1; ++j )
	{
		for ( k = 0; k!= controlWidth - 1; ++k )
		{
			meshBuffer->Indices.push_back( m + k + 0 );
			meshBuffer->Indices.push_back( m + k + controlWidth + 0 );
			meshBuffer->Indices.push_back( m + k + controlWidth + 1 );

			meshBuffer->Indices.push_back( m + k + 0 );
			meshBuffer->Indices.push_back( m + k + controlWidth + 1 );
			meshBuffer->Indices.push_back( m + k + 1 );
		}
		m += controlWidth;
	}
}


/*!
*/
void CQ3LevelMesh::createCurvedSurface_bezier(SMeshBufferLightMap* meshBuffer,
					s32 faceIndex,
					s32 patchTesselation,
					s32 storevertexcolor)
{

	tBSPFace * face = &Faces[faceIndex];
	u32 j,k;

	// number of control points across & up

	const u32 controlWidth = face->size[0];
	const u32 controlHeight = face->size[1];

	if ( 0 == controlWidth || 0 == controlHeight )
		return;

	// number of biquadratic patches

	const u32 biquadWidth = (controlWidth - 1)/2;
	const u32 biquadHeight = (controlHeight -1)/2;

	if ( LoadParam.verbose > 1 )
	{
		LoadParam.startTime = os::Timer::getRealTime();
	}

	// Create space for a temporary array of the patch's control points

	core::array<S3DVertex2TCoords_64> controlPoint;
	controlPoint.set_used( controlWidth * controlHeight );

	for( j = 0; j < controlPoint.size(); ++j)
	{
		copy( &controlPoint[j], &Vertices [ face->vertexIndex + j ], storevertexcolor );
	}

	// create a temporary patch

	Bezier.Patch = new scene::SMeshBufferLightMap();

	//Loop through the biquadratic patches

	for( j = 0; j < biquadHeight; ++j)
	{
		for( k = 0; k < biquadWidth; ++k)
		{
			// set up this patch

			const s32 inx = j*controlWidth*2 + k*2;

			// setup bezier control points for this patch

			Bezier.control[0] = controlPoint[ inx + 0];
			Bezier.control[1] = controlPoint[ inx + 1];
			Bezier.control[2] = controlPoint[ inx + 2];
			Bezier.control[3] = controlPoint[ inx + controlWidth + 0 ];
			Bezier.control[4] = controlPoint[ inx + controlWidth + 1 ];
			Bezier.control[5] = controlPoint[ inx + controlWidth + 2 ];
			Bezier.control[6] = controlPoint[ inx + controlWidth * 2 + 0];
			Bezier.control[7] = controlPoint[ inx + controlWidth * 2 + 1];
			Bezier.control[8] = controlPoint[ inx + controlWidth * 2 + 2];

			Bezier.tesselate( patchTesselation );
		}
	}

	// stitch together with existing geometry

	// TODO: only border needs to be checked

	const u32 bsize = Bezier.Patch->getVertexCount();
	const u32 msize = meshBuffer->getVertexCount();
/*
	for ( j = 0; j!= bsize; ++j )
	{
		const core::vector3df &v = Bezier.Patch->Vertices[j].Pos;

		for ( k = 0; k!= msize; ++k )
		{
			const core::vector3df &m = meshBuffer->Vertices[k].Pos;

			if ( !v.equals( m, tolerance ) )
				continue;

			meshBuffer->Vertices[k].Pos = v;
			//Bezier.Patch->Vertices[j].Pos = m;

		}
	}
*/

	// add Patch to meshbuffer

	meshBuffer->Vertices.reallocate(msize+bsize);
	for ( j = 0; j!= bsize; ++j )
	{
		meshBuffer->Vertices.push_back( Bezier.Patch->Vertices[j] );
	}

	// add indices to meshbuffer

	meshBuffer->Indices.reallocate(meshBuffer->getIndexCount()+Bezier.Patch->getIndexCount());
	for ( j = 0; j!= Bezier.Patch->getIndexCount(); ++j )
	{
		meshBuffer->Indices.push_back( msize + Bezier.Patch->Indices[j] );
	}

	delete Bezier.Patch;

	if ( LoadParam.verbose > 1 )
	{
		LoadParam.endTime = os::Timer::getRealTime();

		snprintf( buf, sizeof ( buf ),
			"quake3::createCurvedSurface_bezier needed %04d ms to create bezier patch.(%dx%d)",
			LoadParam.endTime - LoadParam.startTime,
			biquadWidth,
			biquadHeight
			);
		os::Printer::log(buf, ELL_INFORMATION);
	}

}



/*!
	Loads entities from file
*/
void CQ3LevelMesh::getConfiguration( io::IReadFile* file )
{
	tBSPLump l;
	l.offset = file->getPos();
	l.length = file->getSize ();

	core::array<u8> entity;
	entity.set_used( l.length + 2 );
	entity[l.length + 1 ] = 0;

	file->seek(l.offset);
	file->read( entity.pointer(), l.length);

	parser_parse( entity.pointer(), l.length, &CQ3LevelMesh::scriptcallback_config );

	if ( Entity.size () )
		Entity.getLast().name = file->getFileName();
}


//! get's an interface to the entities

tQ3EntityList & CQ3LevelMesh::getEntityList()
{
//	Entity.sort();

	return Entity;
}


/*!
*/
const IShader * CQ3LevelMesh::getShader(u32 index) const
{
	index &= 0xFFFF;

	if ( index < Shader.size() )
	{
		return &Shader[index];
	}

	return 0;
}


/*!
	loads the shader definition
*/
const IShader* CQ3LevelMesh::getShader( const c8 * filename, bool fileNameIsValid )
{
	core::stringc searchName ( filename );

	IShader search;
	search.name = searchName;
	search.name.replace( '\\', '/' );
	search.name.make_lower();


	core::stringc message;
	s32 index;

	//! is Shader already in cache?

	index = Shader.linear_search( search );
	if ( index >= 0 )
	{
		if ( LoadParam.verbose > 1 )
		{
			message = searchName + " found " + Shader[index].name;
			os::Printer::log("quake3:getShader", message.c_str(), ELL_INFORMATION);
		}

		return &Shader[index];
	}

	io::path loadFile;

	if ( !fileNameIsValid )
	{
		// extract the shader name from the last path component in filename

		// "scripts/[name].shader"

		core::stringc cut( search.name );

		s32 end = cut.findLast( '/' );
		s32 start = cut.findLast( '/', end - 1 );

		loadFile = LoadParam.scriptDir;
		loadFile.append( cut.subString( start, end - start ) );
		loadFile.append( ".shader" );
	}
	else
	{
		loadFile = search.name;
	}

	// already loaded the file ?

	index = ShaderFile.binary_search( loadFile );
	if ( index >= 0 )
		return 0;

	// add file to loaded files

	ShaderFile.push_back( loadFile );

	if ( !FileSystem->existFile( loadFile.c_str() ) )
	{
		if ( LoadParam.verbose > 1 )
		{
			message = loadFile + " for " + searchName + " failed ";
			os::Printer::log("quake3:getShader", message.c_str(), ELL_INFORMATION);
		}
		return 0;
	}

	if ( LoadParam.verbose )
	{
		message = loadFile + " for " + searchName;
		os::Printer::log("quake3:getShader Load shader", message.c_str(), ELL_INFORMATION);
	}


	io::IReadFile *file = FileSystem->createAndOpenFile( loadFile.c_str() );
	if ( file )
	{
		getShader ( file );
		file->drop ();
	}


	// search again

	index = Shader.linear_search( search );
	return index >= 0 ? &Shader[index] : 0;
}

/*!
	loads the shader definition
*/
void CQ3LevelMesh::getShader( io::IReadFile* file )
{
	if ( 0 == file )
		return;

	// load script

	core::array<u8> script;
	const long len = file->getSize();

	script.set_used( len + 2 );

	file->seek( 0 );
	file->read( script.pointer(), len );
	script[ len + 1 ] = 0;

	// start a parser instance

	parser_parse( script.pointer(), len, &CQ3LevelMesh::scriptcallback_shader );
}


//! adding default shaders

void CQ3LevelMesh::InitShader()
{
	ReleaseShader();

	IShader element;

	SVarGroup group;
	SVariable variable ( "noshader" );

	group.Variable.push_back( variable );

	element.VarGroup = new SVarGroupList();
	element.VarGroup->VariableGroup.push_back( group );
	element.VarGroup->VariableGroup.push_back( SVarGroup() );
	element.name = element.VarGroup->VariableGroup[0].Variable[0].name;
	element.ID = Shader.size();
	Shader.push_back( element );

	if ( LoadParam.loadAllShaders )
	{
		io::EFileSystemType current = FileSystem->setFileListSystem ( io::FILESYSTEM_VIRTUAL );
		io::path save = FileSystem->getWorkingDirectory();

		io::path newDir;
		newDir = "/";
		newDir += LoadParam.scriptDir;
		newDir += "/";
		FileSystem->changeWorkingDirectoryTo ( newDir.c_str() );

		core::stringc s;
		io::IFileList *fileList = FileSystem->createFileList ();
		for (u32 i=0; i< fileList->getFileCount(); ++i)
		{
			s = fileList->getFullFileName(i);
			if ( s.find ( ".shader" ) >= 0 )
			{
				if ( 0 == LoadParam.loadSkyShader && s.find ( "sky.shader" ) >= 0 )
				{
				}
				else
				{
					getShader ( s.c_str () );
				}
			}
		}
		fileList->drop ();

		FileSystem->changeWorkingDirectoryTo ( save );
		FileSystem->setFileListSystem ( current );
	}
}


//! script callback for shaders

//! i'm having troubles with the reference counting, during callback.. resorting..

void CQ3LevelMesh::ReleaseShader()
{
	for ( u32 i = 0; i!= Shader.size(); ++i )
	{
		Shader[i].VarGroup->drop();
	}
	Shader.clear();
	ShaderFile.clear();
}


/*!
*/
void CQ3LevelMesh::ReleaseEntity()
{
	for ( u32 i = 0; i!= Entity.size(); ++i )
	{
		Entity[i].VarGroup->drop();
	}
	Entity.clear();
}


// config in simple (quake3) and advanced style

void CQ3LevelMesh::scriptcallback_config( SVarGroupList *& grouplist, eToken token )
{
	IShader element;

	if ( token == Q3_TOKEN_END_LIST )
	{
		if ( 0 == grouplist->VariableGroup[0].Variable.size() )
			return;

		element.name = grouplist->VariableGroup[0].Variable[0].name;
	}
	else
	{
		if ( grouplist->VariableGroup.size() != 2 )
			return;

		element.name = "configuration";
	}

	grouplist->grab();
	element.VarGroup = grouplist;
	element.ID = Entity.size();
	Entity.push_back( element );
}


// entity only has only one valid level.. and no assoziative name..

void CQ3LevelMesh::scriptcallback_entity( SVarGroupList *& grouplist, eToken token )
{
	if ( token != Q3_TOKEN_END_LIST || grouplist->VariableGroup.size() != 2 )
		return;

	grouplist->grab();

	IEntity element;
	element.VarGroup = grouplist;
	element.ID = Entity.size();
	element.name = grouplist->VariableGroup[1].get( "classname" );


	Entity.push_back( element );
}


//!. script callback for shaders

void CQ3LevelMesh::scriptcallback_shader( SVarGroupList *& grouplist,eToken token )
{
	if ( token != Q3_TOKEN_END_LIST || grouplist->VariableGroup[0].Variable.size()==0)
		return;


	IShader element;

	grouplist->grab();
	element.VarGroup = grouplist;
	element.name = element.VarGroup->VariableGroup[0].Variable[0].name;
	element.ID = Shader.size();
/*
	core::stringc s;
	dumpShader ( s, &element );
	printf ( s.c_str () );
*/
	Shader.push_back( element );
}


/*!
	delete all buffers without geometry in it.
*/
void CQ3LevelMesh::cleanMeshes()
{
	if ( 0 == LoadParam.cleanUnResolvedMeshes )
		return;

	// delete all buffers without geometry in it.

	u32 run = 0;
	u32 remove = 0;

	irr::scene::SMesh *m;
	IMeshBuffer *b;
	for ( u32 g = 0; g < E_Q3_MESH_SIZE; ++g )
	{
		bool texture0important = ( g == 0 );

		run = 0;
		remove = 0;
		m = Mesh[g];
		if ( LoadParam.verbose > 0 )
		{
			LoadParam.startTime = os::Timer::getRealTime();
			if ( LoadParam.verbose > 1 )
			{
				snprintf( buf, sizeof ( buf ),
					"quake3::cleanMeshes%d start for %d meshes",
					g,
					m->MeshBuffers.size()
					);
				os::Printer::log(buf, ELL_INFORMATION);
			}
		}

		u32 i = 0;
		s32 blockstart = -1;
		s32 blockcount;

		while( i < m->MeshBuffers.size())
		{
			run += 1;

			b = m->MeshBuffers[i];

			if ( b->getVertexCount() == 0 || b->getIndexCount() == 0 ||
				( texture0important && b->getMaterial().getTexture(0) == 0 )
				)
			{
				if ( blockstart < 0 )
				{
					blockstart = i;
					blockcount = 0;
				}
				blockcount += 1;
				i += 1;

				// delete Meshbuffer

				i -= 1;
				remove += 1;
				b->drop();
				m->MeshBuffers.erase(i);
			}
			else
			{
				// clean blockwise

				if ( blockstart >= 0 )
				{
					if ( LoadParam.verbose > 1 )
					{
						snprintf( buf, sizeof ( buf ),
							"quake3::cleanMeshes%d cleaning mesh %d %d size",
							g,
							blockstart,
							blockcount
							);
						os::Printer::log(buf, ELL_INFORMATION);
					}
					blockstart = -1;
				}
				i += 1;
			}
		}

		if ( LoadParam.verbose > 0 )
		{
			LoadParam.endTime = os::Timer::getRealTime();
			snprintf( buf, sizeof ( buf ),
				"quake3::cleanMeshes%d needed %04d ms to clean %d of %d meshes",
				g,
				LoadParam.endTime - LoadParam.startTime,
				remove,
				run
				);
			os::Printer::log(buf, ELL_INFORMATION);
		}
	}
}


// recalculate bounding boxes

void CQ3LevelMesh::calcBoundingBoxes()
{
	if ( LoadParam.verbose > 0 )
	{
		LoadParam.startTime = os::Timer::getRealTime();

		if ( LoadParam.verbose > 1 )
		{
			snprintf( buf, sizeof ( buf ),
				"quake3::calcBoundingBoxes start create %d textures and %d lightmaps",
				NumTextures,
				NumLightMaps
				);
			os::Printer::log(buf, ELL_INFORMATION);
		}
	}

	// create bounding box

	for ( u32 g = 0; g != E_Q3_MESH_SIZE; ++g )
	{
		for ( u32 j=0; j < Mesh[g]->MeshBuffers.size(); ++j)
		{
			((SMeshBufferLightMap*)Mesh[g]->MeshBuffers[j])->recalculateBoundingBox();
		}

		Mesh[g]->recalculateBoundingBox();
		// Mesh[0] is the main bbox

		if (g!=0)
			Mesh[0]->BoundingBox.addInternalBox(Mesh[g]->getBoundingBox());
	}

	if ( LoadParam.verbose > 0 )
	{
		LoadParam.endTime = os::Timer::getRealTime();

		snprintf( buf, sizeof ( buf ),
			"quake3::calcBoundingBoxes needed %04d ms to create %d textures and %d lightmaps",
			LoadParam.endTime - LoadParam.startTime,
			NumTextures,
			NumLightMaps
			);
		os::Printer::log( buf, ELL_INFORMATION);
	}
}


//! loads the textures

void CQ3LevelMesh::loadTextures()
{
	if (!Driver)
		return;

	if ( LoadParam.verbose > 0 )
	{
		LoadParam.startTime = os::Timer::getRealTime();

		if ( LoadParam.verbose > 1 )
		{
			snprintf( buf, sizeof ( buf ),
				"quake3::loadTextures start create %d textures and %d lightmaps",
				NumTextures,
				NumLightMaps
				);
			os::Printer::log( buf, ELL_INFORMATION);
		}
	}

	c8 lightmapname[255];
	s32 t;

	// load lightmaps.

	Lightmap.set_used(NumLightMaps);

/*
	bool oldMipMapState = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
	Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
*/
	core::dimension2d<u32> lmapsize(128,128);

	video::IImage* lmapImg;
	for ( t = 0; t < NumLightMaps ; ++t)
	{
		sprintf(lightmapname, "%s.lightmap.%d", LevelName.c_str(), t);

		// lightmap is a CTexture::R8G8B8 format

		lmapImg = Driver->createImageFromData(
			video::ECF_R8G8B8, lmapsize,
			LightMaps[t].imageBits, false, true );

		Lightmap[t] = Driver->addTexture( lightmapname, lmapImg );
		lmapImg->drop();
	}

//	Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, oldMipMapState);


	// load textures

	Tex.set_used( NumTextures );

	const IShader* shader;

	core::stringc list;
	io::path check;
	tTexArray textureArray;

	// pre-load shaders

	for ( t=0; t< NumTextures; ++t)
	{
		shader = getShader(Textures[t].strName, false);
	}

	for ( t=0; t< NumTextures; ++t)
	{
		Tex[t].ShaderID = -1;
		Tex[t].Texture = 0;

		list = "";

		// get a shader ( if one exists )

		shader = getShader( Textures[t].strName, false);
		if ( shader )
		{
			Tex[t].ShaderID = shader->ID;

			// if texture name == stage1 Texture map

			const SVarGroup * group;

			group = shader->getGroup( 2 );
			if ( group )
			{
				if ( core::cutFilenameExtension( check, group->get( "map" ) ) == Textures[t].strName )
				{
					list += check;
				}
				else
				if ( check == "$lightmap" )
				{
					// we check if lightmap is in stage 1 and texture in stage 2

					group = shader->getGroup( 3 );
					if ( group )
						list += group->get( "map" );
				}
			}
		}
		else
		{
			// no shader, take it

			list += Textures[t].strName;
		}

		u32 pos = 0;
		getTextures( textureArray, list, pos, FileSystem, Driver );

		Tex[t].Texture = textureArray[0];
	}

	if ( LoadParam.verbose > 0 )
	{
		LoadParam.endTime = os::Timer::getRealTime();

		snprintf( buf, sizeof ( buf ),
			"quake3::loadTextures needed %04d ms to create %d textures and %d lightmaps",
			LoadParam.endTime - LoadParam.startTime,
			NumTextures,
			NumLightMaps
			);
		os::Printer::log( buf, ELL_INFORMATION);
	}
}


//! Returns an axis aligned bounding box of the mesh.

const core::aabbox3d<f32>& CQ3LevelMesh::getBoundingBox() const
{
	return Mesh[0]->getBoundingBox();
}


void CQ3LevelMesh::setBoundingBox(const core::aabbox3df& box)
{
	Mesh[0]->setBoundingBox(box);
}


//! Returns the type of the animated mesh.

E_ANIMATED_MESH_TYPE CQ3LevelMesh::getMeshType() const
{
	return scene::EAMT_BSP;
}

} // end namespace scene

} // end namespace irr


#endif // _IRR_COMPILE_WITH_BSP_LOADER_

Options Liens officiels Caractéristiques Statistiques Communauté
Corrections
irrlicht
irrklang
irredit
irrxml
xhtml 1.0
css 2.1
Propulsé par FluxBB
Traduit par FluxBB.fr
881 membres
1427 sujets
11117 messages
Dernier membre inscrit: Bidule
16 invités en ligne
Aucun membre connecté
RSS Feed