#include "vObject.h"

ID3D11InputLayout
	*vVessel::IL_BillBoard	= NULL;

ID3D11VertexShader
	*vVessel::VS_BillBoard	= NULL,
	*vVessel::VS_Mesh		= nullptr;

ID3D11PixelShader
	*vVessel::PS_BillBoard	= NULL,
	*vVessel::PS_Mesh		= nullptr;

ID3D11Buffer
	*vVessel::VB			= NULL,
	*vVessel::IB			= NULL,
	*vVessel::beaconsBuffer = nullptr;

Texture
	*vVessel::Ball[3]		= { NULL },
	*vVessel::def_Exhaust	= NULL,
	*vVessel::def_Reentry	= NULL;

BBVertex
	vVessel::_VB[8];


#define BEACONS_COUNT 8

struct BeaconLightSource
{
	D3DXVECTOR4 color;
	D3DXVECTOR4 position;
	float falloffDistance;
	float size;
	int enabled;
	float lightSourcePadding;
}; //size = 12 * 4

struct cp_VS_beaconLightSources
{
	BeaconLightSource lightSources[BEACONS_COUNT];
};

void vVessel::GlobalInit() 
{

	HRESULT hr;
	ID3DBlob *SBlob = NULL, *EBlob = NULL;

	D3D11_INPUT_ELEMENT_DESC ldesc0[ ] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 36, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};

	//======================================
	//		Meshes
	//======================================

	CompileFromFile( "Modules\\D3D11Shaders\\Vessel.fx", NULL, NULL, "VS_Mesh", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Mesh ) );
	REL( SBlob );
	REL( EBlob );

	CompileFromFile( "Modules\\D3D11Shaders\\Vessel.fx", NULL, NULL, "PS_Mesh", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_Mesh ) );
	REL( SBlob );
	REL( EBlob );

	//======================================
	//		Buffers
	//======================================

//cbuffers:
	//vertex shader
	D3D11_BUFFER_DESC bdesc;
	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = sizeof(cp_VS_beaconLightSources);
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;
	HR( Dev->CreateBuffer( &bdesc, nullptr, &beaconsBuffer ) );

	IL_BillBoard = vStar::IL_BBVertex;
	VS_BillBoard = vStar::VS;
	PS_BillBoard = vStar::PS;

	Ball[0] = TM->LoadTextureFromFile( "Ball.dds", 0 );
	Ball[1] = TM->LoadTextureFromFile( "Ball2.dds", 0 );
	Ball[2] = TM->LoadTextureFromFile( "Ball3.dds", 0 );
	def_Exhaust = TM->LoadTextureFromFile( "Exhaust.dds", 0 );
	def_Reentry = TM->LoadTextureFromFile( "Reentry.dds", 0 );

	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bdesc.ByteWidth = 20*8;
	bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DYNAMIC;

	HR( Dev->CreateBuffer( &bdesc, NULL, &VB ) );

	_VB[0].tex.x = 0.24f;
	_VB[0].tex.y = 0.0f;
	_VB[1].tex.x = 0.24f;
	_VB[1].tex.y = 1.0f;
	_VB[2].tex.x = 0.01f;
	_VB[2].tex.y = 0.0f;
	_VB[3].tex.x = 0.01f;
	_VB[3].tex.y = 1.0f;

	_VB[4].tex.x = 0.504f;
	_VB[4].tex.y = 0.004f;
	_VB[5].tex.x = 0.996f;
	_VB[5].tex.y = 0.004f;
	_VB[6].tex.x = 0.504f;
	_VB[6].tex.y = 0.4961f;
	_VB[7].tex.x = 0.996f;
	_VB[7].tex.y = 0.4961f;

	WORD Idx[12] = { 0, 1, 2,	3, 2, 1,	4, 5, 6,	7, 6, 5 };

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

	D3D11_SUBRESOURCE_DATA sdata;
	ZeroMemory( &sdata, sizeof(sdata) );
	sdata.pSysMem = Idx;

	HR( Dev->CreateBuffer( &bdesc, &sdata, &IB ) );
}

void vVessel::GlobalExit() {
	TM->ReleaseTexture( Ball[0] );
	TM->ReleaseTexture( Ball[1] );
	TM->ReleaseTexture( Ball[2] );
	TM->ReleaseTexture( def_Exhaust );
	TM->ReleaseTexture( def_Reentry );
	REL( VB );
	REL( IB );

	REL( beaconsBuffer );
	REL( VS_Mesh );
	REL( PS_Mesh );
}

vVessel::vVessel( OBJHANDLE _obj ) : vObject( _obj ) {
	UINT j;

	//load vessel meshes:
	MESHHANDLE hMesh;
	D3D11TPLMesh *TMesh;
	VECTOR3 ofs;
	animstate = NULL;

	VSL = oapiGetVesselInterface( obj );
	bAMSO = false;
	bAMSO = (strncmp( (VSL->GetClassName)(), "AMSO", 4 ) == 0);
	nmesh = VSL->GetMeshCount();
	if( nmesh ) {
		MESH = new VSLMESH [nmesh];
		memset( MESH, 0, sizeof(VSLMESH)*nmesh );

	//	D3DXVECTOR3 *BS = new D3DXVECTOR3 [nmesh];
	//	memset( BS, 0, 12*nmesh );
		for( j = 0; j < nmesh; j++ ) {
			hMesh = VSL->GetMeshTemplate( j );

			if( !bAMSO ) {
				char Line[128];
				const char *line;
				line = VSL->GetMeshName( j );
				sprintf( Line, "Loading mesh: %s", line );
				gc->ProgressString( Line, 0 );
			}

			if( bAMSO )	MM->UpdateAMSOMesh( hMesh );
			if( hMesh && (TMesh = MM->Find( hMesh )) )
				MESH[j].MSH = new D3D11Mesh( TMesh );	//shadows
			else
				if( hMesh = VSL->CopyMeshFromTemplate( j ) ) {
					MESH[j].MSH = new D3D11Mesh( hMesh, true );
					oapiDeleteMesh( hMesh );
				}

			if( MESH[j].MSH ) {
			//	BS[j] = MESH[j].MSH->bsPos;
				MESH[j].vis_mode = VSL->GetMeshVisibilityMode( j );
				VSL->GetMeshOffset( j, ofs );
				D3DXMatrixIdentity( &MESH[j].ofs );
				MESH[j].ofs._41 = (float)ofs.x;
				MESH[j].ofs._42 = (float)ofs.y;
				MESH[j].ofs._43 = (float)ofs.z;
			}
		}
//		D3DXComputeBoundingSphere( BS, nmesh, 12, &bsPos, &bsRad );
		
		nanim = VSL->GetAnimPtr( &ANIM );
		if( nanim ) {
			animstate = new double [nanim];
			for( j = 0; j < nanim; j++ )
				animstate[j] = ANIM[j].defstate;
		}
		else {
			ANIM = NULL;
			animstate = NULL;
		}
	}
	else {
		MESH = NULL;
		ANIM = NULL;
		nanim = 0;
	}

	bsRecompute = true;

	nex = 0;
	espc = NULL;

	nbls = 0;
	nblsmax = 16;
	BC = new BeaconData [16];

	nxtsimt = oapiGetSimTime();
}

