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