#include "Scene.h"
#include "vObject.h"
//#include "D3D11Effect.h"
#include "TileManager.h"

//#define SURF_MAX_PATCHLEVEL = 14;
const DWORD NOTILE = (DWORD)-1;			//no tile flag.

static float TEX2_MULTIPLIER = 4.0f;	// microtexture multiplier

struct IDXLIST {
	DWORD idx, ofs;
};

// Some debugging parameters
int tmissing = 0;

TILEVERTEX _TVTX( D3DXVECTOR3 pos, D3DXVECTOR3 norm, float tu0, float tv0, float tu1, float tv1 ) {
	TILEVERTEX out;
	out.pos = pos;
	out.norm = norm;
	out.tex.x = tu0;
	out.tex.y = tv0;
	out.mtex.x = tu1;
	out.mtex.y = tv1;	
	return out;
}

int TileManager::patchidx[9] = { 0, 1, 2, 3, 5, 13, 37, 137, 501 };
TileBuffer *TileManager::tbuf = NULL;


DWORD TileManager::maxbaselvl = 8;
const D3D11Config *TileManager::cfg = NULL;
D3DXMATRIX TileManager::RSouth;

bool TileManager::bSpecular = true;
bool TileManager::bLights = true;
bool TileManager::bRipple = true;
bool TileManager::bPreloadTiles = false;

/*		Tile meshes.		*/
TILEMESH TileManager::TPL_1;
TILEMESH TileManager::TPL_2;
TILEMESH TileManager::TPL_3;
TILEMESH TileManager::TPL_4[2];
TILEMESH TileManager::TPL_5;
TILEMESH TileManager::TPL_6[2];
TILEMESH TileManager::TPL_7[4];
TILEMESH TileManager::TPL_8[8];
TILEMESH TileManager::TPL_9[16];
TILEMESH TileManager::TPL_10[32];
TILEMESH TileManager::TPL_11[64];
TILEMESH TileManager::TPL_12[128];
TILEMESH TileManager::TPL_13[256];
TILEMESH TileManager::TPL_14[512];

TILEMESH *TileManager::TPL[15] = { NULL, &TPL_1, &TPL_2, &TPL_3, TPL_4, &TPL_5, TPL_6, TPL_7, TPL_8, TPL_9, TPL_10, TPL_11, TPL_12, TPL_13, TPL_14 };

int TileManager::NLAT[9] = { 0, 1, 1, 1, 1, 1, 2, 4, 8 };
int TileManager::NLNG5[1] = { 4 };
int TileManager::NLNG6[2] = { 8, 4 };
int TileManager::NLNG7[4] = { 16, 16, 12, 6 };
int TileManager::NLNG8[8] = { 32, 32, 30, 28, 24, 18, 12, 6 };
int *TileManager::NLNG[9] = { NULL, NULL, NULL, NULL, NULL, NLNG5, NLNG6, NLNG7, NLNG8 };

//=============================================================================
//				Non-static:
//=============================================================================
#pragma region TileManager, ~TileManager, Render, ProcessTile and misc functions.
TileManager::TileManager( vPlanet *_planet ) : D3D11Effect()	{
	planet = _planet;
	obj = planet->obj;
	char name[256];
	oapiGetObjectName( obj, name, 256 );
	DWORD len = strlen(name)+2;
	objname = new char [len];
	strcpy_s( objname, len, name );

	ntex = 0;
	nhitex = 0;
	nmask = 0;
	nhispec = 0;
	maxlvl = maxbaselvl = 0;

	microtex = 0;
	microlvl = 0.0;
	tiledesc = NULL;
	texbuf = specbuf = NULL;
	cAmbient = D3DXCOLOR( 0.0f, 0.0f, 0.0f, 0.0f );
	bPreloadTiles = cfg->bPreloadTiles;
//	if( bPreloadTiles )
//		LogAlw("PreLoad Highres textures");

	Planet_tile_fpath = Planet_tile_lmask_fpath = NULL;
	RP = &RP0;
//!!!!!
	atmc = oapiGetPlanetAtmConstants( obj );
}

TileManager::~TileManager() {
	DWORD j;
	if( ntex && texbuf ) {
		for( j = 0; j < ntex; j++ ) {
			ID3D11Texture2D *tex = NULL;
			IDXGISurface1 *surf = NULL;
			texbuf[j]->GetResource( (ID3D11Resource**)&tex );
			tex->QueryInterface( __uuidof(IDXGISurface1), (void**)&surf );
			texbuf[j]->Release();
			tex->Release();
			surf->Release();
		}
		delete [ ] texbuf;
	}
	if( nmask && specbuf ) {
		for( j = 0; j < nmask; j++ ) {
			ID3D11Texture2D *tex = NULL;
			specbuf[j]->GetResource( (ID3D11Resource**)&tex );
			specbuf[j]->Release();
			tex->Release();
		}
	//		specbuf[j]->Release();
		delete [ ] specbuf;
	}

	if( objname )
		delete [ ] objname;
	if( Planet_tile_fpath )
		delete [ ] Planet_tile_fpath;
	if( Planet_tile_lmask_fpath )
		delete [ ] Planet_tile_lmask_fpath;
}

//==========================================================
//				Render
//==========================================================

VECTOR3 TileManager::GetTileCenter( int hemisphere, int ilat, int nlat, int ilng, int nlng ) {
/*	Returns central tile vector.
	*/
	double cntlat = PI05 * ((double)ilat+0.5)/(double)nlat,			slat = sin(cntlat), clat = cos(cntlat);
	double cntlng = PI2 * ((double)ilng+0.5)/(double)nlng + PI,		slng = sin(cntlng), clng = cos(cntlng);
	if( hemisphere )
		return _V( clat*clng, -slat, -clat*slng );
	else
		return _V( clat*clng,  slat,  clat*slng );
}

void TileManager::SetWorldMatrix( int ilat, int nlat, int ilng, int nlng ) {
/*	Sets world matrix.
	*/
void MatrixRotY( D3DXMATRIX *out, float angle );	//function prototype

	D3DXMATRIX rtile, wtrans;
	double lng = PI2*(double)ilng/(double)nlng + PI;

	MatrixRotY( &rtile, (float)lng );

	if( nlat > 8 ) {
		double lat = PI05*(double)ilat/(double)nlat;
		double s = RP->objsize;
		double dx = s*cos(lng)*cos(lat);
		double dy = s*sin(lat);
		double dz = s*sin(lng)*cos(lat);
		RP->mWorld_tmp._41 = (float)(dx*RP->grot.m11 + dy*RP->grot.m12 + dz*RP->grot.m13 + RP->cpos.x);
		RP->mWorld_tmp._42 = (float)(dx*RP->grot.m21 + dy*RP->grot.m22 + dz*RP->grot.m23 + RP->cpos.y);
		RP->mWorld_tmp._43 = (float)(dx*RP->grot.m31 + dy*RP->grot.m32 + dz*RP->grot.m33 + RP->cpos.z);
		D3DXMatrixMultiply( &mWorld, &rtile, &RP->mWorld_tmp );
	}
	else
		D3DXMatrixMultiply( &mWorld, &rtile, &RP->mWorld );
}

void MatrixRotY( D3DXMATRIX *out, float angle ) {
	double sinr = sin( angle ), cosr = cos( angle );
	ZeroMemory( out, 64 );
	out->_11 = out->_33 = (float)cosr;
	out->_31 = -( out->_13 = (float)sinr );
	out->_22 = out->_44 = 1.0f;
}

