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 }