Updated to Box2D 2.3.0
[qml-box2d:qml-box2d-folibis.git] / box2dbody.cpp
1 /*
2  * box2dbody.cpp
3  * Copyright (c) 2010-2011 Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
4  * Copyright (c) 2011 Daker Fernandes Pinheiro <daker.pinheiro@openbossa.org>
5  * Copyright (c) 2011 Tan Miaoqing <miaoqing.tan@nokia.com>
6  * Copyright (c) 2011 Antonio Aloisio <antonio.aloisio@nokia.com>
7  * Copyright (c) 2011 Alessandro Portale <alessandro.portale@nokia.com>
8  * Copyright (c) 2011 Joonas Erkinheimo <joonas.erkinheimo@nokia.com>
9  * Copyright (c) 2011 Antti Krats <antti.krats@digia.com>
10  *
11  * This file is part of the Box2D QML plugin.
12  *
13  * This software is provided 'as-is', without any express or implied warranty.
14  * In no event will the authors be held liable for any damages arising from
15  * the use of this software.
16  *
17  * Permission is granted to anyone to use this software for any purpose,
18  * including commercial applications, and to alter it and redistribute it
19  * freely, subject to the following restrictions:
20  *
21  * 1. The origin of this software must not be misrepresented; you must not
22  *    claim that you wrote the original software. If you use this software in
23  *    a product, an acknowledgment in the product documentation would be
24  *    appreciated but is not required.
25  *
26  * 2. Altered source versions must be plainly marked as such, and must not be
27  *    misrepresented as being the original software.
28  *
29  * 3. This notice may not be removed or altered from any source distribution.
30  */
31
32 #include "box2dbody.h"
33
34 #include "box2dfixture.h"
35 #include "box2dworld.h"
36
37 Box2DBody::Box2DBody(QQuickItem *parent) :
38     QQuickItem(parent),
39     mBody(0),
40     mWorld(0),
41     mLinearDamping(0.0f),
42     mAngularDamping(0.0f),
43     mBodyType(Dynamic),
44     mBullet(false),
45     mSleepingAllowed(true),
46     mFixedRotation(false),
47     mActive(true),
48     mSynchronizing(false),
49     mInitializePending(false)
50 {
51     setTransformOrigin(TopLeft);
52     connect(this, SIGNAL(rotationChanged()), SLOT(onRotationChanged()));
53 }
54
55 Box2DBody::~Box2DBody()
56 {
57     cleanup(mWorld);
58 }
59
60 void Box2DBody::setLinearDamping(qreal linearDamping)
61 {
62     if (mLinearDamping == linearDamping)
63         return;
64
65     mLinearDamping = linearDamping;
66     if (mBody)
67         mBody->SetLinearDamping(linearDamping);
68     emit linearDampingChanged();
69 }
70
71 void Box2DBody::setAngularDamping(qreal angularDamping)
72 {
73     if (mAngularDamping == angularDamping)
74         return;
75
76     mAngularDamping = angularDamping;
77     if (mBody)
78         mBody->SetAngularDamping(angularDamping);
79     emit angularDampingChanged();
80 }
81
82 void Box2DBody::setBodyType(BodyType bodyType)
83 {
84     if (mBodyType == bodyType)
85         return;
86
87     mBodyType = bodyType;
88     if (mBody)
89         mBody->SetType(static_cast<b2BodyType>(bodyType));
90     emit bodyTypeChanged();
91 }
92
93 void Box2DBody::setBullet(bool bullet)
94 {
95     if (mBullet == bullet)
96         return;
97
98     mBullet = bullet;
99     if (mBody)
100         mBody->SetBullet(bullet);
101     emit bulletChanged();
102 }
103
104 void Box2DBody::setSleepingAllowed(bool allowed)
105 {
106     if (mSleepingAllowed == allowed)
107         return;
108
109     mSleepingAllowed = allowed;
110     if (mBody)
111         mBody->SetSleepingAllowed(allowed);
112     emit sleepingAllowedChanged();
113 }
114
115 void Box2DBody::setFixedRotation(bool fixedRotation)
116 {
117     if (mFixedRotation == fixedRotation)
118         return;
119
120     mFixedRotation = fixedRotation;
121     if (mBody)
122         mBody->SetFixedRotation(fixedRotation);
123     emit fixedRotationChanged();
124 }
125
126 void Box2DBody::setActive(bool active)
127 {
128     if (mActive == active)
129         return;
130
131     mActive = active;
132     if (mBody)
133         mBody->SetActive(active);
134 }
135
136 void Box2DBody::setLinearVelocity(const QPointF &linearVelocity)
137 {
138     if (mLinearVelocity == linearVelocity)
139         return;
140
141     mLinearVelocity = linearVelocity;
142     if (mBody)
143         mBody->SetLinearVelocity(b2Vec2(mLinearVelocity.x() / scaleRatio,
144                                         -mLinearVelocity.y() / scaleRatio));
145     emit linearVelocityChanged();
146 }
147
148 QQmlListProperty<Box2DFixture> Box2DBody::fixtures()
149 {
150     return QQmlListProperty<Box2DFixture>(this, 0,
151                                                   &Box2DBody::append_fixture, 0, 0, 0);
152 }
153
154 void Box2DBody::append_fixture(QQmlListProperty<Box2DFixture> *list,
155                                Box2DFixture *fixture)
156 {
157     Box2DBody *body = static_cast<Box2DBody*>(list->object);
158     fixture->setParentItem(body);
159     body->mFixtures.append(fixture);
160 }
161
162 void Box2DBody::initialize(b2World *world)
163 {
164     mWorld = world;
165
166     if (!isComponentComplete()) {
167         // When components are created dynamically, they get their parent
168         // assigned before they have been completely initialized. In that case
169         // we need to delay initialization.
170         mInitializePending = true;
171         return;
172     }
173
174     b2BodyDef bodyDef;
175     bodyDef.type = static_cast<b2BodyType>(mBodyType);
176     bodyDef.position.Set(x() / scaleRatio, -y() / scaleRatio);
177     bodyDef.angle = -(rotation() * (2 * b2_pi)) / 360.0;
178     bodyDef.linearDamping = mLinearDamping;
179     bodyDef.angularDamping = mAngularDamping;
180     bodyDef.bullet = mBullet;
181     bodyDef.allowSleep = mSleepingAllowed;
182     bodyDef.fixedRotation = mFixedRotation;
183
184     mBody = world->CreateBody(&bodyDef);
185     mInitializePending = false;
186
187     foreach (Box2DFixture *fixture, mFixtures)
188         fixture->createFixture(mBody);
189
190     emit bodyCreated();
191 }
192
193 /**
194  * Synchronizes the state of this body with the internal Box2D state.
195  */
196 void Box2DBody::synchronize()
197 {
198     Q_ASSERT(mBody);
199     mSynchronizing = true;
200
201     const b2Vec2 position = mBody->GetPosition();
202     const float32 angle = mBody->GetAngle();
203
204     const qreal newX = position.x * scaleRatio;
205     const qreal newY = -position.y * scaleRatio;
206     const qreal newRotation = -(angle * 360.0) / (2 * b2_pi);
207
208     // Do fuzzy comparisions to avoid small inaccuracies causing repaints
209     if (!qFuzzyCompare(x(), newX))
210         setX(newX);
211     if (!qFuzzyCompare(y(), newY))
212         setY(newY);
213
214     if (!qFuzzyCompare(rotation(), newRotation))
215         setRotation(newRotation);
216
217     b2Vec2 linearVelocity = mBody->GetLinearVelocity();
218     setLinearVelocity(QPointF(linearVelocity.x * scaleRatio,
219                               -linearVelocity.y * scaleRatio));
220
221     mSynchronizing = false;
222 }
223
224 void Box2DBody::cleanup(b2World *world)
225 {
226     world->DestroyBody(mBody);
227     mBody = 0;
228     mWorld = 0;
229 }
230
231 void Box2DBody::componentComplete()
232 {
233     QQuickItem::componentComplete();
234
235     if (mInitializePending)
236         initialize(mWorld);
237 }
238
239 b2Body *Box2DBody::body() const
240 {
241     return mBody;
242 }
243
244 void Box2DBody::geometryChanged(const QRectF &newGeometry,
245                                 const QRectF &oldGeometry)
246 {
247     if (!mSynchronizing && mBody) {
248         if (newGeometry.topLeft() != oldGeometry.topLeft()) {
249             const QPointF pos = newGeometry.topLeft();
250             mBody->SetTransform(b2Vec2(pos.x() / scaleRatio,
251                                        -pos.y() / scaleRatio),
252                                 mBody->GetAngle());
253         }
254     }
255
256     QQuickItem::geometryChanged(newGeometry, oldGeometry);
257 }
258
259 void Box2DBody::onRotationChanged()
260 {
261     if (!mSynchronizing && mBody) {
262         mBody->SetTransform(mBody->GetPosition(),
263                             (rotation() * 2 * b2_pi) / -360.0);
264     }
265 }
266
267 void Box2DBody::applyLinearImpulse(const QPointF &impulse,
268                                    const QPointF &point)
269 {
270     if (mBody) {
271         mBody->ApplyLinearImpulse(b2Vec2(impulse.x() / scaleRatio,
272                                          -impulse.y() / scaleRatio),
273                                   b2Vec2(point.x() / scaleRatio,
274                                          -point.y() / scaleRatio),true);
275     }
276 }
277
278 void Box2DBody::applyTorque(qreal torque)
279 {
280     if (mBody)
281         mBody->ApplyTorque(torque,true);
282 }
283
284 QPointF Box2DBody::getWorldCenter() const
285 {
286     QPointF worldCenter;
287     if (mBody) {
288         const b2Vec2 &center = mBody->GetWorldCenter();
289         worldCenter.setX(center.x * scaleRatio);
290         worldCenter.setY(-center.y * scaleRatio);
291     }
292     return worldCenter;
293 }