#include <Darkstar/darkstar.hpp>
using namespace dse;

#include "splash_system.hpp"

struct Model
{
	Vect3 translation;
	float scale;
	Vect3 rotation;
	shared_ptr<StaticModel> model;
};

shared_ptr<BaseTexture> sky_cubemap;

#define point_lights_number 4

Vect4 plight_swizzled[3] = {vect4_zero, vect4_zero, vect4_zero};
Vect4 plight_inv_range = Vect4(1000, 1000, 1000, 1000);
Vect3 plight_ci[point_lights_number];

void drawSimpleObject(std::vector<Model> models, const FPCameraController& camera)
{
	for(unsigned int i = 0; i < (unsigned int)models.size(); ++i)
	{
		models[i].model->setScaling(models[i].scale);
		models[i].model->setRotationX(models[i].rotation.x);
		models[i].model->setRotationY(models[i].rotation.y);
		models[i].model->setRotationZ(models[i].rotation.z);
		models[i].model->setTranslation(models[i].translation);
		for(unsigned int j = 0; j < models[i].model->getMeshesNumber(); ++j)
		{
			shared_ptr<Effect> eff = models[i].model->getMesh(j)->getEffect();
			shared_ptr<Material> mater = models[i].model->getMesh(j)->getMaterial();

			eff->setMacro("DIRECTIONAL_LIGHT_COUNT", 0);
			eff->setMacro("POINT_LIGHT_COUNT", point_lights_number);
			eff->setVect4Array("point_light_pos", plight_swizzled, 3);
			eff->setVect4("point_light_inv_range", &plight_inv_range);
			eff->setVect3Array("point_light_color_intensity", plight_ci, point_lights_number);
			eff->setMacro("SPECULAR", 0);
			eff->setMacro("OUTDOOR", 1);
			eff->setTexture("skybox", sky_cubemap);
			models[i].model->drawMesh(j, camera.getCamera());
		};
	};
};