void TileManager::TileExtents( int hemisphere, int ilat, int nlat, int ilng, int nlng, double &lat1, double &lat2, double &lng1, double &lng2 ) {
	lat1 = PI05 * (double)ilat/(double)nlat;
	lat2 = lat1 + PI05/(double)nlat;
	lng1 = PI2 * (double)ilng/(double)nlng + PI;
	lng2 = lng1 + PI2/nlng;

	if( hemisphere ) {
		double tmp = lat1;
		lat1 = -lat2;
		lat2 = -tmp;

		tmp = lng1;
		lng1 = -lng2;
		lng2 = -tmp;

		if( lng2 < 0 )
			lng1 += PI2, lng2 += PI2;
	}
}

bool TileManager::IsTileInView( int lvl, int ilat, float scale ) {
	TILEMESH &mesh = TPL[lvl][ilat];
	float rad = mesh.bsRad*scale;
	D3DXVECTOR3 vP;
	D3DXVec3TransformCoord( &vP, &mesh.bsCnt, &mWorld );

	float dist = D3DXVec3Length( &vP );
	if( (dist - rad) > RP->horzdist )
		return false;
	return gc->GetScene()->IsVisibleInCamera( &vP, rad );	//!
}

void TileManager::SaveParams( D3DXMATRIX *wmat, double scale, int level, double viewap, bool bfog ) {
	VECTOR3 gpos;
	D3DXMATRIX imat;

	RP->dist_scale = scale;
	level = min( level, maxlvl );

	RP->mWorld = RP->mWorld_tmp = *wmat;

//	memcpy( &RP->mWorld, &wmat, sizeof(D3DXMATRIX) );
//	memcpy( &RP->mWorld_tmp, &RP->mWorld, sizeof(D3DXMATRIX) );
	D3DMAT_MatrixInvert( &imat, wmat );
	RP->cdir = _V( imat._41, imat._42, imat._43 );
	normalise( RP->cdir );
	RP->cpos = planet->GetCPos()*scale;//*
	RP->bFog = bfog;

	oapiGetRotationMatrix( obj, &RP->grot );
	RP->grot *= scale;
	oapiGetGlobalPos( obj, &gpos );

	RP->bCockpit = ( oapiCameraMode() == CAM_COCKPIT );
	RP->objsize = oapiGetSize( obj );
	RP->cdist = planet->GetCDist()/planet->GetRad();
	RP->viewap = (viewap ? viewap : acos(1.0/max( 1.0, RP->cdist ) ) );
	RP->sdir = tmul( RP->grot, -gpos );
	RP->horzdist = sqrt( RP->cdist*RP->cdist - 1.0 )*RP->objsize;
	normalise( RP->sdir );

	// limit resolution for fast camera movements.
	double limitstep, cstep = acos( dotp( RP->cdir, pcdir ) );
	short maxlevel = SURF_MAX_PATCHLEVEL;
	static double limitstep0 = 5.12*pow( 2.0 , -(double)SURF_MAX_PATCHLEVEL );
	for( limitstep = limitstep0; cstep > limitstep && maxlevel > 5; limitstep *= 2.0 )
		maxlevel--;
	level = min( level, maxlevel );

	RP->tgtlvl = level;
	oapiLocalToEqu( obj, RP->cdir, &RP->clng, &RP->clat, &RP->crad );
}

void TileManager::Render(/* D3DXMATRIX &wmat, double scale, int level, double viewap, bool bfog */) {
/*	VECTOR3 gpos;
	D3DXMATRIX imat;

	RP.dist_scale = scale;
	level = min( level, maxlvl );
	memcpy( &RP.mWorld, &wmat, 64 );
	memcpy( &RP.mWorld_tmp, &RP.mWorld, 64 );
	D3DMAT_MatrixInvert( &imat, &wmat );
	RP.cdir = _V( imat._41, imat._42, imat._43 );
	normalise( RP.cdir );
	RP.cpos = planet->GetCPos()*scale;//*
	RP.bFog = bfog;

//	oapiGetRotationMatrix( obj, &RP.grot );
	RP.grot *= scale;
//	oapiGetGlobalPos( obj, &gpos );

	RP.bCockpit = ( oapiCameraMode() == CAM_COCKPIT );
	RP.objsize = oapiGetSize( obj );
	RP.cdist = planet->CamDist2()/planet->rad;
	RP.viewap = (viewap ? viewap : acos(1.0/max( 1.0, RP.cdist ) ) );
	RP.sdir = tmul( RP.grot, -gpos );
	RP.horzdist = sqrt( RP.cdist*RP.cdist - 1.0 )*RP.objsize;
	normalise( RP.sdir );

	// limit resolution for fast camera movements.(smoother camera movement at lower fps)
	double limitstep, cstep = acos( dotp( RP.cdir, pcdir ) );
	short maxlevel = SURF_MAX_PATCHLEVEL;
	static double limitstep0 = 5.12*pow( 2.0 , -(double)SURF_MAX_PATCHLEVEL );
	for( limitstep = limitstep0; cstep > limitstep && maxlevel > 5; limitstep *= 2.0 )
		maxlevel--;
	level = min( level, maxlevel );
	
	RP.tgtlvl = level;
	*/
//******
	int level = RP->tgtlvl;

	short startlvl = min( level, 8 );
	short hemisphere, ilat, ilng, idx;
	short nlat = NLAT[startlvl];
	int *nlng = NLNG[startlvl];
	TILEDESC *td = tiledesc + patchidx[startlvl-1];

	TEXCRDRANGE range = { 0.0f, 1.0f, 0.0f, 1.0f };
	if( level <= 4 ) {
		short npatch = patchidx[level] - patchidx[level-1];
		RenderSimple( level, npatch, td, &RP->mWorld );
	}
	else {
		InitRenderTile();
		EnterCriticalSection( &tbuf->LQueueCS );
		for( hemisphere = idx = 0; hemisphere < 2; hemisphere++ ) {
			if( hemisphere ) {
				D3DXMatrixMultiply( &RP->mWorld, &RSouth, &RP->mWorld );
				RP->mWorld_tmp = RP->mWorld;
//				memcpy( &RP->mWorld_tmp, &RP->mWorld, 64 );
				RP->grot.m12 = -RP->grot.m12;
				RP->grot.m13 = -RP->grot.m13;
				RP->grot.m22 = -RP->grot.m22;
				RP->grot.m23 = -RP->grot.m23;
				RP->grot.m32 = -RP->grot.m32;
				RP->grot.m33 = -RP->grot.m33;
			}
			for( ilat = nlat-1; ilat >= 0; ilat-- )
				for( ilng = 0; ilng < nlng[ilat]; ilng++ ) {
					ProcessTile( hemisphere, startlvl, ilat, nlat, ilng, nlng[ilat], td+idx,
						range, td[idx].tex, td[idx].ltex, td[idx].flag,
						range, td[idx].tex, td[idx].ltex, td[idx].flag );
					idx++;
				}
		}
		EndRenderTile();
		LeaveCriticalSection( &tbuf->LQueueCS );		
	}
	pcdir = RP->cdir;
}

