+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <algorithm>
+#include <cmath>
+#include <msp/fs/path.h>
+#include <msp/gl/matrix.h>
+#include <msp/gl/texture.h>
+#include <msp/io/print.h>
+#include "overlay.h"
+#include "track.h"
+#include "tracktype.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace Marklin {
+
+Overlay3D::Overlay3D(const Graphics::Window &w, const GL::Camera &c, const GL::Font &f):
+ window(w),
+ camera(c),
+ font(f)
+{ }
+
+Overlay3D::~Overlay3D()
+{
+ for(map<string, GL::Mesh *>::iterator i=graphics.begin(); i!=graphics.end(); ++i)
+ delete i->second;
+}
+
+void Overlay3D::set_label(const Track3D &track, const string &label)
+{
+ get_icon(track).label = label;
+ update_icon(get_icon(track));
+}
+
+void Overlay3D::add_graphic(const Track3D &track, const string &grf_name)
+{
+ const GL::Mesh *grf = get_graphic(grf_name);
+ if(!grf)
+ return;
+
+ Icon &icon = get_icon(track);
+ if(find(icon.graphics.begin(), icon.graphics.end(), grf)==icon.graphics.end())
+ icon.graphics.push_back(grf);
+
+ update_icon(icon);
+}
+
+void Overlay3D::remove_graphic(const Track3D &track, const string &grf_name)
+{
+ const GL::Mesh *grf = get_graphic(grf_name);
+ Icon &icon = get_icon(track);
+ icon.graphics.erase(remove(icon.graphics.begin(), icon.graphics.end(), grf), icon.graphics.end());
+
+ update_icon(icon);
+}
+
+void Overlay3D::clear_graphics(const Track3D &track)
+{
+ get_icon(track).graphics.clear();
+ update_icon(get_icon(track));
+}
+
+void Overlay3D::clear(const Track3D &track)
+{
+ map<const Track3D *, Icon *>::iterator i = icons.find(&track);
+ if(i!=icons.end())
+ {
+ delete i->second;
+ icons.erase(i);
+ }
+}
+
+void Overlay3D::render(const GL::Tag &tag) const
+{
+ if(tag==0)
+ {
+ GL::matrix_mode(GL::PROJECTION);
+ GL::push_matrix();
+ GL::load_identity();
+ GL::scale(2.0/window.get_width(), 2.0/window.get_height(), 1.0);
+ GL::matrix_mode(GL::MODELVIEW);
+ GL::push_matrix();
+ GL::load_identity();
+
+ glLineWidth(1);
+
+ int size = int(font.get_default_size()+0.5);
+ float spacing = round(size*1.1)/size;
+ float baseline = round((0.5-font.get_ascent()*0.5-font.get_descent()*0.5)*size)/size;
+
+ for(map<const Track3D *, Icon *>::const_iterator i=icons.begin(); i!=icons.end(); ++i)
+ {
+ const Icon &icon = *i->second;
+
+ const Point &pos = i->first->get_track().get_position();
+ Point minp;
+ Point maxp;
+ i->first->get_type().get_bounds(0, minp, maxp);
+ float rot = i->first->get_track().get_rotation();
+ float c = cos(rot);
+ float s = sin(rot);
+
+ GL::Vector3 p((minp.x+maxp.x)/2, (minp.y+maxp.y)/2, 0);
+ p = GL::Vector3(pos.x+c*p.x-s*p.y, pos.y+s*p.x+c*p.y, pos.z+0.02);
+ p = camera.project(p);
+
+ GL::PushMatrix push_mat;
+ p.x = int(p.x*0.5*window.get_width()-icon.width*size/2);
+ p.y = int(p.y*0.5*window.get_height());
+ GL::translate(p.x, p.y, p.z);
+ GL::scale_uniform(size);
+
+ icon.background.draw();
+
+ glColor3f(0.0, 1.0, 0.0);
+ for(vector<const GL::Mesh *>::const_iterator j=icon.graphics.begin(); j!=icon.graphics.end(); ++j)
+ {
+ (*j)->draw();
+ GL::translate(spacing, 0, 0);
+ }
+
+ GL::translate(0, baseline, 0);
+ font.draw_string(icon.label);
+ GL::Texture::unbind();
+ }
+
+ GL::matrix_mode(GL::PROJECTION);
+ GL::pop_matrix();
+ GL::matrix_mode(GL::MODELVIEW);
+ GL::pop_matrix();
+ }
+}
+
+Overlay3D::Icon &Overlay3D::get_icon(const Track3D &track)
+{
+ Icon *&icon = icons[&track];
+ if(!icon)
+ icon = new Icon;
+
+ return *icon;
+}
+
+const GL::Mesh *Overlay3D::get_graphic(const string &name)
+{
+ GL::Mesh *&grf = graphics[name];
+ if(!grf)
+ {
+ grf = new GL::Mesh;
+ try
+ {
+ DataFile::load(*grf, (FS::Path("icons")/(name+".mesh")).str());
+ }
+ catch(const Exception &e)
+ {
+ IO::print("Error loading overlay graphic '%s': %s\n", name, e.what());
+ delete grf;
+ grf = 0;
+ }
+ }
+
+ return grf;
+}
+
+void Overlay3D::update_icon(Icon &icon)
+{
+ icon.background.clear();
+
+ icon.width = max(icon.graphics.size()*1.1+font.get_string_width(icon.label), 1.0);
+
+ GL::MeshBuilder bld(icon.background);
+ bld.color(0.2f, 0.2f, 0.2f, 0.7f);
+
+ bld.begin(GL::TRIANGLE_FAN);
+ bld.vertex(0.4, 0.5);
+ bld.vertex(icon.width-0.4, 0.5);
+ bld.vertex(icon.width-0.4, 1.2);
+ for(int i=4; i<=12; ++i)
+ bld.vertex(0.4+cos(i*M_PI/8)*0.7, 0.5+sin(i*M_PI/8)*0.7);
+ bld.end();
+
+ bld.begin(GL::TRIANGLE_FAN);
+ bld.vertex(icon.width-0.4, 0.5);
+ bld.vertex(0.4, 0.5);
+ bld.vertex(0.4, -0.2);
+ for(int i=-4; i<=4; ++i)
+ bld.vertex(icon.width-0.4+cos(i*M_PI/8)*0.7, 0.5+sin(i*M_PI/8)*0.7);
+ bld.end();
+}
+
+
+Overlay3D::Icon::Icon():
+ width(1.0),
+ background((GL::COLOR4_UBYTE, GL::VERTEX2))
+{ }
+
+} // namespace Marklin