#include "demo_abgabe1.h"

#include "scenenode.h"
#include "transformation_node.h"
#include <fstream>

#include <glm/gtx/transform.hpp>
// glm::perspective
#include <glm/gtc/matrix_projection.hpp>
// glm::translate, glm::rotate, glm::scale
#include <glm/gtc/matrix_transform.hpp>
// glm::value_ptr
#include <glm/gtc/type_ptr.hpp> 


/* Schatten anpassen, Kamerafahrt - Coords auslesen
 *
 *
 */

demo_abgabe1::demo_abgabe1()
{
	poly_offset_factor = 1.1;//8;
	poly_offset_units = 4.0;
	title = "RTR - altweiberherbst - Abgabe 2";
	width = 1280;
	height = 800;
	near_far = glm::vec2(0.1f, 200.0f);
	//snow control
	draw_snow = false;
	frustrum_culling_enabled = true;
	//sound control
	volume = 0.06f;
	actualseconds = lastseconds = time(NULL);
	pp_border = 1.0;
	soft_shadows = 0;
	dof_enabled = false;
	automcamera = false;
	enable_odsm = true;
	load_config("../../abgabe_config.txt");
	perspective_matrix = glm::perspective(45.0f, static_cast<GLfloat>(width) / static_cast<GLfloat>(height),near_far.x, near_far.y);
	perspective_shadow = glm::perspective(45.0f, static_cast<GLfloat>(width) / static_cast<GLfloat>(height) ,near_far.x, near_far.y);
	view_frustrum.set_perspective(45.0f, static_cast<GLfloat>(width) / static_cast<GLfloat>(height), near_far.x, near_far.y);
	sm_width = 3000;
	sm_height = 2000;
	object_root = transformation_node();
	//for tuning of the camerapath
	maxfactor = 38000;
	cubemap_dim = 800;
	endscreen = false;
	blackscreen = false;
}

demo_abgabe1::~demo_abgabe1()
{
	glDeleteFramebuffers(1, &shadow_FBO);
	glDeleteFramebuffers(1, &odsm_FBO);
	glDeleteFramebuffers(1, &depth_fbo);
	glDeleteFramebuffers(3, fbo_id);

	glDeleteTextures(NUM_TEXTURES, texture);
	glDeleteTextures(1, &shadow_tex_ID);
	glDeleteTextures(1, &odsm_tex_ID);
	glDeleteTextures(1, &offset_tex);
	glDeleteTextures(3, scene_tex_id);
	glDeleteTextures(3, depth_tex_id);
	glDeleteTextures(3, depth_buffer);
	glDeleteTextures(1, &depth_tex);
}