void TileManager::ProcessTile( int hemisphere, int lvl, int ilat, int nlat, int ilng, int nlng, TILEDESC *tile,
	const TEXCRDRANGE &range, ID3D11ShaderResourceView *tex, ID3D11ShaderResourceView *ltex, DWORD flag,
	const TEXCRDRANGE &bkp_range, ID3D11ShaderResourceView *bkp_tex, ID3D11ShaderResourceView *bkp_ltex, DWORD bkp_flag )
{
	static const double rad0 = sqrt(2.0)*PI05*0.5;
	VECTOR3 cnt = GetTileCenter( hemisphere, ilat, nlat, ilng, nlng );
	double rad = rad0/(double)nlat;
	double adist = acos( dotp( RP->cdir, cnt ) ) - rad;

	if( adist >= RP->viewap )
		return;					//tile is invisible (behind horizon)

	SetWorldMatrix( ilat, nlat, ilng, nlng );

	float bsScale = D3DMAT_BSScaleFactor(&mWorld);

	if( !IsTileInView( lvl, ilat, bsScale ) ) {
		tbuf->DeleteSubTiles( tile );
		return;		//tile is not visible by user
	}

	bool bStepDown = (lvl < RP->tgtlvl);
	bool bCoarseTex = false;
	if( bStepDown && adist > 0.0 ) {
		double lat1, lat2, lng1, lng2;//, clat, clng, crad;
		double adist_lng, adist_lat, adist2;
		TileExtents( hemisphere, ilat, nlat, ilng, nlng, lat1, lat2, lng1, lng2 );

		oapiLocalToEqu( obj, RP->cdir, &RP->clng, &RP->clat, &RP->crad );	//oapi

		if( RP->clng < lng1-PI )			RP->clng += PI2;
		else if( RP->clng > lng2+PI )		RP->clng -= PI2;

		if( RP->clng < lng1 )				adist_lng = lng1 - RP->clng;
		else if( RP->clng > lng2 )			adist_lng = RP->clng - lng2;
			 else							adist_lng = 0.0;

		if( RP->clat < lat1 )				adist_lat = lat1 - RP->clat;
		else if( RP->clat > lat2 )			adist_lat = RP->clat - lat2;
			 else							adist_lat = 0.0;
		adist2 = max( adist_lng, adist_lat );

		double cosa = cos( adist2 );
		double a = sin( adist2 );
		double b = RP->cdist - cosa;
		double ctilt = b*cosa/sqrt(a*a*(1.0+2.0*b)+b*b); // tile visibility tilt angle cosine
		if( adist2 > rad*( 2.0*ctilt+0.3 ) ) {
			bStepDown = false;
			if( adist2 > rad*(4.2*ctilt+0.3) )
				bCoarseTex = true;
		}
	}

	// Recursion to next level: subdivide into 2x2 patch
	if( bStepDown ) {
		int i, j, idx = 0;
		float du = (range.tumax - range.tumin)*0.5f;
		float dv = (range.tvmax - range.tvmin)*0.5f;
		TEXCRDRANGE subrange;
		static TEXCRDRANGE fullrange = { 0.0f, 1.0f, 0.0f, 1.0f };
		for( i = 1; i >= 0; i-- ) {
			subrange.tvmax = (subrange.tvmin = range.tvmin + (1-i)*dv) + dv;
			for( j = 0; j < 2; j++ ) {
				subrange.tumax = (subrange.tumin = range.tumin + j*du) + du;
				TILEDESC *subtile = tile->subtile[idx];

				bool isfull = true;
				if( !subtile ) {
					tile->subtile[idx] = subtile = tbuf->AddTile();
					isfull = false;
				}
				else {
					if( subtile->flag & 0x80 ) { // not yet loaded
						if( (tile->flag & 0x80) == 0 ) // only load subtile texture if parent texture is present
							tbuf->AddTileToLQueue( this, subtile );
						isfull = false;
					}
				}

				if( isfull )
					isfull = (subtile->tex != NULL);
				if( isfull )
					ProcessTile( hemisphere, lvl+1, ilat*2+i, nlat*2, ilng*2+j, nlng*2, subtile,
						fullrange, subtile->tex, subtile->ltex, subtile->flag,
						subrange, tex, ltex, flag );
				else
					ProcessTile( hemisphere, lvl+1, ilat*2+i, nlat*2, ilng*2+j, nlng*2, subtile,
						subrange, tex, ltex, flag,
						subrange, tex, ltex, flag );
				idx++;
			}
		}
	}
	else {
		// actually render the tile at this level ---------------------------------------
		// 
	//	double sdist = acos( dotp( RP->sdir, cnt ) );

	//	gc->GetStats()->Tiles[lvl]++;
	//	gc->GetStats()->Vertices += mesh->nVtx;
	//	gc->GetStats()->Draw++;

		if( bCoarseTex )
			RenderTile( hemisphere, lvl, ilat, nlat, ilng, nlng, tile, bkp_range, bkp_tex, bkp_ltex, bkp_flag );
		else
			RenderTile( hemisphere, lvl, ilat, nlat, ilng, nlng, tile, range, tex, ltex, flag );
	}
}
#pragma endregion
//=============================================================================
//				Loading:
//=============================================================================
#pragma region texture loading
void TileManager::LoadSpecularMaskData() {
/*	Load specular data into tiledesc array in flag vars.
	*/
	FILE *file;
	BYTE minres, maxres, flag;
	int j, idx, npatch;
	char fname[256], cpath[256];

	strcpy_s( fname, 256, objname );
	strcat_s( fname, 256, "_lmask.bin" );
	nmask = 0;
	if( !( bSpecular || bLights ) || !gc->TexturePath( fname, cpath ) || !(file = fopen( cpath, "rb" )) ) {
		for( j = 0; j < patchidx[maxbaselvl]; j++ )
			tiledesc[j].flag = 1;
		return;
	}

	WORD *tflag = NULL;
	LMASKFILEHEADER lmfh;

	fread( &lmfh, sizeof(lmfh), 1, file );
	if( !strncmp( lmfh.id, "PLTA0100", 8 ) ) {
		minres = lmfh.minres;
		maxres = lmfh.maxres;
		npatch = lmfh.npatch;
		tflag = new WORD [npatch];
		fread( tflag, sizeof(WORD), npatch, file );
	}
	else {
		fseek( file, 0, SEEK_SET );
		fread( &minres, 1, 1, file );
		fread( &maxres, 1, 1, file );
		npatch = patchidx[maxres] - patchidx[minres-1];
		tflag = new WORD [npatch];
		for( j = 0; j < npatch; j++ ) {
			fread( &flag, 1, 1, file );
			tflag[j] = flag;
		}
	}
	fclose( file );

	for( j = idx = 0; j < patchidx[maxbaselvl]; j++ ) {
		if( j < patchidx[minres-1] )
			tiledesc[j].flag = 1;
		else {
			flag = (BYTE)tflag[idx++];
			tiledesc[j].flag = flag;
			if( ((flag & 3) == 3 ) || (flag & 4) )
				nmask++;
		}
	}
	delete [ ] tflag;
}

