#include "Mix/Private/Scene/Common/SkyDome.h"

#include "Mix/Graphics/IDevice.h"
#include "Mix/Graphics/IVertexBuffer.h"
#include "Mix/Graphics/IIndexBuffer.h"
#include "Mix/Graphics/ITexture.h"

#include "Mix/Private/Scene/Common/AtmosphereScatter.h"

namespace Mix{ namespace Scene{ namespace Common{

const UInt16 SkyDome::DOME_DIV_TABLE[SkyDome::QL_MAX] =
{
	10, //QL_LOW
	20, //QL_MIDDLE
	30,	//QL_HIGH
};

const UInt16 SkyDome::PANORAMA_DIV_TABLE[SkyDome::QL_MAX] =
{
	6,	//QL_LOW	
	8,	//QL_MIDDLE
	12,	//QL_HIGH
};

const Mix::Vector3 SkyDome::DOME_BASE_POS( 0.0f, 1.0f, 0.0f );

const Mix::Vector3 SkyDome::PANORAMA_BASE_POS( 1.0f, 0.0f, 0.0f );

const wchar_t* SkyDome::FAILED_CREATE = L"スカイドームの作成に失敗";

SkyDome* SkyDome::CreateInstance( Float32 radius, Float32 height )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, SkyDome, radius, height );
}

SkyDome::SkyDome( Float32 radius, Float32 height ) :
m_pAS( NULL ),
m_bEnabled( True ),
m_CRType( ISkyDome::CR_XYZ ),
m_HemisphereQuality( ISkyDome::QL_MIDDLE ),
m_pBaseTex( NULL ),
m_bCloudEnabled( True ),
m_CloudLayerNum( SkyDome::CL_MAX ),
m_PanoramaQuality( ISkyDome::QL_MIDDLE ),
m_bExistsPanorama( False ),
m_bPanoramaEnabled( False ),
m_pPanoramaTex( NULL ),
m_pVertexBuffer( NULL ),
m_pIndexBuffer( NULL )
{
	ISkyDome::HEMISPHERE_SETTINGS hsSettings;

	hsSettings.radius = radius;
	hsSettings.height = height;

	m_PlanetInfo.innerRadius = 0.0;
	m_PlanetInfo.outerRadius = 0.0;

	//初期設定を反映
	SetHemisphereSettings( hsSettings );

	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_BaseSettings.color.Set( 1.0f, 1.0f, 1.0f, 1.0f );
	m_BaseSettings.texScale = 3.0f;
	m_BaseSettings.texOpacity = 1.0f;

	m_CloudLayerSettings[SkyDome::CL_0].initialTex.Set( 0.5f, 0.5f );
	m_CloudLayerSettings[SkyDome::CL_0].vel.Set( 0.00005f, 0.0f );
	m_CloudLayerSettings[SkyDome::CL_0].scale = 10.0f;
	m_CloudLayerSettings[SkyDome::CL_0].opacity = 1.0f;
	m_CloudLayerSettings[SkyDome::CL_1].initialTex.Set( 0.0f, 0.0f );
	m_CloudLayerSettings[SkyDome::CL_1].vel.Set( 0.0001f, 0.0f );
	m_CloudLayerSettings[SkyDome::CL_1].scale = 5.0f;
	m_CloudLayerSettings[SkyDome::CL_1].opacity = 1.0f;
	m_InternalCloudLayerSettings[SkyDome::CL_0].texOffset = m_CloudLayerSettings[SkyDome::CL_0].initialTex;
	m_InternalCloudLayerSettings[SkyDome::CL_1].texOffset = m_CloudLayerSettings[SkyDome::CL_1].initialTex;

	m_CloudGlobalSettings.color.Set( 1.0f, 1.0f, 1.0f, 1.0f );
	m_CloudGlobalSettings.speed = 1.0f;
	m_CloudGlobalSettings.asEffect = True;
	m_CloudGlobalSettings.asSunLighting = False;
	m_CloudGlobalSettings.asNightness = 0.5f;
	m_CloudGlobalSettings.asCornerThreshold = 0.14f;
	m_CloudGlobalSettings.asDarknessStart = 0.2f;
	m_CloudGlobalSettings.asDarknessEnd = 0.98f;

	m_InternalCloudGlobalSettings.asCornerMul = MIX_FLOAT_RECIPROCAL( 1.0f - m_CloudGlobalSettings.asCornerThreshold );
	m_InternalCloudGlobalSettings.asDarkness = 0.0f;	// SetSunPosition で算出される

	m_PanoramaSettings.color.Set( 1.0f, 1.0f, 1.0f, 1.0f );
	m_PanoramaSettings.scale.x = m_HemisphereSettings.radius * 0.9f;
	m_PanoramaSettings.scale.y = m_PanoramaSettings.scale.x / 16.0f * 4.0f;
	m_PanoramaSettings.offsetY = m_PanoramaSettings.scale.y * 0.1f;
	m_PanoramaSettings.dist = m_HemisphereSettings.radius * 0.5f;
	m_PanoramaSettings.texRepeatNum = 0;

	for( UInt32 i = 0; i < SkyDome::CL_MAX; i++ )
	{
		m_pCloudTexs[i] = NULL;
	}

	//初期設定を反映
	SetSunPosition( Mix::Vector3::YAxis() * m_PlanetInfo.outerRadius );

	////////////////////////////////////////////////////////////////////////////////////////////////////
}

SkyDome::~SkyDome( void )
{
	MIX_ASSERT( m_pAS == NULL );

	MIX_RELEASE( m_pVertexBuffer );
	MIX_RELEASE( m_pIndexBuffer );

	MIX_RELEASE( m_pBaseTex );

	for( UInt32 i = 0; i < SkyDome::CL_MAX; i++ )
	{
		MIX_RELEASE( m_pCloudTexs[i] );
	}

	MIX_RELEASE( m_pPanoramaTex );
}

void SkyDome::RotateTexOffset( Mix::Vector2& value )
{
	while( value.x > 1.0f ) { value.x -= 1.0f; }
	while( value.x < 0.0f ) { value.x += 1.0f; }

	while( value.y > 1.0f ) { value.y -= 1.0f; }
	while( value.y < 0.0f ) { value.y += 1.0f; }
}

Boolean SkyDome::Initialize( Mix::Graphics::IDevice* pDevice, UInt32 heQuality, UInt32 paQuality, const wchar_t* pDebugName )
{
	MIX_ASSERT( heQuality < SkyDome::QL_MAX );
	MIX_ASSERT( m_pVertexBuffer == NULL );
	MIX_ASSERT( m_pIndexBuffer == NULL );

	UInt32 domeDiv;
	Float32 domeRad;
	UInt32 domeHorizSubdiv;
	UInt32 domeVertexNum;
	UInt32 domeIndexNum;

	UInt32 prDiv;
	UInt32 prVertexStart;
	UInt32 prIndexStart;
	UInt32 prVertexNum;
	UInt32 prIndexNum;

	Mix::STL::Vector<Mix::Memory::SECTION_SCENE, SkyDome::VERTEX> vertices;
	Mix::STL::Vector<Mix::Memory::SECTION_SCENE, UInt16> indices;

	SkyDome::VERTEX vertex;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 事前処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	// ドーム //

	domeDiv = SkyDome::DOME_DIV_TABLE[heQuality];
	domeRad = MIX_FLOAT_DIV( MIX_PI * 0.5f, static_cast<Float32>( domeDiv ) );
	domeHorizSubdiv = static_cast<UInt32>( MIX_FLOAT_DIV( MIX_PI * 2.0f, domeRad ) );
	domeVertexNum = 1 + domeDiv * domeHorizSubdiv;
	domeIndexNum = ( domeHorizSubdiv * 3 ) + ( ( domeDiv - 1 ) * ( domeHorizSubdiv * 6 ) );

	// パノラマ //

	if( paQuality < SkyDome::QL_MAX )
	{
		prDiv = SkyDome::PANORAMA_DIV_TABLE[paQuality];
		prVertexStart = domeVertexNum;
		prIndexStart = domeIndexNum;
		prVertexNum = ( prDiv + 1 ) * 2;
		prIndexNum = prDiv * 6;
	}
	else
	{
		prDiv = 0;
		prVertexStart = 0;
		prIndexStart = 0;
		prVertexNum = 0;
		prIndexNum = 0;
	}

	// 容量を予約 //

	MIX_ASSERT( ( domeVertexNum + prVertexNum ) <= 65535 );

	vertices.reserve( domeVertexNum + prVertexNum );
	indices.reserve( domeIndexNum + prIndexNum );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ドーム
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Matrix4x4 domeRotX;
	Mix::Matrix4x4 domeRotY;
	Mix::Vector3 domePos;

	//先端の頂点
	vertex.pos[0] = SkyDome::DOME_BASE_POS.x;
	vertex.pos[1] = SkyDome::DOME_BASE_POS.y;
	vertex.pos[2] = SkyDome::DOME_BASE_POS.z;
	vertex.pos[3] = 1.0f;
	vertex.tex[0] = 0.5f;
	vertex.tex[1] = 0.5f;
	vertices.push_back( vertex );

	//先端以降の頂点
	for( UInt16 v = 0; v < domeDiv; v++ )
	{
		domeRotX.SetRotationX( domeRad * static_cast<Float32>( v + 1 ) );

		for( UInt16 h = 0; h < domeHorizSubdiv; h++ )
		{
			domeRotY.SetRotationY( domeRad * static_cast<Float32>( h ) );

			domePos = domeRotX * SkyDome::DOME_BASE_POS;
			domePos = domeRotY * domePos;

			vertex.pos[0] = domePos.x;
			vertex.pos[1] = domePos.y;
			vertex.pos[2] = domePos.z;
			vertex.pos[3] = 1.0f;
			vertex.tex[0] = MIX_CLAMP( 0.5f - domePos.x * 0.5f, 0.0f, 1.0f );
			vertex.tex[1] = MIX_CLAMP( 0.5f - domePos.z * 0.5f, 0.0f, 1.0f );

			vertices.push_back( vertex );
		}
	}

	//ドームの傘のインデックス
	for( UInt16 h = 0; h < domeHorizSubdiv; h++ )
	{
		UInt16 i0 = 0;
		UInt16 i1 = ( h == ( domeHorizSubdiv - 1 ) )? 1 : ( h + 2 );
		UInt16 i2 = h + 1;

		indices.push_back( i0 );
		indices.push_back( i1 );
		indices.push_back( i2 );
	}

	//ドームの傘以降のインデックス
	for( UInt16 v = 0; v < ( domeDiv - 1 ); v++ )
	{
		for( UInt16 h = 0; h < domeHorizSubdiv; h++ )
		{
			UInt16 tf = 1 + ( v * domeHorizSubdiv );
			UInt16 tl = tf + h;
			UInt16 tr = ( h == ( domeHorizSubdiv - 1 ) )? tf : ( tl + 1 );

			UInt16 bf = tf + domeHorizSubdiv;
			UInt16 bl = bf + h;
			UInt16 br = ( h == ( domeHorizSubdiv - 1 ) )? bf : ( bl + 1 );

			indices.push_back( tl );
			indices.push_back( tr );
			indices.push_back( bl );

			indices.push_back( tr );
			indices.push_back( br );
			indices.push_back( bl );
		}
	}

	//サブセット
	m_DomeSubset.vertexStart = 0;
	m_DomeSubset.vertexNum = domeVertexNum;
	m_DomeSubset.indexStart = 0;
	m_DomeSubset.indexNum = domeIndexNum;

	//品質
	m_HemisphereQuality = static_cast<Mix::Scene::ISkyDome::QUALITY>( heQuality );

	MIX_LOG_INFO( L"Dome : VertexNum[%d] IndexNum[%d]", domeVertexNum, domeIndexNum );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// パノラマ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( paQuality < SkyDome::QL_MAX )
	{
		Float32 prDivNumF = 1.0f / static_cast<Float32>( prDiv );
		Mix::Matrix4x4 prRotMat;
		Mix::Vector3 prPos;

		for( UInt16 i = 0; i < ( prDiv + 1 ); i++ )
		{
			Float32 indexF = static_cast<Float32>( i );
			Float32 rad = ( MIX_2PI * prDivNumF ) * indexF;
			Float32 texU = ( 1.0f * prDivNumF ) * indexF;

			prRotMat.SetRotationY( rad );
			prPos = prRotMat * SkyDome::PANORAMA_BASE_POS;

			vertex.pos[0] = prPos.x;
			vertex.pos[1] = +0.5f;
			vertex.pos[2] = prPos.z;
			vertex.pos[3] = 1.0f;
			vertex.tex[0] = texU;
			vertex.tex[1] = 0.0f;
			vertices.push_back( vertex );

			vertex.pos[0] = prPos.x;
			vertex.pos[1] = -0.5f;
			vertex.pos[2] = prPos.z;
			vertex.pos[3] = 1.0f;
			vertex.tex[0] = texU;
			vertex.tex[1] = 1.0f;
			vertices.push_back( vertex );
		}

		for( UInt16 i = 0; i < prDiv; i++ )
		{
			UInt16 i0 = i * 2;
			UInt16 i1 = ( i + 1 ) * 2;

			indices.push_back( prVertexStart + i0 + 0 );
			indices.push_back( prVertexStart + i1 + 0 );
			indices.push_back( prVertexStart + i0 + 1 );

			indices.push_back( prVertexStart + i1 + 0 );
			indices.push_back( prVertexStart + i1 + 1 );
			indices.push_back( prVertexStart + i0 + 1 );
		}

		m_bExistsPanorama = True;
		m_bPanoramaEnabled = True;
		m_PanoramaQuality = static_cast<Mix::Scene::ISkyDome::QUALITY>( paQuality );

		MIX_LOG_INFO( L"Panorama : VertexNum[%d] IndexNum[%d]", prVertexNum, prIndexNum );
	}

	m_PanoramaSubset.vertexStart = prVertexStart;
	m_PanoramaSubset.vertexNum = prVertexNum;
	m_PanoramaSubset.indexStart = prIndexStart;
	m_PanoramaSubset.indexNum = prIndexNum;

	MIX_LOG_INFO( L"" );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// バーテックスバッファを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( pDevice->CreateVertexBuffer(	MIX_UIT_TO_UI32( vertices.size() ), sizeof( SkyDome::VERTEX ),
										False, 0,
										&( vertices[0] ),
										&m_pVertexBuffer,
										MIX_SAFE_NAME( pDebugName ) ) == False )
	{
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// インデックスバッファを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( pDevice->CreateIndexBuffer(	Mix::Graphics::INDEX_USHORT, MIX_UIT_TO_UI32( indices.size() ),
									False, 0,
									&( indices[0] ),
									&m_pIndexBuffer,
									MIX_SAFE_NAME( pDebugName ) ) == False )
	{
		MIX_RELEASE( m_pVertexBuffer );
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////

	return True;
}

