Car.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 "Car.h"
00012 #include <GL/gl.h>
00013 #include <btBulletDynamicsCommon.h>
00014 #include <btBulletCollisionCommon.h>
00015 #include <BulletCollision/CollisionShapes/btTriangleShape.h>
00016 #include "../InputDevice.h"
00017 #include "../ResourceHandler.h"
00018 #include <Debug.h>
00019 #include <cmath>
00020 #include <limits>
00021 #include <libtrack/TrackBooster.h>
00022 
00023 // uncomment and play (or watch replay) with a single human player
00024 // to make artificial neural network training data file.
00025 // Train the neural network to this file with racer_train_ai.
00026 // After that, you can disable it again and run racer t track to improve on
00027 // the neural network by racing mutated AI players against each other.
00028 //#define TRAIN_AI
00029 
00030 #ifdef TRAIN_AI
00031 #include <fstream>
00032 #endif
00033 
00034 const btVector3 feeler_vectors[Engine::GameObjects::Car::NUM_SENSORS_FEELERS] = {
00035                 btVector3( 0,  1, 0),
00036                 btVector3( 1,  0, 0),
00037                 btVector3(-1,  0, 0),
00038                 btVector3( 1,  1, 0).normalized(),
00039                 btVector3(-1,  1, 0).normalized(),
00040                 btVector3(-0.2, 1, 0).normalized(),
00041                 btVector3( 0.2, 1, 0).normalized(),
00042                 btVector3( 0, -1, 0)};
00043 
00044 const btScalar _force_scale = 1.5;
00045 
00047 const btScalar _car_stearing_scale[2] = {0.02, 0.03};
00049 const btScalar _car_stearing_scale_midair[2] = {0.01, 0.015};
00053 const btScalar _car_slide_scale[2] = {1.0, 1.5};
00054 
00055 
00056 /* Correction for stupid bouncing my cars up in the air when they drive over the
00057  * internal edges of flat triangulated sections of the road.
00058  */
00059 
00060 void contact_added_callback_obj (btManifoldPoint& cp,
00061                                  const btCollisionObject* colObj,
00062                                  int partId, int index)
00063 {
00064         (void) partId;
00065         (void) index;
00066         const btCollisionShape *shape = colObj->getCollisionShape();
00067 
00068         if (shape->getShapeType() != TRIANGLE_SHAPE_PROXYTYPE) return;
00069         const btTriangleShape *tshape =
00070                static_cast<const btTriangleShape*>(colObj->getCollisionShape());
00071 
00072 
00073         const btCollisionShape *parent = colObj->getRootCollisionShape();
00074         if (parent == NULL) return;
00075         if (parent->getShapeType() != TRIANGLE_MESH_SHAPE_PROXYTYPE) return;
00076 
00077         btTransform orient = colObj->getWorldTransform();
00078         orient.setOrigin( btVector3(0.0f,0.0f,0.0f ) );
00079 
00080         btVector3 v1 = tshape->m_vertices1[0];
00081         btVector3 v2 = tshape->m_vertices1[1];
00082         btVector3 v3 = tshape->m_vertices1[2];
00083 
00084         btVector3 normal = (v2-v1).cross(v3-v1);
00085 
00086         normal = orient * normal;
00087         normal.normalize();
00088 
00089         btScalar dot = normal.dot(cp.m_normalWorldOnB);
00090         btScalar magnitude = cp.m_normalWorldOnB.length();
00091         normal *= dot > 0 ? magnitude : -magnitude;
00092 
00093         cp.m_normalWorldOnB = normal;
00094 }
00095 
00096 bool contact_added_callback (btManifoldPoint& cp,
00097                              const btCollisionObject* colObj0,
00098                              int partId0, int index0,
00099                              const btCollisionObject* colObj1,
00100                              int partId1, int index1)
00101 {
00102         contact_added_callback_obj(cp, colObj0, partId0, index0);
00103         contact_added_callback_obj(cp, colObj1, partId1, index1);
00104         //std::cout << to_ogre(cp.m_normalWorldOnB) << std::endl;
00105         return true;
00106 }
00107 
00108 extern ContactAddedCallback      gContactAddedCallback;
00109 
00110 namespace Engine
00111 {
00112 
00113 namespace GameObjects
00114 {
00115 
00116 // shorter names for getting the car texture and mesh resources.
00117 typedef ResourceHandler<Track::Texture, int, std::string> TextureStore;
00118 typedef ResourceHandler<Track::BulletMesh, int, Track::BulletMesh::ConstructionInformation> MeshStore;
00119 
00120 Car::Car(Physics::World & world, btTransform start, InputDevice * input_device,
00121          unsigned int car_model, const btVector3 & plane_normal,
00122         const btScalar & plane_distance, const btVector3 & start_point)
00123     :   NearTrack(world.get_track().get_ai_graph(), start.getOrigin())
00124     ,   world(world),
00125         input_device(input_device),
00126         force(0, 0, 0),
00127         torque(0, 0, 0),
00128         local_force(0, 0, 0),
00129         local_torque(0, 0, 0),
00130         floor_stick(false),
00131         car_model(car_model)
00132     ,   lap(0)
00133     ,   max_lap(1)
00134     ,   lap_start_ticks(0)
00135     ,   best_lap_ticks(std::numeric_limits<unsigned long int>::max())
00136     ,   plane_normal(plane_normal)
00137     ,   plane_distance(plane_distance)
00138     ,   start_point(start_point)
00139     ,   elastic_potential_energy(2<<27)
00140     ,   tick_energy_absorbed(0)
00141     ,   used_energy_boost(0)
00142     ,   boost_timer(100)
00143     ,   m_course_complete(false)
00144     ,   m_removed(false)
00145 {
00146     input_device->set_car(this);
00147     
00148     // load a texture and mesh for the car, if not already loaded.
00149     const unsigned int mesh_generator_bits =
00150                             Track::BulletMesh::genererator_convex_hull_bit;
00151     if (car_model == 0)
00152     {
00153         TextureStore::get_instance().check_load(0,
00154                                                 "data/cars/1/craft_full.png");
00155         MeshStore::get_instance().check_load(0,
00156                                              std::make_pair("data/cars/1/mesh",
00157                                                             mesh_generator_bits));
00158     }
00159     else
00160     {
00161         TextureStore::get_instance().check_load(1,
00162                                                 "data/cars/2/craft_full.png");
00163         MeshStore::get_instance().check_load(1,
00164                                              std::make_pair("data/cars/2/mesh",
00165                                                             mesh_generator_bits));
00166     }
00167     TextureStore::get_instance().check_load(-1, "data/generic/flame_grad.png");
00168     flame_texture = TextureStore::get_instance().get(-1);
00169     flame_texture->bind();
00170     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
00171     
00172     MeshStore::get_instance().check_load(-1,
00173                                              std::make_pair("data/generic/engine_flame",
00174                                                             0));
00175     flame_mesh = MeshStore::get_instance().get(-1);
00176     mesh = MeshStore::get_instance().get(car_model);
00177     texture = TextureStore::get_instance().get(car_model);
00178     btConvexHullShape* shape = mesh->get_convex_hull_shape();
00179     shape->setMargin(btScalar(0.04));
00180     btScalar mass(500.0);
00181     btVector3 inertia(0,0,0);
00182     shape->calculateLocalInertia(mass, inertia);
00183     
00184     btRigidBody::btRigidBodyConstructionInfo rigid_body_CI(mass,
00185                                                            0,
00186                                                            shape,
00187                                                            inertia);
00188     rigid_body_CI.m_linearDamping = btScalar(0.1);
00189     rigid_body_CI.m_angularDamping = btScalar(0.9992);
00190     rigid_body_CI.m_friction = btScalar(0.4);
00191     rigid_body_CI.m_restitution = 0.25;
00192     rigid_body_CI.m_startWorldTransform = start;
00193     rigid_body = new btRigidBody(rigid_body_CI);
00194     
00195     rigid_body->setSleepingThresholds(btScalar(0.0),    btScalar(0.0));  
00196     
00197     world.get_dynamics_world().addRigidBody(rigid_body);
00198     
00199     gContactAddedCallback = contact_added_callback;
00200     
00201     rigid_body->setCollisionFlags(rigid_body->getCollisionFlags() |
00202     btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
00203     
00204     world.add_tick_observer(this);
00205     
00206     infront_of_start = false;
00207     
00208     // sound
00209     static SoundBuffer engine_sound_buffer("data/sound/engine.wav");
00210     m_engine_sound_source = boost::shared_ptr<SoundSource>(new SoundSource(engine_sound_buffer));
00211     m_engine_sound_source->set_looping();
00212     
00213     static SoundBuffer drag_sound_buffer("data/sound/car_drag.wav");
00214     m_drag_sound_source = boost::shared_ptr<SoundSource>(new SoundSource(drag_sound_buffer));
00215     m_drag_sound_source->set_looping();
00216     
00217     static SoundBuffer break_sound_buffer("data/sound/car_break.wav");
00218     m_break_sound_source = boost::shared_ptr<SoundSource>(new SoundSource(break_sound_buffer));
00219     m_break_sound_source->set_looping();
00220     
00221     static SoundBuffer boost_sound_buffer("data/sound/boost.wav");
00222     m_boost_sound_source = boost::shared_ptr<SoundSource>(new SoundSource(boost_sound_buffer));
00223     m_boost_sound_source->set_gain(2.0);
00224     
00225     static SoundBuffer release_energy_sound_buffer("data/sound/car_release_energy.wav");
00226     m_release_energy_sound_source = boost::shared_ptr<SoundSource>(new SoundSource(release_energy_sound_buffer));
00227     m_release_energy_sound_source->set_looping();
00228     
00229     static SoundBuffer slide_sound_buffer("data/sound/slide.wav");
00230     m_slide_sound_source = boost::shared_ptr<SoundSource>(new SoundSource(slide_sound_buffer));
00231     m_slide_sound_source->set_looping();
00232     
00233     update_sounds();
00234     m_release_energy_sound_source->play();
00235     m_engine_sound_source->play();
00236     m_drag_sound_source->play();
00237     m_break_sound_source->play();
00238     m_slide_sound_source->play();
00239     
00240     // we should be in a reasonable starting position
00241     assert(get_position().distance2(rigid_body->getCenterOfMassPosition()) < 225.0);
00242 }
00243 
00244 Car::~Car()
00245 {
00246     if (!m_removed) remove();
00247     // Stop the car's InputDevice from sending reports to this car.
00248     input_device->set_car(0);
00249 }
00250 
00251 Track::AxisAlignedBoundingBox Car::get_bounds() const
00252 {
00253     if (m_removed)
00254     {
00255         // no bounds, nothing is drawn.
00256         return Track::AxisAlignedBoundingBox();
00257     }
00262     btTransform transform = rigid_body->getCenterOfMassTransform();
00263     return mesh->get_bounds().transform(transform);
00264 }
00265 
00266 
00267 void Car::draw() const
00268 {
00269     if (m_removed) return;
00270     texture->bind();
00271     glPushMatrix();
00272         // get the position and angle from the physics engine.
00273         btTransform transform = rigid_body->getCenterOfMassTransform();
00274         btScalar transform_matrix[16];
00275         transform.getOpenGLMatrix(transform_matrix);
00276         glMultMatrixf(transform_matrix);
00277         // additionally, roll depending on the slide.
00278         glRotatef(force.getX()*0.0005, 0.0, 1.0, 0.0);
00279         mesh->draw();
00280         if (used_energy_boost > 0)
00281         {
00282             // show released energy by making the car glow.
00283             glDisable(GL_LIGHTING);
00284             glBlendFunc(GL_SRC_ALPHA, GL_ONE);
00285             glEnable(GL_BLEND);
00286             glDepthFunc(GL_LEQUAL);
00287             glDisable(GL_TEXTURE_2D);
00288             glColor3f(used_energy_boost * 0.0000001, used_energy_boost * 0.00000005, used_energy_boost * 0.00000001);
00289             mesh->draw();
00290             glEnable(GL_TEXTURE_2D);
00291             glDisable(GL_BLEND);
00292             glDepthFunc(GL_LESS);
00293             glColor3ub(255, 255, 255);
00294             glEnable(GL_LIGHTING);
00295         }
00296         // engine flames
00297         // The flame is not affected by lighting, double sided, doesn't
00298         // change the depth buffer, doesn't darken the scene behind
00299         // when blended over, and reflects the textures alpha value.
00300         glDisable(GL_LIGHTING);
00301         glEnable(GL_BLEND);
00302         glDisable(GL_CULL_FACE);
00303         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
00304         glDepthMask(GL_FALSE);
00305         glMatrixMode(GL_TEXTURE);
00306         glPushMatrix();
00307             // Move the flame effect randomly to make it flicker.
00308             glTranslatef(0.0, float(std::rand()%256) * 0.0004, 0.0);
00309             // intensity is dependant on how hard you are accelerating.
00310             // there is no flame if not accelerating, breaking, or going
00311             // backwards.
00312             // The flame is brighter just after a boost.
00313             glColor4f(1.0, 1.0, 1.0, ((100.0 - boost_timer) / 200.0 + 0.5) * (force.getY() / 32768.0));
00314             flame_texture->bind();
00315             
00316             glMatrixMode(GL_MODELVIEW);
00317             float b_scale = (100.0 - boost_timer) * 0.05 + 1.0;
00318             switch (car_model)
00319             {
00320                 case 0:
00321                     glPushMatrix();
00322                         glTranslatef(0.046, -0.231, 0.032);
00323                         glScalef(0.03, 0.03*b_scale, 0.03);
00324                         flame_mesh->draw();
00325                     glPopMatrix();
00326                     glPushMatrix();
00327                         glTranslatef(-0.046, -0.231, 0.032);
00328                         glScalef(0.03, 0.03*b_scale, 0.03);
00329                         flame_mesh->draw();
00330                     glPopMatrix();
00331                     break;
00332                 case 1:
00333                     glPushMatrix();
00334                         glTranslatef(0.151, -0.295, 0.0);
00335                         glScalef(0.047, 0.047*b_scale, 0.047);
00336                         flame_mesh->draw();
00337                     glPopMatrix();
00338                     glPushMatrix();
00339                         glTranslatef(-0.151, -0.295, 0.0);
00340                         glScalef(0.047, 0.047*b_scale, 0.047);
00341                         flame_mesh->draw();
00342                     glPopMatrix();
00343                     break;
00344             }
00345             // reset OpenGL settings.
00346             glMatrixMode(GL_TEXTURE);
00347         glPopMatrix();
00348         glMatrixMode(GL_MODELVIEW);
00349         glEnable(GL_LIGHTING);
00350         glEnable(GL_CULL_FACE);
00351         glDepthMask(GL_TRUE);
00352         glDisable(GL_BLEND);
00353     glPopMatrix();
00354 #ifndef NDEBUG
00355     glDisable(GL_TEXTURE_2D);
00356     glBegin(GL_LINE_STRIP);
00357         glVertex3f(get_position().x(), get_position().y(), get_position().z());
00358         glVertex3f(transform.getOrigin().x(),
00359                    transform.getOrigin().y(),
00360                    transform.getOrigin().z());
00361     glEnd();
00362     glBegin(GL_LINES);
00363         glColor3f(0,1,0);
00364         const Track::MeshFaces::Graph & graph = world.get_track().get_ai_graph();
00365         const btVector3 *v = graph[get_face_descriptor()].vertex_positions;
00366         glVertex3f(v[0].x(), v[0].y(), v[0].z());
00367         glVertex3f(v[1].x(), v[1].y(), v[1].z());
00368         glColor3f(0,0,1);
00369         glVertex3f(v[0].x(), v[0].y(), v[0].z());
00370         glVertex3f(v[2].x(), v[2].y(), v[2].z());
00371     glEnd();
00372     glColor3f(1,1,1);
00373     
00374     // AI sensors- disabled because it is slightly slower and distracting
00375 #if 0
00376     btScalar r[NUM_SENSORS];
00377     get_sensors(r);
00378     // feeler distances
00379     glDisable(GL_DEPTH_TEST);
00380     glEnable(GL_TEXTURE_2D);
00381     glBegin(GL_LINES);
00382         
00383         for (unsigned int i = 0; i < NUM_SENSORS_FEELERS; i++)
00384         {
00385             glTexCoord2f(0, 0);
00386             glVertex3f(transform.getOrigin().x(),
00387                    transform.getOrigin().y(),
00388                    transform.getOrigin().z());
00389             btScalar distance = 1.0 / r[i * NUM_FEELER_PROPERTIES];
00390             btVector3 d =transform(feeler_vectors[i] * distance);
00391             glTexCoord2f(distance, distance);
00392             glVertex3f(d.x(), d.y(), d.z());
00393         }
00394     glEnd();
00395     glEnable(GL_DEPTH_TEST);
00396 #endif // 0
00397     glEnable(GL_TEXTURE_2D);
00398 #endif // debug
00399 }
00400 
00401 void Car::take_input(InputReport & report)
00402 {
00403     switch (report.get_report_type())
00404     {
00405         case InputReport::RT_CHANGE_ACCEL:
00407             force.setY(btScalar(report.get_value()));
00408             break;
00409         case InputReport::RT_CHANGE_SLIDE:
00411             force.setX(btScalar(report.get_value()));
00412             break;
00413         case InputReport::RT_CHANGE_STEERING:
00415             torque.setZ(btScalar(-report.get_value()));
00416             break;
00417         default:
00418             // ignore menu commands, etc.
00419             break;
00420     }
00421     // record input for replay
00422     world.add_replay_report(report);
00423     update_sounds();
00424 }
00425 
00426 void Car::set_forces()
00427 {
00428     rigid_body->clearForces();
00429     rigid_body->applyCentralForce(local_force);
00430     rigid_body->applyTorque(local_torque);  
00431 }
00432 
00433 void Car::posttick()
00434 {
00435 #ifdef TRAIN_AI
00436     static std::ofstream data_file("ANN_training_data");
00437     static bool train_data_init = false;
00438     if (!train_data_init)
00439     {
00440         // artifical neural network training data file should begin with the
00441         // number of tests, the number of inputs, and the number of outputs.
00442         // We don't know the number of tests yet, but the rest we can provide:
00443         // The tests is the number of lines subtract 1 (this one), halved (in&out)
00444         data_file << "#tests "<< NUM_SENSORS << " 3\n";
00445         train_data_init = true;
00446     }
00447     // Save sensor data and response pairs for AI neural network training data.
00448     // The inputs to the neural network are the sensor data.
00449     // every tick is a bit excessive, so provide less data:
00450     if (world.get_tick_number() % 4 == 3)
00451     {
00452         float results[NUM_SENSORS];
00453         get_sensors(results);
00454         // infinite or NaN values might upset training.
00455         // Also, these are mostly produced when the edge avoision blending
00456         // gives no weight to the neural network anyway, so it doesn't make
00457         // sense to use a neural network on it.
00458         bool good = true;
00459         for (unsigned int i = 0; i < NUM_SENSORS; i++)
00460         {
00461             good &= std::isfinite(results[i]);
00462         }
00463         good &= results[4] > 0.0;
00464         if (good)
00465         {
00466             for (unsigned int i = 0; i < NUM_SENSORS; i++)
00467             {
00468                 data_file << results[i] << ' ';
00469             }
00470             data_file << '\n';
00471             // The outputs are acceleration, slide, and steering,
00472             // scaled to the range -1 to 1:
00473             data_file << force.y()/32767.0 << ' ' << force.x()/32767.0 << ' ' << torque.z()/-32767.0 << '\n';
00474         }
00475     }
00476 #endif
00477     
00478     btTransform transform = rigid_body->getCenterOfMassTransform();
00479     btTransform rotation_transform(transform);
00480     rotation_transform.setOrigin(btVector3(0, 0, 0));
00481     
00482     move_towards(transform.getOrigin());
00483     
00484     // passed start finish line?
00485     bool new_infront_of_start = transform.getOrigin().dot(plane_normal) + plane_distance > 0;
00486     if (new_infront_of_start != infront_of_start)
00487     {
00488         // is this near enough to the start finish line?
00494         if (transform.getOrigin().distance2(start_point) < 289)
00495         {
00496             if (new_infront_of_start)
00497             {
00498                 lap++;
00499                 // has the user completed this lap before?
00500                 if (lap > max_lap)
00501                 {
00502                     // properly completed a lap.
00503                     unsigned long int tick = world.get_tick_number();
00504                     last_lap_ticks = tick - lap_start_ticks;
00505                     lap_start_ticks = tick;
00506                     max_lap = lap;
00507                     DEBUG_MESSAGE("Car " << this << " completed a lap in " << last_lap_ticks / 6000 << " min, " << (last_lap_ticks / 100) % 60 << " sec, " << last_lap_ticks % 100 << "0 msec");
00508                     if (best_lap_ticks > last_lap_ticks)
00509                     {
00510                         // new lap record for this session and car.
00511                         best_lap_ticks = last_lap_ticks;
00512                     }
00513                     if (lap == 4)
00514                     {
00515                         // end of the race
00516                         m_course_complete = true;
00517                         remove();
00518                         return;
00519                     }
00520                 }
00521             } else {
00522                 lap--;
00523             }
00524         }
00525         infront_of_start = new_infront_of_start;
00526         //DEBUG_MESSAGE("Car " << this << " is on lap " << lap);
00527     }
00528     
00529     // near booster?
00530     if (boost_timer >= 40)
00531     {
00532         const Track::Track & track = world.get_track();
00533         const Track::Path & path = track.get_path();
00534         typedef boost::graph_traits<Track::Path::Graph>::edge_iterator EdgeIterator;
00535         std::pair<EdgeIterator, EdgeIterator> edge_range;
00536         for (edge_range = boost::edges(path.graph);
00537              edge_range.first != edge_range.second;
00538              edge_range.first++)
00539         {
00540             const Track::PathEdge & edge = path.graph[*(edge_range.first)];
00541             typedef std::vector<boost::shared_ptr<Track::TrackAttachment> > Vector;
00542             const Vector & attachments = edge.get_attachments();
00543             for (Vector::const_iterator it = attachments.begin();
00544                  it != attachments.end();
00545                  it++)
00546             {
00547                 const Track::TrackBooster * booster = dynamic_cast<const Track::TrackBooster*>(&(**it));
00548                 if (booster)
00549                 {
00550                     btScalar distance2 = booster->get_global_transform().
00551                                 getOrigin().distance2(transform.getOrigin());
00552                     if (distance2 < 1.0)
00553                     {
00554                         // boost
00555                         boost_timer = 0;
00556                         m_boost_sound_source->play();
00557                         //DEBUG_MESSAGE("Boosted");
00558                     }
00559                 }
00560             }
00561         }
00562         if (boost_timer < 100) boost_timer++;
00563     }
00564     else
00565     {
00566         boost_timer++;
00567     }
00568     
00569     // find where the line from the thruster in direction of force goes in world space.
00570     btVector3 check_vector = transform(btVector3(0.0, 0.0, -5.547));
00571     btVector3 start_vector = transform(btVector3(0.0, 0.0, -0.047));
00572     
00573     // raycast out to see if we find a surface.
00574     btCollisionWorld::ClosestRayResultCallback thruster_ray_callback(start_vector,
00575                                                                      check_vector);
00576     world.get_floor_world().rayTest(
00577                                         start_vector,
00578                                         check_vector,
00579                                         thruster_ray_callback);
00580     btScalar floor_distance = 200.0;
00581     const btScalar desired_height = 0.22;
00582     if (thruster_ray_callback.hasHit())
00583     {
00584         btVector3 & surface_normal = thruster_ray_callback.m_hitNormalWorld;
00585         floor_distance = surface_normal.dot(
00586                         start_vector - thruster_ray_callback.m_hitPointWorld);
00587         
00588         // stick to constant height above floor.
00589         if (floor_distance <= desired_height)
00590         {
00591             if (!floor_stick)
00592             {
00593                 //DEBUG_MESSAGE("Sticking car to floor.");
00594             }
00595             floor_stick = true;
00596             rigid_body->setGravity(btVector3(0.0, 0.0, 0.0));
00597         }
00598         if (floor_stick)
00599         {
00600             // move to an almost constant height above the floor.
00601             // The /2.0 on the end is to reduce the effect, so distance varies
00602             // based on more realistic physics.
00603             btScalar mult = (desired_height - floor_distance) / 2.0 / 2.0;
00604             rigid_body->translate(surface_normal * mult);
00605             
00606             // now remove vertical motion.
00607             btVector3 linear_velocity = rigid_body->getLinearVelocity();
00608             linear_velocity = rotation_transform.inverse()(linear_velocity);
00610             linear_velocity.setZ(0.0);
00611             linear_velocity = rotation_transform(linear_velocity);
00612             rigid_body->setLinearVelocity(linear_velocity);
00613         }
00614         // Rotate so floor's normal is in the same direction as car's z axis.
00615         // find change in roll
00616         btVector3 vec_car_axis = rotation_transform(btVector3(0.0, 1.0, 0.0));
00617         btScalar cross_length = vec_car_axis.cross(surface_normal).length();
00618         // Make numerically stable: asin expects values <= 1, but sometimes
00619         // The cross product returns values greater than 1 (even though it
00620         // shouldn't).
00621         if (cross_length > 1.0) cross_length = 1.0;
00622         btScalar roll = cross_length ? M_PI / 2.0 - std::asin(cross_length) : 0.0;
00623         // reverse if the angle is on the other side
00624         if (vec_car_axis.dot(surface_normal) < 0.0)
00625         {
00626             roll = -roll;
00627         }
00628         
00629         // find change in pitch
00630         vec_car_axis = rotation_transform(btVector3(1.0, 0.0, 0.0));
00631         cross_length = vec_car_axis.cross(surface_normal).length();
00632         if (cross_length > 1.0) cross_length = 1.0;
00633         btScalar pitch = cross_length ? M_PI / 2.0 - std::asin(cross_length) : 0.0;
00634         if (vec_car_axis.dot(surface_normal) < 0.0)
00635         {
00636             pitch = -pitch;
00637         }
00638         btQuaternion rotation = rigid_body->getWorldTransform().getRotation();
00639         /* Smooth rotation so it is not so forceful before applying it.
00640          * This reduces problems with sharp bends and uneven surfaces, and 
00641          * makes it possible to fly off the crest of hills.
00642          */
00643         rotation *= btQuaternion(pitch / 4.0, -roll / 4.0, 0.0);
00644         rigid_body->getWorldTransform().setRotation(rotation);
00645         
00646         // turning left and right.
00647         btVector3 torque_vector = btVector3(0.0, 0.0,
00648             torque.getZ() * _car_stearing_scale[car_model] * _force_scale);
00649         local_torque = rotation_transform(torque_vector);
00650     } else {
00651         // allow reduced stearing in mid air.
00652         btVector3 torque_vector = btVector3(0.0, 0.0,
00653             torque.getZ() * _car_stearing_scale_midair[car_model] * _force_scale);
00654         local_torque = rotation_transform(torque_vector);
00655         // note that you carry on in roughly the same direction.
00656         // The sliding physics make it slightly more useful.
00657         if (floor_stick)
00658         {
00659             rigid_body->setGravity(btVector3(0.0, 0.0, -9.8));
00660             floor_stick = false;
00661             //DEBUG_MESSAGE("Car left floor.");
00662         }
00663     }
00664     // prevent sliding when not pressing slide key, and break when not accelerating.
00665     btVector3 unforce(-rotation_transform.inverse()(rigid_body->getLinearVelocity()) * 600.0);
00666     const btScalar sideways_velocity = -unforce.getX();
00667     const btScalar forward_velocity = -unforce.getY();
00669     unforce.setX(unforce.getX() * (1.0 - force.getX()/32767.0));
00670     unforce.setY(unforce.getY() * (1.0 - force.getY()/32767.0));
00671     // clamp to maximum achievable using other slide key or reverse, and don't change vertical motion.
00672     unforce.setMin(btVector3(32767, 32767, 0));
00673     unforce.setMax(btVector3(-32767, -16383, 0));
00674     
00675     // swap sideways velocity for forward velocity to help cornering
00676     // when sliding in the right direction, we want to reduce the effect though.
00677     btScalar slide_assist = sideways_velocity * force.getX();
00678     // If sliding the wrong way, don't reduce the effect.
00679     if (slide_assist < 0.0) slide_assist = 0.0;
00680     // If sliding the right way really fast, put a limit on the speed.
00681     if (slide_assist > 35.0) slide_assist = 35.0;
00682     unforce.setX((unforce.x() - sideways_velocity * (50.0 - slide_assist)) * _car_slide_scale[car_model]);
00683     
00684     if (sideways_velocity < 0)
00685     {
00686         unforce.setY(unforce.y() - sideways_velocity * 15.0);
00687     } else {
00688         unforce.setY(unforce.y() + sideways_velocity * 15.0);
00689     }
00690     /* additional break force when moving forwards or backwards while the
00691      * accelerate / reverse control is pushing the other way.
00692      */
00693     tick_energy_absorbed  = 0;
00694     if ((forward_velocity < 0.0 && force.y() > 0) ||
00695         (forward_velocity > 0.0 && force.y() < 0.0))
00696     {
00697         unforce.setY(unforce.y() + force.y() * 8.0);
00698         
00699         // Store energy lost while breaking.
00700         if (force.y() < 0)
00701         {
00702             tick_energy_absorbed = force.y() * forward_velocity * -0.05;
00703             elastic_potential_energy +=  tick_energy_absorbed;
00704         }
00705     }
00706     
00707     // additional force to use for accelerating varies with forward speed.
00708     // With a low speed the force is higher, providing greater acceleration.
00709     btScalar accel_multiplier = 5400000.0 / (forward_velocity * forward_velocity + 1800000.0);
00710     btScalar forward_accel_force = force.getY() * accel_multiplier;
00711     unforce.setY(unforce.getY() + forward_accel_force);
00712     
00713     // something similar for sliding sideways. Sharper, but half as strong.
00714     btScalar slide_accel_multiplier = 150000.0 / (sideways_velocity * sideways_velocity + 100000.0);
00715     // always use maximum if sliding the wrong way
00716     if (sideways_velocity * force.getX() < 0)
00717     {
00718         slide_accel_multiplier = 1.5;
00719     }
00720     btScalar slide_accel_force = force.getX() * slide_accel_multiplier;
00721     unforce.setX(unforce.getX() + slide_accel_force);
00722     
00723     // release stored energy while accelerating.
00724     used_energy_boost = 0.0;
00725     if (force.y() > 0)
00726     {
00727         btScalar transfer_force = force.y() * 3.0;
00728         used_energy_boost = transfer_force * forward_velocity * 0.01;
00729         if (used_energy_boost > elastic_potential_energy)
00730         {
00731             used_energy_boost = elastic_potential_energy;
00732             transfer_force = used_energy_boost / forward_velocity / 0.01;
00733         }
00734         unforce.setY(unforce.y() + transfer_force);
00735         elastic_potential_energy -= used_energy_boost;
00736     }
00737     
00738     // provide additional force when boosting.
00739     if (boost_timer < 100.0)
00740     {
00741         unforce.setY(unforce.getY() + float(force.getY()) * (float(100 - boost_timer) / 10.0));
00742     }
00743     
00744     if (floor_stick)
00745     {
00746         local_force = rotation_transform((force+unforce) * 0.012 * _force_scale);
00747     } else {
00748         local_force = rotation_transform((force * btVector3(2.0, 1.0, 1.0) + unforce) * 0.006 * _force_scale);
00749     }
00750     
00751     // Disqualify cars that have fallen off the course, or missed a section of
00752     // the track. If NearTrack cannot find a position within 15m of the track,
00753     // the car is disqualified.
00754     if (get_position().distance2(transform.getOrigin()) > 225.0)
00755     {
00756         remove();
00757     }
00758     
00759     update_sounds();
00760 }
00761 
00762 void Car::get_transform(btTransform & transform)
00763 {
00764     if (m_removed)
00765     {
00766         transform = m_last_transform;
00767         return;
00768     }
00769     transform = rigid_body->getCenterOfMassTransform();
00770 }
00771 
00772 signed int Car::get_lap()
00773 {
00774     return lap;
00775 }
00776 
00777 unsigned long int Car::get_last_lap_ticks()
00778 {
00779     return last_lap_ticks;
00780 }
00781 
00782 unsigned long int Car::get_best_lap_ticks()
00783 {
00784     return best_lap_ticks;
00785 }
00786 
00787 btScalar Car::get_speed()
00788 {
00789     if (m_removed) return 0.0;
00790     return rigid_body->getLinearVelocity().length();
00791 }
00792 
00793 btScalar Car::get_lap_distance() const
00794 {
00795     if (m_removed)
00796     {
00797         return 0;
00798     }
00799     const Track::MeshFaces::FaceGraph face = m_graph[get_face_descriptor()];
00800     return face_u_interpolation(face, get_position());
00801 }
00802 
00803 void Car::remove()
00804 {
00805     m_last_transform = rigid_body->getCenterOfMassTransform();
00806     m_removed = true;
00807     world.get_dynamics_world().removeRigidBody(rigid_body);
00808     world.remove_tick_observer(this);
00809     delete rigid_body;
00810     m_course_complete_time = world.get_tick_number();
00811     m_engine_sound_source->stop();
00812     m_slide_sound_source->stop();
00813     m_release_energy_sound_source->stop();
00814     m_drag_sound_source->stop();
00815     m_break_sound_source->stop();
00816 }
00817 
00818 bool Car::get_finished() const
00819 {
00820     return m_course_complete;
00821 }
00822 
00823 bool Car::get_disqualified() const
00824 {
00825     return m_removed && (!m_course_complete);
00826 }
00827 
00828 unsigned long int Car::get_finish_time() const
00829 {
00830     return m_course_complete_time;
00831 }
00832 
00833 int Car::get_facing_backwards() const
00834 {
00835     if (m_removed)
00836     {
00837         // not facing any direction if removed from the scene.
00838         return 0;
00839     } else {
00840         // current position.
00841         const Track::MeshFaces::FaceGraph & face = m_graph[get_face_descriptor()];
00842         btVector3 current_coords = get_position();
00843         btScalar current_lap_position = face_u_interpolation(face, current_coords);
00844         
00845         // find point 15 meters infront of the car's centre.
00846         // The large distance means small bits where the path is backwards
00847         // like the entrance to the longest option in a split is ignored.
00848         btTransform transform = rigid_body->getCenterOfMassTransform();
00849         btVector3 facing_coords = transform(btVector3(0, 15.0, 0));
00850         Track::NearTrack front(*this);        
00851         front.move_towards(facing_coords);
00852         facing_coords = front.get_position();
00853         if (   facing_coords.dot(plane_normal) + plane_distance > 0.0
00854             && current_coords.dot(plane_normal) + plane_distance <= 0.0
00855             && facing_coords.distance2(start_point) < 289.0)
00856         {
00857             // The nose is infont of the finish line, but centre isn't.
00858             // The car must be facing the right way, even though the lap
00859             // position of the nose is less than the lap position of the
00860             // centre.
00861             return 0;
00862         }
00863         const Track::MeshFaces::FaceGraph & face2 = m_graph[front.get_face_descriptor()];
00864         btScalar facing_lap_position = face_u_interpolation(face2, facing_coords);
00865         btScalar difference = (current_lap_position - facing_lap_position) * 4.0 * 255;
00866         if (difference <= 0)
00867         {
00868             return 0;
00869         }
00870         else  if (difference >= 255)
00871         {
00872             return 255;
00873         }
00874         else
00875         {
00876             return int(difference);
00877         }
00878     }
00879 }
00880 
00881 btScalar Car::get_camera_height() const
00882 {
00883     if (m_removed) return 0.3;
00884     // Use the car's NearTrack to get a point on the road infront of the car.
00885     // find point 12 meters infront of the car's centre.
00886     btTransform transform = rigid_body->getCenterOfMassTransform();
00887     btVector3 facing_coords = transform(btVector3(0, 12.0, 0));
00888     Track::NearTrack front(*this);        
00889     front.move_towards(facing_coords);
00890     facing_coords = front.get_position();
00891     // Now find the difference in height between this point and the car.
00892     facing_coords = transform.inverse()(facing_coords);
00893     btScalar height = facing_coords.getZ();
00894     // if the point is below, move the camera up to get a better view, if it
00895     // is above, move the camera down.
00896     btScalar cam_height = 0.35 - height * 0.15;
00897     if (cam_height >= 0) return cam_height;
00898     return 0;
00899 }
00900 
00901 void Car::get_sensors(float results[NUM_SENSORS]) const
00902 {
00903     if (m_removed) return;
00904     
00905     // Feeler sensors
00906     
00907     // These return the available distance and relative position along the lap in
00908     // several directions.
00909     btTransform transform = rigid_body->getCenterOfMassTransform();
00910     // transformation between a global vector and the vector relative to the
00911     // car's rotation (y forwards along the car).
00912     btTransform rotate = transform;
00913     rotate.setOrigin(btVector3(0, 0, 0));
00914     for (unsigned int i = 0; i < NUM_SENSORS_FEELERS; i++)
00915     {
00916         scan_direction(rotate(feeler_vectors[i]*64.0), &(results[i*NUM_FEELER_PROPERTIES]));
00917     }
00918 
00919     // Non feeler sensors.
00920     btTransform unrotate = rotate.inverse();
00921     btVector3 current_coords = get_position();
00922     // sensors for motion: linear velocity and angular velocity.
00923     // remove rotation of the car to make it meaningful.
00924     const unsigned int nonfeeler_start = NUM_SENSORS_FEELERS*NUM_FEELER_PROPERTIES;
00925     const btVector3 velocity = unrotate(rigid_body->getLinearVelocity());
00926     results[nonfeeler_start] = velocity.x();
00927     results[nonfeeler_start + 1] = velocity.y();
00928     results[nonfeeler_start + 2] = velocity.z();
00929     const btVector3 ang_vel = unrotate(rigid_body->getAngularVelocity());
00930     results[nonfeeler_start + 3] = ang_vel.x() / 10.0;
00931     results[nonfeeler_start + 4] = ang_vel.y() / 10.0;
00932     results[nonfeeler_start + 5] = ang_vel.z();
00933     // stored energy
00934     results[nonfeeler_start + 6] = elastic_potential_energy / float(2 << 27);
00935     assert(results[nonfeeler_start + 6] > -1);
00936     // difference between position on the road and car position.
00937     const btVector3 position_diff = unrotate(rigid_body->getCenterOfMassPosition() - current_coords);
00938     results[nonfeeler_start + 7] = position_diff.x();
00939     results[nonfeeler_start + 8] = position_diff.y();
00940     results[nonfeeler_start + 9] = position_diff.z();
00941     // boost strength- 1 just after boost, 0 when no effect (after a second).
00942     results[nonfeeler_start + 10] = (100.0 - boost_timer) * 0.01;
00943     if (results[nonfeeler_start + 10] < 0) results[nonfeeler_start + 10] = 0;
00944     if (results[nonfeeler_start + 10] > 1) results[nonfeeler_start + 10] = 1;
00945     // gravity in z direction - so it knows when it is flying.
00946     results[nonfeeler_start + 11] = rigid_body->getGravity().z() / 9.8;
00947     
00948     // work out a possible direction for steering from the longest feeler distance.
00949     // since distances are inverted, we find the lowest number.
00950     unsigned int best_direction = 0;
00951     btScalar min = std::numeric_limits<btScalar>::max();
00952     // ignore the backwards one.
00953     for (unsigned int i = 0; i < NUM_SENSORS_FEELERS-1; i++)
00954     {
00955         if (min > results[i * NUM_FEELER_PROPERTIES + Track::SENSOR_NAVIGABLE_DISTANCE])
00956         {
00957             min = results[i * NUM_FEELER_PROPERTIES + Track::SENSOR_NAVIGABLE_DISTANCE];
00958             best_direction = i;
00959         }
00960     }
00961     // which direction to steer this way?
00962     if (feeler_vectors[best_direction].x() > 0.0)
00963     {
00964         results[nonfeeler_start + 12] = 1;
00965     }
00966     else if (feeler_vectors[best_direction].x() < 0.0)
00967     {
00968         results[nonfeeler_start + 12] = -1;
00969     }
00970     else // go straight ahead.
00971     {
00972         results[nonfeeler_start + 12] = 0;
00973     }
00974     // if it points backwards, do the opposite.
00975     if (results[best_direction * NUM_FEELER_PROPERTIES + Track::SENSOR_LAP_DIFFERENTIAL] < 0)
00976     {
00977         results[nonfeeler_start + 12] *= -1;
00978     }
00979     
00980     // work out a possible steering direction from the gradient of lap
00981     // distance of all the feelers.
00982     // this moves the car towards the shortest path, but not necessarily
00983     // the fastest line to take.
00984     // find the most positive gradient's direction.
00985     btScalar max = std::numeric_limits<btScalar>::min();
00986     for (unsigned int i = 0; i < NUM_SENSORS_FEELERS; i++)
00987     {
00988         if (max < results[i * NUM_FEELER_PROPERTIES + Track::SENSOR_LAP_DIFFERENTIAL])
00989         {
00990             max = results[i * NUM_FEELER_PROPERTIES + Track::SENSOR_LAP_DIFFERENTIAL];
00991             best_direction = i;
00992         }
00993     }
00994     // which direction to steer this way?
00995     if (feeler_vectors[best_direction].x() > 0.0)
00996     {
00997         results[nonfeeler_start + 13] = 1;
00998     }
00999     else if (feeler_vectors[best_direction].x() < 0.0)
01000     {
01001         results[nonfeeler_start + 13] = -1;
01002     }
01003     else
01004     {
01005         if (best_direction == 7)
01006         {
01007             // driving the wrong way.
01008             if (results[1 * NUM_FEELER_PROPERTIES + 4] > results[2 * NUM_FEELER_PROPERTIES + Track::SENSOR_LAP_DIFFERENTIAL])
01009             {
01010                 // positive x better than negative x. Turn that way to face
01011                 // the right direction.
01012                 results[nonfeeler_start + 13] = 1;
01013             }
01014             else
01015             {
01016                 results[nonfeeler_start + 13] = -1;
01017             }
01018         } else {
01019             // keep going straight ahead.
01020             results[nonfeeler_start + 13] = 0;
01021         }
01022     }
01023     
01028 }
01029 
01030 void Car::update_sounds()
01031 {
01032     // The sounds are stopped when the car goes.
01033     if (m_removed) return;
01034     
01035     // set the sounds' properties
01036     
01037     m_engine_sound_source->set_position(rigid_body->getCenterOfMassTransform().getOrigin());
01038     m_engine_sound_source->set_velocity(rigid_body->getLinearVelocity());
01039     m_engine_sound_source->set_gain(std::abs(force.y()/603456.0) + 0.1);
01040     m_engine_sound_source->set_pitch(0.5 + 0.1 * (rigid_body->getLinearVelocity().length()));
01041     
01042     m_drag_sound_source->set_position(rigid_body->getCenterOfMassTransform().getOrigin());
01043     m_drag_sound_source->set_velocity(rigid_body->getLinearVelocity());
01044     m_drag_sound_source->set_gain(rigid_body->getLinearVelocity().length() / 4800.0);
01045     m_drag_sound_source->set_pitch(0.5 + 0.12 * (rigid_body->getLinearVelocity().length()));
01046     
01047     m_break_sound_source->set_position(rigid_body->getCenterOfMassTransform().getOrigin());
01048     m_break_sound_source->set_velocity(rigid_body->getLinearVelocity());
01049     m_break_sound_source->set_gain(tick_energy_absorbed / float(2 << 23));
01050     m_break_sound_source->set_pitch(elastic_potential_energy / float(2 << 27));
01051     
01052     m_boost_sound_source->set_position(rigid_body->getCenterOfMassTransform().getOrigin());
01053     m_boost_sound_source->set_velocity(rigid_body->getLinearVelocity());
01054     
01055     if (used_energy_boost > 0)
01056     {
01057         m_release_energy_sound_source->set_position(rigid_body->getCenterOfMassTransform().getOrigin());
01058         m_release_energy_sound_source->set_velocity(rigid_body->getLinearVelocity());
01059         m_release_energy_sound_source->set_gain(5.0 * used_energy_boost / float(2<<23));
01060         m_release_energy_sound_source->set_pitch(1.0 + used_energy_boost / float(2<<21));
01061     } else {
01062         m_release_energy_sound_source->set_gain(0);
01063     }
01064     
01065     m_slide_sound_source->set_position(rigid_body->getCenterOfMassTransform().getOrigin());
01066     m_slide_sound_source->set_velocity(rigid_body->getLinearVelocity());
01067     ALfloat slide_gain = std::abs(force.x())/303456.0;
01068     m_slide_sound_source->set_gain(slide_gain);
01069 }
01070 
01071 }
01072 
01073 }

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.