void TileManager::LoadTileData() {
	FILE *file;
	char fname[256], cpath[256];

	int compare_idx( const void *el1, const void *el2 );

	if( maxlvl <= 8 )
		return;

	strcpy_s( fname, 256, objname );
	strcat_s( fname, 256, "_tile.bin" );
	if( !gc->TexturePath( fname, cpath ) || !(file = fopen( cpath, "rb" )) ) {
//		LogWrn("Surface Tile TOC not found for %s",fname);
		return;
	}
//	LogAlw("Reading Tile Data for %s",fname);

	char idstr[9] = "        ";
	fread( idstr, 1, 8, file );
	if( !strncmp( idstr, "PLTS", 4 ) )
		tilever = 1;
	else {
		tilever = 0;
		fseek( file, 0, SEEK_SET );
	}

	DWORD n, i, j;
	fread( &n, sizeof(DWORD), 1, file );
	TILEFILESPEC *tfs = new TILEFILESPEC [n];
	fread( tfs, sizeof(TILEFILESPEC), n, file );
	fclose( file );

	//converts from new indexes to old.
	if( bPreloadTiles && tilever >= 1 ) {
		IDXLIST *idxlist = new IDXLIST [n];
		for( i = 0; i < n; i++ ) {
			idxlist[i].idx = i;
			idxlist[i].ofs = tfs[i].sidx;
		}
		qsort( idxlist, n, sizeof(IDXLIST), compare_idx );
		for( i = 0; i < n && idxlist[i].ofs != NOTILE; i++ )
			tfs[idxlist[i].idx].sidx = i;

		for (i = 0; i < n; i++) {
				idxlist[i].idx = i;
				idxlist[i].ofs = tfs[i].midx;
			}
		qsort( idxlist, n, sizeof(IDXLIST), compare_idx );
		for( i = 0; i < n && idxlist[i].ofs != NOTILE; i++ )
			tfs[idxlist[i].idx].midx = i;

		tilever = 0;
		delete [ ] idxlist;
	}

	TILEDESC *tile8 = tiledesc + patchidx[7];
	for( i = 0; i < 364; i++ ) {
		TILEDESC &tile8i = tile8[i];
		for( j = 0; j < 4; j++ )
			if( tfs[i].subidx[j] )
				AddSubtileData( tile8i, tfs, i, j, 9 );
	}
	delete [ ] tfs;
}

int compare_idx( const void *el1, const void *el2 ) {
	IDXLIST *idx1 = (IDXLIST*)el1;
	IDXLIST *idx2 = (IDXLIST*)el2;
	return (idx1->ofs < idx2->ofs ? -1 : (idx1->ofs > idx2->ofs ? 1 : 0 ));
}

void TileManager::AddSubtileData( TILEDESC &td, TILEFILESPEC *tfs, DWORD idx, DWORD sub, DWORD lvl ) {
	DWORD j, subidx = tfs[idx].subidx[sub];
	TILEFILESPEC &t = tfs[subidx];
	bool bSubtiles = false;

	for( j = 0; j < 4; j++ )
		if( t.subidx[j] ) {
			bSubtiles = true;
			break;
		}
	if( t.flags || bSubtiles ) {
		if( (int)lvl <= maxlvl ) {
			td.subtile[sub] = tbuf->AddTile();
			td.subtile[sub]->flag = t.flags;
			td.subtile[sub]->tex = (ID3D11ShaderResourceView*)t.sidx;
			if( bSpecular || bLights ) {
				if( t.midx != NOTILE )
					td.subtile[sub]->ltex = (ID3D11ShaderResourceView*)t.midx;
			}
			else
				td.subtile[sub]->flag = 1;
			td.subtile[sub]->flag |= 0x80;	//"not loaded" flag
			if( !tilever )
				td.subtile[sub]->flag |= 0x40;	//"old style index"
			
			if( bSubtiles )
				for( j = 0; j < 4; j++ )
					if( t.subidx[j] )
						AddSubtileData( *td.subtile[sub], tfs, subidx, j, lvl+1 );
			nhitex++;
			if( t.midx != NOTILE )
				nhispec++;
		}
		else
			td.subtile[sub] = NULL;
	}
}

void TileManager::LoadTextures() {
	char fname[256], cpath[256];

	ntex = patchidx[maxbaselvl];
	texbuf = new ID3D11ShaderResourceView *[ntex];
	strcpy_s( fname, 256, objname );
	strcat_s( fname, 256, ".tex" );

//	gc->SetItem( fname );	//writes planet texture loading notification over splash screen.

	if( !gc->TexturePath( fname, cpath ) )
		return;

	if( ntex = tbuf->LoadTextures( cpath, ntex, 0, texbuf ) ) {
		while( (int)ntex < patchidx[maxbaselvl] )	maxlvl = --maxbaselvl;
		while( (int)ntex > patchidx[maxbaselvl] )	texbuf[--ntex]->Release();
		for( DWORD j = 0; j < patchidx[maxbaselvl]; j++ )
			tiledesc[j].tex = texbuf[j];
	}
	else {
		delete [ ] texbuf;
		texbuf = NULL;
	}

	if( bPreloadTiles && nhitex ) {
		TILEDESC *tile8 = tiledesc + patchidx[7];
		PreLoadTileTextures( tile8, nhitex, nhispec );
	}
}

void TileManager::PreLoadTileTextures( TILEDESC *tile8, DWORD _ntex, DWORD _nmask ) {
	char fname[256], cpath[256];
	DWORD i, j, nt = 0, nm = 0;
	ID3D11ShaderResourceView **_texbuf = NULL, **_maskbuf = NULL;

	if( _ntex ) {
		_texbuf = new ID3D11ShaderResourceView *[_ntex];
		strcpy_s( fname, 256, objname );
		strcat_s( fname, 256, "_tile.tex" );

//		gc->SetItem( fname );	//writes planet texture loading notification over splash screen.

		if( gc->TexturePath( fname, cpath ) )
			nt = tbuf->LoadTextures( cpath, _ntex, 0, _texbuf );
	}
	if( _nmask ) {
		_maskbuf = new ID3D11ShaderResourceView *[nmask];
		strcpy_s( fname, 256, objname );
		strcat_s( fname, 256, "_tile_lmask.tex" );
		if( gc->TexturePath( fname, cpath ) )
			nm = tbuf->LoadTextures( cpath, _nmask, 0, _maskbuf );
	}
	for( i = 0; i < 364; i++ ) {
		TILEDESC *tile8i = tile8+i;
		for( j = 0; j < 4; j++ )
			if( tile8i->subtile[j] )
				AddSubtileTextures( tile8i->subtile[j], _texbuf, nt, _maskbuf, nm );
	}
	if( nt )
		delete [ ] _texbuf;
	if( nm )
		delete [ ] _maskbuf;
}

void TileManager::AddSubtileTextures( TILEDESC *td, ID3D11ShaderResourceView **tbuf, DWORD nt, ID3D11ShaderResourceView **mbuf, DWORD nm ) {
	DWORD tidx = (DWORD)td->tex;
	if( tidx != NOTILE ) {
		if( tidx < nt ) {
			td->tex = tbuf[tidx];
			tbuf[tidx] = NULL;	//?
		}
		else {
			tmissing++;
			td->tex = NULL;
		}
	}
	else
		td->tex = NULL;

	DWORD midx = (DWORD)td->ltex;
	if( midx != NOTILE ) {
		if( midx < nm ) {
			td->ltex = mbuf[midx];
			mbuf[midx] = NULL;	//?
		}
		else {
			tmissing++;
			td->ltex = NULL;
		}
	}
	else
		td->ltex = NULL;

	td->flag &= ~0x80;

	for( DWORD j = 0; j < 4; j++ )
		if( td->subtile[j] )
			AddSubtileTextures( td->subtile[j], tbuf, nt, mbuf, nm );
}