void SkyDome::Attach( Mix::Scene::Common::AtmosphereScatter* pAS )
{
	MIX_ASSERT( pAS != NULL );
	MIX_ASSERT( m_pAS == NULL );

	m_pAS = pAS;

	m_pAS->UpdateHemisphere(	m_HemisphereSettings.radius,
								m_HemisphereSettings.height,
								m_PlanetInfo.outerRadius,
								m_PlanetInfo.innerRadius );

	m_pAS->UpdateSunPosition( m_SunPos );

	RendererObject::SetRendering( True );
}

void SkyDome::Detach( void )
{
	m_pAS = NULL;

	RendererObject::SetRendering( False );
}

void SkyDome::Update( Float32 dt )
{
	for( UInt32 i = 0; i < SkyDome::CL_MAX; i++ )
	{
		const ISkyDome::CLOUD_LAYER_SETTINGS* param = &( m_CloudLayerSettings[i] );
		SkyDome::INTERNAL_CLOUD_LAYER_SETTINGS* opt = &( m_InternalCloudLayerSettings[i] );

		opt->texOffset += param->vel * m_CloudGlobalSettings.speed;
		SkyDome::RotateTexOffset( opt->texOffset );
	}
}

Boolean SkyDome::IsCloudAvailabled( void ) const
{
	UInt32 texCount = 0;

	for( UInt32 i = 0; i < SkyDome::CL_MAX; i++ )
	{
		if( m_pCloudTexs[i] != NULL )
		{
			texCount++;
		}
	}

	if( ( m_bCloudEnabled == True ) &&
		( m_CloudLayerNum > 0 ) &&
		( texCount > 0 ) )
	{
		return True;
	}

	return False;
}

