1 // This file is part of Dust Racing 2D.
2 // Copyright (C) 2014 Jussi Lind <jussi.lind@iki.fi>
3 //
4 // Dust Racing 2D is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 // Dust Racing 2D is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Dust Racing 2D. If not, see <http://www.gnu.org/licenses/>.
15
16 #include "scene.hpp"
17
18 #include "ai.hpp"
19 #include "audioworker.hpp"
20 #include "bridge.hpp"
21 #include "car.hpp"
22 #include "carfactory.hpp"
23 #include "carsoundeffectmanager.hpp"
24 #include "checkeredflag.hpp"
25 #include "fadeanimation.hpp"
26 #include "game.hpp"
27 #include "inputhandler.hpp"
28 #include "intro.hpp"
29 #include "layers.hpp"
30 #include "mainmenu.hpp"
31 #include "messageoverlay.hpp"
32 #include "particlefactory.hpp"
33 #include "pit.hpp"
34 #include "race.hpp"
35 #include "renderer.hpp"
36 #include "settings.hpp"
37 #include "startlights.hpp"
38 #include "startlightsoverlay.hpp"
39 #include "statemachine.hpp"
40 #include "timingoverlay.hpp"
41 #include "track.hpp"
42 #include "trackdata.hpp"
43 #include "trackobject.hpp"
44 #include "tracktile.hpp"
45
46 #include "../common/config.hpp"
47 #include "../common/targetnodebase.hpp"
48
49 #include <MenuManager>
50
51 #include <MCAssetManager>
52 #include <MCCamera>
53 #include <MCFrictionGenerator>
54 #include <MCGLAmbientLight>
55 #include <MCGLDiffuseLight>
56 #include <MCGLScene>
57 #include <MCGLShaderProgram>
58 #include <MCObject>
59 #include <MCObjectFactory>
60 #include <MCPhysicsComponent>
61 #include <MCShape>
62 #include <MCSurface>
63 #include <MCSurfaceView>
64 #include <MCTextureFont>
65
66 #include <MCWorld>
67 #include <MCWorldRenderer>
68
69 #include <QApplication>
70 #include <QObject>
71
72 #include <algorithm>
73 #include <cassert>
74 #include <memory>
75
76 using std::dynamic_pointer_cast;
77
78 // Default visible scene size.
79 int Scene::m_width = 1024;
80 int Scene::m_height = 768;
81
82 static const float METERS_PER_UNIT = 0.05f;
83
Scene(Game & game,StateMachine & stateMachine,Renderer & renderer,MCWorld & world)84 Scene::Scene(Game & game, StateMachine & stateMachine, Renderer & renderer, MCWorld & world)
85 : m_game(game)
86 , m_stateMachine(stateMachine)
87 , m_renderer(renderer)
88 , m_messageOverlay(new MessageOverlay)
89 , m_race(std::make_shared<Race>(game, carCount()))
90 , m_activeTrack(nullptr)
91 , m_world(world)
92 , m_startlights(new Startlights)
93 , m_startlightsOverlay(new StartlightsOverlay(*m_startlights))
94 , m_checkeredFlag(new CheckeredFlag)
95 , m_intro(new Intro)
96 , m_particleFactory(new ParticleFactory)
97 , m_fadeAnimation(new FadeAnimation)
98 {
99 initializeComponents();
100
101 connectComponents();
102
103 createMenus();
104 }
105
connectComponents()106 void Scene::connectComponents()
107 {
108 connect(m_startlights.get(), &Startlights::raceStarted, m_race.get(), &Race::start);
109 connect(m_startlights.get(), &Startlights::animationEnded, &m_stateMachine, &StateMachine::endStartlightAnimation);
110
111 connect(&m_stateMachine, &StateMachine::startlightAnimationRequested, m_startlights.get(), &Startlights::beginAnimation);
112 connect(&m_stateMachine, &StateMachine::fadeInRequested, m_fadeAnimation.get(), &FadeAnimation::beginFadeIn);
113 connect(&m_stateMachine, &StateMachine::fadeOutRequested, m_fadeAnimation.get(), &FadeAnimation::beginFadeOut);
114 connect(&m_stateMachine, &StateMachine::fadeOutFlashRequested, m_fadeAnimation.get(), &FadeAnimation::beginFadeOutFlash);
115 connect(&m_stateMachine, &StateMachine::soundsStopped, m_race.get(), &Race::stopEngineSounds);
116
117 connect(m_fadeAnimation.get(), &FadeAnimation::fadeValueChanged, &m_renderer, &Renderer::setFadeValue);
118 connect(m_fadeAnimation.get(), &FadeAnimation::fadeInFinished, &m_stateMachine, &StateMachine::endFadeIn);
119 connect(m_fadeAnimation.get(), &FadeAnimation::fadeOutFinished, &m_stateMachine, &StateMachine::endFadeOut);
120
121 connect(m_race.get(), &Race::finished, &m_stateMachine, &StateMachine::finishRace);
122 connect(m_race.get(), &Race::messageRequested, m_messageOverlay.get(), static_cast<void (MessageOverlay::*)(QString)>(&MessageOverlay::addMessage));
123
124 connect(m_startlights.get(), &Startlights::messageRequested, m_messageOverlay.get(), static_cast<void (MessageOverlay::*)(QString)>(&MessageOverlay::addMessage));
125 connect(this, &Scene::listenerLocationChanged, &m_game.audioWorker(), &AudioWorker::setListenerLocation);
126
127 m_game.audioWorker().connectAudioSource(*m_race);
128 }
129
initializeComponents()130 void Scene::initializeComponents()
131 {
132 for (size_t i = 0; i < 2; i++)
133 {
134 m_cameraOffset.at(i) = 0.0f;
135 m_timingOverlay.at(i).setRace(m_race);
136 }
137
138 m_checkeredFlag->setDimensions(width(), height());
139 m_intro->setDimensions(width(), height());
140 m_startlightsOverlay->setDimensions(width(), height());
141 m_messageOverlay->setDimensions(width(), height());
142
143 m_world.setMetersPerUnit(METERS_PER_UNIT);
144
145 MCAssetManager::textureFontManager().font(m_game.fontName()).setShaderProgram(m_renderer.program("text"));
146 MCAssetManager::textureFontManager().font(m_game.fontName()).setShadowShaderProgram(m_renderer.program("textShadow"));
147
148 const MCGLAmbientLight ambientLight(1.0f, 0.9f, 0.95f, 0.75f);
149 const MCGLDiffuseLight diffuseLight(MCVector3dF(1.0f, -1.0f, -1.0f), 1.0f, 0.9f, 0.5f, 0.75f);
150 const MCGLDiffuseLight specularLight(MCVector3dF(1.0f, -1.0f, -1.0f), 1.0f, 1.0f, 0.8f, 0.9f);
151
152 auto & glScene = MCWorld::instance().renderer().glScene();
153 glScene.setAmbientLight(ambientLight);
154 glScene.setDiffuseLight(diffuseLight);
155 glScene.setSpecularLight(specularLight);
156
157 m_renderer.setFadeValue(0.0);
158 }
159
setupAudio(Car & car,size_t index)160 void Scene::setupAudio(Car & car, size_t index)
161 {
162 CarSoundEffectManager::MultiSoundHandles handles;
163
164 const auto indexStr = std::to_string(index);
165 const auto engine = "carEngine" + indexStr;
166 handles.engineSoundHandle = engine.c_str();
167
168 const auto hit = "carHit" + indexStr;
169 handles.hitSoundHandle = hit.c_str();
170
171 const auto skid = "skid" + indexStr;
172 handles.skidSoundHandle = skid.c_str();
173
174 const auto sfx = std::make_shared<CarSoundEffectManager>(car, handles);
175 m_game.audioWorker().connectAudioSource(*sfx);
176 car.setSoundEffectManager(sfx);
177 }
178
createCars()179 void Scene::createCars()
180 {
181 m_race->removeCars();
182 m_cars.clear();
183 m_ai.clear();
184
185 // Create and add cars.
186 for (size_t i = 0; i < carCount(); i++)
187 {
188 CarPtr car(CarFactory::buildCar(i, carCount(), m_game));
189 if (car)
190 {
191 if (!car->isHuman())
192 {
193 m_ai.push_back(std::make_shared<AI>(*car, m_race));
194 }
195
196 car->shape()->view()->setShaderProgram(m_renderer.program("car"));
197 car->shape()->view()->object()->material()->setDiffuseCoeff(3.4f);
198
199 setupAudio(*car, i);
200
201 m_cars.push_back(car);
202 m_race->addCar(*car);
203 }
204 }
205
206 if (m_game.hasTwoHumanPlayers())
207 {
208 m_timingOverlay.at(1).setCarToFollow(*m_cars.at(1));
209 m_crashOverlay.at(1).setCarToFollow(*m_cars.at(1));
210 }
211
212 m_timingOverlay.at(0).setCarToFollow(*m_cars.at(0));
213 m_crashOverlay.at(0).setCarToFollow(*m_cars.at(0));
214 }
215
setupMinimaps()216 void Scene::setupMinimaps()
217 {
218 const auto minimapSize = static_cast<int>(m_width * 0.2f);
219 const auto minimapY = !m_game.hasTwoHumanPlayers() ? minimapSize : minimapSize / 2 + 10;
220
221 for (size_t i = 0; i < std::min(static_cast<size_t>(2), m_cars.size()); i++)
222 {
223 m_minimap.at(i).initialize(*m_cars.at(i), m_activeTrack->trackData().map(), minimapSize / 2 + 10, minimapY, static_cast<size_t>(minimapSize));
224 }
225 }
226
width()227 int Scene::width()
228 {
229 return Scene::m_width;
230 }
231
height()232 int Scene::height()
233 {
234 return Scene::m_height;
235 }
236
setSize(int width,int height)237 void Scene::setSize(int width, int height)
238 {
239 Scene::m_width = width;
240 Scene::m_height = height;
241 }
242
carCount()243 size_t Scene::carCount()
244 {
245 return 12;
246 }
247
createMenus()248 void Scene::createMenus()
249 {
250 m_menuManager.reset(new MTFH::MenuManager);
251
252 m_mainMenu = std::make_shared<MainMenu>(*m_menuManager, *this, width(), height());
253 connect(
254 std::static_pointer_cast<MainMenu>(m_mainMenu).get(), &MainMenu::exitGameRequested, &m_game, &Game::exitGame);
255
256 m_menuManager->addMenu(m_mainMenu);
257 m_menuManager->enterMenu(m_mainMenu);
258 }
259
updateFrame(InputHandler & handler,int step)260 void Scene::updateFrame(InputHandler & handler, int step)
261 {
262 if (m_stateMachine.state() == StateMachine::State::GameTransitionIn || m_stateMachine.state() == StateMachine::State::GameTransitionOut || m_stateMachine.state() == StateMachine::State::DoStartlights || m_stateMachine.state() == StateMachine::State::Play)
263 {
264 if (m_activeTrack)
265 {
266 if (m_race->started())
267 {
268 processUserInput(handler);
269 updateAi();
270 }
271
272 updateWorld(step);
273 updateRace();
274
275 if (m_game.hasTwoHumanPlayers())
276 {
277 for (size_t i = 0; i < 2; i++)
278 {
279 updateCameraLocation(m_camera.at(i), m_cameraOffset.at(i), *m_cars.at(i));
280 }
281 }
282 else
283 {
284 updateCameraLocation(m_camera.at(0), m_cameraOffset.at(0), *m_cars.at(0));
285 }
286 }
287 }
288 else if (m_stateMachine.state() == StateMachine::State::Menu)
289 {
290 m_menuManager->stepTime(step);
291 }
292
293 if (m_fadeAnimation->isFading())
294 {
295 auto & glScene = MCWorld::instance().renderer().glScene();
296 glScene.setFadeValue(m_renderer.fadeValue());
297 }
298 }
299
updateOverlays()300 void Scene::updateOverlays()
301 {
302 if (m_game.hasTwoHumanPlayers())
303 {
304 m_timingOverlay.at(1).update();
305 m_crashOverlay.at(1).update();
306 }
307
308 m_timingOverlay.at(0).update();
309 m_crashOverlay.at(0).update();
310
311 m_messageOverlay->update();
312 }
313
updateWorld(int timeStep)314 void Scene::updateWorld(int timeStep)
315 {
316 // Step time
317 m_world.stepTime(timeStep);
318 }
319
updateRace()320 void Scene::updateRace()
321 {
322 // Update race situation
323 m_race->update();
324
325 emit listenerLocationChanged(m_cars.at(0)->location().i(), m_cars.at(0)->location().j());
326 }
327
updateCameraLocation(MCCamera & camera,float & offset,MCObject & object)328 void Scene::updateCameraLocation(MCCamera & camera, float & offset, MCObject & object)
329 {
330 // Update camera location with respect to the car speed.
331 // Make changes a bit smoother so that an abrupt decrease
332 // in the speed won't look bad.
333 offset += (object.physicsComponent().velocity().lengthFast() - offset) * 0.2f;
334 const float offsetAmplification = m_game.hasTwoHumanPlayers() ? 9.6f : 13.8f;
335 const auto loc = MCVector2dF(object.location()) + object.direction() * offset * offsetAmplification;
336 camera.setPos(loc.i(), loc.j());
337 }
338
processUserInput(InputHandler & handler)339 void Scene::processUserInput(InputHandler & handler)
340 {
341 for (size_t i = 0; i < (m_game.hasTwoHumanPlayers() ? 2 : 1); i++)
342 {
343 // Handle accelerating / braking
344 if (handler.getActionState(i, InputHandler::Action::Down))
345 {
346 if (!m_race->timing().raceCompleted(i))
347 {
348 m_cars.at(i)->setBrakeEnabled(true);
349 }
350 }
351 else
352 {
353 m_cars.at(i)->setBrakeEnabled(false);
354 }
355
356 if (handler.getActionState(i, InputHandler::Action::Up))
357 {
358 if (!m_race->timing().raceCompleted(i))
359 {
360 m_cars.at(i)->setAcceleratorEnabled(true);
361 }
362 }
363 else
364 {
365 m_cars.at(i)->setAcceleratorEnabled(false);
366 }
367
368 // Handle turning
369 if (handler.getActionState(i, InputHandler::Action::Left))
370 {
371 m_cars.at(i)->steer(Car::Steer::Left);
372 }
373 else if (handler.getActionState(i, InputHandler::Action::Right))
374 {
375 m_cars.at(i)->steer(Car::Steer::Right);
376 }
377 else
378 {
379 m_cars.at(i)->steer(Car::Steer::Neutral);
380 }
381 }
382 }
383
updateAi()384 void Scene::updateAi()
385 {
386 for (auto && ai : m_ai)
387 {
388 const bool isRaceCompleted = m_race->timing().raceCompleted(ai->car().index());
389 ai->update(isRaceCompleted);
390 }
391 }
392
setupCameras(Track & activeTrack)393 void Scene::setupCameras(Track & activeTrack)
394 {
395 m_world.renderer().removeParticleVisibilityCameras();
396 if (m_game.hasTwoHumanPlayers())
397 {
398 for (size_t i = 0; i < 2; i++)
399 {
400 if (m_game.splitType() == Game::SplitType::Vertical)
401 {
402 m_camera.at(i).init(
403 Scene::width() / 2, Scene::height(), 0, 0, activeTrack.width(), activeTrack.height());
404 m_world.renderer().addParticleVisibilityCamera(m_camera.at(i));
405 }
406 else
407 {
408 m_camera.at(i).init(
409 Scene::width(), Scene::height() / 2, 0, 0, activeTrack.width(), activeTrack.height());
410 m_world.renderer().addParticleVisibilityCamera(m_camera.at(i));
411 }
412 }
413 }
414 else
415 {
416 m_camera.at(0).init(
417 Scene::width(), Scene::height(), 0, 0, activeTrack.width(), activeTrack.height());
418 m_world.renderer().addParticleVisibilityCamera(m_camera.at(0));
419 }
420 }
421
setupAI(std::shared_ptr<Track> activeTrack)422 void Scene::setupAI(std::shared_ptr<Track> activeTrack)
423 {
424 for (auto && ai : m_ai)
425 {
426 ai->setTrack(activeTrack);
427 }
428 }
429
setActiveTrack(std::shared_ptr<Track> activeTrack)430 void Scene::setActiveTrack(std::shared_ptr<Track> activeTrack)
431 {
432 m_activeTrack = activeTrack;
433
434 // Remove previous objects
435 m_world.clear();
436
437 setupCameras(*activeTrack);
438
439 setWorldDimensions();
440
441 createCars();
442
443 resizeOverlays();
444
445 addCarsToWorld();
446
447 addTrackObjectsToWorld();
448
449 initializeRace();
450
451 setupAI(activeTrack);
452
453 setupMinimaps();
454 }
455
setWorldDimensions()456 void Scene::setWorldDimensions()
457 {
458 assert(m_activeTrack);
459
460 // Update world dimensions according to the
461 // active track.
462 const size_t minX = 0;
463 const size_t maxX = m_activeTrack->width();
464 const size_t minY = 0;
465 const size_t maxY = m_activeTrack->height();
466 const size_t minZ = 0;
467 const size_t maxZ = 1000;
468
469 m_world.setDimensions(minX, maxX, minY, maxY, minZ, maxZ, METERS_PER_UNIT);
470 }
471
addCarsToWorld()472 void Scene::addCarsToWorld()
473 {
474 // Add objects to the world
475 for (auto && car : m_cars)
476 {
477 car->addToWorld();
478 }
479 }
480
addTrackObjectsToWorld()481 void Scene::addTrackObjectsToWorld()
482 {
483 createNormalObjects();
484
485 createBridgeObjects();
486 }
487
createNormalObjects()488 void Scene::createNormalObjects()
489 {
490 assert(m_activeTrack);
491
492 for (size_t i = 0; i < m_activeTrack->trackData().objects().count(); i++)
493 {
494 const auto trackObject = dynamic_pointer_cast<TrackObject>(m_activeTrack->trackData().objects().object(i));
495 assert(trackObject);
496
497 MCObject & object = trackObject->object();
498 object.addToWorld();
499
500 // Set the base Z of mesh objects at ground level instead of at the object center
501 float baseZ = 0;
502 if (object.shape() && object.shape()->view() && object.shape()->view()->object())
503 {
504 if (dynamic_cast<MCMesh *>(object.shape()->view()->object()))
505 {
506 baseZ = -object.shape()->view()->object()->minZ();
507 }
508 }
509
510 object.translate(object.initialLocation() + MCVector3dF(0, 0, baseZ));
511 object.rotate(object.initialAngle());
512
513 if (auto pit = dynamic_cast<Pit *>(&object))
514 {
515 pit->reset();
516 connect(pit, &Pit::pitStop, m_race.get(), &Race::pitStop);
517 }
518 }
519 }
520
createBridgeObjects()521 void Scene::createBridgeObjects()
522 {
523 assert(m_activeTrack);
524
525 const auto & map = m_activeTrack->trackData().map();
526 for (size_t j = 0; j <= map.rows(); j++)
527 {
528 for (size_t i = 0; i <= map.cols(); i++)
529 {
530 auto tile = dynamic_pointer_cast<TrackTile>(map.getTile(i, j));
531 if (tile && tile->tileTypeEnum() == TrackTile::TileType::Bridge)
532 {
533 const auto bridge = std::make_shared<Bridge>();
534 bridge->translate(MCVector3dF(i * TrackTile::width() + TrackTile::width() / 2, j * TrackTile::height() + TrackTile::height() / 2, 0));
535 bridge->rotate(tile->rotation());
536 bridge->addToWorld();
537 m_bridges.push_back(bridge);
538 }
539 }
540 }
541
542 Bridge::reset();
543 }
544
resizeOverlays()545 void Scene::resizeOverlays()
546 {
547 if (m_game.hasTwoHumanPlayers())
548 {
549 if (m_game.splitType() == Game::SplitType::Vertical)
550 {
551 for (size_t i = 0; i < 2; i++)
552 {
553 m_timingOverlay.at(i).setDimensions(width() / 2, height());
554 m_crashOverlay.at(i).setDimensions(width() / 2, height());
555 }
556 }
557 else
558 {
559 for (size_t i = 0; i < 2; i++)
560 {
561 m_timingOverlay.at(i).setDimensions(width(), height() / 2);
562 m_crashOverlay.at(i).setDimensions(width(), height() / 2);
563 }
564 }
565 }
566 else
567 {
568 m_timingOverlay.at(0).setDimensions(width(), height());
569 m_crashOverlay.at(0).setDimensions(width(), height());
570 }
571 }
572
initializeRace()573 void Scene::initializeRace()
574 {
575 assert(m_activeTrack);
576 m_race->init(m_activeTrack, static_cast<size_t>(m_game.lapCount()));
577 }
578
activeTrack() const579 std::shared_ptr<Track> Scene::activeTrack() const
580 {
581 return m_activeTrack;
582 }
583
trackSelectionMenu() const584 MTFH::MenuPtr Scene::trackSelectionMenu() const
585 {
586 return m_menuManager->getMenuById("trackSelection");
587 }
588
getSplitPositions(MCGLScene::SplitType & p0,MCGLScene::SplitType & p1)589 void Scene::getSplitPositions(MCGLScene::SplitType & p0, MCGLScene::SplitType & p1)
590 {
591 if (m_game.splitType() == Game::SplitType::Vertical)
592 {
593 p1 = MCGLScene::ShowOnLeft;
594 p0 = MCGLScene::ShowOnRight;
595 }
596 else
597 {
598 p1 = MCGLScene::ShowOnTop;
599 p0 = MCGLScene::ShowOnBottom;
600 }
601 }
602
renderTrack()603 void Scene::renderTrack()
604 {
605 switch (m_stateMachine.state())
606 {
607 case StateMachine::State::GameTransitionIn:
608 case StateMachine::State::GameTransitionOut:
609 case StateMachine::State::DoStartlights:
610 case StateMachine::State::Play:
611 {
612 if (m_game.hasTwoHumanPlayers())
613 {
614 MCGLScene::SplitType p1, p0;
615 getSplitPositions(p1, p0);
616
617 auto & glScene = MCWorld::instance().renderer().glScene();
618
619 glScene.setSplitType(p1);
620 m_activeTrack->render(m_camera.at(1));
621
622 glScene.setSplitType(p0);
623 m_activeTrack->render(m_camera.at(0));
624
625 glScene.setSplitType(MCGLScene::ShowFullScreen);
626 }
627 else
628 {
629 m_activeTrack->render(m_camera.at(0));
630 }
631
632 break;
633 }
634 default:
635 break;
636 }
637 }
638
renderMenu()639 void Scene::renderMenu()
640 {
641 switch (m_stateMachine.state())
642 {
643 case StateMachine::State::DoIntro:
644
645 MCWorld::instance().renderer().glScene().setSplitType(MCGLScene::ShowFullScreen);
646
647 m_intro->render();
648
649 break;
650
651 case StateMachine::State::Menu:
652 case StateMachine::State::MenuTransitionOut:
653 case StateMachine::State::MenuTransitionIn:
654
655 MCWorld::instance().renderer().glScene().setSplitType(MCGLScene::ShowFullScreen);
656
657 m_menuManager->render();
658
659 break;
660
661 default:
662 break;
663 }
664 }
665
renderCommonHUD()666 void Scene::renderCommonHUD()
667 {
668 switch (m_stateMachine.state())
669 {
670 case StateMachine::State::GameTransitionIn:
671 case StateMachine::State::GameTransitionOut:
672 case StateMachine::State::DoStartlights:
673 case StateMachine::State::Play:
674 {
675 // Setup for common scene
676 MCWorld::instance().renderer().glScene().setSplitType(MCGLScene::ShowFullScreen);
677
678 if (m_race->checkeredFlagEnabled() && !m_game.hasTwoHumanPlayers())
679 {
680 m_checkeredFlag->render();
681 }
682
683 m_startlightsOverlay->render();
684 m_messageOverlay->render();
685 break;
686 }
687 default:
688 break;
689 }
690 }
691
renderHUD()692 void Scene::renderHUD()
693 {
694 switch (m_stateMachine.state())
695 {
696 case StateMachine::State::GameTransitionIn:
697 case StateMachine::State::GameTransitionOut:
698 case StateMachine::State::DoStartlights:
699 case StateMachine::State::Play:
700 {
701 if (m_game.hasTwoHumanPlayers())
702 {
703 MCGLScene::SplitType p1, p0;
704 getSplitPositions(p1, p0);
705
706 auto & glScene = MCWorld::instance().renderer().glScene();
707
708 glScene.setSplitType(p1);
709 m_timingOverlay.at(1).render();
710 m_minimap.at(1).render(m_cars, *m_race);
711 m_crashOverlay.at(1).render();
712
713 glScene.setSplitType(p0);
714 m_timingOverlay.at(0).render();
715 m_minimap.at(0).render(m_cars, *m_race);
716 m_crashOverlay.at(0).render();
717
718 glScene.setSplitType(MCGLScene::ShowFullScreen);
719 }
720 else
721 {
722 m_timingOverlay.at(0).render();
723 m_minimap.at(0).render(m_cars, *m_race);
724 m_crashOverlay.at(0).render();
725 }
726
727 break;
728 }
729 default:
730 break;
731 }
732 }
733
renderWorld(MCRenderGroup renderGroup,bool prepareRendering)734 void Scene::renderWorld(MCRenderGroup renderGroup, bool prepareRendering)
735 {
736 switch (m_stateMachine.state())
737 {
738 case StateMachine::State::GameTransitionIn:
739 case StateMachine::State::GameTransitionOut:
740 case StateMachine::State::DoStartlights:
741 case StateMachine::State::Play:
742 {
743 if (m_game.hasTwoHumanPlayers())
744 {
745 MCGLScene::SplitType p1, p0;
746 getSplitPositions(p1, p0);
747
748 if (prepareRendering)
749 {
750 m_world.prepareRendering(&m_camera.at(1));
751 m_world.prepareRendering(&m_camera.at(0));
752 }
753
754 auto & glScene = MCWorld::instance().renderer().glScene();
755
756 glScene.setSplitType(p1);
757 m_world.render(&m_camera.at(1), renderGroup);
758
759 glScene.setSplitType(p0);
760 m_world.render(&m_camera.at(0), renderGroup);
761
762 glScene.setSplitType(MCGLScene::ShowFullScreen);
763 }
764 else
765 {
766 if (prepareRendering)
767 {
768 m_world.prepareRendering(&m_camera.at(0));
769 }
770
771 m_world.render(&m_camera.at(0), renderGroup);
772 }
773
774 break;
775 }
776 default:
777 break;
778 }
779 }
780
781 Scene::~Scene() = default;
782