OSDN Git Service

Fix for last commit
[skyscrapersim/skyscraper.git] / src / frontend / skyscraper.cpp
1 /* $Id$ */
2
3 /*
4         Skyscraper 1.11 Alpha - Simulation Frontend
5         Copyright (C)2003-2017 Ryan Thoryk
6         http://www.skyscrapersim.com
7         http://sourceforge.net/projects/skyscraper
8         Contact - ryan@skyscrapersim.com
9
10         This program is free software; you can redistribute it and/or
11         modify it under the terms of the GNU General Public License
12         as published by the Free Software Foundation; either version 2
13         of the License, or (at your option) any later version.
14
15         This program is distributed in the hope that it will be useful,
16         but WITHOUT ANY WARRANTY; without even the implied warranty of
17         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18         GNU General Public License for more details.
19
20         You should have received a copy of the GNU General Public License
21         along with this program; if not, write to the Free Software
22         Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 */
24
25 #include "wx/wxprec.h"
26 #ifndef WX_PRECOMP
27 #include "wx/wx.h"
28 #include "wx/dir.h"
29 #include "wx/progdlg.h"
30 #include "wx/cmdline.h"
31 #include "wx/filename.h"
32 #include "wx/filefn.h"
33 #include "wx/stdpaths.h"
34 #endif
35 #include <locale>
36 #include <OgreRoot.h>
37 #include <OgreRenderWindow.h>
38 #include <OgreConfigFile.h>
39 #include <OgreFontManager.h>
40 #include <OgreRectangle2D.h>
41 #include <fmod.hpp>
42 #include "Caelum.h"
43 #include "globals.h"
44 #include "sbs.h"
45 #include "camera.h"
46 #include "debugpanel.h"
47 #include "skyscraper.h"
48 #include "enginecontext.h"
49 #include "scriptprocessor.h"
50 #include "console.h"
51 #include "mainscreen.h"
52 #include "loaddialog.h"
53 #include "profiler.h"
54 #include "revmain.h"
55
56 #if OGRE_VERSION >= 0x00010900
57 #include <OgreOverlaySystem.h>
58 #endif
59
60 #if defined(__WXGTK__)
61    // NOTE: Find the GTK install config with `pkg-config --cflags gtk+-2.0`
62    #include "gtk/gtk.h"
63    #include "gdk/gdk.h"
64    #include "gdk/gdkx.h"
65    #include "GL/glx.h"
66 #endif
67
68 using namespace SBS;
69
70 namespace Skyscraper {
71
72 IMPLEMENT_APP_NO_MAIN(Skyscraper)
73
74 }
75
76 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
77 #include "uexception.h"
78 #endif
79
80 #ifndef SW_SHOWNORMAL
81         #define SW_SHOWNORMAL 1
82 #endif
83
84 int main (int argc, char* argv[])
85 {
86 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
87         //initialize top-level exception handler
88         Skyscraper::InitUnhandledExceptionFilter();
89 #endif
90
91         //main wxWidgets entry point
92         wxEntry(argc, argv);
93
94         return 0;
95 }
96
97 namespace Skyscraper {
98
99 bool Skyscraper::OnInit(void)
100 {
101         version = "1.11";
102         version_rev = SVN_REVSTR;
103         version_state = "Alpha";
104         version_frontend = version + ".0." + version_rev;
105         StartupRunning = false;
106         Pause = false;
107         FullScreen = false;
108         Shutdown = false;
109 #if OGRE_VERSION >= 0x00010900
110         mOverlaySystem = 0;
111 #endif
112         mRoot = 0;
113         mRenderWindow = 0;
114         mViewport = 0;
115         mSceneMgr = 0;
116         mCamera = 0;
117         sound = 0;
118         channel = 0;
119         SkyMult = 0;
120         mCaelumSystem = 0;
121         buttons = 0;
122         buttoncount = 0;
123         logger = 0;
124         console = 0;
125         soundsys = 0;
126         progdialog = 0;
127         dpanel = 0;
128         window = 0;
129         console = 0;
130         new_location = false;
131         new_time = false;
132         latitude = 0.0f;
133         longitude = 0.0f;
134         datetime = 0.0;
135         active_engine = 0;
136         ConcurrentLoads = false;
137         RenderOnStartup = false;
138         CutLandscape = true;
139         CutBuildings = true;
140         CutExternal = false;
141         CutFloors = false;
142         loaddialog = 0;
143         Verbose = false;
144         show_progress = false;
145         CheckScript = false;
146         ShowMenu = false;
147         Headless = false;
148
149 #if !defined(__WXMAC__)
150         //switch current working directory to executable's path, if needed
151         wxString exefile = wxStandardPaths::Get().GetExecutablePath(); //get full path and filename
152         wxString app_path = wxPathOnly(exefile); //strip off filename
153         wxSetWorkingDirectory(app_path); //set working directory to path
154 #endif
155
156         //define command line options
157         static const wxCmdLineEntryDesc cmdLineDesc[] =
158         {
159                 { wxCMD_LINE_SWITCH, "h", "help", "show this help message",
160                         wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
161
162                 { wxCMD_LINE_SWITCH, "c", "no-console", "hide the console",
163                         wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
164
165                 { wxCMD_LINE_SWITCH, "f", "fullscreen", "start up in full-screen mode",
166                         wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
167
168                 { wxCMD_LINE_SWITCH, "k", "check-script", "quickly check building script, and exit after",
169                         wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
170
171                 { wxCMD_LINE_SWITCH, "m", "no-menu", "hide the main menu",
172                         wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
173
174                 { wxCMD_LINE_SWITCH, "M", "no-music", "disable the intro music",
175                         wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
176
177                 { wxCMD_LINE_SWITCH, "p", "no-panel", "hide the control panel",
178                         wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
179
180                 { wxCMD_LINE_SWITCH, "H", "headless", "run in headless mode",
181                         wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
182
183                 { wxCMD_LINE_SWITCH, "v", "verbose", "enable verbose mode",
184                         wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
185
186                 { wxCMD_LINE_SWITCH, "V", "version", "show version",
187                         wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
188
189                 { wxCMD_LINE_PARAM, NULL, NULL, "building filename",
190                         wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
191
192                 { wxCMD_LINE_NONE }
193         };
194
195         //set up command line parser
196         parser = new wxCmdLineParser(cmdLineDesc, argc, argv);
197
198         //process command line options
199         switch (parser->Parse())
200         {
201                 case -1:
202                         return false; //help was given, exit
203                 case 0:
204                         break; //everything is good, continue
205                 default:
206                         return false; //exit if parameters are incorrect
207         }
208
209         //only run idle events on specified windows, to reduce overhead
210         wxIdleEvent::SetMode(wxIDLE_PROCESS_SPECIFIED);
211
212         //set up unhandled exception handler (crash report system)
213 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
214         UnhandledExceptionSetRoot(this);
215 #endif
216
217         //set locale to default for conversion functions
218 #ifdef OGRE_DEFAULT_LOCALE
219         setlocale(LC_ALL, OGRE_DEFAULT_LOCALE);
220 #else
221         setlocale(LC_ALL, "C");
222 #endif
223
224         //show version number and exit if specified
225         if (parser->Found(wxT("version")) == true)
226         {
227                 printf("Skyscraper version %s\n", version_frontend.c_str());
228                 return false;
229         }
230
231         //set verbose mode if specified
232         if (parser->Found(wxT("verbose")) == true)
233                 Verbose = true;
234
235         //set CheckScript mode if specified
236         if (parser->Found(wxT("check-script")) == true)
237                 CheckScript = true;
238
239         //set headless mode if specified
240         if (parser->Found(wxT("headless")) == true)
241                 Headless = true;
242
243         //load config file
244         try
245         {
246                 configfile = new Ogre::ConfigFile();
247                 configfile->load("skyscraper.ini");
248         }
249         catch (Ogre::Exception &e)
250         {
251                 return ReportFatalError("Error loading skyscraper.ini file\nDetails: " + e.getDescription());
252         }
253
254         showconsole = GetConfigBool("Skyscraper.Frontend.ShowConsole", true);
255
256         //turn off console if specified on command line
257         if (parser->Found(wxT("no-console")) == true)
258                 showconsole = false;
259
260         //create console window
261         if (showconsole == true)
262                 ShowConsole(false);
263
264         //Create main window and set size from INI file defaults
265         //if (Headless == false)
266         //{
267                 window = new MainScreen(this, GetConfigInt("Skyscraper.Frontend.MenuWidth", 640), GetConfigInt("Skyscraper.Frontend.MenuHeight", 480));
268                 //AllowResize(false);
269                 window->ShowWindow();
270                 window->CenterOnScreen();
271         //}
272
273         //start and initialize OGRE
274         if (!Initialize())
275                 return ReportError("Error initializing frontend");
276
277         //autoload a building file if specified
278         std::string filename;
279         if (parser->GetParamCount() > 0)
280         {
281                 filename = parser->GetParam(0).ToStdString();
282
283                 //strip path from filename
284                 wxFileName file (filename);
285                 filename = file.GetFullName();
286         }
287         else
288                 filename = GetConfigString("Skyscraper.Frontend.AutoLoad", "");
289
290         ShowMenu = GetConfigBool("Skyscraper.Frontend.Menu.Show", true);
291
292         //turn off menu if specified on command line
293         if (parser->Found(wxT("no-menu")) == true)
294                 ShowMenu = false;
295
296         if (Headless == true)
297                 ShowMenu = false;
298
299         if (filename != "")
300                 return Load(filename);
301
302         if (ShowMenu == true)
303         {
304                 StartupRunning = true;
305                 StartSound();
306         }
307         else
308         {
309                 //or show building selection window if ShowMenu is false
310                 return Load(SelectBuilding());
311         }
312
313         return true;
314 }
315
316 int Skyscraper::OnExit()
317 {
318         //clean up
319
320         //cleanup
321         Report("Cleaning up...");
322
323         if (loaddialog)
324                 loaddialog->Destroy();
325         loaddialog = 0;
326
327         if (progdialog)
328                 progdialog->Destroy();
329         progdialog = 0;
330
331         UnloadSim();
332
333         //delete Caelum
334         if (mCaelumSystem)
335                 delete mCaelumSystem;
336
337         //cleanup sound
338         StopSound();
339         if (soundsys)
340                 soundsys->release();
341
342         //delete console window
343         if (console)
344                 console->Destroy();
345         console = 0;
346
347         CloseProgressDialog();
348
349         if (window)
350                 window->Destroy();
351         window = 0;
352
353         if (configfile)
354                 delete configfile;
355         configfile = 0;
356
357         if (parser)
358                 delete parser;
359         parser = 0;
360
361 #if OGRE_VERSION >= 0x00010900
362         delete mOverlaySystem;
363 #endif
364
365         Ogre::ResourceGroupManager::getSingleton().shutdownAll();
366
367         mRoot->shutdown(); //shutdown root instead of delete, to fix a crash on certain systems
368         //delete mRoot;
369         delete logger;
370         return wxApp::OnExit();
371 }
372
373 void Skyscraper::UnloadSim()
374 {
375         //delete control panel object
376         if(dpanel)
377                 delete dpanel;
378         dpanel = 0;
379
380         if (mCaelumSystem)
381         {
382                 mCaelumSystem->clear();
383                 mCaelumSystem->detachAllViewports();
384                 delete mCaelumSystem;
385                 mCaelumSystem = 0;
386                 Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup("Caelum");
387         }
388
389         if (console)
390                 console->bSend->Enable(false);
391
392         DeleteEngines();
393
394         //do a full clear of Ogre objects
395
396         //remove all meshes
397         Ogre::MeshManager::getSingleton().removeAll();
398
399         //remove all materials
400         Ogre::MaterialManager::getSingleton().removeAll();
401         Ogre::MaterialManager::getSingleton().initialise();  //restore default materials
402
403         //remove all fonts
404         Ogre::FontManager::getSingleton().removeAll();
405
406         //remove all textures
407         Ogre::TextureManager::getSingleton().removeAll();
408
409         //clear scene manager
410         mSceneMgr->clearScene();
411 }
412
413 void Skyscraper::Render()
414 {
415         SBS_PROFILE_MAIN("Render");
416
417         if (Headless == true)
418                 return;
419
420         // Render to the frame buffer
421         mRoot->renderOneFrame();
422
423 #if defined(__WXGTK__)
424         if (mRenderWindow)
425                 mRenderWindow->update(true);
426 #endif
427 }
428
429 bool Skyscraper::Initialize()
430 {
431         //initialize OGRE
432         try
433         {
434                 mRoot = Ogre::Root::getSingletonPtr();
435         }
436         catch (Ogre::Exception &e)
437         {
438                 return ReportFatalError("Error during initial OGRE check\nDetails: " + e.getDescription());
439         }
440
441         if(!mRoot)
442         {
443                 try
444                 {
445                         //set up custom logger
446                         if (!logger)
447                         {
448                                 logger = new Ogre::LogManager();
449                                 Ogre::Log *log = logger->createLog("skyscraper.log", true, !showconsole, false);
450                                 log->addListener(this);
451                         }
452
453                         //load OGRE
454                         mRoot = new Ogre::Root();
455                 }
456                 catch (Ogre::Exception &e)
457                 {
458                         return ReportFatalError("Error initializing OGRE\nDetails: " + e.getDescription());
459                 }
460                 catch (...)
461                 {
462                         return ReportFatalError("Error initializing OGRE");
463                 }
464         }
465
466         //set up overlay system
467 #if OGRE_VERSION >= 0x00010900
468         try
469         {
470                 mOverlaySystem = new Ogre::OverlaySystem();
471         }
472         catch (Ogre::Exception &e)
473         {
474                 return ReportFatalError("Error creating overlay system\nDetails: " + e.getDescription());
475         }
476 #endif
477
478         //configure render system
479         try
480         {
481                 if(!mRoot->getRenderSystem())
482                 {
483                         //if no render systems are loaded, try to load previous config
484                         if(!mRoot->restoreConfig())
485                         {
486                                 //show dialog if load failed
487                                 mRoot->showConfigDialog();
488                         }
489                 }
490         }
491         catch (Ogre::Exception &e)
492         {
493                 return ReportFatalError("Error configuring render system\nDetails: " + e.getDescription());
494         }
495
496         //if using DirectX, prevent it from switching into single-point floating point mode
497         Ogre::RenderSystem *rendersystem = mRoot->getRenderSystem();
498         if (rendersystem)
499         {
500                 Ogre::ConfigOptionMap& CurrentRendererOptions = mRoot->getRenderSystem()->getConfigOptions();
501                 Ogre::ConfigOptionMap::iterator configItr = CurrentRendererOptions.begin();
502
503                 for (; configItr != CurrentRendererOptions.end(); configItr++)
504                 {
505                         if ((configItr)->first == "Floating-point mode")
506                         {
507                                 rendersystem->setConfigOption("Floating-point mode", "Consistent");
508                                 break;
509                         }
510                 }
511         }
512
513         //initialize render window
514         try
515         {
516                 mRoot->initialise(false);
517
518                 if (Headless == false)
519                         mRenderWindow = CreateRenderWindow();
520         }
521         catch (Ogre::Exception &e)
522         {
523                 return ReportFatalError("Error initializing render window\nDetails: " + e.getDescription());
524         }
525
526         if (Headless == false)
527         {
528                 //get renderer info
529                 Renderer = mRoot->getRenderSystem()->getCapabilities()->getRenderSystemName();
530
531                 //shorten name
532                 int loc = Renderer.find("Rendering Subsystem");
533                 Renderer = Renderer.substr(0, loc - 1);
534         }
535
536         //load resource configuration
537         Ogre::ConfigFile cf;
538         try
539         {
540                 cf.load("resources.cfg");
541         }
542         catch (Ogre::Exception &e)
543         {
544                 return ReportFatalError("Error loading resources.cfg\nDetails: " + e.getDescription());
545         }
546         Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
547
548         //add resource locations
549         try
550         {
551                 std::string secName, typeName, archName;
552                 while(seci.hasMoreElements())
553                 {
554                         secName = seci.peekNextKey();
555                         Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
556                         Ogre::ConfigFile::SettingsMultiMap::iterator i;
557                         for(i = settings->begin(); i != settings->end(); ++i)
558                         {
559                                 typeName = i->first;
560                                 archName = i->second;
561                                 Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
562                         }
563                 }
564
565                 //add app's directory to resource manager
566                 Ogre::ResourceGroupManager::getSingleton().addResourceLocation(".", "FileSystem", "General", true);
567
568                 //add materials group, and autoload
569                 Ogre::ResourceGroupManager::getSingleton().addResourceLocation("data/materials", "FileSystem", "Materials", true);
570                 Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup("Materials");
571         }
572         catch (Ogre::Exception &e)
573         {
574                 return ReportFatalError("Error initializing resources\nDetails: " + e.getDescription());
575         }
576
577         //create scene manager
578         try
579         {
580                 mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);
581         }
582         catch (Ogre::Exception &e)
583         {
584                 return ReportFatalError("Error creating scene manager\nDetails: " + e.getDescription());
585         }
586
587 #if OGRE_VERSION >= 0x00010900
588         mSceneMgr->addRenderQueueListener(mOverlaySystem);
589 #endif
590
591         //set ambient light
592         //mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));
593         //mSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_MODULATIVE);
594
595         if (Headless == false)
596         {
597                 try
598                 {
599                         mCamera = mSceneMgr->createCamera("Main Camera");
600                         mViewport = mRenderWindow->addViewport(mCamera);
601                         mCamera->setAspectRatio(Ogre::Real(mViewport->getActualWidth()) / Ogre::Real(mViewport->getActualHeight()));
602                 }
603                 catch (Ogre::Exception &e)
604                 {
605                         return ReportFatalError("Error creating camera and viewport\nDetails: " + e.getDescription());
606                 }
607         }
608
609         //setup texture filtering
610         int filtermode = GetConfigInt("Skyscraper.Frontend.TextureFilter", 3);
611         int maxanisotropy = GetConfigInt("Skyscraper.Frontend.MaxAnisotropy", 8);
612
613         if (filtermode < 0 || filtermode > 3)
614                 filtermode = 3;
615
616         if (filtermode < 3)
617                 maxanisotropy = 1;
618
619         Ogre::TextureFilterOptions filter;
620         if (filtermode == 0)
621                 filter = Ogre::TFO_NONE;
622         else if (filtermode == 1)
623                 filter = Ogre::TFO_BILINEAR;
624         else if (filtermode == 2)
625                 filter = Ogre::TFO_TRILINEAR;
626         else if (filtermode == 3)
627                 filter = Ogre::TFO_ANISOTROPIC;
628
629         Ogre::MaterialManager::getSingleton().setDefaultTextureFiltering(filter);
630         Ogre::MaterialManager::getSingleton().setDefaultAnisotropy(maxanisotropy);
631
632         //initialize FMOD (sound)
633         DisableSound = GetConfigBool("Skyscraper.Frontend.DisableSound", false);
634         if (DisableSound == false)
635         {
636                 Report("\n FMOD Sound System, copyright (C) Firelight Technologies Pty, Ltd., 1994-2017\n");
637
638                 FMOD_RESULT result = FMOD::System_Create(&soundsys);
639                 if (result != FMOD_OK)
640                 {
641                         ReportFatalError("Error initializing sound");
642                         DisableSound = true;
643                 }
644                 else
645                 {
646                         char name [30] = "Skyscraper"; //set name for PulseAudio on Linux
647                         result = soundsys->init(100, FMOD_INIT_NORMAL, &name);
648                         if (result != FMOD_OK)
649                         {
650                                 ReportFatalError("Error initializing sound");
651                                 DisableSound = true;
652                         }
653                         else
654                         {
655                                 //get FMOD version information
656                                 unsigned int version;
657                                 soundsys->getVersion(&version);
658                                 int major = version >> 16;
659                                 int minor = (version >> 8) & 255;
660                                 int rev = version & 255;
661
662                                 std::string name;
663                                 if (major == 1)
664                                         name = "FMOD Studio";
665                                 else
666                                         name = "FMOD Ex";
667                                 Report("Sound initialized: " + name + " version " + ToString(major) + "." + ToString(minor) + "." + ToString(rev));
668                         }
669                 }
670         }
671         else
672                 Report("Sound Disabled");
673
674         //load Caelum plugin
675         if (GetConfigBool("Skyscraper.Frontend.Caelum", true) == true)
676         {
677                 try
678                 {
679                         if (!Caelum::CaelumPlugin::getSingletonPtr())
680                                 mRoot->installPlugin(new Caelum::CaelumPlugin());
681                 }
682                 catch (Ogre::Exception &e)
683                 {
684                         return ReportFatalError("Error initializing Caelum plugin:\nDetails: " + e.getDescription());
685                 }
686         }
687
688         //set platform name
689         std::string arch;
690
691 #if OGRE_ARCH_TYPE == OGRE_ARCHITECTURE_32
692         arch = "32-bit";
693 #endif
694 #if OGRE_ARCH_TYPE == OGRE_ARCHITECTURE_64
695         arch = "64-bit";
696 #endif
697
698 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
699         Platform = "Windows " + arch;
700 #endif
701 #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
702         Platform = "Linux " + arch;
703 #endif
704 #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
705         Platform = "MacOS " + arch;
706 #endif
707
708         return true;
709 }
710
711 void Skyscraper::Report(const std::string &message)
712 {
713         try
714         {
715                 if (Ogre::LogManager::getSingletonPtr())
716                         Ogre::LogManager::getSingleton().logMessage(message);
717         }
718         catch (Ogre::Exception &e)
719         {
720                 ShowError("Error writing message to log\n" + e.getDescription());
721         }
722 }
723
724 bool Skyscraper::ReportError(const std::string &message)
725 {
726         try
727         {
728                 if (Ogre::LogManager::getSingletonPtr())
729                         Ogre::LogManager::getSingleton().logMessage(message, Ogre::LML_CRITICAL);
730         }
731         catch (Ogre::Exception &e)
732         {
733                 ShowError("Error writing message to log\n" + e.getDescription());
734         }
735         return false;
736 }
737
738 bool Skyscraper::ReportFatalError(const std::string &message)
739 {
740         ReportError(message);
741         ShowError(message);
742         return false;
743 }
744
745 void Skyscraper::ShowError(const std::string &message)
746 {
747         //show error dialog
748         wxMessageDialog dialog(0, message, _("Skyscraper"), wxOK | wxICON_ERROR);
749         dialog.ShowModal();
750 }
751
752 void Skyscraper::ShowMessage(const std::string &message)
753 {
754         //show message dialog
755         wxMessageDialog dialog(0, message, _("Skyscraper"), wxOK | wxICON_INFORMATION);
756         dialog.ShowModal();
757 }
758
759 void Skyscraper::Loop()
760 {
761         //Main simulator loop
762
763         ProfileManager::Reset();
764         ProfileManager::Increment_Frame_Counter();
765
766         //main menu routine
767         if (StartupRunning == true)
768         {
769                 DrawBackground();
770                 GetMenuInput();
771                 Render();
772                 return;
773         }
774
775         //show progress dialog if needed
776         if (show_progress == true)
777                 ShowProgressDialog();
778
779         //run sim engine instances
780         bool result = RunEngines();
781
782         //delete an engine if requested
783         HandleEngineShutdown();
784
785         //exit if full shutdown request received
786         if (Shutdown == true)
787         {
788                 Shutdown = false;
789                 UnloadToMenu();
790         }
791
792         if (result == false && (ConcurrentLoads == false || GetEngineCount() == 1))
793                 return;
794
795         if (!active_engine)
796                 return;
797
798         //make sure active engine is the one the camera is active in
799         if (active_engine->IsCameraActive() == false)
800                 active_engine = FindActiveEngine();
801
802         //exit if active engine is loading
803         if (active_engine->IsLoading() == true)
804                 return;
805
806         //if in CheckScript mode, exit
807         if (CheckScript == true)
808         {
809                 UnloadToMenu();
810                 return;
811         }
812
813         //update Caelum
814         UpdateSky();
815
816         //render graphics
817         Render();
818
819         //handle a building reload
820         HandleReload();
821
822         //handle behavior when a user exits an engine area
823         SwitchEngines();
824
825         //ProfileManager::dumpAll();
826 }
827
828 void Skyscraper::DrawBackground()
829 {
830         //draw menu background
831
832         DrawImage("data/" + GetConfigString("Skyscraper.Frontend.Menu.Image", "menu.png"), 0, -1, -1, false);
833
834         if (buttoncount == 0)
835         {
836                 buttoncount = GetConfigInt("Skyscraper.Frontend.Menu.Buttons", 5);
837                 buttons = new buttondata[buttoncount];
838
839                 for (int i = 0; i < buttoncount; i++)
840                 {
841                         buttons[i].node = 0;
842                         buttons[i].drawn_selected = false;
843                         buttons[i].drawn_pressed = false;
844                         buttons[i].active_button = 0;
845                         buttons[i].rect = 0;
846                 }
847         }
848
849         for (int i = 0; i < buttoncount; i++)
850         {
851                 std::string b1, b2, b3;
852                 float x = 0, y = 0;
853                 bool center = false;
854                 std::string number = ToString(i + 1);
855
856                 if (i == 0)
857                 {
858                         b1 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button1.Image", "button_triton.png");
859                         b2 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button1.Selected", "button_triton_selected.png");
860                         b3 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button1.Pressed", "button_triton_pressed.png");
861                         x = GetConfigFloat("Skyscraper.Frontend.Menu.Button1.X", 0.0f);
862                         y = GetConfigFloat("Skyscraper.Frontend.Menu.Button1.Y", -0.08f);
863                         center = GetConfigBool("Skyscraper.Frontend.Menu.Button1.Center", true);
864                 }
865                 if (i == 1)
866                 {
867                         b1 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button2.Image", "button_glasstower.png");
868                         b2 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button2.Selected", "button_glasstower_selected.png");
869                         b3 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button2.Pressed", "button_glasstower_pressed.png");
870                         x = GetConfigFloat("Skyscraper.Frontend.Menu.Button2.X", 0.0f);
871                         y = GetConfigFloat("Skyscraper.Frontend.Menu.Button2.Y", 0.125f);
872                         center = GetConfigBool("Skyscraper.Frontend.Menu.Button2.Center", true);
873                 }
874                 if (i == 2)
875                 {
876                         b1 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button3.Image", "button_searstower.png");
877                         b2 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button3.Selected", "button_searstower_selected.png");
878                         b3 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button3.Pressed", "button_searstower_pressed.png");
879                         x = GetConfigFloat("Skyscraper.Frontend.Menu.Button3.X", 0.0f);
880                         y = GetConfigFloat("Skyscraper.Frontend.Menu.Button3.Y", 0.333f);
881                         center = GetConfigBool("Skyscraper.Frontend.Menu.Button3.Center", true);
882                 }
883                 if (i == 3)
884                 {
885                         b1 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button4.Image", "button_simple.png");
886                         b2 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button4.Selected", "button_simple_selected.png");
887                         b3 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button4.Pressed", "button_simple_pressed.png");
888                         x = GetConfigFloat("Skyscraper.Frontend.Menu.Button4.X", 0.0f);
889                         y = GetConfigFloat("Skyscraper.Frontend.Menu.Button4.Y", 0.541f);
890                         center = GetConfigBool("Skyscraper.Frontend.Menu.Button4.Center", true);
891                 }
892                 if (i == 4)
893                 {
894                         b1 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button5.Image", "button_other.png");
895                         b2 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button5.Selected", "button_other_selected.png");
896                         b3 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button5.Pressed", "button_other_pressed.png");
897                         x = GetConfigFloat("Skyscraper.Frontend.Menu.Button5.X", 0.0f);
898                         y = GetConfigFloat("Skyscraper.Frontend.Menu.Button5.Y", 0.75f);
899                         center = GetConfigBool("Skyscraper.Frontend.Menu.Button5.Center", true);
900                 }
901                 if (i > 4)
902                 {
903                         b1 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button" + number + ".Image", "");
904                         b2 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button" + number + ".Selected", "");
905                         b3 = "data/" + GetConfigString("Skyscraper.Frontend.Menu.Button" + number + ".Pressed", "");
906                         x = GetConfigFloat("Skyscraper.Frontend.Menu.Button" + number + ".X", 0.0f);
907                         y = GetConfigFloat("Skyscraper.Frontend.Menu.Button" + number + ".Y", 0.0f);
908                         center = GetConfigBool("Skyscraper.Frontend.Menu.Button" + number + ".Center", true);
909                 }
910
911                 DrawImage(b1, &buttons[i], x, y, center, b2, b3);
912         }
913 }
914
915 void Skyscraper::DrawImage(const std::string &filename, buttondata *button, float x, float y, bool center, const std::string &filename_selected, const std::string &filename_pressed)
916 {
917         //X and Y represent the image's top-left location.
918         //values are -1 for the top left, 1 for the top right, -1 for the top, and 1 for the bottom
919         //center is at 0, 0
920
921         float w, h;
922         int w_orig = 0, h_orig = 0, w2 = 0, h2 = 0;
923         bool background = false;
924
925         std::string material = "";
926         std::string Filename = filename;
927
928         if (filename == "")
929                 return;
930
931         //exit if background has already been drawn
932         if (background_image == Filename)
933                 return;
934
935         Ogre::TextureManager::getSingleton().setVerbose(false);
936         Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().getByName(Filename);
937         if (tex.isNull() == false)
938                 material = Filename;
939
940         //load image data from file, if not already preloaded
941         if (material == "")
942         {
943                 int count = 1;
944                 if (button)
945                         count = 3;
946
947                 for (int i = 0; i < count; i++)
948                 {
949                         if (i == 0)
950                                 Filename = filename;
951                         if (i == 1)
952                                 Filename = filename_selected;
953                         if (i == 2)
954                                 Filename = filename_pressed;
955
956                         //create new material
957                         Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create(Filename, "General");
958
959                         //load image data from file
960                         Ogre::Image img;
961                         try
962                         {
963                                 img.load(Filename, "General");
964                         }
965                         catch (Ogre::Exception &e)
966                         {
967                                 ReportError("Error loading texture " + Filename + "\n" + e.getDescription());
968                                 return;
969                         }
970
971                         w_orig = img.getWidth();
972                         h_orig = img.getHeight();
973
974                         //round up image size to power-of-2 value
975                         w2 = powf(2, ceilf(Log2((float)w_orig)));
976                         h2 = powf(2, ceilf(Log2((float)h_orig)));
977
978                         //create texture and blit image onto it - this solves texture quality issues on mainly D3D9
979                         tex = Ogre::TextureManager::getSingleton().createManual(Filename, "General", Ogre::TEX_TYPE_2D, w2, h2, 0, Ogre::PF_R8G8B8A8, Ogre::TU_STATIC);
980                         tex->getBuffer(0, 0)->blitFromMemory(img.getPixelBox(0, 0), Ogre::Box(0, 0, 0, w_orig, h_orig, img.getDepth()));
981
982                         //bind texture to material and set options
983                         Ogre::TextureUnitState *state = mat->getTechnique(0)->getPass(0)->createTextureUnitState(Filename);
984                         Ogre::Pass *pass = mat->getTechnique(0)->getPass(0);
985                         pass->setDepthCheckEnabled(false);
986                         pass->setDepthWriteEnabled(false);
987                         pass->setLightingEnabled(false);
988                         pass->setTextureFiltering(Ogre::TFO_NONE);
989
990                         //rescale texture
991                         state->setTextureScale((Ogre::Real)w2 / (Ogre::Real)w_orig, (Ogre::Real)h2 / (Ogre::Real)h_orig);
992                         state->setTextureScroll(-(Ogre::Real(w2 - w_orig) / (Ogre::Real)w2) / 2.0f, -(Ogre::Real(h2 - h_orig) / (Ogre::Real)h2) / 2.0f);
993
994                         if (tex->hasAlpha() == true && button)
995                         {
996                                 mat->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);
997                                 mat->getTechnique(0)->getPass(0)->setAlphaRejectSettings(Ogre::CMPF_GREATER_EQUAL, 128);
998                         }
999                 }
1000                 material = Filename;
1001                 if (button)
1002                 {
1003                         button->node = 0;
1004                         button->drawn_selected = false;
1005                         button->drawn_pressed = false;
1006                         button->active_button = 0;
1007                         button->rect = 0;
1008                 }
1009                 else
1010                 {
1011                         background_rect = 0;
1012                         background_node = 0;
1013                 }
1014         }
1015
1016         //exit if requested button is already visible
1017         if (button)
1018         {
1019                 if (button->drawn_selected == false && button->drawn_pressed == false)
1020                 {
1021                         if (button->active_button == 1)
1022                                 return;
1023                         else
1024                         {
1025                                 button->active_button = 1;
1026                                 material = filename;
1027                         }
1028                 }
1029
1030                 if (button->drawn_selected == true)
1031                 {
1032                         if (button->active_button == 2)
1033                                 return;
1034                         else
1035                         {
1036                                 button->active_button = 2;
1037                                 material = filename_selected;
1038                         }
1039                 }
1040
1041                 if (button->drawn_pressed == true)
1042                 {
1043                         if (button->active_button == 3)
1044                                 return;
1045                         else
1046                         {
1047                                 button->active_button = 3;
1048                                 material = filename_pressed;
1049                         }
1050                 }
1051         }
1052
1053         //set values and draw button
1054         if (material != "")
1055         {
1056                 w = w_orig / (mRenderWindow->getWidth() / 2.0);
1057                 h = h_orig / (mRenderWindow->getHeight() / 2.0);
1058                 if (button)
1059                 {
1060                         //delete previous object
1061                         if (button->node)
1062                                 button->node->detachAllObjects();
1063                         if (button->rect)
1064                                 delete button->rect;
1065                         button->rect = 0;
1066
1067                         if (button->filename == "")
1068                         {
1069                                 //store general button data
1070                                 button->filename = filename;
1071                                 button->filename_selected = filename_selected;
1072                                 button->filename_pressed = filename_pressed;
1073
1074                                 button->offset_x = x;
1075                                 button->offset_y = y;
1076                                 if (center == true)
1077                                 {
1078                                         button->x = x - (w / 2);
1079                                         button->y = y - (h / 2);
1080                                 }
1081                                 else
1082                                 {
1083                                         button->x = x;
1084                                         button->y = y;
1085                                 }
1086                                 button->size_x = w;
1087                                 button->size_y = h;
1088                         }
1089
1090                         x = button->x;
1091                         y = button->y;
1092                         w = button->size_x;
1093                         h = button->size_y;
1094                 }
1095                 else
1096                 {
1097                         background_image = material;
1098                         background = true;
1099                         if (center == true)
1100                         {
1101                                 x += -(w / 2);
1102                                 y += -(h / 2);
1103                         }
1104                 }
1105
1106                 //create rectangle
1107                 Ogre::Rectangle2D* rect = new Ogre::Rectangle2D(true);
1108                 rect->setCorners(x, -y, x + w, -(y + h));
1109                 rect->setMaterial(material);
1110                 if (background == true)
1111                         rect->setRenderQueueGroup(Ogre::RENDER_QUEUE_BACKGROUND);
1112
1113                 //set infinite bounding box
1114                 Ogre::AxisAlignedBox aabInf;
1115                 aabInf.setInfinite();
1116                 rect->setBoundingBox(aabInf);
1117
1118                 //attach scene node
1119                 Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
1120                 node->attachObject(rect);
1121                 if (button)
1122                 {
1123                         button->node = node;
1124                         button->rect = rect;
1125                 }
1126                 else
1127                 {
1128                         background_node = node;
1129                         background_rect = rect;
1130                 }
1131         }
1132 }
1133
1134 void Skyscraper::GetMenuInput()
1135 {
1136         //input handler for main menu
1137
1138         //exit if there aren't any buttons
1139         if (!buttons || buttoncount == 0)
1140                 return;
1141
1142         //get mouse coordinates
1143         int mouse_x = window->ScreenToClient(wxGetMousePosition()).x;
1144         int mouse_y = window->ScreenToClient(wxGetMousePosition()).y;
1145
1146         for (int i = 0; i < buttoncount; i++)
1147         {
1148                 buttondata *button = &buttons[i];
1149
1150             //only process buttons if main window is selected
1151         if (window->Active != false)
1152         {
1153                         float mx = mouse_x;
1154                         float my = mouse_y;
1155                         float w = mx / window->GetClientSize().x;
1156                         float h = my / window->GetClientSize().y;
1157                         float mouse_x_rel = (w * 2) - 1;
1158                         float mouse_y_rel = (h * 2) - 1;
1159
1160                 //change button status based on mouse position and button press status
1161                 if (mouse_x_rel > button->x && mouse_x_rel < button->x + button->size_x && mouse_y_rel > button->y && mouse_y_rel < button->y + button->size_y)
1162                 {
1163                         if (button->drawn_selected == false && wxGetMouseState().LeftIsDown() == false)
1164                         {
1165                                 if (button->drawn_pressed == true)
1166                                 {
1167                                         //user clicked on button
1168                                         button->drawn_selected = true;
1169                                         Click(i);
1170                                         return;
1171                                 }
1172                                 button->drawn_selected = true;
1173                         }
1174                         if (button->drawn_pressed == false && wxGetMouseState().LeftIsDown() == true)
1175                         {
1176                                 button->drawn_pressed = true;
1177                                 button->drawn_selected = false;
1178                         }
1179                 }
1180                 else if (button->drawn_selected == true || button->drawn_pressed == true)
1181                 {
1182                         button->drawn_selected = false;
1183                         button->drawn_pressed = false;
1184                 }
1185         }
1186         }
1187 }
1188
1189 void Skyscraper::Click(int index)
1190 {
1191         //user clicked a button
1192
1193         std::string number = ToString(index + 1);
1194         std::string filename = "";
1195
1196         if (index == 0)
1197                 filename = GetConfigString("Skyscraper.Frontend.Menu.Button1.File", "Triton Center.bld");
1198         if (index == 1)
1199                 filename = GetConfigString("Skyscraper.Frontend.Menu.Button2.File", "Glass Tower.bld");
1200         if (index == 2)
1201                 filename = GetConfigString("Skyscraper.Frontend.Menu.Button3.File", "Sears Tower.bld");
1202         if (index == 3)
1203                 filename = GetConfigString("Skyscraper.Frontend.Menu.Button4.File", "Simple.bld");
1204         if (index > 3)
1205                 filename = GetConfigString("Skyscraper.Frontend.Menu.Button" + number + ".File", "");
1206
1207         if (filename == "")
1208         {
1209                 //show file selection dialog
1210                 filename = SelectBuilding();
1211         }
1212
1213         if (filename != "")
1214         {
1215                 Load(filename);
1216         }
1217 }
1218
1219 void Skyscraper::DeleteButtons()
1220 {
1221         if (buttoncount > 0)
1222         {
1223                 for (int i = 0; i < buttoncount; i++)
1224                 {
1225                         buttondata *button = &buttons[i];
1226
1227                         if (button->node)
1228                         {
1229                                 button->node->detachAllObjects();
1230                                 button->node->getParent()->removeChild(button->node);
1231                                 button->node = 0;
1232                         }
1233                         if (button->rect)
1234                                 delete button->rect;
1235                         button->rect = 0;
1236                 }
1237                 delete [] buttons;
1238                 buttoncount = 0;
1239         }
1240         buttons = 0;
1241
1242         if (background_node)
1243         {
1244                 background_node->detachAllObjects();
1245                 background_node->getParent()->removeChild(background_node);
1246                 background_node = 0;
1247         }
1248         if (background_rect)
1249                 delete background_rect;
1250         background_rect = 0;
1251         background_image = "";
1252 }
1253
1254 void Skyscraper::StartSound()
1255 {
1256         //load and start background music
1257
1258         if (DisableSound == true)
1259                 return;
1260
1261         if (GetConfigBool("Skyscraper.Frontend.IntroMusic", true) == false)
1262                 return;
1263
1264         if (parser->Found(wxT("no-music")) == true)
1265                 return;
1266
1267         std::string filename = GetConfigString("Skyscraper.Frontend.IntroMusicFile", "intro.ogg");
1268         std::string filename_full = "data/" + filename;
1269
1270         //load new sound
1271 #if (FMOD_VERSION >> 16 == 4)
1272                 FMOD_RESULT result = soundsys->createSound(filename_full.c_str(), (FMOD_MODE)(FMOD_2D | FMOD_ACCURATETIME | FMOD_SOFTWARE | FMOD_LOOP_NORMAL), 0, &sound);
1273 #else
1274                 FMOD_RESULT result = soundsys->createSound(filename_full.c_str(), (FMOD_MODE)(FMOD_2D | FMOD_ACCURATETIME | FMOD_LOOP_NORMAL), 0, &sound);
1275 #endif
1276         if (result != FMOD_OK)
1277         {
1278                 ReportError("Can't load file '" + filename_full + "'");
1279                 return;
1280         }
1281
1282 #if (FMOD_VERSION >> 16 == 4)
1283         result = soundsys->playSound(FMOD_CHANNEL_FREE, sound, true, &channel);
1284 #else
1285         result = soundsys->playSound(sound, 0, true, &channel);
1286 #endif
1287
1288         if (result != FMOD_OK)
1289         {
1290                 ReportError("Error playing " + filename);
1291                 return;
1292         }
1293
1294         channel->setLoopCount(-1);
1295         channel->setVolume(1.0f);
1296         channel->setPaused(false);
1297 }
1298
1299 void Skyscraper::StopSound()
1300 {
1301         //stop and unload sound
1302         if (channel)
1303                 channel->stop();
1304         if (sound)
1305                 sound->release();
1306         sound = 0;
1307 }
1308
1309 std::string Skyscraper::SelectBuilding()
1310 {
1311         //choose a building from a script file
1312
1313         std::string filename = "";
1314
1315         //get listing of building files
1316         wxArrayString filelist;
1317         wxDir::GetAllFiles(_("buildings/"), &filelist, _("*.bld"), wxDIR_FILES);
1318
1319         //strip directory name and extension from entries
1320         for (size_t i = 0; i < filelist.size(); i++)
1321         {
1322                 filelist[i] = filelist[i].substr(10);
1323                 filelist[i] = filelist[i].substr(0, filelist[i].length() - 4);
1324         }
1325
1326         //sort list
1327         filelist.Sort();
1328
1329         //show selection dialog window
1330         wxSingleChoiceDialog Selector (0, _("Select a Building"), _("Load Building"), filelist);
1331         Selector.SetSize(wxSize(350, 350));
1332         Selector.CenterOnScreen();
1333
1334         if (Selector.ShowModal() == wxID_OK)
1335         {
1336                 filename = Selector.GetStringSelection();
1337                 filename += ".bld";
1338         }
1339
1340         return filename;
1341 }
1342
1343 bool Skyscraper::Load(const std::string &filename, EngineContext *parent, const Ogre::Vector3 &position, float rotation, const Ogre::Vector3 &area_min, const Ogre::Vector3 &area_max)
1344 {
1345         //load simulator and data file
1346
1347         if (StartupRunning == true)
1348         {
1349                 DeleteButtons();
1350                 StartupRunning = false;
1351         }
1352
1353         //exit if no building specified
1354         if (filename == "")
1355                 return false;
1356
1357         //set sky name
1358         if (GetEngineCount() == 0)
1359                 SkyName = GetConfigString("Skyscraper.Frontend.SkyName", "DefaultSky");
1360
1361         //clear scene
1362         if (GetEngineCount() == 0)
1363                 mSceneMgr->clearScene();
1364
1365         //clear screen
1366         if (Headless == false)
1367                 mRenderWindow->update();
1368
1369         //set parent to master engine, if not set
1370         if (parent == 0 && GetEngineCount() >= 1)
1371                 parent = GetFirstValidEngine();
1372
1373         //Create simulator instance
1374         EngineContext* engine = CreateEngine(parent, position, rotation, area_min, area_max);
1375
1376         if (!active_engine)
1377                 active_engine = engine;
1378
1379         //have instance load building
1380         bool result = engine->Load(filename);
1381
1382         if (result == false)
1383         {
1384                 if (GetEngineCount() == 1)
1385                         UnloadToMenu();
1386                 else
1387                         DeleteEngine(engine);
1388                 return false;
1389         }
1390
1391         //override SBS startup render option, if specified
1392         if (RenderOnStartup == true)
1393                 engine->GetSystem()->RenderOnStartup = true;
1394
1395         return true;
1396 }
1397
1398 bool Skyscraper::Start(EngineContext *engine)
1399 {
1400         //start simulator
1401
1402         if (!engine)
1403                 return false;
1404
1405         ::SBS::SBS *Simcore = engine->GetSystem();
1406
1407         if (engine == active_engine)
1408         {
1409                 //the sky needs to be created before Prepare() is called
1410                 bool sky_result = false;
1411                 if (GetConfigBool("Skyscraper.Frontend.Caelum", true) == true)
1412                         sky_result = InitSky(engine);
1413
1414                 //create old sky if Caelum is turned off, or failed to initialize
1415                 if (sky_result == false)
1416                         Simcore->CreateSky();
1417
1418                 //switch to fullscreen mode if specified
1419                 bool fullscreen = GetConfigBool("Skyscraper.Frontend.FullScreen", false);
1420
1421                 //override fullscreen setting if specified on the command line
1422                 if (parser->Found(wxT("fullscreen")) == true)
1423                         fullscreen = true;
1424
1425                 if (fullscreen == true)
1426                         SetFullScreen(true);
1427
1428                 //resize main window
1429                 if (FullScreen == false)
1430                 {
1431                         window->SetBackgroundColour(*wxBLACK);
1432                         window->SetClientSize(GetConfigInt("Skyscraper.Frontend.ScreenWidth", 800), GetConfigInt("Skyscraper.Frontend.ScreenHeight", 600));
1433                         window->Center();
1434                 }
1435         }
1436
1437         //start simulation
1438         if (!engine->Start(mCamera))
1439                 return false;
1440
1441         //close progress dialog if no engines are loading
1442         if (IsEngineLoading() == false)
1443                 CloseProgressDialog();
1444
1445         //load control panel
1446         if (engine == active_engine)
1447         {
1448                 bool panel = GetConfigBool("Skyscraper.Frontend.ShowControlPanel", true);
1449
1450                 //override if disabled on the command line
1451                 if (parser->Found(wxT("no-panel")) == true)
1452                         panel = false;
1453
1454                 if (panel == true)
1455                 {
1456                         if (!dpanel)
1457                                 dpanel = new DebugPanel(this, NULL, -1);
1458                         dpanel->Show(true);
1459                         dpanel->SetPosition(wxPoint(GetConfigInt("Skyscraper.Frontend.ControlPanelX", 10), GetConfigInt("Skyscraper.Frontend.ControlPanelY", 25)));
1460                 }
1461         }
1462
1463         RefreshViewport();
1464
1465         //run simulation
1466         Report("Running simulation...");
1467         StopSound();
1468         if (console)
1469                 console->bSend->Enable(true);
1470         return true;
1471 }
1472
1473 void Skyscraper::AllowResize(bool value)
1474 {
1475         //changes the window style to either allow or disallow resizing
1476
1477         if (value)
1478                 window->SetWindowStyleFlag(wxDEFAULT_FRAME_STYLE | wxMAXIMIZE);
1479         else
1480                 window->SetWindowStyleFlag(wxDEFAULT_FRAME_STYLE & ~wxMAXIMIZE);
1481         window->Refresh();
1482 }
1483
1484 void Skyscraper::UnloadToMenu()
1485 {
1486         //unload to main menu
1487
1488         //exit app if ShowMenu is false
1489         if (ShowMenu == false)
1490                 Quit();
1491
1492         Pause = false;
1493         UnloadSim();
1494
1495         //cleanup sound
1496         StopSound();
1497
1498         CloseProgressDialog();
1499
1500         //return to main menu
1501         SetFullScreen(false);
1502         window->SetClientSize(GetConfigInt("Skyscraper.Frontend.Menu.Width", 640), GetConfigInt("Skyscraper.Frontend.Menu.Height", 480));
1503         window->Center();
1504         window->SetCursor(wxNullCursor);
1505
1506         ConcurrentLoads = false;
1507         RenderOnStartup = false;
1508
1509         StartSound();
1510         StartupRunning = true;
1511 }
1512
1513 void Skyscraper::Quit()
1514 {
1515         //exit app
1516         if(dpanel)
1517                 dpanel->EnableTimer(false);
1518
1519         wxGetApp().Exit();
1520 }
1521
1522 Ogre::RenderWindow* Skyscraper::CreateRenderWindow(const Ogre::NameValuePairList* miscParams, const std::string& windowName)
1523 {
1524         std::string name = windowName;
1525
1526         int width, height;
1527         window->GetClientSize(&width, &height);
1528
1529         Ogre::NameValuePairList params;
1530         if (miscParams)
1531                 params = *miscParams;
1532
1533         bool vsync = GetConfigBool("Skyscraper.Frontend.Vsync", true);
1534         if (vsync == true)
1535                 params["vsync"] = "true";
1536         else
1537                 params["vsync"] = "false";
1538         params["vsyncInterval"] = "1";
1539         params["externalWindowHandle"] = getOgreHandle();
1540
1541 #if defined(__WXMAC__)
1542         params["macAPI"] = "cocoa";
1543         params["macAPICocoaUseNSView"] = "true";
1544 #endif
1545
1546         //create the render window
1547         mRenderWindow = Ogre::Root::getSingleton().createRenderWindow(name, width, height, false, &params);
1548
1549         mRenderWindow->setActive(true);
1550         mRenderWindow->windowMovedOrResized();
1551
1552         return mRenderWindow;
1553 }
1554
1555 void Skyscraper::destroyRenderWindow()
1556 {
1557         if (mRenderWindow)
1558            Ogre::Root::getSingleton().detachRenderTarget(mRenderWindow);
1559
1560         mRenderWindow->destroy();
1561         mRenderWindow = 0;
1562 }
1563
1564 const std::string Skyscraper::getOgreHandle() const
1565 {
1566 #if defined(__WXMSW__)
1567         // Handle for Windows systems
1568         return Ogre::StringConverter::toString((size_t)((HWND)window->GetHandle()));
1569 #elif defined(__WXGTK__)
1570         // Handle for GTK-based systems
1571
1572         // wxWidgets uses several internal GtkWidgets, the GetHandle method
1573         // returns a different one than this, but wxWidgets's GLCanvas uses this
1574         // one to interact with GLX, so we do the same.
1575         // NOTE: this method relies on implementation details in wxGTK and could
1576         //      change without any notification from the developers.
1577         GtkWidget* privHandle = window->m_wxwindow;
1578
1579         // prevents flickering
1580         gtk_widget_set_double_buffered(privHandle, false);
1581
1582         gtk_widget_realize(privHandle);
1583
1584         // grab the window object
1585         GdkWindow* gdkWin = gtk_widget_get_window((GtkWidget*)window->GetHandle());
1586         Display* display = GDK_WINDOW_XDISPLAY(gdkWin);
1587         Window wid = GDK_WINDOW_XWINDOW(gdkWin);
1588
1589         // screen (returns "display.screen")
1590         std::string screenStr = DisplayString(display);
1591         screenStr = screenStr.substr(screenStr.find(".") + 1, screenStr.size());
1592
1593         std::stringstream handleStream;
1594         handleStream << (unsigned long)display << ':' << screenStr << ':' << wid;
1595
1596         // retrieve XVisualInfo
1597         // NOTE: '-lGL' linker flag must be specified.
1598         int attrlist[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 16, GLX_STENCIL_SIZE, 8, None };
1599         XVisualInfo* vi = glXChooseVisual(display, DefaultScreen(display), attrlist);
1600         handleStream << ':' << (unsigned long)vi;
1601
1602         return std::string(handleStream.str());
1603
1604 #elif defined(__WXMAC__)
1605         return Ogre::StringConverter::toString((size_t)window->GetHandle());
1606
1607 #else
1608         #error Not supported on this platform!
1609 #endif
1610 }
1611
1612 int Skyscraper::GetConfigInt(const std::string &key, int default_value)
1613 {
1614         std::string result = configfile->getSetting(key, "", ToString(default_value));
1615         return ToInt(result);
1616 }
1617
1618 std::string Skyscraper::GetConfigString(const std::string &key, const std::string &default_value)
1619 {
1620         return configfile->getSetting(key, "", default_value);
1621 }
1622
1623 bool Skyscraper::GetConfigBool(const std::string &key, bool default_value)
1624 {
1625         std::string result = configfile->getSetting(key, "", BoolToString(default_value));
1626         return ToBool(result);
1627 }
1628
1629 float Skyscraper::GetConfigFloat(const std::string &key, float default_value)
1630 {
1631         std::string result = configfile->getSetting(key, "", ToString(default_value));
1632         return ToFloat(result);
1633 }
1634
1635 bool Skyscraper::InitSky(EngineContext *engine)
1636 {
1637         //initialize sky
1638
1639         if (!engine)
1640                 return false;
1641
1642         if (Headless == true)
1643                 return true;
1644
1645         //ensure graphics card and render system are capable of Caelum's shaders
1646         if (Renderer == "Direct3D9")
1647         {
1648                 //on DirectX, Caelum requires a card capable of 3.0 shader levels, which would be
1649                 //an ATI Radeon HD 2000, nVidia Geforce 6, Intel G965 or newer
1650                 //Intel cards: http://www.intel.com/support/graphics/sb/cs-014257.htm
1651                 Ogre::RenderSystemCapabilities::ShaderProfiles profiles = mRoot->getRenderSystem()->getCapabilities()->getSupportedShaderProfiles();
1652
1653                 //for general sky, require both DirectX pixel and vertex shaders 2.0
1654                 if (profiles.find("ps_2_0") == profiles.end() ||
1655                         profiles.find("vs_2_0") == profiles.end())
1656                                 return ReportFatalError("Error initializing Caelum: 2.0 shaders not supported");
1657
1658                 //for clouds, require either DirectX pixel shaders 3.0 or nVidia fragment shaders 4.0
1659                 if (profiles.find("ps_3_0") == profiles.end() &&
1660                         profiles.find("fp40") == profiles.end())
1661                                 return ReportFatalError("Error initializing Caelum: 3.0 fragment shaders not supported");
1662
1663                 //for clouds, require either DirectX vetex shaders 3.0 or nVidia vertex shaders 4.0
1664                 if (profiles.find("vs_3_0") == profiles.end() &&
1665                         profiles.find("vp40") == profiles.end())
1666                                 return ReportFatalError("Error initializing Caelum: 3.0 vertex shaders not supported");
1667         }
1668
1669         if (Renderer == "OpenGL")
1670         {
1671                 //on OpenGL, Caelum requires hardware support for shaders (OpenGL 2.0 or newer)
1672                 Ogre::RenderSystemCapabilities::ShaderProfiles profiles = mRoot->getRenderSystem()->getCapabilities()->getSupportedShaderProfiles();
1673
1674                 //require OpenGL ARB fragment programs
1675                 if (profiles.find("arbfp1") == profiles.end())
1676                         return ReportFatalError("Error initializing Caelum: fragment programs not supported");
1677
1678                 //require OpenGL ARB vertex programs
1679                 if (profiles.find("arbvp1") == profiles.end())
1680                         return ReportFatalError("Error initializing Caelum: vertex programs not supported");
1681         }
1682
1683         //load Caelum resources
1684         try
1685         {
1686                 Ogre::ResourceGroupManager::getSingleton().addResourceLocation("data/caelum", "FileSystem", "Caelum", false);
1687                 Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup("Caelum");
1688
1689                 if (!mCaelumSystem)
1690                         mCaelumSystem = new Caelum::CaelumSystem(mRoot, mSceneMgr, Caelum::CaelumSystem::CAELUM_COMPONENTS_NONE);
1691                 Caelum::CaelumPlugin::getSingleton().loadCaelumSystemFromScript(mCaelumSystem, SkyName);
1692         }
1693         catch (Ogre::Exception &e)
1694         {
1695                 ReportFatalError("Error initializing Caelum:\nDetails: " + e.getDescription());
1696         }
1697         catch (...)
1698         {
1699                 ReportFatalError("Error initializing Caelum");
1700         }
1701
1702         if (!mCaelumSystem)
1703                 return false;
1704
1705         //attach caelum to running viewport
1706         try
1707         {
1708                 mCaelumSystem->attachViewport(mViewport);
1709                 mCaelumSystem->setAutoNotifyCameraChanged(false);
1710                 mCaelumSystem->setSceneFogDensityMultiplier(GetConfigFloat("Skyscraper.Frontend.FogMultiplier", 0.1f) / 1000);
1711                 if (GetConfigBool("Skyscraper.Frontend.EnableFog", true) == false)
1712                         mCaelumSystem->setManageSceneFog(Ogre::FOG_NONE);
1713                 mCaelumSystem->setManageAmbientLight(GetConfigBool("Skyscraper.Frontend.ModifyAmbient", false));
1714
1715                 //fix sky rotation
1716                 Ogre::Quaternion rot(Ogre::Degree(180.0f), Ogre::Vector3::UNIT_Y);
1717                 mCaelumSystem->getCaelumGroundNode()->setOrientation(rot);
1718                 mCaelumSystem->getCaelumCameraNode()->setOrientation(rot);
1719
1720                 //have sky use SBS scaling factor
1721                 float scale = 1 / engine->GetSystem()->UnitScale;
1722                 mCaelumSystem->getCaelumGroundNode()->setScale(scale, scale, scale);
1723                 mCaelumSystem->getCaelumCameraNode()->setScale(scale, scale, scale);
1724         }
1725         catch (Ogre::Exception &e)
1726         {
1727                 ReportFatalError("Error setting Caelum parameters:\nDetails: " + e.getDescription());
1728         }
1729         catch (...)
1730         {
1731                 ReportFatalError("Error setting Caelum parameters");
1732         }
1733
1734         //set sky time multiplier if not already set
1735         if (SkyMult == 0)
1736                 SkyMult = mCaelumSystem->getTimeScale();
1737
1738         //set location if specified
1739         if (new_location == true)
1740         {
1741                 mCaelumSystem->setObserverLatitude(Ogre::Degree(latitude));
1742                 mCaelumSystem->setObserverLongitude(Ogre::Degree(longitude));
1743                 new_location = false;
1744         }
1745
1746         //set date/time if specified
1747         if (new_time == true)
1748         {
1749                 mCaelumSystem->setJulianDay(datetime);
1750                 new_time = false;
1751         }
1752
1753         return true;
1754 }
1755
1756 void Skyscraper::UpdateSky()
1757 {
1758         //update sky
1759         SBS_PROFILE_MAIN("Sky");
1760
1761         if (mCaelumSystem)
1762         {
1763                 mCaelumSystem->notifyCameraChanged(mCamera);
1764                 mCaelumSystem->setTimeScale(SkyMult);
1765                 mCaelumSystem->updateSubcomponents(float(active_engine->GetSystem()->GetElapsedTime()) / 1000);
1766         }
1767 }
1768
1769 void Skyscraper::messageLogged(const Ogre::String &message, Ogre::LogMessageLevel lml, bool maskDebug, const Ogre::String &logName, bool &skipThisMessage)
1770 {
1771         //callback function that receives OGRE log messages
1772         if (console)
1773         {
1774                 console->Write(message);
1775                 console->Update();
1776         }
1777 }
1778
1779 void Skyscraper::ShowConsole(bool send_button)
1780 {
1781         if (!console)
1782                 console = new Console(this, NULL, -1);
1783         console->Show();
1784         console->Raise();
1785         console->SetPosition(wxPoint(GetConfigInt("Skyscraper.Frontend.ConsoleX", 10), GetConfigInt("Skyscraper.Frontend.ConsoleY", 25)));
1786         console->bSend->Enable(send_button);
1787 }
1788
1789 void Skyscraper::CreateProgressDialog(const std::string &message)
1790 {
1791         //don't create progress dialog if concurrent loading is enabled, and one engine is already running
1792         if (GetEngineCount() > 1 && ConcurrentLoads == true)
1793         {
1794                 if (GetFirstValidEngine()->IsRunning() == true)
1795                         return;
1796         }
1797
1798         if (!progdialog)
1799         {
1800                 //show progress dialog in a queued fashion
1801                 show_progress = true;
1802                 prog_text = message;
1803         }
1804         else
1805         {
1806                 wxString msg = progdialog->GetMessage();
1807                 msg += "\n";
1808                 msg += message;
1809                 progdialog->Update(progdialog->GetValue(), msg);
1810         }
1811
1812         //stop control panel timer
1813         if (dpanel)
1814                 dpanel->EnableTimer(false);
1815 }
1816
1817 void Skyscraper::CloseProgressDialog()
1818 {
1819         //close progress dialog
1820         if (progdialog)
1821                 progdialog->Destroy();
1822         progdialog = 0;
1823
1824         //start control panel timer
1825         if (dpanel)
1826                 dpanel->EnableTimer(true);
1827 }
1828
1829 void Skyscraper::ShowProgressDialog()
1830 {
1831         if (!progdialog)
1832                 progdialog = new wxProgressDialog(wxT("Loading..."), prog_text, 100, window);
1833
1834         show_progress = false;
1835 }
1836
1837 void Skyscraper::UpdateProgress()
1838 {
1839         if (!progdialog)
1840                 return;
1841
1842         int total_percent = GetEngineCount() * 100;
1843         int current_percent = 0;
1844
1845         for (size_t i = 0; i < engines.size(); i++)
1846         {
1847                 if (engines[i])
1848                         current_percent += engines[i]->GetProgress();
1849         }
1850
1851         int final = ((float)current_percent / (float)total_percent) * 100;
1852         progdialog->Update(final);
1853 }
1854
1855 void Skyscraper::SetFullScreen(bool enabled)
1856 {
1857         //enable/disable fullscreen
1858
1859         FullScreen = enabled;
1860         window->ShowFullScreen(FullScreen);
1861
1862 #if defined(__WXMSW__)
1863         //in Windows, enable double-buffering when switching to fullscreen
1864         //to fix framerate drop issues
1865         /*if (enabled == true)
1866                 window->SetDoubleBuffered(true);
1867         else
1868                 window->SetDoubleBuffered(false);*/
1869 #endif
1870 }
1871
1872 void Skyscraper::SetLocation(float latitude, float longitude)
1873 {
1874         this->latitude = latitude;
1875         this->longitude = longitude;
1876         new_location = true;
1877 }
1878
1879 void Skyscraper::SetDateTime(double julian_date_time)
1880 {
1881         datetime = julian_date_time;
1882         new_time = true;
1883 }
1884
1885 EngineContext* Skyscraper::CreateEngine(EngineContext *parent, const Ogre::Vector3 &position, float rotation, const Ogre::Vector3 &area_min, const Ogre::Vector3 &area_max)
1886 {
1887         EngineContext* engine = new EngineContext(parent, this, mSceneMgr, soundsys, position, rotation, area_min, area_max);
1888         return engine;
1889 }
1890
1891 bool Skyscraper::DeleteEngine(EngineContext *engine)
1892 {
1893         //delete a specified sim engine instance
1894
1895         if (!engine)
1896                 return false;
1897
1898         for (size_t i = 0; i < engines.size(); i++)
1899         {
1900                 if (engines[i] == engine)
1901                 {
1902                         engines[i] = 0;
1903                         delete engine;
1904
1905                         int count = GetEngineCount();
1906
1907                         if (active_engine == engine)
1908                         {
1909                                 active_engine = 0;
1910                                 if (count > 0)
1911                                 {
1912                                         int number = GetFirstValidEngine()->GetNumber();
1913                                         SetActiveEngine(number);
1914                                 }
1915                         }
1916                         else if (active_engine)
1917                                 active_engine->RefreshCamera();
1918
1919                         //exit to main menu if all engines have been deleted
1920                         if (count == 0)
1921                                 Shutdown = true;
1922
1923                         return true;
1924                 }
1925         }
1926         return false;
1927 }
1928
1929 void Skyscraper::DeleteEngines()
1930 {
1931         //delete all sim emgine instances
1932
1933         for (size_t i = 0; i < engines.size(); i++)
1934         {
1935                 if (engines[i])
1936                         delete engines[i];
1937         }
1938         engines.clear();
1939         active_engine = 0;
1940 }
1941
1942 EngineContext* Skyscraper::FindActiveEngine()
1943 {
1944         //find engine instance with an active camera
1945
1946         for (size_t i = 0; i < engines.size(); i++)
1947         {
1948                 if (engines[i])
1949                 {
1950                         if (engines[i]->IsCameraActive() == true)
1951                                 return engines[i];
1952                 }
1953         }
1954         return active_engine;
1955 }
1956
1957 void Skyscraper::SetActiveEngine(int number, bool switch_engines)
1958 {
1959         //set an engine instance to be active
1960
1961         EngineContext *engine = GetEngine(number);
1962
1963         if (!engine)
1964                 return;
1965
1966         if (active_engine == engine)
1967                 return;
1968
1969         //don't switch if the camera's already active in the specified engine
1970         if (engine->IsCameraActive() == true)
1971                 return;
1972
1973         //don't switch to engine if it's loading
1974         if (engine->IsLoading() == true)
1975                 return;
1976
1977         CameraState state;
1978         bool state_set = false;
1979
1980         if (active_engine)
1981         {
1982                 //get previous engine's camera state
1983                 if (switch_engines == true)
1984                 {
1985                         state = active_engine->GetCameraState();
1986                         state_set = true;
1987                 }
1988
1989                 //detach camera from current engine
1990                 active_engine->DetachCamera(switch_engines);
1991         }
1992
1993         Report("Setting engine " + ToString(number) + " as active");
1994
1995         //switch context to new engine instance
1996         active_engine = engine;
1997         active_engine->AttachCamera(mCamera, !switch_engines);
1998
1999         //apply camera state to new engine
2000         if (switch_engines == true && state_set == true)
2001                 active_engine->SetCameraState(state, false);
2002
2003         //update mouse cursor for freelook mode
2004         window->EnableFreelook(active_engine->GetSystem()->camera->Freelook);
2005 }
2006
2007 bool Skyscraper::RunEngines()
2008 {
2009         bool result = true;
2010         bool isloading = IsEngineLoading();
2011
2012         if (ConcurrentLoads == true && isloading == true)
2013                 RefreshViewport();
2014
2015         for (size_t i = 0; i < engines.size(); i++)
2016         {
2017                 if (!engines[i])
2018                         continue;
2019
2020                 //process engine run loops, and also prevent other instances from running if
2021                 //one or more engines are loading
2022                 if (ConcurrentLoads == true || isloading == false || engines[i]->IsLoading() == true)
2023                 {
2024                         bool run = true;
2025                         if (i > 0 && ConcurrentLoads == false)
2026                         {
2027                                 //if concurrent loads is off, skip running if previous engine is not finished loading
2028                                 if (engines[i - 1])
2029                                 {
2030                                         if (engines[i - 1]->IsLoading() == true && engines[i - 1]->IsLoadingFinished() == false)
2031                                                 run = false;
2032                                 }
2033                         }
2034
2035                         if (engines[i]->IsLoadingFinished() == false && run == true)
2036                         {
2037                                 if (engines[i]->Run() == false)
2038                                         result = false;
2039                         }
2040                 }
2041
2042                 //start engine if loading is finished
2043                 if (engines[i]->IsLoadingFinished() == true)
2044                 {
2045                         if (active_engine)
2046                         {
2047                                 //exit if active engine is still loading
2048                                 if (active_engine->IsLoading() == true && active_engine->IsLoadingFinished() == false)
2049                                         continue;
2050
2051                                 //exit if active engine is finished, but other buildings are still loading
2052                                 if (active_engine->IsLoadingFinished() == true && isloading == true)
2053                                         continue;
2054                         }
2055                         Start(engines[i]);
2056                 }
2057         }
2058         return result;
2059 }
2060
2061 bool Skyscraper::IsEngineLoading()
2062 {
2063         //return true if an engine is loading
2064
2065         bool result = false;
2066         for (size_t i = 0; i < engines.size(); i++)
2067         {
2068                 if (engines[i])
2069                 {
2070                         if (engines[i]->IsLoading() == true && engines[i]->IsLoadingFinished() == false)
2071                                 result = true;
2072                 }
2073         }
2074         return result;
2075 }
2076
2077 void Skyscraper::HandleEngineShutdown()
2078 {
2079         bool deleted = false;
2080
2081         for (size_t i = 0; i < engines.size(); i++)
2082         {
2083                 if (engines[i])
2084                 {
2085                         if (engines[i]->GetShutdownState() == true)
2086                         {
2087                                 if (DeleteEngine(engines[i]) == true)
2088                                 {
2089                                         RefreshViewport();
2090                                         i--;
2091                                         deleted = true;
2092                                 }
2093                         }
2094                 }
2095         }
2096
2097         //clean up empty engine slots at the end of the list
2098         if (deleted == true)
2099         {
2100                 for (size_t i = engines.size() - 1; i < engines.size(); --i)
2101                 {
2102                         if (!engines[i])
2103                                 engines.erase(engines.begin() + i);
2104                         else
2105                                 break;
2106                 }
2107         }
2108 }
2109
2110 void Skyscraper::HandleReload()
2111 {
2112         //reload building if requested
2113
2114         for (size_t i = 0; i < engines.size(); i++)
2115         {
2116                 if (engines[i])
2117                 {
2118                         if (engines[i]->Reload == true)
2119                         {
2120                                 Pause = false;
2121                                 engines[i]->DoReload();
2122                         }
2123                 }
2124         }
2125 }
2126
2127 EngineContext* Skyscraper::GetEngine(int number)
2128 {
2129         //get an engine by instance number
2130
2131         if (number < 0 || number >= (int)engines.size())
2132                 return 0;
2133
2134         return engines[number];
2135 }
2136
2137 int Skyscraper::GetEngineCount()
2138 {
2139         //get number of valid engines
2140
2141         int count = 0;
2142
2143         for (size_t i = 0; i < engines.size(); i++)
2144         {
2145                 if (engines[i])
2146                         count++;
2147         }
2148         return count;
2149 }
2150
2151 void Skyscraper::RaiseWindow()
2152 {
2153         window->Raise();
2154         window->SetFocus();
2155 }
2156
2157 void Skyscraper::RefreshConsole()
2158 {
2159         if (console)
2160         {
2161                 console->Refresh();
2162                 console->Update();
2163         }
2164 }
2165
2166 void Skyscraper::RefreshViewport()
2167 {
2168         //refresh viewport to prevent rendering issues
2169
2170         if (Headless == false)
2171                 mViewport->_updateDimensions();
2172 }
2173
2174 void Skyscraper::SwitchEngines()
2175 {
2176         //if user is no longer inside the active engine, find a new engine to attach to
2177
2178         if (!active_engine)
2179                 return;
2180
2181         if (active_engine->IsInside() == true)
2182                 return;
2183
2184         EngineContext *parent = active_engine->GetParent();
2185
2186         //if active engine has a parent, switch to the parent if possible
2187         if (parent)
2188         {
2189                 if (parent->IsInside() == true && parent->IsCameraActive() == false)
2190                 {
2191                         SetActiveEngine(parent->GetNumber(), true);
2192                         return;
2193                 }
2194         }
2195
2196         //otherwise search for a valid engine to attach to
2197         for (size_t i = 0; i < engines.size(); i++)
2198         {
2199                 if (engines[i] != active_engine && engines[i])
2200                 {
2201                         if (engines[i]->IsInside() == true && engines[i]->IsCameraActive() == false)
2202                         {
2203                                 SetActiveEngine((int)i, true);
2204                                 return;
2205                         }
2206                 }
2207         }
2208
2209         //if user has stepped outside of all sim engines, revert the movement
2210         //to place them back into the active engine
2211         active_engine->RevertMovement();
2212 }
2213
2214 bool Skyscraper::IsValidEngine(EngineContext *engine)
2215 {
2216         if (!engine)
2217                 return false;
2218
2219         for (size_t i = 0; i < engines.size(); i++)
2220         {
2221                 if (engines[i] == engine)
2222                         return true;
2223         }
2224         return false;
2225 }
2226
2227 bool Skyscraper::IsValidSystem(::SBS::SBS *sbs)
2228 {
2229         if (!sbs)
2230                 return false;
2231
2232         for (size_t i = 0; i < engines.size(); i++)
2233         {
2234                 if (engines[i])
2235                 {
2236                         if (engines[i]->GetSystem() == sbs)
2237                                 return true;
2238                 }
2239         }
2240         return false;
2241 }
2242
2243 int Skyscraper::GetFreeInstanceNumber()
2244 {
2245         //get an available engine instance number
2246
2247         for (size_t i = 0; i < engines.size(); i++)
2248         {
2249                 if (!engines[i])
2250                         return (int)i;
2251         }
2252         return (int)engines.size();
2253 }
2254
2255 int Skyscraper::RegisterEngine(EngineContext *engine)
2256 {
2257         //register an engine with the frontend, returning the assigned instance number
2258
2259         int number = GetFreeInstanceNumber();
2260
2261         if (number < (int)engines.size())
2262                 engines[number] = engine;
2263         else
2264                 engines.push_back(engine);
2265
2266         return number;
2267 }
2268
2269 EngineContext* Skyscraper::GetFirstValidEngine()
2270 {
2271         for (size_t i = 0; i < engines.size(); i++)
2272         {
2273                 if (engines[i])
2274                         return engines[i];
2275         }
2276         return 0;
2277 }
2278
2279 void Skyscraper::EnableSky(bool value)
2280 {
2281         //enable or disable sky system
2282
2283         //enable/disable old skybox system in engine 0
2284         if (GetEngine(0))
2285                 GetEngine(0)->GetSystem()->EnableSkybox(value);
2286
2287         //enable/disable Caelum sky system
2288         if (mCaelumSystem)
2289         {
2290                 mCaelumSystem->getCaelumGroundNode()->setVisible(value);
2291                 mCaelumSystem->getCaelumCameraNode()->setVisible(value);
2292         }
2293 }
2294
2295 void Skyscraper::MacOpenFile(const wxString &filename)
2296 {
2297         //support launching app with a building file, on Mac
2298
2299         if (IsEngineLoading() == true)
2300                 return;
2301
2302         if (StartupRunning == true)
2303                 StopSound();
2304
2305         //strip path from filename
2306         wxFileName file (filename);
2307
2308         Load(file.GetFullName().ToStdString());
2309 }
2310
2311 }