Boolean SkyDome::IsPanoramaAvailabled( void ) const
{
	if( ( m_bExistsPanorama == False ) ||
		( m_bPanoramaEnabled == False ) ||
		( m_pPanoramaTex == NULL ) )
	{
		return False;
	}

	return True;
}

UInt32 SkyDome::GetValidCloudLayerNum( void ) const
{
	UInt32 texCount = 0;

	for( UInt32 i = 0; i < SkyDome::CL_MAX; i++ )
	{
		if( m_pCloudTexs[i] != NULL )
		{
			texCount++;
		}
	}

	return texCount;
}

const ISkyDome::CLOUD_LAYER_SETTINGS* SkyDome::GetCloudLayerSettings( void ) const
{
	const ISkyDome::CLOUD_LAYER_SETTINGS* ret = NULL;

	for( UInt32 i = 0; ( i < SkyDome::CL_MAX ) && ( ret == NULL ); i++ )
	{
		if( m_pCloudTexs[i] != NULL )
		{
			ret = &( m_CloudLayerSettings[i] );
		}
	}

	return ret;
}

const SkyDome::INTERNAL_CLOUD_LAYER_SETTINGS* SkyDome::GetInternalCloudLayerSettings( void ) const
{
	const SkyDome::INTERNAL_CLOUD_LAYER_SETTINGS* ret = NULL;

	for( UInt32 i = 0; ( i < SkyDome::CL_MAX ) && ( ret == NULL ); i++ )
	{
		if( m_pCloudTexs[i] != NULL )
		{
			ret = &( m_InternalCloudLayerSettings[i] );
		}
	}

	return ret;
}

