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