vVessel::~vVessel() {
	if( MESH ) {
		for( UINT j = 0; j < nmesh; j++ )
			delete MESH[j].MSH;
		delete [ ] MESH;
	}

	if( nanim )
		delete [ ] animstate;
	delete [ ] BC;
}

//=====================================
//		Animations
//=====================================
#pragma region animations
void vVessel::Animate( UINT midx, UINT anim, double state ) {
	UINT i, ii;
	D3DXMATRIX T;
	ANIMATIONCOMP *AnimComp;
	double s0, s1, ds;

	ANIMATION *An = ANIM + anim;
	for( i = 0; i < An->ncomp; i++ ) {
		ii = ( state > animstate[anim] ? i : An->ncomp-i-1 );
		AnimComp = An->comp[ii];
		if( midx != (UINT)-1 && midx != AnimComp->trans->mesh )//???
			continue;
		s0 = animstate[anim];
		if( s0 < AnimComp->state0 )
			s0 = AnimComp->state0;
		else
			if( s0 > AnimComp->state1 )
				s0 = AnimComp->state1;
		s1 = state;
		if( s1 < AnimComp->state0 )
			s1 = AnimComp->state0;
		else
			if( s1 > AnimComp->state1 )
				s1 = AnimComp->state1;
		if( (ds = s1 - s0) == 0 )
			continue;

		ds /= ( AnimComp->state1 - AnimComp->state0 );

		switch( AnimComp->trans->Type() ) {
			case MGROUP_TRANSFORM::ROTATE:
				{
					MGROUP_ROTATE *rot = (MGROUP_ROTATE*)AnimComp->trans;
					D3DXVECTOR3 axis;
					V3toD3( &axis, &rot->axis );
					D3DXMatrixRotationAxis( &T, &axis, (float)(rot->angle*ds) );
					float dx, dy, dz;

					dx = (float)rot->ref.x;
					dy = (float)rot->ref.y;
					dz = (float)rot->ref.z;

					T._41 = dx - dx * T._11 - dy * T._21 - dz * T._31;
					T._42 = dy - dx * T._12 - dy * T._22 - dz * T._32;
					T._43 = dz - dx * T._13 - dy * T._23 - dz * T._33;

					AnimateComponent( AnimComp, T );
				}
				break;
			case MGROUP_TRANSFORM::SCALE:
				{
					MGROUP_SCALE *scale = (MGROUP_SCALE*)AnimComp->trans;
					s0 = (s0 - AnimComp->state0)/(AnimComp->state1 - AnimComp->state0);
					s1 = (s1 - AnimComp->state0)/(AnimComp->state1 - AnimComp->state0);
					D3DXMatrixIdentity( &T );

					T._11 = (float)( ( s1 * ( scale->scale.x - 1 ) + 1 ) / ( s0 * ( scale->scale.x - 1 ) + 1 ) );
					T._22 = (float)( ( s1 * ( scale->scale.y - 1 ) + 1 ) / ( s0 * ( scale->scale.y - 1 ) + 1 ) );
					T._33 = (float)( ( s1 * ( scale->scale.z - 1 ) + 1 ) / ( s0 * ( scale->scale.z - 1 ) + 1 ) );
					T._41 = (float)scale->ref.x * ( 1.0f - T._11 );
					T._42 = (float)scale->ref.y * ( 1.0f - T._22 );
					T._43 = (float)scale->ref.z * ( 1.0f - T._33 );

					AnimateComponent( AnimComp, T );
				}
				break;
			case MGROUP_TRANSFORM::TRANSLATE:
				{
					MGROUP_TRANSLATE *trans = (MGROUP_TRANSLATE*)AnimComp->trans;
					D3DXMatrixIdentity( &T );
					T._41 = (float)(trans->shift.x*ds);
					T._42 = (float)(trans->shift.y*ds);
					T._43 = (float)(trans->shift.z*ds);

					AnimateComponent( AnimComp, T );
				}
				break;
			case MGROUP_TRANSFORM::NULLTRANSFORM:
				D3DXMatrixIdentity( &T );
				AnimateComponent( AnimComp, T );
				break;
		}
	}
}

void vVessel::AnimateComponent( ANIMATIONCOMP *AnimComp, D3DXMATRIX &T ) {
	UINT j;
	MGROUP_TRANSFORM *trans = AnimComp->trans;

	if( trans->mesh != LOCALVERTEXLIST ) {
		if( trans->mesh >= nmesh )
			return;
		if( trans->grp )
			for( j = 0; j < trans->ngrp; j++ )
				MESH[trans->mesh].MSH->TransformGroup( trans->grp[j], &T );	//add this!
		else
			MESH[trans->mesh].MSH->TransformAll( &T );	//add this!
	}

	for( j = 0; j < AnimComp->nchildren; j++ ) {
		ANIMATIONCOMP *Child = AnimComp->children[j];
		AnimateComponent( Child, T );

		switch( Child->trans->Type() ) {
		case MGROUP_TRANSFORM::ROTATE:
			{
				MGROUP_ROTATE *rot = (MGROUP_ROTATE*)Child->trans;
				TransformPoint( rot->ref, T );
				TransformDirection( rot->axis, T, true );
			}
			break;
		case MGROUP_TRANSFORM::SCALE:
			{
				MGROUP_SCALE *scale = (MGROUP_SCALE*)Child->trans;
				TransformPoint( scale->ref, T );
			}
			break;
		case MGROUP_TRANSFORM::TRANSLATE:
			{
				MGROUP_TRANSLATE *trans = (MGROUP_TRANSLATE*)Child->trans;
				TransformDirection( trans->shift, T, false );
			}
			break;
		case MGROUP_TRANSFORM::NULLTRANSFORM:
			break;
		}
	}
}

