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