void TileManager::LoadSpecularMasks() {
	char fname[256], cpath[256];
	DWORD n = nmask, j;

	if( !nmask )
		return;

	strcpy_s( fname, 256, objname );
	strcat_s( fname, 256, "_lmask.tex" );

//	gc->SetItem(fname);	//writes planet texture loading notification over splash screen.

	specbuf = new ID3D11ShaderResourceView *[nmask];
	if( gc->TexturePath( fname, cpath ) ) {
		if( n = tbuf->LoadTextures( cpath, nmask, 0, specbuf ) ) {
			if( n < nmask ) {
				for( j = 0; j < n; j++ )
					specbuf[j]->Release();
				delete [ ] specbuf;
				specbuf = NULL;
				nmask = 0;
				for( j = 0; j < patchidx[maxbaselvl]; j++ )
					tiledesc[j].flag = 1;
			}
			else {
				for( j = n = 0; j < patchidx[maxbaselvl]; j++ ) {
					if( ((tiledesc[j].flag & 3) == 3) || (tiledesc[j].flag & 4 ) ) {
						if( n < nmask )
							tiledesc[j].ltex = specbuf[n++];
						else
							tiledesc[j].flag = 1;
					}
					if( !bLights )
						tiledesc[j].flag &= 0xFB;
					if( !bSpecular )
						tiledesc[j].flag &= 0xFD, tiledesc[j].flag |= 1;
				}
			}
		}
		else {
			nmask = 0;
			for( j = 0; j < patchidx[maxbaselvl]; j++ )
				tiledesc[j].flag = 1;
		}
	}
}

void TileManager::SetMicrotexture( const char *fname ) {
	if( fname )	{
		if( tbuf->LoadMicroTexture( fname, &microtex ) != S_OK )
			microtex = NULL;
	}
	else	microtex = NULL;
}

void TileManager::SetMicrolevel( double lvl ) {
	microlvl = lvl;
}

void TileManager::SetAmbientColor( D3DXCOLOR *acol ) {
	cAmbient = *acol;
}

bool TileManager::SpecularColor( D3DXCOLOR *col ) {
	if( !atmc ) {
		col->r = col->g = col->b = spec_base;
		return false;
	}
	else {
		double fac = 0.7;
		double cosa = dotp( RP->cdir, RP->sdir );
		double alpha = 0.5*acos(cosa);
		double scale = sin( alpha )*fac;
		col->r = (float)max( 0.0, spec_base - scale*atmc->color0.x );
		col->g = (float)max( 0.0, spec_base - scale*atmc->color0.y );
		col->b = (float)max( 0.0, spec_base - scale*atmc->color0.z );
		return true;
	}
}
#pragma endregion
//===============================================
//				Static functions:
//===============================================
#pragma region static
void TileManager::GlobalInit( D3D11Config *_cfg, ID3D11Device *dev, ID3D11DeviceContext *dctx ) {

	cfg = _cfg;
	D3D11Effect::InitPlanetEffect();

	bSpecular = *(bool*)gc->GetConfigParam( CFGPRM_SURFACEREFLECT );
	bLights = *(bool*)gc->GetConfigParam( CFGPRM_SURFACELIGHTS );
	bRipple = bSpecular && *(bool*)gc->GetConfigParam( CFGPRM_SURFACERIPPLE );
	bPreloadTiles = cfg->bPreloadTiles;

	tbuf = new TileBuffer();

	//spheres:
	CreateSphere( TPL_1, 6, false, 0, 64 );
	CreateSphere( TPL_2, 8, false, 0, 128 );
	CreateSphere( TPL_3, 12, false, 0, 256 );
	//hemispheres:
	CreateSphere( TPL_4[0], 16, true, 0, 256 );
	CreateSphere( TPL_4[1], 16, true, 1, 256 );
	//tile meshes:
	CreateSpherePatch( TPL_5, 0, 1, 4, 18 );

	CreateSpherePatch( TPL_6[0], 0, 2, 8, 10, 16 );
	CreateSpherePatch( TPL_6[1], 1, 2, 4, 12 );
	
	CreateSpherePatch( TPL_7[0], 0, 4, 16, 12, 12, false ); 
	CreateSpherePatch( TPL_7[1], 1, 4, 16, 12, 12, false );
	CreateSpherePatch( TPL_7[2], 2, 4, 12, 10, 16, true );
	CreateSpherePatch( TPL_7[3], 3, 4, 6, 12, -1, true );

	CreateSpherePatch( TPL_8[0], 0, 8, 32, 12, 15, false );
	CreateSpherePatch( TPL_8[1], 1, 8, 32, 12, 15, false );
	CreateSpherePatch( TPL_8[2], 2, 8, 30, 12, 16, false );
	CreateSpherePatch( TPL_8[3], 3, 8, 28, 12, 12, false );
	CreateSpherePatch( TPL_8[4], 4, 8, 24, 12, 12, false );
	CreateSpherePatch( TPL_8[5], 5, 8, 18, 12, 12, false );
	CreateSpherePatch( TPL_8[6], 6, 8, 12, 10, 16, true );
	CreateSpherePatch( TPL_8[7], 7, 8, 6, 12, -1, true );

	const int n = 8;
	const int nlng8[8] = { 32, 32, 30, 28, 24, 18, 12, 6 };
	const int res8[8] = { 15, 15, 16, 12, 12, 12, 12, 12 };
	int mult = 2, idx, lvl, i, j;

	for( lvl = 9; lvl <= SURF_MAX_PATCHLEVEL; lvl++ ) {
		idx = 0;
		for( i = 0; i < 8; i++ ) {
			for( j = 0; j < mult; j++ ) {
				if( idx < n*mult )
					CreateSpherePatch( TPL[lvl][idx], idx, n*mult, nlng8[i]*mult, 12, res8[i], false, true );
				else
					CreateSpherePatch( TPL[lvl][idx], idx, n*mult, nlng8[i]*mult, 12, -1, true, true );
				idx++;
			}
		}
		mult *= 2;
	}
	//rotation matrix for tiles in south hemisphere:
	D3DXMatrixRotationX( &RSouth, (float)PI );
}

//cleans all memory used by tile meshes.
void TileManager::GlobalExit() {
	DWORD j;

	DeleteTileMesh( TPL_1 );
	DeleteTileMesh( TPL_2 );
	DeleteTileMesh( TPL_3 );
	DeleteTileMesh( TPL_4[0] );
	DeleteTileMesh( TPL_4[1] );
	DeleteTileMesh( TPL_5 );
	DeleteTileMesh( TPL_6[0] );
	DeleteTileMesh( TPL_6[1] );
	for( j = 0; j < 4; j++ )	DeleteTileMesh( TPL_7[j] );
	for( j = 0; j < 8; j++ )	DeleteTileMesh( TPL_8[j] );

	const int n = 8;
	int mult = 2, lvl;
	for( lvl = 9; lvl <= SURF_MAX_PATCHLEVEL; lvl++ ) {
		for( j = 0; j < n*mult; j++ )
			DeleteTileMesh( TPL[lvl][j] );
		mult *= 2;
	}

	delete tbuf;
}

//deletes tile mesh.
void TileManager::DeleteTileMesh( TILEMESH &mesh ) {
	mesh.VB->Release();
	mesh.IB->Release();
}

