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