void sub_uzo_display::post_display(game& gm) const
{
	if (gm.get_player()->get_target()) {
		projection_data pd = get_projection_data(gm);
		ui.show_target(pd.x, pd.y, pd.w, pd.h, get_viewpos(gm));
	}

	sys().prepare_2d_drawing();
	
	int tex_w = compass->get_width();
	int tex_h = compass->get_height();

	int bearing = int(tex_w*ui.get_relative_bearing().value()/360);

	if( bearing>dx && bearing<tex_w-dx){
		compass->draw_subimage(xi, yi, comp_size, tex_h, bearing-dx, 0, comp_size, tex_h);
	} else {
		int dx1=0,dx2=0;
		if( bearing<dx ){ dx1=dx-bearing; dx2=dx+bearing; } else if( bearing>tex_w-dx ){ dx1=dx+(tex_w-bearing); dx2=comp_size-dx; }
		compass->draw_subimage(xi, yi, dx1, tex_h, tex_w-(dx1), 0, dx1, tex_h);
		compass->draw_subimage(xi+dx1, yi, dx2, tex_h, 0, 0, dx2, tex_h );
	}
	
	uzotex->draw(0, 0, 1024, 768);
	ui.draw_infopanel(true);

	sys().unprepare_2d_drawing();
}
void sub_uzo_display::set_modelview_matrix(game& gm, const vector3& viewpos) const
{
	glLoadIdentity();

	// set up rotation (player's view direction)
	// limit elevation to -20...20 degrees.
	float elev = -ui.get_elevation().value();
	const float LIMIT = 20.0f;
	if (elev < -LIMIT - 90.0f) elev = -LIMIT - 90.0f;
	if (elev > +LIMIT - 90.0f) elev = +LIMIT - 90.0f;
	glRotated(elev,1,0,0);

	// if we're aboard the player's vessel move the world instead of the ship
	if (aboard) {
		// This should be a negative angle, but nautical view dir is clockwise,
		// OpenGL uses ccw values, so this is a double negation
		glRotated(ui.get_relative_bearing().value(),0,0,1);
		gm.get_player()->get_orientation().conj().rotmat4().multiply_gl();
	} else {
		// This should be a negative angle, but nautical view dir is clockwise,
		// OpenGL uses ccw values, so this is a double negation
		glRotated(ui.get_absolute_bearing().value(),0,0,1);
	}

	// set up modelview matrix as if player is at position (0, 0, 0), so do NOT set a translational part.
	// This is done to avoid rounding errors caused by large x/y values (modelview matrix seems to store floats,
	// but coordinates are in real meters, so float is not precise enough).
}
user_interface* user_interface::create(game& gm)
{
	sea_object* p = gm.get_player();
	user_interface* ui = 0;
	// check for interfaces
	if (dynamic_cast<submarine*>(p)) ui = new submarine_interface(gm);
#if 0
	else if (dynamic_cast<ship*>(p)) ui = new ship_interface(gm);
	else if (dynamic_cast<airplane*>(p)) ui = new airplane_interface(gm);
#endif
	if (ui) ui->finish_construction();
	return ui;
}
user_interface::user_interface(game& gm) :
	mygame(&gm),
	pause(false),
	time_scale(1),
	panel_visible(true),
	screen_selector_visible(false),
	playlist_visible(false),
	main_menu_visible(false),
	bearing(0),
	elevation(90),
	bearing_is_relative(true),
	current_display(0),
	current_popup(0),
	mycoastmap(get_map_dir() + "default.xml"),
	daymode(gm.is_day_mode())
{
	add_loading_screen("coast map initialized");
	mysky.reset(new sky());
	panel.reset(new widget(0, 768-32, 1024, 32, "", 0));
	panel->set_background(0);
	// ca. 1024-2*8 for 6 texts => 168 pix. for each text
	int paneltextnrs[6] = { 1, 4, 5, 2, 98, 61 };
	const char* paneltexts[6] = { "000", "000", "000", "000", "000", "00:00:00" };
	for (int i = 0; i < 6; ++i) {
		int off = 8 + i*(1024-2*8)/6;
		string tx = texts::get(paneltextnrs[i]);
		vector2i sz = widget::get_theme()->myfont->get_size(tx);
		panel->add_child(new widget_text(off, 4, 0, 0, tx));
		panel_valuetexts[i] = new widget_text(off + 8 + sz.x, 4, 0, 0, paneltexts[i]);
		panel->add_child(panel_valuetexts[i]);
	}

	// create screen selector widget
	screen_selector.reset(new widget(0, 0, 256, 32, "", 0));
	screen_selector->set_background(0);

	// create playlist widget
	music_playlist.reset(new widget(0, 0, 384, 512, texts::get(262), 0));
	music_playlist->set_background(0);
	struct musiclist : public widget_list
	{
		bool active;
		void on_sel_change() {
			if (!active) return;
			int s = get_selected();
			if (s >= 0)
				music::instance().play_track(unsigned(s), 500);
		}
		musiclist(int x, int y, int w, int h) : widget_list(x, y, w, h), active(false) {}
	};
	musiclist* playlist = new musiclist(0, 0, 384, 512);
	music_playlist->add_child_near_last_child(playlist);
	music& m = music::instance();
	vector<string> mpl = m.get_playlist();
	for (vector<string>::const_iterator it = mpl.begin();
	     it != mpl.end(); ++it) {
		playlist->append_entry(*it);
	}
	typedef widget_caller_checkbox<user_interface, void (user_interface::*)()> wccui;
	// fixme: use checkbox here...
	playlist_repeat_checkbox = new wccui(this, &user_interface::playlist_mode_changed, 0, 0, 192, 32, false, texts::get(263));
	music_playlist->add_child_near_last_child(playlist_repeat_checkbox);
	playlist_shuffle_checkbox = new wccui(this, &user_interface::playlist_mode_changed, 0, 0, 192, 32, false, texts::get(264));
	music_playlist->add_child_near_last_child(playlist_shuffle_checkbox, 0, 1);
	playlist_mute_checkbox = new wccui(this, &user_interface::playlist_mute, 0, 0, 384, 32, false, texts::get(265));
	music_playlist->add_child_near_last_child(playlist_mute_checkbox, 0);
	playlist_mute_checkbox->move_pos(vector2i(-192, 0));
	music_playlist->add_child_near_last_child(new widget_set_button<bool>(playlist_visible, false, 0, 0, 384, 32, texts::get(260)), 0);
	music_playlist->clip_to_children_area();
	music_playlist->set_pos(vector2i(0, 0));
	// enable music switching finally, to avoid on_sel_change changing the music track,
	// because on_sel_change is called above, when adding entries.
	playlist->active = true;

	// create main menu widget
	main_menu.reset(new widget(0, 0, 256, 128, texts::get(104), 0));
	main_menu->set_background(0);
	typedef widget_caller_button<user_interface, void (user_interface::*)()> wcbui;
	main_menu->add_child_near_last_child(new wcbui(this, &user_interface::show_screen_selector, 0, 0, 256, 32, texts::get(266)));
	main_menu->add_child_near_last_child(new wcbui(this, &user_interface::toggle_popup, 0, 0, 256, 32, texts::get(267)), 0);
	main_menu->add_child_near_last_child(new wcbui(this, &user_interface::show_playlist, 0, 0, 256, 32, texts::get(261)), 0);
	main_menu->add_child_near_last_child(new wcbui(this, &user_interface::toggle_pause, 0, 0, 256, 32, texts::get(268)), 0);
	main_menu->add_child_near_last_child(new widget_caller_arg_button<user_interface, void (user_interface::*)(bool), bool>(this, &user_interface::request_abort, true, 0, 0, 256, 32, texts::get(177)), 0);
	main_menu->add_child_near_last_child(new widget_set_button<bool>(main_menu_visible, false, 0, 0, 256, 32, texts::get(260)), 0);
	main_menu->clip_to_children_area();
	vector2i mmp = sys().get_res_2d() - main_menu->get_size();
	main_menu->set_pos(vector2i(mmp.x/2, mmp.y/2));

	// create weather effects textures

	// rain
#ifdef RAIN
#define NR_OF_RAIN_FRAMES 16
#define NR_OF_RAIN_DROPS 800
#define RAIN_TEX_W 256
#define RAIN_TEX_H 256
	raintex.resize(NR_OF_RAIN_FRAMES);
	vector<Uint8> raintmptex(RAIN_TEX_W * RAIN_TEX_H * 2);

	for (unsigned j = 0; j < NR_OF_RAIN_FRAMES; ++j) {
		for (unsigned k = 0; k < RAIN_TEX_W * RAIN_TEX_H * 2; k += 2) {
			raintmptex[k + 0] = 128;
			raintmptex[k + 1] = 0;
		}
		for (unsigned i = 0; i < NR_OF_RAIN_DROPS; ++i) {
			vector2i pos(rnd(RAIN_TEX_W-2)+2, rnd(RAIN_TEX_H-2));
			Uint8 c = rnd(64)+128;
			raintmptex[(RAIN_TEX_W*pos.y + pos.x) * 2 + 0] = c;
			raintmptex[(RAIN_TEX_W*pos.y + pos.x) * 2 + 1] = 128;
			pos.x -= 1; pos.y += 1;
			raintmptex[(RAIN_TEX_W*pos.y + pos.x) * 2 + 0] = c;
			raintmptex[(RAIN_TEX_W*pos.y + pos.x) * 2 + 1] = 192;
			pos.x -= 1; pos.y += 1;
			raintmptex[(RAIN_TEX_W*pos.y + pos.x) * 2 + 0] = c;
			raintmptex[(RAIN_TEX_W*pos.y + pos.x) * 2 + 1] = 255;
		}
		raintex.reset(j, new texture(raintmptex, RAIN_TEX_W, RAIN_TEX_H, GL_LUMINANCE_ALPHA, texture::LINEAR_MIPMAP_LINEAR, texture::REPEAT));
	}
#endif
	// snow
#ifdef SNOW
#define NR_OF_SNOW_FRAMES 23
#define NR_OF_SNOW_FLAKES 2000
#define SNOW_TEX_W 256
#define SNOW_TEX_H 256
	snowtex.resize(NR_OF_SNOW_FRAMES);
	vector<Uint8> snowtmptex(SNOW_TEX_W * SNOW_TEX_H * 2, 255);
	vector<vector2i> snowflakepos(NR_OF_SNOW_FLAKES);
	vector<int> snowxrand(NR_OF_SNOW_FRAMES);

	// create random x coordinate sequence (perturbation)
	vector<unsigned> snowxtrans(SNOW_TEX_W);
	for (unsigned k = 0; k < SNOW_TEX_W; ++k) {
		snowxtrans[k] = k;
	}
	for (unsigned k = 0; k < SNOW_TEX_W * 20; ++k) {
		unsigned a = rnd(SNOW_TEX_W), b = rnd(SNOW_TEX_W);
		unsigned c = snowxtrans[a];
		snowxtrans[a] = snowxtrans[b];
		snowxtrans[b] = c;
	}

	for (unsigned j = 0; j < NR_OF_SNOW_FRAMES; ++j) {
		snowxrand[j] = rnd(3)-1;
	}
	snowflakepos[0] = vector2i(snowxtrans[0], 0);
	for (unsigned i = 1; i < NR_OF_SNOW_FLAKES; ++i) {
		vector2i oldpos = snowflakepos[i-1];
		for (unsigned j = 0; j < NR_OF_SNOW_FRAMES; ++j) {
			oldpos.x += snowxrand[(j+3*i)%NR_OF_SNOW_FRAMES];
			if (oldpos.x < 0) oldpos.x += SNOW_TEX_W;
			if (oldpos.x >= SNOW_TEX_W) oldpos.x -= SNOW_TEX_W;
			oldpos.y += 1;	// fixme add more complex "fall down" function
			if (oldpos.y >= SNOW_TEX_H) {
				oldpos.x = snowxtrans[oldpos.x];
				oldpos.y = 0;
			}
		}
		snowflakepos[i] = oldpos;
	}
	for (unsigned i = 0; i < NR_OF_SNOW_FRAMES; ++i) {
		for (unsigned k = 0; k < SNOW_TEX_W * SNOW_TEX_H * 2; k += 2)
			snowtmptex[k + 1] = 0;
		for (unsigned j = 0; j < NR_OF_SNOW_FLAKES; ++j) {
			snowtmptex[(SNOW_TEX_H*snowflakepos[j].y + snowflakepos[j].x) * 2 + 1] = 255;
			vector2i& oldpos = snowflakepos[j];
			oldpos.x += snowxrand[(j+3*i)%NR_OF_SNOW_FRAMES];
			if (oldpos.x < 0) oldpos.x += SNOW_TEX_W;
			if (oldpos.x >= SNOW_TEX_W) oldpos.x -= SNOW_TEX_W;
			oldpos.y += 1;	// fixme add more complex "fall down" function
			if (oldpos.y >= SNOW_TEX_H) {
				oldpos.x = snowxtrans[oldpos.x];
				oldpos.y = 0;
			}
		}
/*
		ostringstream oss;
		oss << "snowframe"<<i<<".pgm";
		ofstream osg(oss.str().c_str());
		osg << "P5\n"<<SNOW_TEX_W<<" "<<SNOW_TEX_H<<"\n255\n";
		osg.write((const char*)(&snowtmptex[0]), SNOW_TEX_W * SNOW_TEX_H);
*/
		snowtex.reset(i, new texture(snowtmptex, SNOW_TEX_W, SNOW_TEX_H, GL_LUMINANCE_ALPHA, texture::LINEAR_MIPMAP_LINEAR, texture::REPEAT));
	}
#endif

	particle::init();

	// level size is N * sample_spacing * 2^j, here we give n = log2(N)
	// where j is level number from 0 on.
	// so we compute number of levels:
	// 2^n * sample_spacing * 2^j_max <= z_far
	// j_max <= log2(z_far / (2^n * sample_spacing))
	// and #levels = j_max+1
	// so #levels = floor(log2(z_far / (2^n * sample_spacing))) + 1
	//const double z_far = 20000.0;

  add_loading_screen("user interface initialized");

	mygeoclipmap.reset(new geoclipmap(TERRAIN_NR_LEVELS, TERRAIN_RESOLUTION_N, mygame->get_height_gen()));
  mygeoclipmap->set_viewerpos(gm.get_player()->get_pos());

	add_loading_screen("terrain loaded");
}