/*
Creates sphere(L1-L3) or hemisphere(L4);
Params:
mesh
nrings
hemisphere
which_half
texres
*/
void TileManager::CreateSphere( TILEMESH &mesh, DWORD nrings, bool hemisphere, int which_half, int texres ) {
	mesh.nvtx = hemisphere ? nrings*(nrings+1)+2 : nrings*(2*nrings+1)+2;
	mesh.nidx = hemisphere ? 6*nrings*nrings : 12*nrings*nrings;

	TILEVERTEX *VData, *vdata;
	VData = new TILEVERTEX [mesh.nvtx];
	vdata = VData;

	WORD *IData, *idata;
	IData = new WORD [mesh.nidx];
	idata = IData;
	
	WORD x, y, nvtx = 0, nidx = 0;
	float fDAng = (float)PI/nrings;
	float fDAngY0 = fDAng;

	DWORD x1 = ( hemisphere ? nrings : nrings*2 );
	DWORD x2 = x1 + 1;

	float du = 0.5f/(float)texres;
	float a = (1.0f-2.0f*du)/(float)x1;
	float y0, r0, tv, fDAngX0, tu;
	D3DXVECTOR3 vec;

	for( y = 0; y < nrings; y++ ) {
		y0 = (float)cos(fDAngY0);
		r0 = (float)sin(fDAngY0);
		tv = fDAngY0/(float)PI;
		for( x = 0; x < x2; x++ ) {
			fDAngX0 = x*fDAng - (float)PI;
			if( hemisphere && which_half )
				fDAngX0 += (float)PI;
			vec = D3DXVECTOR3( r0*(float)cos(fDAngX0), y0, r0*(float)sin(fDAngX0) );
			tu = a*(float)x + du;
			*vdata++ = _TVTX( vec, vec, tu, tv, tu, tv );
			nvtx++;
		}
		fDAngY0 += fDAng;
	}

	for( y = 0; y < nrings-1; y++ ) {
		for( x = 0; x < x1; x++ ) {
			*idata++ = (WORD)( (y+0)*x2 + (x+0) );
			*idata++ = (WORD)( (y+0)*x2 + (x+1) );
			*idata++ = (WORD)( (y+1)*x2 + (x+0) );
			*idata++ = (WORD)( (y+0)*x2 + (x+1) );
			*idata++ = (WORD)( (y+1)*x2 + (x+1) );
			*idata++ = (WORD)( (y+1)*x2 + (x+0) );
			nidx += 6;
		}
	}

	vec = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
	WORD wNorthVtx = nvtx;
	*vdata++ = _TVTX( vec, vec, 0.5f, 0.0f, 0.5f, 0.0f );
	nvtx++;

	vec = D3DXVECTOR3( 0.0f, -1.0f, 0.0f );
	WORD wSouthVtx = nvtx;
	*vdata++ = _TVTX( vec, vec, 0.5f, 1.0f, 0.5f, 1.0f );
	nvtx++;

	for( x = 0; x < x1; x++ ) {
		*idata++ = wSouthVtx;
		*idata++ = (WORD)( y*x2 + x + 1 );
		*idata++ = (WORD)( y*x2 + x );
		nidx += 3;
	}

	for( x = 0; x < x1; x++ ) {
		*idata++ = wNorthVtx;
		*idata++ = (WORD)( x + 1 );
		*idata++ = (WORD)x;
		nidx += 3;
	}

	D3D11_BUFFER_DESC Desc;
	ZeroMemory( &Desc, sizeof(Desc) );
	Desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	Desc.ByteWidth = mesh.nvtx*40;
	Desc.CPUAccessFlags = 0;
	Desc.MiscFlags = 0;
	Desc.StructureByteStride = 0;
	Desc.Usage = D3D11_USAGE_IMMUTABLE;

	D3D11_SUBRESOURCE_DATA SData;
	ZeroMemory( &SData, sizeof(SData) );
	SData.pSysMem = VData;

	HR( Dev->CreateBuffer( &Desc, &SData, &mesh.VB ) );

	ZeroMemory( &Desc, sizeof(Desc) );
	Desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	Desc.ByteWidth = mesh.nidx*2;
	Desc.CPUAccessFlags = 0;
	Desc.MiscFlags = 0;
	Desc.StructureByteStride = 0;
	Desc.Usage = D3D11_USAGE_IMMUTABLE;

	ZeroMemory( &SData, sizeof(SData) );
	SData.pSysMem = IData;

	HR( Dev->CreateBuffer( &Desc, &SData, &mesh.IB ) );
	mesh.nFace = mesh.nidx / 3;

	delete [ ] VData;
	delete [ ] IData;
}

void TileManager::CreateSpherePatch( TILEMESH &mesh, int ilat, int nlat, int nlng, int res, int bseg, bool reduce, bool shift_origin ) {
	/*	Constructs spherical mesh patch for Levels L5-L14 (L5-L16).

	msh - template mesh for writing.
	nlng - 360°/angular size. (number of patches per 360°)
	ilat - latitude segment.
	nlat - 90°/angular size. (number of patches per 90°)
	res - resolution vertical.
	bseg - resolution horizontal.
	reduce - whether reduce number of vertices or not.
	shift_origin - shifts origin of the mesh in order to avoid precision errors*/
	double	minlat = PI05 * (double)ilat/(double)nlat,
			maxlat = PI05 * (double)(ilat+1)/(double)nlat,
			minlng = 0.0,
			maxlng = PI2/(double)nlng;

	if( bseg < 0 || ilat == nlat-1 )
		bseg = (nlat-ilat)*res;

	mesh.nvtx = (bseg+1)*(res+1);
	if( reduce )
		mesh.nvtx -= ((res+1)*res)/2;

	TILEVERTEX *vdata = new TILEVERTEX [mesh.nvtx];

	float dx, dy;
	if( shift_origin ) {
		dx = (float)cos(minlat);
		dy = (float)sin(minlat);
	}
	//------------------------------------
	//	Vertex buffer.
	//------------------------------------
	double lat, slat, clat, lng, slng, clng;
	int i, j, n, nseg;//counters.
	VECTOR3 pos, tpos;

	for( i = n = 0; i <= res; i++ ) {
		lat = minlat + (maxlat-minlat)*(double)i/(double)res;
		slat = sin(lat), clat = cos(lat);
		nseg = ( reduce ? bseg-i : bseg );
		for( j = 0; j <= nseg; j++ ) {
			lng = ( nseg ? minlng + (maxlng-minlng)*(double)j/(double)nseg : 0.0 );
			slng = sin(lng), clng = cos(lng);

			pos = _V( clat*clng, slat, clat*slng );
	//		tpos = mul( R, pos-pref );

			vdata[n].pos.x = vdata[n].norm.x = (float)pos.x;
			vdata[n].pos.y = vdata[n].norm.y = (float)pos.y;
			vdata[n].pos.z = vdata[n].norm.z = (float)pos.z;

			if( shift_origin ) {
				vdata[n].pos.x -= dx;
				vdata[n].pos.y -= dy;
			}

			vdata[n].tex.x = (float)( nseg ? (float)j/(float)nseg : 0.5f );
			vdata[n].tex.y = (float)( ((float)res-(float)i)/(float)res );

			vdata[n].mtex.x = (nseg ? vdata[n].tex.x * TEX2_MULTIPLIER : 0.5f);
			vdata[n].mtex.y = vdata[n].tex.y * TEX2_MULTIPLIER;
			n++;
		}
	}
	//------------------------------------
	//	Index buffer.
	//------------------------------------
	mesh.nidx = ( reduce ? res*(2*bseg-res) : 2*res*bseg )*3;
	WORD *idata = new WORD [mesh.nidx];
	int nofs0, nofs1;

	for( i = n = nofs0 = 0; i < res; i++ ) {
		nseg = ( reduce ? bseg-i : bseg );
		nofs1 = nofs0 + nseg + 1;
		for( j = 0; j < nseg; j++ ) {
			idata[n++] = nofs0 + j;
			idata[n++] = nofs1 + j;
			idata[n++] = nofs0 + j + 1;
			if( reduce && j == nseg-1 )
				break;
			idata[n++] = nofs0 + j + 1;
			idata[n++] = nofs1 + j;
			idata[n++] = nofs1 + j + 1;
		}
		nofs0 = nofs1;
	}

	D3D11_BUFFER_DESC Desc;
	ZeroMemory( &Desc, sizeof(Desc) );
	Desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	Desc.ByteWidth = mesh.nvtx*40;
	Desc.CPUAccessFlags = 0;
	Desc.MiscFlags = 0;
	Desc.StructureByteStride = 0;
	Desc.Usage = D3D11_USAGE_IMMUTABLE;

	D3D11_SUBRESOURCE_DATA SData;
	ZeroMemory( &SData, sizeof(SData) );
	SData.pSysMem = vdata;

	HR( Dev->CreateBuffer( &Desc, &SData, &mesh.VB ) );

	ZeroMemory( &Desc, sizeof(Desc) );
	Desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	Desc.ByteWidth = mesh.nidx*2;
	Desc.CPUAccessFlags = 0;
	Desc.MiscFlags = 0;
	Desc.StructureByteStride = 0;
	Desc.Usage = D3D11_USAGE_IMMUTABLE;

	ZeroMemory( &SData, sizeof(SData) );
	SData.pSysMem = idata;

	HR( Dev->CreateBuffer( &Desc, &SData, &mesh.IB ) );
	D3DXComputeBoundingSphere( (const D3DXVECTOR3*)&vdata->pos.x, mesh.nvtx, 40, &mesh.bsCnt, &mesh.bsRad );
	mesh.nFace = mesh.nidx / 3;

	delete [ ] vdata;
	delete [ ] idata;
}
#pragma endregion
//=============================================================================
//							TileBuffer
//=============================================================================

