File indexing completed on 2024-04-28 11:21:41
0001 /* 0002 * box2dworld.cpp 0003 * Copyright (c) 2010-2011 Thorbjørn Lindeijer <thorbjorn@lindeijer.nl> 0004 * Copyright (c) 2011 Joonas Erkinheimo <joonas.erkinheimo@nokia.com> 0005 * Copyright (c) 2012 Adriano Rezende <atdrez@gmail.com> 0006 * 0007 * This file is part of the Box2D QML plugin. 0008 * 0009 * This software is provided 'as-is', without any express or implied warranty. 0010 * In no event will the authors be held liable for any damages arising from 0011 * the use of this software. 0012 * 0013 * Permission is granted to anyone to use this software for any purpose, 0014 * including commercial applications, and to alter it and redistribute it 0015 * freely, subject to the following restrictions: 0016 * 0017 * 1. The origin of this software must not be misrepresented; you must not 0018 * claim that you wrote the original software. If you use this software in 0019 * a product, an acknowledgment in the product documentation would be 0020 * appreciated but is not required. 0021 * 0022 * 2. Altered source versions must be plainly marked as such, and must not be 0023 * misrepresented as being the original software. 0024 * 0025 * 3. This notice may not be removed or altered from any source distribution. 0026 */ 0027 0028 #include "box2dworld.h" 0029 0030 #include "box2dbody.h" 0031 #include "box2dcontact.h" 0032 #include "box2dfixture.h" 0033 #include "box2djoint.h" 0034 #include "box2draycast.h" 0035 0036 StepDriver::StepDriver(Box2DWorld *world) 0037 : QAbstractAnimation(world) 0038 , mWorld(world) 0039 { 0040 setLoopCount(-1); // loop forever 0041 } 0042 0043 int StepDriver::duration() const 0044 { 0045 return 1000; 0046 } 0047 0048 void StepDriver::updateCurrentTime(int) 0049 { 0050 mWorld->step(); 0051 } 0052 0053 0054 class ContactEvent 0055 { 0056 public: 0057 enum Type { 0058 BeginContact, 0059 EndContact 0060 }; 0061 0062 Type type; 0063 Box2DFixture *fixtureA; 0064 Box2DFixture *fixtureB; 0065 }; 0066 0067 class ContactListener : public b2ContactListener 0068 { 0069 public: 0070 explicit ContactListener(Box2DWorld *world); 0071 void BeginContact(b2Contact *contact); 0072 void EndContact(b2Contact *contact); 0073 void PreSolve(b2Contact *contact, const b2Manifold *oldManifold); 0074 void PostSolve(b2Contact *contact, const b2ContactImpulse *impulse); 0075 0076 void removeEvent(int index) { mEvents.removeAt(index); } 0077 void clearEvents() { mEvents.clear(); } 0078 const QList<ContactEvent> &events() { return mEvents; } 0079 0080 private: 0081 QList<ContactEvent> mEvents; 0082 Box2DWorld *mWorld; 0083 Box2DContact mContact; 0084 }; 0085 0086 ContactListener::ContactListener(Box2DWorld *world) : 0087 mWorld(world) 0088 { 0089 } 0090 0091 void ContactListener::BeginContact(b2Contact *contact) 0092 { 0093 ContactEvent event; 0094 event.type = ContactEvent::BeginContact; 0095 event.fixtureA = toBox2DFixture(contact->GetFixtureA()); 0096 event.fixtureB = toBox2DFixture(contact->GetFixtureB()); 0097 mEvents.append(event); 0098 } 0099 0100 void ContactListener::EndContact(b2Contact *contact) 0101 { 0102 ContactEvent event; 0103 event.type = ContactEvent::EndContact; 0104 event.fixtureA = toBox2DFixture(contact->GetFixtureA()); 0105 event.fixtureB = toBox2DFixture(contact->GetFixtureB()); 0106 mEvents.append(event); 0107 } 0108 0109 void ContactListener::PreSolve(b2Contact *contact, const b2Manifold *oldManifold) 0110 { 0111 Q_UNUSED(oldManifold) 0112 mContact.setContact(contact); 0113 emit mWorld->preSolve(&mContact); 0114 } 0115 0116 void ContactListener::PostSolve(b2Contact *contact, const b2ContactImpulse *impulse) 0117 { 0118 Q_UNUSED(impulse) 0119 mContact.setContact(contact); 0120 emit mWorld->postSolve(&mContact); 0121 } 0122 0123 static Box2DWorld * mDefaultWorld; 0124 0125 Box2DWorld::Box2DWorld(QObject *parent) : 0126 QObject(parent), 0127 mWorld(b2Vec2(0.0f, -10.0f)), 0128 mContactListener(0), 0129 mTimeStep(1.0f / 60.0f), 0130 mVelocityIterations(8), 0131 mPositionIterations(3), 0132 mComponentComplete(false), 0133 mIsRunning(true), 0134 mSynchronizing(false), 0135 mStepDriver(new StepDriver(this)), 0136 mProfile(new Box2DProfile(&mWorld, this)), 0137 mEnableContactEvents(true), 0138 mPixelsPerMeter(32.0f) 0139 0140 { 0141 mWorld.SetDestructionListener(this); 0142 if (!mDefaultWorld) 0143 mDefaultWorld = this; 0144 } 0145 0146 Box2DWorld::~Box2DWorld() 0147 { 0148 // The bodies and joints will be deleted as part of the world, so it's 0149 // important that they are no longer referenced from the Box2DBody and 0150 // Box2DJoint instances. 0151 for (b2Body *body = mWorld.GetBodyList(); body; body = body->GetNext()) 0152 toBox2DBody(body)->nullifyBody(); 0153 for (b2Joint *joint = mWorld.GetJointList(); joint; joint = joint->GetNext()) 0154 toBox2DJoint(joint)->nullifyJoint(); 0155 enableContactListener(false); 0156 if (mDefaultWorld == this) 0157 mDefaultWorld = 0; 0158 } 0159 0160 void Box2DWorld::setTimeStep(float timeStep) 0161 { 0162 if (mTimeStep != timeStep) { 0163 mTimeStep = timeStep; 0164 emit timeStepChanged(); 0165 } 0166 } 0167 0168 void Box2DWorld::setRunning(bool running) 0169 { 0170 if (mIsRunning == running) 0171 return; 0172 0173 mIsRunning = running; 0174 emit runningChanged(); 0175 0176 if (mComponentComplete) { 0177 if (running) 0178 mStepDriver->start(); 0179 else 0180 mStepDriver->stop(); 0181 } 0182 } 0183 0184 void Box2DWorld::setVelocityIterations(int iterations) 0185 { 0186 if (mVelocityIterations != iterations) { 0187 mVelocityIterations = iterations; 0188 emit velocityIterationsChanged(); 0189 } 0190 } 0191 0192 void Box2DWorld::setPositionIterations(int iterations) 0193 { 0194 if (mPositionIterations != iterations) { 0195 mPositionIterations = iterations; 0196 emit positionIterationsChanged(); 0197 } 0198 } 0199 0200 QPointF Box2DWorld::gravity() const 0201 { 0202 return invertY(mWorld.GetGravity()); 0203 } 0204 0205 void Box2DWorld::setGravity(const QPointF &gravity) 0206 { 0207 const b2Vec2 invertedGravity = invertY(gravity); 0208 if (mWorld.GetGravity() == invertedGravity) 0209 return; 0210 0211 mWorld.SetGravity(invertedGravity); 0212 emit gravityChanged(); 0213 } 0214 0215 void Box2DWorld::setAutoClearForces(bool autoClearForces) 0216 { 0217 if (mWorld.GetAutoClearForces() == autoClearForces) 0218 return; 0219 0220 mWorld.SetAutoClearForces(autoClearForces); 0221 emit autoClearForcesChanged(); 0222 } 0223 0224 void Box2DWorld::setEnableContactEvents(bool enableContactEvents) 0225 { 0226 if(enableContactEvents == mEnableContactEvents) 0227 return; 0228 mEnableContactEvents = enableContactEvents; 0229 enableContactListener(mEnableContactEvents); 0230 0231 emit enableContactEventsChanged(); 0232 } 0233 0234 void Box2DWorld::enableContactListener(bool enable) 0235 { 0236 if (enable) { 0237 mContactListener = new ContactListener(this); 0238 mWorld.SetContactListener(mContactListener); 0239 } else { 0240 mWorld.SetContactListener(0); 0241 delete mContactListener; 0242 } 0243 } 0244 0245 void Box2DWorld::setPixelsPerMeter(float pixelsPerMeter) 0246 { 0247 if (pixelsPerMeter <= 0.0f) { 0248 qWarning("World: pixelsPerMeter must be > 0.0f"); 0249 return; 0250 } 0251 0252 if (mPixelsPerMeter != pixelsPerMeter) { 0253 mPixelsPerMeter = pixelsPerMeter; 0254 pixelsPerMeterChanged(); 0255 } 0256 } 0257 0258 void Box2DWorld::classBegin() 0259 { 0260 } 0261 0262 void Box2DWorld::componentComplete() 0263 { 0264 mComponentComplete = true; 0265 0266 enableContactListener(mEnableContactEvents); 0267 0268 if (mIsRunning) 0269 mStepDriver->start(); 0270 } 0271 0272 void Box2DWorld::SayGoodbye(b2Joint *joint) 0273 { 0274 if (Box2DJoint *temp = toBox2DJoint(joint)) { 0275 temp->nullifyJoint(); 0276 delete temp; 0277 } 0278 } 0279 0280 void Box2DWorld::SayGoodbye(b2Fixture *fixture) 0281 { 0282 if (mEnableContactEvents) { 0283 Box2DFixture *f = toBox2DFixture(fixture); 0284 0285 QList<ContactEvent> events = mContactListener->events(); 0286 for (int i = events.count() - 1; i >= 0; i--) { 0287 if(events.at(i).fixtureA == f || events.at(i).fixtureB == f) 0288 mContactListener->removeEvent(i); 0289 } 0290 } 0291 } 0292 0293 void Box2DWorld::step() 0294 { 0295 // Update Box2D state before stepping 0296 for (b2Body *body = mWorld.GetBodyList(); body; body = body->GetNext()) { 0297 Box2DBody *b = toBox2DBody(body); 0298 if (b->transformDirty() && b->isActive()) 0299 b->updateTransform(); 0300 } 0301 0302 mWorld.Step(mTimeStep, mVelocityIterations, mPositionIterations); 0303 0304 b2Timer timer; 0305 0306 // Update QML state after stepping 0307 mSynchronizing = true; 0308 for (b2Body *body = mWorld.GetBodyList(); body; body = body->GetNext()) { 0309 Box2DBody *b = toBox2DBody(body); 0310 if (b->isActive() && b->bodyType() != Box2DBody::Static && b->target()) 0311 b->synchronize(); 0312 } 0313 mSynchronizing = false; 0314 0315 mProfile->mSynchronize = timer.GetMilliseconds(); 0316 timer.Reset(); 0317 0318 if (mEnableContactEvents) { 0319 // Emit contact signals 0320 foreach (const ContactEvent &event, mContactListener->events()) { 0321 switch (event.type) { 0322 case ContactEvent::BeginContact: 0323 emit event.fixtureA->beginContact(event.fixtureB); 0324 emit event.fixtureB->beginContact(event.fixtureA); 0325 break; 0326 case ContactEvent::EndContact: 0327 emit event.fixtureA->endContact(event.fixtureB); 0328 emit event.fixtureB->endContact(event.fixtureA); 0329 break; 0330 } 0331 } 0332 } 0333 mContactListener->clearEvents(); 0334 0335 mProfile->mEmitSignals = timer.GetMilliseconds(); 0336 0337 emit stepped(); 0338 } 0339 0340 void Box2DWorld::rayCast(Box2DRayCast *rayCast, 0341 const QPointF &point1, 0342 const QPointF &point2) 0343 { 0344 mWorld.RayCast(rayCast, toMeters(point1), toMeters(point2)); 0345 } 0346 0347 Box2DWorld *Box2DWorld::defaultWorld() 0348 { 0349 return mDefaultWorld; 0350 }