1 /*
2  * box2dworld.cpp
3  * Copyright (c) 2010-2011 Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
4  * Copyright (c) 2011 Joonas Erkinheimo <joonas.erkinheimo@nokia.com>
5  * Copyright (c) 2012 Adriano Rezende <atdrez@gmail.com>
6  *
7  * This file is part of the Box2D QML plugin.
8  *
9  * This software is provided 'as-is', without any express or implied warranty.
10  * In no event will the authors be held liable for any damages arising from
11  * the use of this software.
12  *
13  * Permission is granted to anyone to use this software for any purpose,
14  * including commercial applications, and to alter it and redistribute it
15  * freely, subject to the following restrictions:
16  *
17  * 1. The origin of this software must not be misrepresented; you must not
18  *    claim that you wrote the original software. If you use this software in
19  *    a product, an acknowledgment in the product documentation would be
20  *    appreciated but is not required.
21  *
22  * 2. Altered source versions must be plainly marked as such, and must not be
23  *    misrepresented as being the original software.
24  *
25  * 3. This notice may not be removed or altered from any source distribution.
26  */
27 
28 #include "box2dworld.h"
29 
30 #include "box2dbody.h"
31 #include "box2dcontact.h"
32 #include "box2dfixture.h"
33 #include "box2djoint.h"
34 #include "box2draycast.h"
35 
StepDriver(Box2DWorld * world)36 StepDriver::StepDriver(Box2DWorld *world)
37     : QAbstractAnimation(world)
38     , mWorld(world)
39 {
40     setLoopCount(-1); // loop forever
41 }
42 
duration() const43 int StepDriver::duration() const
44 {
45     return 1000;
46 }
47 
updateCurrentTime(int)48 void StepDriver::updateCurrentTime(int)
49 {
50     mWorld->step();
51 }
52 
53 
54 class ContactEvent
55 {
56 public:
57     enum Type {
58         BeginContact,
59         EndContact
60     };
61 
62     Type type;
63     Box2DFixture *fixtureA;
64     Box2DFixture *fixtureB;
65 };
66 
67 class ContactListener : public b2ContactListener
68 {
69 public:
70     explicit ContactListener(Box2DWorld *world);
71     void BeginContact(b2Contact *contact);
72     void EndContact(b2Contact *contact);
73     void PreSolve(b2Contact *contact, const b2Manifold *oldManifold);
74     void PostSolve(b2Contact *contact, const b2ContactImpulse *impulse);
75 
removeEvent(int index)76     void removeEvent(int index) { mEvents.removeAt(index); }
clearEvents()77     void clearEvents() { mEvents.clear(); }
events()78     const QList<ContactEvent> &events() { return mEvents; }
79 
80 private:
81     QList<ContactEvent> mEvents;
82     Box2DWorld *mWorld;
83     Box2DContact mContact;
84 };
85 
ContactListener(Box2DWorld * world)86 ContactListener::ContactListener(Box2DWorld *world) :
87     mWorld(world)
88 {
89 }
90 
BeginContact(b2Contact * contact)91 void ContactListener::BeginContact(b2Contact *contact)
92 {
93     ContactEvent event;
94     event.type = ContactEvent::BeginContact;
95     event.fixtureA = toBox2DFixture(contact->GetFixtureA());
96     event.fixtureB = toBox2DFixture(contact->GetFixtureB());
97     mEvents.append(event);
98 }
99 
EndContact(b2Contact * contact)100 void ContactListener::EndContact(b2Contact *contact)
101 {
102     ContactEvent event;
103     event.type = ContactEvent::EndContact;
104     event.fixtureA = toBox2DFixture(contact->GetFixtureA());
105     event.fixtureB = toBox2DFixture(contact->GetFixtureB());
106     mEvents.append(event);
107 }
108 
PreSolve(b2Contact * contact,const b2Manifold * oldManifold)109 void ContactListener::PreSolve(b2Contact *contact, const b2Manifold *oldManifold)
110 {
111     Q_UNUSED(oldManifold)
112     mContact.setContact(contact);
113     emit mWorld->preSolve(&mContact);
114 }
115 
PostSolve(b2Contact * contact,const b2ContactImpulse * impulse)116 void ContactListener::PostSolve(b2Contact *contact, const b2ContactImpulse *impulse)
117 {
118     Q_UNUSED(impulse)
119     mContact.setContact(contact);
120     emit mWorld->postSolve(&mContact);
121 }
122 
123 static Box2DWorld * mDefaultWorld;
124 
Box2DWorld(QObject * parent)125 Box2DWorld::Box2DWorld(QObject *parent) :
126     QObject(parent),
127     mWorld(b2Vec2(0.0f, -10.0f)),
128     mContactListener(0),
129     mTimeStep(1.0f / 60.0f),
130     mVelocityIterations(8),
131     mPositionIterations(3),
132     mComponentComplete(false),
133     mIsRunning(true),
134     mSynchronizing(false),
135     mStepDriver(new StepDriver(this)),
136     mProfile(new Box2DProfile(&mWorld, this)),
137     mEnableContactEvents(true),
138     mPixelsPerMeter(32.0f)
139 
140 {
141     mWorld.SetDestructionListener(this);
142     if (!mDefaultWorld)
143         mDefaultWorld = this;
144 }
145 
~Box2DWorld()146 Box2DWorld::~Box2DWorld()
147 {
148     // The bodies and joints will be deleted as part of the world, so it's
149     // important that they are no longer referenced from the Box2DBody and
150     // Box2DJoint instances.
151     for (b2Body *body = mWorld.GetBodyList(); body; body = body->GetNext())
152         toBox2DBody(body)->nullifyBody();
153     for (b2Joint *joint = mWorld.GetJointList(); joint; joint = joint->GetNext())
154         toBox2DJoint(joint)->nullifyJoint();
155     enableContactListener(false);
156     if (mDefaultWorld == this)
157         mDefaultWorld = 0;
158 }
159 
setTimeStep(float timeStep)160 void Box2DWorld::setTimeStep(float timeStep)
161 {
162     if (mTimeStep != timeStep) {
163         mTimeStep = timeStep;
164         emit timeStepChanged();
165     }
166 }
167 
setRunning(bool running)168 void Box2DWorld::setRunning(bool running)
169 {
170     if (mIsRunning == running)
171         return;
172 
173     mIsRunning = running;
174     emit runningChanged();
175 
176     if (mComponentComplete) {
177         if (running)
178             mStepDriver->start();
179         else
180             mStepDriver->stop();
181     }
182 }
183 
setVelocityIterations(int iterations)184 void Box2DWorld::setVelocityIterations(int iterations)
185 {
186     if (mVelocityIterations != iterations) {
187         mVelocityIterations = iterations;
188         emit velocityIterationsChanged();
189     }
190 }
191 
setPositionIterations(int iterations)192 void Box2DWorld::setPositionIterations(int iterations)
193 {
194     if (mPositionIterations != iterations) {
195         mPositionIterations = iterations;
196         emit positionIterationsChanged();
197     }
198 }
199 
gravity() const200 QPointF Box2DWorld::gravity() const
201 {
202     return invertY(mWorld.GetGravity());
203 }
204 
setGravity(const QPointF & gravity)205 void Box2DWorld::setGravity(const QPointF &gravity)
206 {
207     const b2Vec2 invertedGravity = invertY(gravity);
208     if (mWorld.GetGravity() == invertedGravity)
209         return;
210 
211     mWorld.SetGravity(invertedGravity);
212     emit gravityChanged();
213 }
214 
setAutoClearForces(bool autoClearForces)215 void Box2DWorld::setAutoClearForces(bool autoClearForces)
216 {
217     if (mWorld.GetAutoClearForces() == autoClearForces)
218         return;
219 
220     mWorld.SetAutoClearForces(autoClearForces);
221     emit autoClearForcesChanged();
222 }
223 
setEnableContactEvents(bool enableContactEvents)224 void Box2DWorld::setEnableContactEvents(bool enableContactEvents)
225 {
226     if(enableContactEvents == mEnableContactEvents)
227         return;
228     mEnableContactEvents = enableContactEvents;
229     enableContactListener(mEnableContactEvents);
230 
231     emit enableContactEventsChanged();
232 }
233 
enableContactListener(bool enable)234 void Box2DWorld::enableContactListener(bool enable)
235 {
236     if (enable) {
237         mContactListener = new ContactListener(this);
238         mWorld.SetContactListener(mContactListener);
239     } else {
240         mWorld.SetContactListener(0);
241         delete mContactListener;
242     }
243 }
244 
setPixelsPerMeter(float pixelsPerMeter)245 void Box2DWorld::setPixelsPerMeter(float pixelsPerMeter)
246 {
247     if (pixelsPerMeter <= 0.0f) {
248         qWarning("World: pixelsPerMeter must be > 0.0f");
249         return;
250     }
251 
252     if (mPixelsPerMeter != pixelsPerMeter) {
253         mPixelsPerMeter = pixelsPerMeter;
254         pixelsPerMeterChanged();
255     }
256 }
257 
classBegin()258 void Box2DWorld::classBegin()
259 {
260 }
261 
componentComplete()262 void Box2DWorld::componentComplete()
263 {
264     mComponentComplete = true;
265 
266     enableContactListener(mEnableContactEvents);
267 
268     if (mIsRunning)
269         mStepDriver->start();
270 }
271 
SayGoodbye(b2Joint * joint)272 void Box2DWorld::SayGoodbye(b2Joint *joint)
273 {
274     if (Box2DJoint *temp = toBox2DJoint(joint)) {
275         temp->nullifyJoint();
276         delete temp;
277     }
278 }
279 
SayGoodbye(b2Fixture * fixture)280 void Box2DWorld::SayGoodbye(b2Fixture *fixture)
281 {
282     if (mEnableContactEvents) {
283         Box2DFixture *f = toBox2DFixture(fixture);
284 
285         QList<ContactEvent> events = mContactListener->events();
286         for (int i = events.count() - 1; i >= 0; i--) {
287             if(events.at(i).fixtureA == f || events.at(i).fixtureB == f)
288                 mContactListener->removeEvent(i);
289         }
290     }
291 }
292 
step()293 void Box2DWorld::step()
294 {
295     // Update Box2D state before stepping
296     for (b2Body *body = mWorld.GetBodyList(); body; body = body->GetNext()) {
297         Box2DBody *b = toBox2DBody(body);
298         if (b->transformDirty() && b->isActive())
299             b->updateTransform();
300     }
301 
302     mWorld.Step(mTimeStep, mVelocityIterations, mPositionIterations);
303 
304     b2Timer timer;
305 
306     // Update QML state after stepping
307     mSynchronizing = true;
308     for (b2Body *body = mWorld.GetBodyList(); body; body = body->GetNext()) {
309         Box2DBody *b = toBox2DBody(body);
310         if (b->isActive() && b->bodyType() != Box2DBody::Static && b->target())
311             b->synchronize();
312     }
313     mSynchronizing = false;
314 
315     mProfile->mSynchronize = timer.GetMilliseconds();
316     timer.Reset();
317 
318     if (mEnableContactEvents) {
319         // Emit contact signals
320         foreach (const ContactEvent &event, mContactListener->events()) {
321             switch (event.type) {
322             case ContactEvent::BeginContact:
323                 emit event.fixtureA->beginContact(event.fixtureB);
324                 emit event.fixtureB->beginContact(event.fixtureA);
325                 break;
326             case ContactEvent::EndContact:
327                 emit event.fixtureA->endContact(event.fixtureB);
328                 emit event.fixtureB->endContact(event.fixtureA);
329                 break;
330             }
331         }
332         mContactListener->clearEvents();
333     }
334 
335     mProfile->mEmitSignals = timer.GetMilliseconds();
336 
337     emit stepped();
338 }
339 
rayCast(Box2DRayCast * rayCast,const QPointF & point1,const QPointF & point2)340 void Box2DWorld::rayCast(Box2DRayCast *rayCast,
341                          const QPointF &point1,
342                          const QPointF &point2)
343 {
344     mWorld.RayCast(rayCast, toMeters(point1), toMeters(point2));
345 }
346 
defaultWorld()347 Box2DWorld *Box2DWorld::defaultWorld()
348 {
349     return mDefaultWorld;
350 }
351