const SkyDome::INTERNAL_CLOUD_GLOBAL_SETTINGS& SkyDome::GetInternalCloudGlobalSettings( void ) const
{
	return m_InternalCloudGlobalSettings;
}

Mix::Vector4 SkyDome::ComputeCloudColorAS( void ) const
{
	MIX_ASSERT( m_pAS != NULL );

	const Mix::Scene::IAtmosphereScatter::DAY_SETTINGS& asDaySettings = m_pAS->GetDaySettings();
	const Mix::Vector4& asNightRGBA = m_pAS->GetInternalNightColor();
	const Mix::Vector3& asSunRGB = m_pAS->GetSunColor();

	// colodColor.rgb *= lerp( sunColor.rgb, internalNightColor.rgb * rcp( sunIntensity ), internalNightColor.a );

	const Mix::Vector4& cloudRGBA = m_CloudGlobalSettings.color;
	Mix::Vector3 cloudRGB( cloudRGBA.r, cloudRGBA.g, cloudRGBA.b );
	Float32 nightCloudIntensity = MIX_FLOAT_RECIPROCAL( asDaySettings.sunIntensity ) * ( 1.0f - m_CloudGlobalSettings.asNightness );
	Mix::Vector3 nightRGB( asNightRGBA.r, asNightRGBA.g, asNightRGBA.b );
	Float32 nightA = asNightRGBA.a;

	cloudRGB *= Mix::Vector3::Lerp( asSunRGB, nightRGB * nightCloudIntensity, nightA );

	return Mix::Vector4( cloudRGB.r, cloudRGB.g, cloudRGB.b, cloudRGBA.a );
}

