/* * Copyright (c) 2006-2007 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "b2Body.h" #include "b2Fixture.h" #include "b2World.h" #include "Contacts/b2Contact.h" #include "Joints/b2Joint.h" b2Body::b2Body(const b2BodyDef* bd, b2World* world) { b2Assert(bd->position.IsValid()); b2Assert(bd->linearVelocity.IsValid()); b2Assert(b2IsValid(bd->angle)); b2Assert(b2IsValid(bd->angularVelocity)); b2Assert(b2IsValid(bd->angularDamping) && bd->angularDamping >= 0.0f); b2Assert(b2IsValid(bd->linearDamping) && bd->linearDamping >= 0.0f); m_flags = 0; if (bd->bullet) { m_flags |= e_bulletFlag; } if (bd->fixedRotation) { m_flags |= e_fixedRotationFlag; } if (bd->allowSleep) { m_flags |= e_autoSleepFlag; } if (bd->awake) { m_flags |= e_awakeFlag; } if (bd->active) { m_flags |= e_activeFlag; } m_world = world; m_xf.p = bd->position; m_xf.q.Set(bd->angle); m_sweep.localCenter.SetZero(); m_sweep.c0 = m_xf.p; m_sweep.c = m_xf.p; m_sweep.a0 = bd->angle; m_sweep.a = bd->angle; m_sweep.alpha0 = 0.0f; m_jointList = NULL; m_contactList = NULL; m_prev = NULL; m_next = NULL; m_linearVelocity = bd->linearVelocity; m_angularVelocity = bd->angularVelocity; m_linearDamping = bd->linearDamping; m_angularDamping = bd->angularDamping; m_gravityScale = bd->gravityScale; m_force.SetZero(); m_torque = 0.0f; m_sleepTime = 0.0f; m_type = bd->type; if (m_type == b2_dynamicBody) { m_mass = 1.0f; m_invMass = 1.0f; } else { m_mass = 0.0f; m_invMass = 0.0f; } m_I = 0.0f; m_invI = 0.0f; m_userData = bd->userData; m_fixtureList = NULL; m_fixtureCount = 0; } b2Body::~b2Body() { // shapes and joints are destroyed in b2World::Destroy } void b2Body::SetType(b2BodyType type) { b2Assert(m_world->IsLocked() == false); if (m_world->IsLocked() == true) { return; } if (m_type == type) { return; } m_type = type; ResetMassData(); if (m_type == b2_staticBody) { m_linearVelocity.SetZero(); m_angularVelocity = 0.0f; m_sweep.a0 = m_sweep.a; m_sweep.c0 = m_sweep.c; SynchronizeFixtures(); } SetAwake(true); m_force.SetZero(); m_torque = 0.0f; // Since the body type changed, we need to flag contacts for filtering. for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { f->Refilter(); } } b2Fixture* b2Body::CreateFixture(const b2FixtureDef* def) { b2Assert(m_world->IsLocked() == false); if (m_world->IsLocked() == true) { return NULL; } b2BlockAllocator* allocator = &m_world->m_blockAllocator; void* memory = allocator->Allocate(sizeof(b2Fixture)); b2Fixture* fixture = new (memory) b2Fixture; fixture->Create(allocator, this, def); if (m_flags & e_activeFlag) { b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; fixture->CreateProxies(broadPhase, m_xf); } fixture->m_next = m_fixtureList; m_fixtureList = fixture; ++m_fixtureCount; fixture->m_body = this; // Adjust mass properties if needed. if (fixture->m_density > 0.0f) { ResetMassData(); } // Let the world know we have a new fixture. This will cause new contacts // to be created at the beginning of the next time step. m_world->m_flags |= b2World::e_newFixture; return fixture; } b2Fixture* b2Body::CreateFixture(const b2Shape* shape, float32 density) { b2FixtureDef def; def.shape = shape; def.density = density; return CreateFixture(&def); } void b2Body::DestroyFixture(b2Fixture* fixture) { b2Assert(m_world->IsLocked() == false); if (m_world->IsLocked() == true) { return; } b2Assert(fixture->m_body == this); // Remove the fixture from this body's singly linked list. b2Assert(m_fixtureCount > 0); b2Fixture** node = &m_fixtureList; bool found = false; while (*node != NULL) { if (*node == fixture) { *node = fixture->m_next; found = true; break; } node = &(*node)->m_next; } // You tried to remove a shape that is not attached to this body. b2Assert(found); // Destroy any contacts associated with the fixture. b2ContactEdge* edge = m_contactList; while (edge) { b2Contact* c = edge->contact; edge = edge->next; b2Fixture* fixtureA = c->GetFixtureA(); b2Fixture* fixtureB = c->GetFixtureB(); if (fixture == fixtureA || fixture == fixtureB) { // This destroys the contact and removes it from // this body's contact list. m_world->m_contactManager.Destroy(c); } } b2BlockAllocator* allocator = &m_world->m_blockAllocator; if (m_flags & e_activeFlag) { b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; fixture->DestroyProxies(broadPhase); } fixture->Destroy(allocator); fixture->m_body = NULL; fixture->m_next = NULL; fixture->~b2Fixture(); allocator->Free(fixture, sizeof(b2Fixture)); --m_fixtureCount; // Reset the mass data. ResetMassData(); } void b2Body::ResetMassData() { // Compute mass data from shapes. Each shape has its own density. m_mass = 0.0f; m_invMass = 0.0f; m_I = 0.0f; m_invI = 0.0f; m_sweep.localCenter.SetZero(); // Static and kinematic bodies have zero mass. if (m_type == b2_staticBody || m_type == b2_kinematicBody) { m_sweep.c0 = m_xf.p; m_sweep.c = m_xf.p; m_sweep.a0 = m_sweep.a; return; } b2Assert(m_type == b2_dynamicBody); // Accumulate mass over all fixtures. b2Vec2 localCenter = b2Vec2_zero; for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { if (f->m_density == 0.0f) { continue; } b2MassData massData; f->GetMassData(&massData); m_mass += massData.mass; localCenter += massData.mass * massData.center; m_I += massData.I; } // Compute center of mass. if (m_mass > 0.0f) { m_invMass = 1.0f / m_mass; localCenter *= m_invMass; } else { // Force all dynamic bodies to have a positive mass. m_mass = 1.0f; m_invMass = 1.0f; } if (m_I > 0.0f && (m_flags & e_fixedRotationFlag) == 0) { // Center the inertia about the center of mass. m_I -= m_mass * b2Dot(localCenter, localCenter); b2Assert(m_I > 0.0f); m_invI = 1.0f / m_I; } else { m_I = 0.0f; m_invI = 0.0f; } // Move center of mass. b2Vec2 oldCenter = m_sweep.c; m_sweep.localCenter = localCenter; m_sweep.c0 = m_sweep.c = b2Mul(m_xf, m_sweep.localCenter); // Update center of mass velocity. m_linearVelocity += b2Cross(m_angularVelocity, m_sweep.c - oldCenter); } void b2Body::SetMassData(const b2MassData* massData) { b2Assert(m_world->IsLocked() == false); if (m_world->IsLocked() == true) { return; } if (m_type != b2_dynamicBody) { return; } m_invMass = 0.0f; m_I = 0.0f; m_invI = 0.0f; m_mass = massData->mass; if (m_mass <= 0.0f) { m_mass = 1.0f; } m_invMass = 1.0f / m_mass; if (massData->I > 0.0f && (m_flags & b2Body::e_fixedRotationFlag) == 0) { m_I = massData->I - m_mass * b2Dot(massData->center, massData->center); b2Assert(m_I > 0.0f); m_invI = 1.0f / m_I; } // Move center of mass. b2Vec2 oldCenter = m_sweep.c; m_sweep.localCenter = massData->center; m_sweep.c0 = m_sweep.c = b2Mul(m_xf, m_sweep.localCenter); // Update center of mass velocity. m_linearVelocity += b2Cross(m_angularVelocity, m_sweep.c - oldCenter); } bool b2Body::ShouldCollide(const b2Body* other) const { // At least one body should be dynamic. if (m_type != b2_dynamicBody && other->m_type != b2_dynamicBody) { return false; } // Does a joint prevent collision? for (b2JointEdge* jn = m_jointList; jn; jn = jn->next) { if (jn->other == other) { if (jn->joint->m_collideConnected == false) { return false; } } } return true; } void b2Body::SetTransform(const b2Vec2& position, float32 angle) { b2Assert(m_world->IsLocked() == false); if (m_world->IsLocked() == true) { return; } m_xf.q.Set(angle); m_xf.p = position; m_sweep.c = b2Mul(m_xf, m_sweep.localCenter); m_sweep.a = angle; m_sweep.c0 = m_sweep.c; m_sweep.a0 = angle; b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { f->Synchronize(broadPhase, m_xf, m_xf); } m_world->m_contactManager.FindNewContacts(); } void b2Body::SynchronizeFixtures() { b2Transform xf1; xf1.q.Set(m_sweep.a0); xf1.p = m_sweep.c0 - b2Mul(xf1.q, m_sweep.localCenter); b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { f->Synchronize(broadPhase, xf1, m_xf); } } void b2Body::SetActive(bool flag) { b2Assert(m_world->IsLocked() == false); if (flag == IsActive()) { return; } if (flag) { m_flags |= e_activeFlag; // Create all proxies. b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { f->CreateProxies(broadPhase, m_xf); } // Contacts are created the next time step. } else { m_flags &= ~e_activeFlag; // Destroy all proxies. b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { f->DestroyProxies(broadPhase); } // Destroy the attached contacts. b2ContactEdge* ce = m_contactList; while (ce) { b2ContactEdge* ce0 = ce; ce = ce->next; m_world->m_contactManager.Destroy(ce0->contact); } m_contactList = NULL; } } void b2Body::Dump() { int32 bodyIndex = m_islandIndex; b2Log("{\n"); b2Log(" b2BodyDef bd;\n"); b2Log(" bd.type = b2BodyType(%d);\n", m_type); b2Log(" bd.position.Set(%.15lef, %.15lef);\n", m_xf.p.x, m_xf.p.y); b2Log(" bd.angle = %.15lef;\n", m_sweep.a); b2Log(" bd.linearVelocity.Set(%.15lef, %.15lef);\n", m_linearVelocity.x, m_linearVelocity.y); b2Log(" bd.angularVelocity = %.15lef;\n", m_angularVelocity); b2Log(" bd.linearDamping = %.15lef;\n", m_linearDamping); b2Log(" bd.angularDamping = %.15lef;\n", m_angularDamping); b2Log(" bd.allowSleep = bool(%d);\n", m_flags & e_autoSleepFlag); b2Log(" bd.awake = bool(%d);\n", m_flags & e_awakeFlag); b2Log(" bd.fixedRotation = bool(%d);\n", m_flags & e_fixedRotationFlag); b2Log(" bd.bullet = bool(%d);\n", m_flags & e_bulletFlag); b2Log(" bd.active = bool(%d);\n", m_flags & e_activeFlag); b2Log(" bd.gravityScale = %.15lef;\n", m_gravityScale); b2Log(" bodies[%d] = m_world->CreateBody(&bd);\n", m_islandIndex); b2Log("\n"); for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { b2Log(" {\n"); f->Dump(bodyIndex); b2Log(" }\n"); } b2Log("}\n"); }