int main()
{
	DarkstarInit();

	Log::ptr()->open("log.html");

	InputDriver::ptr()->init();

	FileManager::ptr()->registerNewLoader(shared_ptr<StandardFileLoader>(new StandardFileLoader(".\\")));
	FileManager::ptr()->registerNewLoader(shared_ptr<StandardFileLoader>(new StandardFileLoader("Data\\Effects\\")));
	FileManager::ptr()->registerNewLoader(shared_ptr<StandardFileLoader>(new StandardFileLoader("Data\\Shaders\\")));
	FileManager::ptr()->registerNewLoader(shared_ptr<StandardFileLoader>(new StandardFileLoader("Data\\Other\\")));	
	FileManager::ptr()->registerNewLoader(shared_ptr<StandardFileLoader>(new StandardFileLoader("Data\\Textures\\")));	
	FileManager::ptr()->registerNewLoader(shared_ptr<StandardFileLoader>(new StandardFileLoader("Data\\Models\\")));

	DFile graphic_settings_file("graphic.txt");
	Parser graphic_setting(graphic_settings_file);
	shared_ptr<Section> gr_root = graphic_setting.getRootSection();
	WindowSettings ws;
	ws.x = 0;
	ws.y = 0;
	ws.is_fullscreen = gr_root->values.find("fullscreen")->second.value == "true" ? true : false;
#if _DEBUG
	//ws.is_fullscreen = false;
#endif
	ws.width = atoi(gr_root->values.find("width")->second.value.c_str());
	ws.height = atoi(gr_root->values.find("height")->second.value.c_str());
	ws.name = "Test";
	ApplicationWindow w;
	w.createWindow(ws);
	shared_ptr<DisplayMode> display(new DisplayMode(w.getWidth(), w.getHeight(), DPF_R8G8B8A8_UNORM, 75));
	DRTAATYPE aa_t;
	aa_t.count = atoi(gr_root->values.find("aa")->second.value.c_str());
	aa_t.quality = 0;
	GraphicDriver::ptr()->init(w.getHWND(), display, !w.getIsFullScreen(), aa_t, 2);
	new RenderStateManager;

	Keyboard::ptr()->init(w.getHWND());
	Mouse::ptr()->init(w.getHWND());

	shared_ptr<StaticModel> planar_landscape = ModelLoader::ptr()->load("plane.dgd");
	planar_landscape->setTranslation(Vect3(0, -3.4f, 0));
	planar_landscape->setScaling(10);

	shared_ptr<Effect> text_effect = EffectManager::ptr()->load("text_simple.def");

	shared_ptr<StaticModel> sky_dome = ModelLoader::ptr()->load("sphere.dgd");
	sky_dome->setTranslation(Vect3(0, 0, 0));
	shared_ptr<BaseTexture> sky_cube = TextureManager::ptr()->load("sky_9_cube.dds");

	Skybox sky(sky_dome, sky_cube);

	TextManager::ptr()->init(text_effect, w.getWidth(), w.getHeight());
	shared_ptr<Type> standard_font(new Type);
	standard_font->create("font.dfd");
	TextManager::ptr()->registerNewType(standard_font);

	std::vector<Model> models;
	
	DFile levelini("level.ini");
	Parser p(levelini, true);

	if(p.getIsParsedSuccessfully())
	{
		shared_ptr<Section> root = p.getRootSection();
		std::vector<shared_ptr<Section> >::iterator it;
		for(it = root->sections.begin(); it != root->sections.end(); ++it)
		{
			shared_ptr<StaticModel> next_model = ModelLoader::ptr()->load((*it)->section_name);
			if(!next_model)
				continue;
			Vect3 rotation;
			Vect3 translation;
			float scale;
			std::map<std::string, PairInfo>::iterator vit = (*it)->values.find("scale");
			if(vit != (*it)->values.end())
			{
				std::string value = vit->second.value;
				scale = (float)atof(value.c_str());
			}
			else
				scale = 1.0f;
			vit = (*it)->values.find("rotation");
			if(vit != (*it)->values.end())
			{
				std::string value = vit->second.value;
				sscanf_s(value.c_str(), "(%f, %f, %f)", &rotation.x, &rotation.y, &rotation.z);
			}
			else
				rotation = vect3_zero;
			vit = (*it)->values.find("translation");
			if(vit != (*it)->values.end())
			{
				std::string value = vit->second.value;
				sscanf_s(value.c_str(), "(%f, %f, %f)", &translation.x, &translation.y, &translation.z);
			}
			else
				translation = vect3_zero;
			Model new_model;
			new_model.model = next_model;
			new_model.rotation = rotation;
			new_model.scale = scale;
			new_model.translation = translation;
			models.push_back(new_model);
		};
	};

	shared_ptr<Camera> cam(new Camera(70*deg_to_rad, (float)w.getWidth()/(float)w.getHeight(), 0.1f, 4000.0f));
	cam->setPosition(Vect3(0, 0, 0));
	cam->setTarget(Vect3(0, 0, -1));
	FPCameraController camera(cam);
	ShowCursor(false);
	AverageStack<float> fps_stack(100);
	float fps = 60;
	float mouse_sensitivity = 3.6f;
	bool f1_pressed, g_pressed;
	bool draw_console = true;
	unsigned int run_multip = 1;

	// ORGANIZATION OF SCENE LIGHTS

#if point_lights_number > 0
	PointLight plights[point_lights_number];
	plights[0].setPosition(Vect3(-30, 8.4f, 0));
	plight_ci[0] = Vect3(1, 1, 1);
#endif
#if point_lights_number > 1
	plights[1].setPosition(Vect3(0, 8.4f, -20));
	plight_ci[1] = Vect3(1, 1, 1);
#endif
#if point_lights_number > 2
	plights[2].setPosition(Vect3(30, 8.4f, 0));
	plight_ci[2] = Vect3(1, 1, 1);
#endif
#if point_lights_number > 3
	plights[3].setPosition(Vect3(0, 8.4f, 20));
	plight_ci[3] = Vect3(1, 1, 1);
#endif
#if point_lights_number > 0
	for(unsigned int i = 0; i < point_lights_number; ++i)
		plights[i].setRange(35);

	Vect3 point_light_pos[point_lights_number];
	for(unsigned int i = 0; i < point_lights_number; ++i)
	{
		point_light_pos[i] = plights[i].getPosition();
		plight_inv_range[i] = 1 / plights[i].getRange() / plights[i].getRange();
	};

	for(unsigned int i = 0; i < 3; ++i)
	{
		for(unsigned int j = 0; j < point_lights_number; ++j)
		{
			plight_swizzled[i][j] = point_light_pos[j][i];
		}
	}
#endif

	// ORGANIZATION OF DRAW TO QUAD RENDERING AND SCENE DOWNSAMPLING
	HDR no_hdr("draw_to_quad.def", "down_sampler.def", w.getWidth(), w.getHeight(), aa_t);

	// GLOW EFFECT
	shared_ptr<Effect> glow = EffectManager::ptr()->load("glow.def");
	shared_ptr<RenderTarget> treshold_rt(new RenderTarget());
	unsigned int streched_level = 1;
	Vect2 treshold_tex_size = no_hdr.getStrechedSceneSize(streched_level);
	Vect2 inv_treshold_tex_size = Vect2(1.0f/treshold_tex_size.x, 1.0f/treshold_tex_size.y);
	treshold_rt->create2D((unsigned int)treshold_tex_size.x, (unsigned int)treshold_tex_size.y, DRTFMT_R16G16B16A16_FLOAT, aa_t, true);
	shared_ptr<Camera> tmp_camera(new Camera());

	shared_ptr<RenderTarget> blured_rt[2];
	for(unsigned int i = 0; i < 2; ++i)
	{
		blured_rt[i].reset(new RenderTarget());
		blured_rt[i]->create2D((unsigned int)treshold_tex_size.x, (unsigned int)treshold_tex_size.y, DRTFMT_R16G16B16A16_FLOAT, aa_t, true);
	};

	Vect3 glare_intensity = Vect3(2.1f, 2.1f, 2.1f);
	Vect3 glare_params = Vect3(0.55f, 0.55f, 0.55f);
	bool is_glare = true;

	// ORGANIZATION OF RAIN
	shared_ptr<BlendRenderState> rain_blend(new BlendRenderState());
	rain_blend->setBlendEnable(0, true);
	rain_blend->setBlendOp(DBLENDOP_ADD);
	rain_blend->setSrcBlend(DBLEND_INV_SRC_ALPHA);
	rain_blend->setDstBlend(DBLEND_ONE);
	rain_blend->setBlendOpAlpha(DBLENDOP_ADD);
	rain_blend->setDstBlendAlpha(DBLEND_ONE);
	rain_blend->setSrcBlendAlpha(DBLEND_ONE);

	shared_ptr<CullModeRenderState> rain_cull(new CullModeRenderState(DCULL_NONE));

	shared_ptr<BaseTexture> rain_texture = TextureManager::ptr()->load("rain.dds");
	shared_ptr<BaseTexture> rain_normal_texture = TextureManager::ptr()->load("rain_normal.dds");
	shared_ptr<Effect> rain_effect = EffectManager::ptr()->load("rain.def");
	shared_ptr<Quad> rain_quad(new Quad(no_hdr.getFullscreenQuad()->getLX(), no_hdr.getFullscreenQuad()->getLY(), 2.0f, rain_effect));
	shared_ptr<Material> rain_mat = rain_quad->getModel()->getMesh(0)->getMaterial();
	rain_mat->setEffect(rain_effect);
	rain_mat->setBlendState(rain_blend);
	rain_mat->setCullMode(rain_cull);
	rain_mat->setTechnique("base");
	rain_mat->addTexture("base_tex", rain_texture);
	rain_mat->addTexture("normal", rain_normal_texture);


	const unsigned int layer_number = 4;
	Vect3 rain_direction = Vect3(1.0f, -100.0f, 0.0f);
	Vect4 rain_params = Vect4(2.74f, 1.75f, 0.34f, 8.0f);
	float rain_speed = 45.1f;
	Vect4 additional_rain_params = Vect4(rain_speed, rain_speed, 0, 0.25f);
	Vect4 vLightPos0 = Vect4(plights[0].getPosition());
	Vect4 light_color_brightness = Vect4(1.0f, 1.0f, 1.0f, 1.0f);
	Vect4 rain_directionCP[layer_number];
	for(unsigned int i = 0; i < layer_number; ++i)
		rain_directionCP[i] = vect4_zero;
	float rain_bias[layer_number];
	float rain_contrast[layer_number];
	float rain_parallax[layer_number];
	for(unsigned int i = 0; i < layer_number; ++i)
	{
		rain_bias[i] = rain_params.z;
		rain_contrast[i] = rain_params.y;
		rain_parallax[i] = rain_params.x;
	};
	rain_bias[0] = 0.434f;
	rain_contrast[0] = 1.8405f;
	rain_parallax[0] = 4.8016f;

	rain_bias[1] = 0.383f;
	rain_contrast[1] = 1.766f;
	rain_parallax[1] = 3.5f;

	rain_bias[2] = 0.293f;
	rain_contrast[2] = 1.75f;
	rain_parallax[2] = 3.245f;

	rain_bias[3] = 0.1938f;
	rain_contrast[3] = 1.75f;
	rain_parallax[3] = 2.74f;

	shared_ptr<RenderTarget> environment(new RenderTarget());
	shared_ptr<RenderTarget> depth_env(new RenderTarget());
	unsigned int cubemap_size = 256;
#if GD_D3D9
	depth_env->create2D(cubemap_size, cubemap_size, DRTFMT_D16_UNORM, WITHOUT_AA, false);
	environment->createCube(cubemap_size, cubemap_size, DRTFMT_B8G8R8A8_UNORM);
#endif
#if GD_D3D10
	depth_env->createCube(cubemap_size, cubemap_size, DRTFMT_D16_UNORM);
	environment->createCube(cubemap_size, cubemap_size, DRTFMT_R8G8B8A8_UNORM);
#endif
	std::vector<std::string> tech_names(6);
	tech_names[0] = "cube_posx";
	tech_names[1] = "cube_negx";
	tech_names[2] = "cube_posy";
	tech_names[3] = "cube_negy";
	tech_names[4] = "cube_posz";
	tech_names[5] = "cube_negz";
	std::vector<Vect3> camera_dir(6);
	camera_dir[0] = vect3_sing_x;
	camera_dir[1] = -vect3_sing_x;
	camera_dir[2] = vect3_sing_y;
	camera_dir[3] = -vect3_sing_y;
	camera_dir[4] = vect3_sing_z;
	camera_dir[5] = -vect3_sing_z;

	float time = 0.0f;
	float previous_time = 0.0f;

	// MENU ORGANIZATION
	unsigned int menu_position = 0;
	unsigned int menu_size = 9 + 3*layer_number;
	bool down_pressed = false;
	bool up_pressed = false;
	std::vector<std::string> menu(menu_size);
	menu[0] = "rain direction x ";
	menu[1] = "rain direction y ";
	menu[2] = "rain parallax ";
	menu[3] = "rain contrast ";
	menu[4] = "rain bias ";
	menu[5] = "rain specular ";
	menu[6] = "rain speed ";
	menu[7] = "rain random ";
	menu[8] = "rain direction z ";
	
	std::vector<float*> menu_values(menu_size + 3*layer_number);
	menu_values[0] = &rain_direction.x;
	menu_values[1] = &rain_direction.y;
	menu_values[2] = &rain_params.x;
	menu_values[3] = &rain_params.y;
	menu_values[4] = &rain_params.z;
	menu_values[5] = &rain_params.w;
	menu_values[6] = &additional_rain_params.x;
	menu_values[7] = &additional_rain_params.w;
	menu_values[8] = &rain_direction.z;

	for(unsigned int i = 0; i < layer_number; ++i)
	{
		unsigned int index = 8 + i*3 + 1;
		std::stringstream biasstr;
		biasstr << "rain bias layer " << i << " ";
		menu[index] = biasstr.str();
		menu_values[index] = &rain_bias[i];

		std::stringstream contrstr;
		contrstr << "rain contrast layer " << i << " ";
		menu[index + 1] = contrstr.str();
		menu_values[index + 1] = &rain_contrast[i];

		std::stringstream paralstr;
		paralstr << "rain parallax layer " << i%3 << " ";
		menu[index + 2] = paralstr.str();
		menu_values[index + 2] = &rain_parallax[i];
	};

// SPLASH ORGANIZATION
	SplashSystem splash_system("splash.def", "splash.dds", 21, 30, 2000, camera.getCamera()->getPosition());

	while(true && w.getIsExist())
	{
		Profiler::ptr()->startTimer();
		w.handle();
		Keyboard::ptr()->updateImmediateData();
		Mouse::ptr()->updateImmediateData();
		if(Keyboard::ptr()->getIsKeyDownImmediate(DSI_ESCAPE))
		{
			SendMessage(w.getHWND(), WM_CLOSE, 0, 0);
			w.destroyWindow();
			break;
		};

		previous_time = time;
		time += (1.0f / fps) / 60.0f;
		if(Keyboard::ptr()->getIsKeyDownImmediate(DSI_LSHIFT))
			run_multip = 5;
		if(Keyboard::ptr()->getIsKeyReleaseImmediate(DSI_LSHIFT))
			run_multip = 1;
		if(Keyboard::ptr()->getIsKeyDownImmediate(DSI_W))
			camera.move(2*(float)run_multip/fps);
		if(Keyboard::ptr()->getIsKeyDownImmediate(DSI_S))
			camera.move(-2*(float)run_multip/fps);
		if(Keyboard::ptr()->getIsKeyDownImmediate(DSI_D))
			camera.strafe(1.5f*(float)run_multip/fps);
		if(Keyboard::ptr()->getIsKeyDownImmediate(DSI_A))
			camera.strafe(-1.5f*(float)run_multip/fps);
		if(Keyboard::ptr()->getIsKeyDownImmediate(DSI_F1) && !f1_pressed)
		{
			f1_pressed = true;
			draw_console = !draw_console;
		};
		if(Keyboard::ptr()->getIsKeyReleaseImmediate(DSI_F1))
			f1_pressed = false;
		int x = Mouse::ptr()->getRelMouseMoveImmediate(DMI_X);
		int y = Mouse::ptr()->getRelMouseMoveImmediate(DMI_Y);
		if(x != 0 || y != 0)
		{
			float dx = -(float)x*mouse_sensitivity*20/fps;
			float dy = (float)y*mouse_sensitivity*20/fps;
			camera.rotateX(dy);
			camera.rotateY(dx);
		};

		if(Keyboard::ptr()->getIsKeyDownImmediate(DSI_G) && !g_pressed)
		{
			g_pressed = true;
			is_glare = !is_glare;
		};
		if(Keyboard::ptr()->getIsKeyReleaseImmediate(DSI_G))
			g_pressed = false;

		if(Keyboard::ptr()->getIsKeyDownImmediate(DSI_DOWN) && !down_pressed)
		{
			down_pressed = true;
			if(menu_position < menu_size-1)
				menu_position++;
		};
		if(Keyboard::ptr()->getIsKeyReleaseImmediate(DSI_DOWN))
			down_pressed = false;

		if(Keyboard::ptr()->getIsKeyDownImmediate(DSI_UP) && !up_pressed)
		{
			up_pressed = true;
			if(menu_position > 0)
				menu_position--;
		};
		if(Keyboard::ptr()->getIsKeyReleaseImmediate(DSI_UP))
			up_pressed = false;

		if(Keyboard::ptr()->getIsKeyDownImmediate(DSI_LEFT))
			*menu_values[menu_position] -= 0.2f*run_multip/fps;
		if(Keyboard::ptr()->getIsKeyDownImmediate(DSI_RIGHT))
			*menu_values[menu_position] += 0.2f*run_multip/fps;

		splash_system.update(fps, camera.getCamera());

		for(unsigned int k = 0; k < 6; ++k)
		{
			GraphicDriver::ptr()->begin(environment, depth_env, k);
			GraphicDriver::ptr()->setViewport(cubemap_size, cubemap_size);
			GraphicDriver::ptr()->clear();

			shared_ptr<Camera> cubemap_camera(new Camera());
			cubemap_camera->setAspect(1);
			cubemap_camera->setFOV(90*deg_to_rad);
			cubemap_camera->setPosition(camera.getCamera()->getPosition());
			cubemap_camera->setTarget(cubemap_camera->getPosition() + camera_dir[k]);
			cubemap_camera->setZnearFar(camera.getCamera()->getZnear(), camera.getCamera()->getZfar());
			if(k == 0 || k == 1 || k == 4 || k == 5)
				cubemap_camera->setUp(vect3_sing_y);
			if(k == 2)
				cubemap_camera->setUp(-vect3_sing_z);
			if(k == 3)
				cubemap_camera->setUp(vect3_sing_z);

			sky.draw(cubemap_camera);

			sky_cubemap = sky.getSkybox();

			for(unsigned int i = 0; i < planar_landscape->getMeshesNumber(); ++i)
			{
				shared_ptr<Effect> eff = planar_landscape->getMesh(i)->getEffect();
				eff->setMacro("POINT_LIGHT_COUNT", point_lights_number);
				eff->setVect4Array("point_light_pos", plight_swizzled, 3);
				eff->setVect4("point_light_inv_range", &plight_inv_range);
				eff->setVect3Array("point_light_color_intensity", plight_ci, point_lights_number);
				eff->setMacro("SPECULAR", 0);
				eff->setMacro("DIRECTIONAL_LIGHT_COUNT", 0);
				eff->setMacro("SPOT_LIGHT_COUNT", 0);
				eff->setMacro("OUTDOOR", 1);
				eff->setTexture("skybox", sky_cubemap);
				planar_landscape->drawMesh(i, cubemap_camera);
			};

			drawSimpleObject(models, cubemap_camera);
			
			GraphicDriver::ptr()->end();
		};


		// DRAW SCENE TO RENDER TARGET
		no_hdr.beginRenderObjects();
		GraphicDriver::ptr()->clear(Vect4(0.1f, 0.1f, 0.1f, 1.0f), 1, 0);
		GraphicDriver::ptr()->setViewport(0, 0);

		sky.draw(camera.getCamera());

		sky_cubemap = sky.getSkybox();

		for(unsigned int i = 0; i < planar_landscape->getMeshesNumber(); ++i)
		{
			shared_ptr<Effect> eff = planar_landscape->getMesh(i)->getEffect();
			eff->setMacro("POINT_LIGHT_COUNT", point_lights_number);
			eff->setVect4Array("point_light_pos", plight_swizzled, 3);
			eff->setVect4("point_light_inv_range", &plight_inv_range);
			eff->setVect3Array("point_light_color_intensity", plight_ci, point_lights_number);
			eff->setMacro("SPECULAR", 0);
			eff->setMacro("DIRECTIONAL_LIGHT_COUNT", 0);
			eff->setMacro("SPOT_LIGHT_COUNT", 0);
			eff->setMacro("OUTDOOR", 1);
			eff->setTexture("skybox", sky_cubemap);
			planar_landscape->drawMesh(i, camera.getCamera());
		};

		drawSimpleObject(models, camera);

		splash_system.draw(camera.getCamera());

		// DRAW RAIN

		Matr4 inverse_WVP = camera.getCamera()->getViewProjection().getInverse();
		rain_effect->setMatrix4x4("mViewProjClipSpace", &inverse_WVP);
		for(int j = layer_number; j > 0; --j)
		{
			Vect4 rain_dirCP = rain_directionCP[j-1];
			for(unsigned int i = 0; i < rain_quad->getModel()->getMeshesNumber(); ++i)
			{
				shared_ptr<Material> mat = rain_quad->getModel()->getMesh(i)->getMaterial();
				mat->addTexture("environment", environment->getTexture());

				Vect4 rp = rain_params;
				rp.x = rain_parallax[j-1];
				rp.y = rain_contrast[j-1];
				rp.z = rain_bias[j-1];
				additional_rain_params.z = time;
				Vect4 arp = additional_rain_params;
				arp.y /= j;
				arp.x /= j;

				float old_bias_x = rain_dirCP.z;
				float old_bias_y = rain_dirCP.w;
				Vect2 old_dirCP = Vect2(rain_dirCP.x, rain_dirCP.y);
				// calculate rain direction in clip space
				rain_dirCP = (camera.getCamera()->getViewProjection() * rain_direction);
				// we need only normalize (x, y) vector
				Vect2 rdcp = Vect2(rain_dirCP.x, rain_dirCP.y).getNormalize();
				rain_dirCP = Vect4(rdcp.x, rdcp.y, 0, 0);
				rain_dirCP.z = (old_dirCP.x-rain_dirCP.x) * arp.x * previous_time + old_bias_x;
				rain_dirCP.w = (old_dirCP.y-rain_dirCP.y) * arp.y * previous_time + old_bias_y;
				rain_directionCP[j-1] = rain_dirCP;

				mat->addVect4("rain_directionCP", rain_dirCP);
				mat->addVect4("vLightPos0", vLightPos0);
				mat->addVect4("rain_params", rp);
				mat->addVect4("additional_params", arp);
				mat->addVect4("light_color_brightness", light_color_brightness);

				float depth = 0.0f;
				if(j == 4)
					depth = 0.999f;
				if(j == 3)
					depth = 0.995f;
				if(j == 2)
					depth = 0.985f;
				rain_effect->setFloat("depth", &depth);
				rain_quad->getModel()->drawMesh(i, camera.getCamera());
			};

		};

		if(draw_console)
		{
			unsigned int x_start = 0;
			char fpss[80];
			_gcvt_s(fpss, 10, fps, 6);
			std::string fpsstr = "FPS " + std::string(fpss);
			TextManager::ptr()->addText(fpsstr, x_start, 0, 1.0f);
			sprintf_s(fpss, "Camera position (%.3f, %.3f, %.3f)", camera.getCamera()->getPosition().x, camera.getCamera()->getPosition().y, camera.getCamera()->getPosition().z);
			TextManager::ptr()->addText(fpss, x_start, 20, 1.0f);

			unsigned int text_y_start = 40;
			for(unsigned int i = 0; i < menu_size; ++i)
			{
				if(i != menu_position)
					sprintf_s(fpss, "%s%f", menu[i].c_str(), *menu_values[i]);
				else
					sprintf_s(fpss, "->%s%f", menu[i].c_str(), *menu_values[i]);
				TextManager::ptr()->addText(fpss, x_start, text_y_start, 1.0f);
				text_y_start += 20;
			};
			TextManager::ptr()->drawAll();
		};
		// FINISH DRAWING TO RENDER TARGET
		no_hdr.end();

		// DOWNSAMPLING SCENE
		no_hdr.calculateAverageLuminocity();
		// FINISH DOWNSAMPLING

		// GLOW EFFECT PROCCESS
		shared_ptr<RenderTarget> streched_scene = no_hdr.getStrechedScene(streched_level);
		shared_ptr<Quad> fullscreen_quad = no_hdr.getFullscreenQuad(streched_level + 1);
		
		GraphicDriver::ptr()->beginWithColor(treshold_rt);
		GraphicDriver::ptr()->setViewport((unsigned int)treshold_tex_size.x, (unsigned int)treshold_tex_size.y);
		for(unsigned int i = 0; i < fullscreen_quad->getModel()->getMeshesNumber(); ++i)
		{
			shared_ptr<Material> mat = fullscreen_quad->getModel()->getMesh(i)->getMaterial();
			mat->setEffect(glow);
			mat->setTechnique("threshold");
			mat->addTexture("scene_texture", streched_scene->getTexture());
			mat->addVect3("glare_params", glare_params);
			fullscreen_quad->getModel()->drawMesh(i, tmp_camera);
		};
		GraphicDriver::ptr()->end();

		for(unsigned int j = 0; j < 2; ++j)
		{
			shared_ptr<RenderTarget> tmp_rt = j == 0 ? treshold_rt : blured_rt[j-1];
			GraphicDriver::ptr()->beginWithColor(blured_rt[j]);
			for(unsigned int i = 0; i < fullscreen_quad->getModel()->getMeshesNumber(); ++i)
			{
				shared_ptr<Material> mat = fullscreen_quad->getModel()->getMesh(i)->getMaterial();
				glow->setCurTechnique("blur");
				mat->setEffect(glow);
				mat->setTechnique("blur");
				mat->addTexture("stretched_scene", tmp_rt->getTexture());
				mat->addVect2("one_pixel_offset", inv_treshold_tex_size);
				glow->setMacro("HORIZONTAL_PASS", j);
				fullscreen_quad->getModel()->drawMesh(i, tmp_camera);
			};
			GraphicDriver::ptr()->end();
		};

		// DRAW FULLSCREEN QUAD
		GraphicDriver::ptr()->setViewport(0, 0);
		GraphicDriver::ptr()->beginWithColor(GraphicDriver::ptr()->getBackBuffer());
		fullscreen_quad = no_hdr.getFullscreenQuad();
		for(unsigned int i = 0; i < fullscreen_quad->getModel()->getMeshesNumber(); ++i)
		{
			shared_ptr<Material> mat = fullscreen_quad->getModel()->getMesh(i)->getMaterial();
			mat->setEffect(glow);
			mat->setTechnique("final");
			mat->addTexture("scene_texture", no_hdr.getSceneRenderTarget()->getTexture());
			mat->addTexture("blured_texture", blured_rt[1]->getTexture());
			if(is_glare)
				mat->addVect3("glare_intensity", glare_intensity);
			else
				mat->addVect3("glare_intensity", vect3_zero);
			fullscreen_quad->getModel()->drawMesh(i, tmp_camera);
		};

		GraphicDriver::ptr()->end();
		// FINISH DRAWING QUAD

		GraphicDriver::ptr()->present();

		GraphicDriver::ptr()->unsetAll();

		FileManager::ptr()->reportToAllListeners();
		float ptime = Profiler::ptr()->endTimer();
		fps_stack.push(ptime);
		fps = 1000.0f/fps_stack.getAverage();
		TextManager::ptr()->clear();
	};
	return 0;
};