void vVessel::TransformPoint( VECTOR3 &p, D3DXMATRIX &T ) {
	double x = p.x * T._11 + p.y * T._21 + p.z * T._31 + T._41;
	double y = p.x * T._12 + p.y * T._22 + p.z * T._32 + T._42;
	double z = p.x * T._13 + p.y * T._23 + p.z * T._33 + T._43;
	double w = p.x * T._14 + p.y * T._24 + p.z * T._34 + T._44;

	p.x = x / w;
	p.y = y / w;
	p.z = z / w;

	bsRecompute = true;
}

void vVessel::TransformDirection( VECTOR3 &a, D3DXMATRIX &T, bool normalize ) {
	double x = a.x * T._11 + a.y * T._21 + a.z * T._31;
	double y = a.x * T._12 + a.y * T._22 + a.z * T._32;
	double z = a.x * T._13 + a.y * T._23 + a.z * T._33;

	a.x = x;
	a.y = y;
	a.z = z;

	if( normalize ) {
		double len = sqrt( x*x + y*y + z*z );
		a.x /= len;
		a.y /= len;
		a.z /= len;
	}

	bsRecompute = true;
}
#pragma endregion
//=====================================
//		Update and render
//=====================================

void vVessel::SaveData() {
	const UINT midx = (UINT)-1;
	double nstate;

	vObject::SaveData();
	if( !active )	return;

	//animations:
	for( DWORD j = 0; j < nanim; j++ ) {
		if( !ANIM[j].ncomp )
			continue;
		if( animstate[j] != ( nstate = ANIM[j].state ) ) {
			Animate( midx, j, nstate );
			animstate[j] = nstate;
		}
	}

	//orbital lighting:
	nxtsimt = 0.0;
	simt = oapiGetSimTime();
	if( simt > nxtsimt ) {
		SunLight = D3D11Mesh::WhiteSunLight;
		D3DXVECTOR3 _one( 1, 1, 1 );

		OBJHANDLE hRef = VSL->GetSurfaceRef();
		if( hRef ) {
			OBJHANDLE hSun = oapiGetGbodyByIndex( 0 );

			VECTOR3	PPos, SPos;

			oapiGetGlobalPos( hSun, &SPos );
			oapiGetGlobalPos( hRef, &PPos );

			VECTOR3
				S = SPos - GPos_new,
				P = GPos_new - PPos;

			double
				r = length( P ),
				s = length( S ),
				pres = 1.0,
				size = oapiGetSize( hRef );
			double grav = oapiGetMass( hRef )*6.67259e-11 / (size*size);

			float
				aalt = 1.0f,
				amb0 = 0.0f,
				disp = 0.0f,
				amb  = 0.0f,
				aq   = 0.342f,
				ae   = 0.242f,
				al   = 0.0f,
				k    = float( sqrt( r*r - size*size ) ),		// Horizon distance
				alt  = float( r - size),
				rs   = float( oapiGetSize( hSun )/s ),
				ac   = float( -dotp( S, P )/(r*s) );

			if( ac > 1.0f )				ac = 1.0f;
			if( ac < -1.0f )			ac = -1.0f;

			ac = acos(ac) - asin(float(size/r));
	
			if( ac > 1.39f )			ac = 1.39f;
			if( ac < -1.39f )			ac = -1.39f;

			float h = tan( ac );

			const ATMCONST *atm = ( oapiGetObjectType( hRef ) == OBJTP_PLANET ? oapiGetPlanetAtmConstants( hRef ) : NULL );

			if( atm ) {
				aalt = float( atm->p0*log( atm->p0/pres )/( atm->rho0*grav ));
				amb0 = float( min( 0.7, log( atm->rho0 + 1.0f )*0.4) );
				disp = float( max( 0.02, min(0.9, log(atm->rho0 + 1.0)) ) );
			}

			if( alt > 10e3f )	al = aalt/k;
			else				al = 0.173f;

			D3DXVECTOR3
				lcol(1,1,1),
				r0 = _one - D3DXVECTOR3( 0.45f, 0.75f, 1.0f )*disp;

			if( atm ) {
				lcol = (r0 + (_one - r0) * saturate( (h/al) ) ) * saturate((h + rs)/(2.0f*rs));
				amb = amb0 / (alt*0.5e-4f + 1.0f);
				amb  = saturate( max( amb*saturate(((h + ae)/aq) - 0.05f), fAmbient) );
				lcol *= 1.0f - amb*0.5f; // reduce direct light component to avoid overexposure
			} 
			else {
				lcol = r0 * saturate( (h + rs)/(2.0f*rs) ); 
				amb  = fAmbient;
				lcol *= 1.0f - amb*0.5f; // reduce direct light component to avoid overexposure
			}

			SunLight.diffuse = SunLight.specular = D3DXCOLOR( lcol.x, lcol.y, lcol.z, 1.0f );
			SunLight.ambient = D3DXCOLOR( amb, amb, amb, 1.0f );
		}
		nxtsimt = simt;
		nxtsimt += 0.5f;
	}
	VECTOR3 pos( GPos );
	normalise( pos );
	SunLight.SunDir = D3DXVECTOR4( (float)pos.x, (float)pos.y, (float)pos.z, 1.0f );

	//exhausts:
	DWORD j;
	CDir = tmul( mROT, CPos );
	j = VSL->GetExhaustCount();
	if( nex != j ) {
		delete [ ] espc;
		espc = new ExhaustData [j];
		nex = j;
	}
	EXHAUSTSPEC ex;
	for( j = 0; j < nex; j++ ) {
		VSL->GetExhaustSpec( j, &ex );
		espc[j].ldir = *ex.ldir;
		espc[j].lofs = ex.lofs;
		espc[j].lpos = *ex.lpos;
		espc[j].level = *ex.level;
		espc[j].modulate = ex.modulate;
		espc[j].wsize = ex.wsize;
		espc[j].lsize = ex.lsize;
		espc[j].tex = ex.tex ? (Texture*)ex.tex : def_Exhaust;
	}

	//reeentry:
	rspc.pressure = VSL->GetAtmDensity();
	rspc.airspeed = VSL->GetAirspeed();
	rspc.size = VSL->GetSize()*1.7;
	VSL->GetShipAirspeedVector( rspc.d );
	VSL->GlobalRot( rspc.d, rspc.d );
	normalise( rspc.d );

	//beacons (if any):
	nbls = 0;
	const BEACONLIGHTSPEC *bls = VSL->GetBeacon( 0 );
	if( bls ) {
		for( j = 0; (bls = VSL->GetBeacon( j )); j++ ) {
			if( bls->active ) {
				if( nbls == nblsmax ) {
					BeaconData *tmp = new BeaconData [nblsmax+16];
					memset( tmp, 0, sizeof(BeaconData)*(nblsmax+16) );
					memcpy( tmp, BC, sizeof(BeaconData)*nblsmax );
					delete [ ] BC;
					BC = tmp;
					nblsmax += 16;
				}
				memcpy( &BC[nbls].bls, bls, sizeof(BEACONLIGHTSPEC) );
				BC[nbls].pos = *bls->pos;
				BC[nbls].col = *bls->col;
				nbls++;
			}
		}
	}
	else
		nbls = 0;
}

