1 /*
2  * CRRCsim - the Charles River Radio Control Club Flight Simulator Project
3  *
4  * Copyright (C) 2000, 2001, 2004 Jan Edward Kansky (original author)
5  * Copyright (C) 2004-2010 Jens Wilhelm Wulf
6  * Copyright (C) 2004-2009 Jan Reucker
7  * Copyright (C) 2004 Kees Lemmens
8  * Copyright (C) 2005 Joel Lienard
9  * Copyright (C) 2005 Lionel Cailler
10  * Copyright (C) 2005, 2008 Olivier Bordes
11  * Copyright (C) 2006 Todd Templeton
12  * Copyright (C) 2007, 2008 Chris Bayley
13  * Copyright (C) 2009 Joel Lienard
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License version 2
17  * as published by the Free Software Foundation.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place, Suite 330,
27  * Boston, MA 02111-1307, USA.
28  *
29  */
30 
31 /*****************************************************************************
32 Title: CRRCsim, the Charles River Radio Control Club Flight Simulator Project
33 Authors:
34 Jan Kansky:  Programming
35 Mark Drela:  Aerodynamics
36 
37 Purpose:  The idea for CRRCsim is to take away any last excuse you might
38 have for not using a flight simulator to keep your thumbs certified for RC
39 flying.   It's free, it works, enjoy.  This is an open source project, so if
40 you don't like the way something works, help us fix it!
41 
42 Thanks:
43   Bruce Jackson for the LaRCsim framework.
44   Flight Gear project for the sky sphere.
45 
46 Contacts:
47 If you'd like to help with CRRCSIM, then send me an email!
48     email                : kansky@ll.mit.edu
49 *****************************************************************************/
50 
51 #include "i18n.h"
52 #include <crrc_config.h>
53 
54 #include "global.h"
55 #include "defines.h"
56 #include "crrc_loadair.h"
57 #include "crrc_main.h"
58 #include "crrc_system.h"
59 #include "crrc_sound.h"
60 #include "mod_landscape/crrc_scenery.h"
61 #include "SimStateHandler.h"
62 #include "mod_windfield/windfield.h"
63 #include "GUI/crrc_gui_main.h"
64 #include "GUI/crrc_joy.h"
65 #include "mod_misc/SimpleXMLTransfer.h"
66 #include "mod_misc/lib_conversions.h"
67 #include "config.h"
68 #include "mod_misc/filesystools.h"
69 #include "zoom.h"
70 #include "CTime.h"
71 #include "mod_mode/T_GameHandler.h"
72 #include "mod_mode/F3A/handlerF3A.h"
73 #include "mod_mode/F3F/handlerF3F.h"
74 #include "mod_fdm/formats/airtoxml.h"
75 #include "mod_fdm/xmlmodelfile.h"
76 #include "mod_misc/crrc_rand.h"
77 #include "mod_video/glconsole.h"
78 #include "crrc_fdm.h"
79 #include "mod_misc/ls_constants.h"
80 #include "mod_misc/scheduler.h"
81 #include "aircraft.h"
82 #include "global_video.h"
83 #include "mod_video/crrc_graphics.h"
84 
85 #include "mod_main/eventhandler.h"
86 #include "mod_main/crrc_checkopts.h"
87 
88 #include "mod_inputdev/inputdev_serial2/inputdev_serial2.h"
89 #include "mod_inputdev/inputdev_mnav/inputdev_mnav.h"
90 #include "mod_inputdev/inputdev_audio/inputdev_audio.h"
91 #include "mod_inputdev/inputdev_parallel/inputdev_parallel.h"
92 #include "mod_inputdev/inputdev_software/inputdev_software.h"
93 #include "mod_inputdev/inputdev_serpic/inputdev_serpic.h"
94 #include "mod_inputdev/inputdev_serial/inputdev_serial.h"
95 #include "mod_inputdev/inputdev_zhenhua/inputdev_zhenhua.h"
96 #include "mod_inputdev/inputdev_ct6a/inputdev_ct6a.h"
97 
98 #ifdef linux
99 #include "mod_inputdev/inputdev_rctran/inputdev_rctran.h"
100 #include "mod_inputdev/inputdev_rctran2/inputdev_rctran2.h"
101 #endif
102 
103 #include "record.h"
104 #include "robots.h"
105 #include "mod_video/fonts.h"
106 
107 
108 #include <math.h>
109 #include <string>
110 #include <time.h>
111 
112 //#define LOG_FRAMES
113 
114 /*****************************************************************************/
115 //Configuration Data
116 //TInputDev  inputDev;
117 
118 // Game timing control
119 CTime *crrc_time;
120 
121 // Player position in FDM coordinates
122 CRRCMath::Vector3 player_pos;
123 
124 // Variometer sound
125 T_VariometerSound *vario_sound = NULL;
126 int vario_sound_channel = -1;
127 
128 // Enviroment interface to FDM
129 FDMEnviroment* fdmenv = 0;
130 
131 
132 /*****************************************************************************/
activate_test_mode()133 void activate_test_mode()
134 {
135   TSimInputs inp = TSimInputs();
136 
137   Global::testmode = TRUE;
138 
139   // Set distance to plane to make it visible in a good size,
140   // at an average field of view of 35deg:
141   // tan(fieldOfView/2) * distance = aircraftSize
142   // with aircraftSize approximately equal to half model size.
143   // Take some 30% margin to allow for model movement.
144   float fieldOfView = 35.0;
145   float aircraftSize = Global::aircraft->getModel()->getAircraftSize();
146   float distance = 1.3*aircraftSize/tan(M_PI/180.0*0.5*fieldOfView);
147 
148   // enter testmode
149   CRRCMath::Vector3 ppos = Global::scenery->getPlayerPosition();
150   CRRCMath::Vector3 planepos(ppos.r[0] - distance, ppos.r[1], ppos.r[2]);
151   Global::aircraft->enterTestmode(planepos);
152   initialize_flight_model();
153   Global::aircraft->getFDMInterface()->update(&inp, 0, 0);
154   Video::UpdateCamera(1); //Only once here to initialize. Then, the camera is still
155 }
156 
leave_test_mode()157 void leave_test_mode()
158 {
159   Global::testmode = FALSE;
160   Global::aircraft->leaveTestmode();
161 }
162 
163 /*****************************************************************************/
164 
165 /** \brief Calculate Z rotation for simulated SAL
166  *
167  *  The angular velocity is calculated using an estimated
168  *  rotation radius for the C-of-G of 0.8 m (stretched arm)
169  *  plus half the wingspan. The value is negative, so a right-hand
170  *  SAL is simulated.
171  *
172  *  \param vel relative launch velocity
173  *
174  *  \return body rotation around Z axis in rad/s
175  */
calculate_z_rotation(double vel)176 double calculate_z_rotation(double vel)
177 {
178   double radius = (0.8 / 0.3048) + (Global::aircraft->getFDM()->getWingspan() / 2.0);
179   double velocity = vel * Global::aircraft->getFDM()->getTrimmedFlightVelocity(); // ft/s
180   return (-1 * velocity / radius);
181 }
182 
183 
initialize_flight_model()184 void initialize_flight_model()
185 {
186   float Altitude;
187 
188   double velocity_rel = cfgfile->getDouble("launch.velocity_rel", 1);
189   double dZRot = 0.0;
190 
191   if (cfgfile->getInt("launch.sal", 0) == 1)
192   {
193     dZRot = calculate_z_rotation(velocity_rel);
194   }
195   double wind_direction = (cfg->wind->getDirection()*M_PI/180);
196   double posX, posY;
197 
198   int StartFromPlayer = cfgfile->getInt("launch.rel_to_player", 1);
199   std::string CurrentStartPositionName = cfg->getCurLocCfgPtr(cfgfile)->getString("start.position","");
200   if (Global::scenery->getNumStartPosition() == 0)
201     StartFromPlayer = 1;
202 
203   if (StartFromPlayer == 1)
204   {
205     // default relative position is similar to what has been used on original 'Cape Cod' and 'Davis':
206     double launchx = cfgfile->getDouble("launch.rel_front", MODELSTART_REL_FRONT);
207     double launchy = cfgfile->getDouble("launch.rel_right", MODELSTART_REL_RIGHT);
208     posX = -player_pos.r[2] + launchx*cos(wind_direction) - launchy*sin(wind_direction);
209     posY =  player_pos.r[0] + launchx*sin(wind_direction) + launchy*cos(wind_direction);
210   }
211   else
212   {
213     CRRCMath::Vector3 start_pos = Global::scenery->getStartPosition(CurrentStartPositionName);
214     posX = -start_pos.r[2];
215     posY =  start_pos.r[0];
216   }
217   double phi,theta,psi,height;
218   float plane[4];
219   phi = 0;
220   theta = cfgfile->getDouble("launch.angle", 0);
221   psi = wind_direction;
222   Altitude = cfgfile->getDouble("launch.altitude", 6);
223   double zlow = Global::aircraft->getFDM()->getZLow();
224   height = Global::scenery->getHeightAndPlane(posX, posY, plane);
225   if(Altitude == 0)
226     //start on ground : calculate phi et theta so that the airplane is parallel to the ground
227     {
228     double p_n = -plane[2];//north
229     double p_e = plane[0];//est
230     double p_u = plane[1];//up
231     double h1 = -(plane[3]+p_n*(posX+cos(psi)) + p_e*(posY+sin(psi)) )/p_u;
232     double h2 = -(plane[3]+p_n*(posX+sin(psi)) + p_e*(posY+cos(psi)) )/p_u;
233     //double h0 = -(plane[3]+p_n*posX + p_e*posY )/p_u;
234    theta =atan(h1-height);
235     phi = -atan(h2-height);
236     zlow = zlow/cos(theta);
237     zlow = zlow/cos(phi);
238     //printf ("START theta : %.1f phi: %.1f \n",theta, phi);////
239     //printf ("START h: %.1f h0: %.1f h1: %.1f h2: %.1f \n",height,h0,h1,h2);
240     }
241   Altitude = Altitude + zlow + height;
242   printf ("START ALTITUDE : %.1f (%.1f+%.1f)\n",Altitude,zlow,height );////
243   Global::aircraft->getFDMInterface()->initAirplaneState(
244                                  velocity_rel,
245                                  phi,
246                                  theta,
247                                  psi,
248                                  posX,
249                                  posY,
250                                  -1*Altitude,
251                                  0.0,
252                                  0.0,
253                                  dZRot);
254 
255   Global::Simulation->resume();
256 
257   {
258     SimpleXMLTransfer* header = Global::gameHandler->GetRecordHeader();
259     SimpleXMLTransfer* config = Global::aircraft->GetLatestConfig();
260     header->setAttribute("airplane.file",     config->getString("airplane.file"));
261     header->setAttribute("airplane.graphics", config->getString("airplane.graphics", "0"));
262     Global::recorder->Start(header);
263     delete header;
264   }
265 }
266 
267 /*****************************************************************************/
268 
Init_mod_windfield()269 void Init_mod_windfield()
270 {
271   initialize_wind_field(cfg->getCurLocCfgPtr(cfgfile));
272 
273   // Check if we're using the standard slope windfield
274   // or the dynamic soaring windfield
275   cfg->checkDynamicSoaring();
276 }
277 
278 /*****************************************************************************/
279 
280 /**
281  * Reverts to mouse input.
282  */
input_method_failed(std::string msg,bool boRevertToMouse=true)283 std::string input_method_failed(std::string msg, bool boRevertToMouse = true)
284 {
285   if (msg.length())
286     msg += "\n";
287 
288   msg += _("Failed to initialize selected input method.\n");
289   if (boRevertToMouse)
290   {
291     msg += _("Reverting to mouse input.");
292 
293     // close previous interface
294     if (Global::TXInterface != (T_TX_Interface*)0)
295     {
296       delete Global::TXInterface;
297       Global::TXInterface = (T_TX_Interface*)0;
298     }
299 
300     // select and open mouse interface
301     cfgfile->setAttributeOverwrite("inputMethod.method", Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_mouse]);
302 
303     Global::TXInterface  = new T_TX_InterfaceSoftware(T_TX_Interface::eIM_mouse, 2);
304     if (Global::TXInterface->init(cfgfile))
305       msg += "\nEven mouse input failed:\n" + Global::TXInterface->getErrMsg();
306   }
307 
308   return(msg);
309 }
310 
311 /**
312  * (Re)configures the input method according to the configuration
313  * in <tt>cfgfile</tt>.
314  * Returns a message, if something (bad) happened.
315  */
reconfigureInputMethod(bool boRevertToMouse)316 std::string reconfigureInputMethod(bool boRevertToMouse)
317 {
318   printf("std::string reconfigureInputMethod()\n");
319 
320   std::string method = strU(cfgfile->getString(
321         "inputMethod.method",
322         Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_mouse]));
323   std::string msg;
324 
325   printf("New input method: %s\n", method.c_str());
326 
327   // stop old input method
328   if (Global::TXInterface != (T_TX_Interface*)0)
329   {
330     Global::inputDev->closeJoystick();
331     delete Global::TXInterface;
332     Global::TXInterface = (T_TX_Interface*)0;
333   }
334 
335   // create new one
336   if (method.compare(
337       strU(Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_keyboard])) == 0)
338   {
339     Global::TXInterface  = new T_TX_InterfaceSoftware(T_TX_Interface::eIM_keyboard, 4);
340     Global::TXInterface->init(cfgfile);
341   }
342   else if (method.compare(
343       strU(Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_mouse])) == 0)
344   {
345     Global::TXInterface  = new T_TX_InterfaceSoftware(T_TX_Interface::eIM_mouse, 2);
346     Global::TXInterface->init(cfgfile);
347   }
348   else if (method.compare(
349       strU(Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_joystick])) == 0)
350   {
351 #if TEST_WITHOUT_JOYSTICK == 0
352     msg = Global::inputDev->openJoystick();
353     if (msg.length())
354       return(input_method_failed(msg, boRevertToMouse));
355 #endif
356 
357     Global::TXInterface  = new T_TX_InterfaceSoftware(T_TX_Interface::eIM_joystick, Global::inputDev->getJoystickNumAxes());
358     Global::TXInterface->init(cfgfile);
359   }
360   else if (method.compare(
361       strU(Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_audio])) == 0)
362   {
363     #if PORTAUDIO > 0
364     Global::TXInterface  = new T_TX_InterfaceAudio();
365     if (Global::TXInterface->init(cfgfile))
366       return(input_method_failed(Global::TXInterface->getErrMsg(), boRevertToMouse));
367     #else
368       return(input_method_failed("This version of CRRCsim was built without support for the AUDIO interface.\n", boRevertToMouse));
369     #endif
370   }
371 #ifdef linux
372   else if (method.compare(
373       strU(Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_rctran])) == 0)
374   {
375     Global::TXInterface  = new T_TX_InterfaceRCTRAN();
376     if (Global::TXInterface->init(cfgfile))
377       return(input_method_failed(Global::TXInterface->getErrMsg(), boRevertToMouse));
378   }
379   else if (method.compare(
380       strU(Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_rctran2])) == 0)
381   {
382     Global::TXInterface  = new T_TX_Interface_RCTran2();
383     if (Global::TXInterface->init(cfgfile))
384       return(input_method_failed(Global::TXInterface->getErrMsg(), boRevertToMouse));
385   }
386 #endif
387   else if (method.compare(
388       strU(Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_parallel]))  == 0)
389   {
390     printf("Trying to init parallel\n");
391     Global::TXInterface  = new T_TX_InterfaceParallel();
392     if (Global::TXInterface->init(cfgfile))
393       return(input_method_failed(Global::TXInterface->getErrMsg(), boRevertToMouse));
394   }
395   else if (method.compare(
396       strU(Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_serial2])) == 0)
397   {
398     Global::TXInterface  = new T_TX_InterfaceSerial2();
399     if (Global::TXInterface->init(cfgfile))
400       return(input_method_failed(Global::TXInterface->getErrMsg(), boRevertToMouse));
401   }
402   else if (method.compare(
403       strU(Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_mnav])) == 0)
404   {
405     Global::TXInterface  = new T_TX_InterfaceMNAV();
406     if (Global::TXInterface->init(cfgfile))
407       return(input_method_failed(Global::TXInterface->getErrMsg(), boRevertToMouse));
408   }
409   else if (method.compare(
410       strU(Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_serpic])) == 0)
411   {
412     Global::TXInterface  = new T_TX_InterfaceSerPIC();
413     if (Global::TXInterface->init(cfgfile))
414       return(input_method_failed(Global::TXInterface->getErrMsg(), boRevertToMouse));
415   }
416   else if (method.compare(
417       strU(Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_zhenhua])) == 0)
418   {
419     Global::TXInterface  = new T_TX_InterfaceZhenHua();
420     if (Global::TXInterface->init(cfgfile))
421       return(input_method_failed(Global::TXInterface->getErrMsg(), boRevertToMouse));
422   }
423   else if (method.compare(
424       strU(Global::inputDev->InputMethodStrings[T_TX_Interface::eIM_CT6A])) == 0)
425   {
426     Global::TXInterface  = new T_TX_InterfaceCT6A();
427     if (Global::TXInterface->init(cfgfile))
428       return(input_method_failed(Global::TXInterface->getErrMsg()));
429   }
430   else
431     return(input_method_failed("", boRevertToMouse));
432 
433   return("");
434 }
435 
436 /**
437  * Reading of (mostly initial) values from the xml config into
438  * global variables (at least global to this file).
439  * Maybe those variables can be encapsulated somewhere else...
440  */
read_config_into_globals()441 void read_config_into_globals()
442 {
443   zoom_reset();
444   Global::training_mode = cfgfile->getInt("training_mode.fUse", 0);
445   Global::wind_mode = cfgfile->getInt("wind_mode.fUse", 2);
446   Global::nVerbosity = cfgfile->getInt("nVerbosity.level", 0);
447   Global::HUDCompass = cfgfile->getInt("HUDCompass.fUse", 0);
448   Global::windVectors = cfgfile->getInt("windVectors.fUse", 0);
449   Global::modelView = cfgfile->getInt("modelViewWindow.fUse", 2);
450   Global::dt = cfgfile->getDouble("simulation.flightModel.dt", 0.002777);
451   Global::slowMotion = cfgfile->getInt("simulation.slowMotion", 0);
452   Global::slowTimeScale = cfgfile->getDouble("simulation.slowTimeScale", 2.0);
453   Video::read_config(cfgfile);
454 
455   // round dt to the integer millisecond, as this allow for
456   // maximum display smoothness (see CTime.cpp)
457   Global::dt = (int)(Global::dt*1000 + 0.5)/1000.;
458 }
459 
write_globals_into_config()460 void write_globals_into_config()
461 {
462   cfgfile->setAttributeOverwrite("training_mode.fUse", Global::training_mode);
463   cfgfile->setAttributeOverwrite("wind_mode.fUse", Global::wind_mode);
464   cfgfile->setAttributeOverwrite("nVerbosity.level", Global::nVerbosity);
465   cfgfile->setAttributeOverwrite("HUDCompass.fUse", Global::HUDCompass);
466   cfgfile->setAttributeOverwrite("windVectors.fUse", Global::windVectors);
467   cfgfile->setAttributeOverwrite("modelViewWindow.fUse", Global::modelView);
468   cfgfile->setAttributeOverwrite("simulation.flightModel.dt",
469                                  doubleToString(Global::dt));
470   cfgfile->setAttributeOverwrite("simulation.slowMotion", Global::slowMotion);
471   cfgfile->setAttributeOverwrite("simulation.slowTimeScale",
472                                  doubleToString(Global::slowTimeScale));
473 }
474 
475 /*****************************************************************************/
initializeRandomNumberGenerator()476 void initializeRandomNumberGenerator()
477 {
478   time_t sometime = time(0);
479   srand((unsigned int) sometime);
480   CRRC_Random::insertData(rand());
481   std::cout << "RAND_MAX = " << RAND_MAX << "\n";
482 }
483 
484 /*****************************************************************************/
485 /**
486  *  This function tries to perform some cleanup when
487  *  exiting. If errmsg != NULL, a system message box
488  *  will be opened with the message string.
489  *
490  *  \param exit_code should be CRRC_EXIT_SUCCESS or CRRC_EXIT_FAILURE
491  *  \param errmsg    optional message to be displayed on exit
492  */
crrc_exit(int exit_code,const char * errmsg)493 void crrc_exit(int exit_code, const char *errmsg)
494 {
495   // ToDo: clean up allocated objects
496   SDL_Quit();
497 
498   if ((errmsg != NULL) && (*errmsg != '\0'))
499   {
500     if (exit_code == CRRC_EXIT_SUCCESS)
501     {
502       SystemMessage(errmsg, SM_INFO);
503     }
504     else
505     {
506       SystemMessage(errmsg, SM_ERROR);
507     }
508   }
509 
510   exit(exit_code);
511 }
512 
513 /**
514  * Description: see header file
515  */
loadAirplane()516 void loadAirplane()
517 {
518   Global::aircraft->load(cfgfile, fdmenv);
519 }
520 
521 
set_aux(int aux_num,int setting)522 void set_aux(int aux_num, int setting)
523 {
524   if (aux_num <= 0 || aux_num > TSimInputs::NUM_AUX_INPUTS)
525     return;
526   switch(setting)
527   {
528     case 1:
529       Global::inputs.aux[aux_num - 1] = -0.5;
530       break;
531     case 2:
532       Global::inputs.aux[aux_num - 1] =  0.0;
533       break;
534     case 3:
535       Global::inputs.aux[aux_num - 1] =  0.5;
536       break;
537     default:
538       break;
539   }
540 }
541 
542 /** Raise an event containing the actual input values
543  *
544  *  \param inputs   input value structure with current values
545  */
raiseInputEvent(TSimInputs const & inputs)546 void raiseInputEvent(TSimInputs const& inputs)
547 {
548   static AxisUpdateEvent event;
549 
550   event.set(inputs.aileron,
551             inputs.elevator,
552             inputs.rudder,
553             inputs.throttle,
554             inputs.flap,
555             inputs.spoiler,
556             inputs.retract,
557             inputs.pitch);
558   EventDispatcher::getInstance()->raise(&event);
559 }
560 /*****************************************************************************
561 *
562 * try to load the configured scenery. On error case, load default scenery
563 *
564 **/
load_initial_scenery(T_Config * cfg)565 void load_initial_scenery(T_Config *cfg)
566 {
567   std::string sceneryfile = cfg->getLocationName();
568   int sky_variant = cfg->getSkyVariant();
569   Global::scenery = loadScenery(FileSysTools::getDataPath(sceneryfile).c_str(),
570                                 sky_variant);
571   if (Global::scenery == NULL)
572   {
573     fprintf(stderr, "Unable to initialize scenery from file %s,\n",
574                         sceneryfile.c_str());
575     fprintf(stderr, "reverting to default scenery \"scenery/davis-orig.xml\"\n");
576 
577     // try the default scenery
578     Global::scenery = loadScenery(FileSysTools::getDataPath("scenery/davis-orig.xml").c_str());
579     if (Global::scenery == NULL)
580     {
581       std::string s;
582       s = "Unable to initialize default scenery from file \"scenery/davis-orig.xml\"";
583       crrc_exit(CRRC_EXIT_FAILURE, s.c_str());
584     }
585     else
586     {
587       // set configuration file back to default
588       cfg->setLocation("scenery/davis-orig.xml", cfgfile);
589     }
590   }
591   // re-read the wind configuration to activate any scenery defaults if
592   // no wind config is present in the cfgfile for this location
593   cfg->wind->read(cfgfile, cfg);
594 }
595 
596 
597 
598 /*****************************************************************************/
main(int argc,char ** argv)599 int main(int argc,char **argv)
600 {
601   float field_of_view;
602 
603   if (crrc_checkversionopt(argc, argv))
604   {
605     crrc_exit(CRRC_EXIT_SUCCESS);
606   }
607 
608   try
609   {
610     // Internationalisation
611     char *loc = setlocale (LC_MESSAGES, "");
612     // getDataPath only looks for file not folder, so the following does not work
613     //string locale_dir =  FileSysTools::getDataPath("locale");
614     //printf("dir_locale: %s\n", locale_dir.c_str());
615     //bindtextdomain ("crrcsim",locale_dir.c_str());
616     bindtextdomain ("crrcsim","./locale/");
617     textdomain ("crrcsim");
618     //Plib does not work with UTF-8. It is necessary to make a 8bits transcoding
619     bind_textdomain_codeset ("crrcsim","iso8859-15");//Temporarily, before obtaining the correct codeset
620     string codeset = _("iso8859-15");//return correct codeset
621     bind_textdomain_codeset ("crrcsim",codeset.c_str());
622     printf("Locale : %s Internal codeset: %s\n",loc, codeset.c_str());
623     Video::codeSet = codeset;
624     Global::TXInterface = (T_TX_Interface*)0;
625 
626     Global::inputDev = new TInputDev();
627     FileSysTools::SetAppname("crrcsim");
628     Global::testmode = FALSE;
629 
630     Global::Simulation = new SimStateHandler();
631 
632     Global::aircraft = new Aircraft();
633 
634     std::string startup_time;
635     getSystemTimeString(startup_time);
636     printf("CRRCsim %s started at %s\n", PACKAGE_VERSION, startup_time.c_str());
637     printf("Running on %s\n", getOSVersionString());
638     printf("Using plib version %d.%d.%d\n", PLIB_MAJOR_VERSION,
639                                             PLIB_MINOR_VERSION,
640                                             PLIB_TINY_VERSION);
641     printf("Compiled with SDL version %d.%d.%d\n",  SDL_MAJOR_VERSION,
642                                                     SDL_MINOR_VERSION,
643                                                     SDL_PATCHLEVEL);
644     printf("(Linked SDL version is %d.%d.%d)\n",  SDL_Linked_Version()->major,
645                                                   SDL_Linked_Version()->minor,
646                                                   SDL_Linked_Version()->patch);
647 #ifdef CRRC_DATA_PATH
648     printf("Configured data path: %s\n", CRRC_DATA_PATH);
649 #endif
650 
651     {
652       std::vector<std::string> search_path;
653 
654       FileSysTools::getSearchPathList(search_path, "");
655       std::cout << "Data file search path:" << std::endl;
656       for (std::vector<std::string>::size_type i = 0; i < search_path.size(); i++)
657       {
658         std::cout << "  " << search_path[i] << std::endl;
659       }
660     }
661 
662     initializeRandomNumberGenerator();
663 
664     {
665       unsigned int SDLFlags = SDL_INIT_JOYSTICK;
666       int nRetCodeCmdline;
667       int i;
668 
669       try
670       {
671         // ***** Read configuration, parse commandline... ***********************
672         for (i = 1; i < argc - 1; i++)
673         {
674           if (!strcmp(argv[i], "-g"))
675             T_Config::putConfigFilePath(argv[i+1]);
676         }
677 
678         cfg = new T_Config(cfgfile); // This will also set up cfgfile
679         cfg->read(cfgfile);
680 
681         {
682           int nNum = air_to_xml();
683           if (nNum != 0)
684           {
685             // todo: files have been converted; issue message.
686           }
687         }
688 
689         Global::inputDev->init(cfgfile);
690         fdmenv = new CRRC_FDM_Env(cfgfile);
691 
692         // command line options override settings read from the config file
693         nRetCodeCmdline = crrc_checkopts(argc, argv, cfgfile, cfg);
694 
695         if (nRetCodeCmdline)
696           crrc_exit(CRRC_EXIT_FAILURE);
697 
698         // must be after crrc_checkopts because crrc_checkopts can change
699         // video.enabled and sound.enabled based on command line options
700         if (cfgfile->getInt("video.enabled", 1))
701           SDLFlags |= SDL_INIT_VIDEO;
702         if (cfgfile->getInt("sound.enabled", 1))
703           SDLFlags |= SDL_INIT_AUDIO;
704         SDL_Init(SDLFlags);
705         SDL_EnableUNICODE(1); // We need this to pass keys to pui
706         SDL_EnableKeyRepeat(50, 150);
707 
708         Global::recorder = new FlightRecorder(FileSysTools::getHomePath());
709         Global::robots = new Robots();
710 
711         read_config_into_globals();
712 
713         std::string msg = reconfigureInputMethod();
714         if (msg.length())
715           printf("%s", msg.c_str());
716 
717         // ***** Video setup ****************************************************
718         if (cfgfile->getInt("video.enabled", 1))
719         {
720           Video::setupScreen(0, 0, 0);
721           Video::setWindowTitleString();
722         }
723 
724         // ***** Sound **********************************************************
725         if (cfgfile->getInt("sound.enabled", 1))
726         {
727           try
728           {
729             Global::soundserver = new CRRCAudioServer(cfgfile);
730             // init the variometer sound
731             cfgfile->makeSureAttributeExists("sound.variometer.vol", "0");
732             int vario_sound_volume = cfgfile->getInt("sound.variometer.vol") << 3;
733             vario_sound = new T_VariometerSound(Global::soundserver->getAudioSpec());
734             vario_sound_channel = Global::soundserver->playSample((T_SoundSample*)vario_sound, vario_sound_volume);
735             soundUpdate3D(0.0, 0.0, 0.0, 0.0);
736 
737             Global::soundserver->pause();
738           }
739           catch (std::runtime_error& e)
740           {
741             fprintf(stderr, "%s\n", e.what());
742             Global::soundserver = (CRRCAudioServer*)0;
743           }
744         }
745         else
746           Global::soundserver = (CRRCAudioServer*)0;
747 
748         // ***** Video **********************************************************
749 
750         Video::initialize_scenegraph();
751         // Temporarily empty scenery.
752         // It allows to have a graphic context to display a message
753         // during the load of the configured scenery
754         Global::scenery = new SceneryNull();
755 
756         Init_mod_windfield();
757 
758         player_pos = Global::scenery->getPlayerPosition();
759         // Create invisible GUI
760         if (cfgfile->getInt("video.enabled", 1))
761           Global::gui = new CGUIMain(false);
762         else
763           Global::gui = NULL;
764 
765         //initialise generic game mode
766         Global::gameHandler= new T_GameHandler();
767 
768         // load airplane
769         {
770           bool airplane_failed = false;
771           try
772           {
773             // load the airplane specified in the config file
774             loadAirplane();
775             initialize_flight_model();
776           }
777           catch (std::runtime_error& e)
778           {
779             fprintf(stderr, "%s\n", e.what());
780             airplane_failed = true;
781           }
782           if (airplane_failed)
783           {
784             // Failed to load airplane file.
785             // Using some fallback.
786             cfgfile->setAttributeOverwrite("airplane.file", FileSysTools::getDataPath("models/allegro.xml"));
787             try
788             {
789               loadAirplane();
790               initialize_flight_model();
791             }
792             catch (std::runtime_error& e)
793             {
794               std::string s = "Unable to load airplane file:\n";
795               s += e.what();
796               fprintf(stderr, "%s\n", s.c_str());
797               crrc_exit(CRRC_EXIT_FAILURE, s.c_str());
798             }
799           }
800         }
801       }
802       catch (XMLException e)
803       {
804         std::string s = "XMLException: ";
805         s += e.what();
806         fprintf(stderr, "%s\n", s.c_str());
807         crrc_exit(CRRC_EXIT_FAILURE, s.c_str());
808       }
809     }
810 
811     if (Global::soundserver != (CRRCAudioServer*)0)
812     {
813       Global::soundserver->pause(false);
814     }
815     Global::Simulation->reset();
816 
817     //~ nVerbosity = 3;
818     crrc_time = new CTime(cfgfile);
819 
820     Video::initConsole();
821 
822 #ifdef LOG_FRAMES
823     FILE* fp = fopen("frames.dat", "w");
824     if (fp == NULL)
825     {
826       crrc_exit(CRRC_EXIT_FAILURE, "Failed.");
827     }
828 #endif
829    //load configured scenery or default scenery
830     load_initial_scenery(cfg);
831     cfg->read(cfgfile);
832     if (cfgfile->getInt("video.enabled", 1))
833     {
834       Video::setWindowTitleString();
835     }
836     player_pos = Global::scenery->getPlayerPosition();
837     Init_mod_windfield();
838 
839     //initialise f3a game mode
840     if (cfgfile->getInt("game.f3a.enabled",0))
841     {
842       delete Global::gameHandler;
843       Global::gameHandler= new HandlerF3A();
844     }
845 
846     //initialise f3f game mode
847     if (cfgfile->getInt("game.f3f.enabled",0))
848     {
849       delete Global::gameHandler;
850       Global::gameHandler= new HandlerF3F();
851     }
852 
853     Global::Simulation->reset();
854 
855     Scheduler scheduler;
856     EventHandler eventHandler(&scheduler);
857 
858     while (Global::Simulation->getState() != STATE_EXIT)
859     {
860       scheduler.Run();
861 
862       Global::TXInterface->getInputData(&Global::inputs);
863       raiseInputEvent(Global::inputs);
864 
865       if (Global::training_mode)
866       {
867         Global::inputs.heli_fixed_z = -Global::scenery->getPlayerPosition().r[1];
868       }
869       else
870       {
871         Global::inputs.heli_fixed_z = EOM01_FIXED_Z_OFF;
872       }
873 
874       Global::Simulation->doIdle(&Global::inputs, crrc_time->update());
875 
876       Global::inputs.ClearKeys();
877 
878       // random data
879       {
880         CRRC_Random::insertData(SDL_GetTicks());
881         CRRC_Random::insertData(Global::inputs.getRandNum());
882       }
883 
884       // get aircraft position from FDM
885       CRRCMath::Vector3 aircraft_pos = Video::FDM2Graphics(Global::aircraft->getPos());
886       float distance_to_model = (aircraft_pos - player_pos).length();
887 
888       field_of_view = zoom_calc(distance_to_model);
889       if (Global::gui)
890       {
891         Video::adjust_zoom(field_of_view);
892       }
893 
894       #if 0
895       Global::verboseString += " X: " + ftoStr(vFdmPos.r[0], 2, 2, true, false);
896       Global::verboseString += " Y: " + ftoStr(vFdmPos.r[1], 2, 2, true, false);
897       Global::verboseString += " Z: " + ftoStr(vFdmPos.r[2], 2, 2, true, false);
898       Global::verboseString += " Phi: " + ftoStr(Global::aircraft->getFDM()->getPhi() * SG_RADIANS_TO_DEGREES, 2, 2, true, false);
899       Global::verboseString += " Theta: " + ftoStr(Global::aircraft->getFDM()->getTheta() * SG_RADIANS_TO_DEGREES, 2, 2, true, false);
900       Global::verboseString += " Psi: " + ftoStr(Global::aircraft->getFDM()->getPsi() * SG_RADIANS_TO_DEGREES, 2, 2, true, false);
901       if (Global::gui)
902         Global::gui->setVerboseText(Global::verboseString.c_str());
903       #else
904       switch (Global::nVerbosity)
905       {
906        case 3:
907         Global::verboseString += "FPS: " + itoStr(crrc_time->getGameFPS(), ' ', 1) + " ";
908         //fallthrough
909        case 2:
910         Global::verboseString += "FoV: " + ftoStr(field_of_view, 2, 1, false, false);
911         //fallthrough
912        case 1:
913         {
914           int NrOfMixers = T_TX_Mixer::NUM_MIXERS;
915           int mixer_on = false;
916           std::string drate = "OFF";
917           std::string mixers = "";
918 
919           if (Global::TXInterface->mixer->enabled)
920           {
921             if (Global::TXInterface->mixer->dr_enabled)
922               drate = "ON";
923 
924             for (int n=0; n<NrOfMixers; n++)
925               if (Global::TXInterface->mixer->mixer_enabled[n])
926               {
927                 if (mixer_on)
928                   mixers += ",";
929                 mixers += itoStr(n+1, ' ', 1);
930                 mixer_on = true;
931               }
932           }
933           if (!mixer_on)
934             mixers += "-";
935 
936           Global::verboseString +=
937               "\nAil: " + ftoStr(Global::inputs.aileron,  2, 2, true, false)
938             + " Ele: "  + ftoStr(Global::inputs.elevator, 2, 2, true, false)
939             + " Rud: "  + ftoStr(Global::inputs.rudder,   2, 2, true, false)
940             + " Thr: "  + ftoStr(Global::inputs.throttle, 2, 2, true, false)
941             + " | D/r: " + drate
942             + "\nFlp: " + ftoStr(Global::inputs.flap,     2, 2, true, false)
943             + " Spo: "  + ftoStr(Global::inputs.spoiler,  2, 2, true, false)
944             + " Ret: "  + ftoStr(Global::inputs.retract,  2, 2, true, false)
945             + " Pit: "  + ftoStr(Global::inputs.pitch,    2, 2, true, false)
946             + " | Mix: " + mixers;
947         }
948         if (Global::gui)
949           Global::gui->setVerboseText(Global::verboseString.c_str());
950         else
951         {
952           static int verbose_print_c = 0;
953           if (++verbose_print_c >= 30)
954           {
955             Global::verboseString += "\n";
956             std::cout << Global::verboseString;
957             verbose_print_c = 0;
958           }
959         }
960         break;
961 
962        default:
963         if (Global::gui)
964         {
965           Global::gui->setVerboseText("");
966         }
967         break;
968       }
969       #endif
970 
971       if (Global::gui)
972       {
973         Global::gui->doHUDCompass(field_of_view);
974       }
975 
976       if (Global::gui)
977       {
978         Video::display();
979       }
980       Global::verboseString = "";
981 
982 #ifdef LOG_FRAMES
983       fprintf(fp, "%lu\n", (unsigned long)SDL_GetTicks());
984 #endif
985 
986       // sound calculations
987       if (Global::soundserver != (CRRCAudioServer*)0)
988       {
989         soundUpdate3D(distance_to_model,
990                       Global::aircraft->getFDM()->getPropFreq(),
991                       aircraft_pos.r[1],
992                       Global::aircraft->getFDM()->getVRelAirmass()/Global::aircraft->getFDM()->getTrimmedFlightVelocity());
993       }
994     }
995 #ifdef LOG_FRAMES
996     fclose(fp);
997 #endif
998 
999     Global::recorder->Stop();
1000   }
1001   catch (std::exception& e)
1002   {
1003     std::string s = "Caught exception: ";
1004     s += e.what();
1005     crrc_exit(CRRC_EXIT_FAILURE, s.c_str());
1006   }
1007 
1008   delete fdmenv;
1009   if (vario_sound != (T_VariometerSound*)0)
1010   {
1011     Global::soundserver->stopChannel(vario_sound_channel);
1012     delete vario_sound;
1013   }
1014   delete Global::aircraft;
1015   delete Global::scenery;
1016   Video::cleanup();
1017   if (Global::soundserver != (CRRCAudioServer*)0)
1018   {
1019     delete Global::soundserver;
1020   }
1021   delete Global::Simulation;
1022 
1023   crrc_exit(CRRC_EXIT_SUCCESS, NULL);
1024 
1025   // crrc_exit() will never return, keep the compiler happy anyway:
1026   return 0;
1027 }
1028 
1029