File indexing completed on 2024-04-21 14:43:26

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 }