void vVessel::Update() {
	if( !active )	return;

	vObject::Update();
	for( DWORD j = 0; j < nmesh; j++ ) {
		if( !MESH[j].MSH )	continue;
		MESH[j].MSH->Update();
	}
}

//=========================================================================
//				Shadows
//=========================================================================

void vVessel::RenderShadows( vPlanet *PLN ) {
	if( !active )	return;

	float alpha = 0.5f;

	static const double eps = 1e-2, shadow_elev_limit = 0.07;
	double d, alt;
	VECTOR3
		sd = GPos,
		pvr = GPos - PLN->GPos;

	d = length( pvr );
	alt = d - PLN->rad;
	if( alt*eps > rad )		return;

	normalise( sd );

	// calculate the intersection of the vessel's shadow with the planet surface
	double fac1 = dotp( sd, pvr );
	if( fac1 > 0.0 )				return;		// shadow doesn't intersect planet surface	
	double csun = -fac1/d;						// sun elevation above horizon
	if( csun < shadow_elev_limit )	return;		// sun too low to cast shadow
	double arg  = fac1*fac1 - ( dotp( pvr, pvr ) - PLN->rad*PLN->rad );
	if( arg <= 0.0 )				return;		// shadow doesn't intersect with planet surface
	double a = -fac1 - sqrt(arg);

	VECTOR3 sdv = tmul( mROT, sd );	// projection direction in vessel frame
	VECTOR3 shp = sdv*a;			// projection point
	VECTOR3 hnp = sd*a + pvr;
	normalise( hnp );				// horizon normal
	VECTOR3 hn = tmul( mROT, hnp );	// horizon normal in vessel frame

	// perform projections
	double nr0	= dotp( hn, shp );
	double nd	= dotp( hn, sdv );
	VECTOR3 sdvs = sdv/nd;

	D3DXMATRIX mProj, mProjWorld, mProjWorldShift;

	mProj._11 = 1.0f - (float)(sdvs.x*hn.x);
	mProj._12 =      - (float)(sdvs.y*hn.x);
	mProj._13 =      - (float)(sdvs.z*hn.x);
	mProj._14 = 0;
	mProj._21 =      - (float)(sdvs.x*hn.y);
	mProj._22 = 1.0f - (float)(sdvs.y*hn.y);
	mProj._23 =      - (float)(sdvs.z*hn.y);
	mProj._24 = 0;
	mProj._31 =      - (float)(sdvs.x*hn.z);
	mProj._32 =      - (float)(sdvs.y*hn.z);
	mProj._33 = 1.0f - (float)(sdvs.z*hn.z);
	mProj._34 = 0;
	mProj._41 =        (float)(sdvs.x*nr0);
	mProj._42 =        (float)(sdvs.y*nr0);
	mProj._43 =        (float)(sdvs.z*nr0);
	mProj._44 = 1;

	D3DXMatrixMultiply( &mProjWorld, &mProj, &mWorld );

	float scale = float( min( 1, ( csun - 0.07 )/0.015 ) );
	if( scale < 1 )		alpha *= scale;
	
	D3DXMATRIX mProjWorldOfs;
	for( DWORD j = 0; j < nmesh; j++ ) {
		if( !MESH[j].MSH )		continue;
		if( !(MESH[j].vis_mode & MESHVIS_EXTERNAL) )	continue;
		if( MESH[j].vis_mode == MESHVIS_VC )			continue;

		D3DXMatrixMultiply( &mProjWorldOfs, &MESH[j].ofs, &mProjWorld );

		MESH[j].MSH->RenderShadows( &mProjWorldOfs, alpha );
	}
}

//=========================================================================
//				Beacons, Exhaust, Reentry
//=========================================================================

void vVessel::InitRenderBeacons() {
	const UINT BBvertexStride = 20, VBOffset = 0;
	iCtx->IASetIndexBuffer(nullptr, DXGI_FORMAT_R32_UINT, 0);
	iCtx->IASetVertexBuffers( 0, 1, &vStar::VB, &BBvertexStride, &VBOffset );
	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );
	//iCtx->OMSetDepthStencilState( DSS_NoDepth_NoStencil, 0 );
}

void vVessel::RenderBeacons() {
	if( !active )	return;
	if( !nbls )		return;

	for( DWORD j = 0; j < nbls; j++ ) 
	{
		if( BC[j].bls.period && (fmod( simt + BC[j].bls.tofs, BC[j].bls.period ) > BC[j].bls.duration ) )
			continue;
		double size = BC[j].bls.size;
		if( CDist > 50.0 )
			size *= pow( CDist/50.0f, BC[j].bls.falloff );

		//render spot
		VECTOR3 cpos( CPos );
		if( BC[j].bls.pos )
			cpos += mul( mROT, BC[j].pos );

		double intensity = 1.0;

		D3DXMATRIX W;
		D3DXVECTOR3 vPos( (float)cpos.x, (float)cpos.y, (float)cpos.z ), vCam;
		VECTOR3 vPosD(cpos), vCamD(cpos);
		normalise(vCamD);
		D3DMAT_CreateX_Billboard( &vCamD, &vPosD, size, &W );

		/*D3DXVec3Normalize( &vCam, &vPos );
		D3DMAT_CreateX_Billboard( &vCam, &vPos, (float)size, &W );*/
		D3DXVECTOR4 color( (float)BC[j].col.x, (float)BC[j].col.y, (float)BC[j].col.z, (float)intensity );

		//render spot...
		D3DXMATRIX M = W * *SC->GetVP();
		D3DXVECTOR4 ex = D3DXVECTOR4( 0, -1, 1, 1 );
		D3DXVECTOR4 ex0;
		D3DXVec4Transform( &ex0, &ex, &M );
		iCtx->UpdateSubresource( cb_D3DXMATRIX_x1, 0, NULL, &M, 0, 0 );
		iCtx->UpdateSubresource( cb_D3DXVECTOR4, 0, NULL, color, 0, 0 );

		iCtx->PSSetShaderResources( 0, 1, Ball[BC[j].bls.shape]->GetSRV() );

		iCtx->Draw( 4, 0 );
	}
}

