File indexing completed on 2024-04-14 14:08:22

0001 /*
0002  * box2dbody.cpp
0003  * Copyright (c) 2010-2011 Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
0004  * Copyright (c) 2011 Daker Fernandes Pinheiro <daker.pinheiro@openbossa.org>
0005  * Copyright (c) 2011 Tan Miaoqing <miaoqing.tan@nokia.com>
0006  * Copyright (c) 2011 Antonio Aloisio <antonio.aloisio@nokia.com>
0007  * Copyright (c) 2011 Alessandro Portale <alessandro.portale@nokia.com>
0008  * Copyright (c) 2011 Joonas Erkinheimo <joonas.erkinheimo@nokia.com>
0009  * Copyright (c) 2011 Antti Krats <antti.krats@digia.com>
0010  *
0011  * This file is part of the Box2D QML plugin.
0012  *
0013  * This software is provided 'as-is', without any express or implied warranty.
0014  * In no event will the authors be held liable for any damages arising from
0015  * the use of this software.
0016  *
0017  * Permission is granted to anyone to use this software for any purpose,
0018  * including commercial applications, and to alter it and redistribute it
0019  * freely, subject to the following restrictions:
0020  *
0021  * 1. The origin of this software must not be misrepresented; you must not
0022  *    claim that you wrote the original software. If you use this software in
0023  *    a product, an acknowledgment in the product documentation would be
0024  *    appreciated but is not required.
0025  *
0026  * 2. Altered source versions must be plainly marked as such, and must not be
0027  *    misrepresented as being the original software.
0028  *
0029  * 3. This notice may not be removed or altered from any source distribution.
0030  */
0031 
0032 #include "box2dbody.h"
0033 
0034 #include "box2dfixture.h"
0035 #include "box2dworld.h"
0036 #include <qmath.h>
0037 
0038 static bool sync(float &value, float newValue)
0039 {
0040     if (qFuzzyCompare(value, newValue))
0041         return false;
0042 
0043     value = newValue;
0044     return true;
0045 }
0046 
0047 static bool sync(b2Vec2 &value, const b2Vec2 &newValue)
0048 {
0049     if (qFuzzyCompare(value.x, newValue.x) && qFuzzyCompare(value.y, newValue.y))
0050         return false;
0051 
0052     value = newValue;
0053     return true;
0054 }
0055 
0056 
0057 Box2DBody::Box2DBody(QObject *parent) :
0058     QObject(parent),
0059     mWorld(0),
0060     mTarget(0),
0061     mBody(0),
0062     mComponentComplete(false),
0063     mTransformDirty(false),
0064     mCreatePending(false)
0065 {
0066     mBodyDef.userData = this;
0067     setWorld(Box2DWorld::defaultWorld());
0068 }
0069 
0070 Box2DBody::~Box2DBody()
0071 {
0072     if (mBody)
0073         mWorld->world().DestroyBody(mBody);
0074 }
0075 
0076 void Box2DBody::setLinearDamping(float linearDamping)
0077 {
0078     if (mBodyDef.linearDamping == linearDamping)
0079         return;
0080 
0081     mBodyDef.linearDamping = linearDamping;
0082     if (mBody)
0083         mBody->SetLinearDamping(linearDamping);
0084 
0085     emit linearDampingChanged();
0086 }
0087 
0088 void Box2DBody::setAngularDamping(float angularDamping)
0089 {
0090     if (mBodyDef.angularDamping == angularDamping)
0091         return;
0092 
0093     mBodyDef.angularDamping = angularDamping;
0094     if (mBody)
0095         mBody->SetAngularDamping(angularDamping);
0096 
0097     emit angularDampingChanged();
0098 }
0099 
0100 void Box2DBody::setBodyType(BodyType bodyType)
0101 {
0102     if (mBodyDef.type == static_cast<b2BodyType>(bodyType))
0103         return;
0104 
0105     mBodyDef.type = static_cast<b2BodyType>(bodyType);
0106     if (mBody)
0107         mBody->SetType(mBodyDef.type);
0108 
0109     emit bodyTypeChanged();
0110 }
0111 
0112 void Box2DBody::setBullet(bool bullet)
0113 {
0114     if (mBodyDef.bullet == bullet)
0115         return;
0116 
0117     mBodyDef.bullet = bullet;
0118     if (mBody)
0119         mBody->SetBullet(bullet);
0120 
0121     emit bulletChanged();
0122 }
0123 
0124 void Box2DBody::setSleepingAllowed(bool sleepingAllowed)
0125 {
0126     if (mBodyDef.allowSleep == sleepingAllowed)
0127         return;
0128 
0129     mBodyDef.allowSleep = sleepingAllowed;
0130     if (mBody)
0131         mBody->SetSleepingAllowed(sleepingAllowed);
0132 
0133     emit sleepingAllowedChanged();
0134 }
0135 
0136 void Box2DBody::setFixedRotation(bool fixedRotation)
0137 {
0138     if (mBodyDef.fixedRotation == fixedRotation)
0139         return;
0140 
0141     mBodyDef.fixedRotation = fixedRotation;
0142     if (mBody)
0143         mBody->SetFixedRotation(fixedRotation);
0144 
0145     emit fixedRotationChanged();
0146 }
0147 
0148 void Box2DBody::setActive(bool active)
0149 {
0150     if (mBodyDef.active == active)
0151         return;
0152 
0153     mBodyDef.active = active;
0154     if (mBody)
0155         mBody->SetActive(active);
0156 }
0157 
0158 bool Box2DBody::isAwake() const
0159 {
0160     return mBody ? mBody->IsAwake() : mBodyDef.awake;
0161 }
0162 
0163 void Box2DBody::setAwake(bool awake)
0164 {
0165     mBodyDef.awake = awake;
0166     if (mBody)
0167         mBody->SetAwake(awake);
0168 }
0169 
0170 QPointF Box2DBody::linearVelocity() const
0171 {
0172     if (mBody)
0173         return invertY(mBody->GetLinearVelocity());
0174     return invertY(mBodyDef.linearVelocity);
0175 }
0176 
0177 void Box2DBody::setLinearVelocity(const QPointF &velocity)
0178 {
0179     if (linearVelocity() == velocity)
0180         return;
0181 
0182     mBodyDef.linearVelocity = invertY(velocity);
0183     if (mBody)
0184         mBody->SetLinearVelocity(mBodyDef.linearVelocity);
0185 
0186     emit linearVelocityChanged();
0187 }
0188 
0189 float Box2DBody::angularVelocity() const
0190 {
0191     if (mBody)
0192         return toDegrees(mBody->GetAngularVelocity());
0193     return toDegrees(mBodyDef.angularVelocity);
0194 }
0195 
0196 void Box2DBody::setAngularVelocity(float velocity)
0197 {
0198     if (angularVelocity() == velocity)
0199         return;
0200 
0201     mBodyDef.angularVelocity = toRadians(velocity);
0202     if (mBody)
0203         mBody->SetAngularVelocity(mBodyDef.angularVelocity);
0204 
0205     emit angularVelocityChanged();
0206 }
0207 
0208 void Box2DBody::setGravityScale(float gravityScale)
0209 {
0210     if (mBodyDef.gravityScale == gravityScale)
0211         return;
0212 
0213     mBodyDef.gravityScale = gravityScale;
0214     if (mBody)
0215         mBody->SetGravityScale(gravityScale);
0216 
0217     emit gravityScaleChanged();
0218 }
0219 
0220 QQmlListProperty<Box2DFixture> Box2DBody::fixtures()
0221 {
0222     return QQmlListProperty<Box2DFixture>(this, 0,
0223                                           &Box2DBody::append_fixture,
0224                                           &Box2DBody::count_fixture,
0225                                           &Box2DBody::at_fixture,
0226                                           0);
0227 }
0228 
0229 void Box2DBody::append_fixture(QQmlListProperty<Box2DFixture> *list,
0230                                Box2DFixture *fixture)
0231 {
0232     Box2DBody *body = static_cast<Box2DBody*>(list->object);
0233     body->mFixtures.append(fixture);
0234 }
0235 
0236 int Box2DBody::count_fixture(QQmlListProperty<Box2DFixture> *list)
0237 {
0238     Box2DBody *body = static_cast<Box2DBody*>(list->object);
0239     return body->mFixtures.length();
0240 }
0241 
0242 Box2DFixture *Box2DBody::at_fixture(QQmlListProperty<Box2DFixture> *list, int index)
0243 {
0244     Box2DBody *body = static_cast<Box2DBody*>(list->object);
0245     return body->mFixtures.at(index);
0246 }
0247 
0248 QPointF Box2DBody::originOffset() const
0249 {
0250     Q_ASSERT(mTarget);
0251 
0252     QPointF origin = -mTarget->transformOriginPoint();
0253     qreal c = qCos(-mBodyDef.angle);
0254     qreal s = qSin(-mBodyDef.angle);
0255 
0256     return QPointF(origin.x() * c - origin.y() * s - origin.x(),
0257                    origin.x() * s + origin.y() * c - origin.y());
0258 }
0259 
0260 void Box2DBody::addFixture(Box2DFixture *fixture)
0261 {
0262     mFixtures.append(fixture);
0263     if (mBody)
0264         fixture->initialize(this);
0265 }
0266 
0267 void Box2DBody::createBody()
0268 {
0269     if (!mWorld)
0270         return;
0271 
0272     if (!mComponentComplete) {
0273         // When components are created dynamically, they get their parent
0274         // assigned before they have been completely initialized. In that case
0275         // we need to delay initialization.
0276         mCreatePending = true;
0277         return;
0278     }
0279 
0280     if (!mTarget)
0281         mTarget = qobject_cast<QQuickItem *>(parent());
0282 
0283     if (mTarget) {
0284         mBodyDef.angle = toRadians(mTarget->rotation());
0285         mBodyDef.position = mWorld->toMeters(
0286                     mTarget->transformOrigin() == QQuickItem::TopLeft ?
0287                         mTarget->position() :
0288                         mTarget->position() + originOffset());
0289     }
0290 
0291     mBody = mWorld->world().CreateBody(&mBodyDef);
0292     mCreatePending = false;
0293     mTransformDirty = false;
0294     foreach (Box2DFixture *fixture, mFixtures)
0295         fixture->initialize(this);
0296     emit bodyCreated();
0297 }
0298 
0299 /**
0300  * Synchronizes the state of this body with the internal Box2D state.
0301  */
0302 void Box2DBody::synchronize()
0303 {
0304     Q_ASSERT(mBody);
0305 
0306     if (sync(mBodyDef.angle, mBody->GetAngle()))
0307         if (mTarget)
0308             mTarget->setRotation(toDegrees(mBodyDef.angle));
0309 
0310     if (sync(mBodyDef.position, mBody->GetPosition())) {
0311         if (mTarget) {
0312             mTarget->setPosition(
0313                         mTarget->transformOrigin() == QQuickItem::TopLeft ?
0314                             mWorld->toPixels(mBodyDef.position) :
0315                             mWorld->toPixels(mBodyDef.position) - originOffset());
0316 
0317         }
0318         emit positionChanged();
0319     }
0320 }
0321 
0322 void Box2DBody::classBegin()
0323 {
0324 }
0325 
0326 void Box2DBody::componentComplete()
0327 {
0328     mComponentComplete = true;
0329 
0330     if (mCreatePending)
0331         createBody();
0332 }
0333 
0334 void Box2DBody::setWorld(Box2DWorld *world)
0335 {
0336     if (mWorld == world)
0337         return;
0338 
0339     if (mWorld)
0340         disconnect(mWorld, SIGNAL(pixelsPerMeterChanged()), this, SLOT(onWorldPixelsPerMeterChanged()));
0341     if (world)
0342         connect(world, SIGNAL(pixelsPerMeterChanged()), this, SLOT(onWorldPixelsPerMeterChanged()));
0343 
0344     // Destroy body when leaving our previous world
0345     if (mWorld && mBody) {
0346         mWorld->world().DestroyBody(mBody);
0347         mBody = 0;
0348     }
0349 
0350     mWorld = world;
0351     createBody();
0352 }
0353 
0354 void Box2DBody::setTarget(QQuickItem *target)
0355 {
0356     if (mTarget == target)
0357         return;
0358 
0359     if (mTarget)
0360         mTarget->disconnect(this);
0361 
0362     mTarget = target;
0363     mTransformDirty = target != 0;
0364 
0365     if (target) {
0366         connect(target, SIGNAL(xChanged()), this, SLOT(markTransformDirty()));
0367         connect(target, SIGNAL(yChanged()), this, SLOT(markTransformDirty()));
0368         connect(target, SIGNAL(rotationChanged()), this, SLOT(markTransformDirty()));
0369     }
0370 
0371     emit targetChanged();
0372 }
0373 
0374 void Box2DBody::updateTransform()
0375 {
0376     Q_ASSERT(mTarget);
0377     Q_ASSERT(mBody);
0378     Q_ASSERT(mTransformDirty);
0379 
0380     mBodyDef.angle = toRadians(mTarget->rotation());
0381     mBodyDef.position = mWorld->toMeters(
0382                 mTarget->transformOrigin() == QQuickItem::TopLeft ?
0383                     mTarget->position() :
0384                     mTarget->position() + originOffset());
0385 
0386     mBody->SetTransform(mBodyDef.position, mBodyDef.angle);
0387     mTransformDirty = false;
0388 }
0389 
0390 void Box2DBody::applyLinearImpulse(const QPointF &impulse,
0391                                    const QPointF &point)
0392 {
0393     if (mBody)
0394         mBody->ApplyLinearImpulse(invertY(impulse), mWorld->toMeters(point), true);
0395 }
0396 
0397 void Box2DBody::applyAngularImpulse(qreal impulse)
0398 {
0399     if (mBody)
0400         mBody->ApplyAngularImpulse(impulse, true);
0401 }
0402 
0403 void Box2DBody::applyTorque(qreal torque)
0404 {
0405     if (mBody)
0406         mBody->ApplyTorque(torque, true);
0407 }
0408 
0409 QPointF Box2DBody::getWorldCenter() const
0410 {
0411     if (mBody)
0412         return mWorld->toPixels(mBody->GetWorldCenter());
0413     return QPointF();
0414 }
0415 
0416 QPointF Box2DBody::getLocalCenter() const
0417 {
0418     if (mBody)
0419         return mWorld->toPixels(mBody->GetLocalCenter());
0420     return QPointF();
0421 }
0422 
0423 void Box2DBody::applyForce(const QPointF &force, const QPointF &point)
0424 {
0425     if (mBody)
0426         mBody->ApplyForce(invertY(force), mWorld->toMeters(point), true);
0427 }
0428 
0429 void Box2DBody::applyForceToCenter(const QPointF &force)
0430 {
0431     if (mBody)
0432         mBody->ApplyForceToCenter(invertY(force), true);
0433 }
0434 
0435 float Box2DBody::getMass() const
0436 {
0437     return mBody ? mBody->GetMass() : 0.0;
0438 }
0439 
0440 void Box2DBody::resetMassData()
0441 {
0442     if (mBody)
0443         mBody->ResetMassData();
0444 }
0445 
0446 float Box2DBody::getInertia() const
0447 {
0448     return mBody ? mBody->GetInertia() : 0.0;
0449 }
0450 
0451 QPointF Box2DBody::toWorldPoint(const QPointF &localPoint) const
0452 {
0453     if (mBody)
0454         return mWorld->toPixels(mBody->GetWorldPoint(mWorld->toMeters(localPoint)));
0455     return QPointF();
0456 }
0457 
0458 QPointF Box2DBody::toWorldVector(const QPointF &localVector) const
0459 {
0460     if (mBody)
0461         return mWorld->toPixels(mBody->GetWorldVector(mWorld->toMeters(localVector)));
0462     return QPointF();
0463 }
0464 
0465 QPointF Box2DBody::toLocalPoint(const QPointF &worldPoint) const
0466 {
0467     if (mBody)
0468         return mWorld->toPixels(mBody->GetLocalPoint(mWorld->toMeters(worldPoint)));
0469     return QPointF();
0470 }
0471 
0472 QPointF Box2DBody::toLocalVector(const QPointF &worldVector) const
0473 {
0474     if (mBody)
0475         return mWorld->toPixels(mBody->GetLocalVector(mWorld->toMeters(worldVector)));
0476     return QPointF();
0477 }
0478 
0479 QPointF Box2DBody::getLinearVelocityFromWorldPoint(const QPointF &point) const
0480 {
0481     if (mBody)
0482         return invertY(mBody->GetLinearVelocityFromWorldPoint(mWorld->toMeters(point)));
0483     return QPointF();
0484 }
0485 
0486 QPointF Box2DBody::getLinearVelocityFromLocalPoint(const QPointF &point) const
0487 {
0488     if (mBody)
0489         return invertY(mBody->GetLinearVelocityFromLocalPoint(mWorld->toMeters(point)));
0490     return QPointF();
0491 }
0492 
0493 void Box2DBody::markTransformDirty()
0494 {
0495     mTransformDirty = mTransformDirty || (mWorld && !mWorld->isSynchronizing());
0496 }
0497 
0498 void Box2DBody::onWorldPixelsPerMeterChanged()
0499 {
0500     if (mBody) {
0501         foreach (Box2DFixture *fixture, mFixtures)
0502             fixture->recreateFixture();
0503         markTransformDirty();
0504         updateTransform();
0505     }
0506 }