bool demo_abgabe1::init()
{	
	if (!init_window(fullscreen))
	{
		return false;
	}
	
	init_light_positions();

	//cam
	cam_eye = glm::vec3(7.0f, 5.0f, 28.f);
	cam_center = glm::vec3(0.0f, 5.0f, 0.0f);
	//for debuggung purposes
	//cam_eye = glm::vec3(-7.97996f,1.99005f,-19.5445f);
	//cam_center = glm::vec3(-28.6317f,2.86953f,0.598813f);
	//cam_eye = glm::vec3(-6.83028f,3.61306f,22.103f);
	//cam_center = glm::vec3(-6.32693f,3.79412f,-6.75422f);
	cam_up = glm::vec3(0.0f, 1.0f, 0.0f);
	view_frustrum.set_camera(cam_eye, cam_center, cam_up);

	//light
	light_0 = glm::vec3(-15.0f,65.0f,-29.6643f);		//beginn!!!
	//light_0 = glm::vec3(35.99f,65.0f,30.6295f);	//TESTING!!!
	light_1 = glm::vec3(23.0f, 0.0f, 23.0f);		//BEGINN!!
	//light_1 = glm::vec3(-14.3603f,8.12279f,-4.11972f);	//testing
	corner_fire_shadow = odsm_source(800, light_1, glm::vec2(0.1f, 60.0f));

	//do opengl initialization here
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
	glEnable(GL_DEPTH_TEST);
	glClearDepth(1.0f);
	glDepthFunc(GL_LEQUAL);
	//glEnable(GL_CULL_FACE);


	//---------------------
	// draw the loadscreen
	//---------------------
			
	texture_simple = std::auto_ptr<Shader>( new Shader ("../../shader/texture") );
	if (!(*texture_simple)) {
		cerr << "Could not compile texture_simple shader program." << endl;
		return 1;
	}

	texture_simple->bind_frag_data_location("out_color");

	if (!load_tga_texture(&texture[BG], "../../textures/bg1.tga", GL_SRGB8_ALPHA8) 
		)
	{
		std::cout << "failed to load textures background!\n";
		return false;
	}

	if (!load_tga_texture(&texture[LOADSCREEN_TEX], "../../textures/loadscreen_tex.tga", GL_SRGB8_ALPHA8) 
		)
	{
		std::cout << "failed to load textures background!\n";
		return false;
	}
	
	init_loadscreen(*texture_simple, loadscreen_vbo, &loadscreen_vao);
	bg = object_node(  glm::translate(0.0f, 0.0f, -5.0f) * glm::scale(10.0f, 10.0f, 10.0f), loadscreen_vao, 6, SH_TEXTURE);
	//bg.set_texture_id(texture[LOADSCREEN]);


	draw_loadscreen(bg, texture[LOADSCREEN_TEX]);

	//--------------
	//load textures
	//--------------
	std::cout << "loading textures...";	
	if (!load_tga_texture(&texture[LOADSCREEN_SHA], "../../textures/loadscreen_sha.tga", GL_SRGB8_ALPHA8) 
		)
	{
		std::cout << "failed to load textures background!\n";
		return false;
	}
	if (!load_tga_texture(&texture[LOADSCREEN_MOD], "../../textures/loadscreen_mod.tga", GL_SRGB8_ALPHA8) 
		)
	{
		std::cout << "failed to load textures background!\n";
		return false;
	}
	if (!load_tga_texture(&texture[LOADSCREEN_END], "../../textures/loadscreen_end.tga", GL_SRGB8_ALPHA8) 
		)
	{
		std::cout << "failed to load textures background!\n";
		return false;
	}
	if (!load_tga_texture(&texture[TERRAIN], "../../textures/Terrain.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures terrain!\n";
		return false;
	}
	if (!load_tga_texture(&texture[TREE1], "../../textures/bark11_2.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures tree1!\n";
		return false;
	}
	if(!load_tga_texture(&texture[NTREE1], "../../textures/bark11_2_n.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures ntree1!\n";
		return false;
	}
	if (!load_tga_texture(&texture[ALTAR], "../../textures/marble05.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures altar!\n";
		return false;
	}
	if(!load_tga_texture(&texture[NALTAR], "../../textures/marble05b.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures naltar!\n";
		return false;
	}
	if (!load_tga_texture(&texture[RUIN], "../../textures/brick.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures ruin!\n"; //brick18
		return false;
	}
	if(!load_tga_texture(&texture[NRUIN], "../../textures/brick_n.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures nruin!\n";
		return false;
	}
	if (!load_tga_texture(&texture[TREE2], "../../textures/bark08.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures tree2!\n";
		return false;
	}
	if(!load_tga_texture(&texture[NTREE2], "../../textures/bark08_n.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures ntree2!\n";
		return false;
	}
	if (!load_tga_texture(&texture[TREE3], "../../textures/wood_06.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures tree3!\n";
		return false;
	}
	if(!load_tga_texture(&texture[NTREE3], "../../textures/wood_06_n.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures ntree3!\n";
		return false;
	}
	if (!load_tga_texture(&texture[TREE4], "../../textures/wood_02.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures tree4!\n";
		return false;
	}
	if(!load_tga_texture(&texture[NTREE4], "../../textures/wood_02_n.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures ntree4!\n";
		return false;
	}
	if (!load_tga_texture(&texture[CELTICCROSS], "../../textures/marble05.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures celticcross!\n";
		return false;
	}
	if(!load_tga_texture(&texture[NCELTICCROSS], "../../textures/marble05b.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures ncelticcross!\n";
		return false;
	}
	if (!load_tga_texture(&texture[FENCEPOST], "../../textures/stone09.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures fencepost!\n";
		return false;
	}
	if (!load_tga_texture(&texture[NFENCEPOST], "../../textures/stone09n.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures fencepost normal!\n";
		return false;
	}
	if (!load_tga_texture(&texture[FIREPLACE], "../../textures/Fireplace_Tex.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures fireplace!\n";
		return false;
	}
	if(!load_tga_texture(&texture[NFIREPLACE], "../../textures/Fireplace_Tex_n.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures nfireplace!\n";
		return false;
	}
	if (!load_tga_texture(&texture[WATCHTOWER], "../../textures/Wachturm_tex_x.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures watchtower!\n";
		return false;
	}
	if(!load_tga_texture(&texture[NWATCHTOWER], "../../textures/Wachturm_tex_x_n.tga", GL_SRGB8_ALPHA8, true))
	{
		std::cout << "failed to load textures nwatchtower!\n";
		return false;
	}
	if (!load_tga_texture(&texture[FIRE_PARTICLE_1], "../../textures/fire_particle_1.tga", GL_SRGB8_ALPHA8))
	{
		std::cout << "failed to load textures particle1!\n";
		return false;
	}
	if (!load_tga_texture(&texture[FIRE_PARTICLE_2], "../../textures/fire_particle_2.tga", GL_SRGB8_ALPHA8))
	{
		std::cout << "failed to load textures particle2!\n";
		return false;
	}
	if(!load_tga_texture(&texture[TERRAIN2], "../../textures/misc02.tga", GL_SRGB8_ALPHA8))
	{
		std::cout << "failed to load texture terrain2!\n";
		return false;
	}
	
	if(!load_tga_texture(&texture[SNOWFLAKE1], "../../textures/snowflake_1.tga", GL_SRGB8_ALPHA8))
	{
		std::cout << "failed to load texture terrain2!\n";
		return false;
	}



	std::cout << "done\n";

	
	draw_loadscreen(bg, texture[LOADSCREEN_SHA]);

	std::cout << "loading shaders...";
	//-------------------------------
	// Load and compile Shader files
	//-------------------------------
	texture_shader = std::auto_ptr<Shader>( new Shader ("../../shader/texture_twolights") );
	if (!(*texture_shader)) {
		cerr << "Could not compile texture_shadowmap program." << endl;
		return false;
	}

	texture_shader->bind_frag_data_location("out_color");

	
	// Load and compile Shader files	
	shadowmap_gen = std::auto_ptr<Shader>( new Shader ("../../shader/shadowmap_gen") );
	if (!(*shadowmap_gen)) {
		cerr << "Could not compile  shadowmap_gen program." << endl;
		return false;
	}

	shadowmap_gen->bind_frag_data_location("out_color");

	texture_cm = std::auto_ptr<Shader>( new Shader ("../../shader/texture_cm") );
	if (!(*texture_cm)) {
		cerr << "Could not compile texture_cm shader program." << endl;
		return 1;
	}

	texture_cm->bind_frag_data_location("out_color");

	texture_normalmap = std::auto_ptr<Shader>( new Shader("../../shader/texture_shadownorm"));
	if(!(*texture_normalmap)) {
		cerr << "Could not compile texture_normalmap program." << endl;
		return 1;
	}
	texture_normalmap->bind_frag_data_location("out_color");

	
	gauss_vertical = std::auto_ptr<Shader>( new Shader ("../../shader/gauss_vertical") );
	if (!(*gauss_vertical)) {
		cerr << "Could not compile gauss_vertical shader program." << endl;
		return 1;
	}

	gauss_vertical->bind_frag_data_location("out_color");

	gauss_horizontal = std::auto_ptr<Shader>( new Shader ("../../shader/gauss_horizontal") );
	if (!(*gauss_horizontal)) {
		cerr << "Could not compile gauss_horizontal shader program." << endl;
		return 1;
	}

	gauss_horizontal->bind_frag_data_location("out_color");
	
	//depthmap shader for dof	
	depthmap_gen = std::auto_ptr<Shader>( new Shader ("../../shader/depthmap_gen") );
	if (!(*depthmap_gen)) {
		cerr << "Could not compile depthmap shader program." << endl;
		return false;
	}

	//depthmap_gen->bind_frag_data_location("out_color");

	// Load and compile Shader files	
	pp_dof = std::auto_ptr<Shader>( new Shader ("../../shader/pp_dof") );
	if (!(*pp_dof)) {
		cerr << "Could not compile pp_dof shader program." << endl;
		return false;
	}

	pp_dof->bind_frag_data_location("out_color");

		
	shadow_r2cm = std::auto_ptr<geometry_shader>( new geometry_shader("../../shader/shadow_r2cm") );
	if (!(*shadow_r2cm)) {
		cerr << "Could not compile shadow_r2cm shader program." << endl;
		return false;
	}
	//shadow_r2cm->bind_frag_data_location("out_color");

	texture_odsm = std::auto_ptr<Shader>( new Shader ("../../shader/texture_odsm") );
	if (!(*texture_odsm)) {
		cerr << "Could not compile texure_odsm shader program." << endl;
		return false;
	}
	texture_odsm->bind_frag_data_location("out_color");

	
	snow_shadow = std::tr1::shared_ptr<Shader>( new Shader ("../../shader/snow_shadow") );
	if (!(*snow_shadow)) {
		cerr << "Could not compile snow_shadow shader program." << endl;
		return false;
	}
	//snow_shadow->bind_frag_data_location("out_color");

	
	snow_render = std::tr1::shared_ptr<Shader>( new Shader ("../../shader/snow_render") );
	if (!(*snow_render)) {
		cerr << "Could not compile snow_render shader program." << endl;
		return false;
	}
	snow_render->bind_frag_data_location("out_color");

	
	std::cout << "done\n";

	
	draw_loadscreen(bg, texture[LOADSCREEN_MOD]);
	//----------------------------
	//wavefront object model setup
	//----------------------------
	std::cout << "loading models..." << std::endl;
	texture_shader->bind();
	texture_shader->set_uniform("soft_shadows", soft_shadows);
	texture_shader->set_uniform("ultra_shadows", ultra_shadows);

	//comment lines here if you want to test something quickly without huge loading times!
	if (!load_terrain()) {return false;}
	texture_shader->unbind();
	texture_normalmap->bind();
	texture_normalmap->set_uniform("soft_shadows", soft_shadows);
	if (!load_trees()) {return false;}
	if (!load_stones()) {return false;}
	if (!load_ruin()) {return false;}
	texture_normalmap->unbind();
	//texture_shader->bind();
	

	std::cout << "done\n";
	//texture_shader->unbind();

	shadowmap_gen->bind();

	shadowmap_gen->set_uniform("soft_shadows", soft_shadows);
	std::cout << "assign vertices to shadowmap shader:\n";
	//init shadowmap_gen shader
	glBindVertexArray(object_model.getVAO());
	glBindBuffer(GL_ARRAY_BUFFER, object_model.getVBO_vertices());
	GLint vertex_location = shadowmap_gen->get_attrib_location("in_position");
	glEnableVertexAttribArray(vertex_location);
	glVertexAttribPointer(	vertex_location, 4, GL_FLOAT, GL_FALSE, 0, NULL);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_model.getVBO_indices());

	
	//set far plane for sm gen shader
	//TODO add some member for near/far plane
	shadowmap_gen->set_uniform("near_far", glm::vec2(0.1f, 200.0f));

	std::cout << "assign vertices to shadowmap shader done \n";
	shadowmap_gen->unbind();

	depthmap_gen->bind();

	std::cout << "assign vertices to depthmap shader:\n";
	//init depthmap_gen shader
	glBindVertexArray(object_model.getVAO());
	glBindBuffer(GL_ARRAY_BUFFER, object_model.getVBO_vertices());
	vertex_location = depthmap_gen->get_attrib_location("in_position");
	glEnableVertexAttribArray(vertex_location);
	glVertexAttribPointer(	vertex_location, 4, GL_FLOAT, GL_FALSE, 0, NULL);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_model.getVBO_indices());

	
	//set far plane for sm gen shader
	//TODO add some member for near/far plane
	depthmap_gen->set_uniform("near_far", glm::vec2(0.1f, 200.0f));

	std::cout << "assign vertices to depthmap shader done \n";
	depthmap_gen->unbind();

	std::cout << "init particles...";
	init_particles();

	//particle node placement
	fire_node.transformation_matrix = glm::translate(0.0f, 0.1f, 0.0f);
	fireplace_position.add_child(fire_node);
	corner_fire_node.transformation_matrix = glm::translate(light_1);
	object_root.add_child(corner_fire_node);
	snow_node.transformation_matrix = glm::translate(0.0f, 30.0f, 0.0f);
	//draw_snow=true;
	if (draw_snow) {
		root.add_child(snow_node);
	}

	root.add_child(object_root);
	std::cout << "done" << endl;

	//init shadowmap fbo and texture
	std::cout << "init shadowmap... ";
	init_shadowmap();
	corner_fire_shadow.init_odsm();
	get_errors();
	std::cout << "done" << endl;
	
	//skybox
	std::cout << "init skybox ";
	init_skybox();
	get_errors();
	std::cout << "done" << endl;

	
	//init the sound system
	std::cout << "init sound ";
	init_soundsystem();
	std::cout << "done" << endl;

	
	//post-process init
	init_postprocess();
	
	//fbo init
	if (!init_fbo()) {
		std::cout << "error creating fbo" << std::endl;
		return false;
	}

	//abfrage, ob die automatische kamera aktiviert wurde
	if(automcamera)
	{
		spline.load_cam_positions("../../camerapoints.txt");
		firespline.load_firecam_positions("../../firepoints.txt");
		preparecampath();
		preparefirepath();

		//start the backgroundsound
		result = system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel);
		errcheck(result);
		//set the volume
		channel->setVolume(volume);
		isplaying = true;

		licht1 = false;
		licht2 = false;
		licht3 = false;
		licht4 = false;
		windsound = false;
		kamin = false;
		cntcam = 0;
		cntwind = 0;
	}


	return true;
}

void demo_abgabe1::init_shadowmap()
{
	// create the texture we'll use for the shadowmap
	glGenTextures(1, &shadow_tex_ID);
	glBindTexture(GL_TEXTURE_2D, shadow_tex_ID);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, sm_width,
	sm_height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
	glGenFramebuffers(1, &shadow_FBO);
	glBindFramebuffer(GL_FRAMEBUFFER, shadow_FBO);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
	GL_TEXTURE_2D, shadow_tex_ID, 0);
	glDrawBuffer(GL_NONE); // essential for depth-only FBOs!!!
	glReadBuffer(GL_NONE); // essential for depth-only FBOs!!!
	

	std::cout << "framebuffer completeness check:\n";
	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
	{
		// ToDo:
		printf("GL_FRAMEBUFFER_COMPLETE error 0x%x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
	}
	std::cout << "framebuffer completeness check done\n";

	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	// then, just before rendering
	//glBindFramebuffer(GL_FRAMEBUFFER, shadow_FBO);

	init_offset_texture();


}

//make an offset texture for 
void demo_abgabe1::init_offset_texture() 
{
	srand(0);

	//first, build the image data
	const int owidth = 1;
	const int oheight = 1;
	int odepth = 8;

	GLfloat image[8][oheight][owidth][3];

	GLfloat offset_values[] = {
		0,0,
		0,0,
		-1,0,
		0,1,
		1,1,
		-1,1,
		0,-1,
		1,-1,
		-1,-1 };

	int s,t,r;
	float jitter_weight = 0.5;
	for(s=0; s < owidth; s++) {
		float sjitter = 1.0 - (rand()%51)/100.0f; //jitter for current pixel
		for (t=0; t < oheight; t++) {
			float tjitter = 1.0 - (rand()%51)/100.0f; //jitter for current pixel
			for (r=0; r < odepth; r++) {
				image[r][t][s][0] = sjitter * jitter_weight +
									offset_values[2*r];//(rand()%101)/100.0f;//
				image[r][t][s][1] = tjitter * jitter_weight +
									offset_values[2*r + 1];//(rand()%101)/100.0f;//
				image[r][t][s][2] = 0.0f;

			}
		}
	}

	//generate the texture
	glGenTextures(1, &offset_tex);
	glActiveTexture(GL_TEXTURE4);
	glBindTexture(GL_TEXTURE_3D, offset_tex);

	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB, owidth, oheight, odepth, 0, GL_RGB, GL_FLOAT, image);
	
	texture_shader->bind();
	texture_shader->set_uniform("shadow_offsets", 4);
	texture_shader->unbind();
	texture_normalmap->bind();
	texture_normalmap->set_uniform("shadow_offsets", 4);
	texture_normalmap->unbind();

	get_errors();
}

//build the initial particle arrays and the particle nodes
void demo_abgabe1::init_particles() 
{
	
	srand(time(0));

	int particle_lookup_width = 64;
	std::vector<GLfloat> points(particle_lookup_width*particle_lookup_width*2);		//points for referencing each texel
	std::vector<GLfloat> particles(particle_lookup_width*particle_lookup_width*4);	//initial particle data
		//create points for referencing texels
	for (int y=0; y < particle_lookup_width; y++ ) 
	{
		for (int x=0; x < particle_lookup_width; x++ ) 
		{
			points[x*2+0 + y*particle_lookup_width*2] = static_cast<GLfloat>(x) / static_cast<GLfloat>(particle_lookup_width);
			points[x*2+1 + y*particle_lookup_width*2] = static_cast<GLfloat>(y) / static_cast<GLfloat>(particle_lookup_width);
			
		}
	}

	//set initial values for each particle
	for (int y=0; y < particle_lookup_width; y++ ) 
	{
		for (int x=0; x < particle_lookup_width; x++ ) 
		{
			particles[x*4 +0 + y*particle_lookup_width*4] = 0.0f;
			particles[x*4 +1 + y*particle_lookup_width*4] = 0.0f;
			particles[x*4 +2 + y*particle_lookup_width*4] = 0.0f;
			particles[x*4 +3 + y*particle_lookup_width*4] = (rand()%101)/100.0f;
		}
	}

	fire_node = particle_node(points, particles, texture[FIRE_PARTICLE_1], texture[FIRE_PARTICLE_2], particle_lookup_width);
	fire_node.set_modelview_matrix(modelview_matrix);
	fire_node.set_perspective_matrix(perspective_matrix);
	fire_node.init();

	//hack the view frustrum culling with a hardcoded bounding sphere
	float dim = 1.0f;
	fire_node.set_dimension(glm::vec3(dim, dim, dim));

	
	/*-------------------
	  corner fire particles
	-------------------*/
	particle_lookup_width = 32;
		//create points for referencing texels
	for (int y=0; y < particle_lookup_width; y++ ) 
	{
		for (int x=0; x < particle_lookup_width; x++ ) 
		{
			points[x*2+0 + y*particle_lookup_width*2] = static_cast<GLfloat>(x) / static_cast<GLfloat>(particle_lookup_width);
			points[x*2+1 + y*particle_lookup_width*2] = static_cast<GLfloat>(y) / static_cast<GLfloat>(particle_lookup_width);
			
		}
	}

	//set initial values for each particle
	for (int y=0; y < particle_lookup_width; y++ ) 
	{
		for (int x=0; x < particle_lookup_width; x++ ) 
		{
			particles[x*4 +0 + y*particle_lookup_width*4] = 0.0f;
			particles[x*4 +1 + y*particle_lookup_width*4] = 0.0f;
			particles[x*4 +2 + y*particle_lookup_width*4] = 0.0f;
			particles[x*4 +3 + y*particle_lookup_width*4] = (rand()%101)/100.0f;
		}
	}

	corner_fire_node = particle_node(points, particles, texture[FIRE_PARTICLE_1], texture[FIRE_PARTICLE_2], particle_lookup_width);
	corner_fire_node.set_modelview_matrix(modelview_matrix);
	corner_fire_node.set_perspective_matrix(perspective_matrix);
	corner_fire_node.init();
	corner_fire_node.set_burn_speed(15.0);
	//corner_fire_node.set_depth_enabled(true);

	//hack the view frustrum culling with a hardcoded bounding sphere
	//corner_fire_node.set_dimension(glm::vec3(dim, dim, dim));
	corner_fire_node.set_dimension(glm::vec3(300, 300, 300));

	/*-------------------
	  init snow particles
	-------------------*/
	particle_lookup_width = 64;
	//create points for referencing texels
	for (int y=0; y < particle_lookup_width; y++ ) 
	{
		for (int x=0; x < particle_lookup_width; x++ ) 
		{
			points[x*2+0 + y*particle_lookup_width*2] = static_cast<GLfloat>(x) / static_cast<GLfloat>(particle_lookup_width);
			points[x*2+1 + y*particle_lookup_width*2] = static_cast<GLfloat>(y) / static_cast<GLfloat>(particle_lookup_width);
			
		}
	}

	//set initial values for each particle
	const int snow_spread = 200;
	const int half_snow_spread = snow_spread / 2;
	for (int y=0; y < particle_lookup_width; y++ ) 
	{
		for (int x=0; x < particle_lookup_width; x++ ) 
		{
			particles[x*4 +0 + y*particle_lookup_width*4] = (rand()%snow_spread)/1.0f - half_snow_spread;
			particles[x*4 +1 + y*particle_lookup_width*4] = 0.0f;
			particles[x*4 +2 + y*particle_lookup_width*4] = (rand()%snow_spread)/1.0f - half_snow_spread;
			particles[x*4 +3 + y*particle_lookup_width*4] = (rand()%101)/100.0f;
		}
	}

	snow_node = particle_node(points, particles, texture[SNOWFLAKE1], texture[SNOWFLAKE1], particle_lookup_width, "../../shader/snow_render",  "../../shader/snow_update");
	snow_node.set_modelview_matrix(modelview_matrix);
	snow_node.set_perspective_matrix(perspective_matrix);
	snow_node.init();
	snow_node.set_forcefield(glm::vec3(0.0f));
	snow_node.set_depth_enabled(false);
	snow_node.set_shader_type(SH_SNOW);

}

void demo_abgabe1::draw()
{	
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	if(endscreen)
	{
		if(blackscreen)
		{
			bg = object_node(  glm::translate(0.0f, 0.0f, 0.0f) * glm::scale(20.0f, 20.0f, 20.0f), loadscreen_vao, 60, SH_TEXTURE);
			draw_loadscreen(bg, texture[LOADSCREEN_END]);

			Sleep(10000);
			exit(0);
		}
		blackscreen = true;
	}
	else
	{
	
		//TODO hier irgendwie die Kamera aufrufen - noch genau überlegen!
		if(automcamera)
		{
			//do something
			camerapath();
		}

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		load_identity(modelview_matrix);
		load_identity(lightview_matrix);

		//particle systems timeslice update
		fire_node.set_delta(static_cast<float>(delta()));
		corner_fire_node.set_delta(static_cast<float>(delta()));
		snow_node.set_delta(static_cast<float>(delta()));

		//-----------------------
		//generate the shadow map
		//-----------------------
		//look at from light position
		look_at(modelview_matrix, light_0, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

		//glEnable(GL_CULL_FACE);
		//glCullFace(GL_FRONT);
		glBindFramebuffer(GL_FRAMEBUFFER, shadow_FBO);
		glEnable(GL_POLYGON_OFFSET_FILL);
		glPolygonOffset(poly_offset_factor, poly_offset_units);
		glViewport(0, 0, sm_width, sm_height);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		render_shadowmap(object_root);
		//glCullFace(GL_BACK);
		//glDisable(GL_CULL_FACE);

		glDisable(GL_POLYGON_OFFSET_FILL);

		//--------------------------
		//generate cubemap shadowmap
		//--------------------------
		if (enable_odsm) {
			glBindFramebuffer(GL_FRAMEBUFFER, corner_fire_shadow.odsm_FBO);
			glViewport(0,0,corner_fire_shadow.cubemap_dim, corner_fire_shadow.cubemap_dim);
	
			//we do no not set any camera position via look_at
			//this way, modelview_matrix == model_matrix
			load_identity(modelview_matrix);
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			render_odsm(object_root);

			glBindFramebuffer(GL_FRAMEBUFFER, 0);
		}

		//--------------
		//draw the scene
		//--------------
	
		//set camera position
		load_identity(modelview_matrix);
		load_identity(view_matrix);
		look_at(view_matrix, cam_eye, cam_center, cam_up);	
		//position light
		glm::vec4 temp =  view_matrix * glm::vec4(light_0, 0.0f);
		transformed_light_0 = glm::vec3(temp.x, temp.y, temp.z);

	
		//-----------------------
		//generate the depth map (from camera position)
		//-----------------------

		glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo);
		glViewport(0,0,width,height);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		render_depthmap(root);
		glBindFramebuffer(GL_FRAMEBUFFER, 0);

		//--------------
		//actual scene rendering
		//--------------
		if (dof_enabled) {
			glBindFramebuffer(GL_FRAMEBUFFER, fbo_id[0]);
			fire_node.set_default_framebuffer(fbo_id[0]);
			corner_fire_node.set_default_framebuffer(fbo_id[0]);
			snow_node.set_default_framebuffer(fbo_id[0]);
		}
		else {
			glEnable(GL_FRAMEBUFFER_SRGB);
		}
		load_identity(modelview_matrix);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glViewport(0, 0, width, height);

		glDepthMask(GL_FALSE);
		//draw skybox
		draw_skybox();
		glDepthMask(GL_TRUE);

		render_node(root);

		glUseProgram(0);
		if (dof_enabled) {
			glBindFramebuffer(GL_FRAMEBUFFER, 0);
		}
		else {
			glDisable(GL_FRAMEBUFFER_SRGB);
		}
	

		//------------------
		// post processing
		//------------------
		if (dof_enabled) {
		glBindFramebuffer(GL_FRAMEBUFFER, fbo_id[1]);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glViewport(0,0,width / 2,height / 2);

		draw_postprocess(*gauss_horizontal, scene_tex_id[0], depth_tex);

		glBindFramebuffer(GL_FRAMEBUFFER, fbo_id[2]); 
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glViewport(0,0,width / 2,height / 2);

		draw_postprocess(*gauss_vertical, scene_tex_id[1], depth_tex);

		glBindFramebuffer(GL_FRAMEBUFFER, 0); 
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glEnable(GL_FRAMEBUFFER_SRGB);
	
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glViewport(0,0,width,height);
		////do the mix of scene_tex_id 0 and 2 here (based on depth texture)
		////set blur texture
		glActiveTexture(GL_TEXTURE2);
		glBindTexture(GL_TEXTURE_2D, scene_tex_id[2]);
		//set depth texture
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, depth_tex);

		draw_postprocess(*pp_dof, scene_tex_id[0], depth_tex);
		glDisable(GL_FRAMEBUFFER_SRGB);
		}
	}
	//culling statistics
	std::ostringstream out;
	out << number_culled;
	std::string str(out.str());
	str = "altweiberherbst  - objects culled: " + str + "  ";
	set_title(str);
	number_culled = 0;

}

//recursively render the scenegraph starting at node
void demo_abgabe1::render_node(scene_node& node)
{
	bool cull = false;

	//save current transformation
	push_modelview_matrix();

	//apply transformation of this node
	modelview_matrix = modelview_matrix *  node.transformation_matrix;

	//setup shader and render node
	//Shader* current_shader; //point to the shader of the node
	GLint modelview_location, perspective_location, light_location, tex_location, shadowmap_location, lightview_location, identity_location;
	glm::mat4 range_map = glm::mat4(1.0f);
	glm::mat4 modelview_inverted = glm::mat4(1.0f);
	look_at(modelview_inverted, cam_eye, cam_center, cam_up);	
	modelview_inverted = glm::inverse(modelview_inverted);
	glm::mat4 model_inverted = glm::inverse(modelview_matrix);
	glm::vec4 objectlight, odsm_objectlight;
	switch (node.shader_type())
	{
	case SH_NONE:
		break;
	case SH_PARTICLE://view frustrum culling
		if (frustrum_culling_enabled && in_frustrum(node)) {
				cull = true;
				number_culled++;
				break;
		}
		//add view transformations to modelview matrix
		modelview_matrix = view_matrix * modelview_matrix;
		//do particle update
		node.pre_render();
		//reset vieport
		glViewport(0,0,width,height);

		break;
	case SH_SNOW:
		//add view transformations to modelview matrix
		modelview_matrix = view_matrix * modelview_matrix;
		//do particle update
		node.pre_render();
		//reset vieport
		glViewport(0,0,width,height);

		break;
		
	case SH_NORMTEXTURE:
		//view frustrum culling
		if (frustrum_culling_enabled && in_frustrum(node)) {
				cull = true;
				number_culled++;
				break;
		}

		// Set our matrix uniform
		texture_normalmap->bind();

		//do preparations before rendering
		node.pre_render();

		//set light
		//light_location = texture_shader->get_uniform_location("light_position");
		//glUniform3f(light_location, transformed_light_0.x, transformed_light_0.y, transformed_light_0.z);
		//texture_normalmap->set_uniform("light_position", glm::vec3(transformed_light_0.x, transformed_light_0.y, transformed_light_0.z));
		objectlight = model_inverted * glm::vec4(light_0, 1.0);
		texture_normalmap->set_uniform("light_position", glm::vec3(objectlight.x, objectlight.y, objectlight.z));
		odsm_objectlight = model_inverted * glm::vec4(light_1, 1.0);
		texture_normalmap->set_uniform("light_position_odsm", glm::vec3(odsm_objectlight.x, odsm_objectlight.y, odsm_objectlight.z));

		
		texture_normalmap->set_uniform("ws_odsm_light_position", 
			glm::vec4(corner_fire_shadow.light.x, corner_fire_shadow.light.y, corner_fire_shadow.light.z, 0.0f));
		
		texture_normalmap->set_uniform("in_texture", 0);
		
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, shadow_tex_ID);
		//shadowmap_location = texture_normalmap->get_uniform_location("in_shadowmap");
		//glUniform1i(shadowmap_location, 1);
		texture_normalmap->set_uniform("in_shadowmap", 1);
		glActiveTexture(GL_TEXTURE2);

		node.pre_normal();
		texture_normalmap->set_uniform("normal_map", 2);
		
		glActiveTexture(GL_TEXTURE3);
		glBindTexture(GL_TEXTURE_CUBE_MAP, corner_fire_shadow.odsm_tex_ID);
		texture_shader->set_uniform("shadow_cubemap", 3);
		glActiveTexture(GL_TEXTURE0);

		//set to normalmapping operation - DEBUG, wieder löschen???
		identity_location = texture_normalmap->get_uniform_location("identity");
		glUniform1i(identity_location, 1);

		modelview_location = texture_normalmap->get_uniform_location("modelview_matrix");
		glUniformMatrix4fv( modelview_location, 1, GL_FALSE, glm::value_ptr(view_matrix * modelview_matrix));
		//glUniformMatrix4fv( modelview_location, 1, GL_FALSE, glm::value_ptr(modelview_inverted));

		// Set our matrix uniform
		perspective_location = texture_normalmap->get_uniform_location("perspective_matrix");
		glUniformMatrix4fv( perspective_location, 1, GL_FALSE, glm::value_ptr(perspective_matrix));

		texture_normalmap->set_uniform("model_matrix", modelview_matrix);

		texture_normalmap->set_uniform("model_inverse_matrix", glm::inverse(modelview_matrix));

		//set lightview matrix
		//push_modelview_matrix();
		load_identity(lightview_matrix);
		look_at(lightview_matrix, light_0, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

		lightview_location = texture_normalmap->get_uniform_location("lightview_matrix");
		//bias to scale shadowmap values correctly
		range_map = glm::translate(0.5f, 0.5f, 0.5f) * glm::scale(0.5f, 0.5f, 0.5f);

		//complete lightview aka texture matrix
		//bias * Perspective_light * Cam_light * Cam_inverse *	modelview
		lightview_matrix = range_map * perspective_matrix * lightview_matrix *
							modelview_inverted * view_matrix * modelview_matrix;

		glUniformMatrix4fv( lightview_location, 1, GL_FALSE, glm::value_ptr(lightview_matrix));

		break;

	case SH_TERRAIN:
		// Set our matrix uniform
		texture_shader->bind();

		//do preparations before rendering
		node.pre_render();

		//set light
		texture_shader->set_uniform("light_position", 
			glm::vec3(transformed_light_0.x, transformed_light_0.y, transformed_light_0.z));
		
		texture_shader->set_uniform("ws_odsm_light_position", 
			glm::vec4(corner_fire_shadow.light.x, corner_fire_shadow.light.y, corner_fire_shadow.light.z, 0.0f));

		//texture setup
		texture_shader->set_uniform("in_texture", 0);
		
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, shadow_tex_ID);
		texture_shader->set_uniform("in_shadowmap", 1);

		glActiveTexture(GL_TEXTURE2);
		node.pre_normal();
		texture_shader->set_uniform("mix_texture", 2);

		glActiveTexture(GL_TEXTURE3);
		glBindTexture(GL_TEXTURE_CUBE_MAP, corner_fire_shadow.odsm_tex_ID);
		texture_shader->set_uniform("shadow_cubemap", 3);

		glActiveTexture(GL_TEXTURE0);
		//identity_location = texture_shader->get_uniform_location("identity");
		//glUniform1i(identity_location, 2);
		texture_shader->set_uniform("identity",2);

		//matrices
		texture_shader->set_uniform("model_matrix", modelview_matrix);
		texture_shader->set_uniform("modelview_matrix", view_matrix * modelview_matrix);
		texture_shader->set_uniform("perspective_matrix", perspective_matrix);

		//lightview matrix
		load_identity(lightview_matrix);
		look_at(lightview_matrix, light_0, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
		//bias to scale shadowmap values correctly
		range_map = glm::translate(0.5f, 0.5f, 0.5f) * glm::scale(0.5f, 0.5f, 0.5f);
		//complete lightview aka texture matrix
		//bias * Perspective_light * Cam_light * Cam_inverse *	modelview
		lightview_matrix = range_map * perspective_matrix * lightview_matrix *
							modelview_inverted * view_matrix * modelview_matrix;
		texture_shader->set_uniform("lightview_matrix", lightview_matrix);
		break;

	default:
		break;

	}
	//render the node, unless its view frustrum culled
	if (!cull) {
		node.render();
	}

	//render children
	std::vector<scene_node*>::iterator it;
	for (it = node.children.begin(); it != node.children.end(); it++)
	{
		render_node(**it);
	}

	//restore previous transformation
	pop_modelview_matrix();
}

//recursively render the scenegraph starting at node
void demo_abgabe1::render_shadowmap(scene_node& node)
{
	//save current transformation
	push_modelview_matrix();

	//apply transformation of this node
	modelview_matrix *= node.transformation_matrix;

	//setup shader and render node
	//Shader* current_shader; //point to the shader of the node
	GLint modelview_location, perspective_location, light_location, tex_location;
	switch (node.shader_type())
	{
	case SH_NONE:
		break;
	//articles dont cast shadows for now!
	case SH_PARTICLE:
		break;
	case SH_SNOW:
		break;
	default:
		//// Set our matrix uniform
		shadowmap_gen->bind();

		//do preparations before rendering
		node.pre_render();

		modelview_location = shadowmap_gen->get_uniform_location("modelview_matrix");
		glUniformMatrix4fv( modelview_location, 1, GL_FALSE, glm::value_ptr(modelview_matrix));

		// Set our matrix uniform
		perspective_location = shadowmap_gen->get_uniform_location("perspective_matrix");
		glUniformMatrix4fv( perspective_location, 1, GL_FALSE, glm::value_ptr(perspective_shadow));
		node.render();

		break;

	}


	glUseProgram(0);

	//render children
	std::vector<scene_node*>::iterator it;
	for (it = node.children.begin(); it != node.children.end(); it++)
	{
		render_shadowmap(**it);
	}

	//restore previous transformation
	pop_modelview_matrix();
}

void demo_abgabe1::process_keys()
{
	//factor determines the speed of the camera movement
	float factor = static_cast<float>(delta()) * 5;

	//debug
	//cout << "factor " << factor << endl;

	update_light(factor);

	update_camera_movement(factor);
	view_frustrum.set_camera(cam_eye, cam_center, cam_up);

	//debug
	if (glfwGetKey(GLFW_KEY_ENTER))
	{
		std::cout << poly_offset_factor << ", " << poly_offset_units << std::endl;
	}
	//debug - get the camera coordinates
	if(glfwGetKey('C'))
	{
		//cout << "e " << cam_eye.x << "/" << cam_eye.y << "/" << cam_eye.z << endl;
		//cout << "c " << cam_center.x << "/" << cam_center.y << "/" << cam_center.z << endl;
		cout << "fe " << light_1.x << "/" << light_1.y << "/" << light_1.z << endl;
		//cout << "light " << light_0.x << "/" << light_0.y << "/" << light_0.z << endl;
	}

	//snow forcefield, wind sound
	;
	if (glfwGetKey('F'))
	{
		//check if the sound has to be played
		actualseconds = time(NULL);
		if(actualseconds > (lastseconds+2))
		{
			lastseconds = actualseconds;
			result = system->playSound(FMOD_CHANNEL_FREE, wind, false, &channelwind);
			errcheck(result);
			channelwind->setVolume(volume);
		}
		snow_node.set_forcefield(glm::vec3(1.0f, 0.0f, 0.0f));
	}
	else
	{
		snow_node.set_forcefield(glm::vec3(0.0f));
		//channelwind->setPaused(true);
	}

	//sound system
	if(glfwGetKey('B'))
	{
		//start the backgroundsound
		result = system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel);
		errcheck(result);
		//set the volume
		channel->setVolume(volume);
		isplaying = true;
	}
	if(glfwGetKey('P'))
	{
		//pause, unpause the sound
		if(paused)
		{
			paused = false;
			channel->setPaused(paused);
			channelfire->setPaused(paused);
			channelwind->setPaused(paused);
		}
		else
		{
			paused = true;
			channel->setPaused(paused);
			channelfire->setPaused(paused);
			channelwind->setPaused(paused);
		}
	}
	if(glfwGetKey('Z'))
	{
		//check if the sound has to be played
		actualseconds = time(NULL);
		if(actualseconds > (lastseconds+8))
		{
			//play the fire effekt
			result = system->playSound(FMOD_CHANNEL_FREE, fire, false, &channelfire);
			errcheck(result);
			//set the volume
			channelfire->setVolume(volume);
			lastseconds = actualseconds;
		}
		else
		{
			//do nothing
		}
	}
	if(glfwGetKey('I'))
	{
		//increase the sound volume
		volume = volume + 0.02f;
		channel->setVolume(volume);
		channelwind->setVolume(volume);
		channelfire->setVolume(volume);
	}
	if(glfwGetKey('U'))
	{
		//decrease the sound volume
		volume = volume - 0.02f;
		channel->setVolume(volume);
		channelwind->setVolume(volume);
		channelfire->setVolume(volume);
	}

}

void demo_abgabe1::update_light(float factor)
{
	float weight = 0.33;
	if (glfwGetKey(GLFW_KEY_RIGHT))
	{
		light_0.x += factor;
		//light_1.x += factor;
		//poly_offset_factor += factor * weight;
	}	
	if (glfwGetKey(GLFW_KEY_LEFT))
	{
		light_0.x -= factor;
		//light_1.x -= factor;
		//poly_offset_factor -= factor * weight;
	}
	if (glfwGetKey(GLFW_KEY_UP))
	{
		light_0.z -= factor;
		//light_1.z -= factor;
		//poly_offset_units += factor * weight;
	}	
	if (glfwGetKey(GLFW_KEY_DOWN))
	{
		light_0.z += factor;
		//light_1.z += factor;
		//poly_offset_units -= factor * weight;
	}
	if (glfwGetKey(GLFW_KEY_PAGEDOWN ))
	{
		//light_0.y -= factor;
		light_1.y -= factor;
		//poly_offset_units += factor * weight;
	}	
	if (glfwGetKey(GLFW_KEY_PAGEUP))
	{
		//light_0.y += factor;
		light_1.y += factor;
		//poly_offset_units -= factor * weight;
	}
	//shadow offset factor	
	if (glfwGetKey('I'))
	{
		light_1.z -= factor;
		//poly_offset_factor += factor * weight;
	}	
	if (glfwGetKey('J'))
	{
		light_1.x -= factor;
		//poly_offset_factor -= factor * weight;
	}
	if (glfwGetKey('K'))
	{
		light_1.z += factor;
		//poly_offset_units += factor * weight;
	}	
	if (glfwGetKey('L'))
	{
		light_1.x += factor;
		//poly_offset_units -= factor * weight;
	}
	light.transformation_matrix = glm::translate(light_0);
	corner_fire_node.transformation_matrix = glm::translate(light_1);
	corner_fire_shadow.light = light_1;
}


//taken from nopper tutorials
bool demo_abgabe1::init_skybox()
{
	//texture setup
	glGenTextures(1, &skybox_texture);
	get_errors();

	glBindTexture(GL_TEXTURE_CUBE_MAP, skybox_texture);

	tga::Texture tex;

	string filepaths[] = {
		"../../textures/alpine_right.tga",
		"../../textures/alpine_left.tga",
		"../../textures/alpine_top.tga",
		"../../textures/alpine_bottom.tga",
		"../../textures/alpine_front.tga",
		"../../textures/alpine_back.tga",
	};

	for(int side = 0; side < 6; side++) {

		if (tga::LoadTGA(&tex, filepaths[side].c_str()))
		{
			glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + side, 0, GL_SRGB8_ALPHA8 , tex.width, tex.height, 0, tex.type, GL_UNSIGNED_BYTE, tex.imageData);

		
			if (tex.imageData)					// If Texture Image Exists
			{
				free(tex.imageData);				// Free The Texture Image Memory
			}
		}
	}

	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,GL_LINEAR);	// Linear Filtering
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,GL_LINEAR);	// Linear Filtering
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	//vao/vbo setup
	// Points of a cube.
	GLfloat points[] = {  -0.5f, -0.5f, -0.5f, 1.0f,
						  -0.5f, -0.5f, +0.5f, 1.0f,
						  +0.5f, -0.5f, +0.5f, 1.0f,
						  +0.5f, -0.5f, -0.5f, 1.0f,
						  -0.5f, +0.5f, -0.5f, 1.0f,
						  -0.5f, +0.5f, +0.5f, 1.0f,
						  +0.5f, +0.5f, +0.5f, 1.0f,
						  +0.5f, +0.5f, -0.5f, 1.0f,
						  -0.5f, -0.5f, -0.5f, 1.0f,
						  -0.5f, +0.5f, -0.5f, 1.0f,
						  +0.5f, +0.5f, -0.5f, 1.0f,
						  +0.5f, -0.5f, -0.5f, 1.0f,
						  -0.5f, -0.5f, +0.5f, 1.0f,
						  -0.5f, +0.5f, +0.5f, 1.0f,
						  +0.5f, +0.5f, +0.5f, 1.0f,
						  +0.5f, -0.5f, +0.5f, 1.0f,
						  -0.5f, -0.5f, -0.5f, 1.0f,
						  -0.5f, -0.5f, +0.5f, 1.0f,
						  -0.5f, +0.5f, +0.5f, 1.0f,
						  -0.5f, +0.5f, -0.5f, 1.0f,
						  +0.5f, -0.5f, -0.5f, 1.0f,
						  +0.5f, -0.5f, +0.5f, 1.0f,
						  +0.5f, +0.5f, +0.5f, 1.0f,
						  +0.5f, +0.5f, -0.5f, 1.0f
						};

	// Normals of a cube.
	GLfloat normals[] = { +0.0f, -1.0f, +0.0f,
						  +0.0f, -1.0f, +0.0f,
						  +0.0f, -1.0f, +0.0f,
						  +0.0f, -1.0f, +0.0f,
						  +0.0f, +1.0f, +0.0f,
						  +0.0f, +1.0f, +0.0f,
						  +0.0f, +1.0f, +0.0f,
						  +0.0f, +1.0f, +0.0f,
						  +0.0f, +0.0f, -1.0f,
						  +0.0f, +0.0f, -1.0f,
						  +0.0f, +0.0f, -1.0f,
						  +0.0f, +0.0f, -1.0f,
						  +0.0f, +0.0f, +1.0f,
						  +0.0f, +0.0f, +1.0f,
						  +0.0f, +0.0f, +1.0f,
						  +0.0f, +0.0f, +1.0f,
						  -1.0f, +0.0f, +0.0f,
						  -1.0f, +0.0f, +0.0f,
						  -1.0f, +0.0f, +0.0f,
						  -1.0f, +0.0f, +0.0f,
						  +1.0f, +0.0f, +0.0f,
						  +1.0f, +0.0f, +0.0f,
						  +1.0f, +0.0f, +0.0f,
						  +1.0f, +0.0f, +0.0f
						};

	// The associated indices.
	GLuint indices[] = {		0, 2, 1,
								0, 3, 2, 
								4, 5, 6,
								4, 6, 7,
								8, 9, 10,
								8, 10, 11, 
								12, 15, 14,
								12, 14, 13, 
								16, 17, 18,
								16, 18, 19, 
								20, 23, 22,
								20, 22, 21
						};

	glGenVertexArrays(1, &skybox_vao);
	glBindVertexArray(skybox_vao);

	// Allocate and assign two Vertex Buffer Objects (VBOs) to our handle
	glGenBuffers(3, skybox_vbo);

	// Bind our first VBO as being the active buffer and storing vertex attributes (coordinates)	
	glBindBuffer(GL_ARRAY_BUFFER, skybox_vbo[0]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
	get_errors();

	GLint vertex_location = texture_cm->get_attrib_location("in_position");
	std::cout << "vertex_location of skybox: " << vertex_location << std::endl;
	glEnableVertexAttribArray(vertex_location);
	glVertexAttribPointer(	vertex_location, 4, GL_FLOAT, 
		GL_FALSE, 0, NULL);
	get_errors();

	// Repeat for second VBO storing vertex colors
	//glBindBuffer(GL_ARRAY_BUFFER, skybox_vbo[1]);
	//glBufferData(GL_ARRAY_BUFFER, sizeof(normals), normals, GL_STATIC_DRAW);

	//GLint normal_location = texture_cm->get_attrib_location("in_normal");
	//std::cout << "normal_location of skybox: " << normal_location << std::endl;
	//glEnableVertexAttribArray(normal_location);
	//glVertexAttribPointer(	normal_location, 3, GL_FLOAT, 
	//	GL_FALSE, 0, NULL);
	//get_errors();


	//vbo for texcoords
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, skybox_vbo[2]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

	return true;
}

void demo_abgabe1::draw_skybox() {
		texture_cm->bind();

		glBindTexture(GL_TEXTURE_CUBE_MAP, skybox_texture);

		
		GLint tex_location = texture_cm->get_uniform_location("in_texture");
		glUniform1i(tex_location, 0);
		
		GLint modelview_location = texture_cm->get_uniform_location("modelview_matrix");
		glUniformMatrix4fv( modelview_location, 1, GL_FALSE, glm::value_ptr(view_matrix * modelview_matrix));
		
		// Set our matrix uniform
		GLint perspective_location = texture_cm->get_uniform_location("perspective_matrix");
		glUniformMatrix4fv( perspective_location, 1, GL_FALSE, glm::value_ptr(perspective_matrix));

		GLint camera_pos = texture_cm->get_uniform_location("camera_position");
		
		glUniform3f(camera_pos, cam_eye[0], cam_eye[1], cam_eye[2]);
		
		glBindVertexArray(skybox_vao);
		// Draw triangle in VAO
		glDrawElements(GL_TRIANGLES, 6*2*3, GL_UNSIGNED_INT, 0);
		
		get_errors();
		glBindVertexArray(0);
		
		get_errors();
		// Unbind VBOs
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

		texture_cm->unbind();
}

void demo_abgabe1::cleanup() {
	glDeleteVertexArrays(1, &skybox_vao);

	glDeleteBuffers(3, skybox_vbo);

	glDeleteVertexArrays(1, &loadscreen_vao);

	glDeleteBuffers(3, loadscreen_vbo);

	//cleanup of the soundsystem
	result = sound->release();
	errcheck(result);
	result = system->close();
	errcheck(result);
	result = system->release();
	errcheck(result);
}

bool demo_abgabe1::load_terrain()
{
	if (object_model.loadMod("../../models/Terrain_3.obj"))
	{
		cout << "object model loading failed\n";
		return false;
	}	

	
	object_model.buildVBOs(*texture_shader);

	
	model = object_node(glm::scale(35.0f, 35.0f, 35.0f), object_model.getVAO(), object_model.getMesh()->getIndicesCount() , SH_TERRAIN);
	model.set_texture_id(texture[TERRAIN]);
	model.set_texture_norm_id(texture[TERRAIN2]);

	root.add_child(model);

	return true;
}
bool demo_abgabe1::load_trees()
{
	if (tree_1_model.loadMod("../../models/Tree1.obj"))
	{
		cout << "tree1 model loading failed\n";
		return false;
	}	
	if (tree_2_model.loadMod("../../models/Tree2.obj"))
	{
		cout << "tree2 model loading failed\n";
		return false;
	}	
	if (tree_3_model.loadMod("../../models/Tree3.obj"))
	{
		cout << "tree3 model loading failed\n";
		return false;
	}	
	if (tree_4_model.loadMod("../../models/Tree4.obj"))
	{
		cout << "tree4 model loading failed\n";
		return false;
	}
	
	tree_1_model.buildVBOs(*texture_normalmap);
	tree_2_model.buildVBOs(*texture_normalmap);
	tree_3_model.buildVBOs(*texture_normalmap);
	tree_4_model.buildVBOs(*texture_normalmap);

	
	tree1 = object_node(glm::translate(10.0f, 0.0f, 0.0f)*glm::scale(0.4f, 0.4f, 0.4f), tree_1_model, SH_NORMTEXTURE);
	tree1.set_texture_id(texture[TREE1]);
	tree1.set_texture_norm_id(texture[NTREE1]);
	tree2 = object_node(glm::translate(-17.0f, 4.0f, -2.0f)*glm::scale(3.0f, 3.0f, 3.0f), tree_2_model, SH_NORMTEXTURE);
	tree2.set_texture_id(texture[TREE2]);
	tree2.set_texture_norm_id(texture[NTREE2]);
	tree3 = object_node(glm::translate(0.0f, 2.0f, -17.0f) * glm::rotate(90.0f, 0.0f, 1.0f, 0.0f) *glm::scale(2.0f, 2.0f, 2.0f), tree_3_model, SH_NORMTEXTURE);
	tree3.set_texture_id(texture[TREE3]);
	tree3.set_texture_norm_id(texture[NTREE3]);
	//tree4 = object_node(glm::translate(0.0f, -6.0f, 0.0f) * glm::scale(0.8f, 0.8f, 0.8f), tree_4_model, SH_NORMTEXTURE);
	tree4 = object_node(glm::translate(10.0f, -3.5f, -18.0f) * glm::scale(4.5f, 4.5f, 4.5f), tree_4_model, SH_NORMTEXTURE);
	tree4.set_texture_id(texture[TREE4]);
	tree4.set_texture_norm_id(texture[NTREE4]);
	tree5 = object_node(glm::translate(-20.0f, 4.0f, -7.0f)*glm::scale(3.0f, 3.0f, 3.0f) * glm::rotate(30.0f, 0.0f, 1.0f, 0.0f), tree_2_model, SH_NORMTEXTURE);
	tree5.set_texture_id(texture[TREE2]);
	tree5.set_texture_norm_id(texture[NTREE2]);
	tree6 = object_node(glm::translate(-23.0f, 4.0f, -3.0f)*glm::scale(3.0f, 2.0f, 3.0f) * glm::rotate(30.0f, 0.0f, 1.0f, 0.0f), tree_2_model, SH_NORMTEXTURE);
	tree6.set_texture_id(texture[TREE2]);
	tree6.set_texture_norm_id(texture[NTREE2]);
	tree7 = object_node(glm::translate(-20.0f, 4.0f, 2.0f)*glm::scale(2.0f, 2.0f, 2.0f) * glm::rotate(180.0f, 0.0f, 1.0f, 0.0f), tree_2_model, SH_NORMTEXTURE);
	tree7.set_texture_id(texture[TREE2]);
	tree7.set_texture_norm_id(texture[NTREE2]);
	tree8 = object_node(glm::translate(20.0f, 0.0f, -17.0f) * glm::rotate(-90.0f, 0.0f, 1.0f, 0.0f) *glm::scale(1.6f, 1.6f, 1.6f), tree_3_model, SH_NORMTEXTURE);
	tree8.set_texture_id(texture[TREE3]);
	tree8.set_texture_norm_id(texture[NTREE3]);
	//tree9 = object_node(glm::translate(20.0f, -3.5f, 25.0f) * glm::scale(0.4f, 0.4f, 0.4f) , tree_4_model, SH_NORMTEXTURE);
	tree9 = object_node(glm::translate(28.0f, -1.8f, 20.0f) * glm::rotate(280.0f, 0.0f, 1.0f, 0.0f) * glm::scale(4.1f, 4.1f, 4.1f), tree_4_model, SH_NORMTEXTURE);
	tree9.set_texture_id(texture[TREE4]);
	tree9.set_texture_norm_id(texture[NTREE4]);
	//tree10 = object_node(glm::translate(16.0f, -4.0f, 35.0f) * glm::scale(0.4f, 0.4f, 0.4f), tree_4_model, SH_NORMTEXTURE);
	tree10 = object_node(glm::translate(27.5f, -1.5f, 27.5f) * glm::rotate(10.0f, 0.0f, 1.0f, 0.0f) * glm::scale(4.8f, 4.8f, 4.8f), tree_4_model, SH_NORMTEXTURE);
	tree10.set_texture_id(texture[TREE4]);
	tree10.set_texture_norm_id(texture[NTREE4]);
	//tree11 = object_node(glm::translate(20.0f, -3.7f, 31.0f) * glm::scale(0.4f, 0.6f, 0.4f), tree_4_model, SH_NORMTEXTURE);
	tree11 = object_node(glm::translate(21.0f, -3.5f, 28.0f) * glm::rotate(180.0f, 0.0f,1.0f, 0.0f) *glm::scale(4.4f, 4.4f, 4.4f), tree_4_model, SH_NORMTEXTURE);
	tree11.set_texture_id(texture[TREE4]);
	tree11.set_texture_norm_id(texture[NTREE4]);
	tree12 = object_node(glm::translate(-18.0f, 5.0f, -4.0f)*glm::scale(1.7f, 1.7f, 1.7f) * glm::rotate(130.0f, 0.0f, 1.0f, 0.0f), tree_2_model, SH_NORMTEXTURE);
	tree12.set_texture_id(texture[TREE2]);	
	tree12.set_texture_norm_id(texture[NTREE2]);
	tree13 = object_node(glm::translate(-19.0f, 4.0f, -8.0f)*glm::scale(1.9f, 1.9f, 1.9f) * glm::rotate(120.0f, 0.0f, 1.0f, 0.0f), tree_2_model, SH_NORMTEXTURE);
	tree13.set_texture_id(texture[TREE2]);
	tree13.set_texture_norm_id(texture[NTREE2]);
	tree14 = object_node(glm::translate(20.0f, -2.2f, 4.0f)*glm::scale(0.6f, 0.6f, 0.6f)*glm::rotate(100.0f, 0.0f, 1.0f, 0.0f), tree_1_model, SH_NORMTEXTURE);
	tree14.set_texture_id(texture[TREE1]);
	tree14.set_texture_norm_id(texture[NTREE1]);
	tree15 = object_node(glm::translate(26.5f, -1.0f, -5.0f)*glm::scale(0.44f, 0.44f, 0.44f)*glm::rotate(140.0f, 0.0f, 1.0f, 0.0f), tree_1_model, SH_NORMTEXTURE);
	tree15.set_texture_id(texture[TREE1]);
	tree15.set_texture_norm_id(texture[NTREE1]);
	tree16 = object_node(glm::translate(25.0f, -2.0f, -1.0f)*glm::scale(0.35f, 0.35f, 0.35f)*glm::rotate(200.0f, 0.0f, 1.0f, 0.0f), tree_1_model, SH_NORMTEXTURE);
	tree16.set_texture_id(texture[TREE1]);
	tree16.set_texture_norm_id(texture[NTREE1]);
	tree17 = object_node(glm::translate(25.5f, 2.5f, -22.0f) * glm::rotate(-30.0f, 0.0f, 1.0f, 0.0f) *glm::scale(1.3f, 1.3f, 1.3f), tree_3_model, SH_NORMTEXTURE);
	tree17.set_texture_id(texture[TREE3]);
	tree17.set_texture_norm_id(texture[NTREE3]);
	tree18 = object_node(glm::translate(-4.0f, 0.5f, -24.0f) * glm::rotate(1.0f, 0.0f, 1.0f, 0.0f) *glm::scale(1.4f, 1.4f, 1.4f), tree_3_model, SH_NORMTEXTURE);
	tree18.set_texture_id(texture[TREE3]);
	tree18.set_texture_norm_id(texture[NTREE3]);
	tree19 = object_node(glm::translate(-10.0f, 1.5f, -19.0f) * glm::rotate(180.0f, 0.0f, 1.0f, 0.0f) *glm::scale(1.7f, 1.7f, 1.7f), tree_3_model, SH_NORMTEXTURE);
	tree19.set_texture_id(texture[TREE3]);
	tree19.set_texture_norm_id(texture[NTREE3]);
	//tree20 = object_node(glm::translate(9.0f, -6.4f, 30.0f) * glm::scale(0.4f, 0.4f, 0.4f), tree_4_model, SH_NORMTEXTURE);
	tree20 = object_node(glm::translate(22.0f, -5.0f, 19.0f) * glm::rotate(180.0f, 0.0f, 1.0f, 0.0f) *glm::scale(4.0f, 4.0f, 4.0f), tree_4_model, SH_NORMTEXTURE);
	tree20.set_texture_id(texture[TREE4]);
	tree20.set_texture_norm_id(texture[NTREE4]);
	//tree21 = object_node(glm::translate(32.0f, -6.7f, 10.0f) * glm::rotate(180.0f, 0.0f, 1.0f, 0.0f) * glm::scale(0.5f, 0.5f, 0.5f), tree_4_model, SH_NORMTEXTURE);
	//tree21.set_texture_id(texture[TREE4]);
	//tree21.set_texture_norm_id(texture[NTREE4]);

	//test_node = object_node(glm::translate(20.0f, -2.2f, 4.0f)*glm::scale(0.6f, 0.6f, 0.6f)*glm::rotate(100.0f, 0.0f, 1.0f, 0.0f), tree_2_model, SH_NORMTEXTURE);
	//test_node.set_texture_id(texture[TREE2]);
	//test_node.set_texture_norm_id(texture[NTREE2]);
	//object_root.add_child(test_node);

	//hacked bounding boxes for slim trees
	//tree2.set_dimension();

	object_root.add_child(tree1);
	object_root.add_child(tree2);
	object_root.add_child(tree3);
	object_root.add_child(tree4);
	object_root.add_child(tree5);
	object_root.add_child(tree6);
	object_root.add_child(tree7);
	object_root.add_child(tree8);
	object_root.add_child(tree9);
	object_root.add_child(tree10);
	object_root.add_child(tree11);
	object_root.add_child(tree12);
	object_root.add_child(tree13);
	object_root.add_child(tree14);
	object_root.add_child(tree15);
	object_root.add_child(tree16);
	object_root.add_child(tree17);
	object_root.add_child(tree18);
	object_root.add_child(tree19);
	object_root.add_child(tree20);
	//object_root.add_child(tree21);

	return true;
}
bool demo_abgabe1::load_stones()
{
	
	if (altar_model.loadMod("../../models/altar.obj"))
	{
		cout << "altar model loading failed\n";
		return false;
	}
	if (celtic_cross_model.loadMod("../../models/celtic_cross_Tombstone.obj"))
	{
		cout << "celtic cross model loading failed\n";
		return false;
	}
	if (fence_post_model.loadMod("../../models/fence_post.obj"))
	{
		cout << "fence post model loading failed\n";
		return false;
	}

	altar_model.buildVBOs(*texture_normalmap);
	celtic_cross_model.buildVBOs(*texture_normalmap);
	fence_post_model.buildVBOsNorm(*texture_normalmap);

	altar = object_node(glm::translate(23.0f, -3.6f, 23.0f) * glm::scale(0.35f, 0.35f, 0.35f), altar_model, SH_NORMTEXTURE);
	altar.set_texture_id(texture[ALTAR]);
	altar.set_texture_norm_id(texture[NALTAR]);
	altar2 = object_node(glm::translate(-28.0f, -0.3f, 26.0f) * glm::scale(0.36f, 0.36f, 0.36f), altar_model, SH_NORMTEXTURE);
	altar2.set_texture_id(texture[ALTAR]);
	altar2.set_texture_norm_id(texture[NALTAR]);
	celtic_cross = object_node(glm::translate(26.0f, -1.5f, 23.0f) * glm::scale(0.7f, 0.7f, 0.7f) * glm::rotate(30.0f, 1.0f, 0.0f, 0.0f), celtic_cross_model, SH_NORMTEXTURE);
	celtic_cross.set_texture_id(texture[CELTICCROSS]);
	celtic_cross.set_texture_norm_id(texture[NCELTICCROSS]);
	celtic_cross2 = object_node(glm::translate(20.0f, -2.3f, 26.0f) * glm::scale(0.7f, 0.7f, 0.7f) * glm::rotate(-30.0f, 1.0f, 0.0f, 0.0f), celtic_cross_model, SH_NORMTEXTURE);
	celtic_cross2.set_texture_id(texture[CELTICCROSS]);
	celtic_cross2.set_texture_norm_id(texture[NCELTICCROSS]);
	celtic_cross3 = object_node(glm::translate(-28.0f, -1.0f, 23.0f) * glm::scale(0.7f, 0.7f, 0.7f) * glm::rotate(-20.0f, 1.0f, 0.0f, 0.0f), celtic_cross_model, SH_NORMTEXTURE);
	celtic_cross3.set_texture_id(texture[CELTICCROSS]);
	celtic_cross3.set_texture_norm_id(texture[NCELTICCROSS]);
	celtic_cross4 = object_node(glm::translate(-30.0f, 0.0f, 23.0f) * glm::scale(0.7f, 0.7f, 0.7f) * glm::rotate(10.0f, 1.0f, 0.0f, 0.0f), celtic_cross_model, SH_NORMTEXTURE);
	celtic_cross4.set_texture_id(texture[CELTICCROSS]);
	celtic_cross4.set_texture_norm_id(texture[NCELTICCROSS]);
	celtic_cross5 = object_node(glm::translate(-30.0f, 1.5f, 28.0f) * glm::scale(0.7f, 0.7f, 0.7f) * glm::rotate(-10.0f, 1.0f, 0.0f, 0.0f) * glm::rotate(45.0f, 0.0f, 1.0f, 0.0f), celtic_cross_model, SH_NORMTEXTURE);
	celtic_cross5.set_texture_id(texture[CELTICCROSS]);
	celtic_cross5.set_texture_norm_id(texture[NCELTICCROSS]);
	celtic_cross6 = object_node(glm::translate(-27.0f, 0.9f, 29.5f) * glm::scale(0.7f, 0.7f, 0.7f) * glm::rotate(10.0f, 1.0f, 0.0f, 0.0f) * glm::rotate(45.0f, 0.0f, 1.0f, 0.0f), celtic_cross_model, SH_NORMTEXTURE);
	celtic_cross6.set_texture_id(texture[CELTICCROSS]);
	celtic_cross6.set_texture_norm_id(texture[NCELTICCROSS]);
	
	fence_post = object_node(glm::translate(5.0f, 3.5f, 2.0f) * glm::scale(0.3f, 0.3f, 0.3f) * glm::rotate(-35.0f, 0.0f, 0.0f, 1.0f), fence_post_model, SH_NORMTEXTURE);
	fence_post.set_texture_id(texture[FENCEPOST]);
	fence_post.set_texture_norm_id(texture[NFENCEPOST]);
	fence_post2 = object_node(glm::translate(5.0f, 4.5f, -3.0f) * glm::scale(0.3f, 0.3f, 0.3f) * glm::rotate(-20.0f, 0.0f, 0.0f, 1.0f), fence_post_model, SH_NORMTEXTURE);
	fence_post2.set_texture_id(texture[FENCEPOST]);
	fence_post2.set_texture_norm_id(texture[NFENCEPOST]);

	object_root.add_child(altar);
	object_root.add_child(celtic_cross);
	object_root.add_child(celtic_cross2);
	object_root.add_child(altar2);
	object_root.add_child(celtic_cross3);
	object_root.add_child(celtic_cross4);
	object_root.add_child(celtic_cross5);
	object_root.add_child(celtic_cross6);
	object_root.add_child(fence_post);
	object_root.add_child(fence_post2);

	return true;
}
bool demo_abgabe1::load_ruin()
{
	
	if (fireplace_model.loadMod("../../models/fireplace.obj"))
	{
		cout << "fireplace model loading failed\n";
		return false;
	}
	if (watchtower_model.loadMod("../../models/watchtower.obj"))
	{
		cout << "watchtower model loading failed\n";
		return false;
	}
	if (back_model.loadMod("../../models/Back.obj"))
	{
		cout << "back model loading failed\n";
		return false;
	}
	if (door_model.loadMod("../../models/Door.obj"))
	{
		cout << "door model loading failed\n";
		return false;
	}
	if (window_model.loadMod("../../models/Windows.obj"))
	{
		cout << "windows model loading failed\n";
		return false;
	}

	
	fireplace_model.buildVBOs(*texture_normalmap);
	watchtower_model.buildVBOs(*texture_normalmap);
	window_model.buildVBOs(*texture_normalmap);
	back_model.buildVBOs(*texture_normalmap);
	door_model.buildVBOs(*texture_normalmap);

	
	windows = object_node(glm::translate(-2.0f, 6.2f, -5.0f) * glm::scale(1.1f, 1.1f, 1.1f), window_model, SH_NORMTEXTURE);
	windows.set_texture_id(texture[RUIN]);
	windows.set_texture_norm_id(texture[NRUIN]);
	back = object_node(glm::translate(-7.65f, 6.0f, 1.0f) * glm::scale(1.2f, 1.2f, 1.2f), back_model, SH_NORMTEXTURE);
	back.set_texture_id(texture[RUIN]);
	back.set_texture_norm_id(texture[NRUIN]);
	//back.set_dimension(glm::vec3(100.0f, 100.0f, 100.0f)); //fix view frustrum culling bug
	door = object_node(glm::translate(4.2f, 5.6f, -1.5f) * glm::scale(3.2f, 2.1f, 3.2f), door_model, SH_NORMTEXTURE);
	door.set_texture_id(texture[RUIN]);
	door.set_texture_norm_id(texture[NRUIN]);

	
	fireplace_position = glm::translate(-7.28f, 5.83f, -2.5f);
	fireplace = object_node(glm::rotate(90.0f, 0.0f, 1.0f, 0.0f) * glm::rotate(9.0f, 0.0f, 0.0f, 1.0f) * glm::scale(0.12f, 0.12f, 0.12f),fireplace_model, SH_NORMTEXTURE);
	fireplace.set_texture_id(texture[FIREPLACE]);
	fireplace.set_texture_norm_id(texture[NFIREPLACE]);
	fireplace2 = object_node(glm::scale(-1.0f, 1.0f, 1.0f), fireplace_model, SH_NORMTEXTURE);
	fireplace2.set_texture_id(texture[FIREPLACE]);//1.0f, -1.0f, -1.0f
	fireplace2.set_texture_norm_id(texture[NFIREPLACE]);
	watchtower = object_node(glm::translate(-11.0f, 5.1f, -3.5f)*glm::rotate(180.0f, 0.0f, 1.0f, 0.0f)*glm::scale(4.0f, 4.0f, 4.0f), watchtower_model, SH_NORMTEXTURE);
	watchtower.set_texture_id(texture[WATCHTOWER]);
	watchtower.set_texture_norm_id(texture[NWATCHTOWER]);
	watchtower.set_dimension(glm::vec3(100.0f, 100.0f, 100.0f));

	object_root.add_child(back);
	object_root.add_child(windows);
	object_root.add_child(door);
	object_root.add_child(fireplace_position);
	object_root.add_child(watchtower);
	fireplace_position.add_child(fireplace);
	fireplace.add_child(fireplace2);

	return true;
}

//init loadscreen quad
void demo_abgabe1::init_loadscreen(const Shader &shader, GLuint *vbo_id, GLuint *vao_id)
{
			// Our triangle data
	GLfloat bg_positions[] = {	 
		-1.0f, -1.0f, 0.0f, 1.0f,
		1.0f, -1.0f, 0.0f, 1.0f,
		1.0f, 1.0f, 0.0f, 1.0f,
		-1.0f, 1.0f, 0.0f, 1.0f	};

	GLfloat bg_normals[] = {	
		0, 0, 1,
		0, 0, 1,
		0, 0, 1,
		0, 0, 1	};

	GLfloat bg_texcoords[] = {	 
		0.0f, 0.0f,
		1.0f, 0.0f,
		1.0f, 1.0f,
		0.0f, 1.0f	};

	GLuint bg_indices[] = {
		0,1,2,3,0,2
	};


	// Allocate and assign a Vertex Array Object to our handle

	glGenVertexArrays(1, vao_id);

	// Bind our Vertex Array Object as the current used object

	glBindVertexArray(*vao_id);

	// Allocate and assign two Vertex Buffer Objects (VBOs) to our handle
	glGenBuffers(4, vbo_id);

	// Bind our first VBO as being the active buffer and storing vertex attributes (coordinates)	
	glBindBuffer(GL_ARRAY_BUFFER, vbo_id[0]);
	glBufferData(GL_ARRAY_BUFFER, 4 * 4 * sizeof(GLfloat), bg_positions, GL_STATIC_DRAW);

	GLint vertex_location = shader.get_attrib_location("in_position");
	std::cout << "vertex_location of bg: " << vertex_location << std::endl;
	glEnableVertexAttribArray(vertex_location);
	glVertexAttribPointer(	vertex_location, 4, GL_FLOAT, 
		GL_FALSE, 0, NULL);
	get_errors();

	// Repeat for second VBO storing vertex colors
	glBindBuffer(GL_ARRAY_BUFFER, vbo_id[1]);
	glBufferData(GL_ARRAY_BUFFER, 4 * 3 * sizeof(GLfloat), bg_normals, GL_STATIC_DRAW);

	GLint normal_location = shader.get_attrib_location("in_normal");
	std::cout << "normal_location of bg: " << normal_location << std::endl;
	glEnableVertexAttribArray(normal_location);
	glVertexAttribPointer(	normal_location, 3, GL_FLOAT, 
		GL_FALSE, 0, NULL);
	get_errors();

	//vbo for texcoords
	glBindBuffer(GL_ARRAY_BUFFER, vbo_id[2]);
	glBufferData(GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), bg_texcoords, GL_STATIC_DRAW);
	
	GLint texcoord_location = shader.get_attrib_location("in_texcoord");
	glEnableVertexAttribArray(texcoord_location);
	glVertexAttribPointer(	texcoord_location, 2, GL_FLOAT, GL_FALSE, 0, NULL);

	//vbo for texcoords
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_id[3]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(bg_indices), bg_indices, GL_STATIC_DRAW);

	//glBindTexture(GL_TEXTURE_2D, texture[0]);
	get_errors();

	// unbind VAO
	glBindVertexArray(0);

	// Unbind VBOs
	glBindBuffer(GL_ARRAY_BUFFER, 0);


}

//draw the loadscreen (its just a quad)
void demo_abgabe1::draw_loadscreen(scene_node& node, GLuint texture_id )
{
		
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glClearColor(0.0, 0.0, 0.0, 0.0);

		texture_simple->bind();
		
		//do preparations before rendering
		glBindTexture(GL_TEXTURE_2D, texture_id);

		//set light
		GLint light_location = texture_simple->get_uniform_location("light_position");
		glUniform3f(light_location, 0.0, 0.0, 1.0);
		
		GLint tex_location = texture_simple->get_uniform_location("in_texture");
		glUniform1i(tex_location, 0);

		GLint modelview_location = texture_simple->get_uniform_location("modelview_matrix");
		glUniformMatrix4fv( modelview_location, 1, GL_FALSE, glm::value_ptr(glm::translate(0.0f, 0.0f, -3.0f)));

		// Set our matrix uniform
		GLint perspective_location = texture_simple->get_uniform_location("perspective_matrix");
		glUniformMatrix4fv( perspective_location, 1, GL_FALSE, glm::value_ptr(perspective_matrix));

		node.render();
		
		texture_simple->unbind();

		glfwSwapBuffers();
}


//error checking method of the sound system
void demo_abgabe1::errcheck(FMOD_RESULT result)
{
	if(result != FMOD_OK)
	{
		cout << "SOUND ERROR! "<< FMOD_ErrorString(result) << endl;
	}


}//end of errcheck(FMOD_RESULT result)


//init the soundsystem
bool demo_abgabe1::init_soundsystem()
{
	paused = false;
	isplaying = false;

	//create a system object and initialize it
	result = FMOD::System_Create(&system);
	errcheck(result);

	result = system->getVersion(&version);
	errcheck(result);

	if(version < FMOD_VERSION)
	{
		cout << "SOUND ERROR: Usage of an older FMOD-Version. We need " << FMOD_VERSION << endl;
		return false;
	}

	result = system->init(3, FMOD_INIT_NORMAL, 0);
	errcheck(result);

	result = system->createStream("../../media/medley_original.mp3", FMOD_HARDWARE | FMOD_LOOP_NORMAL | FMOD_2D, 0, &sound);
	errcheck(result);
	result = system->createStream("../../media/wind.mp3", FMOD_HARDWARE | FMOD_LOOP_OFF | FMOD_2D, 0, &wind);
	errcheck(result);
	result = system->createStream("../../media/fire.mp3", FMOD_HARDWARE | FMOD_LOOP_OFF | FMOD_2D, 0, &fire);
	errcheck(result);

	//now play the sound
	//result = system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel);
	//errcheck(result);
	//set the volume
	//channel->setVolume(volume);

	//TODO teile von main-loop benötigt?

	return true;
} //end of init_soundsystem()

bool demo_abgabe1::in_frustrum(const scene_node& node) const
{
	//center point of our object
	glm::vec4 ref_point = modelview_matrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
	//bounding sphere radius
	glm::vec4 model_dimension = modelview_matrix * glm::vec4(node.get_dimension(), 1.0f);
	GLfloat radius = glm::length( model_dimension - ref_point);

	return !view_frustrum.sphere_in_frustrum(glm::vec3(ref_point.x, ref_point.y, ref_point.z), radius / 2.0f);

}


void demo_abgabe1::init_postprocess()
{
	gauss_vertical->bind();

	// Our triangle data
	GLfloat positions[] = {	 
		-1.0f, -1.0f, 0.0f, 1.0f,
		1.0f, -1.0f, 0.0f, 1.0f,
		1.0f, 1.0f, 0.0f, 1.0f,
		-1.0f, 1.0f, 0.0f, 1.0f	};

	GLfloat texcoords[] = {	 
		0.0f, 0.0f,
		1.0f, 0.0f,
		1.0f, 1.0f,
		0.0f, 1.0f	};

	//build vao, vbo
	glGenVertexArrays(1, &pp_vao_id);
	glBindVertexArray(pp_vao_id);

	glGenBuffers(2, pp_vbo);
	glBindBuffer(GL_ARRAY_BUFFER, pp_vbo[0]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
	
	gauss_vertical->set_attrib_location("in_position", 4);
	gauss_horizontal->bind();
	gauss_horizontal->set_attrib_location("in_position", 4);
	pp_dof->bind();
	pp_dof->set_attrib_location("in_position", 4);

	glBindBuffer(GL_ARRAY_BUFFER, pp_vbo[1]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(texcoords), texcoords, GL_STATIC_DRAW);
	
	pp_dof->set_attrib_location("in_texcoord", 2);
	gauss_horizontal->bind();
	gauss_horizontal->set_attrib_location("in_texcoord", 2);
	gauss_vertical->bind();
	gauss_vertical->set_attrib_location("in_texcoord", 2);

	//glBindTexture(GL_TEXTURE_2D, texture[0]);

	//set some uniforms
	gauss_vertical->set_uniform("height", static_cast<GLfloat>(height));
	gauss_vertical->set_uniform("border", static_cast<GLfloat>(pp_border));
	glm::mat4 ortho_matrix = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f);
	gauss_vertical->set_uniform("perspective_matrix",  ortho_matrix);
	gauss_vertical->set_uniform("in_texture", 0);

	gauss_horizontal->bind();
	gauss_horizontal->set_uniform("width", static_cast<GLfloat>(width));
	gauss_horizontal->set_uniform("border", static_cast<GLfloat>(pp_border));
	gauss_horizontal->set_uniform("perspective_matrix",  ortho_matrix);
	gauss_horizontal->set_uniform("in_texture", 0);

	pp_dof->bind();
	pp_dof->set_uniform("perspective_matrix",  ortho_matrix);
	
	pp_dof->set_uniform("in_texture", 0);
	pp_dof->set_uniform("in_depth_texture", 1);
	pp_dof->set_uniform("in_blurred_texture", 2);
	pp_dof->set_uniform("far_blur", 0.35f);
	pp_dof->set_uniform("near_blur", 0.03f);

	pp_dof->unbind();

	get_errors();

	glBindVertexArray(0);
}


void demo_abgabe1::draw_postprocess(const Shader& shader, GLuint source_tex_id, GLuint z_tex_id)
{
		shader.bind();
		//shader.set_uniform("in_depth_texture", 1);

		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, source_tex_id);
		//shader.set_uniform("in_texture", 0);

		shader.set_uniform("border", pp_border);
	
		glBindVertexArray(pp_vao_id);

		//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
		glDrawArrays(GL_TRIANGLE_FAN , 0, 4);

		glBindVertexArray(0);

		// Unbind VBOs
		glBindBuffer(GL_ARRAY_BUFFER, 0);

		shader.unbind();
}

//recursively render the scenegraph starting at node
void demo_abgabe1::render_depthmap(scene_node& node)
{
	//save current transformation
	push_modelview_matrix();

	//apply transformation of this node
	modelview_matrix *= node.transformation_matrix;

	//setup shader and render node
	//Shader* current_shader; //point to the shader of the node
	switch (node.shader_type())
	{
	case SH_NONE:
		break;
	case SH_PARTICLE:
		break;
	case SH_SNOW:
		//ugly hack
		modelview_matrix = view_matrix * modelview_matrix;

		//node.pre_render();
		
		//glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo);

		snow_shadow->bind();

		glBindVertexArray(snow_node.get_vao_id());

		snow_shadow->set_uniform("near_far", glm::vec2(near_far.x, 200.0f));
		snow_shadow->set_uniform("modelview_matrix", modelview_matrix);
		snow_shadow->set_uniform("perspective_matrix", perspective_matrix);

		glBindTexture(GL_TEXTURE_2D, snow_node.get_lookup_texture());

		snow_shadow->set_uniform("readTexture", 0);

		glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);

		glDrawArrays(GL_POINTS, 0, 64 * 64);

		glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);

		break;
	default:
		// Set our matrix uniform
		depthmap_gen->bind();

		//do preparations before rendering
		node.pre_render();

		depthmap_gen->set_uniform("near_far", glm::vec2(near_far.x, 100.0f));
		depthmap_gen->set_uniform("modelview_matrix", view_matrix * modelview_matrix);

		// Set our matrix uniform
		depthmap_gen->set_uniform("perspective_matrix", perspective_matrix);
		node.render();
		break;

	}


	glUseProgram(0);

	//render children
	std::vector<scene_node*>::iterator it;
	for (it = node.children.begin(); it != node.children.end(); it++)
	{
		render_depthmap(**it);
	}

	//restore previous transformation
	pop_modelview_matrix();
}

bool demo_abgabe1::init_fbo()
{
	glGenTextures(3, depth_tex_id);
	glGenTextures(3, scene_tex_id);
	glGenFramebuffers(3, fbo_id);
	int widths[] = {width, width / 2, width / 2};
	int heights[] = {height, height / 2, height / 2};

	for (int i=0; i < 3; i++) {

	//depth texture
	glBindTexture(GL_TEXTURE_2D, depth_tex_id[i]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,
		GL_COMPARE_REF_TO_TEXTURE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, widths[i],
	heights[i], 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);

	//color texture
	glBindTexture(GL_TEXTURE_2D, scene_tex_id[i]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,
		GL_COMPARE_REF_TO_TEXTURE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, widths[i],
	heights[i], 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

	glBindFramebuffer(GL_FRAMEBUFFER, fbo_id[i]);
	glBindTexture(GL_TEXTURE_2D, scene_tex_id[i]);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 ,
	GL_TEXTURE_2D, scene_tex_id[i], 0);

	// create a depth-buffer in case depth texture doesnt work
	//glGenRenderbuffers(1, depth_buffer[i]);
	//// bind our depth-buffer
	//glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer[i]);
	//// create storage for our renderbuffer
	//glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,
	//width, height);
	//// attach renderbuffer to FBO
	//glFramebufferRenderbuffer(GL_FRAMEBUFFER,
	//GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_buffer[i]);

	glBindTexture(GL_TEXTURE_2D, depth_tex_id[i]);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
	GL_TEXTURE_2D, depth_tex_id[i], 0);
	

	std::cout << "framebuffer completeness check " << i <<":\n";
	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
	{
		// ToDo:
		printf("GL_FRAMEBUFFER_COMPLETE error 0x%x\n", glCheckFramebufferStatus(GL_FRAMEBUFFER));
	}
	std::cout << "framebuffer completeness check done\n";

	}

	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	//---------------------
	//init shadow/depth map
	//---------------------
		// create the texture we'll use for the shadowmap
	glGenTextures(1, &depth_tex);
	glBindTexture(GL_TEXTURE_2D, depth_tex);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,
	//	GL_COMPARE_REF_TO_TEXTURE);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width,
	height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
	glGenFramebuffers(1, &depth_fbo);
	glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
	GL_TEXTURE_2D, depth_tex, 0);
	glDrawBuffer(GL_NONE); // essential for depth-only FBOs!!!
	glReadBuffer(GL_NONE); // essential for depth-only FBOs!!!
	

	std::cout << "framebuffer completeness check:\n";
	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
	{
		// ToDo:
		printf("GL_FRAMEBUFFER_COMPLETE error 0x%x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
	}
	std::cout << "framebuffer completeness check done\n";

	glBindFramebuffer(GL_FRAMEBUFFER, 0);


	return true;
}


//very simple function to set some values from a config file
void demo_abgabe1::load_config(const std::string& filepath)
{
	std::ifstream fp_in;
	fp_in.open(filepath, std::ios::in);

	string key;
	string bla;

	while (getline(fp_in, key, ' '))
	{
		if (key.compare("width") == 0) {
			fp_in >> width;
		}
		else if (key.compare("height") == 0) {
			fp_in >> height;
		}
		else if (key.compare("ultra_shadows") == 0) {
			fp_in >> ultra_shadows;
		}
		else if (key.compare("snow") == 0) {
			int i;
			fp_in >> i;
			if (i == 0) {
				draw_snow = false;
			}
			else
				draw_snow = true;
		}
		else if (key.compare("dof") == 0) {
			int i;
			fp_in >> i;
			if (i == 0) {
				dof_enabled = false;
			}
			else
				dof_enabled = true;
		}
		else if (key.compare("fullscreen") == 0) {
			int i;
			fp_in >> i;
			if (i == 0) {
				fullscreen = false;
			}
			else
				fullscreen = true;
		}
		else if (key.compare("soft_shadows") == 0) {
			int i;
			fp_in >> i;
			//std::cout << i << std::endl;
			soft_shadows = i;
		}
		else if (key.compare("camera") == 0) {
			int i;
			fp_in >> i;
			if (i == 0) {
				automcamera = false;
			}
			else
			{
				automcamera = true;
			}
		}
		else if (key.compare("odsm") == 0) {
			int i;
			fp_in >> i;
			if (i == 0) {
				enable_odsm = false;
			}
			else
			{
				enable_odsm = true;
			}
		}
		//extend as needed
		//pattern:
		//else if (key.compare("something") == 0) {
		//	fp_in >> some_member;
		//}
		else {
			std::cout << "unknown option '" << key << "' !\n";
			fp_in >> bla;
		}
		//ignore content until end of file
		getline(fp_in, key);
	}
	std::cout << "done loading config\n";
}

//render omni-directional shadow mapping
void demo_abgabe1::render_odsm(scene_node& node) 
{
	push_modelview_matrix();

	//apply transformation of this node
	modelview_matrix *= node.transformation_matrix;

	//setup shader and render node
	//Shader* current_shader; //point to the shader of the node
	switch (node.shader_type())
	{
	case SH_NONE:
		break;
	//particles dont cast shadows for now!
	case SH_PARTICLE:
		break;
	case SH_SNOW:
		break;
	default:

		shadow_r2cm->bind();

		shadow_r2cm->set_uniform("model_matrix", modelview_matrix);

		//TODO: un-hardcode this (corner_fire_shadow)
		for (int i =0; i< 6; i++) {
			char mat_loc[10];
			sprintf(mat_loc, "cm_mat[%d]",i);
			shadow_r2cm->set_uniform(mat_loc, corner_fire_shadow.perspective_odsm *
								corner_fire_shadow.cm_rots[i] *
							glm::translate(-corner_fire_shadow.light) );
		}

		shadow_r2cm->set_uniform("near_far", corner_fire_shadow.near_far);

		shadow_r2cm->set_uniform("light_pos", glm::vec4(corner_fire_shadow.light, 1.0f));
		
		node.render();
		break;

		glUseProgram(0);
	}


	//render children
	std::vector<scene_node*>::iterator it;
	for (it = node.children.begin(); it != node.children.end(); it++)
	{
		render_odsm(**it);
	}

	//restore previous transformation
	pop_modelview_matrix();
}

void demo_abgabe1::init_odsm()
{
	//create FBO
	glGenFramebuffers(1, &odsm_FBO);
	glBindFramebuffer(GL_FRAMEBUFFER, odsm_FBO);

		// generate cubemap texture
	glGenTextures(1, &odsm_tex_ID);
	glBindTexture(GL_TEXTURE_CUBE_MAP, odsm_tex_ID);

	//fix seam-artifacts due to numerical precision limitations
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	//unsigned 24bit int z-buffer
	//if more precision is needed go GL_DEPTH_COMPONENT32F
	GLint internal_format = GL_DEPTH_COMPONENT24;
	GLenum data_type = GL_UNSIGNED_INT;

	GLenum format = GL_DEPTH_COMPONENT;

	for (GLint face=0; face < 6; face++) {
		glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
			0,
			internal_format,
			cubemap_dim, cubemap_dim, 0,
			format,
			data_type,
			NULL // dont need to specify content
			);
	}

	//attach cubemap at FBO's depth attachment point
	glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, odsm_tex_ID, 0);

	//tell opengl we did not add a color texture on purpose
	//else the FBO is considered incomplete
	glDrawBuffer(GL_NONE);
	glReadBuffer(GL_NONE);

	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
	{
		// ToDo:
		printf("GL_FRAMEBUFFER_COMPLETE error 0x%x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
	}
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	// then, just before rendering
	//glBindFramebuffer(GL_FRAMEBUFFER, odsm_FBO);

	//setup matrices
	perspective_odsm = glm::perspective(90.0f, 1.0f, near_far.x, near_far.y);
	//pos x
	cm_rots[0] = glm::mat4(glm::vec4(0.0f, 0.0f, -1.0f, 0.0f),
							glm::vec4(0.0f, -1.0f, 0.0f, 0.0f),
							glm::vec4(-1.0f, 0.0f, 0.0f, 0.0f),
							glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
	//neg x
	cm_rots[1] = glm::mat4(glm::vec4(0.0, 0.0, 1.0, 0.0),
							glm::vec4(0.0, -1.0, 0.0, 0.0),
							glm::vec4(1.0, 0.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, 0.0, 1.0));
	//pos y
	cm_rots[2] = glm::mat4(glm::vec4(1.0, 0.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, -1.0, 0.0),
							glm::vec4(0.0, 1.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, 0.0, 1.0));
	//neg y
	cm_rots[3] = glm::mat4(glm::vec4(1.0, 0.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, 1.0, 0.0),
							glm::vec4(0.0, -1.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, 0.0, 1.0));
	//pos z
	cm_rots[4] = glm::mat4(glm::vec4(1.0, 0.0, 0.0, 0.0),
							glm::vec4(0.0, -1.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, -1.0, 0.0),
							glm::vec4(0.0, 0.0, 0.0, 1.0));
	//neg z
	cm_rots[5] = glm::mat4(glm::vec4(-1.0, 0.0, 0.0, 0.0),
							glm::vec4(0.0, -1.0, 0.0, 0.0),
							glm::vec4(0.0, 0.0, 1.0, 0.0),
							glm::vec4(0.0, 0.0, 0.0, 1.0));

	cm_trans = glm::translate(-light_1);
}

//set light0 positions and delta steps between them along the camera path
void demo_abgabe1::init_light_positions() 
{
	float steps = 200;
	
	light0_pos1.x = -15.0f;
	light0_pos1.y = 65.0f;
	light0_pos1.z = -0.667828f;

	//pos_delta1 =  (light0_pos1 - light_0) / steps;
	pos_delta1 = glm::vec3(0.0f, 0.0f, 0.14498236f); // see if this works better

	//position 2
	
	light0_pos2.x = -35.99f;
	light0_pos2.y = 65.0f;
	light0_pos2.z = 30.6295f;

	pos_delta2 =  (light0_pos2 - light0_pos1) / steps;

	//position 3

	light0_pos3 = light0_pos2;
	//light0_pos3.x = -43.1267f;//-38.2842f;//-36.3685f;
	//light0_pos3.y = 65.0f;
	//light0_pos3.z = 100.805f;//90.629f;
	//light0_pos3.z = 129.712f;
	
	pos_delta3 =  (light0_pos3 - light0_pos2) / steps;

	//position 4

	light0_pos4.x = -57.6345f;
	light0_pos4.y = 65.0f;
	light0_pos4.z = 89.6927f;

	pos_delta4 =  (light0_pos4 - light0_pos3) / steps;
}

void demo_abgabe1::camerapath()
{
	//TODO - ist der Zeitfaktor korrekt
	//TODO - Abfrage ob Punkt nun nicht gleich dem Controlpoint
	//http://www.cubic.org/docs/hermite.htm

	//create a new spline, read in the camera coordinates
	//spline.load_cam_positions("../../camerapoints.txt");

	//zeitfaktor, dann aufrufen von der interpolationsmethode für die neuen Punkte, Kamera die werte zuweisen, 
	//für interpolationsmethodenaufruf, abfrage ob Punkt nicht schon gleich Kontrollpunkt - zuweisen der nächsten Punkte
	//wenn s oder t = 1, Reise zu ende da es 0...erster Punkt   1...letzter Punkt
		
	//current_time = glfwGetTime();
	//delta_ = current_time - last_time;
	//last_time = current_time;
	//und dann current_time immer um den faktor delta_ eigentlich erhöhen, bzw nur mit delta_ arbeiten....


	float factor = static_cast<float>(delta()) * 5100;
	//cout << "factor " << factor << endl;
	//cout << "delta " << delta() << endl;

	//schleife wo die Punkte berechnet werden und der Kamera hinzugefügt werden


	if(et2 == spline.tangentpointseye.end())
	{
		endscreen = true;
		timespline = maxfactor;
		timefire = maxfactor;
		glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
	}
	else if((timespline < maxfactor) && (et2 != spline.tangentpointseye.end()))
	{
		//TODO entweder bei den Punkten nachsehen oder sonst anfragen ob s > 1 - und dann die nächsten Punkte wählen

		float s = (float)timespline/(float)maxfactor;

		//method to update fire
		updatefire();

		//cout << "s " << s << endl;

		//eye
		glm::vec3 eye = spline.getInterpolPoint(s, (*ep1), (*et1), (*ep2), (*et2));
		//center
		glm::vec3 center = spline.getInterpolPoint(s, (*cp1), (*ct1), (*cp2), (*ct2));

		//nun der Kamera die Werte zuweisen
		cam_eye = eye;
		cam_center = center;
		//view_frustrum.set_camera(cam_eye, cam_center, cam_up);

		//fire
		//glm::vec3 fire = firespline.getInterpolPoint(t, (*fep1), (*fet1), (*fep2), (*fet2));
		//light_1 = fire;
		//corner_fire_node.transformation_matrix = glm::translate(light_1);
		//corner_fire_shadow.light = light_1;
		//cout << "fire " << fire.x << "/" << fire.y << "/" << fire.z << endl;
		//cout << "t " << t << endl;

		//glm::vec3 newdirview = glm::normalize(cam_center - cam_eye);
		//glm::vec3 frontcam = cam_eye + newdirview*3.0f;
		//light_1 = frontcam;
		//corner_fire_node.transformation_matrix = glm::translate(light_1);
		//corner_fire_shadow.light = light_1;
		
		//test_node.transformation_matrix = glm::translate(frontcam)*glm::scale(0.6f, 0.6f, 0.6f);

		//hardcoded effects for the path
		//Lichtanpassungen
		if(licht1)
		{
			//-15.0f,65.0f,-29.6643f   -> -15/65/-0.667828		
			//light_0.z +=  0.14498236f;
			light_0 += pos_delta1;
			cntcam++;
			if(cntcam == 200)
			{
				licht1 = false;
				cntcam = 0;
				light_0 = light0_pos1;
				//light_0.x = -15.0f;
				//light_0.y = 65.0f;
				//light_0.z = -0.667828f;
			}
		}
		if(licht2)
		{
			//-15/65/-0.667828   ->  -35.99/65/30.6295
			cntcam++;
			//light_0.x -= 0.10495f;
			//light_0.z += 0.15648664f;
			light_0 += pos_delta2;
			if(cntcam == 200)
			{
				licht2 = false;
				cntcam = 0;
				light_0 = light0_pos2;
				//light_0.x = -35.99f;
				//light_0.y = 65.0f;
				//light_0.z = 30.6295f;
			}
		}
		if(licht3)
		{
			//-35.99/65/30.6295  -> 0.548635/65/129.712
			
			//0.548635/65/129.712	versuch 1
			//-68.9981/65/129.712
			//-57.6345/65/89.6927
			cntcam++;
			//light_0.x += 0.182693175f;
			//light_0.z += 0.4954125f;
			light_0 += pos_delta3;
			if(cntcam == 200)
			{
				licht3 = false;
				cntcam = 0;
				light_0 = light0_pos3;
				//light_0.x = 0.548635f;
				//light_0.y = 65.0f;
				//light_0.z = 129.712f;
			}
		}
		if(licht4)
		{
			//0.548635/65/129.712	-> -57.6345/65/89.6927
			cntcam++;
			//light_0.x -= 0.290915675f;
			//light_0.z -= 0.2000965f;
			light_0 += pos_delta4;
			if(cntcam == 200)
			{
				licht4 = false;
				cntcam = 0;
				light_0 = light0_pos4;
				//light_0.x = -57.6345f;
				//light_0.y = 65.0f;
				//light_0.z = 89.6927f;
			}
		}
		if(windsound)
		{
			//check if the sound has to be played
			actualseconds = time(NULL);
			if(actualseconds > (lastseconds+2))
			{
				lastseconds = actualseconds;
				result = system->playSound(FMOD_CHANNEL_FREE, wind, false, &channelwind);
				errcheck(result);
				channelwind->setVolume((volume+0.02f));
			}
			snow_node.set_forcefield(glm::vec3(1.7f, 0.0f, 0.0f));
			cntwind++;
		
			//if(cntwind > 140)
			if(cntwind > 240)
			{
				windsound = false;
			}
		
		}
		

		//TODO, vermutlich anderen Faktor wählen für das Feuer!!!!
		if(factor < -maxfactor || factor > maxfactor)
		{
			timespline += 1.0f;
			timefire += 1.0f;
		}
		else
		{
			timespline += factor;
			timefire += (factor*0.95);
		}

	}
	else if((timespline >= maxfactor) && (et2 != spline.tangentpointseye.end()))
	{
		timespline = 1.0f;
		//nächster Teil
			ep1++;
			ep2++;
			et1++;
			et2++;

			cp1++;
			cp2++;
			ct1++;
			ct2++;

			//abfrage, damit entschieden wird was geschieht
			if(((*ep1).x == 7.01184f) && ((*ep1).y == 0.0911906f) && ((*ep1).z == 7.249f))
			{	
				licht1 = true;
			}
			if(((*ep1).x == -7.97996f) && ((*ep1).y == 1.99005f) && ((*ep1).z == -19.5445f))
			{	
				
				licht2 = true;
			}
			if(((*ep1).x == -17.3472f) && ((*ep1).y == 10.879f) && ((*ep1).z == 1.44956f))
			{	
				
				licht3 = true;
			}
			if(((*ep1).x == -17.3472f) && ((*ep1).y == 7.64597f) && ((*ep1).z == 1.44956f))
			{	
				
				licht4 = true;
			}
			//if(((*ep1).x == -22.2278f) && ((*ep1).y == 2.32764f) && ((*ep1).z == -19.4926f))
			if(((*ep1).x == -7.97996f) && ((*ep1).y == 1.99005f) && ((*ep1).z == -19.5445f))
			{	
				windsound = true;
			}
			if(((*ep1).x == 0.331357f) && ((*ep1).y == 5.21198f) && ((*ep1).z == 4.31797f))
			{	
				//kamin = true;
				//play the fire effect
				result = system->playSound(FMOD_CHANNEL_FREE, fire, false, &channelfire);
				errcheck(result);
				//set the volume
				channelfire->setVolume((volume+0.02f));
				lastseconds = actualseconds;
			}

	}
	else
	{
		endscreen = true;
		timespline = maxfactor;
		timefire = maxfactor;
		glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
	}
	

}//end of camerapath()


void demo_abgabe1::updatefire()
{
	//for the firepath
	float t = (float)timefire/(float)maxfactor;
	//cout << "t " << t << endl;
	
	if((timefire < maxfactor) && ( fet2 != firespline.tangentpointseye.end()))
	{
		glm::vec3 fire = firespline.getInterpolPoint(t, (*fep1), (*fet1), (*fep2), (*fet2));
		light_1 = fire;
		corner_fire_node.transformation_matrix = glm::translate(light_1);
		corner_fire_shadow.light = light_1;
	}
	else if((timefire >= maxfactor) && (fet2 != firespline.tangentpointseye.end()))
	{
			timefire = 1.0f;
			fep1++;
			fep2++;
			fet1++;
			fet2++;
	}
	else
	{
		timefire = maxfactor;
	}



}


void demo_abgabe1::preparecampath()
{
	//iterator für die Punkte - eye of the camera
	ep1 = spline.controlpointseye.begin();
	cam_eye = (*ep1);
	ep1++;
	ep2 = spline.controlpointseye.begin();
	ep2++;
	ep2++;
	et1 = spline.tangentpointseye.begin();
	et2 = spline.tangentpointseye.begin();
	et2++;

	//iterator für die Punkte - center of the camera
	cp1 = spline.controlpointscenter.begin();
	cam_center = (*cp1);
	cp1++;
	cp2 = spline.controlpointscenter.begin();
	cp2++;
	cp2++;
	ct1 = spline.tangentpointscenter.begin();
	ct2 = spline.tangentpointscenter.begin();
	ct2++;

	timespline = 1.0f;
}


void demo_abgabe1::preparefirepath()
{
	//iterator für die Punkte 
	fep1 = firespline.controlpointseye.begin();
	//cam_eye = (*ep1);
	fep1++;
	fep2 = firespline.controlpointseye.begin();
	fep2++;
	fep2++;
	fet1 = firespline.tangentpointseye.begin();
	fet2 = firespline.tangentpointseye.begin();
	fet2++;

	timefire = 1.0f;
}