void vVessel::InitRenderExhaust() {
	const UINT Stride = 20, VBOffset = 0;

	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	iCtx->IASetInputLayout( IL_BillBoard );
	iCtx->IASetVertexBuffers( 0, 1, &VB, &Stride, &VBOffset );
	iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );

	iCtx->VSSetConstantBuffers( 0, 1, &cb_D3DXMATRIX_x1 );
	iCtx->VSSetShader( VS_BillBoard, NULL, NULL );
	
	iCtx->PSSetConstantBuffers( 0, 1, &cb_D3DXVECTOR4 );
	iCtx->PSSetSamplers( 0, 1, &SS_Linear_Wrap );
	iCtx->PSSetShader( PS_BillBoard, NULL, NULL );

	iCtx->OMSetBlendState( BS_SrcAlpha, D3DXCOLOR( 1, 1, 1, 1 ), 0xFFFFFFFF );
	iCtx->OMSetDepthStencilState( DSS_TestDepthOnly_NoStencil, 0 );
}

void vVessel::RenderExhaust() {
	if( !active )	return;
	if( !nex )		return;

	MaxExhaustLenght = 0.0;
	D3D11_MAPPED_SUBRESOURCE SRes;
	//Texture *tex;
	for( DWORD j = 0; j < nex; j++ ) {
		if( espc[j].level == 0.0 )
			continue;

		double alpha = espc[j].level;
		if( espc[j].modulate )
			alpha *= ((1.0 - espc[j].modulate) + (double)rand()*espc[j].modulate/(double)RAND_MAX);

		VECTOR3
			edir = -espc[j].ldir,
			ref = espc[j].lpos - espc[j].ldir*espc[j].lofs;

		VECTOR3 sdir = crossp( CDir, edir );
		normalise( sdir );
		VECTOR3 tdir = crossp( CDir, sdir );
		normalise( tdir );

		//exhaust
		float
			rx = (float)ref.x,
			ry = (float)ref.y,
			rz = (float)ref.z,

			sx = (float)(sdir.x*espc[j].wsize),
			sy = (float)(sdir.y*espc[j].wsize),
			sz = (float)(sdir.z*espc[j].wsize),

			ex = (float)(edir.x*espc[j].lsize),
			ey = (float)(edir.y*espc[j].lsize),
			ez = (float)(edir.z*espc[j].lsize);

		_VB[1].pos.x = ( _VB[0].pos.x = rx + sx ) + ex;
		_VB[1].pos.y = ( _VB[0].pos.y = ry + sy ) + ey;
		_VB[1].pos.z = ( _VB[0].pos.z = rz + sz ) + ez;
		_VB[3].pos.x = ( _VB[2].pos.x = rx - sx ) + ex;
		_VB[3].pos.y = ( _VB[2].pos.y = ry - sy ) + ey;
		_VB[3].pos.z = ( _VB[2].pos.z = rz - sz ) + ez;

		//flare
		double wscale = espc[j].wsize*7.0;
		sx *= 7.0, sy *= 7.0, sz *= 7.0;

		float
			tx = (float)(tdir.x*wscale),
			ty = (float)(tdir.y*wscale),
			tz = (float)(tdir.z*wscale);

		_VB[4].pos.x = rx - sx + tx;	_VB[5].pos.x = rx + sx + tx;
		_VB[4].pos.y = ry - sy + ty;	_VB[5].pos.y = ry + sy + ty;
		_VB[4].pos.z = rz - sz + tz;	_VB[5].pos.z = rz + sz + tz;
		_VB[6].pos.x = rx - sx - tx;	_VB[7].pos.x = rx + sx - tx;
		_VB[6].pos.y = ry - sy - ty;	_VB[7].pos.y = ry + sy - ty;
		_VB[6].pos.z = rz - sz - tz;	_VB[7].pos.z = rz + sz - tz;

		memset( &SRes, 0, sizeof(SRes) );
		iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
		memcpy( SRes.pData, _VB, 20*8 );
		iCtx->Unmap( VB, 0 );

		D3DXMATRIX M = mWorld * *SC->GetVP();
		iCtx->UpdateSubresource( cb_D3DXMATRIX_x1, 0, NULL, &M, 0, 0 );
		iCtx->UpdateSubresource( cb_D3DXVECTOR4, 0, NULL, D3DXVECTOR4(  1.0f, 1.0f, 1.0f, 1.0f ), 0, 0 );

		iCtx->PSSetShaderResources( 0, 1, espc[j].tex->GetSRV() );
		
		iCtx->DrawIndexed( 12, 0, 0 );

		if( espc[j].lsize > MaxExhaustLenght )
			MaxExhaustLenght = (float)espc[j].lsize;
	}
}

