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 00012 #include <fstream> 00013 00014 #include "World.h" 00015 #include <Debug.h> 00016 #include "../InputHandler.h" 00017 #include "../Audio.h" 00018 00019 namespace Engine 00020 { 00021 00022 namespace Physics 00023 { 00024 00025 const int milliseconds_per_tick = 12; 00026 const float seconds_per_tick = btScalar(milliseconds_per_tick) / btScalar(1000.0); 00027 /* Limit ticks so if physics takes longer to calculate than the time we are 00028 * simulating, we don't grind to a halt. The maximum amount of ticks should 00029 * be about enough to support 12 frames per second before slowing down the 00030 * simulation. 00031 */ 00032 const unsigned int max_tick_count = 1000 / 12 / milliseconds_per_tick + 1; 00033 00034 World::World(const Track::Track & track) 00035 : m_track(track) 00036 , milliseconds_remaining(milliseconds_per_tick) 00037 , tick_number(1) 00038 { 00039 // create world stuff 00040 00041 // maximum number of rigid bodies in the scene 00042 int maxProxies = 64; 00043 // maximum bounds of the scene for broadphase 00044 Track::AxisAlignedBoundingBox bounds = track.get_path().get_bounds(); 00045 bounds.add_border(20.0); 00046 broadphase = new btAxisSweep3(bounds.get_min(), bounds.get_max(), maxProxies); 00047 // full collision detection 00048 collisionConfiguration = new btDefaultCollisionConfiguration(); 00049 dispatcher = new btCollisionDispatcher(collisionConfiguration); 00050 // solver 00051 solver = new btSequentialImpulseConstraintSolver; 00052 // dynamics world 00053 dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration); 00054 dynamicsWorld->setGravity(btVector3(0,0,-0.98)); 00055 00056 /* Floor collision world, with only the surfaces from course_collision_world 00057 * you can drive on. Mostly usefult to Car, not us. 00058 * Use a different collision detection so only the floor is detected. 00059 */ 00060 floor_broadphase = new btAxisSweep3(bounds.get_min(), bounds.get_max(), maxProxies); 00061 floor_collision_configuration = new btDefaultCollisionConfiguration(); 00062 floor_dispatcher = new btCollisionDispatcher(collisionConfiguration); 00063 floor_world = new btCollisionWorld(floor_dispatcher, floor_broadphase, floor_collision_configuration); 00064 00065 /* Reserve some space for replay data, so we aren't constantly reallocating 00066 * a large array. 00067 */ 00068 replay_events.reserve(100 * 60 * 3 // 100 frames per second, for 3 minutes 00069 * 4 * 3); // 4 cars, 3 inputs each per frame. 00070 // 3 is the maximum number of inputs per frame, may be far less on average. 00071 00072 } 00073 00074 World::~World() 00075 { 00076 delete dynamicsWorld; 00077 delete solver; 00078 delete collisionConfiguration; 00079 delete dispatcher; 00080 delete broadphase; 00081 00082 delete floor_world; 00083 delete floor_broadphase; 00084 delete floor_collision_configuration; 00085 delete floor_dispatcher; 00086 } 00087 00088 void World::update(unsigned int milliseconds_ellapsed) 00089 { 00090 // milliseconds_remaining is the time in milliseconds that has passed but 00091 // hasn't been simulated yet. 00092 // Physics is done in fixed timesteps for determanism. 00093 // If there are left over milliseconds, we just leave them. 00094 milliseconds_remaining += milliseconds_ellapsed; 00095 while (milliseconds_remaining >= milliseconds_per_tick) 00096 { 00097 dynamicsWorld->stepSimulation(seconds_per_tick, 1, seconds_per_tick); 00098 00099 // collision sounds 00100 int numManifolds = dynamicsWorld->getDispatcher()->getNumManifolds(); 00101 for (int i=0;i<numManifolds;i++) 00102 { 00103 btPersistentManifold* contactManifold = dynamicsWorld->getDispatcher()->getManifoldByIndexInternal(i); 00104 btCollisionObject* obA = static_cast<btCollisionObject*>(contactManifold->getBody0()); 00105 btCollisionObject* obB = static_cast<btCollisionObject*>(contactManifold->getBody1()); 00106 00107 int numContacts = contactManifold->getNumContacts(); 00108 for (int j=0;j<numContacts;j++) 00109 { 00110 btManifoldPoint& pt = contactManifold->getContactPoint(j); 00111 if (pt.getDistance()<0.f) 00112 { 00113 const btVector3& ptA = pt.getPositionWorldOnA(); 00114 const btVector3& ptB = pt.getPositionWorldOnB(); 00115 const btVector3& normalOnB = pt.m_normalWorldOnB; 00116 static SoundBuffer sound_buffer("data/sound/collision.wav"); 00117 static SoundSource sound_source(sound_buffer); 00118 sound_source.set_position(ptA); 00119 sound_source.play(); 00120 } 00121 } 00122 } 00123 // check for input (some of the time so AI doesn't take so long). 00124 // input is additionaly checked by scenes once per rendered frame. 00125 if (!(tick_number % 4)) 00126 { 00127 Engine::InputHandler::get_instance().poll(); 00128 } 00129 // forces set in the tick callback don't seem to work, hence this 00130 // loop. Call pretick set the forces. 00131 for (std::set<TickObserver *>::iterator it = observers.begin(); 00132 it != observers.end(); 00133 it++) 00134 { 00135 (*it)->posttick(); 00136 (*it)->set_forces(); 00137 } 00138 tick_number++; 00139 milliseconds_remaining -= milliseconds_per_tick; 00140 } 00141 } 00142 00143 btDiscreteDynamicsWorld & World::get_dynamics_world() 00144 { 00145 return *dynamicsWorld; 00146 } 00147 00148 btCollisionWorld & World::get_floor_world() 00149 { 00150 return *floor_world; 00151 } 00152 00153 void World::add_tick_observer(TickObserver * tick_observer) 00154 { 00155 observers.insert(tick_observer); 00156 } 00157 00158 void World::remove_tick_observer(TickObserver * tick_observer) 00159 { 00160 observers.erase(tick_observer); 00161 } 00162 00163 void World::add_replay_report(const InputReport & report) 00164 { 00165 switch (report.get_report_type()) 00166 { 00167 case InputReport::RT_MENU_BACK: 00168 case InputReport::RT_MENU_DOWN: 00169 case InputReport::RT_MENU_LEFT: 00170 case InputReport::RT_MENU_RIGHT: 00171 case InputReport::RT_MENU_SELECT: 00172 case InputReport::RT_MENU_UP: 00173 case InputReport::RT_DISCONNECT: 00174 case InputReport::RT_BATTERY_LOW: 00175 // don't record menu inputs or anything else that doesn't affect physics. 00176 break; 00177 default: 00178 // record other inputs. 00179 replay_events.push_back(ReplayEvent(tick_number, report)); 00180 } 00181 } 00182 00183 World::ReplayEvent::ReplayEvent(unsigned long int tick_number, 00184 const InputReport & report) 00185 : tick_number(tick_number) 00186 , report(report) 00187 { 00188 } 00189 00190 00191 void World::write_replay_events(std::ostream & stream) 00192 { 00193 // record replay to file. 00195 00198 DEBUG_MESSAGE("Writing replay"); 00199 for (std::vector<ReplayEvent>::iterator it = replay_events.begin(); 00200 it != replay_events.end(); it++) 00201 { 00202 const ReplayEvent & event = *it; 00203 const InputReport & report = event.report; 00204 stream << event.tick_number << " " 00205 << (unsigned long int)&*(report.get_input_device()) << " " 00206 << report.get_report_type() << " " 00207 << report.get_value() << " "; 00208 } 00209 } 00210 00211 unsigned long int World::get_tick_number() 00212 { 00213 return tick_number; 00214 } 00215 00216 const Track::Track & World::get_track() 00217 { 00218 return m_track; 00219 } 00220 00221 } 00222 00223 }
Generated at Mon Sep 6 00:41:12 2010 by Doxygen version 1.4.7 for Racer version svn335.