Mix::Graphics::ITexture* SkyDome::GetBaseTexturePtr( void )
{
	return m_pBaseTex;
}

Mix::Graphics::ITexture* SkyDome::GetCloudTexturePtr1( void )
{
	return ( m_pCloudTexs[SkyDome::CL_0] != NULL )? m_pCloudTexs[SkyDome::CL_0] : m_pCloudTexs[SkyDome::CL_1];
}

Mix::Graphics::ITexture* SkyDome::GetCloudTexturePtr2( void )
{
	return ( m_pCloudTexs[SkyDome::CL_0] != NULL )? m_pCloudTexs[SkyDome::CL_1] : NULL;
}

Mix::Graphics::ITexture* SkyDome::GetPanoramaTexturePtr( void )
{
	return m_pPanoramaTex;
}

Mix::Graphics::IVertexBuffer* SkyDome::GetVertexBufferPtr( void ) const
{
	return m_pVertexBuffer;
}

Mix::Graphics::IIndexBuffer* SkyDome::GetIndexBufferPtr( void ) const
{
	return m_pIndexBuffer;
}

const SkyDome::DRAW_SUBSET* SkyDome::GetDomeSubsetPtr( void ) const
{
	return &m_DomeSubset;
}

const SkyDome::DRAW_SUBSET* SkyDome::GetPanoramaSubsetPtr( void ) const
{
	return &m_PanoramaSubset;
}

Boolean SkyDome::IsEnabled( void ) const
{
	return m_bEnabled;
}

void SkyDome::SetEnabled( Boolean state )
{
	m_bEnabled = state;
}

Mix::Scene::ISkyDome::CONSTRAINT_TYPE SkyDome::GetConstraintType( void ) const
{
	return m_CRType;
}

void SkyDome::SetConstraintType( Mix::Scene::ISkyDome::CONSTRAINT_TYPE type )
{
	m_CRType = type;
}

Mix::Scene::ISkyDome::QUALITY SkyDome::GetHemisphereQuality( void ) const
{
	return m_HemisphereQuality;
}

const Mix::Scene::ISkyDome::HEMISPHERE_SETTINGS& SkyDome::GetHemisphereSettings( void ) const
{
	return m_HemisphereSettings;
}

