]> git.tdb.fi Git - r2c2.git/blobdiff - source/3d/overlay.cpp
Add an overlay to display aspects of tracks
[r2c2.git] / source / 3d / overlay.cpp
diff --git a/source/3d/overlay.cpp b/source/3d/overlay.cpp
new file mode 100644 (file)
index 0000000..5f07ac8
--- /dev/null
@@ -0,0 +1,202 @@
+/* $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