bool TileBuffer::bRunLThread = true;
DWORD TileBuffer::nqueue = 0;
QUEUEDESC TileBuffer::LQueue[10];
CRITICAL_SECTION TileBuffer::LQueueCS;
HANDLE TileBuffer::hLThread = NULL;
DWORD TileBuffer::LThreadID = 0;

#pragma region TileBuffer
TileBuffer::TileBuffer() {
	//mipmaps for tiles ????

	buf = NULL;
	nbuf = 0;
	bufsize = 0;

	memset( LQueue, 0, sizeof(QUEUEDESC)*MAXQUEUE );
	nqueue = 0;

	InitializeCriticalSection( &LQueueCS );
	hLThread = CreateThread( NULL, 2048, LThreadProc, this, 0, &LThreadID );	
}

TILEDESC *TileBuffer::AddTile() {
	TILEDESC *td = new TILEDESC;
	memset( td, 0, sizeof(TILEDESC) );

	//extend size of buffer if needed:
	if( nbuf == bufsize ) {
		TILEDESC **tmp = new TILEDESC *[bufsize+16];
		if( nbuf ) {
			memcpy( tmp, buf, sizeof(TILEDESC*)*bufsize );
			delete [ ] buf;
		}
		ZeroMemory( tmp+bufsize, sizeof(TILEDESC*)*16 );
		buf = tmp;
		bufsize += 16;
	}

	//write new tile to buffer:
	buf[nbuf] = td;
	td->ofs = nbuf;
	nbuf++;
	return td;
}

TileBuffer::~TileBuffer() {
	//terminate loading thread:
	bRunLThread = false;
//	LogAlw("=============== Deleting %u tiles in tile buffer=================",nbuf);
	if( hLThread ) {
		TerminateThread( hLThread, 0xFF );		//terminate it if it still exist
		hLThread = NULL;
	}
	DeleteCriticalSection( &LQueueCS );

	//delete all loaded L9-L14 textures:
	ID3D11Texture2D *res;
	if( nbuf ) {
		for( DWORD j = 0; j < nbuf; j++ ) {
			if( !(buf[j]->flag & 0x80) ) {		//if tile was loaded
				REL( buf[j]->tex );				
				REL( buf[j]->ltex );
				delete buf[j];
			}
		}
		delete [ ] buf;
	}
}

bool TileBuffer::AddTileToLQueue( TileManager *tm, TILEDESC *tile ) {
	if( nqueue == MAXQUEUE )
		return false;

	for( DWORD j = 0; j < nqueue; j++ )
		if( LQueue[j].td == tile )
			return false;

	LQueue[nqueue].TM = tm;
	LQueue[nqueue].td = tile;
	nqueue++;
	return true;
}

void TileBuffer::DeleteSubTiles( TILEDESC *tile ) {
	for( DWORD j = 0; j < 4; j++ )
		if( tile->subtile[j] )
			if( DeleteTile( tile->subtile[j] ) )
				tile->subtile[j] = NULL;
}

bool TileBuffer::DeleteTile( TILEDESC *tile ) {
	bool del = true;
	for( DWORD j = 0; j < 4; j++ )
		if( tile->subtile[j] )
			if( DeleteTile( tile->subtile[j] ) )
				tile->subtile[j] = NULL;
			else
				del =  false;

	if( tile->tex || !del )
		return false;
	else {
		buf[nbuf-1]->ofs = tile->ofs;
		buf[tile->ofs] = buf[nbuf-1];
		buf[nbuf-1] = NULL;
		nbuf--;
		delete tile;
		tile = NULL;
		return true;
	}
}

DWORD WINAPI TileBuffer::LThreadProc( void *data ) {
	TileBuffer *tb = (TileBuffer*)data;
	static const long TILESIZE = 32896;	// default texture size for old-style texture files
	const DWORD IdleTime = 1000/cfg->TileLoadingFrequency;
	bool load;
	QUEUEDESC qd;

	char fname[256];

	while( bRunLThread ) {
		Sleep( IdleTime );

		//sync.
		EnterCriticalSection( &LQueueCS );			
		if( load = ( nqueue > 0) )
			memcpy( &qd, &LQueue[tb->nqueue-1], sizeof(QUEUEDESC) );
		LeaveCriticalSection( &LQueueCS );

		if( load  ) {
			TILEDESC *td = qd.td;
			ID3D11ShaderResourceView *tex, *mask = NULL;
			DWORD tidx;
			long ofs;

			//diffuse texture:
			tidx = (DWORD)td->tex;
			if( tidx == NOTILE )
				tex = NULL;
			else {
				ofs = (td->flag & 0x40 ? (long)tidx*TILESIZE : (long)tidx );
				if( LoadTexture( qd.TM->Planet_tile_fpath, ofs, &tex ) != S_OK ) {
					tex = NULL;
			//		LogErr( "0.Failed to load a tile using LoadTextureFromTEX() offset=%u, name=%s, ErrorCode = %d", ofs, qd.TM->DiffuseTEXFilePath, hr );
				}
			}

			//specular texture:
			if( (( td->flag & 3 ) == 3) || ( td->flag & 4 ) ) {
				tidx = (DWORD)td->ltex;
				if( tidx == NOTILE )
					mask = NULL;
				else {
					ofs = ( td->flag & 0x40 ? (long)tidx*TILESIZE : (long)tidx );
					if( LoadTexture( qd.TM->Planet_tile_lmask_fpath, ofs, &mask ) != S_OK ) {
						mask = NULL;
			//			LogErr( "1.Failed to load a tile using LoadTextureFromTEX() offset=%u, name=%s, ErrorCode = %d", ofs, qd.TM->SpecularTEXFilePath, hr );
					}
				}
			}

			//write result to tile and decrement queue counter.
			EnterCriticalSection( &LQueueCS );
			td->tex = tex;
			td->ltex = mask;
			td->flag &= 0x3F;
			tb->nqueue--;
			LeaveCriticalSection( &LQueueCS );
		}
	}
	return 0;
}

