////////////////////////////////////////////////////////
// filename: CSky.cpp
// author: Chafumi Touji
// version: 1.0.0
// date: 2020/07/24
////////////////////////////////////////////////////////

#include "defcha3dcore.h"
#include "CSky.h"

CSky::CSky(){
	
	SkyWidth = 1000;
	SkyHeight = 1000;
	VtxBufferSkyCount = 0;
	SceneTime = 12 * 3600;
	CloudVolume = 0.0f;
	CloudAlpha = 0.5f;
	CloudVelocity = 1.0f;
	SunPos = XMFLOAT4(0, 0, 0, 0);
	MoonPos = XMFLOAT4(0, 0, 0, 0);
	SunSize = DEFAULT_SUN_SIZE;
	MoonSize = DEFAULT_MOON_SIZE;

	m_pTextureCloud = NULL;
	m_pCloudShaderResourceView = NULL;
	m_pTextureStar = NULL;
	m_pStarShaderResourceView = NULL;
	m_pTextureMoon = NULL;
	m_pMoonShaderResourceView = NULL;
	VtxBufferSky = NULL;
	VtxBufferMoon = NULL;
	m_pConstantBufferSky = NULL;

	ZeroMemory(&constant_buffer_sky, sizeof(ConstantBufferSky));
}

CSky::~CSky(){

	SAFE_RELEASE( m_pTextureCloud );
	SAFE_RELEASE(m_pCloudShaderResourceView );
	SAFE_RELEASE(m_pTextureStar);
	SAFE_RELEASE(m_pStarShaderResourceView);
	SAFE_RELEASE(m_pTextureMoon);
	SAFE_RELEASE(m_pMoonShaderResourceView);
	SAFE_RELEASE(VtxBufferSky);
	SAFE_RELEASE(VtxBufferMoon);
	SAFE_RELEASE(m_pConstantBufferSky);
}

bool CSky::SetInitParam(float sun_size, float moon_size){

	//CreateResourceを呼び出す前に使用してください。

	SunSize = sun_size;
	MoonSize = moon_size;

	return true;
}

bool CSky::CreateResource(CCommonDevice *device, wchar_t *path, wchar_t *texture_cloud_name, wchar_t *texture_star_name, wchar_t *texture_moon_name ){

	return CreateResource(device, SKY_SIZE_DEFAULT, SKY_DIV_COUNT_DEFAULT, path, texture_cloud_name, texture_star_name, texture_moon_name);
}

