| /* | |
| * 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 <Box2D/Dynamics/b2Body.h> | |
| #include <Box2D/Dynamics/b2Fixture.h> | |
| #include <Box2D/Dynamics/b2World.h> | |
| #include <Box2D/Dynamics/Contacts/b2Contact.h> | |
| #include <Box2D/Dynamics/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"); | |
| } |