GameScene.cpp

Go to the documentation of this file.
00001 
00005 /* Copyright © 2009, 2010 James Legg.
00006     This program is free software: you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation, either version 3 of the License, or
00009     (at your option) any later version.
00010 */
00011 #include <config.h>
00012 #include "GameScene.h"
00013 #include "../Graphics/Window.h"
00014 #include <Debug.h>
00015 #include "InputHandler.h"
00016 #include "InputDeviceAI.h"
00017 #include "ResourceHandler.h"
00018 #include "../UI/BasicFonts.h"
00019 #include "../MainLoop.h"
00020 #include "Audio.h"
00021 
00022 #include <libtrack/Texture.h>
00023 #include <libtrack/StartingPosition.h>
00024 
00025 #include <GL/glu.h>
00026 #include <cmath>
00027 #include <fstream>
00028 #include <limits>
00029 
00030 #include <glibmm/miscutils.h>
00031 
00032 #ifdef HAVE_FTGL_2_1_2
00033 typedef FTGLTextureFont FTTextureFont;
00034 #endif
00035 
00036 namespace Engine
00037 {
00038 
00040 typedef ResourceHandler<Track::Texture, std::string, std::string> TextureStore;
00041 
00042 GameScene::GameScene(std::vector<std::pair<InputHandler::iterator, unsigned int> > input_devices,
00043                      const Track::Track & track)
00044     :   m_input_devices(input_devices)
00045     ,   sky(track.get_theme().get_skybox())
00046     ,   track(track)
00047     ,   world(track)
00048     ,   save_replay(true)
00049     ,   fps(0)
00050     ,   countdown_timer(-3000)
00051     ,   paused(false)
00052     ,   show_debug(false)
00053 #ifndef NDEBUG
00054     // prepare ai_mesh's display list so we can call it recusively.
00055     ,   ai_mesh(track.get_ai_mesh())
00056     ,   m_total_frames(0)
00057     ,   m_total_time(0)
00058 #endif
00059 {
00060     // make sure textures randomly accessed by the hud are cached before the
00061     // game starts, to prevent mini freezes while the textures are loaded.
00062     // These remain in memory after the GameScene ends, but are reused next
00063     // time without loading them again.
00064     TextureStore & tex_store = TextureStore::get_instance();
00065     #define tex_load(bind_name, file_name, variable)\
00066                 tex_store.check_load(bind_name, file_name);\
00067                 variable = tex_store.get(bind_name);\
00068                 variable->make_cache()
00069     tex_load("speedometer.hud.ui", "data/ui/hud/speedometer.png", m_speedometer_texture);
00070     tex_load("best_lap.hud.ui", "data/ui/hud/best_lap.png", m_best_lap_texture);
00071     tex_load("border.finish.hud.ui", "data/ui/hud/finish_border.png", m_finish_border_texture);
00072     tex_load("text.finish.hud.ui", "data/ui/hud/finished_text.png", m_finish_text_texture);
00073     tex_load("disqualified.hud.ui", "data/ui/hud/disqualified_text.png", m_disqualified_text_texture);
00074     tex_load("reverse.hud.ui", "data/ui/hud/reverse.png", m_reverse_texture);
00075     tex_load("countdown.hud.ui", "data/ui/hud/countdown.png", m_countdown_texture);
00076     #undef tex_load
00077 
00084     // first line contains the track's filename
00085     m_replay_header << track.get_filename() << std::endl;
00086     // The number of cars.
00087     m_replay_header << input_devices.size() << " ";
00088     // We add information about each car as we set it up
00089     
00090     // Put cars in the starting positions.
00091     const Track::Path & path = track.get_path();
00092     const Track::PathEdge & edge = path.get_edge(path.get_starting_edge());
00093     start_point = path.find_start_position();
00094     path.find_start_plane(start_plane_normal, start_plane_distance);
00095     
00096     const std::size_t number_of_cars = input_devices.size();
00097     cars.reserve(number_of_cars);
00099     car_cameras.reserve(number_of_cars);
00100     for(unsigned int i = 0; i< number_of_cars; i++)
00101     {
00102         // Find the starting position for this car.
00103         btTransform initial_transform =
00104             // guess transformation when starting position cannot be found.
00105             edge.get_transform(float(i) / float(number_of_cars));
00106         // find the starting position track attachment for this car's rank.
00107         for (std::vector<boost::shared_ptr<Track::TrackAttachment> >::const_iterator it = edge.get_attachments().begin();
00108              it != edge.get_attachments().end();
00109              it++)
00110         {
00111             const Track::StartingPosition * pos = dynamic_cast<const Track::StartingPosition *>(&(**it));
00112             if (pos)
00113             {
00114                 // cars go in reverse order, so human players go last.
00115                 if (pos->get_rank() == number_of_cars - i - 1)
00116                 {
00117                     initial_transform = pos->get_global_transform();
00118                 }
00119             }
00120         }
00121         // raise the car above the track slightly.
00122         initial_transform.setOrigin(initial_transform(btVector3(0.0, 0.0, 0.2)));
00123         cars.push_back(new GameObjects::Car(world,
00124                                             initial_transform,
00125                                             *(input_devices[i].first),
00126                                             input_devices[i].second,
00127                                             start_plane_normal,
00128                                             start_plane_distance,
00129                                             start_point));
00130         if (!dynamic_cast<InputDeviceAI*>(*(input_devices[i].first)))
00131         {
00132             // Not a computer player (though it could be a replay of one).
00136             car_cameras.push_back(new CarCamera(*cars[i], world));
00137             car_skip.push_back(false);
00138             m_humans.push_back(i);
00139         }
00140         // The pointer to the input device will be used to identify input
00141         // reports during the game. Store it as a long int instead of hex.
00142         m_replay_header << (unsigned long int)&*(input_devices[i].first) << " "
00143         // Record which car model was used.
00144                         << input_devices[i].second << " ";
00145         
00146     }
00147     btDefaultMotionState motion_state(btTransform(btQuaternion(0, 0, 0, 1),
00148                                         btVector3(0, 0, 0)));
00149     track_shape = track.get_collision_shape();
00150     btRigidBody::btRigidBodyConstructionInfo
00151                 rigid_body_CI(btScalar(0),
00152                               &motion_state,
00153                               &(*track_shape),
00154                               btVector3(0, 0, 0));
00155     rigid_body_CI.m_restitution = 1.0;
00156     track_body = new btRigidBody(rigid_body_CI);
00157     world.get_dynamics_world().addRigidBody(track_body);
00158     
00159     // now do the floor shape in the floor world.
00160     floor_shape = track.get_floor_shape();
00161     rigid_body_CI.m_collisionShape = &(*floor_shape);
00162     floor_body = new btRigidBody(rigid_body_CI);
00163     world.get_floor_world().addCollisionObject(floor_body);
00164     
00165     // stage specific light and fog.
00166     track.get_lighting().initalise();
00167     
00168     
00169 #ifndef NDEBUG
00170     // Create a display list to show the debug version of the track.
00171     // This shows the navigation mesh, textured by lap position, with the
00172     // connections between faces drawn over the top.
00173     
00174     // Set up texture
00175     GLubyte t[16] = {0, 15, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 207, 223, 239};
00176     glGenTextures(1, &m_debug_texture_name);
00177     glBindTexture(GL_TEXTURE_1D, m_debug_texture_name);
00178     gluBuild1DMipmaps(GL_TEXTURE_1D, GL_LUMINANCE4, 16, GL_LUMINANCE, GL_UNSIGNED_BYTE, t);
00179     
00180     ai_mesh.make_cache();
00181     m_debug_list_name = glGenLists(1);
00182     glNewList(m_debug_list_name, GL_COMPILE);
00183         glPushAttrib(GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_TRANSFORM_BIT);
00184             // navigation mesh
00185             glEnable(GL_TEXTURE_1D);
00186             glDisable(GL_TEXTURE_2D);
00187             glBindTexture(GL_TEXTURE_1D, m_debug_texture_name);
00188             glMatrixMode(GL_TEXTURE);
00189             glScalef(0.2, 1.0, 1.0);
00190             ai_mesh.draw();
00191             glLoadIdentity();
00192             glDisable(GL_TEXTURE_1D);
00193             
00194             // connection lines
00195             const Track::MeshFaces::Graph & graph = track.get_ai_graph();
00196             glDisable(GL_DEPTH_TEST);
00197             glBegin(GL_LINES);
00198                 typedef boost::graph_traits<Track::MeshFaces::Graph>::edge_iterator EdgeIterator;
00199                 std::pair<EdgeIterator, EdgeIterator> edge_range;
00200                 for (edge_range = boost::edges(graph);
00201                      edge_range.first != edge_range.second;
00202                      edge_range.first++)
00203                 {
00204                     const btVector3 & source = graph[boost::source(*(edge_range.first), graph)].face_centre;
00205                     const btVector3 & target = graph[boost::target(*(edge_range.first), graph)].face_centre;
00206                     glColor3f(1.0, graph[boost::source(*(edge_range.first), graph)].fv1.texture_coord_u / 1000.0, 0);
00207                     glVertex3f(source.x(), source.y(), source.z());
00208                     glVertex3f(target.x(), target.y(), target.z());
00209                 }
00210             glEnd();
00211         glPopAttrib();
00212     glEndList();
00213 #endif
00214 }
00215 
00216 GameScene::~GameScene()
00217 {
00218     // record the replay if requested.
00219     if (save_replay)
00220     {
00221         try
00222         {
00223             std::ofstream out;
00224             out.exceptions(std::ofstream::badbit | std::ofstream::failbit);
00225             out.open((Glib::get_user_config_dir() + "/racer_last_replay").c_str());
00226             // Write metadata identifing the scene and cars
00227             out << m_replay_header.str();
00228             // Write the input device events that occured during the game.
00229             world.write_replay_events(out);
00230         } catch (std::ofstream::failure e)
00231         {
00232             DEBUG_MESSAGE("Error while writing replay file.");
00233         }
00234     }
00235     
00236     world.get_dynamics_world().removeRigidBody(track_body);
00237     delete track_body;
00238     world.get_floor_world().removeCollisionObject(floor_body);
00239     delete floor_body;
00240     // delete the cars' cameras
00241     for (std::vector<CarCamera *>::iterator i = car_cameras.begin();
00242          i != car_cameras.end(); i++)
00243     {
00244         delete *i;
00245     }
00246     // delete the cars
00247     for (std::vector<GameObjects::Car *>::iterator i = cars.begin();
00248          i != cars.end(); i++)
00249     {
00250         delete *i;
00251     }
00252     
00253 #ifndef NDEBUG
00254     glDeleteLists(m_debug_list_name, 1);
00255     glDeleteTextures(1, &m_debug_texture_name);
00256     DEBUG_MESSAGE("Framerate: " << (float(m_total_frames) / float(m_total_time) * 1000.0));
00257 #endif
00258 }
00259 
00260 void GameScene::take_input(InputReport & report)
00261 {
00262     if (paused)
00263     {
00264         m_pause_menu.take_input(report);
00265         return;
00266     }
00267     switch (report.get_report_type())
00268     {
00269         case InputReport::RT_MENU_BACK:
00270 #ifndef NDEBUG
00271             /* In debug builds, toggle debugging overlay when pressing escape
00272              * or backspace instead of pausing.
00273              */
00274             show_debug = !show_debug;
00275             break;
00276 #endif
00277         case InputReport::RT_MENU_SELECT:
00278             {
00279                 std::size_t car = -1;
00280                 for (unsigned int i = 0; i < m_input_devices.size(); i++)
00281                 {
00282                     if (m_input_devices[i].first == report.get_input_device())
00283                     {
00284                         car = i;
00285                         break;
00286                     }
00287                 }
00288                 if (car != std::size_t(-1))
00289                 {
00290                     if (cars[car]->get_disqualified() || cars[car]->get_finished())
00291                     {
00292                         car_skip[car] = true;
00293                     } else {
00294                         paused = true;
00295                         Audio::get_instance().pause();
00296                     }
00297                 } else {
00298                     // unused controller.
00299                     paused = true;
00300                     Audio::get_instance().pause();
00301                 }
00302             }
00303             break;
00304         case InputReport::RT_DISCONNECT:
00305             // automatically pause the game if connection to a device is lost.
00306             paused = true;
00307         case InputReport::RT_BATTERY_LOW:
00311             break;
00312         default:
00313             // all car actions are passed to the car.
00314             break;
00315     }
00316 }
00317 
00318 void GameScene::update_logic(unsigned int milliseconds_elapsed)
00319 {
00320 #ifndef NDEBUG
00321     m_total_frames++;
00322     m_total_time += milliseconds_elapsed;
00323 #endif
00324     fps = 1000.0 / float(milliseconds_elapsed);
00325     if (paused)
00326     {
00327         if (m_pause_menu.want_to_quit())
00328         {
00329             // escape or backspace hit while pause menu showing.
00330             unpause();
00331         } else {
00332             switch (m_pause_menu.get_response())
00333             {
00334                 case UI::PauseMenu::R_NONE:
00335                     // wait for user to respond to pause menu.
00336                     break;
00337                 case UI::PauseMenu::R_CONTINUE:
00338                     // continue the game.
00339                     unpause();
00340                     break;
00341                 case UI::PauseMenu::R_QUIT:
00342                     // quit selected, then confirm pressed (space or enter).
00343                     main_loop->pop_scene();
00344                     break;
00345             }
00346         }
00347         return;
00348     }
00349     if (track.get_theme().get_use_sky_particles())
00350     {
00351         m_sky_particles.update(milliseconds_elapsed);
00352     }
00353     if (countdown_timer <=500)
00354     {
00355         countdown_timer += milliseconds_elapsed;
00356         // don't do physics simulation until race starts.
00357         if (countdown_timer < 0) return;
00358     }
00359     world.update(milliseconds_elapsed);
00360     
00361     // if we are done with non computer controlled cars, quit the scene.
00362     bool quit = true;
00363     for (unsigned int i = 0; i < m_humans.size(); i++)
00364     {
00365         int car_index = m_humans[i];
00366         bool this_car = cars[car_index]->get_disqualified() ||
00367                         cars[car_index]->get_finished();
00368         if (this_car)
00369         {
00370             // done after a few seconds or if manually skipped.
00371             if (   (world.get_tick_number() - cars[car_index]->get_finish_time() < 300)
00372                 && !car_skip[car_index])
00373             {
00374                 quit = false;
00375             }
00376         }
00377         else
00378         {
00379             quit = false;
00380         }
00381     }
00382     if (quit)
00383     {
00384         main_loop->pop_scene();
00385     }
00386 }
00387 
00388 void GameScene::draw()
00389 {
00390     // Find the correct ranking of cars to display.
00391     rank_cars();
00392     
00393     std::size_t camera_count = car_cameras.size();
00397     // find reasonable way to split the viewports.
00398     // We pick the layout with the aspect ratio closest to 4:3. Given multiple
00399     // choices, we use the layout with the most screen utilisation (least area
00400     // spent on unused viewports).
00401     unsigned int height = Graphics::Window::get_instance().get_height();
00402     unsigned int width = Graphics::Window::get_instance().get_width();
00403     // try each possible number of columns
00404     unsigned int best_rows(1);
00405     unsigned int best_columns(1);
00406     float best_utilisation(0.0);
00407     float best_aspect(0.0);
00408     float best_aspect_wrongness(height * width);
00409     for (unsigned int rows = 1; rows <= camera_count; rows++)
00410     {
00411         unsigned int columns = (camera_count + rows - 1) / rows;
00412         float aspect =   (float(width) / float(columns))
00413                        / (float(height) / float(rows));
00414         float utilisation = float(camera_count) / float(rows * columns);
00415         float aspect_wrongness = 2.35 - aspect;
00416         if (aspect_wrongness < 0.0)
00417         {
00418             aspect_wrongness = -aspect_wrongness;
00419         }
00420         bool better = false;
00421         if (best_aspect_wrongness > aspect_wrongness)
00422         {
00423             better = true;
00424         }
00425         else if (    best_aspect_wrongness == aspect_wrongness
00426                   && utilisation > best_utilisation)
00427         {
00428             // The aspect ratio is just as good, but this has less screen
00429             // space used for unused viewports.
00430             better = true;
00431         }
00432         if (better)
00433         {
00434             best_rows = rows;
00435             best_columns = columns;
00436             best_utilisation = utilisation;
00437             best_aspect = aspect;
00438             best_aspect_wrongness = aspect_wrongness;
00439         }
00440     }
00441     // now render each viewport with the choosen layout.
00442     glEnable(GL_SCISSOR_TEST);
00443     glEnable(GL_DEPTH_TEST);
00444     glEnable(GL_TEXTURE_2D);
00445     glEnable(GL_CULL_FACE);
00446     unsigned int row = 0;
00447     unsigned int column = 0;
00448     unsigned int row_size = height / best_rows;
00449     unsigned int column_size = width / best_columns;
00450     unsigned int player = 0;
00451     assert(best_aspect > 0);
00452     for (std::vector<CarCamera *>::iterator it = car_cameras.begin();
00453          it != car_cameras.end(); it++)
00454     {
00455         glViewport(column * column_size,  row * row_size,
00456                    column_size, row_size);
00457         glScissor(column * column_size,  row * row_size,
00458                   column_size, row_size);
00464         draw_for_player(player, best_aspect);
00465         column++;
00466         player++;
00467         if (column == best_columns)
00468         {
00469             row++;
00470             column = 0;
00471         }
00472     }
00473     // blank the unused windows.
00474     
00475     while(player < best_rows * best_columns)
00476     {
00477         glViewport(column * column_size,  row * row_size,
00478                    column_size, row_size);
00479         glScissor(column * column_size,  row * row_size,
00480                   column_size, row_size);
00481         glClear(GL_COLOR_BUFFER_BIT);
00482         player++;
00483         column++;
00484         if (column == best_columns)
00485         {
00486             row++;
00487             column = 0;
00488         }
00489     }
00490     glDisable(GL_SCISSOR_TEST);
00491     
00492     // Indicate when paused
00493     if (paused)
00494     { 
00495         draw_paused_screen();
00496     }
00497     
00498     // show the fps counter in adebug build.
00499 #ifndef NDEBUG
00500     char msg[10] = "f/s: 000";
00501     // take the average frames per second over the last 60 frames.
00502     const int number_of_readings = 60;
00503     static int readings[number_of_readings] = {0.0};
00504     static int which_reading = 0;
00505     static int total = 0;
00506     total += int(fps);
00507     total -= readings[which_reading];
00508     readings[which_reading] = int(fps);
00509     which_reading++;
00510     if (which_reading == number_of_readings)
00511     {
00512         which_reading = 0;
00513     }
00514     int fpsi = total > 999 * number_of_readings ? 999 : total / number_of_readings;
00515     msg[5] = fpsi / 100 + '0';
00516     msg[6] = (fpsi / 10) % 10 + '0';
00517     msg[7] = fpsi % 10 + '0';
00518     Graphics::Window::get_instance().set_ortho_projection();
00519     glLoadIdentity();
00520     glTranslatef(Graphics::Window::get_instance().get_width() - 150, 15, 0);
00521     FTTextureFont & font = UI::BasicFonts::get_instance().small_font;
00522     glDisable(GL_DEPTH_TEST);
00523     glColor4ub(0, 0, 0, 127);
00524     glEnable(GL_BLEND);
00525     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00526     glDisable(GL_TEXTURE_2D);
00527     glBegin(GL_QUADS);
00528         glVertex2f(-5, -5);
00529         glVertex2f(50, -5);
00530         glVertex2f(50, 12);
00531         glVertex2f(-5, 12);
00532     glEnd();
00533     glEnable(GL_TEXTURE_2D);
00534     if (fpsi > 60)
00535     {
00536         glColor3ub(0, 255, 0); // green, good refresh rate
00537     }
00538     else if (fpsi > 45)
00539     {
00540         glColor3ub(255, 255, 0); // yellow, mediocre refresh rate.
00541     }
00542     else if (fpsi > 30)
00543     {
00544         glColor3ub(255, 127, 0); // orange, poor refresh rate
00545     }
00546     else
00547     {
00548         glColor3ub(255, 0, 0); // red, very bad refresh rate.
00549     }
00550     font.Render(msg);
00551     glColor3ub(255, 255, 255);
00552     glEnable(GL_DEPTH_TEST);
00553     glDisable(GL_BLEND);
00554     glLoadIdentity();
00555 #endif
00556 }
00557 
00558 void GameScene::draw_paused_screen()
00559 {
00560     glViewport(0,
00561                0,
00562                Graphics::Window::get_instance().get_width(),
00563                Graphics::Window::get_instance().get_height());
00564     Graphics::Window::get_instance().set_ortho_projection();
00565     glLoadIdentity();
00566     glDisable(GL_DEPTH_TEST);
00567     // darken screen to reduce burn in
00568     glDisable(GL_TEXTURE_2D);
00569     glEnable(GL_BLEND);
00570     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00571     glColor4f(0.0, 0.0, 0.0, 0.5);
00572     glBegin(GL_QUADS);
00573         float x = Graphics::Window::get_instance().get_width() + 1.0;
00574         float y = Graphics::Window::get_instance().get_height() + 1.0;
00575         glVertex2f(-1, -1);
00576         glVertex2f(x, -1);
00577         glVertex2f(x, y);
00578         glVertex2f(-1, y);
00579     glEnd();
00580     // show menu in the middle of the screen.
00581     glEnable(GL_TEXTURE_2D);
00582     glColor3f(1, 1, 1);
00583     glTranslatef(int(x/2), int(y/2), 0.0);
00584     glDisable(GL_CULL_FACE);
00585     m_pause_menu.draw();
00586     glEnable(GL_DEPTH_TEST);
00587     glEnable(GL_CULL_FACE);
00588     glDisable(GL_BLEND);
00589 }
00590 
00591 void GameScene::draw_for_player(unsigned int player, float aspect)
00592 {
00593     glMatrixMode(GL_PROJECTION);
00594     glLoadIdentity();
00595     gluPerspective(65.0, aspect, 0.03, 1000.0);
00596     glMatrixMode(GL_MODELVIEW);
00597     glClear(GL_DEPTH_BUFFER_BIT);
00598     
00599     draw_world(player, aspect);
00600     
00601     glMatrixMode(GL_PROJECTION);
00602     glLoadIdentity();
00603     if (aspect > 1.0)
00604     {
00605         // screen wider than it is tall, add extra horizontal space
00606         glOrtho(-aspect, aspect, -1, 1, -1, 1);
00607     } else {
00608         // screen taller than it is wide, add extra vertical space.
00609         GLdouble aspect_inv = 1.0 / aspect;
00610         glOrtho(-1, 1, -aspect_inv, aspect_inv, -1, 1);
00611     }
00612     glMatrixMode(GL_MODELVIEW);
00613        
00614     draw_hud(player);
00615 }
00616 
00617 void GameScene::draw_world(unsigned int player, float aspect)
00618 {
00619     glLoadIdentity();
00620     car_cameras[player]->full_transform();
00621     car_cameras[player]->update_occlusion_tester(occlusion_tester, aspect);
00622     
00623     // set up light and fog
00624     track.get_lighting().prepare_render(); 
00625     
00626     if (!show_debug)
00627     {
00628         const Track::Path & path = track.get_path();
00629         path.conditional_draw(occlusion_tester);
00630     }
00631     
00632     // draw sky box using only the camera rotation
00633     // (no translation to fake infinite distance)
00634     glDisable(GL_FOG);
00635     glDisable(GL_LIGHTING);
00636     glPushMatrix();
00637         glLoadIdentity();
00638         car_cameras[player]->rotation_transform();
00639         sky.draw();
00640     glPopMatrix();    
00641     if (track.get_lighting().get_fog().enabled) glEnable(GL_FOG);
00642     glEnable(GL_LIGHTING);
00643     
00644     // Cars
00645     // Need to be drawn over track & sky because of the blended engine flame.
00646     for (unsigned int i = 0; i < cars.size(); i++)
00647     { 
00648         cars[i]->conditional_draw(occlusion_tester);
00649     }
00650     if (track.get_theme().get_use_sky_particles())
00651     {
00652         m_sky_particles.draw(*(car_cameras[player]));
00653     }
00654     glDisable(GL_LIGHTING);
00655     glDisable(GL_FOG);
00656     // (Debug) draw the ai navigation graph.
00657 #ifndef NDEBUG
00658     if (show_debug)
00659     {
00660         glCallList(m_debug_list_name);
00661     }
00662 #endif
00663 }
00664 
00665 void GameScene::draw_hud(unsigned int player)
00666 {
00667     // Heads up display overlayed over the game.
00668     // We leave a border of 0.1 (5%) around the screen edges to keep in the
00669     // safe area incase of playing on a TV.
00670     glLoadIdentity();
00671     glEnable(GL_BLEND);
00672     glDisable(GL_DEPTH_TEST);
00673     glDisable(GL_CULL_FACE);
00674     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00675     
00676     if (cars[player]->get_finished())
00677     {
00678         draw_finished(player);
00679     }
00680     else if (cars[player]->get_disqualified())
00681     {
00682         draw_disqualified(player);
00683     }
00684     else
00685     {
00686         draw_speedometer(player);
00687         draw_lap_info(player);
00688         draw_rank(player);
00689         int backwards = cars[player]->get_facing_backwards();
00690         if (backwards)
00691         {
00692             glColor4ub(255, 255, 255, backwards);
00693             draw_reverse_sign(player);
00694             glColor3ub(255, 255, 255);
00695         }
00696         if (countdown_timer < 500)
00697         {
00698             draw_countdown();
00699         }
00700     }
00701     
00702     glEnable(GL_DEPTH_TEST);
00703     glEnable(GL_CULL_FACE);
00704     glDisable(GL_BLEND);
00705 }
00706 
00707 void GameScene::draw_speedometer(unsigned int player)
00708 {
00709     // size: 0.4, middle: 0.
00710     btScalar speed = cars[player]->get_speed() * 10.0;
00711     m_speedometer_texture->bind();
00712     glColor3f(1.0, speed / 80.0 + 0.5, speed / 150.0 + 0.5);
00713     glBegin(GL_QUADS);
00714         // background
00715         glTexCoord2f(0.0,  1.0); glVertex2f(0.5, -0.9);
00716         glTexCoord2f(0.0,  0.5); glVertex2f(0.5, -0.5);
00717         glTexCoord2f(0.5,  0.5); glVertex2f(0.9, -0.5);
00718         glTexCoord2f(0.5,  1.0); glVertex2f(0.9, -0.9);
00719     glEnd();
00720     // needle
00721     glTranslatef(0.7, -0.7, 0.0);
00722     glRotatef(speed * 2.25 + 180, 0.0, 0.0, -1.0);
00723     glBegin(GL_QUADS);
00724         glTexCoord2f(0.0,  0.5); glVertex2f(-0.2, -0.2);
00725         glTexCoord2f(0.0,  0.0); glVertex2f(-0.2, 0.2);
00726         glTexCoord2f(0.5,  0.0); glVertex2f(0.2, 0.2);
00727         glTexCoord2f(0.5,  0.5); glVertex2f(0.2, -0.2);
00728     glEnd();
00729     glLoadIdentity();
00730     glColor3f(1.0, speed / 60.0, speed / 100.0);
00731     glBegin(GL_QUADS);
00732         // needle cover
00733         glTexCoord2f(0.5,  1.0); glVertex2f(0.5, -0.9);
00734         glTexCoord2f(0.5,  0.5); glVertex2f(0.5, -0.5);
00735         glTexCoord2f(1.0,  0.5); glVertex2f(0.9, -0.5);
00736         glTexCoord2f(1.0,  1.0); glVertex2f(0.9, -0.9);
00737     glEnd();
00738     // text stating the speed
00739     std::stringstream speedo_text;
00740     speedo_text.precision(1);
00741     speedo_text << std::fixed << speed << " m/s";
00742     glTranslatef(0.565, -0.865, 0.0);
00743     glScalef(0.002, 0.002, 1);
00744     FTTextureFont & font = UI::BasicFonts::get_instance().big_font;
00745     glColor4f(1.0, 1.0, 1.0, 0.7);
00746     font.Render(speedo_text.str().c_str());
00747     glLoadIdentity();
00748     m_speedometer_texture->bind();
00749     glColor3f(1.0, 1.0, 1.0);
00750     glBegin(GL_QUADS);
00751         // number cover
00752         glTexCoord2f(0.5,  0.5); glVertex2f(0.5, -0.9);
00753         glTexCoord2f(0.5,  0.0); glVertex2f(0.5, -0.5);
00754         glTexCoord2f(1.0,  0.0); glVertex2f(0.9, -0.5);
00755         glTexCoord2f(1.0,  0.5); glVertex2f(0.9, -0.9);
00756     glEnd();
00757 }
00758 
00759 void GameScene::draw_lap_info(unsigned int player)
00760 {
00761     // display lap counter
00762     // darken corner.
00763     glDisable(GL_TEXTURE_2D);
00764     glColor4f(0.0, 0.0, 0.0, 0.5);
00765     glBegin(GL_QUADS);
00766         glVertex2f(0.5, 0.7);
00767         glVertex2f(0.5, 100.0);
00768         glVertex2f(100.0, 100.0);
00769         glVertex2f(100.0, 0.7);
00770     glEnd();
00771     glColor3ub(255, 255, 255);
00772     glEnable(GL_TEXTURE_2D);
00773     
00774     // display message
00775     std::stringstream lap_text;
00776     lap_text << "Lap " << cars[player]->get_lap() << " of 3";
00777     glTranslatef(0.5, 0.8, 0.0);
00778     glScalef(0.75/256.0, 0.75/256.0, 1);
00779     FTTextureFont & font = UI::BasicFonts::get_instance().big_font;
00780     font.Render(lap_text.str().c_str());
00781     glLoadIdentity();
00782     
00783     glDisable(GL_TEXTURE_2D);
00784     glBegin(GL_LINE_LOOP);
00785         glVertex2f(0.55, 0.76);
00786         glVertex2f(0.9, 0.76);
00787         glVertex2f(0.9, 0.78);
00788         glVertex2f(0.55, 0.78);
00789     glEnd();
00790     float x_end = 0.55 + 0.35 * cars[player]->get_lap_distance() / track.get_lap_length();
00791     glBegin(GL_QUADS);
00792         glVertex2f(0.55, 0.76);
00793         glVertex2f(x_end, 0.76);
00794         glVertex2f(x_end, 0.78);
00795         glVertex2f(0.55, 0.78);
00796     glEnd();
00797     glEnable(GL_TEXTURE_2D);
00798     if (cars[player]->get_lap() > 1)
00799     {
00800         std::stringstream time_text;
00801         long unsigned int time = cars[player]->get_last_lap_ticks();
00802         time_text << "Last " << time / 6000 << ":" << (time % 6000) / 100
00803                   << "." << ((time / 10) % 10) << (time % 10);
00804         glTranslatef(0.5, 0.7, 0.0);
00805         glScalef(0.75/256.0, 0.75/256.0, 1);
00806         font.Render(time_text.str().c_str());
00807         glLoadIdentity();
00808         // if the lap time is the best of all players, draw an icon next to it.
00809         unsigned long int best_time = std::numeric_limits<unsigned long int>::max();
00810         for (std::vector<GameObjects::Car *>::const_iterator it = cars.begin();
00811              it != cars.end(); it++)
00812         {
00813             unsigned long int car_best = (*it)->get_best_lap_ticks();
00814             if (best_time > car_best)
00815             {
00816                 best_time = car_best;
00817             }
00818         }
00819         if (time == best_time)
00820         {
00821             // draw icon.
00822             m_best_lap_texture->bind();
00823             glBegin(GL_QUADS);
00824                 glTexCoord2f(0.0,  1.0); glVertex2f(0.4, 0.675);
00825                 glTexCoord2f(0.0,  0.0); glVertex2f(0.4, 0.775);
00826                 glTexCoord2f(1.0,  0.0); glVertex2f(0.5, 0.775);
00827                 glTexCoord2f(1.0,  1.0); glVertex2f(0.5, 0.675);
00828             glEnd();
00829         }
00830     }
00831 }
00832 
00833 void GameScene::draw_rank(unsigned int player)
00834 {
00835     glDisable(GL_TEXTURE_2D);
00836     glColor4f(0, 0, 0, .75);
00837     glBegin(GL_QUADS);
00838         glVertex2f(-0.1, 0.8);
00839         glVertex2f( 0.1, 0.8);
00840         glVertex2f( 0.1, 100.0);
00841         glVertex2f(-0.1, 100.0);
00842     glEnd();
00843     glEnable(GL_TEXTURE_2D);
00844     glColor3f(1.0, 1.0, 1.0);
00845     
00846     std::stringstream rank_text;
00847     int rank = car_ranks[player] + 1;
00848     rank_text << rank;
00849     // work out what suffix to use (st, nd, rd, or th)
00850     int rank_mod100 = rank % 100;
00851     int rank_mod10 = rank % 10;
00852     // catch 11th, 12th, and 13th, so they don't become 11st, 12nd, and 13rd.
00853     if (rank_mod100 >= 4 && rank_mod100 <= 20) rank_text << "th";
00854     else if (rank_mod10 == 1) rank_text << "st";
00855     else if (rank_mod10 == 2) rank_text << "nd";
00856     else if (rank_mod10 == 3) rank_text << "rd";
00857     else rank_text << "th";
00858     glTranslatef(-0.1, 0.8, 0.0);
00859     glScalef(0.004, 0.004, 1);
00860     FTTextureFont & font = UI::BasicFonts::get_instance().big_font;
00861     font.Render(rank_text.str().c_str());
00862     glLoadIdentity();
00863 }
00864 
00865 void GameScene::draw_disqualified(unsigned int player)
00866 {
00867     glDisable(GL_TEXTURE_2D);
00868     unsigned long int ticks_since = world.get_tick_number() - cars[player]->get_finish_time();
00869     glColor4f(0, 0, 0, 1.0 - 1.0 / (float(ticks_since) / 100.0));
00870     glBegin(GL_QUADS);
00871         glVertex2f(-100.0, -100.0);
00872         glVertex2f( 100.0, -100.0);
00873         glVertex2f( 100.0,  100.0);
00874         glVertex2f(-100.0,  100.0);
00875     glEnd();
00876     glEnable(GL_TEXTURE_2D);
00877     glColor3f(1.0, 1.0, 1.0);
00878     
00879     m_disqualified_text_texture->bind();
00880     glBegin(GL_QUADS);
00881         glTexCoord2f(0, 1); glVertex2f(-1.0, -0.25);
00882         glTexCoord2f(1, 1); glVertex2f( 1.0, -0.25);
00883         glTexCoord2f(1, 0); glVertex2f( 1.0,  0.25);
00884         glTexCoord2f(0, 0); glVertex2f(-1.0,  0.25);
00885     glEnd();
00886 }
00887 
00888 void GameScene::draw_finished(unsigned int player)
00889 {
00890     // black background
00891     glDisable(GL_TEXTURE_2D);
00892     glColor4f(0.0, 0.0, 0.0, 0.75);
00893     glBegin(GL_QUADS);
00894         glVertex2f(-100.0, -0.3);
00895         glVertex2f( 100.0, -0.3);
00896         glVertex2f( 100.0,  0.3);
00897         glVertex2f(-100.0,  0.3);
00898     glEnd();
00899     
00900     // checkered border
00901     glEnable(GL_TEXTURE_2D);
00902     m_finish_border_texture->bind();
00903     glColor3f(1.0, 1.0, 1.0);
00904     glMatrixMode(GL_TEXTURE);
00905     // make it move
00906     glTranslatef(float(world.get_tick_number()) * 0.003, 0, 0);
00907     glBegin(GL_QUADS);
00908         glTexCoord2f(-1000, 1); glVertex2f(-100.0, 0.3);
00909         glTexCoord2f( 1000, 1); glVertex2f( 100.0, 0.3);
00910         glTexCoord2f( 1000, 0); glVertex2f( 100.0, 0.4);
00911         glTexCoord2f(-1000, 0); glVertex2f(-100.0, 0.4);
00912         
00913         glTexCoord2f( 1000, 1); glVertex2f(-100.0, -0.3);
00914         glTexCoord2f(-1000, 1); glVertex2f( 100.0, -0.3);
00915         glTexCoord2f(-1000, 0); glVertex2f( 100.0, -0.4);
00916         glTexCoord2f( 1000, 0); glVertex2f(-100.0, -0.4);
00917     glEnd();
00918     glLoadIdentity();
00919     glMatrixMode(GL_MODELVIEW);
00920     
00921     // text that says "Finished" in the middle.
00922     m_finish_text_texture->bind();
00923     glBegin(GL_QUADS);
00924         glTexCoord2f(0, 1); glVertex2f(-1.0, -0.25);
00925         glTexCoord2f(1, 1); glVertex2f( 1.0, -0.25);
00926         glTexCoord2f(1, 0); glVertex2f( 1.0,  0.25);
00927         glTexCoord2f(0, 0); glVertex2f(-1.0,  0.25);
00928     glEnd();
00929     
00930     glDisable(GL_TEXTURE_2D);
00931     
00932     // white screen flash
00933     unsigned long int ticks_since = world.get_tick_number() - cars[player]->get_finish_time();
00934     glColor4f(1.0, 1.0, 1.0, 1.0 / (float(ticks_since) / 10.0));
00935     glBegin(GL_QUADS);
00936         glVertex2f(-100.0, -100.0);
00937         glVertex2f( 100.0, -100.0);
00938         glVertex2f( 100.0,  100.0);
00939         glVertex2f(-100.0,  100.0);
00940     glEnd();
00941     glEnable(GL_TEXTURE_2D);
00942     glColor3f(1.0, 1.0, 1.0);
00943     
00944     // text displaying the time taken.
00945     std::stringstream time_text;
00946     unsigned long int time = cars[player]->get_finish_time();
00947     time_text << "Time: " << (time / 6000)
00948               << ((time / 6000 == 1) ? " minute, " : " minutes, ")
00949               << (time / 100) % 60 << "."
00950               << (time / 10) % 10 << (time % 10) << " seconds";
00951     glTranslatef(-0.9, -0.27, 0.0);
00952     glScalef(0.004, 0.004, 1);
00953     FTTextureFont & font = UI::BasicFonts::get_instance().big_font;
00954     font.Render(time_text.str().c_str());
00955     glLoadIdentity();
00956 }
00957 
00958 void GameScene::draw_reverse_sign(unsigned int player)
00959 {
00960     m_reverse_texture->bind();
00961     glBegin(GL_QUADS);
00962         glTexCoord2f(0.0, 1.0); glVertex2f(-0.3, 0.0);
00963         glTexCoord2f(1.0, 1.0); glVertex2f( 0.3, 0.0);
00964         glTexCoord2f(1.0, 0.0); glVertex2f( 0.3, 0.6);
00965         glTexCoord2f(0.0, 0.0); glVertex2f(-0.3, 0.6);
00966     glEnd();
00967 }
00968 
00969 void GameScene::draw_countdown()
00970 {
00971     m_countdown_texture->bind();
00972     unsigned int tick = 3000+countdown_timer;
00973     // which sign to show (0->3, 1->2, 2->1, 3->Go)
00974     unsigned int n = tick/1000;
00975     float xl = float(n) / 4.0;
00976     float xh = xl + 0.25;
00977     // time taken on this sign in seconds
00978     float s_time = float (tick - n * 1000) / 1000.0;
00979     glPushMatrix();
00980         if (n == 3)
00981         {
00982             s_time *= 2.0;
00983             float scale = 1.0 + sqrt(s_time) * 2.0;
00984             glScalef(scale, scale, 1.0);
00985         }
00986         if (s_time > 0.875)
00987         {
00988             //glColor4f(1.0, 1.0, 1.0, s_time*16.0-15.0);
00989             float scale_t = 8.0+s_time*-8.0;
00990             float h_scale = 1.0/(scale_t);
00991             float v_scale = scale_t * scale_t;
00992             glScalef(h_scale, v_scale, 1.0);
00993             // white and grey
00994             glBegin(GL_QUADS);
00995                 glTexCoord2f(xl, 0.5); glVertex2f(-0.3, -0.3);
00996                 glTexCoord2f(xh, 0.5); glVertex2f( 0.3, -0.3);
00997                 glTexCoord2f(xh, 0.0); glVertex2f( 0.3, 0.3);
00998                 glTexCoord2f(xl, 0.0); glVertex2f(-0.3, 0.3);
00999             glEnd();
01000         } else {
01001             // coloured outline, dark shadow
01002             glBegin(GL_QUADS);
01003                 glTexCoord2f(xl, 1.0); glVertex2f(-0.3, -0.3);
01004                 glTexCoord2f(xh, 1.0); glVertex2f( 0.3, -0.3);
01005                 glTexCoord2f(xh, 0.5); glVertex2f( 0.3, 0.3);
01006                 glTexCoord2f(xl, 0.5); glVertex2f(-0.3, 0.3);
01007             glEnd();
01008         }
01009     glPopMatrix();
01010 }
01011 
01012 void GameScene::do_sound()
01013 {
01014     // The changes in audio object's state are done in update_logic
01015     // OpenAL needs to be told the result, though:
01016     Audio::get_instance().commit();
01017 }
01018 
01019 Physics::World & GameScene::get_world()
01020 {
01021     return world;
01022 }
01023 
01024 void GameScene::set_save_replay(bool value)
01025 {
01026     save_replay = value;
01027 }
01028 
01029 void GameScene::rank_cars()
01030 {
01031     car_ranks.resize(cars.size());
01032     // sort cars by lap position + lap length * number of laps completed.
01033     std::multimap<btScalar, std::size_t> car_positions;
01034     
01035     // Add a bit on to the lap length. The lap length is the shortest route
01036     // around, so a car may be further around the lap than the lap is long,
01037     // but we don't want to count this in the position so cars don't get
01038     // ranked behind a car on the previous lap.
01039     btScalar lap_length = track.get_lap_length() + 100.0;
01040     
01041     for (std::size_t index = 0; index < cars.size(); index++)
01042     {
01043         GameObjects::Car & car = *cars[index];
01044         btScalar position;
01045         if (car.get_disqualified())
01046         {
01047             position = 0;
01048         } else {
01049             position = car.get_lap_distance() + car.get_lap() * lap_length;
01050         }
01051         car_positions.insert(std::pair<btScalar, std::size_t>(position, index));
01052     }
01053     // rank cars in reverse order of car_positions, as it is sorted from the
01054     // lowest distance around to course to the highest: highest is the leader.
01055     int rank = cars.size() - 1;
01056     for (std::map<btScalar, std::size_t>::iterator it = car_positions.begin();
01057          it != car_positions.end();
01058          it++)
01059     {
01060         car_ranks[it->second] = rank;
01061         rank--;
01062     }
01063     
01064 }
01065 
01066 void GameScene::unpause()
01067 {
01068     // menu is already giving a response. Reset it so we can show it again.
01069     m_pause_menu.reset();
01070     paused = false;
01071     // start the sound
01072     Audio::get_instance().resume();
01073 }
01074 
01075 }

Get Racer at SourceForge.net. Fast, secure and Free Open Source software downloads

Generated at Mon Sep 6 00:41:12 2010 by Doxygen version 1.4.7 for Racer version svn335.