bool CSky::CreateResource( CCommonDevice *device, int sky_size, int div_count, wchar_t *path, wchar_t *texture_cloud_name, wchar_t *texture_star_name, wchar_t *texture_moon_name ){

	///////////////////////////////////////////////////
	//バッファ作成

	int DivCount = div_count;
	CVertex *VtxDataSky = new CVertex[4 * ( DivCount + 1 ) * ( DivCount + 1 )];
	VtxBufferSkyCount = 0;

	float SkyR = sky_size;
	float arc1 = 0.000f;
	float height = sky_size / 3.0f;
	VtxBufferSkyCount = 0;
	while (arc1 < 3.14159265f / 2.0f + 0.001f){
		float arc2 = 0.0000f;
		int count = 0;
		while (arc2 < 3.14159265f * 2.0f - 0.001f){

			float arc_div1 = arc1;
			float arc_div2 = arc1 + (3.14159265f / 2.0f) / (float)DivCount;
			float arc_div3 = arc2;
			float arc_div4 = arc2 + 3.14159265f * 2.0f / (float)DivCount;

			float x1 = sin(arc_div3) * SkyR * cos(arc_div1);
			float y1 = cos(arc_div3) * SkyR * cos(arc_div1);
			float z1 = cos(arc_div1) * SkyR * 0.5f - height;
			float x2 = sin(arc_div4) * SkyR * cos(arc_div1);
			float y2 = cos(arc_div4) * SkyR * cos(arc_div1);
			float z2 = cos(arc_div1) * SkyR * 0.5f - height;
			float x3 = sin(arc_div3) * SkyR * cos(arc_div2);
			float y3 = cos(arc_div3) * SkyR * cos(arc_div2);
			float z3 = cos(arc_div2) * SkyR * 0.5f - height;
			float x4 = sin(arc_div4) * SkyR * cos(arc_div2);
			float y4 = cos(arc_div4) * SkyR * cos(arc_div2);
			float z4 = cos(arc_div2) * SkyR * 0.5f - height;
			z1 = -z1;
			z2 = -z2;
			z3 = -z3;
			z4 = -z4;

			float tex_x1 = (arc_div3 / (3.14159265f * 2.0f));
			float tex_x2 = (arc_div4 / (3.14159265f * 2.0f));
			float tex_y1 = cos(arc1) * 2.0f + 0.1f;
			float tex_y2 = cos(arc1 + 3.14159265f / 2.0f / (float)DivCount) * 2.0f + 0.1f;
			count++;

			SetVertex( &VtxDataSky[VtxBufferSkyCount], x1, y1, z1, x1, y1, z1, 0, 0, 0, 0, tex_x1, tex_y1 );
			SetVertex(&VtxDataSky[VtxBufferSkyCount + 1], x3, y3, z3, x3, y3, z3, 0, 0, 0, 0, tex_x1, tex_y2);
			SetVertex(&VtxDataSky[VtxBufferSkyCount + 2], x2, y2, z2, x2, y2, z2, 0, 0, 0, 0, tex_x2, tex_y1);
			SetVertex(&VtxDataSky[VtxBufferSkyCount + 3], x4, y4, z4, x4, y4, z4, 0, 0, 0, 0, tex_x2, tex_y2);

			VtxBufferSkyCount += 4;

			arc2 += (3.14159265f * 2.0f) / (float)DivCount;
		}
		arc1 += (3.14159265f / 2.0f) / (float)DivCount;
	}

	D3D11_BUFFER_DESC desc_buffer;
	ZeroMemory(&desc_buffer, sizeof(D3D11_BUFFER_DESC));
	desc_buffer.ByteWidth = sizeof(CVertex)* VtxBufferSkyCount;
	desc_buffer.Usage = D3D11_USAGE_DYNAMIC;
	desc_buffer.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	desc_buffer.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	desc_buffer.MiscFlags = 0;
	desc_buffer.StructureByteStride = sizeof(CVertex);
	D3D11_SUBRESOURCE_DATA msr;
	msr.pSysMem = VtxDataSky;
	device->m_pDevice->CreateBuffer(&desc_buffer, &msr, &VtxBufferSky);

	delete[] VtxDataSky;

	////////////////////////////////////////////////////////
	//月

	CVertex *vtx_moon = new CVertex[4];

	float vtx_size = MoonSize;
	SetVertex( &vtx_moon[0], vtx_size, 0.0f, vtx_size, 0, 0, 1, 0, 0, 0, 0, -1.0f, -1.0f );
	SetVertex(&vtx_moon[1], -vtx_size, 0.0f, vtx_size, 0, 0, 1, 0, 0, 0, 0, 1.0f, -1.0f );
	SetVertex(&vtx_moon[2], vtx_size, 0.0f, -vtx_size, 0, 0, 1, 0, 0, 0, 0, -1.0f, 1.0f );
	SetVertex(&vtx_moon[3], -vtx_size, 0.0f, -vtx_size, 0, 0, 1, 0, 0, 0, 0, 1.0f, 1.0f );

	ZeroMemory(&desc_buffer, sizeof(D3D11_BUFFER_DESC));
	desc_buffer.ByteWidth = sizeof(CVertex)* 4;
	desc_buffer.Usage = D3D11_USAGE_DYNAMIC;
	desc_buffer.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	desc_buffer.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	desc_buffer.MiscFlags = 0;
	desc_buffer.StructureByteStride = sizeof(CVertex);
	msr.pSysMem = vtx_moon;
	device->m_pDevice->CreateBuffer(&desc_buffer, &msr, &VtxBufferMoon);

	delete[] vtx_moon;

	/////////////////////////////////////////////////////
	//コンスタントバッファ作成

	m_pConstantBufferSky = NULL;
	ZeroMemory(&desc_buffer, sizeof(D3D11_BUFFER_DESC));
	desc_buffer.ByteWidth = sizeof(ConstantBufferSky);
	desc_buffer.Usage = D3D11_USAGE_DYNAMIC;
	desc_buffer.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	desc_buffer.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	desc_buffer.MiscFlags = 0;
	desc_buffer.StructureByteStride = sizeof(float);
	device->m_pDevice->CreateBuffer(&desc_buffer, NULL, &m_pConstantBufferSky);

	srand(1238);
	ZeroMemory(&constant_buffer_sky, sizeof(ConstantBufferSky));

	for (int i = 0; i <= 15; i++){
		constant_buffer_sky.CloudParam2[i].x = sin(((float)(rand() % 10000) * (100.0f / (float)sky_size) / 1000.0f) * 3.14159265f * 2.0f);
		constant_buffer_sky.CloudParam2[i].y = cos(((float)(rand() % 10000) * (100.0f / (float)sky_size) / 1000.0f) * 3.14159265f * 2.0f);
		constant_buffer_sky.CloudParam2[i].z = (float)(rand() % 10000) * ( 100.0f / (float) sky_size) / 10000.0f + 0.001f;
		constant_buffer_sky.CloudParam2[i].w = (float)(rand() % 10000) * ( 100.0f / (float) sky_size) / 10000.0f + 0.001f;
	}
	constant_buffer_sky.CloudParam1.y = 0.15f * (100.0f / (float)sky_size);
	constant_buffer_sky.CloudParam1.x = 0.8f * (100.0f / (float)sky_size);

	D3D11_MAPPED_SUBRESOURCE subresource;
	device->m_pDeviceContext->Map(m_pConstantBufferSky, 0, D3D11_MAP_WRITE_DISCARD, 0, &subresource);
	memcpy(subresource.pData, &constant_buffer_sky, sizeof(ConstantBufferSky));
	device->m_pDeviceContext->Unmap(m_pConstantBufferSky, 0);

	////////////////////////////////////////////////////////////////////
	//テクスチャ作成

	D3D11_TEXTURE2D_DESC desc_texture_2d;
	ZeroMemory(&desc_texture_2d, sizeof(D3D11_TEXTURE2D_DESC));
	desc_texture_2d.MipLevels = 1;
	desc_texture_2d.ArraySize = 1;
	desc_texture_2d.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	desc_texture_2d.SampleDesc.Count = 1;
	desc_texture_2d.SampleDesc.Quality = 0;
	desc_texture_2d.Usage = D3D11_USAGE_DEFAULT;
	desc_texture_2d.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	desc_texture_2d.CPUAccessFlags = 0;
	desc_texture_2d.MiscFlags = 0;

	D3DX11_IMAGE_LOAD_INFO info;
	ZeroMemory(&info, sizeof(D3DX11_IMAGE_LOAD_INFO));
	info.Width = D3DX11_DEFAULT;
	info.Height = D3DX11_DEFAULT;
	info.Depth = D3DX11_DEFAULT;
	info.FirstMipLevel = D3DX11_DEFAULT;
	info.MipLevels = 1;
	info.Usage = D3D11_USAGE_DEFAULT;
	info.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
	info.CpuAccessFlags = 0;
	info.MiscFlags = 0;
	info.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	info.Filter = D3DX11_FILTER_LINEAR;
	info.MipFilter = D3DX11_FILTER_LINEAR;
	info.pSrcInfo = NULL;

	D3D11_SHADER_RESOURCE_VIEW_DESC desc_shader_resource;
	ZeroMemory(&desc_shader_resource, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
	desc_shader_resource.Format = info.Format;
	desc_shader_resource.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	desc_shader_resource.Texture2D.MipLevels = info.MipLevels;
	desc_shader_resource.Texture2D.MostDetailedMip = 0;

	ID3D11Resource *resource = NULL;

	/*-----------------------------------------------------*/

	wchar_t filepath[SIZE_CHAR_MAX];
	wmemset(filepath, 0, SIZE_CHAR_MAX);
	wcscpy_s(filepath, SIZE_CHAR_MAX, path);
	wcscat_s(filepath, _T("/"));
	wcscat_s(filepath, texture_cloud_name);
	m_pTextureCloud = NULL;
	D3DX11CreateTextureFromFile(device->m_pDevice, filepath, &info, NULL, &resource, NULL);
	m_pTextureCloud = (ID3D11Texture2D *)resource;
	device->m_pDevice->CreateShaderResourceView(m_pTextureCloud, &desc_shader_resource, &m_pCloudShaderResourceView);

	/*-----------------------------------------------------*/

	wmemset(filepath, 0, SIZE_CHAR_MAX);
	wcscpy_s(filepath, SIZE_CHAR_MAX, path);
	wcscat_s(filepath, _T("/"));
	wcscat_s(filepath, texture_star_name);
	m_pTextureStar = NULL;
	D3DX11CreateTextureFromFile(device->m_pDevice, filepath, &info, NULL, &resource, NULL);
	m_pTextureStar = (ID3D11Texture2D *)resource;

	m_pTextureStar->GetDesc(&desc_texture_2d);
	desc_shader_resource.Format = desc_texture_2d.Format;
	desc_shader_resource.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	desc_shader_resource.Texture2D.MipLevels = desc_texture_2d.MipLevels;
	desc_shader_resource.Texture2D.MostDetailedMip = 0;
	device->m_pDevice->CreateShaderResourceView(m_pTextureStar, &desc_shader_resource, &m_pStarShaderResourceView);

	/*-----------------------------------------------------*/

	wmemset(filepath, 0, SIZE_CHAR_MAX);
	wcscpy_s(filepath, SIZE_CHAR_MAX, path);
	wcscat_s(filepath, _T("/"));
	wcscat_s(filepath, texture_moon_name);
	m_pTextureMoon = NULL;
	D3DX11CreateTextureFromFile(device->m_pDevice, filepath, &info, NULL, &resource, NULL);
	m_pTextureMoon = (ID3D11Texture2D *)resource;

	m_pTextureStar->GetDesc(&desc_texture_2d);
	desc_shader_resource.Format = desc_texture_2d.Format;
	desc_shader_resource.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	desc_shader_resource.Texture2D.MipLevels = desc_texture_2d.MipLevels;
	desc_shader_resource.Texture2D.MostDetailedMip = 0;
	device->m_pDevice->CreateShaderResourceView(m_pTextureMoon, &desc_shader_resource, &m_pMoonShaderResourceView);

	return true;
}

bool CSky::Draw(CCommonDevice *device){

	UINT stride = sizeof(CVertex);
	UINT offset = 0;

	//////////////////////////////////////////////////////////////////////////////
	//シェーダーパラメータ設定

	XMMATRIX matrix1 = XMMatrixRotationX(-3.14159265f / 2.0f + device->RotationX);
	XMMATRIX matrix2 = XMMatrixTranslation(MoonPos.x, MoonPos.y, MoonPos.z);
	XMMATRIX matrix3 = matrix1 * matrix2;

	XMStoreFloat4x4(&constant_buffer_sky.MatMoon, XMMatrixTranspose(matrix3));

	device->m_pDeviceContext->IASetInputLayout(device->m_pLayout2);
	device->m_pDeviceContext->RSSetState(device->m_pRasterizeState);
	device->m_pDeviceContext->OMSetBlendState(device->m_pBlendState2, NULL, 0xffffffff);
	device->m_pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

	D3D11_MAPPED_SUBRESOURCE resource;
	device->m_pDeviceContext->Map(m_pConstantBufferSky, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
	memcpy(resource.pData, &constant_buffer_sky, sizeof(ConstantBufferSky));
	device->m_pDeviceContext->Unmap(m_pConstantBufferSky, 0);

	device->m_pDeviceContext->VSSetConstantBuffers(3, 1, &m_pConstantBufferSky);
	device->m_pDeviceContext->PSSetConstantBuffers(3, 1, &m_pConstantBufferSky);

	///////////////////////////////////////////////////////////////////////////////
	//空を描く

	device->m_pDeviceContext->VSSetShader(device->m_pVtxShader1_2, NULL, 0);
	device->m_pDeviceContext->PSSetShader(device->m_pPxShader6, NULL, 0);

	device->m_pDeviceContext->PSSetShaderResources(0, 1, &m_pCloudShaderResourceView);
	device->m_pDeviceContext->PSSetSamplers(0, 1, &device->m_pSampleState);
	device->m_pDeviceContext->PSSetShaderResources(1, 1, &m_pStarShaderResourceView);
	device->m_pDeviceContext->PSSetSamplers(1, 1, &device->m_pSampleState);

	device->m_pDeviceContext->IASetVertexBuffers(0, 1, &VtxBufferSky, &stride, &offset);
	device->m_pDeviceContext->Draw(VtxBufferSkyCount, 0);

	//////////////////////////////////////////////////////////////////////////////////////
	//　月を描く

	device->m_pDeviceContext->VSSetShader(device->m_pVtxShader1_3, NULL, 0);
	device->m_pDeviceContext->PSSetShader(device->m_pPxShader7, NULL, 0);

	device->m_pDeviceContext->PSSetShaderResources(2, 1, &m_pMoonShaderResourceView);
	device->m_pDeviceContext->PSSetSamplers(2, 1, &device->m_pSampleState);

	device->m_pDeviceContext->IASetVertexBuffers(0, 1, &VtxBufferMoon, &stride, &offset);
	device->m_pDeviceContext->Draw(4, 0);
	
	return true;
}

bool CSky::UpdateFrame(){

	int r, g, b;
	GetSkyColor(&r, &g, &b);
	float fr = (float)r / 400.00f;
	float fg = (float)g / 400.00f;
	float fb = (float)b / 400.00f;

	static float time1 = 0.0f;
	static float time2 = 0.0f;
	time1 += 0.0002f * CloudVelocity;
	time2 += 0.005f * CloudVelocity;
	if (time1 > 3.14159265f * 2.0f)
		time1 = 0.0f;
	if (time2 > 3.14159265f * 2.0f)
		time2 = 0.0f;
	float cloud_volume = 0.0f;

	constant_buffer_sky.SkyColor = XMFLOAT4(fr, fg, fb, 1.00);
	constant_buffer_sky.SunPos = SunPos;
	constant_buffer_sky.MoonPos = MoonPos;
	constant_buffer_sky.SkyParam = XMFLOAT4(time1, time2, CloudVolume, CloudAlpha );

	return true;
}

bool CSky::SetCloudAlpha(float alpha){

	CloudAlpha = alpha;

	return true;
}

bool CSky::SetCloudVolume(float volume){

	CloudVolume = volume;

	return true;
}

bool CSky::SetCloudVelocity(float velocity){

	CloudVelocity = velocity;

	return true;
}

bool CSky::SetSunPos(float x, float y, float z){

	SunPos.x = x;
	SunPos.y = y;
	SunPos.z = z;

	return true;
}

bool CSky::SetMoonPos(float x, float y, float z){

	MoonPos.x = x;
	MoonPos.y = y;
	MoonPos.z = z;

	return true;
}

bool CSky::SetMoonCrescentRate(float rate){

	MoonPos.w = rate;

	return true;
}

bool CSky::SetSceneTime(int hour, int min, int sec){

	SceneTime = hour * 3600 + min * 60 + sec;

	return true;
}

bool CSky::GetSkyColor(int *R, int *G, int *B){

	double total_time = (double)(24 * 60 * 60);
	double rate1 = pow(sin(((double) SceneTime / total_time) * 2 * 3.14159265), 2);
	double rate2 = -cos(((double) SceneTime / total_time) * 2 * 3.14159265);
	int r = (int)(rate2 * 100);
	int g = (int)(rate2 * 180);
	int b = (int)(rate2 * 255);
	r += (int)(rate1 * 255);
	g += (int)(rate1 * 180);
	b += (int)(rate1 * 100);
	r = (r < 10) ? 10 : r;
	g = (g < 10) ? 10 : g;
	b = (b < 60) ? 60 : b;
	*R = (r > 255) ? 255 : r;
	*G = (g > 255) ? 255 : g;
	*B = (b > 255) ? 255 : b;

	return true;
}