void SkyDome::SetHemisphereSettings( const Mix::Scene::ISkyDome::HEMISPHERE_SETTINGS& settings )
{
	m_HemisphereSettings.radius = max( 1.0f, settings.radius );
	m_HemisphereSettings.height = min( m_HemisphereSettings.radius, max( 1.0f, settings.height ) );

	// 円状の三点から、惑星の 外側の半径、内側の半径、中心へのオフセット を求める

	Mix::Vector2 p0( 0.0f, m_HemisphereSettings.height );
	Mix::Vector2 p1( -m_HemisphereSettings.radius, 0.0f );
	Mix::Vector2 p2( +m_HemisphereSettings.radius, 0.0f );

	Float32 d1 = p0.GetLengthSqr();
	Float32 d2 = p1.GetLengthSqr();
	Float32 d3 = p2.GetLengthSqr();
	Float32 u = MIX_FLOAT_DIV( 0.5f, ( p0.x * p1.y - p1.x * p0.y + p1.x * p2.y - p2.x * p1.y + p2.x * p0.y - p0.x * p2.y ) );
	Float32 a = u * ( d1 * p1.y - d2 * p0.y + d2 * p2.y - d3 * p1.y + d3 * p0.y - d1 * p2.y );
	Float32 b = u * ( p0.x * d2 - p1.x * d1 + p1.x * d3 - p2.x * d2 + p2.x * d1 - p0.x * d3 );

	m_PlanetInfo.outerRadius = ::sqrtf( ( a - p0.x ) * ( a - p0.x ) + ( b - p0.y ) * ( b - p0.y ) );
	m_PlanetInfo.innerRadius = m_PlanetInfo.outerRadius - m_HemisphereSettings.height;
	m_PlanetInfo.coreOffset.Set( 0.0f, -( m_PlanetInfo.innerRadius ), 0.0f );

	if( m_pAS != NULL )
	{
		// 大気スキャッターに通知
		m_pAS->UpdateHemisphere(	m_HemisphereSettings.radius,
									m_HemisphereSettings.height,
									m_PlanetInfo.outerRadius,
									m_PlanetInfo.innerRadius );
	}
}

const Mix::Scene::ISkyDome::PLANET_INFO& SkyDome::GetPlanetInfo( void ) const
{
	return m_PlanetInfo;
}

const Mix::Scene::ISkyDome::BASE_SETTINGS& SkyDome::GetBaseSettings( void ) const
{
	return m_BaseSettings;
}

void SkyDome::SetBaseSettings( const Mix::Scene::ISkyDome::BASE_SETTINGS& settings )
{
	m_BaseSettings.color = settings.color;
	m_BaseSettings.texOpacity = max( 0.0f, settings.texOpacity );
	m_BaseSettings.texScale = max( 0.0f, settings.texScale );
}

Boolean SkyDome::GetBaseTexture( Mix::Graphics::ITexture** ppTexture )
{
	MIX_ASSERT( ppTexture != NULL );

	if( m_pBaseTex == NULL )
	{
		return False;
	}

	MIX_ADD_REF( m_pBaseTex );
	( *ppTexture ) = m_pBaseTex;

	return True;
}

void SkyDome::SetBaseTexture( Mix::Graphics::ITexture* pTexture )
{
	MIX_RELEASE( m_pBaseTex );

	MIX_ADD_REF( pTexture );
	m_pBaseTex = pTexture;
}

const Mix::Scene::ISkyDome::SUN_LIGHT& SkyDome::GetSunLight( void ) const
{
	return m_SunLight;
}

const Mix::Vector3& SkyDome::GetSunPosition( void ) const
{
	return m_SunPos;
}

void SkyDome::SetSunPosition( const Mix::Vector3& pos )
{
	Mix::Vector3 normSunPos = pos.ToNormalize();
	Mix::Vector3 sunPos = normSunPos * m_PlanetInfo.outerRadius;
	Mix::Vector3 habitatPos( 0.0f, m_PlanetInfo.innerRadius, 0.0f );

	Float32 temp;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// サンライト
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_SunPos = pos;

	m_SunLight.pos = normSunPos * m_PlanetInfo.outerRadius - Mix::Vector3( 0.0f, m_PlanetInfo.innerRadius, 0.0f );
	m_SunLight.dir = ( habitatPos - sunPos ).ToNormalize();
	m_SunLight.height = Mix::Vector3::Dot( Mix::Vector3::YAxis(), -m_SunLight.dir );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 内部セッティング
	////////////////////////////////////////////////////////////////////////////////////////////////////

	// 太陽の光が遮断されている部分の暗さ

	temp = max( 0.0f, Mix::Vector3::Dot( Mix::Vector3::YAxis(), -( m_SunLight.dir ) ) );
	temp = 1.0f - ::expf( -temp * 4.0f );
	temp *= 1.0186573334999978969819349892543f; /* ( 1.0f / 0.981684387f( temp の最大値 ) ) -> ( 0.0f <= temp <= 1.0f ) */

	m_InternalCloudGlobalSettings.asDarkness = MIX_CLAMP( temp, m_CloudGlobalSettings.asDarknessStart, m_CloudGlobalSettings.asDarknessEnd );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 大気プリファレンスに通知
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pAS != NULL )
	{
		m_pAS->UpdateSunPosition( m_SunPos );
	}
}