DWORD TileBuffer::LoadTextures( const char *fname, DWORD ntex, DWORD flags, ID3D11ShaderResourceView **out ) {
	DWORD ltex = 0;
	FILE *file;
	D3DX11_IMAGE_INFO IInfo;
	D3DX11_IMAGE_LOAD_INFO LInfo;
	HRESULT hr;
	
	if( !(file = fopen( fname, "rb" )) )
		return 0;

	fseek( file, 0, SEEK_END );
	DWORD fsize = ftell( file );
	DWORD byteleft = fsize;

	BYTE *location, *buffer = new BYTE [fsize+1];
	rewind( file );
	fread( buffer, 1, fsize, file );
	fclose( file );

	location = buffer;
	while( ntex > ltex && byteleft > 0 ) {
		if( *(DWORD*)location != MAKEFOURCC( 'D', 'D', 'S', ' ' ) ) {
			delete [ ] buffer;
			return ltex;
		}
		DDSURFACEDESC2 *DDSdesc = (DDSURFACEDESC2*)(location+4);
		DWORD esize = DDSdesc->dwLinearSize;
		esize += (4 + sizeof(DDSURFACEDESC2));
	//	DDSdesc->dwMipMapCount = 1; not working (wrong esize -> fails anyway)
	//	DDSdesc->dwRefreshRate = 0;
	//	DDSdesc->dwSrcVBHandle = 0;

		D3DX11GetImageInfoFromMemory( location, esize, NULL, &IInfo, &hr );
		if( hr != S_OK ) {
			location += esize;
			byteleft -= esize;
			continue;
		}

		LInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;
		LInfo.CpuAccessFlags = 0;
		LInfo.Depth = 1;
		LInfo.Filter = D3DX11_FILTER_NONE;
		LInfo.FirstMipLevel = 0;
		LInfo.Format = IInfo.Format;
		LInfo.MipFilter = D3DX11_FILTER_NONE;
		LInfo.MipLevels = IInfo.MipLevels;
		LInfo.MiscFlags = 0;
		LInfo.pSrcInfo = &IInfo;
		LInfo.Usage = D3D11_USAGE_IMMUTABLE;
		LInfo.Width = IInfo.Width;
		LInfo.Height = IInfo.Height;

		D3DX11CreateShaderResourceViewFromMemory( Dev, location, esize, &LInfo, NULL, &out[ltex], &hr );

		location += esize;
		byteleft -= esize;
		if( hr == S_OK )
			ltex++;
	}
	delete [ ] buffer;
	return ltex;
}

HRESULT TileBuffer::LoadTexture( const char *fname, long ofs, ID3D11ShaderResourceView **out ) {
	FILE *file;
	DDSURFACEDESC2 DDSdesc;
	HRESULT hr;
	DWORD key;
	BYTE *data;
//	DXGI_FORMAT Format;
//	ID3D11Texture2D *tex;
		
	//opening file:
	if( !fname || !(file = fopen( fname, "rb" )) )
		return E_FAIL;

	//check for "DDS ":
	fseek( file, ofs, SEEK_SET );
	fread( &key, 4, 1, file );
	if( key != MAKEFOURCC( 'D', 'D', 'S', ' ' ) )
		return E_FAIL;

	//read desc, create buffer, and read whole DDS there:
	fread( &DDSdesc, sizeof(DDSURFACEDESC2), 1, file );
	DWORD bufsize = DDSdesc.dwLinearSize;
	bufsize += (4 + sizeof(DDSURFACEDESC2));
	data = new BYTE [bufsize+1];
	fseek( file, ofs, SEEK_SET );
	fread( data, 1, bufsize, file );
	fclose( file );

	DDSURFACEDESC2 *dds = (DDSURFACEDESC2*)(data+4);
	dds->dwMipMapCount = 1;//not 9
	dds->dwRefreshRate = 1;//not 9
	dds->dwSrcVBHandle = 1;//not 9

	D3DX11_IMAGE_INFO IInfo;
	D3DX11_IMAGE_LOAD_INFO LInfo;

	D3DX11GetImageInfoFromMemory( data, bufsize, NULL, &IInfo, &hr );
	if( hr != S_OK ) {
		delete [ ] data;
		return E_FAIL;
	}

	LInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	LInfo.CpuAccessFlags = 0;
	LInfo.Depth = 1;
	LInfo.Filter = D3DX11_FILTER_NONE;
	LInfo.FirstMipLevel = 0;
	LInfo.Format = IInfo.Format;
	LInfo.MipFilter = D3DX11_FILTER_NONE;
	LInfo.MipLevels = 1;
	LInfo.MiscFlags = 0;
	LInfo.pSrcInfo = &IInfo;
	LInfo.Usage = D3D11_USAGE_IMMUTABLE;
	LInfo.Width = IInfo.Width;
	LInfo.Height = IInfo.Height;

	HR( D3DX11CreateShaderResourceViewFromMemory( Dev, data, bufsize, &LInfo, NULL, out, NULL ) );
	delete [ ] data;
	if( hr != S_OK )
		return E_FAIL;
	return hr == S_OK ? S_OK : E_FAIL;
}

HRESULT TileBuffer::LoadMicroTexture( const char *fname, ID3D11ShaderResourceView **out ) {
	char cpath[256];
	FILE *file;
	HRESULT hr;
	DWORD key;
	BYTE *data;
	DWORD bufsize;

	strcpy( cpath, "Textures\\" );
	strcat( cpath, fname );
	if( !(file = fopen( cpath, "rb" ) ) )
		return E_FAIL;

	fread( &key, 4, 1, file );
	if( key != MAKEFOURCC( 'D', 'D', 'S', ' ' ) )
		return E_FAIL;

	fseek( file, 0, SEEK_END );
	bufsize = ftell( file );
	fseek( file, 0, SEEK_SET );
	data = new BYTE [bufsize+1];
	fread( data, 1, bufsize, file );
	fclose( file );

	D3DX11_IMAGE_INFO IInfo;
	D3DX11_IMAGE_LOAD_INFO LInfo;

	D3DX11GetImageInfoFromMemory( data, bufsize, NULL, &IInfo, &hr );
	if( hr != S_OK ) {
		delete [ ] data;
		return E_FAIL;
	}

	LInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	LInfo.CpuAccessFlags = 0;
	LInfo.Depth = 1;
	LInfo.Filter = D3DX11_FILTER_NONE;
	LInfo.FirstMipLevel = 0;
	LInfo.Format = IInfo.Format;
	LInfo.MipFilter = D3DX11_FILTER_NONE;
	LInfo.MipLevels = 1;
	LInfo.MiscFlags = 0;
	LInfo.pSrcInfo = &IInfo;
	LInfo.Usage = D3D11_USAGE_IMMUTABLE;
	LInfo.Width = IInfo.Width;
	LInfo.Height = IInfo.Height;

	D3DX11CreateShaderResourceViewFromMemory( Dev, data, bufsize, &LInfo, NULL, out, &hr );
	delete [ ] data;
	if( hr != S_OK )
		return E_FAIL;
	return S_OK;
}
#pragma endregion
