]> git.tdb.fi Git - r2c2.git/blob - source/engineer/engineer.cpp
Add Track::get_endpoint_position to avoid duplicating calculations
[r2c2.git] / source / engineer / engineer.cpp
1 #include <cmath>
2 #include <limits>
3 #include <SDL.h>
4 #include <GL/gl.h>
5 #include <msp/core/error.h>
6 #include <msp/core/getopt.h>
7 #include <msp/gl/matrix.h>
8 #include <msp/gl/transform.h>
9 #include <msp/strings/formatter.h>
10 #include <msp/strings/lexicalcast.h>
11 #include <msp/strings/regex.h>
12 #include "engineer.h"
13 #include "mainpanel.h"
14 #include "trainpanel.h"
15
16 using namespace std;
17 using namespace Marklin;
18 using namespace Msp;
19
20 #include <iostream>
21
22 Engineer::Engineer(int argc, char **argv):
23         screen_w(1280),
24         screen_h(960),
25         fullscreen(false),
26         layout(catalogue),
27         layout_3d(layout),
28         no_lighting(false),
29         placing_train(0),
30         placing_block(0),
31         placing_entry(0),
32         simulate(false)
33 {
34         string res;
35         bool   debug=false;
36         string device="/dev/ttyS0";
37         unsigned quality=4;
38
39         GetOpt getopt;
40         getopt.add_option('r', "resolution",  res,         GetOpt::REQUIRED_ARG);
41         getopt.add_option('f', "fullscreen",  fullscreen,  GetOpt::NO_ARG);
42         getopt.add_option('g', "debug",       debug,       GetOpt::NO_ARG);
43         getopt.add_option('d', "device",      device,      GetOpt::REQUIRED_ARG);
44         getopt.add_option('q', "quality",     quality,     GetOpt::REQUIRED_ARG);
45         getopt.add_option('s', "simulate",    simulate,    GetOpt::NO_ARG);
46         getopt.add_option(     "no-lighting", no_lighting, GetOpt::NO_ARG);
47         getopt(argc, argv);
48
49         if(!res.empty())
50         {
51                 if(RegMatch m=Regex("([1-9][0-9]*)x([1-9][0-9]*)").match(res))
52                 {
53                         screen_w=lexical_cast<unsigned>(m[1].str);
54                         screen_h=lexical_cast<unsigned>(m[2].str);
55                 }
56                 else
57                         throw UsageError("Invalid resolution");
58         }
59
60         if(device!="none")
61                 control.open(device);
62
63         control.set_debug(debug);
64
65         layout_3d.set_quality(quality);
66
67         catalogue.load("tracks.dat");
68
69         const list<string> &args=getopt.get_args();
70         if(args.empty())
71                 throw UsageError("No layout given");
72         layout.load(args.front());
73
74         control.signal_sensor_event.connect(sigc::mem_fun(this, &Engineer::sensor_event));
75
76         trfc_mgr=new TrafficManager(control, layout);
77         trfc_mgr->signal_block_reserved.connect(sigc::mem_fun(this, &Engineer::block_reserved));
78
79         view_all();
80 }
81
82 Engineer::~Engineer()
83 {
84         delete trfc_mgr;
85 }
86
87 void Engineer::add_train(unsigned addr)
88 {
89         if(control.get_locomotive(addr))
90                 return;
91
92         Locomotive *loco=new Locomotive(control, addr);
93         Train *train=new Train(*trfc_mgr, *loco);
94         train->set_name(format("Train %d", trfc_mgr->get_trains().size()));
95
96         TrainPanel *tpanel=new TrainPanel(*this, ui_res, *train);
97         int y=main_panel->get_geometry().y;
98         for(TrainPanelSeq::iterator i=train_panels.begin(); i!=train_panels.end(); ++i)
99                 y-=(*i)->get_geometry().h;
100         tpanel->set_position(0, y-tpanel->get_geometry().h);
101         train_panels.push_back(tpanel);
102
103         placing_train=train;
104         placing_block=0;
105         status_text="Select train location";
106 }
107
108 int Engineer::main()
109 {
110         SDL_Init(SDL_INIT_VIDEO);
111         SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
112         SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
113         SDL_Surface *screen=SDL_SetVideoMode(screen_w, screen_h, 32, SDL_OPENGL|(fullscreen?SDL_FULLSCREEN:0));
114         if(!screen)
115         {
116                 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
117                 screen=SDL_SetVideoMode(screen_w, screen_h, 32, SDL_OPENGL|(fullscreen?SDL_FULLSCREEN:0));
118         }
119         if(!screen)
120         {
121                 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 0);
122                 screen=SDL_SetVideoMode(screen_w, screen_h, 32, SDL_OPENGL|(fullscreen?SDL_FULLSCREEN:0));
123         }
124         if(!screen)
125                 throw Exception("Couldn't create window");
126
127         glEnableClientState(GL_VERTEX_ARRAY);
128         glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
129         glEnable(GL_COLOR_MATERIAL);
130         glDepthFunc(GL_LEQUAL);
131         glEnable(GL_BLEND);
132         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
133
134         Parser::load(ui_res, "engineer.res");
135         main_panel=new MainPanel(*this, ui_res);
136         main_panel->set_position(0, screen_h-main_panel->get_geometry().h);
137
138         Application::main();
139
140         delete main_panel;
141         for(TrainPanelSeq::iterator i=train_panels.begin(); i!=train_panels.end(); ++i)
142                 delete *i;
143
144         SDL_Quit();
145
146         return exit_code;
147 }
148
149 void Engineer::tick()
150 {
151         //cout<<"tick\n";
152
153         SDL_Event event;
154         while(SDL_PollEvent(&event))
155         {
156                 switch(event.type)
157                 {
158                 case SDL_MOUSEBUTTONDOWN:
159                         button_press(event.button.x, screen_h-1-event.button.y, event.button.button);
160                         break;
161                 case SDL_MOUSEBUTTONUP:
162                         button_release(event.button.x, screen_h-1-event.button.y, event.button.button);
163                         break;
164                 case SDL_MOUSEMOTION:
165                         pointer_motion(event.motion.x, screen_h-1-event.motion.y);
166                         break;
167                 case SDL_KEYDOWN:
168                         key_press(event.key.keysym.sym, event.key.keysym.mod);
169                         break;
170                 case SDL_QUIT:
171                         exit(0);
172                         break;
173                 }
174         }
175
176         control.tick();
177
178         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
179
180         project_3d();
181         glLoadIdentity();
182         glRotatef(-cam_rot*180/M_PI, 0, 0, 1);
183         glTranslatef(-cam_pos.x, -cam_pos.y, -cam_pos.z);
184
185         if(!no_lighting)
186         {
187                 glEnable(GL_LIGHTING);
188                 glEnable(GL_LIGHT0);
189                 float params[4];
190                 params[0]=0;
191                 params[1]=-0.2;
192                 params[2]=1;
193                 params[3]=0;
194                 glLightfv(GL_LIGHT0, GL_POSITION, params);
195         }
196
197         //glEnable(GL_DEPTH_TEST);
198         glEnable(GL_MULTISAMPLE);
199
200         layout_3d.render();
201
202         glDisable(GL_LIGHTING);
203         glColor4f(1, 1, 1, 1);
204         const Track3DSeq &ltracks=layout_3d.get_tracks();
205         for(Track3DSeq::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
206         {
207                 Track &track=(*i)->get_track();
208                 if(track.get_turnout_id())
209                 {
210                         Turnout *trnt=control.get_turnout(track.get_turnout_id());
211                         if(trnt)
212                                 (*i)->render_route(trnt->get_route());
213                         else
214                                 (*i)->render_route(-1);
215                 }
216                 else
217                         (*i)->render_route(-1);
218         }
219
220         if(placing_train && placing_block)
221         {
222                 float rot=placing_entry->track->get_rotation()+placing_entry->track_ep->rot;
223                 Point pos=placing_entry->track->get_endpoint_position(*placing_entry->track_ep);
224                 GL::push_matrix();
225                 GL::translate(pos.x, pos.y, pos.z+0.03);
226                 GL::rotate(rot*180/M_PI+180, 0, 0, 1);
227                 GL::Texture::unbind();
228                 glColor4f(1, 1, 1, 1);
229                 glBegin(GL_TRIANGLE_FAN);
230                 glVertex2f(0.08,   0);
231                 glVertex2f(0.05,   0.03);
232                 glVertex2f(0.05,   0.01);
233                 glVertex2f(0,      0.01);
234                 glVertex2f(0,     -0.01);
235                 glVertex2f(0.05,  -0.01);
236                 glVertex2f(0.05,  -0.03);
237                 glEnd();
238                 GL::pop_matrix();
239         }
240
241         glMatrixMode(GL_PROJECTION);
242         glLoadIdentity();
243         glOrtho(0, screen_w, 0, screen_h, 0, 1);
244         glMatrixMode(GL_MODELVIEW);
245         glLoadIdentity();
246
247         glDisable(GL_DEPTH_TEST);
248         glDisable(GL_LIGHTING);
249         glDisable(GL_MULTISAMPLE);
250
251         main_panel->render();
252         for(TrainPanelSeq::iterator i=train_panels.begin(); i!=train_panels.end(); ++i)
253                 (*i)->render();
254
255         const GL::Font &font=ui_res.get_font("dejavu-12");
256         GL::load_identity();
257         GL::translate(340, 10, 0);
258         GL::scale_uniform(font.get_default_size());
259         glColor4f(1, 1, 1, 1);
260         font.draw_string(status_text);
261
262         SDL_GL_SwapBuffers();
263 }
264
265 void Engineer::key_press(unsigned, unsigned)
266 {
267 }
268
269 void Engineer::button_press(int x, int y, unsigned btn)
270 {
271         if(main_panel->get_geometry().is_inside(x, y))
272         {
273                 main_panel->button_press(x, y, btn);
274                 return;
275         }
276         for(TrainPanelSeq::iterator i=train_panels.begin(); i!=train_panels.end(); ++i)
277                 if((*i)->get_geometry().is_inside(x, y))
278                 {
279                         (*i)->button_press(x, y, btn);
280                         return;
281                 }
282
283         if(placing_train)
284         {
285                 if(btn==1 && placing_block)
286                 {
287                         set_block_color(*placing_block, Color(1, 1, 1));
288
289                         placing_train->place(placing_block, placing_entry);
290                         placing_train=0;
291                 }
292                 else if(btn==3)
293                 {
294                         const Block::EndpointSeq &endpoints=placing_block->get_endpoints();
295                         Block::EndpointSeq::const_iterator i;
296                         for(i=endpoints.begin(); i!=endpoints.end(); ++i)
297                                 if(&*i==placing_entry)
298                                         break;
299                         ++i;
300                         if(i==endpoints.end())
301                                 i=endpoints.begin();
302                         placing_entry=&*i;
303                 }
304         }
305         else
306         {
307                 Track3D *track=pick_track(x, y);
308                 if(track)
309                 {
310                         Turnout *turnout=control.get_turnout(track->get_track().get_turnout_id());
311                         if(turnout)
312                                 turnout->set_route(1-turnout->get_route());
313                         else if(simulate)
314                         {
315                                 Sensor *sensor=control.get_sensor(track->get_track().get_sensor_id());
316                                 if(sensor)
317                                         control.signal_sensor_event.emit(track->get_track().get_sensor_id(), !sensor->get_state());
318                         }
319                 }
320         }
321 }
322
323 void Engineer::button_release(int x, int y, unsigned btn)
324 {
325         if(main_panel->get_geometry().is_inside(x, y))
326         {
327                 main_panel->button_release(x, y, btn);
328                 return;
329         }
330         for(TrainPanelSeq::iterator i=train_panels.begin(); i!=train_panels.end(); ++i)
331                 if((*i)->get_geometry().is_inside(x, y))
332                 {
333                         (*i)->button_release(x, y, btn);
334                         return;
335                 }
336 }
337
338 void Engineer::pointer_motion(int x, int y)
339 {
340         if(main_panel->get_geometry().is_inside(x, y))
341         {
342                 main_panel->pointer_motion(x, y);
343                 return;
344         }
345         for(TrainPanelSeq::iterator i=train_panels.begin(); i!=train_panels.end(); ++i)
346                 if((*i)->get_geometry().is_inside(x, y))
347                 {
348                         (*i)->pointer_motion(x, y);
349                         return;
350                 }
351
352         Track3D *track=pick_track(x, y);
353         if(track && placing_train)
354         {
355                 Block *block=trfc_mgr->get_block_by_track(&track->get_track());
356                 if(block!=placing_block)
357                 {
358                         if(placing_block)
359                                 set_block_color(*placing_block, Color(1, 1, 1));
360                         placing_block=block;
361                         placing_entry=&block->get_endpoints().front();
362                         set_block_color(*placing_block, Color(0.5, 1, 0.7));
363                 }
364         }
365         else if(track && track->get_track().get_turnout_id())
366         {
367                 ostringstream ss;
368                 ss<<"Turnout "<<track->get_track().get_turnout_id();
369                 status_text=ss.str();
370         }
371         else if(!placing_train)
372                 status_text="";
373 }
374
375 void Engineer::view_all()
376 {
377         const Track3DSeq &tracks=layout_3d.get_tracks();
378
379         cam_rot=0;
380         float best_height=-1;
381         float mid_x=0;
382         float mid_y=0;
383         for(float angle=0; angle<M_PI; angle+=0.01)
384         {
385                 float min_x=0;
386                 float max_x=0;
387                 float min_y=0;
388                 float max_y=0;
389                 for(Track3DSeq::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
390                 {
391                         Point minp,maxp;
392                         (*i)->get_bounds(angle, minp, maxp);
393                         min_x=min(min_x, minp.x);
394                         max_x=max(max_x, maxp.x);
395                         min_y=min(min_y, minp.y);
396                         max_y=max(max_y, maxp.y);
397                 }
398
399                 float width=max_x-min_x;
400                 float height=max_y-min_y;
401                 height=max(height, width);
402
403                 if(height<best_height || best_height<0)
404                 {
405                         cam_rot=angle;
406                         best_height=height;
407                         mid_x=(min_x+max_x)/2;
408                         mid_y=(min_y+max_y)/2;
409                 }
410         }
411
412         float c=cos(cam_rot);
413         float s=sin(cam_rot);
414         cam_pos.x=c*mid_x-s*mid_y;
415         cam_pos.y=s*mid_x+c*mid_y;
416         cam_pos.z=max(best_height*1.05/0.82843, 0.15);
417 }
418
419 void Engineer::set_block_color(const Block &block, const Color &color)
420 {
421         const TrackSet &tracks=block.get_tracks();
422         for(TrackSet::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
423                 layout_3d.get_track(*i)->set_color(color);
424 }
425
426 void Engineer::sensor_event(unsigned addr, bool state)
427 {
428         cout<<"sensor_event "<<state<<" @ "<<addr<<'\n';
429         const Track3DSeq &ltracks=layout_3d.get_tracks();
430         for(Track3DSeq::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
431                 if((*i)->get_track().get_sensor_id()==addr)
432                 {
433                         if(state)
434                                 (*i)->set_color(Color(1, 0.5, 0.3));
435                         else
436                                 (*i)->set_color(Color(1, 1, 1));
437                 }
438 }
439
440 void Engineer::block_reserved(const Block &block, const Train *train)
441 {
442         if(Sensor *sensor=control.get_sensor(block.get_sensor_id()))
443                 if(sensor->get_state())
444                         return;
445
446         if(train)
447                 set_block_color(block, Color(1, 1, 0.3));
448         else
449                 set_block_color(block, Color(1, 1, 1));
450 }
451
452 void Engineer::project_3d()
453 {
454         glMatrixMode(GL_PROJECTION);
455         glLoadIdentity();
456         //glFrustum(-0.055228, 0.055228, -0.041421, 0.041421, 0.1, 10);
457         glFrustum(-0.069036, 0.041421, -0.041421, 0.041421, 0.1, 10);
458         glMatrixMode(GL_MODELVIEW);
459 }
460
461 Track3D *Engineer::pick_track(int x, int y)
462 {
463         float xx=((float)(x-(int)screen_w*5/8)/screen_h)*0.82843;
464         //float xx=((float)(x-(int)screen_w/2)/screen_h)*0.82843;
465         float yy=((float)y/screen_h-0.5)*0.82843;
466         float size=(float)4/screen_h*0.82843;
467
468         project_3d();
469         glLoadIdentity();
470         glRotatef(-cam_rot*180/M_PI, 0, 0, 1);
471         glTranslatef(-cam_pos.x, -cam_pos.y, -cam_pos.z);
472
473         return layout_3d.pick_track(xx, yy, size);
474 }
475
476 Application::RegApp<Engineer> Engineer::reg;