From fcaa883538f98bac71ba1a90f98950bb2aa08d88 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 10 Oct 2010 10:18:00 +0000 Subject: [PATCH] Add TrainView for viewing the layout from the train's perspective Make TrainPanel collapsible to save space Add button for taking train off the track --- marklin.res | 91 ++++++++++++++++-------- source/engineer/engineer.cpp | 31 +++++++-- source/engineer/engineer.h | 8 ++- source/engineer/trainpanel.cpp | 118 ++++++++++++++++++++++--------- source/engineer/trainpanel.h | 10 +++ source/engineer/trainview.cpp | 123 +++++++++++++++++++++++++++++++++ source/engineer/trainview.h | 51 ++++++++++++++ 7 files changed, 367 insertions(+), 65 deletions(-) create mode 100644 source/engineer/trainview.cpp create mode 100644 source/engineer/trainview.h diff --git a/marklin.res b/marklin.res index 8efa84e..e44ac9d 100644 --- a/marklin.res +++ b/marklin.res @@ -13,6 +13,14 @@ style "label" special "text"; }; +style "image" +{ + special "image" + { + fill 1.0 1.0; + }; +}; + graphic "tooltip" { texture "gui.png"; @@ -85,6 +93,34 @@ graphic "red_button_pressed" shadow { top 0; right 1; bottom 2; left 0; }; }; +graphic "up_arrow_button" +{ + texture "gui.png"; + slice 2 18 13 13; + shadow { top 0; right 1; bottom 2; left 0; }; +}; + +graphic "up_arrow_button_pressed" +{ + texture "gui.png"; + slice 17 18 13 13; + shadow { top 0; right 1; bottom 2; left 0; }; +}; + +graphic "down_arrow_button" +{ + texture "gui.png"; + slice 2 4 13 13; + shadow { top 0; right 1; bottom 2; left 0; }; +}; + +graphic "down_arrow_button_pressed" +{ + texture "gui.png"; + slice 17 4 13 13; + shadow { top 0; right 1; bottom 2; left 0; }; +}; + style "button" { font_color 0 0 0; @@ -142,6 +178,28 @@ style "button-red" }; }; +style "button-arrow_up" +{ + part + { + graphic NORMAL "up_arrow_button"; + graphic ACTIVE "up_arrow_button_pressed"; + align 0.5 0.5; + fill 0.0 0.0; + }; +}; + +style "button-arrow_down" +{ + part + { + graphic NORMAL "down_arrow_button"; + graphic ACTIVE "down_arrow_button_pressed"; + align 0.5 0.5; + fill 0.0 0.0; + }; +}; + graphic "yellow_lamp" { texture "gui.png"; @@ -227,6 +285,11 @@ style "panel" special "children"; }; +style "panel-group" +{ + special "children"; +}; + graphic "sunken_black_bg" { texture "gui.png"; @@ -358,34 +421,6 @@ style "list" }; }; -graphic "up_arrow_button" -{ - texture "gui.png"; - slice 2 18 13 13; - shadow { top 0; right 1; bottom 2; left 0; }; -}; - -graphic "up_arrow_button_pressed" -{ - texture "gui.png"; - slice 17 18 13 13; - shadow { top 0; right 1; bottom 2; left 0; }; -}; - -graphic "down_arrow_button" -{ - texture "gui.png"; - slice 2 4 13 13; - shadow { top 0; right 1; bottom 2; left 0; }; -}; - -graphic "down_arrow_button_pressed" -{ - texture "gui.png"; - slice 17 4 13 13; - shadow { top 0; right 1; bottom 2; left 0; }; -}; - style "dropdown" { font_color 0.0 0.0 0.0; diff --git a/source/engineer/engineer.cpp b/source/engineer/engineer.cpp index b665134..59aaeec 100644 --- a/source/engineer/engineer.cpp +++ b/source/engineer/engineer.cpp @@ -5,6 +5,7 @@ Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa Distributed under the GPL */ +#include #include #include #include @@ -33,6 +34,7 @@ Distributed under the GPL #include "mainpanel.h" #include "trainpanel.h" #include "trainproperties.h" +#include "trainview.h" using namespace std; using namespace Marklin; @@ -137,6 +139,26 @@ void Engineer::set_status(const string &text) status_timeout = Time::now()+10*Time::sec; } +void Engineer::rearrange_panels() +{ + int y = main_panel->get_geometry().y; + for(list::iterator i=train_panels.begin(); i!=train_panels.end(); ++i) + { + y -= (*i)->get_geometry().h; + (*i)->set_position(0, y); + } +} + +void Engineer::add_train_view(TrainView &tv) +{ + train_views.push_back(&tv); +} + +void Engineer::remove_train_view(TrainView &tv) +{ + train_views.erase(remove(train_views.begin(), train_views.end(), &tv), train_views.end()); +} + void Engineer::pick(bool with_ep) { picking = true; @@ -158,6 +180,9 @@ void Engineer::tick() layout.tick(); event_disp.tick(Time::zero); + for(list::iterator i=train_views.begin(); i!=train_views.end(); ++i) + (*i)->prepare(); + if(status_timeout && Time::now()>status_timeout) { main_panel->set_status_text(string()); @@ -403,12 +428,8 @@ void Engineer::train_added(Train &train) { TrainPanel *tpanel = new TrainPanel(*this, ui_res, train); root->add(*tpanel); - int y = main_panel->get_geometry().y; - for(list::iterator i=train_panels.begin(); i!=train_panels.end(); ++i) - y -= (*i)->get_geometry().h; - tpanel->set_position(0, y-tpanel->get_geometry().h); train_panels.push_back(tpanel); - tpanel->set_visible(true); + rearrange_panels(); Vehicle3D &loco3d = layout_3d.get_vehicle(train.get_vehicle(0)); overlay->set_label(loco3d, train.get_name()); diff --git a/source/engineer/engineer.h b/source/engineer/engineer.h index dc7a7a0..61a2f59 100644 --- a/source/engineer/engineer.h +++ b/source/engineer/engineer.h @@ -26,7 +26,7 @@ Distributed under the GPL class MainPanel; class TrainPanel; -class TrainProperties; +class TrainView; class Engineer: public Msp::Application { @@ -56,6 +56,7 @@ private: MainPanel *main_panel; std::list train_panels; + std::list train_views; Msp::Time::TimeStamp status_timeout; bool picking; Marklin::Track *picking_track; @@ -73,7 +74,12 @@ public: Msp::GLtk::Root &get_root() const { return *root; } const Marklin::Catalogue &get_catalogue() const { return catalogue; } Marklin::Layout &get_layout() { return layout; } + Marklin::Layout3D &get_layout_3d() { return layout_3d; } + const Msp::GL::Lighting &get_lighting() const { return lighting; } void set_status(const std::string &); + void rearrange_panels(); + void add_train_view(TrainView &); + void remove_train_view(TrainView &); void pick(bool); int main(); void quit() { exit(0); } diff --git a/source/engineer/trainpanel.cpp b/source/engineer/trainpanel.cpp index 75024dc..922e374 100644 --- a/source/engineer/trainpanel.cpp +++ b/source/engineer/trainpanel.cpp @@ -6,7 +6,6 @@ Distributed under the GPL */ #include -#include #include #include "libmarklin/timetable.h" #include "libmarklin/vehicletype.h" @@ -15,6 +14,7 @@ Distributed under the GPL #include "timetabledialog.h" #include "trainpanel.h" #include "trainproperties.h" +#include "trainview.h" using namespace std; using namespace Msp; @@ -24,46 +24,61 @@ TrainPanel::TrainPanel(Engineer &e, const GLtk::Resources &r, Train &t): Widget(r), Panel(r), engineer(e), - train(t) + train(t), + expanded(false) { - set_size(200, 175); + set_size(200, 65); train.signal_control_changed.connect(sigc::mem_fun(this, &TrainPanel::train_control_changed)); - add(*(lbl_addr=new GLtk::Label(res, format("%2d", train.get_address())))); + add(*(pnl_basic = new GLtk::Panel(res))); + pnl_basic->set_style("group"); + pnl_basic->set_geometry(GLtk::Geometry(0, geom.h-58, geom.w, 45)); + + pnl_basic->add(*(lbl_addr = new GLtk::Label(res, format("%2d", train.get_address())))); lbl_addr->set_style("digital"); - lbl_addr->set_geometry(GLtk::Geometry(10, geom.h-30, 35, 20)); + lbl_addr->set_geometry(GLtk::Geometry(10, 28, 35, 20)); - add(*(lbl_name=new GLtk::Label(res, train.get_name()))); + pnl_basic->add(*(lbl_name = new GLtk::Label(res, train.get_name()))); lbl_name->set_style("digital"); - lbl_name->set_geometry(GLtk::Geometry(50, geom.h-30, geom.w-55, 20)); + lbl_name->set_geometry(GLtk::Geometry(50, 28, geom.w-77, 20)); train.signal_name_changed.connect(sigc::mem_fun(lbl_name, &GLtk::Label::set_text)); - add(*(lbl_speed=new GLtk::Label(res, " 0"))); + pnl_basic->add(*(lbl_speed = new GLtk::Label(res, " 0"))); lbl_speed->set_style("digital"); - lbl_speed->set_geometry(GLtk::Geometry(10, geom.h-55, 35, 20)); + lbl_speed->set_geometry(GLtk::Geometry(10, 3, 35, 20)); - add(*(sld_speed=new GLtk::HSlider(res))); - sld_speed->set_geometry(GLtk::Geometry(50, geom.h-50, geom.w-80, 10)); + pnl_basic->add(*(sld_speed = new GLtk::HSlider(res))); + sld_speed->set_geometry(GLtk::Geometry(50, 8, geom.w-80, 10)); sld_speed->set_range(0, 200); sld_speed->set_step(5); sld_speed->signal_value_changed.connect(sigc::mem_fun(this, &TrainPanel::speed_slider_changed)); - add(*(tgl_forward=new GLtk::Toggle(res))); + pnl_basic->add(*(tgl_forward = new GLtk::Toggle(res))); tgl_forward->set_text("Fwd"); - tgl_forward->set_geometry(GLtk::Geometry(geom.w-30, geom.h-58, 20, 27)); + tgl_forward->set_geometry(GLtk::Geometry(geom.w-30, 0, 20, 27)); tgl_forward->set_value(true); tgl_forward->signal_toggled.connect(sigc::mem_fun(this, &TrainPanel::forward_toggled)); + pnl_basic->add(*(btn_expand = new GLtk::Button(res))); + btn_expand->set_style("arrow_down"); + btn_expand->set_geometry(GLtk::Geometry(geom.w-22, 28, 12, 20)); + btn_expand->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::expand_clicked)); + + add(*(pnl_extra = new GLtk::Panel(res))); + pnl_extra->set_style("group"); + pnl_extra->set_geometry(GLtk::Geometry(0, 10, geom.w, 135)); + pnl_extra->set_visible(false); + const Route *route = train.get_route(); - add(*(lbl_route=new GLtk::Label(res, (route ? route->get_name() : "Free run")))); + pnl_extra->add(*(lbl_route = new GLtk::Label(res, (route ? route->get_name() : "Free run")))); lbl_route->set_style("digital"); - lbl_route->set_geometry(GLtk::Geometry(10, 65, geom.w-20, 20)); + lbl_route->set_geometry(GLtk::Geometry(10, 85, geom.w-20, 20)); train.signal_route_changed.connect(sigc::mem_fun(this, &TrainPanel::train_route_changed)); - add(*(lbl_status=new GLtk::Label(res, train.get_status()))); + pnl_extra->add(*(lbl_status = new GLtk::Label(res, train.get_status()))); lbl_status->set_style("digital"); - lbl_status->set_geometry(GLtk::Geometry(10, 40, geom.w-20, 20)); + lbl_status->set_geometry(GLtk::Geometry(10, 60, geom.w-20, 20)); train.signal_status_changed.connect(sigc::mem_fun(this, &TrainPanel::train_status_changed)); const map &funcs = train.get_locomotive_type().get_functions(); @@ -73,9 +88,9 @@ TrainPanel::TrainPanel(Engineer &e, const GLtk::Resources &r, Train &t): string fname = i->second; fname[0] = toupper(fname[0]); GLtk::Toggle *tgl; - add(*(tgl=new GLtk::Toggle(res))); + pnl_extra->add(*(tgl = new GLtk::Toggle(res))); tgl->set_text(fname); - tgl->set_geometry(GLtk::Geometry(x, geom.h-87, 36, 27)); + tgl->set_geometry(GLtk::Geometry(x, 108, 36, 27)); tgl->set_value(train.get_function(i->first)); tgl->signal_toggled.connect(sigc::bind(sigc::mem_fun(this, &TrainPanel::func_toggled), i->first)); @@ -85,25 +100,51 @@ TrainPanel::TrainPanel(Engineer &e, const GLtk::Resources &r, Train &t): GLtk::Button *btn; - add(*(btn=new GLtk::Button(res, "Edit"))); - btn->set_geometry(GLtk::Geometry(geom.w-46, 10, 36, 25)); + pnl_extra->add(*(btn = new GLtk::Button(res, "Edit"))); + btn->set_geometry(GLtk::Geometry(10, 30, 36, 25)); btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::edit_clicked)); - add(*(btn=new GLtk::Button(res, "Place"))); - btn->set_geometry(GLtk::Geometry(geom.w-82, 10, 36, 25)); + pnl_extra->add(*(btn = new GLtk::Button(res, "Place"))); + btn->set_geometry(GLtk::Geometry(10, 0, 36, 25)); btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::place_clicked)); - add(*(btn=new GLtk::Button(res, "GoTo"))); - btn->set_geometry(GLtk::Geometry(geom.w-118, 10, 36, 25)); + pnl_extra->add(*(btn = new GLtk::Button(res, "Take"))); + btn->set_geometry(GLtk::Geometry(46, 0, 36, 25)); + btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::take_clicked)); + + pnl_extra->add(*(btn = new GLtk::Button(res, "GoTo"))); + btn->set_geometry(GLtk::Geometry(100, 0, 36, 25)); btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::goto_clicked)); - add(*(btn=new GLtk::Button(res, "Route"))); - btn->set_geometry(GLtk::Geometry(geom.w-154, 10, 36, 25)); + pnl_extra->add(*(btn = new GLtk::Button(res, "Route"))); + btn->set_geometry(GLtk::Geometry(100, 30, 36, 25)); btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::route_clicked)); - add(*(btn=new GLtk::Button(res, "TTbl"))); - btn->set_geometry(GLtk::Geometry(geom.w-190, 10, 36, 25)); + pnl_extra->add(*(btn = new GLtk::Button(res, "TTbl"))); + btn->set_geometry(GLtk::Geometry(46, 30, 36, 25)); btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::timetable_clicked)); + + pnl_extra->add(*(btn = new GLtk::Button(res, "View"))); + btn->set_geometry(GLtk::Geometry(geom.w-46, 30, 36, 25)); + btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::view_clicked)); +} + +void TrainPanel::expand(bool e) +{ + expanded = e; + pnl_extra->set_visible(expanded); + if(expanded) + { + set_size(geom.w, 205); + btn_expand->set_style("arrow_up"); + } + else + { + set_size(geom.w, 65); + btn_expand->set_style("arrow_down"); + } + pnl_basic->set_geometry(GLtk::Geometry(0, geom.h-58, geom.w, 45)); + engineer.rearrange_panels(); } void TrainPanel::train_control_changed(const string &control, float value) @@ -144,12 +185,16 @@ void TrainPanel::place_clicked() pick_conn = engineer.signal_pick_done.connect(sigc::mem_fun(this, &TrainPanel::place)); } +void TrainPanel::take_clicked() +{ + train.unplace(); +} + void TrainPanel::edit_clicked() { TrainProperties *dialog = new TrainProperties(engineer, res, &train); engineer.get_root().add(*dialog); dialog->set_position(geom.x+geom.w, geom.y+geom.h-dialog->get_geometry().h); - dialog->set_visible(true); } void TrainPanel::route_clicked() @@ -157,7 +202,6 @@ void TrainPanel::route_clicked() RouteSelect *dialog = new RouteSelect(engineer, res, train); engineer.get_root().add(*dialog); dialog->set_position(geom.x+geom.w, geom.y+geom.h-dialog->get_geometry().h); - dialog->set_visible(true); } void TrainPanel::goto_clicked() @@ -180,6 +224,18 @@ void TrainPanel::timetable_clicked() dialog->set_position(geom.x+geom.w, geom.y+geom.h-dialog->get_geometry().h); } +void TrainPanel::view_clicked() +{ + TrainView *dialog = new TrainView(engineer, train); + engineer.get_root().add(*dialog); + dialog->set_position(geom.x+geom.w, geom.y+geom.h-dialog->get_geometry().h); +} + +void TrainPanel::expand_clicked() +{ + expand(!expanded); +} + void TrainPanel::speed_slider_changed(double value) { float speed = value/3.6*engineer.get_layout().get_catalogue().get_scale(); diff --git a/source/engineer/trainpanel.h b/source/engineer/trainpanel.h index 9b2a544..d15462e 100644 --- a/source/engineer/trainpanel.h +++ b/source/engineer/trainpanel.h @@ -9,6 +9,7 @@ Distributed under the GPL #define TRAINPANEL_H_ #include +#include #include #include #include @@ -23,6 +24,9 @@ class TrainPanel: public Msp::GLtk::Panel, public sigc::trackable private: Engineer &engineer; Marklin::Train &train; + Msp::GLtk::Panel *pnl_basic; + Msp::GLtk::Panel *pnl_extra; + Msp::GLtk::Button *btn_expand; Msp::GLtk::Label *lbl_addr; Msp::GLtk::Label *lbl_name; Msp::GLtk::HSlider *sld_speed; @@ -32,19 +36,25 @@ private: Msp::GLtk::Toggle *tgl_forward; std::map tgl_funcs; sigc::connection pick_conn; + bool expanded; public: TrainPanel(Engineer &, const Msp::GLtk::Resources &, Marklin::Train &); + void expand(bool = true); + private: void train_control_changed(const std::string &, float); void train_function_changed(unsigned, bool); void train_route_changed(const Marklin::Route *); void train_status_changed(const std::string &); void place_clicked(); + void take_clicked(); void edit_clicked(); void route_clicked(); void goto_clicked(); void timetable_clicked(); + void view_clicked(); + void expand_clicked(); void speed_slider_changed(double); void forward_toggled(bool); void func_toggled(bool, unsigned); diff --git a/source/engineer/trainview.cpp b/source/engineer/trainview.cpp new file mode 100644 index 0000000..b27136c --- /dev/null +++ b/source/engineer/trainview.cpp @@ -0,0 +1,123 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#include +#include +#include +#include "libmarklin/vehicle.h" +#include "libmarklin/vehicletype.h" +#include "engineer.h" +#include "trainview.h" + +using namespace Msp; +using namespace Marklin; + +TrainView::TrainView(Engineer &e, const Train &t): + GLtk::Widget(e.get_ui_resources()), + GLtk::Panel(e.get_ui_resources()), + engineer(e), + train(t), + mode(SIDE), + pipeline(280, 280, false), + stale(false) +{ + set_size(300, 330); + + tex.set_min_filter(GL::LINEAR); + tex.storage(GL::RGB, 280, 280, 0); + tex.image(0, GL::RGB, GL::UNSIGNED_BYTE, 0); + fbo.attach(GL::COLOR_ATTACHMENT0, tex, 0); + depth.storage(GL::DEPTH_COMPONENT, 280, 280); + fbo.attach(GL::DEPTH_ATTACHMENT, depth); + + camera.set_up_direction(GL::Vector3(0, 0, 1)); + camera.set_depth_clip(0.01, 10); + camera.set_aspect(1); + pipeline.set_camera(&camera); + + pipeline.add_renderable(engineer.get_layout_3d().get_scene()); + + GL::PipelinePass *pass = &pipeline.add_pass(0); + pass->depth_test = &GL::DepthTest::lequal(); + pass->lighting = &engineer.get_lighting(); + + GLtk::Image *image; + add(*(image = new GLtk::Image(res, &tex))); + image->set_geometry(GLtk::Geometry(10, 40, geom.w-20, geom.h-50)); + + GLtk::Button *btn; + + add(*(btn = new GLtk::Button(res, "Roof"))); + btn->set_geometry(GLtk::Geometry(10, 10, 36, 25)); + btn->signal_clicked.connect(sigc::bind(sigc::mem_fun(this, &TrainView::set_mode), ROOF)); + + add(*(btn = new GLtk::Button(res, "Side"))); + btn->set_geometry(GLtk::Geometry(46, 10, 36, 25)); + btn->signal_clicked.connect(sigc::bind(sigc::mem_fun(this, &TrainView::set_mode), SIDE)); + + add(*(btn = new GLtk::Button(res, "Head"))); + btn->set_geometry(GLtk::Geometry(82, 10, 36, 25)); + btn->signal_clicked.connect(sigc::bind(sigc::mem_fun(this, &TrainView::set_mode), HEAD)); + + add(*(btn = new GLtk::Button(res, "Close"))); + btn->set_geometry(GLtk::Geometry(geom.w-46, 10, 36, 25)); + btn->signal_clicked.connect(sigc::mem_fun(this, &TrainView::close_clicked)); + + engineer.add_train_view(*this); +} + +TrainView::~TrainView() +{ + engineer.remove_train_view(*this); +} + +void TrainView::set_mode(Mode m) +{ + mode = m; +} + +void TrainView::prepare() +{ + const Vehicle &veh = train.get_vehicle(0); + const Point &pos = veh.get_position(); + float angle = veh.get_direction(); + float c = cos(angle); + float s = sin(angle); + float l = veh.get_type().get_length(); + + switch(mode) + { + case ROOF: + camera.set_position(GL::Vector3(pos.x-l*c, pos.y-l*s, pos.z+0.07)); + camera.set_look_direction(GL::Vector3(c, s, -0.2)); + break; + case SIDE: + camera.set_position(GL::Vector3(pos.x-l*0.8*c+0.05*s, pos.y-l*0.8*s-0.05*c, pos.z+0.03)); + camera.set_look_direction(GL::Vector3(c-0.2*s, s+0.2*c, 0)); + break; + case HEAD: + camera.set_position(GL::Vector3(pos.x+l*0.55*c, pos.y+l*0.55*s, pos.z+0.03)); + camera.set_look_direction(GL::Vector3(c, s, 0)); + break; + } + + GL::Bind _bind_fbo(fbo); + fbo.clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT); + pipeline.render_all(); +} + +void TrainView::button_release(int x, int y, unsigned btn) +{ + GLtk::Panel::button_release(x, y, btn); + if(stale) + delete this; +} + +void TrainView::close_clicked() +{ + stale = true; +} diff --git a/source/engineer/trainview.h b/source/engineer/trainview.h new file mode 100644 index 0000000..16a1ef9 --- /dev/null +++ b/source/engineer/trainview.h @@ -0,0 +1,51 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#ifndef TRAINVIEW_H_ +#define TRAINVIEW_H_ + +#include +#include +#include +#include +#include "libmarklin/train.h" + +class Engineer; + +class TrainView: public Msp::GLtk::Panel +{ +public: + enum Mode + { + ROOF, + SIDE, + HEAD + }; + +private: + Engineer &engineer; + const Marklin::Train &train; + Mode mode; + Msp::GL::Framebuffer fbo; + Msp::GL::Texture2D tex; + Msp::GL::Renderbuffer depth; + Msp::GL::Camera camera; + Msp::GL::Pipeline pipeline; + bool stale; + +public: + TrainView(Engineer &, const Marklin::Train &); + ~TrainView(); + + void set_mode(Mode); + void prepare(); +private: + virtual void button_release(int, int, unsigned); + void close_clicked(); +}; + +#endif -- 2.43.0