Skyscraper 2.0
skyscraper.cpp
Go to the documentation of this file.
1/*
2 Skyscraper 2.0 Alpha - Simulation Frontend
3 Copyright (C)2003-2024 Ryan Thoryk
4 https://www.skyscrapersim.net
5 https://sourceforge.net/projects/skyscraper/
6 Contact - ryan@skyscrapersim.net
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21*/
22
23#ifdef USING_WX
24#include "wx/wx.h"
25#include "wx/dir.h"
26#include "wx/cmdline.h"
27#include "wx/filename.h"
28#include "wx/filefn.h"
29#include "wx/stdpaths.h"
30#include "wx/joystick.h"
31#else
32#include <filesystem>
33#include "Ogre.h"
34#include "OgreApplicationContext.h"
35#include "OgreInput.h"
36#include "OgreRTShaderSystem.h"
37#include "OgreCameraMan.h"
38#endif
39#include <locale>
40#include <time.h>
41#include <thread>
42#include "globals.h"
43#include "sbs.h"
44#include "camera.h"
45#include "skyscraper.h"
46#include "vm.h"
47#include "gui.h"
48#include "hal.h"
49#include "sky.h"
50#include "enginecontext.h"
51#include "scriptproc.h"
52#ifdef USING_WX
53#include "mainscreen.h"
54#endif
55#include "profiler.h"
56#include "startscreen.h"
57
58#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
59#include <sysdir.h> // for sysdir_start_search_path_enumeration
60#include <glob.h> // for glob needed to expand ~ to user dir
61#include <stdio.h>
62#include <sys/sysctl.h>
63#endif
64
65#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
66#include <windows.h>
67#endif
68
69using namespace SBS;
70
71#ifdef USING_WX
72namespace Skyscraper {
73
74IMPLEMENT_APP_NO_MAIN(Skyscraper)
75
76}
77#endif
78
79#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
80#if OGRE_CPU != OGRE_CPU_ARM
81#ifdef USING_WX
82#include "uexception.h"
83#endif
84#endif
85#endif
86
87#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
88
89//code to get Application Support folder on Mac
90
91std::string expandTilde(const char* str) {
92 if (!str) return {};
93
94 glob_t globbuf;
95 if (glob(str, GLOB_TILDE, nullptr, &globbuf) == 0) {
96 std::string result(globbuf.gl_pathv[0]);
97 globfree(&globbuf);
98 return result;
99 } else {
100 throw "Unable to expand tilde";
101 }
102}
103
104std::string settingsPath() {
105 char path[PATH_MAX];
106 auto state = sysdir_start_search_path_enumeration(SYSDIR_DIRECTORY_APPLICATION_SUPPORT,
107 SYSDIR_DOMAIN_MASK_USER);
108 if ((state = sysdir_get_next_search_path_enumeration(state, path))) {
109 return expandTilde(path);
110 } else {
111 throw "Failed to get settings folder";
112 }
113}
114
115#endif
116
117namespace Skyscraper {
118
119#ifndef USING_WX
120Skyscraper::Skyscraper() : OgreBites::ApplicationContext("Skyscraper")
121{
122}
123
125{
126 OnInit();
127}
128#endif
129
131{
132#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
133#if OGRE_CPU != OGRE_CPU_ARM
134#ifdef USING_WX
135 //initialize top-level exception handler
137#endif
138#endif
139#endif
140
141 StartupRunning = false;
142 FullScreen = false;
143 Maximized = false;
144 ShowMenu = false;
145 mRenderWindow = 0;
146 window = 0;
147 wireframe = 0;
148
149 //reset input states
150 boxes = false;
151 colliders = false;
152 strafe_left = false;
153 strafe_right = false;
154 float_up = false;
155 float_down = false;
156 spin_up = false;
157 spin_down = false;
158 turn_left = false;
159 turn_right = false;
160 look_up = false;
161 look_down = false;
162 step_forward = false;
163 step_backward = false;
164 freelook = false;
165
166 //reset loop states
167 startscreen_loaded = false;
168
169#ifdef USING_WX
170 parser = 0;
171#else
172 alt_down = false;
173 ctrl_down = false;
174 shift_down = false;
175#endif
176
177 //create VM instance
178 vm = new VM();
179
180#ifdef USING_WX
181 gui = vm->GetGUI();
182#endif
183 startscreen = new StartScreen(this);
184
185 //set working directory
186 SetCWD();
187
188 //set up unhandled exception handler (crash report system)
189#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
190#if OGRE_CPU != OGRE_CPU_ARM
191#ifdef USING_WX
193#endif
194#endif
195#endif
196
197 //set locale to default for conversion functions
198#ifdef OGRE_DEFAULT_LOCALE
199 setlocale(LC_ALL, OGRE_DEFAULT_LOCALE);
200#else
201 setlocale(LC_ALL, "C");
202#endif
203
204bool showconsole = true;
205#ifdef USING_WX
206 //show version number and exit if specified
207 if (parser->Found(wxT("version")) == true)
208 {
209 printf("Skyscraper version %s\n", vm->version_full.c_str());
210 return false;
211 }
212
213 //set verbose mode if specified
214 if (parser->Found(wxT("verbose")) == true)
215 vm->Verbose = true;
216
217 //set CheckScript mode if specified
218 if (parser->Found(wxT("check-script")) == true)
219 vm->CheckScript = true;
220
221 //turn off console if specified on command line
222 if (parser->Found(wxT("no-console")) == true)
223 showconsole = false;
224#endif
225
226 //load config files
227 HAL *hal = vm->GetHAL();
228 hal->LoadConfiguration(vm->data_path, showconsole);
229
230#ifdef USING_WX
231 //Create main window and set size from INI file defaults
232 window = new MainScreen(this, hal->GetConfigInt(hal->configfile, "Skyscraper.Frontend.Menu.Width", 800), hal->GetConfigInt(hal->configfile, "Skyscraper.Frontend.Menu.Height", 600));
233 //AllowResize(false);
234 window->ShowWindow();
235 window->CenterOnScreen();
236 window->Maximize(hal->GetConfigBool(hal->configfile, "Skyscraper.Frontend.Maximized", false));
237
238 vm->SetParent(window);
239#else
240
241 //initialize Ogre
242 OgreBites::ApplicationContext::setup();
243 addInputListener(this);
244
245 //get overlay system if already created
246 Ogre::OverlaySystem *overlay = getOverlaySystem();
247#endif
248
249 //start and initialize abstraction layer
250#ifdef USING_WX
251 if (!hal->Initialize(vm->data_path))
252#else
253 if (!hal->Initialize(vm->data_path, getRoot(), overlay))
254#endif
255 return ReportError("Error initializing HAL");
256
257 Report("");
258 Report("Creating render window...");
259#ifdef USING_WX
260 mRenderWindow = CreateRenderWindow(0, "SkyscraperVR");
261#else
262 mRenderWindow = getRenderWindow();
263#endif
264
265 //load system
266 if (!hal->LoadSystem(vm->data_path, mRenderWindow))
267 return ReportError("Error loading system");
268
269#ifndef __FreeBSD__
270#ifdef USING_WX
271 //set up joystick if available
272 wxJoystick joystick(wxJOYSTICK1);
273 if (!joystick.IsOk())
274 Report("No joystick detected");
275 else
276 {
277 Report("");
278 Report("Joystick detected: " + joystick.GetProductName().ToStdString());
279 Report("Joystick buttons: " + ToString(joystick.GetNumberButtons()));
280 Report("");
281 }
282#endif
283#endif
284 vm->ShowPlatform();
285
286 //autoload a building file if specified
287 std::string filename;
288#ifdef USING_WX
289 if (parser->GetParamCount() > 0)
290 {
291 filename = parser->GetParam(0).ToStdString();
292
293 //strip path from filename
294 wxFileName file (filename);
295 filename = file.GetFullName();
296 }
297 else
298 filename = hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.AutoLoad", "");
299
300 ShowMenu = hal->GetConfigBool(hal->configfile, "Skyscraper.Frontend.Menu.Show", true);
301
302 //turn off menu if specified on command line
303 if (parser->Found(wxT("no-menu")) == true)
304 ShowMenu = false;
305
306 if (hal->GetConfigBool(hal->configfile, "Skyscraper.Frontend.VR", false) == true)
307 ShowMenu = false;
308#else
309 ShowMenu = false;
310
311#endif
312
313 //start console
314 vm->StartConsole();
315#ifdef USING_WX
316 gui->EnableConsole(true);
317#endif
318
319 if (filename != "")
320 return Load(filename);
321
322 if (ShowMenu == true)
323 {
324 StartupRunning = true;
325 StartSound();
326 }
327 else
328 {
329 //or show building selection window if ShowMenu is false
330 return Load(SelectBuilding());
331 }
332
333 return true;
334}
335
337{
338 //clean up
339
340 //cleanup
341 Report("Cleaning up...");
342
343 UnloadSim();
344
345 //cleanup sound
346 vm->GetHAL()->StopSound();
347
348#ifdef USING_WX
349 if (window)
350 window->Destroy();
351 window = 0;
352
353 if (parser)
354 delete parser;
355 parser = 0;
356#endif
357
358 delete startscreen;
359 delete vm;
360
361#ifdef USING_WX
362 return wxApp::OnExit();
363#else
364 return 0;
365#endif
366}
367
369{
370 //unload GUI
371#ifdef USING_WX
372 gui->Unload();
373#endif
374
375 //unload sky system
376 vm->GetSkySystem()->UnloadSky();
377
378 //delete all sim engines
379 vm->DeleteEngines();
380
381 //clear scene
382 vm->GetHAL()->Clear();
383}
384
386{
387 //Main simulator loop
388
391
392 if (vm->unloaded == true)
393 {
394 UnloadSim();
395 vm->unloaded = false;
396 vm->loadstart = false;
397 return true;
398 }
399
400 vm->ProcessConsole();
401
402 //main menu routine
403 if (StartupRunning == true)
404 {
405 bool result = false;
406
407 if (startscreen_loaded == false)
408 {
409#ifdef USING_WX
410 Maximized = window->IsMaximized();
411 if (Maximized == true)
412 window->Maximize(false);
413
414 window->SetClientSize(vm->GetHAL()->GetConfigInt(vm->GetHAL()->configfile, "Skyscraper.Frontend.Menu.Width", 800), vm->GetHAL()->GetConfigInt(vm->GetHAL()->configfile, "Skyscraper.Frontend.Menu.Height", 600));
415 window->Center();
416 window->SetCursor(wxNullCursor);
417#endif
418 }
419
420 result = startscreen->DrawBackground();
421
422 if (result == false)
423 return false;
424
425 result = startscreen->GetMenuInput();
426
427 if (result == false)
428 return false;
429
430 if (vm->loadstart == true)
431 {
432 StopMenu();
433 vm->loadstart = false;
434 return true;
435 }
436
437 if (startscreen_loaded == false)
438 {
439#ifdef USING_WX
440 if (Maximized == true)
441 window->Maximize();
442#endif
443 }
444
445 startscreen_loaded = true;
446
447 //have HAL render frame
448 return vm->GetHAL()->Render();
449 }
450
451#ifdef USING_WX
452 gui->ShowProgress();
453#endif
454
455 //run sim engine instances
456 std::vector<EngineContext*> newengines;
457 int status = vm->Run(newengines);
458
459 //start a new engine if needed
460 if (status == 3)
461 {
462 for (size_t i = 0; i < newengines.size(); i++)
463 {
464 Start(newengines[i]);
465 }
466 }
467
468 if (status == 2 || status == -1)
469 UnloadToMenu();
470
471 //ProfileManager::dumpAll();
472
473 return true;
474}
475
477{
478 //load and start background music
479
480 if (vm->GetHAL()->DisableSound == true)
481 return;
482
483 if (vm->GetHAL()->GetConfigBool(vm->GetHAL()->configfile, "Skyscraper.Frontend.IntroMusic", true) == false)
484 return;
485
486#ifdef USING_WX
487 if (parser->Found(wxT("no-music")) == true)
488 return;
489#endif
490
491 std::string filename = vm->GetHAL()->GetConfigString(vm->GetHAL()->configfile, "Skyscraper.Frontend.IntroMusicFile", "intro.ogg");
492 std::string filename_full = "data/" + filename;
493
494#ifdef USING_WX
495 //check for an intro sound file in the data path location instead
496 if (wxFileExists(vm->data_path + filename_full))
497 filename_full = vm->data_path + filename_full;
498#endif
499
500 Real volume = vm->GetHAL()->GetConfigFloat(vm->GetHAL()->configfile, "Skyscraper.Frontend.IntroMusicVolume", 1.0);
501
502 //fix name clash
503 #undef PlaySound
504
505 //play music
506 vm->GetHAL()->PlaySound(filename_full, volume);
507}
508
509std::string Skyscraper::SelectBuilding(bool native_dialog)
510{
511#ifdef USING_WX
512
513 if (native_dialog == true)
514 return gui->SelectBuildingNative(vm->data_path);
515
516 if (vm->GetHAL()->GetConfigBool(vm->GetHAL()->configfile, "Skyscraper.Frontend.SelectBuildingNative", false) == false)
517 return gui->SelectBuilding(vm->data_path);
518 else
519 return gui->SelectBuildingNative(vm->data_path);
520#else
521 return "";
522#endif
523}
524
525bool Skyscraper::Load(const std::string &filename, EngineContext *parent, const Vector3 &position, Real rotation, const Vector3 &area_min, const Vector3 &area_max)
526{
527 //load simulator and data file
528
529 StopMenu();
530
531 bool result = vm->Load(true, filename, parent, position, rotation, area_min, area_max);
532
533 if (result == false && vm->GetEngineCount() == 1)
534 UnloadToMenu();
535
536 return result;
537}
538
540{
541 if (StartupRunning == true)
542 {
543 startscreen->DeleteButtons();
544 StartupRunning = false;
545 startscreen_loaded = false;
546 }
547}
548
550{
551 //start simulator
552
553 if (!engine)
554 return false;
555
556 ::SBS::SBS *Simcore = engine->GetSystem();
557 HAL *hal = vm->GetHAL();
558
559 if (engine == vm->GetActiveEngine())
560 {
561 //the sky needs to be created before Prepare() is called
562 vm->GetSkySystem()->CreateSky(engine);
563
564 //switch to fullscreen mode if specified
565 bool fullscreen = hal->GetConfigBool(hal->configfile, "Skyscraper.Frontend.FullScreen", false);
566
567#ifdef USING_WX
568 //override fullscreen setting if specified on the command line
569 if (parser->Found(wxT("fullscreen")) == true)
570 fullscreen = true;
571#endif
572
573 if (fullscreen == true)
574 SetFullScreen(true);
575
576#ifdef USING_WX
577 Maximized = window->IsMaximized();
578
579 //resize main window
580 if (FullScreen == false)
581 {
582#if !defined(__WXMAC__)
583 window->SetBackgroundColour(*wxBLACK);
584#endif
585 if (Maximized == false)
586 {
587 window->SetClientSize(hal->GetConfigInt(hal->configfile, "Skyscraper.Frontend.ScreenWidth", 1024), hal->GetConfigInt(hal->configfile, "Skyscraper.Frontend.ScreenHeight", 768));
588 window->Center();
589 }
590 }
591#endif
592 }
593
594 //start simulation
595 if (!vm->StartEngine(engine, hal->mCameras))
596 return false;
597
598 //close progress dialog if no engines are loading
599#ifdef USING_WX
600 if (vm->IsEngineLoading() == false)
601 gui->CloseProgressDialog();
602#endif
603
604 //load control panel
605 if (engine == vm->GetActiveEngine())
606 {
607 bool panel = vm->GetHAL()->GetConfigBool(hal->configfile, "Skyscraper.Frontend.ShowControlPanel", true);
608
609#ifdef USING_WX
610 //override if disabled on the command line
611 if (parser->Found(wxT("no-panel")) == true)
612 panel = false;
613
614 if (panel == true)
615 gui->CreateDebugPanel();
616#endif
617 }
618
619 hal->RefreshViewport();
620
621 //set ambient light
622 if (hal->GetConfigBool(hal->configfile, "Skyscraper.SBS.Lighting", false) == true)
623 {
624 Real value = hal->GetConfigFloat(hal->configfile, "Skyscraper.SBS.AmbientLight", 0.5);
625 hal->GetSceneManager()->setAmbientLight(Ogre::ColourValue(value, value, value));
626 }
627
628 //show frame stats
629 hal->EnableStats(hal->GetConfigBool(hal->configfile, "Skyscraper.Frontend.Stats", true));
630
631 //run simulation
632 Report("Running simulation...");
633 hal->StopSound();
634
635 return true;
636}
637
639{
640 //unload to main menu
641
642 //exit app if ShowMenu is false
643 if (ShowMenu == false)
644 Quit();
645
646 vm->Pause = false;
647 UnloadSim();
648
649 //cleanup sound
650 vm->GetHAL()->StopSound();
651
652#ifdef USING_WX
653 gui->CloseProgressDialog();
654#endif
655
656 //return to main menu
657 SetFullScreen(false);
658
659 vm->ConcurrentLoads = false;
660 vm->SetRenderOnStartup(false);
661
662 StartSound();
663 StartupRunning = true;
664}
665
667{
668 //exit app
669
670#ifdef USING_WX
671 gui->EnableTimer(false);
672
673 wxGetApp().Exit();
674#else
675 OnExit();
676#endif
677}
678
680{
681 vm->GetHAL()->DestroyRenderWindow();
682}
683
685{
686 //enable/disable fullscreen
687
688 FullScreen = enabled;
689#ifdef USING_WX
690 window->ShowFullScreen(FullScreen);
691#endif
692
693#if defined(__WXMSW__)
694 //in Windows, enable double-buffering when switching to fullscreen
695 //to fix framerate drop issues
696 /*if (enabled == true)
697 window->SetDoubleBuffered(true);
698 else
699 window->SetDoubleBuffered(false);*/
700#endif
701}
702
704{
705#ifdef USING_WX
706 window->Raise();
707 window->SetFocus();
708#endif
709}
710
711#ifdef USING_WX
712void Skyscraper::MacOpenFile(const wxString &filename)
713{
714 //support launching app with a building file, on Mac
715
716 if (vm->IsEngineLoading() == true)
717 return;
718
719 if (StartupRunning == true)
720 vm->GetHAL()->StopSound();
721
722 //strip path from filename
723 wxFileName file (filename);
724
725 Load(file.GetFullName().ToStdString());
726}
727#endif
728
730{
731 return vm->data_path;
732}
733
735{
736 return window;
737}
738
740{
741 return vm;
742}
743
744#ifdef USING_WX
745GUI* Skyscraper::GetGUI()
746{
747 return gui;
748}
749#endif
750
751void Skyscraper::Report(const std::string &message)
752{
753 vm->GetHAL()->Report(message, "");
754}
755
756bool Skyscraper::ReportError(const std::string &message)
757{
758 return vm->GetHAL()->ReportError(message, "");
759}
760
761}
static void Reset(void)
Definition profiler.cpp:263
static void Increment_Frame_Counter(void)
Definition profiler.cpp:279
void LoadConfiguration(const std::string data_path, bool show_console)
Definition hal.cpp:952
std::string GetConfigString(Ogre::ConfigFile *file, const std::string &key, const std::string &default_value)
Definition hal.cpp:292
int GetConfigInt(Ogre::ConfigFile *file, const std::string &key, int default_value)
Definition hal.cpp:286
void EnableStats(bool value)
Definition hal.cpp:771
bool LoadSystem(const std::string &data_path, Ogre::RenderWindow *renderwindow)
Definition hal.cpp:438
Ogre::SceneManager * GetSceneManager()
Definition hal.cpp:893
Real GetConfigFloat(Ogre::ConfigFile *file, const std::string &key, Real default_value)
Definition hal.cpp:307
bool GetConfigBool(Ogre::ConfigFile *file, const std::string &key, bool default_value)
Definition hal.cpp:299
std::vector< Ogre::Camera * > mCameras
Definition hal.h:90
bool Initialize(const std::string &data_path, Ogre::Root *root=0, Ogre::OverlaySystem *overlay=0)
Definition hal.cpp:315
void RefreshViewport()
Definition hal.cpp:927
Ogre::ConfigFile * configfile
Definition hal.h:97
void StopSound()
Definition hal.cpp:729
std::string SelectBuilding(bool native_dialog=false)
MainScreen * GetWindow()
void Report(const std::string &message)
bool ReportError(const std::string &message)
virtual bool OnInit()
bool Load(const std::string &filename, EngineContext *parent=0, const Vector3 &position=Vector3::ZERO, Real rotation=0.0, const Vector3 &area_min=Vector3::ZERO, const Vector3 &area_max=Vector3::ZERO)
std::string GetDataPath()
bool Start(EngineContext *engine)
void SetFullScreen(bool enabled)
Ogre::Vector3 Vector3
Definition globals.h:58
Ogre::Real Real
Definition globals.h:57
std::string ToString(int number)
Definition globals.cpp:279
static void UnhandledExceptionSetRoot(Skyscraper *root)
Definition uexception.h:181
static void InitUnhandledExceptionFilter()
Definition uexception.h:164
std::string expandTilde(const char *str)
std::string settingsPath()