Boolean SkyDome::IsCloudEnabled( void ) const
{
	return m_bCloudEnabled;
}

void SkyDome::SetCloudEnabled( Boolean state )
{
	m_bCloudEnabled = state;
}

UInt32 SkyDome::GetCloudLayerNum( void ) const
{
	return m_CloudLayerNum;
}

Boolean SkyDome::GetCloudLayerTexture( UInt32 layerIndex, Mix::Graphics::ITexture** ppTex )
{
	MIX_ASSERT( layerIndex < m_CloudLayerNum );

	if( m_pCloudTexs[layerIndex] == NULL )
	{
		return False;
	}

	MIX_ADD_REF( m_pCloudTexs[layerIndex] );
	( *ppTex ) = m_pCloudTexs[layerIndex];

	return True;
}

Boolean SkyDome::SetCloudLayerTexture( UInt32 layerIndex, Mix::Graphics::ITexture* pTex )
{
	MIX_ASSERT( layerIndex < m_CloudLayerNum );

	MIX_RELEASE( m_pCloudTexs[layerIndex] );

	MIX_ADD_REF( pTex );
	m_pCloudTexs[layerIndex] = pTex;

	return True;
}

const Mix::Scene::ISkyDome::CLOUD_LAYER_SETTINGS& SkyDome::GetCloudLayerSettings( UInt32 layerIndex ) const
{
	MIX_ASSERT( layerIndex < m_CloudLayerNum );

	return m_CloudLayerSettings[layerIndex];
}

UInt32 SkyDome::SetCloudLayerSettings( const Mix::Scene::ISkyDome::CLOUD_LAYER_SETTINGS* settings, UInt32 num )
{
	if( ( settings != NULL ) && ( num > 0 ) )
	{
		m_CloudLayerNum = ( num <= SkyDome::CL_MAX )? num : SkyDome::CL_MAX;
		Mix::Memory::Copy( m_CloudLayerSettings, settings, sizeof( ISkyDome::CLOUD_LAYER_SETTINGS ) * m_CloudLayerNum );
	}
	else
	{
		m_CloudLayerNum = 0;
	}

	return m_CloudLayerNum;
}

UInt32 SkyDome::LockCloudLayerSettings( Mix::Scene::ISkyDome::CLOUD_LAYER_SETTINGS** settings, UInt32 num )
{
	if( num > 0 )
	{
		m_CloudLayerNum = ( num <= SkyDome::CL_MAX )? num : SkyDome::CL_MAX;
		( *settings ) = &( m_CloudLayerSettings[0] );
	}
	else
	{
		m_CloudLayerNum = 0;
	}

	return m_CloudLayerNum;
}

const Mix::Scene::ISkyDome::CLOUD_GLOBAL_SETTINGS& SkyDome::GetCloudGlobalSettings( void ) const
{
	return m_CloudGlobalSettings;
}

void SkyDome::SetCloudGlobalSettings( const Mix::Scene::ISkyDome::CLOUD_GLOBAL_SETTINGS& settings )
{
	Float32 temp;

	// 設定

	m_CloudGlobalSettings.color = settings.color.ToSaturate();
	m_CloudGlobalSettings.speed = settings.speed;
	m_CloudGlobalSettings.asEffect = settings.asEffect;
	m_CloudGlobalSettings.asSunLighting = settings.asSunLighting;
	m_CloudGlobalSettings.asNightness = MIX_CLAMP( settings.asNightness, 0.0f, 1.0f );
	m_CloudGlobalSettings.asCornerThreshold = MIX_CLAMP( settings.asCornerThreshold, 0.0f, 1.0f );
	m_CloudGlobalSettings.asDarknessStart = MIX_CLAMP( settings.asDarknessStart, 0.0f, 1.0f );
	m_CloudGlobalSettings.asDarknessEnd = MIX_CLAMP( settings.asDarknessEnd, m_CloudGlobalSettings.asDarknessStart, 1.0f );

	// 内部設定

	temp = max( 0.0f, 1.0f - m_CloudGlobalSettings.asCornerThreshold );
	m_InternalCloudGlobalSettings.asCornerMul = ( temp > 0.00001f )? ( 1.0f / temp ) : 1.0f;
}