void vVessel::RenderReentry() {
	if( !def_Reentry )	return;

	float www = float( rspc.pressure*rspc.airspeed*rspc.airspeed*rspc.airspeed );
	float ints = (float)(max( 0.0, (www - 100e6))/500e6);

	if( ints > 1.0f )		ints = 1.0f;
	if( ints < 0.01f )		return;

	float x = float( dotp( rspc.d, unit( CPos ) ) );
	if( x < 0.0f )			x = -x;
	x = pow( x, 0.3f );

	float alpha_B = (x*0.4f + 0.6f)*ints;
	float alpha_A = (1.0f - x*0.5f)*ints*1.2f;
	float size = float(rspc.size)*1.7f;

	D3DXVECTOR3
		vPosA( (float)CPos.x, (float)CPos.y, (float)CPos.z ),
		vDir( (float)rspc.d.x, (float)rspc.d.y, (float)rspc.d.z ),
		vPosB = vPosA + vDir*((float)rspc.size*0.25f),
		vCam;
	//vectors...
	float REVtxB[20] = {
		0.0f, -1.0f, -1.0f,		0.51f, 0.01f,
		0.0f, -1.0f,  1.0f,		0.99f, 0.01f,
		0.0f,  1.0f, -1.0f,		0.51f, 0.49f,
		0.0f,  1.0f,  1.0f,		0.99f, 0.49f
	};

	float X = 4.5f + sin( fmod( (float)simt*60.0f, 6.283185f ))*0.5f;

	float REVtxA[20] = {
		0.0f,  1.0f,  1.0f,		0.49f, 0.01f,
		0.0f,  1.0f,  -X,		0.49f, 0.99f,
		0.0f, -1.0f,  1.0f,		0.01f, 0.01f,
		0.0f, -1.0f,  -X,		0.01f, 0.99f
	};

	D3DXMATRIX WA, WB;
	D3DXVec3Normalize( &vCam, &vPosA );
	D3DMAT_CreateX_Billboard( &vCam, &vPosB, (float)rspc.size*(0.8f + x*0.02f), &WB );
	D3DMAT_CreateX_Billboard( &vCam, &vPosA, &vDir, (float)rspc.size, (float)rspc.size, &WA );

	//B:
	D3D11_MAPPED_SUBRESOURCE SRes;
	memset( &SRes, 0, sizeof(SRes) );
	iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
	memcpy( SRes.pData, REVtxB, 80 );
	iCtx->Unmap( VB, 0 );

	WB *= *SC->GetVP();
	iCtx->UpdateSubresource( cb_D3DXMATRIX_x1, 0, NULL, &WB, 0, 0 );
	iCtx->UpdateSubresource( cb_D3DXVECTOR4, 0, NULL, D3DXVECTOR4( 1.0f, 1.0f, 1.0f, alpha_B ), 0, 0 );	
	iCtx->PSSetShaderResources( 0, 1, def_Reentry->GetSRV() );
	iCtx->DrawIndexed( 6, 0, 0 );

	//A:
	memset( &SRes, 0, sizeof(SRes) );
	iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
	memcpy( SRes.pData, REVtxA, 80 );
	iCtx->Unmap( VB, 0 );

	WA *= *SC->GetVP();
	iCtx->UpdateSubresource( cb_D3DXMATRIX_x1, 0, NULL, &WA, 0, 0 );
	iCtx->UpdateSubresource( cb_D3DXVECTOR4, 0, NULL, D3DXVECTOR4( 1.0f, 1.0f, 1.0f, alpha_A ), 0, 0 );	
	iCtx->DrawIndexed( 6, 0, 0 );
}

