Skyscraper 2.0
startscreen.cpp
Go to the documentation of this file.
1/*
2 Skyscraper 2.0 - Startup Screen
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#endif
26#include "globals.h"
27#include "skyscraper.h"
28#include "vm.h"
29#include "hal.h"
30#include "mainscreen.h"
31#include "startscreen.h"
32
33using namespace SBS;
34
35namespace Skyscraper
36{
37
39{
40 this->frontend = frontend;
41 this->vm = frontend->GetVM();
42 buttons = 0;
43 buttoncount = 0;
46}
47
52
54{
55 //draw menu background
56
57 bool result = false;
58
59 HAL *hal = vm->GetHAL();
60
61 result = DrawImage("data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Image", "menu.png"), 0, -1, -1, false);
62
63 if (result == false)
64 return false;
65
66 if (buttoncount == 0)
67 {
68 buttoncount = hal->GetConfigInt(hal->configfile, "Skyscraper.Frontend.Menu.Buttons", 5);
70
71 for (int i = 0; i < buttoncount; i++)
72 {
73 buttons[i].node = 0;
74 buttons[i].drawn_selected = false;
75 buttons[i].drawn_pressed = false;
77 buttons[i].rect = 0;
78 }
79 }
80
81 for (int i = 0; i < buttoncount; i++)
82 {
83 std::string b1, b2, b3;
84 Real x = 0, y = 0;
85 bool center = false;
86 std::string number = ToString(i + 1);
87
88 if (i == 0)
89 {
90 b1 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button1.Image", "button_triton.png");
91 b2 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button1.Selected", "button_triton_selected.png");
92 b3 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button1.Pressed", "button_triton_pressed.png");
93 x = hal->GetConfigFloat(hal->configfile, "Skyscraper.Frontend.Menu.Button1.X", 0.0);
94 y = hal->GetConfigFloat(hal->configfile, "Skyscraper.Frontend.Menu.Button1.Y", -0.08);
95 center = hal->GetConfigBool(hal->configfile, "Skyscraper.Frontend.Menu.Button1.Center", true);
96 }
97 if (i == 1)
98 {
99 b1 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button2.Image", "button_glasstower.png");
100 b2 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button2.Selected", "button_glasstower_selected.png");
101 b3 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button2.Pressed", "button_glasstower_pressed.png");
102 x = hal->GetConfigFloat(hal->configfile, "Skyscraper.Frontend.Menu.Button2.X", 0.0);
103 y = hal->GetConfigFloat(hal->configfile, "Skyscraper.Frontend.Menu.Button2.Y", 0.125);
104 center = hal->GetConfigBool(hal->configfile, "Skyscraper.Frontend.Menu.Button2.Center", true);
105 }
106 if (i == 2)
107 {
108 b1 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button3.Image", "button_searstower.png");
109 b2 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button3.Selected", "button_searstower_selected.png");
110 b3 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button3.Pressed", "button_searstower_pressed.png");
111 x = hal->GetConfigFloat(hal->configfile, "Skyscraper.Frontend.Menu.Button3.X", 0.0);
112 y = hal->GetConfigFloat(hal->configfile, "Skyscraper.Frontend.Menu.Button3.Y", 0.333);
113 center = hal->GetConfigBool(hal->configfile, "Skyscraper.Frontend.Menu.Button3.Center", true);
114 }
115 if (i == 3)
116 {
117 b1 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button4.Image", "button_simple.png");
118 b2 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button4.Selected", "button_simple_selected.png");
119 b3 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button4.Pressed", "button_simple_pressed.png");
120 x = hal->GetConfigFloat(hal->configfile, "Skyscraper.Frontend.Menu.Button4.X", 0.0);
121 y = hal->GetConfigFloat(hal->configfile, "Skyscraper.Frontend.Menu.Button4.Y", 0.541);
122 center = hal->GetConfigBool(hal->configfile, "Skyscraper.Frontend.Menu.Button4.Center", true);
123 }
124 if (i == 4)
125 {
126 b1 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button5.Image", "button_other.png");
127 b2 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button5.Selected", "button_other_selected.png");
128 b3 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button5.Pressed", "button_other_pressed.png");
129 x = hal->GetConfigFloat(hal->configfile, "Skyscraper.Frontend.Menu.Button5.X", 0.0);
130 y = hal->GetConfigFloat(hal->configfile, "Skyscraper.Frontend.Menu.Button5.Y", 0.75);
131 center = hal->GetConfigBool(hal->configfile, "Skyscraper.Frontend.Menu.Button5.Center", true);
132 }
133 if (i > 4)
134 {
135 b1 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button" + number + ".Image", "");
136 b2 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button" + number + ".Selected", "");
137 b3 = "data/" + hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button" + number + ".Pressed", "");
138 x = hal->GetConfigFloat(hal->configfile, "Skyscraper.Frontend.Menu.Button" + number + ".X", 0.0);
139 y = hal->GetConfigFloat(hal->configfile, "Skyscraper.Frontend.Menu.Button" + number + ".Y", 0.0);
140 center = hal->GetConfigBool(hal->configfile, "Skyscraper.Frontend.Menu.Button" + number + ".Center", true);
141 }
142
143 result = DrawImage(b1, &buttons[i], x, y, center, b2, b3);
144
145 if (result == false)
146 return false;
147 }
148
149 return true;
150}
151
152bool StartScreen::DrawImage(const std::string &filename, buttondata *button, Real x, Real y, bool center, const std::string &filename_selected, const std::string &filename_pressed)
153{
154 //X and Y represent the image's top-left location.
155 //values are -1 for the top left, 1 for the top right, -1 for the top, and 1 for the bottom
156 //center is at 0, 0
157
158 Real w, h;
159 int w_orig = 0, h_orig = 0, w2 = 0, h2 = 0;
160 bool background = false;
161
162 std::string material = "";
163 std::string Filename = filename;
164
165 if (filename == "")
166 return false;
167
168 //exit if background has already been drawn
169 if (background_image == Filename)
170 return true;
171
172 Ogre::TextureManager::getSingleton().setVerbose(false);
173 Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().getByName(Filename);
174 if (tex)
175 material = Filename;
176
177 //load image data from file, if not already preloaded
178 if (material == "")
179 {
180 int count = 1;
181 if (button)
182 count = 3;
183
184 for (int i = 0; i < count; i++)
185 {
186 if (i == 0)
187 Filename = filename;
188 if (i == 1)
189 Filename = filename_selected;
190 if (i == 2)
191 Filename = filename_pressed;
192
193 //create new material
194 Ogre::MaterialPtr mat;
195 try
196 {
197 mat = Ogre::MaterialManager::getSingleton().create(Filename, "General");
198 }
199 catch (Ogre::Exception& e)
200 {
201 return vm->GetHAL()->ReportFatalError("Error creating material for texture " + Filename + "\n" + e.getDescription(), "");
202 }
203
204 //load image data from file
205 Ogre::Image img;
206 try
207 {
208 img.load(Filename, "General");
209 }
210 catch (Ogre::Exception &e)
211 {
212 return vm->GetHAL()->ReportFatalError("Error loading texture " + Filename + "\n" + e.getDescription(), "");
213 }
214
215 w_orig = img.getWidth();
216 h_orig = img.getHeight();
217
218 //round up image size to power-of-2 value
219 w2 = powf(2, ceilf(Log2((Real)w_orig)));
220 h2 = powf(2, ceilf(Log2((Real)h_orig)));
221
222 //create texture and blit image onto it - this solves texture quality issues on mainly D3D9
223 tex = Ogre::TextureManager::getSingleton().createManual(Filename, "General", Ogre::TEX_TYPE_2D, w2, h2, 0, Ogre::PF_R8G8B8A8, Ogre::TU_STATIC);
224 tex->getBuffer(0, 0)->blitFromMemory(img.getPixelBox(0, 0), Ogre::Box(0, 0, 0, w_orig, h_orig, img.getDepth()));
225
226 //bind texture to material and set options
227 Ogre::TextureUnitState *state = mat->getTechnique(0)->getPass(0)->createTextureUnitState(Filename);
228 Ogre::Pass *pass = mat->getTechnique(0)->getPass(0);
229 pass->setDepthCheckEnabled(false);
230 pass->setDepthWriteEnabled(false);
231 pass->setLightingEnabled(false);
232 pass->setTextureFiltering(Ogre::TFO_NONE);
233
234 //rescale texture
235 state->setTextureScale((Real)w2 / (Real)w_orig, (Real)h2 / (Real)h_orig);
236 state->setTextureScroll(-(Real(w2 - w_orig) / (Real)w2) / 2.0, -(Real(h2 - h_orig) / (Real)h2) / 2.0);
237
238 if (tex->hasAlpha() == true && button)
239 {
240 mat->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);
241 mat->getTechnique(0)->getPass(0)->setAlphaRejectSettings(Ogre::CMPF_GREATER_EQUAL, 128);
242 }
243 }
244 material = Filename;
245 if (button)
246 {
247 button->node = 0;
248 button->drawn_selected = false;
249 button->drawn_pressed = false;
250 button->active_button = 0;
251 button->rect = 0;
252 }
253 else
254 {
255 background_rect = 0;
256 background_node = 0;
257 }
258 }
259
260 //exit if requested button is already visible
261 if (button)
262 {
263 if (button->drawn_selected == false && button->drawn_pressed == false)
264 {
265 if (button->active_button == 1)
266 return true;
267 else
268 {
269 button->active_button = 1;
270 material = filename;
271 }
272 }
273
274 if (button->drawn_selected == true)
275 {
276 if (button->active_button == 2)
277 return true;
278 else
279 {
280 button->active_button = 2;
281 material = filename_selected;
282 }
283 }
284
285 if (button->drawn_pressed == true)
286 {
287 if (button->active_button == 3)
288 return true;
289 else
290 {
291 button->active_button = 3;
292 material = filename_pressed;
293 }
294 }
295 }
296
297 //set values and draw button
298 if (material != "")
299 {
300 //apply content scaling factor, fixes issues for example on Retina displays
301#ifdef USING_WX
302 Real scale = frontend->GetWindow()->GetContentScaleFactor();
303#else
304 Real scale = 1.0;
305#endif
306
307#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
308 //set scale to 1.0 on MacOS versions earlier than 10.15
309 if (vm->macos_major == 10 && vm->macos_minor < 15)
310 scale = 1.0;
311#endif
312
313 w = w_orig / (vm->GetHAL()->GetRenderWindow()->getWidth() / 2.0 / scale);
314 h = h_orig / (vm->GetHAL()->GetRenderWindow()->getHeight() / 2.0 / scale);
315 if (button)
316 {
317 //delete previous object
318 if (button->node)
319 button->node->detachAllObjects();
320 if (button->rect)
321 delete button->rect;
322 button->rect = 0;
323
324 if (button->filename == "")
325 {
326 //store general button data
327 button->filename = filename;
328 button->filename_selected = filename_selected;
329 button->filename_pressed = filename_pressed;
330
331 button->offset_x = x;
332 button->offset_y = y;
333 if (center == true)
334 {
335 button->x = x - (w / 2);
336 button->y = y - (h / 2);
337 }
338 else
339 {
340 button->x = x;
341 button->y = y;
342 }
343 button->size_x = w;
344 button->size_y = h;
345 }
346
347 x = button->x;
348 y = button->y;
349 w = button->size_x;
350 h = button->size_y;
351 }
352 else
353 {
354 background_image = material;
355 background = true;
356 if (center == true)
357 {
358 x += -(w / 2);
359 y += -(h / 2);
360 }
361 }
362
363 //create rectangle
364 Ogre::Rectangle2D* rect = new Ogre::Rectangle2D(true);
365 rect->setCorners(x, -y, x + w, -(y + h));
366 Ogre::MaterialPtr materialptr = Ogre::MaterialManager::getSingleton().getByName(material);
367 rect->setMaterial(materialptr);
368 if (background == true)
369 rect->setRenderQueueGroup(Ogre::RENDER_QUEUE_BACKGROUND);
370
371 //set infinite bounding box
372 Ogre::AxisAlignedBox aabInf;
373 aabInf.setInfinite();
374 rect->setBoundingBox(aabInf);
375
376 //attach scene node
377 Ogre::SceneNode* node = vm->GetHAL()->GetSceneManager()->getRootSceneNode()->createChildSceneNode();
378 node->attachObject(rect);
379 if (button)
380 {
381 button->node = node;
382 button->rect = rect;
383 }
384 else
385 {
386 background_node = node;
387 background_rect = rect;
388 }
389 }
390 return true;
391}
392
394{
395 //input handler for main menu
396
397 //exit if there aren't any buttons
398 if (!buttons || buttoncount == 0)
399 return false;
400
401#ifdef USING_WX
402 MainScreen *window = frontend->GetWindow();
403
404 //get mouse coordinates
405 int mouse_x = window->ScreenToClient(wxGetMousePosition()).x;
406 int mouse_y = window->ScreenToClient(wxGetMousePosition()).y;
407
408 for (int i = 0; i < buttoncount; i++)
409 {
410 buttondata *button = &buttons[i];
411
412 //only process buttons if main window is selected
413 if (window->Active != false)
414 {
415 Real mx = mouse_x;
416 Real my = mouse_y;
417 Real w = mx / window->GetClientSize().x;
418 Real h = my / window->GetClientSize().y;
419 Real mouse_x_rel = (w * 2) - 1;
420 Real mouse_y_rel = (h * 2) - 1;
421
422 //change button status based on mouse position and button press status
423 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)
424 {
425 if (button->drawn_selected == false && wxGetMouseState().LeftIsDown() == false)
426 {
427 if (button->drawn_pressed == true)
428 {
429 //user clicked on button
430 button->drawn_selected = true;
431 Click(i);
432 return true;
433 }
434 button->drawn_selected = true;
435 }
436 if (button->drawn_pressed == false && wxGetMouseState().LeftIsDown() == true)
437 {
438 button->drawn_pressed = true;
439 button->drawn_selected = false;
440 }
441 }
442 else if (button->drawn_selected == true || button->drawn_pressed == true)
443 {
444 button->drawn_selected = false;
445 button->drawn_pressed = false;
446 }
447 }
448 }
449#endif
450 return true;
451}
452
453void StartScreen::Click(int index)
454{
455 //user clicked a button
456
457 std::string number = ToString(index + 1);
458 std::string filename = "";
459
460 HAL *hal = vm->GetHAL();
461
462 if (index == 0)
463 filename = hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button1.File", "Triton Center.bld");
464 if (index == 1)
465 filename = hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button2.File", "Glass Tower.bld");
466 if (index == 2)
467 filename = hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button3.File", "Sears Tower.bld");
468 if (index == 3)
469 filename = hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button4.File", "Simple.bld");
470 if (index > 3)
471 filename = hal->GetConfigString(hal->configfile, "Skyscraper.Frontend.Menu.Button" + number + ".File", "");
472
473 if (filename == "")
474 {
475 //show file selection dialog
476 filename = frontend->SelectBuilding();
477 }
478
479 if (filename != "")
480 {
481 frontend->Load(filename);
482 }
483}
484
486{
487 if (buttoncount > 0)
488 {
489 for (int i = 0; i < buttoncount; i++)
490 {
491 buttondata *button = &buttons[i];
492
493 if (button->node)
494 {
495 button->node->detachAllObjects();
496 button->node->getParent()->removeChild(button->node);
497 button->node = 0;
498 }
499 if (button->rect)
500 delete button->rect;
501 button->rect = 0;
502 }
503 delete [] buttons;
504 buttoncount = 0;
505 }
506 buttons = 0;
507
508 if (background_node)
509 {
510 background_node->detachAllObjects();
511 background_node->getParent()->removeChild(background_node);
512 background_node = 0;
513 }
514 if (background_rect)
515 delete background_rect;
516 background_rect = 0;
517 background_image = "";
518}
519
520}
bool ReportFatalError(const std::string &message, const std::string &prompt)
Definition hal.cpp:256
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
Ogre::RenderWindow * GetRenderWindow()
Definition hal.cpp:847
Ogre::SceneManager * GetSceneManager()
Definition hal.cpp:895
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
Ogre::ConfigFile * configfile
Definition hal.h:97
MainScreen * GetWindow()
std::string SelectBuilding()
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)
bool DrawImage(const std::string &filename, buttondata *button, Real x, Real y, bool center, const std::string &filename_selected="", const std::string &filename_pressed="")
Ogre::Rectangle2D * background_rect
Definition startscreen.h:58
StartScreen(Skyscraper *frontend)
std::string background_image
Definition startscreen.h:60
Ogre::SceneNode * background_node
Definition startscreen.h:59
int macos_major
Definition vm.h:134
HAL * GetHAL()
Definition vm.cpp:128
int macos_minor
Definition vm.h:135
Ogre::Real Real
Definition globals.h:57
std::string ToString(int number)
Definition globals.cpp:279
float Log2(float number)
Definition globals.cpp:336