void SkyDome::ResetCloud( void )
{
	Mix::Vector2 texOffset = Mix::Vector2::Zero();

	for( UInt32 i = 0; i < SkyDome::CL_MAX; i++ )
	{
		m_InternalCloudLayerSettings[i].texOffset = m_CloudLayerSettings[i].initialTex;
	}
}

Boolean SkyDome::ExistsPanorama( void ) const
{
	return m_bExistsPanorama;
}

Mix::Scene::ISkyDome::QUALITY SkyDome::GetPanoramaQuality( void ) const
{
	return m_PanoramaQuality;
}

Boolean SkyDome::IsPanoramaEnabled( void ) const
{
	return m_bPanoramaEnabled;
}

void SkyDome::SetPanoramaEnabled( Boolean state )
{
	m_bPanoramaEnabled = state;
}

const Mix::Scene::ISkyDome::PANORAMA_SETTINGS& SkyDome::GetPanoramaSettings( void ) const
{
	return m_PanoramaSettings;
}

void SkyDome::SetPanoramaSettings( const Mix::Scene::ISkyDome::PANORAMA_SETTINGS& settings )
{
	UInt32 div = SkyDome::PANORAMA_DIV_TABLE[m_PanoramaQuality];

	m_PanoramaSettings.color = settings.color;

	m_PanoramaSettings.scale = Mix::Vector2::Max( Mix::Vector2::Zero(), settings.scale );
	m_PanoramaSettings.offsetY = settings.offsetY;
	m_PanoramaSettings.dist = max( 0.0f, settings.dist );

	if( settings.texRepeatNum == 0 )
	{
		m_PanoramaSettings.texRepeatNum = 0;
	}
	else if( settings.texRepeatNum >= div )
	{
		MIX_ASSERT( div > 0 );
		m_PanoramaSettings.texRepeatNum = div - 1;
	}
	else
	{
		UInt32 temp = settings.texRepeatNum + 1;

		if( ( div % temp ) == 0 )
		{
			m_PanoramaSettings.texRepeatNum = settings.texRepeatNum;
		}
		else
		{
			m_PanoramaSettings.texRepeatNum = div / temp;
		}
	}
}

Boolean SkyDome::GetPanoramaTexture( Mix::Graphics::ITexture** ppTex )
{
	MIX_ASSERT( ppTex != NULL );

	if( m_pPanoramaTex == NULL )
	{
		return False;
	}

	MIX_ADD_REF( m_pPanoramaTex );
	( *ppTex ) = m_pPanoramaTex;

	return True;
}

void SkyDome::SetPanoramaTexture( Mix::Graphics::ITexture* pTex )
{
	MIX_RELEASE( m_pPanoramaTex );

	MIX_ADD_REF( pTex );
	m_pPanoramaTex = pTex;
}

Mix::Vector2 SkyDome::GetPanoramaTextureAspectRatio( void ) const
{
	const Mix::Vector2& scale = m_PanoramaSettings.scale;
	const UInt32& texRepeatNum = m_PanoramaSettings.texRepeatNum;

	UInt16 div;
	Float32 divF;

	UInt16 rn;
	Float32 ecF;
	Float32 rad;

	Mix::Matrix4x4 mat;
	Mix::Vector3 p0;
	Mix::Vector3 p1;
	Float32 w;
	Float32 h;
	Float32 wh;

	// 分割数
	div = SkyDome::PANORAMA_DIV_TABLE[m_PanoramaQuality];
	divF = static_cast<Float32>( div );

	// リピート数
	rn = texRepeatNum + 1;
	if( rn > div )
	{
		rn = div;
	}

	// リピートに使用する辺の数
	ecF = static_cast<Float32>( div / rn );

	// 角度の間隔
	rad = MIX_2PI / divF;

	// 算出
	mat.SetScaling( scale.x, 0.0f, scale.x );
	mat *= Mix::Quaternion( Mix::Vector3( 0.0f, 1.0f, 0.0f ), rad );

	p0 = mat * SkyDome::PANORAMA_BASE_POS;
	p1 = mat * p1;

	w = ( p1 - p0 ).GetLength() * ecF;
	h = scale.y;

	wh = w + h;

	w = MIX_FLOAT_DIV( w, wh );
	h = MIX_FLOAT_DIV( h, wh );

	return Mix::Vector2( 1.0f, MIX_FLOAT_DIV( h, w ) );
}

Mix::Scene::IRendererObject::TYPE SkyDome::GetType( void ) const
{
	return Mix::Scene::IRendererObject::SKY_DOME;
}

Boolean SkyDome::IsRendering( void ) const
{
	return RendererObject::IsRendering();
}

}}}