void vVessel::InitRender()
{
	//set vars
	iCtx->IASetInputLayout( D3D11Mesh::IL_MESH_VERTEX );
	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	iCtx->VSSetShader( VS_Mesh, NULL, NULL );
	iCtx->RSSetState( RS_CullBack_Solid );
	iCtx->OMSetDepthStencilState( D3D11Mesh::DSS_Mesh, 0 );

	iCtx->VSSetConstantBuffers( 0, 1, &D3D11Mesh::cb_VS_per_frame );
	iCtx->VSSetConstantBuffers( 1, 1, &D3D11Mesh::cb_VS_per_object );
	iCtx->VSSetConstantBuffers( 2, 1, &D3D11Mesh::cb_VS_per_group );
	iCtx->VSSetConstantBuffers( 3, 1, &beaconsBuffer);

	iCtx->PSSetShader( PS_Mesh, NULL, NULL );
	iCtx->PSSetConstantBuffers( 0, 1, &D3D11Mesh::cb_PS_per_object );
	iCtx->PSSetConstantBuffers( 1, 1, &D3D11Mesh::cb_PS_per_group );

	iCtx->PSSetSamplers( 0, 1, &D3D11Mesh::SS_Texture );

	//update per frame data
	D3D11_MAPPED_SUBRESOURCE SRes;
	memset( &SRes, 0, sizeof(SRes) );
	iCtx->Map( D3D11Mesh::cb_VS_per_frame, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
	BYTE *data = (BYTE*)SRes.pData;

	ATM_FOG_PARAMS *AP = SC->GetAtmParams();	//get atmosphere params of the nearest planet (if any).
	if( AP ) 
	{
		D3D11Mesh::VSCB_per_frame.FogColor = D3DXVECTOR3( AP->FogColor.r, AP->FogColor.g, AP->FogColor.b );
		D3D11Mesh::VSCB_per_frame.FogDensity = AP->FogDensity;
		D3D11Mesh::VSCB_per_frame.HazeMode = AP->HazeMode;
		memcpy( data, &D3D11Mesh::VSCB_per_frame, 20 );
	}
	else 
	{
		D3D11Mesh::VSCB_per_frame.HazeMode = 0;
		memcpy( data, &D3D11Mesh::VSCB_per_frame, 4 );
	}

	UINT cnt = SC->GetLocalLightCount();
	if( cnt ) {
		D3D11Light *LL = SC->GetLocalLights();
		memcpy( data+32, LL, cnt*96 );
	}
	iCtx->Unmap( D3D11Mesh::cb_VS_per_frame, 0 );
}

void vVessel::RenderCompleted()
{
	//we need to pass empty buffer so other meshed will be rendered properly
	/*cp_VS_beaconLightSources lights;
	ZeroMemory(&lights, sizeof(cp_VS_beaconLightSources));
	iCtx->UpdateSubresource(beaconsBuffer, 0, nullptr, &lights, 0, 0);*/
}

//=========================================================================
//				External meshes
//=========================================================================

void vVessel::Render() {

	memcpy( &D3D11Mesh::VSCB_per_object.Sun, &SunLight, 64 );
	D3D11Mesh::VSCB_per_object.VP = *SC->GetVP();
	D3D11Mesh::VSCB_per_object.ActiveLightsCount = SC->GetLocalLightCount();
	iCtx->UpdateSubresource( D3D11Mesh::cb_VS_per_object, 0, NULL, &D3D11Mesh::VSCB_per_object, 0, 0 );

	memcpy( &D3D11Mesh::PSCB_per_object.Sun, &SunLight, 64 );
	iCtx->UpdateSubresource( D3D11Mesh::cb_PS_per_object, 0, NULL, &D3D11Mesh::PSCB_per_object, 0, 0 );

	//setting up cbuffer for beacon lights
	cp_VS_beaconLightSources lights;
	ZeroMemory(&lights, sizeof(cp_VS_beaconLightSources));

	for(UINT j = 0; j < BEACONS_COUNT && j < nbls; j++)
	{
		BEACONLIGHTSPEC & beacon = BC[j].bls;
		if (beacon.active && beacon.period)
		{
			beacon.active = fmod(simt+beacon.tofs, beacon.period) <= beacon.duration;
		}
		lights.lightSources[j].color = D3DXVECTOR4((float)beacon.col->x, (float)beacon.col->y, (float)beacon.col->z, 1.0f);
		lights.lightSources[j].enabled = (beacon.active) ? 1 : 0;
		lights.lightSources[j].falloffDistance = (float) (beacon.falloff / 4.0);
		lights.lightSources[j].size = (float)beacon.size;
		VECTOR3 cpos = { 0.0f, 0.0f, 0.0f };
		if (BC[j].bls.pos)
		{
			cpos = *BC[j].bls.pos;
		}
		lights.lightSources[j].position = D3DXVECTOR4((float)cpos.x, (float)cpos.y, (float)cpos.z, 1.0f);
	}
	iCtx->UpdateSubresource(beaconsBuffer, 0, nullptr, &lights, 0, 0);
	

	D3DXMATRIX OfsWorld;
	for( DWORD j = 0; j < nmesh; j++ ) {
		if( !MESH[j].MSH )							continue;

		if( !(MESH[j].vis_mode & MESHVIS_EXTERNAL) )	continue;

		OfsWorld = MESH[j].ofs*mWorld;
		MESH[j].MSH->Render( this, &OfsWorld );
	}	
}

//=========================================================================
//				Bouding boxes
//=========================================================================

void vVessel::RenderBoundingBox() {
	D3DXMATRIX OfsWorld;
	for( DWORD j = 0; j < nmesh; j++ ) {
		if( !MESH[j].MSH )								continue;

		if( !(MESH[j].vis_mode & MESHVIS_EXTERNAL) )	continue;

		OfsWorld = MESH[j].ofs*mWorld;
		MESH[j].MSH->RenderBoundingBox(&OfsWorld );
	}
}

//=========================================================================
//				Cockpit
//=========================================================================

void vVessel::RenderCockpit() {

	memcpy( &D3D11Mesh::VSCB_per_object.Sun, &SunLight, 64 );
	D3D11Mesh::VSCB_per_object.VP = *SC->GetVP();
	D3D11Mesh::VSCB_per_object.ActiveLightsCount = 0;
	iCtx->UpdateSubresource( D3D11Mesh::cb_VS_per_object, 0, NULL, &D3D11Mesh::VSCB_per_object, 0, 0 );

	memcpy( &D3D11Mesh::PSCB_per_object.Sun, &SunLight, 64 );
	iCtx->UpdateSubresource( D3D11Mesh::cb_PS_per_object, 0, NULL, &D3D11Mesh::PSCB_per_object, 0, 0 );

	D3DXMATRIX OfsWorld;
	for( DWORD j = 0; j < nmesh; j++ ) {
		if( !MESH[j].MSH )							continue;

		if( !(MESH[j].vis_mode & MESHVIS_COCKPIT) )	continue;
		if( !(MESH[j].vis_mode & MESHVIS_EXTPASS) )	continue;
		OfsWorld = MESH[j].ofs*mWorld;
		MESH[j].MSH->Render( this, &OfsWorld );
	}

	for( DWORD j = 0; j < nmesh; j++ ) {
		if( !MESH[j].MSH )							continue;

		if( !(MESH[j].vis_mode & MESHVIS_COCKPIT) )	continue;

		OfsWorld = MESH[j].ofs*mWorld;
		MESH[j].MSH->Render( this, &OfsWorld );
	}
}

//=========================================================================
//				Virtual cockpit
//=========================================================================

void vVessel::RenderVC() {

	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	iCtx->IASetInputLayout( D3D11Mesh::IL_MESH_VERTEX );

	iCtx->VSSetShader( D3D11Mesh::VS_VC, NULL, NULL );
	iCtx->VSSetConstantBuffers( 0, 1, &D3D11Mesh::cb_VS_VC_per_frame );
	iCtx->VSSetConstantBuffers( 1, 1, &cb_D3DXMATRIX_x1 );

	iCtx->PSSetConstantBuffers( 0, 1, &D3D11Mesh::cb_PS_per_object );
	iCtx->PSSetConstantBuffers( 1, 1, &D3D11Mesh::cb_PS_per_group );
	iCtx->PSSetSamplers( 0, 1, &D3D11Mesh::SS_Texture );

	iCtx->UpdateSubresource( D3D11Mesh::cb_VS_VC_per_frame, 0, NULL, SC->GetVP_VC(), 0, 0 );

	memcpy( &D3D11Mesh::PSCB_per_object.Sun, &SunLight, 64 );
	iCtx->UpdateSubresource( D3D11Mesh::cb_PS_per_object, 0, NULL, &D3D11Mesh::PSCB_per_object, 0, 0 );

	iCtx->OMSetBlendState( BS_SrcAlpha, D3DXCOLOR( 1, 1, 1, 1 ), 0xFFFFFFFF );
	iCtx->OMSetDepthStencilState( D3D11Mesh::DSS_Mesh, 0 );
	iCtx->RSSetState( RS_CullBack_Solid );

	D3DXMATRIX OfsWorld;
	for( DWORD j = 0; j < nmesh; j++ ) {
		if( !MESH[j].MSH )		continue;

		if( !(MESH[j].vis_mode & MESHVIS_EXTPASS ) )	continue;
		OfsWorld = MESH[j].ofs*mWorld;
		MESH[j].MSH->RenderVC( this, &OfsWorld, j/*, &SunLight*/ );
	}

	for( DWORD j = 0; j < nmesh; j++ ) {
		if( !MESH[j].MSH )		continue;

		if( !(MESH[j].vis_mode & MESHVIS_VC) && !(MESH[j].vis_mode & MESHVIS_COCKPIT) )	continue;

		OfsWorld = MESH[j].ofs*mWorld;
		MESH[j].MSH->RenderVC( this, &OfsWorld, j/*, &SunLight*/ );
	}
}

//=====================================
//		Vessel events
//=====================================

MESHHANDLE vVessel::clbkGetMesh( UINT idx ) {
	return ( idx < nmesh ? MESH[idx].MSH : NULL );
}

int vVessel::VisEvent( DWORD msg, UINT context ) {

	switch( msg ) {

	//adds meshes:
	case EVENT_VESSEL_INSMESH://context = mesh index.
		return InsertMesh( context );

	//deletes one or all mesh:
	case EVENT_VESSEL_DELMESH://context = mesh index, -1 for all.
		return DelMesh( context );

	//sets mesh visibility mode:
	case EVENT_VESSEL_MESHVISMODE://context = mesh index.		
		return SetVisibilityMode( context );

	case EVENT_VESSEL_RESETANIM:		
		return ResetAnimations( context );

	case EVENT_VESSEL_CLEARANIM://context UINT[ 1 reset animations, 0 leave animation at the current state		
		return ClearAnimations( context );

	case EVENT_VESSEL_DELANIM://context = animation index.		
		return DelAnimation( context );

	case EVENT_VESSEL_NEWANIM://context = animation index.	
		return NewAnimation( context );

	//sets mesh offset in the vessel:
	case EVENT_VESSEL_MESHOFS://context = mesh index.		
		return SetMeshOffset( context );

	case EVENT_VESSEL_MODMESHGROUP:
		//which group ???
		return 0;
	}
	return 0;
}

int vVessel::InsertMesh( UINT idx ) {
	DWORD pmesh = nmesh;

	if( idx >= nmesh ) {
		VSLMESH *tmp = new VSLMESH [idx+1];
		memset( tmp, 0, sizeof(VSLMESH)*(idx+1) );
		if( nmesh ) {
			memcpy( tmp, MESH, sizeof(VSLMESH)*nmesh );
			delete [ ] MESH;
		}
		MESH = tmp;
		nmesh = idx + 1;
	}
	else
		if( MESH[idx].MSH )
			delete MESH[idx].MSH;

	VECTOR3 ofs;
	D3D11TPLMesh *tMesh;
	MESHHANDLE hMesh;
	hMesh = VSL->GetMeshTemplate( idx );
	if( bAMSO )	MM->UpdateAMSOMesh( hMesh );
	if( hMesh && (tMesh = MM->Find( hMesh )) )
		MESH[idx].MSH = new D3D11Mesh( tMesh );
	else {
		if( (hMesh = VSL->CopyMeshFromTemplate( idx )) ) {
			MESH[idx].MSH = new D3D11Mesh( hMesh, true );
			oapiDeleteMesh( hMesh );
		}
	}

	if( MESH[idx].MSH ) {
		MESH[idx].vis_mode = VSL->GetMeshVisibilityMode( idx );
		VSL->GetMeshOffset( idx, ofs );
		D3DXMatrixIdentity( &MESH[idx].ofs );
		MESH[idx].ofs._41 = (float)ofs.x;
		MESH[idx].ofs._42 = (float)ofs.y;
		MESH[idx].ofs._43 = (float)ofs.z;
	}
	return 1;
}

int vVessel::DelMesh( UINT idx ) {
	if( idx == (UINT)-1 ) {
		for( DWORD j = 0; j < nmesh; j++ ) {
			delete MESH[j].MSH;
			MESH[j].MSH = NULL;
		}
		return 1;
	}

	if( idx >= nmesh )		return 0;
	if( !MESH[idx].MSH )	return 0;

	delete MESH[idx].MSH;
	MESH[idx].MSH = NULL;
	return 1;
}

int vVessel::SetVisibilityMode( UINT idx ) {
	if( idx >= nmesh )	return 0;

	MESH[idx].vis_mode = VSL->GetMeshVisibilityMode( idx );
	return 1;
}

int vVessel::SetMeshOffset( UINT idx ) {
	if( idx >= nmesh )	return 0;

	VECTOR3 ofs;
	D3DXMatrixIdentity( &MESH[idx].ofs );
	VSL->GetMeshOffset( idx, ofs );
	MESH[idx].ofs._41 = (float)ofs.x;
	MESH[idx].ofs._42 = (float)ofs.y;
	MESH[idx].ofs._43 = (float)ofs.z;
	return 1;
}

int vVessel::ResetAnimations( UINT idx ) {
	DWORD j;

	//update
	if( animstate )	delete [ ] animstate;
	nanim = VSL->GetAnimPtr( &ANIM );
	if( nanim ) {
		animstate = new double [nanim];
		for( j = 0; j < nanim; j++ )
			animstate[j] = ANIM[j].defstate;
	}
	else {
		ANIM = NULL;
		animstate = NULL;
	}

	for( j = 0; j < nmesh; j++ ) {
		if( !MESH[j].MSH )	continue;
		MESH[j].MSH->ResetTransforms();
	}

	return 1;
}

int vVessel::ClearAnimations( UINT idx ) {
	if( animstate )	{
		delete [ ] animstate;
		animstate = NULL;
	}
	nanim = 0;
	return 1;
}

int vVessel::DelAnimation( UINT idx ) {	
	return ResetAnimations( 0 );
}

int vVessel::NewAnimation( UINT idx ) {	
	return ResetAnimations( 0 );
}

float vVessel::GetBoundingSphereRad() {
	if( !bsRecompute )	return bsRad;

	float a, b, c, d, e, f; 
	a = b = c = 1e12f;
	d = e = f = -1e12f;

	for( DWORD j = 0; j < nmesh; j++ ) {
		if( !MESH[j].MSH )	continue;

		D3DXVECTOR3 q, w;

		if( MESH[j].MSH->bMeshT ) {
			D3DXVec3TransformCoord( &q, &MESH[j].MSH->AABB[0], &MESH[j].MSH->mT );
			D3DXVec3TransformCoord( &w, &MESH[j].MSH->AABB[1], &MESH[j].MSH->mT );
		}
		else
			q = MESH[j].MSH->AABB[0], w = MESH[j].MSH->AABB[1];

		if( q.x < a )	a=q.x;
		if( q.y < b )	b=q.y;
		if( q.z < c )	c=q.z;
		if( w.x > d )	d=w.x;
		if( w.y > e )	e=w.y;
		if( w.z > f )	f=w.z;
	}
	AABB[0] = D3DXVECTOR3( a, b, c );
	AABB[1] = D3DXVECTOR3( d, e, f );
	bsRecompute = false;

	return bsRad = 0.5f*sqrt((a-d)*(a-d) + (b-e)*(b-e) + (c-f)*(c-f));
}