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 }
Generated at Mon Sep 6 00:41:12 2010 by Doxygen version 1.4.7 for Racer version svn335.