Liquidfun links

This commit is contained in:
viktorljung
2015-09-09 18:04:17 +01:00
parent 8833377855
commit 67f7a30ca6
184 changed files with 52115 additions and 2 deletions
+8
View File
@@ -0,0 +1,8 @@
find_path(liquidfun_INCLUDE_DIR Box2D/Box2D.h)
find_library(liquidfun_LIBRARY NAMES liquidfun)
set(liquidfun_INCLUDE_DIRS ${liquidfun_INCLUDE_DIR})
set(liquidfun_LIBRARIES ${liquidfun_LIBRARY})
find_package_handle_standard_args(liquidfun DEFAULT_MSG liquidfun_LIBRARY liquidfun_INCLUDE_DIR)
mark_as_advanced(liquidfun_FOUND liquidfun_INCLUDE_DIR liquidfun_LIBRARY)
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
* Copyright (c) 2013 Google, Inc.
*
* 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.
*/
#ifndef BOX2D_H
#define BOX2D_H
/**
\mainpage LiquidFun API Documentation
*/
// These include files constitute the main Box2D API
#include <Box2D/Common/b2Settings.h>
#include <Box2D/Common/b2Draw.h>
#include <Box2D/Common/b2Stat.h>
#include <Box2D/Common/b2Timer.h>
#include <Box2D/Collision/Shapes/b2CircleShape.h>
#include <Box2D/Collision/Shapes/b2EdgeShape.h>
#include <Box2D/Collision/Shapes/b2ChainShape.h>
#include <Box2D/Collision/Shapes/b2PolygonShape.h>
#include <Box2D/Collision/b2BroadPhase.h>
#include <Box2D/Collision/b2Distance.h>
#include <Box2D/Collision/b2DynamicTree.h>
#include <Box2D/Collision/b2TimeOfImpact.h>
#include <Box2D/Dynamics/b2Body.h>
#include <Box2D/Dynamics/b2Fixture.h>
#include <Box2D/Dynamics/b2WorldCallbacks.h>
#include <Box2D/Dynamics/b2TimeStep.h>
#include <Box2D/Dynamics/b2World.h>
#include <Box2D/Dynamics/Contacts/b2Contact.h>
#include <Box2D/Dynamics/Joints/b2DistanceJoint.h>
#include <Box2D/Dynamics/Joints/b2FrictionJoint.h>
#include <Box2D/Dynamics/Joints/b2GearJoint.h>
#include <Box2D/Dynamics/Joints/b2MotorJoint.h>
#include <Box2D/Dynamics/Joints/b2MouseJoint.h>
#include <Box2D/Dynamics/Joints/b2PrismaticJoint.h>
#include <Box2D/Dynamics/Joints/b2PulleyJoint.h>
#include <Box2D/Dynamics/Joints/b2RevoluteJoint.h>
#include <Box2D/Dynamics/Joints/b2RopeJoint.h>
#include <Box2D/Dynamics/Joints/b2WeldJoint.h>
#include <Box2D/Dynamics/Joints/b2WheelJoint.h>
#include <Box2D/Particle/b2Particle.h>
#include <Box2D/Particle/b2ParticleGroup.h>
#endif
+203
View File
@@ -0,0 +1,203 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
* Copyright (c) 2013 Google, Inc.
*
* 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/Collision/Shapes/b2ChainShape.h>
#include <Box2D/Collision/Shapes/b2EdgeShape.h>
#include <new>
#include <memory.h>
#include <string.h>
b2ChainShape::~b2ChainShape()
{
b2Free(m_vertices);
m_vertices = NULL;
m_count = 0;
}
void b2ChainShape::CreateLoop(const b2Vec2* vertices, int32 count)
{
b2Assert(m_vertices == NULL && m_count == 0);
b2Assert(count >= 3);
for (int32 i = 1; i < count; ++i)
{
#if B2_ASSERT_ENABLED
b2Vec2 v1 = vertices[i-1];
b2Vec2 v2 = vertices[i];
// If the code crashes here, it means your vertices are too close together.
b2Assert(b2DistanceSquared(v1, v2) > b2_linearSlop * b2_linearSlop);
#endif // B2_ASSERT_ENABLED
}
m_count = count + 1;
m_vertices = (b2Vec2*)b2Alloc(m_count * sizeof(b2Vec2));
memcpy(m_vertices, vertices, count * sizeof(b2Vec2));
m_vertices[count] = m_vertices[0];
m_prevVertex = m_vertices[m_count - 2];
m_nextVertex = m_vertices[1];
m_hasPrevVertex = true;
m_hasNextVertex = true;
}
void b2ChainShape::CreateChain(const b2Vec2* vertices, int32 count)
{
b2Assert(m_vertices == NULL && m_count == 0);
b2Assert(count >= 2);
for (int32 i = 1; i < count; ++i)
{
#if B2_ASSERT_ENABLED
b2Vec2 v1 = vertices[i-1];
b2Vec2 v2 = vertices[i];
// If the code crashes here, it means your vertices are too close together.
b2Assert(b2DistanceSquared(v1, v2) > b2_linearSlop * b2_linearSlop);
#endif // B2_ASSERT_ENABLED
}
m_count = count;
m_vertices = (b2Vec2*)b2Alloc(count * sizeof(b2Vec2));
memcpy(m_vertices, vertices, m_count * sizeof(b2Vec2));
m_hasPrevVertex = false;
m_hasNextVertex = false;
m_prevVertex.SetZero();
m_nextVertex.SetZero();
}
void b2ChainShape::SetPrevVertex(const b2Vec2& prevVertex)
{
m_prevVertex = prevVertex;
m_hasPrevVertex = true;
}
void b2ChainShape::SetNextVertex(const b2Vec2& nextVertex)
{
m_nextVertex = nextVertex;
m_hasNextVertex = true;
}
b2Shape* b2ChainShape::Clone(b2BlockAllocator* allocator) const
{
void* mem = allocator->Allocate(sizeof(b2ChainShape));
b2ChainShape* clone = new (mem) b2ChainShape;
clone->CreateChain(m_vertices, m_count);
clone->m_prevVertex = m_prevVertex;
clone->m_nextVertex = m_nextVertex;
clone->m_hasPrevVertex = m_hasPrevVertex;
clone->m_hasNextVertex = m_hasNextVertex;
return clone;
}
int32 b2ChainShape::GetChildCount() const
{
// edge count = vertex count - 1
return m_count - 1;
}
void b2ChainShape::GetChildEdge(b2EdgeShape* edge, int32 index) const
{
b2Assert(0 <= index && index < m_count - 1);
edge->m_type = b2Shape::e_edge;
edge->m_radius = m_radius;
edge->m_vertex1 = m_vertices[index + 0];
edge->m_vertex2 = m_vertices[index + 1];
if (index > 0)
{
edge->m_vertex0 = m_vertices[index - 1];
edge->m_hasVertex0 = true;
}
else
{
edge->m_vertex0 = m_prevVertex;
edge->m_hasVertex0 = m_hasPrevVertex;
}
if (index < m_count - 2)
{
edge->m_vertex3 = m_vertices[index + 2];
edge->m_hasVertex3 = true;
}
else
{
edge->m_vertex3 = m_nextVertex;
edge->m_hasVertex3 = m_hasNextVertex;
}
}
void b2ChainShape::ComputeDistance(const b2Transform& xf, const b2Vec2& p, float32* distance, b2Vec2* normal, int32 childIndex) const
{
b2EdgeShape edge;
GetChildEdge(&edge, childIndex);
edge.ComputeDistance(xf, p, distance, normal, 0);
}
bool b2ChainShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const
{
B2_NOT_USED(xf);
B2_NOT_USED(p);
return false;
}
bool b2ChainShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
const b2Transform& xf, int32 childIndex) const
{
b2Assert(childIndex < m_count);
b2EdgeShape edgeShape;
int32 i1 = childIndex;
int32 i2 = childIndex + 1;
if (i2 == m_count)
{
i2 = 0;
}
edgeShape.m_vertex1 = m_vertices[i1];
edgeShape.m_vertex2 = m_vertices[i2];
return edgeShape.RayCast(output, input, xf, 0);
}
void b2ChainShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const
{
b2Assert(childIndex < m_count);
int32 i1 = childIndex;
int32 i2 = childIndex + 1;
if (i2 == m_count)
{
i2 = 0;
}
b2Vec2 v1 = b2Mul(xf, m_vertices[i1]);
b2Vec2 v2 = b2Mul(xf, m_vertices[i2]);
aabb->lowerBound = b2Min(v1, v2);
aabb->upperBound = b2Max(v1, v2);
}
void b2ChainShape::ComputeMass(b2MassData* massData, float32 density) const
{
B2_NOT_USED(density);
massData->mass = 0.0f;
massData->center.SetZero();
massData->I = 0.0f;
}
+106
View File
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
* Copyright (c) 2013 Google, Inc.
*
* 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.
*/
#ifndef B2_CHAIN_SHAPE_H
#define B2_CHAIN_SHAPE_H
#include <Box2D/Collision/Shapes/b2Shape.h>
class b2EdgeShape;
/// A chain shape is a free form sequence of line segments.
/// The chain has two-sided collision, so you can use inside and outside collision.
/// Therefore, you may use any winding order.
/// Since there may be many vertices, they are allocated using b2Alloc.
/// Connectivity information is used to create smooth collisions.
/// WARNING: The chain will not collide properly if there are self-intersections.
class b2ChainShape : public b2Shape
{
public:
b2ChainShape();
/// The destructor frees the vertices using b2Free.
~b2ChainShape();
/// Create a loop. This automatically adjusts connectivity.
/// @param vertices an array of vertices, these are copied
/// @param count the vertex count
void CreateLoop(const b2Vec2* vertices, int32 count);
/// Create a chain with isolated end vertices.
/// @param vertices an array of vertices, these are copied
/// @param count the vertex count
void CreateChain(const b2Vec2* vertices, int32 count);
/// Establish connectivity to a vertex that precedes the first vertex.
/// Don't call this for loops.
void SetPrevVertex(const b2Vec2& prevVertex);
/// Establish connectivity to a vertex that follows the last vertex.
/// Don't call this for loops.
void SetNextVertex(const b2Vec2& nextVertex);
/// Implement b2Shape. Vertices are cloned using b2Alloc.
b2Shape* Clone(b2BlockAllocator* allocator) const;
/// @see b2Shape::GetChildCount
int32 GetChildCount() const;
/// Get a child edge.
void GetChildEdge(b2EdgeShape* edge, int32 index) const;
/// This always return false.
/// @see b2Shape::TestPoint
bool TestPoint(const b2Transform& transform, const b2Vec2& p) const;
// @see b2Shape::ComputeDistance
void ComputeDistance(const b2Transform& xf, const b2Vec2& p, float32* distance, b2Vec2* normal, int32 childIndex) const;
/// Implement b2Shape.
bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
const b2Transform& transform, int32 childIndex) const;
/// @see b2Shape::ComputeAABB
void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const;
/// Chains have zero mass.
/// @see b2Shape::ComputeMass
void ComputeMass(b2MassData* massData, float32 density) const;
/// The vertices. Owned by this class.
b2Vec2* m_vertices;
/// The vertex count.
int32 m_count;
b2Vec2 m_prevVertex, m_nextVertex;
bool m_hasPrevVertex, m_hasNextVertex;
};
inline b2ChainShape::b2ChainShape()
{
m_type = e_chain;
m_radius = b2_polygonRadius;
m_vertices = NULL;
m_count = 0;
m_hasPrevVertex = false;
m_hasNextVertex = false;
}
#endif
+111
View File
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
* Copyright (c) 2013 Google, Inc.
*
* 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/Collision/Shapes/b2CircleShape.h>
#include <new>
b2Shape* b2CircleShape::Clone(b2BlockAllocator* allocator) const
{
void* mem = allocator->Allocate(sizeof(b2CircleShape));
b2CircleShape* clone = new (mem) b2CircleShape;
*clone = *this;
return clone;
}
int32 b2CircleShape::GetChildCount() const
{
return 1;
}
bool b2CircleShape::TestPoint(const b2Transform& transform, const b2Vec2& p) const
{
b2Vec2 center = transform.p + b2Mul(transform.q, m_p);
b2Vec2 d = p - center;
return b2Dot(d, d) <= m_radius * m_radius;
}
void b2CircleShape::ComputeDistance(const b2Transform& transform, const b2Vec2& p, float32* distance, b2Vec2* normal, int32 childIndex) const
{
B2_NOT_USED(childIndex);
b2Vec2 center = transform.p + b2Mul(transform.q, m_p);
b2Vec2 d = p - center;
float32 d1 = d.Length();
*distance = d1 - m_radius;
*normal = 1 / d1 * d;
}
// Collision Detection in Interactive 3D Environments by Gino van den Bergen
// From Section 3.1.2
// x = s + a * r
// norm(x) = radius
bool b2CircleShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
const b2Transform& transform, int32 childIndex) const
{
B2_NOT_USED(childIndex);
b2Vec2 position = transform.p + b2Mul(transform.q, m_p);
b2Vec2 s = input.p1 - position;
float32 b = b2Dot(s, s) - m_radius * m_radius;
// Solve quadratic equation.
b2Vec2 r = input.p2 - input.p1;
float32 c = b2Dot(s, r);
float32 rr = b2Dot(r, r);
float32 sigma = c * c - rr * b;
// Check for negative discriminant and short segment.
if (sigma < 0.0f || rr < b2_epsilon)
{
return false;
}
// Find the point of intersection of the line with the circle.
float32 a = -(c + b2Sqrt(sigma));
// Is the intersection point on the segment?
if (0.0f <= a && a <= input.maxFraction * rr)
{
a /= rr;
output->fraction = a;
output->normal = s + a * r;
output->normal.Normalize();
return true;
}
return false;
}
void b2CircleShape::ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const
{
B2_NOT_USED(childIndex);
b2Vec2 p = transform.p + b2Mul(transform.q, m_p);
aabb->lowerBound.Set(p.x - m_radius, p.y - m_radius);
aabb->upperBound.Set(p.x + m_radius, p.y + m_radius);
}
void b2CircleShape::ComputeMass(b2MassData* massData, float32 density) const
{
massData->mass = density * b2_pi * m_radius * m_radius;
massData->center = m_p;
// inertia about the local origin
massData->I = massData->mass * (0.5f * m_radius * m_radius + b2Dot(m_p, m_p));
}
+107
View File
@@ -0,0 +1,107 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
* Copyright (c) 2013 Google, Inc.
*
* 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.
*/
#ifndef B2_CIRCLE_SHAPE_H
#define B2_CIRCLE_SHAPE_H
#include <Box2D/Collision/Shapes/b2Shape.h>
/// A circle shape.
class b2CircleShape : public b2Shape
{
public:
b2CircleShape();
/// Implement b2Shape.
b2Shape* Clone(b2BlockAllocator* allocator) const;
/// @see b2Shape::GetChildCount
int32 GetChildCount() const;
/// Implement b2Shape.
bool TestPoint(const b2Transform& transform, const b2Vec2& p) const;
// @see b2Shape::ComputeDistance
void ComputeDistance(const b2Transform& xf, const b2Vec2& p, float32* distance, b2Vec2* normal, int32 childIndex) const;
/// Implement b2Shape.
bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
const b2Transform& transform, int32 childIndex) const;
/// @see b2Shape::ComputeAABB
void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const;
/// @see b2Shape::ComputeMass
void ComputeMass(b2MassData* massData, float32 density) const;
/// Get the supporting vertex index in the given direction.
int32 GetSupport(const b2Vec2& d) const;
/// Get the supporting vertex in the given direction.
const b2Vec2& GetSupportVertex(const b2Vec2& d) const;
/// Get the vertex count.
int32 GetVertexCount() const { return 1; }
/// Get a vertex by index. Used by b2Distance.
const b2Vec2& GetVertex(int32 index) const;
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
public:
/// Set position with direct floats.
void SetPosition(float32 x, float32 y) { m_p.Set(x, y); }
/// Get x-coordinate of position.
float32 GetPositionX() const { return m_p.x; }
/// Get y-coordinate of position.
float32 GetPositionY() const { return m_p.y; }
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
/// Position
b2Vec2 m_p;
};
inline b2CircleShape::b2CircleShape()
{
m_type = e_circle;
m_radius = 0.0f;
m_p.SetZero();
}
inline int32 b2CircleShape::GetSupport(const b2Vec2 &d) const
{
B2_NOT_USED(d);
return 0;
}
inline const b2Vec2& b2CircleShape::GetSupportVertex(const b2Vec2 &d) const
{
B2_NOT_USED(d);
return m_p;
}
inline const b2Vec2& b2CircleShape::GetVertex(int32 index) const
{
B2_NOT_USED(index);
b2Assert(index == 0);
return m_p;
}
#endif
+168
View File
@@ -0,0 +1,168 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
* Copyright (c) 2013 Google, Inc.
*
* 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/Collision/Shapes/b2EdgeShape.h>
#include <new>
void b2EdgeShape::Set(const b2Vec2& v1, const b2Vec2& v2)
{
m_vertex1 = v1;
m_vertex2 = v2;
m_hasVertex0 = false;
m_hasVertex3 = false;
}
b2Shape* b2EdgeShape::Clone(b2BlockAllocator* allocator) const
{
void* mem = allocator->Allocate(sizeof(b2EdgeShape));
b2EdgeShape* clone = new (mem) b2EdgeShape;
*clone = *this;
return clone;
}
int32 b2EdgeShape::GetChildCount() const
{
return 1;
}
bool b2EdgeShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const
{
B2_NOT_USED(xf);
B2_NOT_USED(p);
return false;
}
void b2EdgeShape::ComputeDistance(const b2Transform& xf, const b2Vec2& p, float32* distance, b2Vec2* normal, int32 childIndex) const
{
B2_NOT_USED(childIndex);
b2Vec2 v1 = b2Mul(xf, m_vertex1);
b2Vec2 v2 = b2Mul(xf, m_vertex2);
b2Vec2 d = p - v1;
b2Vec2 s = v2 - v1;
float32 ds = b2Dot(d, s);
if (ds > 0)
{
float32 s2 = b2Dot(s, s);
if (ds > s2)
{
d = p - v2;
}
else
{
d -= ds / s2 * s;
}
}
float32 d1 = d.Length();
*distance = d1;
*normal = d1 > 0 ? 1 / d1 * d : b2Vec2_zero;
}
// p = p1 + t * d
// v = v1 + s * e
// p1 + t * d = v1 + s * e
// s * e - t * d = p1 - v1
bool b2EdgeShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
const b2Transform& xf, int32 childIndex) const
{
B2_NOT_USED(childIndex);
// Put the ray into the edge's frame of reference.
b2Vec2 p1 = b2MulT(xf.q, input.p1 - xf.p);
b2Vec2 p2 = b2MulT(xf.q, input.p2 - xf.p);
b2Vec2 d = p2 - p1;
b2Vec2 v1 = m_vertex1;
b2Vec2 v2 = m_vertex2;
b2Vec2 e = v2 - v1;
b2Vec2 normal(e.y, -e.x);
normal.Normalize();
// q = p1 + t * d
// dot(normal, q - v1) = 0
// dot(normal, p1 - v1) + t * dot(normal, d) = 0
float32 numerator = b2Dot(normal, v1 - p1);
float32 denominator = b2Dot(normal, d);
if (denominator == 0.0f)
{
return false;
}
float32 t = numerator / denominator;
if (t < 0.0f || input.maxFraction < t)
{
return false;
}
b2Vec2 q = p1 + t * d;
// q = v1 + s * r
// s = dot(q - v1, r) / dot(r, r)
b2Vec2 r = v2 - v1;
float32 rr = b2Dot(r, r);
if (rr == 0.0f)
{
return false;
}
float32 s = b2Dot(q - v1, r) / rr;
if (s < 0.0f || 1.0f < s)
{
return false;
}
output->fraction = t;
if (numerator > 0.0f)
{
output->normal = -b2Mul(xf.q, normal);
}
else
{
output->normal = b2Mul(xf.q, normal);
}
return true;
}
void b2EdgeShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const
{
B2_NOT_USED(childIndex);
b2Vec2 v1 = b2Mul(xf, m_vertex1);
b2Vec2 v2 = b2Mul(xf, m_vertex2);
b2Vec2 lower = b2Min(v1, v2);
b2Vec2 upper = b2Max(v1, v2);
b2Vec2 r(m_radius, m_radius);
aabb->lowerBound = lower - r;
aabb->upperBound = upper + r;
}
void b2EdgeShape::ComputeMass(b2MassData* massData, float32 density) const
{
B2_NOT_USED(density);
massData->mass = 0.0f;
massData->center = 0.5f * (m_vertex1 + m_vertex2);
massData->I = 0.0f;
}
+94
View File
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
* Copyright (c) 2013 Google, Inc.
*
* 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.
*/
#ifndef B2_EDGE_SHAPE_H
#define B2_EDGE_SHAPE_H
#include <Box2D/Collision/Shapes/b2Shape.h>
/// A line segment (edge) shape. These can be connected in chains or loops
/// to other edge shapes. The connectivity information is used to ensure
/// correct contact normals.
class b2EdgeShape : public b2Shape
{
public:
b2EdgeShape();
/// Set this as an isolated edge.
void Set(const b2Vec2& v1, const b2Vec2& v2);
/// Implement b2Shape.
b2Shape* Clone(b2BlockAllocator* allocator) const;
/// @see b2Shape::GetChildCount
int32 GetChildCount() const;
/// @see b2Shape::TestPoint
bool TestPoint(const b2Transform& transform, const b2Vec2& p) const;
// @see b2Shape::ComputeDistance
void ComputeDistance(const b2Transform& xf, const b2Vec2& p, float32* distance, b2Vec2* normal, int32 childIndex) const;
/// Implement b2Shape.
bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
const b2Transform& transform, int32 childIndex) const;
/// @see b2Shape::ComputeAABB
void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const;
/// @see b2Shape::ComputeMass
void ComputeMass(b2MassData* massData, float32 density) const;
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
public:
/// Set this as an isolated edge, with direct floats.
void Set(float32 vx1, float32 vy1, float32 vx2, float32 vy2);
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
/// These are the edge vertices
b2Vec2 m_vertex1, m_vertex2;
/// Optional adjacent vertices. These are used for smooth collision.
b2Vec2 m_vertex0, m_vertex3;
bool m_hasVertex0, m_hasVertex3;
};
inline b2EdgeShape::b2EdgeShape()
{
m_type = e_edge;
m_radius = b2_polygonRadius;
m_vertex0.x = 0.0f;
m_vertex0.y = 0.0f;
m_vertex3.x = 0.0f;
m_vertex3.y = 0.0f;
m_hasVertex0 = false;
m_hasVertex3 = false;
}
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
inline void b2EdgeShape::Set(float32 vx1,
float32 vy1,
float32 vx2,
float32 vy2) {
Set(b2Vec2(vx1, vy1), b2Vec2(vx2, vy2));
}
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
#endif
+504
View File
@@ -0,0 +1,504 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
* Copyright (c) 2013 Google, Inc.
*
* 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/Collision/Shapes/b2PolygonShape.h>
#include <new>
b2Shape* b2PolygonShape::Clone(b2BlockAllocator* allocator) const
{
void* mem = allocator->Allocate(sizeof(b2PolygonShape));
b2PolygonShape* clone = new (mem) b2PolygonShape;
*clone = *this;
return clone;
}
void b2PolygonShape::SetAsBox(float32 hx, float32 hy)
{
m_count = 4;
m_vertices[0].Set(-hx, -hy);
m_vertices[1].Set( hx, -hy);
m_vertices[2].Set( hx, hy);
m_vertices[3].Set(-hx, hy);
m_normals[0].Set(0.0f, -1.0f);
m_normals[1].Set(1.0f, 0.0f);
m_normals[2].Set(0.0f, 1.0f);
m_normals[3].Set(-1.0f, 0.0f);
m_centroid.SetZero();
}
void b2PolygonShape::SetAsBox(float32 hx, float32 hy, const b2Vec2& center, float32 angle)
{
m_count = 4;
m_vertices[0].Set(-hx, -hy);
m_vertices[1].Set( hx, -hy);
m_vertices[2].Set( hx, hy);
m_vertices[3].Set(-hx, hy);
m_normals[0].Set(0.0f, -1.0f);
m_normals[1].Set(1.0f, 0.0f);
m_normals[2].Set(0.0f, 1.0f);
m_normals[3].Set(-1.0f, 0.0f);
m_centroid = center;
b2Transform xf;
xf.p = center;
xf.q.Set(angle);
// Transform vertices and normals.
for (int32 i = 0; i < m_count; ++i)
{
m_vertices[i] = b2Mul(xf, m_vertices[i]);
m_normals[i] = b2Mul(xf.q, m_normals[i]);
}
}
int32 b2PolygonShape::GetChildCount() const
{
return 1;
}
static b2Vec2 ComputeCentroid(const b2Vec2* vs, int32 count)
{
b2Assert(count >= 3);
b2Vec2 c; c.Set(0.0f, 0.0f);
float32 area = 0.0f;
// pRef is the reference point for forming triangles.
// It's location doesn't change the result (except for rounding error).
b2Vec2 pRef(0.0f, 0.0f);
#if 0
// This code would put the reference point inside the polygon.
for (int32 i = 0; i < count; ++i)
{
pRef += vs[i];
}
pRef *= 1.0f / count;
#endif
const float32 inv3 = 1.0f / 3.0f;
for (int32 i = 0; i < count; ++i)
{
// Triangle vertices.
b2Vec2 p1 = pRef;
b2Vec2 p2 = vs[i];
b2Vec2 p3 = i + 1 < count ? vs[i+1] : vs[0];
b2Vec2 e1 = p2 - p1;
b2Vec2 e2 = p3 - p1;
float32 D = b2Cross(e1, e2);
float32 triangleArea = 0.5f * D;
area += triangleArea;
// Area weighted centroid
c += triangleArea * inv3 * (p1 + p2 + p3);
}
// Centroid
b2Assert(area > b2_epsilon);
c *= 1.0f / area;
return c;
}
void b2PolygonShape::Set(const b2Vec2* vertices, int32 count)
{
b2Assert(3 <= count && count <= b2_maxPolygonVertices);
if (count < 3)
{
SetAsBox(1.0f, 1.0f);
return;
}
int32 n = b2Min(count, b2_maxPolygonVertices);
// Perform welding and copy vertices into local buffer.
b2Vec2 ps[b2_maxPolygonVertices];
int32 tempCount = 0;
for (int32 i = 0; i < n; ++i)
{
b2Vec2 v = vertices[i];
bool unique = true;
for (int32 j = 0; j < tempCount; ++j)
{
if (b2DistanceSquared(v, ps[j]) < 0.5f * b2_linearSlop)
{
unique = false;
break;
}
}
if (unique)
{
ps[tempCount++] = v;
}
}
n = tempCount;
if (n < 3)
{
// Polygon is degenerate.
b2Assert(false);
SetAsBox(1.0f, 1.0f);
return;
}
// Create the convex hull using the Gift wrapping algorithm
// http://en.wikipedia.org/wiki/Gift_wrapping_algorithm
// Find the right most point on the hull
int32 i0 = 0;
float32 x0 = ps[0].x;
for (int32 i = 1; i < n; ++i)
{
float32 x = ps[i].x;
if (x > x0 || (x == x0 && ps[i].y < ps[i0].y))
{
i0 = i;
x0 = x;
}
}
int32 hull[b2_maxPolygonVertices];
int32 m = 0;
int32 ih = i0;
for (;;)
{
hull[m] = ih;
int32 ie = 0;
for (int32 j = 1; j < n; ++j)
{
if (ie == ih)
{
ie = j;
continue;
}
b2Vec2 r = ps[ie] - ps[hull[m]];
b2Vec2 v = ps[j] - ps[hull[m]];
float32 c = b2Cross(r, v);
if (c < 0.0f)
{
ie = j;
}
// Collinearity check
if (c == 0.0f && v.LengthSquared() > r.LengthSquared())
{
ie = j;
}
}
++m;
ih = ie;
if (ie == i0)
{
break;
}
}
m_count = m;
// Copy vertices.
for (int32 i = 0; i < m; ++i)
{
m_vertices[i] = ps[hull[i]];
}
// Compute normals. Ensure the edges have non-zero length.
for (int32 i = 0; i < m; ++i)
{
int32 i1 = i;
int32 i2 = i + 1 < m ? i + 1 : 0;
b2Vec2 edge = m_vertices[i2] - m_vertices[i1];
b2Assert(edge.LengthSquared() > b2_epsilon * b2_epsilon);
m_normals[i] = b2Cross(edge, 1.0f);
m_normals[i].Normalize();
}
// Compute the polygon centroid.
m_centroid = ComputeCentroid(m_vertices, m);
}
bool b2PolygonShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const
{
b2Vec2 pLocal = b2MulT(xf.q, p - xf.p);
for (int32 i = 0; i < m_count; ++i)
{
float32 dot = b2Dot(m_normals[i], pLocal - m_vertices[i]);
if (dot > 0.0f)
{
return false;
}
}
return true;
}
void b2PolygonShape::ComputeDistance(const b2Transform& xf, const b2Vec2& p, float32* distance, b2Vec2* normal, int32 childIndex) const
{
B2_NOT_USED(childIndex);
b2Vec2 pLocal = b2MulT(xf.q, p - xf.p);
float32 maxDistance = -FLT_MAX;
b2Vec2 normalForMaxDistance = pLocal;
for (int32 i = 0; i < m_count; ++i)
{
float32 dot = b2Dot(m_normals[i], pLocal - m_vertices[i]);
if (dot > maxDistance)
{
maxDistance = dot;
normalForMaxDistance = m_normals[i];
}
}
if (maxDistance > 0)
{
b2Vec2 minDistance = normalForMaxDistance;
float32 minDistance2 = maxDistance * maxDistance;
for (int32 i = 0; i < m_count; ++i)
{
b2Vec2 distance = pLocal - m_vertices[i];
float32 distance2 = distance.LengthSquared();
if (minDistance2 > distance2)
{
minDistance = distance;
minDistance2 = distance2;
}
}
*distance = b2Sqrt(minDistance2);
*normal = b2Mul(xf.q, minDistance);
normal->Normalize();
}
else
{
*distance = maxDistance;
*normal = b2Mul(xf.q, normalForMaxDistance);
}
}
bool b2PolygonShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
const b2Transform& xf, int32 childIndex) const
{
B2_NOT_USED(childIndex);
// Put the ray into the polygon's frame of reference.
b2Vec2 p1 = b2MulT(xf.q, input.p1 - xf.p);
b2Vec2 p2 = b2MulT(xf.q, input.p2 - xf.p);
b2Vec2 d = p2 - p1;
float32 lower = 0.0f, upper = input.maxFraction;
int32 index = -1;
for (int32 i = 0; i < m_count; ++i)
{
// p = p1 + a * d
// dot(normal, p - v) = 0
// dot(normal, p1 - v) + a * dot(normal, d) = 0
float32 numerator = b2Dot(m_normals[i], m_vertices[i] - p1);
float32 denominator = b2Dot(m_normals[i], d);
if (denominator == 0.0f)
{
if (numerator < 0.0f)
{
return false;
}
}
else
{
// Note: we want this predicate without division:
// lower < numerator / denominator, where denominator < 0
// Since denominator < 0, we have to flip the inequality:
// lower < numerator / denominator <==> denominator * lower > numerator.
if (denominator < 0.0f && numerator < lower * denominator)
{
// Increase lower.
// The segment enters this half-space.
lower = numerator / denominator;
index = i;
}
else if (denominator > 0.0f && numerator < upper * denominator)
{
// Decrease upper.
// The segment exits this half-space.
upper = numerator / denominator;
}
}
// The use of epsilon here causes the assert on lower to trip
// in some cases. Apparently the use of epsilon was to make edge
// shapes work, but now those are handled separately.
//if (upper < lower - b2_epsilon)
if (upper < lower)
{
return false;
}
}
b2Assert(0.0f <= lower && lower <= input.maxFraction);
if (index >= 0)
{
output->fraction = lower;
output->normal = b2Mul(xf.q, m_normals[index]);
return true;
}
return false;
}
void b2PolygonShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const
{
B2_NOT_USED(childIndex);
b2Vec2 lower = b2Mul(xf, m_vertices[0]);
b2Vec2 upper = lower;
for (int32 i = 1; i < m_count; ++i)
{
b2Vec2 v = b2Mul(xf, m_vertices[i]);
lower = b2Min(lower, v);
upper = b2Max(upper, v);
}
b2Vec2 r(m_radius, m_radius);
aabb->lowerBound = lower - r;
aabb->upperBound = upper + r;
}
void b2PolygonShape::ComputeMass(b2MassData* massData, float32 density) const
{
// Polygon mass, centroid, and inertia.
// Let rho be the polygon density in mass per unit area.
// Then:
// mass = rho * int(dA)
// centroid.x = (1/mass) * rho * int(x * dA)
// centroid.y = (1/mass) * rho * int(y * dA)
// I = rho * int((x*x + y*y) * dA)
//
// We can compute these integrals by summing all the integrals
// for each triangle of the polygon. To evaluate the integral
// for a single triangle, we make a change of variables to
// the (u,v) coordinates of the triangle:
// x = x0 + e1x * u + e2x * v
// y = y0 + e1y * u + e2y * v
// where 0 <= u && 0 <= v && u + v <= 1.
//
// We integrate u from [0,1-v] and then v from [0,1].
// We also need to use the Jacobian of the transformation:
// D = cross(e1, e2)
//
// Simplification: triangle centroid = (1/3) * (p1 + p2 + p3)
//
// The rest of the derivation is handled by computer algebra.
b2Assert(m_count >= 3);
b2Vec2 center; center.Set(0.0f, 0.0f);
float32 area = 0.0f;
float32 I = 0.0f;
// s is the reference point for forming triangles.
// It's location doesn't change the result (except for rounding error).
b2Vec2 s(0.0f, 0.0f);
// This code would put the reference point inside the polygon.
for (int32 i = 0; i < m_count; ++i)
{
s += m_vertices[i];
}
s *= 1.0f / m_count;
const float32 k_inv3 = 1.0f / 3.0f;
for (int32 i = 0; i < m_count; ++i)
{
// Triangle vertices.
b2Vec2 e1 = m_vertices[i] - s;
b2Vec2 e2 = i + 1 < m_count ? m_vertices[i+1] - s : m_vertices[0] - s;
float32 D = b2Cross(e1, e2);
float32 triangleArea = 0.5f * D;
area += triangleArea;
// Area weighted centroid
center += triangleArea * k_inv3 * (e1 + e2);
float32 ex1 = e1.x, ey1 = e1.y;
float32 ex2 = e2.x, ey2 = e2.y;
float32 intx2 = ex1*ex1 + ex2*ex1 + ex2*ex2;
float32 inty2 = ey1*ey1 + ey2*ey1 + ey2*ey2;
I += (0.25f * k_inv3 * D) * (intx2 + inty2);
}
// Total mass
massData->mass = density * area;
// Center of mass
b2Assert(area > b2_epsilon);
center *= 1.0f / area;
massData->center = center + s;
// Inertia tensor relative to the local origin (point s).
massData->I = density * I;
// Shift to center of mass then to original body origin.
massData->I += massData->mass * (b2Dot(massData->center, massData->center) - b2Dot(center, center));
}
bool b2PolygonShape::Validate() const
{
for (int32 i = 0; i < m_count; ++i)
{
int32 i1 = i;
int32 i2 = i < m_count - 1 ? i1 + 1 : 0;
b2Vec2 p = m_vertices[i1];
b2Vec2 e = m_vertices[i2] - p;
for (int32 j = 0; j < m_count; ++j)
{
if (j == i1 || j == i2)
{
continue;
}
b2Vec2 v = m_vertices[j] - p;
float32 c = b2Cross(e, v);
if (c < 0.0f)
{
return false;
}
}
}
return true;
}
+134
View File
@@ -0,0 +1,134 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
* Copyright (c) 2013 Google, Inc.
*
* 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.
*/
#ifndef B2_POLYGON_SHAPE_H
#define B2_POLYGON_SHAPE_H
#include <Box2D/Collision/Shapes/b2Shape.h>
/// A convex polygon. It is assumed that the interior of the polygon is to
/// the left of each edge.
/// Polygons have a maximum number of vertices equal to b2_maxPolygonVertices.
/// In most cases you should not need many vertices for a convex polygon.
class b2PolygonShape : public b2Shape
{
public:
b2PolygonShape();
/// Implement b2Shape.
b2Shape* Clone(b2BlockAllocator* allocator) const;
/// @see b2Shape::GetChildCount
int32 GetChildCount() const;
/// Create a convex hull from the given array of local points.
/// The count must be in the range [3, b2_maxPolygonVertices].
/// @warning the points may be re-ordered, even if they form a convex polygon
/// @warning collinear points are handled but not removed. Collinear points
/// may lead to poor stacking behavior.
void Set(const b2Vec2* points, int32 count);
/// Build vertices to represent an axis-aligned box centered on the local origin.
/// @param hx the half-width.
/// @param hy the half-height.
void SetAsBox(float32 hx, float32 hy);
/// Build vertices to represent an oriented box.
/// @param hx the half-width.
/// @param hy the half-height.
/// @param center the center of the box in local coordinates.
/// @param angle the rotation of the box in local coordinates.
void SetAsBox(float32 hx, float32 hy, const b2Vec2& center, float32 angle);
/// @see b2Shape::TestPoint
bool TestPoint(const b2Transform& transform, const b2Vec2& p) const;
// @see b2Shape::ComputeDistance
void ComputeDistance(const b2Transform& xf, const b2Vec2& p, float32* distance, b2Vec2* normal, int32 childIndex) const;
/// Implement b2Shape.
bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
const b2Transform& transform, int32 childIndex) const;
/// @see b2Shape::ComputeAABB
void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const;
/// @see b2Shape::ComputeMass
void ComputeMass(b2MassData* massData, float32 density) const;
/// Get the vertex count.
int32 GetVertexCount() const { return m_count; }
/// Get a vertex by index.
const b2Vec2& GetVertex(int32 index) const;
/// Validate convexity. This is a very time consuming operation.
/// @returns true if valid
bool Validate() const;
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
public:
/// Set centroid with direct floats.
void SetCentroid(float32 x, float32 y);
/// SetAsBox with direct floats for center.
/// @see b2Shape::SetAsBox
void SetAsBox(float32 hx,
float32 hy,
float32 centerX,
float32 centerY,
float32 angle);
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
b2Vec2 m_centroid;
b2Vec2 m_vertices[b2_maxPolygonVertices];
b2Vec2 m_normals[b2_maxPolygonVertices];
int32 m_count;
};
inline b2PolygonShape::b2PolygonShape()
{
m_type = e_polygon;
m_radius = b2_polygonRadius;
m_count = 0;
m_centroid.SetZero();
}
inline const b2Vec2& b2PolygonShape::GetVertex(int32 index) const
{
b2Assert(0 <= index && index < m_count);
return m_vertices[index];
}
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
inline void b2PolygonShape::SetCentroid(float32 x, float32 y)
{
m_centroid.Set(x, y);
}
inline void b2PolygonShape::SetAsBox(float32 hx,
float32 hy,
float32 centerX,
float32 centerY,
float32 angle) {
SetAsBox(hx, hy, b2Vec2(centerX, centerY), angle);
}
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
#endif
+109
View File
@@ -0,0 +1,109 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
* Copyright (c) 2013 Google, Inc.
*
* 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.
*/
#ifndef B2_SHAPE_H
#define B2_SHAPE_H
#include <Box2D/Common/b2BlockAllocator.h>
#include <Box2D/Common/b2Math.h>
#include <Box2D/Collision/b2Collision.h>
/// This holds the mass data computed for a shape.
struct b2MassData
{
/// The mass of the shape, usually in kilograms.
float32 mass;
/// The position of the shape's centroid relative to the shape's origin.
b2Vec2 center;
/// The rotational inertia of the shape about the local origin.
float32 I;
};
/// A shape is used for collision detection. You can create a shape however you like.
/// Shapes used for simulation in b2World are created automatically when a b2Fixture
/// is created. Shapes may encapsulate a one or more child shapes.
class b2Shape
{
public:
enum Type
{
e_circle = 0,
e_edge = 1,
e_polygon = 2,
e_chain = 3,
e_typeCount = 4
};
virtual ~b2Shape() {}
/// Clone the concrete shape using the provided allocator.
virtual b2Shape* Clone(b2BlockAllocator* allocator) const = 0;
/// Get the type of this shape. You can use this to down cast to the concrete shape.
/// @return the shape type.
Type GetType() const;
/// Get the number of child primitives.
virtual int32 GetChildCount() const = 0;
/// Test a point for containment in this shape. This only works for convex shapes.
/// @param xf the shape world transform.
/// @param p a point in world coordinates.
virtual bool TestPoint(const b2Transform& xf, const b2Vec2& p) const = 0;
/// Compute the distance from the current shape to the specified point. This only works for convex shapes.
/// @param xf the shape world transform.
/// @param p a point in world coordinates.
/// @param distance returns the distance from the current shape.
/// @param normal returns the direction in which the distance increases.
virtual void ComputeDistance(const b2Transform& xf, const b2Vec2& p, float32* distance, b2Vec2* normal, int32 childIndex) const= 0;
/// Cast a ray against a child shape.
/// @param output the ray-cast results.
/// @param input the ray-cast input parameters.
/// @param transform the transform to be applied to the shape.
/// @param childIndex the child shape index
virtual bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
const b2Transform& transform, int32 childIndex) const = 0;
/// Given a transform, compute the associated axis aligned bounding box for a child shape.
/// @param aabb returns the axis aligned box.
/// @param xf the world transform of the shape.
/// @param childIndex the child shape
virtual void ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const = 0;
/// Compute the mass properties of this shape using its dimensions and density.
/// The inertia tensor is computed about the local origin.
/// @param massData returns the mass data for this shape.
/// @param density the density in kilograms per meter squared.
virtual void ComputeMass(b2MassData* massData, float32 density) const = 0;
Type m_type;
float32 m_radius;
};
inline b2Shape::Type b2Shape::GetType() const
{
return m_type;
}
#endif
+119
View File
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2006-2009 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/Collision/b2BroadPhase.h>
b2BroadPhase::b2BroadPhase()
{
m_proxyCount = 0;
m_pairCapacity = 16;
m_pairCount = 0;
m_pairBuffer = (b2Pair*)b2Alloc(m_pairCapacity * sizeof(b2Pair));
m_moveCapacity = 16;
m_moveCount = 0;
m_moveBuffer = (int32*)b2Alloc(m_moveCapacity * sizeof(int32));
}
b2BroadPhase::~b2BroadPhase()
{
b2Free(m_moveBuffer);
b2Free(m_pairBuffer);
}
int32 b2BroadPhase::CreateProxy(const b2AABB& aabb, void* userData)
{
int32 proxyId = m_tree.CreateProxy(aabb, userData);
++m_proxyCount;
BufferMove(proxyId);
return proxyId;
}
void b2BroadPhase::DestroyProxy(int32 proxyId)
{
UnBufferMove(proxyId);
--m_proxyCount;
m_tree.DestroyProxy(proxyId);
}
void b2BroadPhase::MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement)
{
bool buffer = m_tree.MoveProxy(proxyId, aabb, displacement);
if (buffer)
{
BufferMove(proxyId);
}
}
void b2BroadPhase::TouchProxy(int32 proxyId)
{
BufferMove(proxyId);
}
void b2BroadPhase::BufferMove(int32 proxyId)
{
if (m_moveCount == m_moveCapacity)
{
int32* oldBuffer = m_moveBuffer;
m_moveCapacity *= 2;
m_moveBuffer = (int32*)b2Alloc(m_moveCapacity * sizeof(int32));
memcpy(m_moveBuffer, oldBuffer, m_moveCount * sizeof(int32));
b2Free(oldBuffer);
}
m_moveBuffer[m_moveCount] = proxyId;
++m_moveCount;
}
void b2BroadPhase::UnBufferMove(int32 proxyId)
{
for (int32 i = 0; i < m_moveCount; ++i)
{
if (m_moveBuffer[i] == proxyId)
{
m_moveBuffer[i] = e_nullProxy;
}
}
}
// This is called from b2DynamicTree::Query when we are gathering pairs.
bool b2BroadPhase::QueryCallback(int32 proxyId)
{
// A proxy cannot form a pair with itself.
if (proxyId == m_queryProxyId)
{
return true;
}
// Grow the pair buffer as needed.
if (m_pairCount == m_pairCapacity)
{
b2Pair* oldBuffer = m_pairBuffer;
m_pairCapacity *= 2;
m_pairBuffer = (b2Pair*)b2Alloc(m_pairCapacity * sizeof(b2Pair));
memcpy(m_pairBuffer, oldBuffer, m_pairCount * sizeof(b2Pair));
b2Free(oldBuffer);
}
m_pairBuffer[m_pairCount].proxyIdA = b2Min(proxyId, m_queryProxyId);
m_pairBuffer[m_pairCount].proxyIdB = b2Max(proxyId, m_queryProxyId);
++m_pairCount;
return true;
}
+257
View File
@@ -0,0 +1,257 @@
/*
* Copyright (c) 2006-2009 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.
*/
#ifndef B2_BROAD_PHASE_H
#define B2_BROAD_PHASE_H
#include <Box2D/Common/b2Settings.h>
#include <Box2D/Collision/b2Collision.h>
#include <Box2D/Collision/b2DynamicTree.h>
#include <algorithm>
struct b2Pair
{
int32 proxyIdA;
int32 proxyIdB;
};
/// The broad-phase is used for computing pairs and performing volume queries and ray casts.
/// This broad-phase does not persist pairs. Instead, this reports potentially new pairs.
/// It is up to the client to consume the new pairs and to track subsequent overlap.
class b2BroadPhase
{
public:
enum
{
e_nullProxy = -1
};
b2BroadPhase();
~b2BroadPhase();
/// Create a proxy with an initial AABB. Pairs are not reported until
/// UpdatePairs is called.
int32 CreateProxy(const b2AABB& aabb, void* userData);
/// Destroy a proxy. It is up to the client to remove any pairs.
void DestroyProxy(int32 proxyId);
/// Call MoveProxy as many times as you like, then when you are done
/// call UpdatePairs to finalized the proxy pairs (for your time step).
void MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement);
/// Call to trigger a re-processing of it's pairs on the next call to UpdatePairs.
void TouchProxy(int32 proxyId);
/// Get the fat AABB for a proxy.
const b2AABB& GetFatAABB(int32 proxyId) const;
/// Get user data from a proxy. Returns NULL if the id is invalid.
void* GetUserData(int32 proxyId) const;
/// Test overlap of fat AABBs.
bool TestOverlap(int32 proxyIdA, int32 proxyIdB) const;
/// Get the number of proxies.
int32 GetProxyCount() const;
/// Update the pairs. This results in pair callbacks. This can only add pairs.
template <typename T>
void UpdatePairs(T* callback);
/// Query an AABB for overlapping proxies. The callback class
/// is called for each proxy that overlaps the supplied AABB.
template <typename T>
void Query(T* callback, const b2AABB& aabb) const;
/// Ray-cast against the proxies in the tree. This relies on the callback
/// to perform a exact ray-cast in the case were the proxy contains a shape.
/// The callback also performs the any collision filtering. This has performance
/// roughly equal to k * log(n), where k is the number of collisions and n is the
/// number of proxies in the tree.
/// @param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
/// @param callback a callback class that is called for each proxy that is hit by the ray.
template <typename T>
void RayCast(T* callback, const b2RayCastInput& input) const;
/// Get the height of the embedded tree.
int32 GetTreeHeight() const;
/// Get the balance of the embedded tree.
int32 GetTreeBalance() const;
/// Get the quality metric of the embedded tree.
float32 GetTreeQuality() const;
/// Shift the world origin. Useful for large worlds.
/// The shift formula is: position -= newOrigin
/// @param newOrigin the new origin with respect to the old origin
void ShiftOrigin(const b2Vec2& newOrigin);
private:
friend class b2DynamicTree;
void BufferMove(int32 proxyId);
void UnBufferMove(int32 proxyId);
bool QueryCallback(int32 proxyId);
b2DynamicTree m_tree;
int32 m_proxyCount;
int32* m_moveBuffer;
int32 m_moveCapacity;
int32 m_moveCount;
b2Pair* m_pairBuffer;
int32 m_pairCapacity;
int32 m_pairCount;
int32 m_queryProxyId;
};
/// This is used to sort pairs.
inline bool b2PairLessThan(const b2Pair& pair1, const b2Pair& pair2)
{
if (pair1.proxyIdA < pair2.proxyIdA)
{
return true;
}
if (pair1.proxyIdA == pair2.proxyIdA)
{
return pair1.proxyIdB < pair2.proxyIdB;
}
return false;
}
inline void* b2BroadPhase::GetUserData(int32 proxyId) const
{
return m_tree.GetUserData(proxyId);
}
inline bool b2BroadPhase::TestOverlap(int32 proxyIdA, int32 proxyIdB) const
{
const b2AABB& aabbA = m_tree.GetFatAABB(proxyIdA);
const b2AABB& aabbB = m_tree.GetFatAABB(proxyIdB);
return b2TestOverlap(aabbA, aabbB);
}
inline const b2AABB& b2BroadPhase::GetFatAABB(int32 proxyId) const
{
return m_tree.GetFatAABB(proxyId);
}
inline int32 b2BroadPhase::GetProxyCount() const
{
return m_proxyCount;
}
inline int32 b2BroadPhase::GetTreeHeight() const
{
return m_tree.GetHeight();
}
inline int32 b2BroadPhase::GetTreeBalance() const
{
return m_tree.GetMaxBalance();
}
inline float32 b2BroadPhase::GetTreeQuality() const
{
return m_tree.GetAreaRatio();
}
template <typename T>
void b2BroadPhase::UpdatePairs(T* callback)
{
// Reset pair buffer
m_pairCount = 0;
// Perform tree queries for all moving proxies.
for (int32 i = 0; i < m_moveCount; ++i)
{
m_queryProxyId = m_moveBuffer[i];
if (m_queryProxyId == e_nullProxy)
{
continue;
}
// We have to query the tree with the fat AABB so that
// we don't fail to create a pair that may touch later.
const b2AABB& fatAABB = m_tree.GetFatAABB(m_queryProxyId);
// Query tree, create pairs and add them pair buffer.
m_tree.Query(this, fatAABB);
}
// Reset move buffer
m_moveCount = 0;
// Sort the pair buffer to expose duplicates.
std::sort(m_pairBuffer, m_pairBuffer + m_pairCount, b2PairLessThan);
// Send the pairs back to the client.
int32 i = 0;
while (i < m_pairCount)
{
b2Pair* primaryPair = m_pairBuffer + i;
void* userDataA = m_tree.GetUserData(primaryPair->proxyIdA);
void* userDataB = m_tree.GetUserData(primaryPair->proxyIdB);
callback->AddPair(userDataA, userDataB);
++i;
// Skip any duplicate pairs.
while (i < m_pairCount)
{
b2Pair* pair = m_pairBuffer + i;
if (pair->proxyIdA != primaryPair->proxyIdA || pair->proxyIdB != primaryPair->proxyIdB)
{
break;
}
++i;
}
}
// Try to keep the tree balanced.
//m_tree.Rebalance(4);
}
template <typename T>
inline void b2BroadPhase::Query(T* callback, const b2AABB& aabb) const
{
m_tree.Query(callback, aabb);
}
template <typename T>
inline void b2BroadPhase::RayCast(T* callback, const b2RayCastInput& input) const
{
m_tree.RayCast(callback, input);
}
inline void b2BroadPhase::ShiftOrigin(const b2Vec2& newOrigin)
{
m_tree.ShiftOrigin(newOrigin);
}
#endif
+154
View File
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2007-2009 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/Collision/b2Collision.h>
#include <Box2D/Collision/Shapes/b2CircleShape.h>
#include <Box2D/Collision/Shapes/b2PolygonShape.h>
void b2CollideCircles(
b2Manifold* manifold,
const b2CircleShape* circleA, const b2Transform& xfA,
const b2CircleShape* circleB, const b2Transform& xfB)
{
manifold->pointCount = 0;
b2Vec2 pA = b2Mul(xfA, circleA->m_p);
b2Vec2 pB = b2Mul(xfB, circleB->m_p);
b2Vec2 d = pB - pA;
float32 distSqr = b2Dot(d, d);
float32 rA = circleA->m_radius, rB = circleB->m_radius;
float32 radius = rA + rB;
if (distSqr > radius * radius)
{
return;
}
manifold->type = b2Manifold::e_circles;
manifold->localPoint = circleA->m_p;
manifold->localNormal.SetZero();
manifold->pointCount = 1;
manifold->points[0].localPoint = circleB->m_p;
manifold->points[0].id.key = 0;
}
void b2CollidePolygonAndCircle(
b2Manifold* manifold,
const b2PolygonShape* polygonA, const b2Transform& xfA,
const b2CircleShape* circleB, const b2Transform& xfB)
{
manifold->pointCount = 0;
// Compute circle position in the frame of the polygon.
b2Vec2 c = b2Mul(xfB, circleB->m_p);
b2Vec2 cLocal = b2MulT(xfA, c);
// Find the min separating edge.
int32 normalIndex = 0;
float32 separation = -b2_maxFloat;
float32 radius = polygonA->m_radius + circleB->m_radius;
int32 vertexCount = polygonA->m_count;
const b2Vec2* vertices = polygonA->m_vertices;
const b2Vec2* normals = polygonA->m_normals;
for (int32 i = 0; i < vertexCount; ++i)
{
float32 s = b2Dot(normals[i], cLocal - vertices[i]);
if (s > radius)
{
// Early out.
return;
}
if (s > separation)
{
separation = s;
normalIndex = i;
}
}
// Vertices that subtend the incident face.
int32 vertIndex1 = normalIndex;
int32 vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0;
b2Vec2 v1 = vertices[vertIndex1];
b2Vec2 v2 = vertices[vertIndex2];
// If the center is inside the polygon ...
if (separation < b2_epsilon)
{
manifold->pointCount = 1;
manifold->type = b2Manifold::e_faceA;
manifold->localNormal = normals[normalIndex];
manifold->localPoint = 0.5f * (v1 + v2);
manifold->points[0].localPoint = circleB->m_p;
manifold->points[0].id.key = 0;
return;
}
// Compute barycentric coordinates
float32 u1 = b2Dot(cLocal - v1, v2 - v1);
float32 u2 = b2Dot(cLocal - v2, v1 - v2);
if (u1 <= 0.0f)
{
if (b2DistanceSquared(cLocal, v1) > radius * radius)
{
return;
}
manifold->pointCount = 1;
manifold->type = b2Manifold::e_faceA;
manifold->localNormal = cLocal - v1;
manifold->localNormal.Normalize();
manifold->localPoint = v1;
manifold->points[0].localPoint = circleB->m_p;
manifold->points[0].id.key = 0;
}
else if (u2 <= 0.0f)
{
if (b2DistanceSquared(cLocal, v2) > radius * radius)
{
return;
}
manifold->pointCount = 1;
manifold->type = b2Manifold::e_faceA;
manifold->localNormal = cLocal - v2;
manifold->localNormal.Normalize();
manifold->localPoint = v2;
manifold->points[0].localPoint = circleB->m_p;
manifold->points[0].id.key = 0;
}
else
{
b2Vec2 faceCenter = 0.5f * (v1 + v2);
float32 separation = b2Dot(cLocal - faceCenter, normals[vertIndex1]);
if (separation > radius)
{
return;
}
manifold->pointCount = 1;
manifold->type = b2Manifold::e_faceA;
manifold->localNormal = normals[vertIndex1];
manifold->localPoint = faceCenter;
manifold->points[0].localPoint = circleB->m_p;
manifold->points[0].id.key = 0;
}
}
+698
View File
@@ -0,0 +1,698 @@
/*
* Copyright (c) 2007-2009 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/Collision/b2Collision.h>
#include <Box2D/Collision/Shapes/b2CircleShape.h>
#include <Box2D/Collision/Shapes/b2EdgeShape.h>
#include <Box2D/Collision/Shapes/b2PolygonShape.h>
// Compute contact points for edge versus circle.
// This accounts for edge connectivity.
void b2CollideEdgeAndCircle(b2Manifold* manifold,
const b2EdgeShape* edgeA, const b2Transform& xfA,
const b2CircleShape* circleB, const b2Transform& xfB)
{
manifold->pointCount = 0;
// Compute circle in frame of edge
b2Vec2 Q = b2MulT(xfA, b2Mul(xfB, circleB->m_p));
b2Vec2 A = edgeA->m_vertex1, B = edgeA->m_vertex2;
b2Vec2 e = B - A;
// Barycentric coordinates
float32 u = b2Dot(e, B - Q);
float32 v = b2Dot(e, Q - A);
float32 radius = edgeA->m_radius + circleB->m_radius;
b2ContactFeature cf;
cf.indexB = 0;
cf.typeB = b2ContactFeature::e_vertex;
// Region A
if (v <= 0.0f)
{
b2Vec2 P = A;
b2Vec2 d = Q - P;
float32 dd = b2Dot(d, d);
if (dd > radius * radius)
{
return;
}
// Is there an edge connected to A?
if (edgeA->m_hasVertex0)
{
b2Vec2 A1 = edgeA->m_vertex0;
b2Vec2 B1 = A;
b2Vec2 e1 = B1 - A1;
float32 u1 = b2Dot(e1, B1 - Q);
// Is the circle in Region AB of the previous edge?
if (u1 > 0.0f)
{
return;
}
}
cf.indexA = 0;
cf.typeA = b2ContactFeature::e_vertex;
manifold->pointCount = 1;
manifold->type = b2Manifold::e_circles;
manifold->localNormal.SetZero();
manifold->localPoint = P;
manifold->points[0].id.key = 0;
manifold->points[0].id.cf = cf;
manifold->points[0].localPoint = circleB->m_p;
return;
}
// Region B
if (u <= 0.0f)
{
b2Vec2 P = B;
b2Vec2 d = Q - P;
float32 dd = b2Dot(d, d);
if (dd > radius * radius)
{
return;
}
// Is there an edge connected to B?
if (edgeA->m_hasVertex3)
{
b2Vec2 B2 = edgeA->m_vertex3;
b2Vec2 A2 = B;
b2Vec2 e2 = B2 - A2;
float32 v2 = b2Dot(e2, Q - A2);
// Is the circle in Region AB of the next edge?
if (v2 > 0.0f)
{
return;
}
}
cf.indexA = 1;
cf.typeA = b2ContactFeature::e_vertex;
manifold->pointCount = 1;
manifold->type = b2Manifold::e_circles;
manifold->localNormal.SetZero();
manifold->localPoint = P;
manifold->points[0].id.key = 0;
manifold->points[0].id.cf = cf;
manifold->points[0].localPoint = circleB->m_p;
return;
}
// Region AB
float32 den = b2Dot(e, e);
b2Assert(den > 0.0f);
b2Vec2 P = (1.0f / den) * (u * A + v * B);
b2Vec2 d = Q - P;
float32 dd = b2Dot(d, d);
if (dd > radius * radius)
{
return;
}
b2Vec2 n(-e.y, e.x);
if (b2Dot(n, Q - A) < 0.0f)
{
n.Set(-n.x, -n.y);
}
n.Normalize();
cf.indexA = 0;
cf.typeA = b2ContactFeature::e_face;
manifold->pointCount = 1;
manifold->type = b2Manifold::e_faceA;
manifold->localNormal = n;
manifold->localPoint = A;
manifold->points[0].id.key = 0;
manifold->points[0].id.cf = cf;
manifold->points[0].localPoint = circleB->m_p;
}
// This structure is used to keep track of the best separating axis.
struct b2EPAxis
{
enum Type
{
e_unknown,
e_edgeA,
e_edgeB
};
Type type;
int32 index;
float32 separation;
};
// This holds polygon B expressed in frame A.
struct b2TempPolygon
{
b2Vec2 vertices[b2_maxPolygonVertices];
b2Vec2 normals[b2_maxPolygonVertices];
int32 count;
};
// Reference face used for clipping
struct b2ReferenceFace
{
int32 i1, i2;
b2Vec2 v1, v2;
b2Vec2 normal;
b2Vec2 sideNormal1;
float32 sideOffset1;
b2Vec2 sideNormal2;
float32 sideOffset2;
};
// This class collides and edge and a polygon, taking into account edge adjacency.
struct b2EPCollider
{
void Collide(b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA,
const b2PolygonShape* polygonB, const b2Transform& xfB);
b2EPAxis ComputeEdgeSeparation();
b2EPAxis ComputePolygonSeparation();
enum VertexType
{
e_isolated,
e_concave,
e_convex
};
b2TempPolygon m_polygonB;
b2Transform m_xf;
b2Vec2 m_centroidB;
b2Vec2 m_v0, m_v1, m_v2, m_v3;
b2Vec2 m_normal0, m_normal1, m_normal2;
b2Vec2 m_normal;
VertexType m_type1, m_type2;
b2Vec2 m_lowerLimit, m_upperLimit;
float32 m_radius;
bool m_front;
};
// Algorithm:
// 1. Classify v1 and v2
// 2. Classify polygon centroid as front or back
// 3. Flip normal if necessary
// 4. Initialize normal range to [-pi, pi] about face normal
// 5. Adjust normal range according to adjacent edges
// 6. Visit each separating axes, only accept axes within the range
// 7. Return if _any_ axis indicates separation
// 8. Clip
void b2EPCollider::Collide(b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA,
const b2PolygonShape* polygonB, const b2Transform& xfB)
{
m_xf = b2MulT(xfA, xfB);
m_centroidB = b2Mul(m_xf, polygonB->m_centroid);
m_v0 = edgeA->m_vertex0;
m_v1 = edgeA->m_vertex1;
m_v2 = edgeA->m_vertex2;
m_v3 = edgeA->m_vertex3;
bool hasVertex0 = edgeA->m_hasVertex0;
bool hasVertex3 = edgeA->m_hasVertex3;
b2Vec2 edge1 = m_v2 - m_v1;
edge1.Normalize();
m_normal1.Set(edge1.y, -edge1.x);
float32 offset1 = b2Dot(m_normal1, m_centroidB - m_v1);
float32 offset0 = 0.0f, offset2 = 0.0f;
bool convex1 = false, convex2 = false;
// Is there a preceding edge?
if (hasVertex0)
{
b2Vec2 edge0 = m_v1 - m_v0;
edge0.Normalize();
m_normal0.Set(edge0.y, -edge0.x);
convex1 = b2Cross(edge0, edge1) >= 0.0f;
offset0 = b2Dot(m_normal0, m_centroidB - m_v0);
}
// Is there a following edge?
if (hasVertex3)
{
b2Vec2 edge2 = m_v3 - m_v2;
edge2.Normalize();
m_normal2.Set(edge2.y, -edge2.x);
convex2 = b2Cross(edge1, edge2) > 0.0f;
offset2 = b2Dot(m_normal2, m_centroidB - m_v2);
}
// Determine front or back collision. Determine collision normal limits.
if (hasVertex0 && hasVertex3)
{
if (convex1 && convex2)
{
m_front = offset0 >= 0.0f || offset1 >= 0.0f || offset2 >= 0.0f;
if (m_front)
{
m_normal = m_normal1;
m_lowerLimit = m_normal0;
m_upperLimit = m_normal2;
}
else
{
m_normal = -m_normal1;
m_lowerLimit = -m_normal1;
m_upperLimit = -m_normal1;
}
}
else if (convex1)
{
m_front = offset0 >= 0.0f || (offset1 >= 0.0f && offset2 >= 0.0f);
if (m_front)
{
m_normal = m_normal1;
m_lowerLimit = m_normal0;
m_upperLimit = m_normal1;
}
else
{
m_normal = -m_normal1;
m_lowerLimit = -m_normal2;
m_upperLimit = -m_normal1;
}
}
else if (convex2)
{
m_front = offset2 >= 0.0f || (offset0 >= 0.0f && offset1 >= 0.0f);
if (m_front)
{
m_normal = m_normal1;
m_lowerLimit = m_normal1;
m_upperLimit = m_normal2;
}
else
{
m_normal = -m_normal1;
m_lowerLimit = -m_normal1;
m_upperLimit = -m_normal0;
}
}
else
{
m_front = offset0 >= 0.0f && offset1 >= 0.0f && offset2 >= 0.0f;
if (m_front)
{
m_normal = m_normal1;
m_lowerLimit = m_normal1;
m_upperLimit = m_normal1;
}
else
{
m_normal = -m_normal1;
m_lowerLimit = -m_normal2;
m_upperLimit = -m_normal0;
}
}
}
else if (hasVertex0)
{
if (convex1)
{
m_front = offset0 >= 0.0f || offset1 >= 0.0f;
if (m_front)
{
m_normal = m_normal1;
m_lowerLimit = m_normal0;
m_upperLimit = -m_normal1;
}
else
{
m_normal = -m_normal1;
m_lowerLimit = m_normal1;
m_upperLimit = -m_normal1;
}
}
else
{
m_front = offset0 >= 0.0f && offset1 >= 0.0f;
if (m_front)
{
m_normal = m_normal1;
m_lowerLimit = m_normal1;
m_upperLimit = -m_normal1;
}
else
{
m_normal = -m_normal1;
m_lowerLimit = m_normal1;
m_upperLimit = -m_normal0;
}
}
}
else if (hasVertex3)
{
if (convex2)
{
m_front = offset1 >= 0.0f || offset2 >= 0.0f;
if (m_front)
{
m_normal = m_normal1;
m_lowerLimit = -m_normal1;
m_upperLimit = m_normal2;
}
else
{
m_normal = -m_normal1;
m_lowerLimit = -m_normal1;
m_upperLimit = m_normal1;
}
}
else
{
m_front = offset1 >= 0.0f && offset2 >= 0.0f;
if (m_front)
{
m_normal = m_normal1;
m_lowerLimit = -m_normal1;
m_upperLimit = m_normal1;
}
else
{
m_normal = -m_normal1;
m_lowerLimit = -m_normal2;
m_upperLimit = m_normal1;
}
}
}
else
{
m_front = offset1 >= 0.0f;
if (m_front)
{
m_normal = m_normal1;
m_lowerLimit = -m_normal1;
m_upperLimit = -m_normal1;
}
else
{
m_normal = -m_normal1;
m_lowerLimit = m_normal1;
m_upperLimit = m_normal1;
}
}
// Get polygonB in frameA
m_polygonB.count = polygonB->m_count;
for (int32 i = 0; i < polygonB->m_count; ++i)
{
m_polygonB.vertices[i] = b2Mul(m_xf, polygonB->m_vertices[i]);
m_polygonB.normals[i] = b2Mul(m_xf.q, polygonB->m_normals[i]);
}
m_radius = 2.0f * b2_polygonRadius;
manifold->pointCount = 0;
b2EPAxis edgeAxis = ComputeEdgeSeparation();
// If no valid normal can be found than this edge should not collide.
if (edgeAxis.type == b2EPAxis::e_unknown)
{
return;
}
if (edgeAxis.separation > m_radius)
{
return;
}
b2EPAxis polygonAxis = ComputePolygonSeparation();
if (polygonAxis.type != b2EPAxis::e_unknown && polygonAxis.separation > m_radius)
{
return;
}
// Use hysteresis for jitter reduction.
const float32 k_relativeTol = 0.98f;
const float32 k_absoluteTol = 0.001f;
b2EPAxis primaryAxis;
if (polygonAxis.type == b2EPAxis::e_unknown)
{
primaryAxis = edgeAxis;
}
else if (polygonAxis.separation > k_relativeTol * edgeAxis.separation + k_absoluteTol)
{
primaryAxis = polygonAxis;
}
else
{
primaryAxis = edgeAxis;
}
b2ClipVertex ie[2];
b2ReferenceFace rf;
if (primaryAxis.type == b2EPAxis::e_edgeA)
{
manifold->type = b2Manifold::e_faceA;
// Search for the polygon normal that is most anti-parallel to the edge normal.
int32 bestIndex = 0;
float32 bestValue = b2Dot(m_normal, m_polygonB.normals[0]);
for (int32 i = 1; i < m_polygonB.count; ++i)
{
float32 value = b2Dot(m_normal, m_polygonB.normals[i]);
if (value < bestValue)
{
bestValue = value;
bestIndex = i;
}
}
int32 i1 = bestIndex;
int32 i2 = i1 + 1 < m_polygonB.count ? i1 + 1 : 0;
ie[0].v = m_polygonB.vertices[i1];
ie[0].id.cf.indexA = 0;
ie[0].id.cf.indexB = static_cast<uint8>(i1);
ie[0].id.cf.typeA = b2ContactFeature::e_face;
ie[0].id.cf.typeB = b2ContactFeature::e_vertex;
ie[1].v = m_polygonB.vertices[i2];
ie[1].id.cf.indexA = 0;
ie[1].id.cf.indexB = static_cast<uint8>(i2);
ie[1].id.cf.typeA = b2ContactFeature::e_face;
ie[1].id.cf.typeB = b2ContactFeature::e_vertex;
if (m_front)
{
rf.i1 = 0;
rf.i2 = 1;
rf.v1 = m_v1;
rf.v2 = m_v2;
rf.normal = m_normal1;
}
else
{
rf.i1 = 1;
rf.i2 = 0;
rf.v1 = m_v2;
rf.v2 = m_v1;
rf.normal = -m_normal1;
}
}
else
{
manifold->type = b2Manifold::e_faceB;
ie[0].v = m_v1;
ie[0].id.cf.indexA = 0;
ie[0].id.cf.indexB = static_cast<uint8>(primaryAxis.index);
ie[0].id.cf.typeA = b2ContactFeature::e_vertex;
ie[0].id.cf.typeB = b2ContactFeature::e_face;
ie[1].v = m_v2;
ie[1].id.cf.indexA = 0;
ie[1].id.cf.indexB = static_cast<uint8>(primaryAxis.index);
ie[1].id.cf.typeA = b2ContactFeature::e_vertex;
ie[1].id.cf.typeB = b2ContactFeature::e_face;
rf.i1 = primaryAxis.index;
rf.i2 = rf.i1 + 1 < m_polygonB.count ? rf.i1 + 1 : 0;
rf.v1 = m_polygonB.vertices[rf.i1];
rf.v2 = m_polygonB.vertices[rf.i2];
rf.normal = m_polygonB.normals[rf.i1];
}
rf.sideNormal1.Set(rf.normal.y, -rf.normal.x);
rf.sideNormal2 = -rf.sideNormal1;
rf.sideOffset1 = b2Dot(rf.sideNormal1, rf.v1);
rf.sideOffset2 = b2Dot(rf.sideNormal2, rf.v2);
// Clip incident edge against extruded edge1 side edges.
b2ClipVertex clipPoints1[2];
b2ClipVertex clipPoints2[2];
int32 np;
// Clip to box side 1
np = b2ClipSegmentToLine(clipPoints1, ie, rf.sideNormal1, rf.sideOffset1, rf.i1);
if (np < b2_maxManifoldPoints)
{
return;
}
// Clip to negative box side 1
np = b2ClipSegmentToLine(clipPoints2, clipPoints1, rf.sideNormal2, rf.sideOffset2, rf.i2);
if (np < b2_maxManifoldPoints)
{
return;
}
// Now clipPoints2 contains the clipped points.
if (primaryAxis.type == b2EPAxis::e_edgeA)
{
manifold->localNormal = rf.normal;
manifold->localPoint = rf.v1;
}
else
{
manifold->localNormal = polygonB->m_normals[rf.i1];
manifold->localPoint = polygonB->m_vertices[rf.i1];
}
int32 pointCount = 0;
for (int32 i = 0; i < b2_maxManifoldPoints; ++i)
{
float32 separation;
separation = b2Dot(rf.normal, clipPoints2[i].v - rf.v1);
if (separation <= m_radius)
{
b2ManifoldPoint* cp = manifold->points + pointCount;
if (primaryAxis.type == b2EPAxis::e_edgeA)
{
cp->localPoint = b2MulT(m_xf, clipPoints2[i].v);
cp->id = clipPoints2[i].id;
}
else
{
cp->localPoint = clipPoints2[i].v;
cp->id.cf.typeA = clipPoints2[i].id.cf.typeB;
cp->id.cf.typeB = clipPoints2[i].id.cf.typeA;
cp->id.cf.indexA = clipPoints2[i].id.cf.indexB;
cp->id.cf.indexB = clipPoints2[i].id.cf.indexA;
}
++pointCount;
}
}
manifold->pointCount = pointCount;
}
b2EPAxis b2EPCollider::ComputeEdgeSeparation()
{
b2EPAxis axis;
axis.type = b2EPAxis::e_edgeA;
axis.index = m_front ? 0 : 1;
axis.separation = FLT_MAX;
for (int32 i = 0; i < m_polygonB.count; ++i)
{
float32 s = b2Dot(m_normal, m_polygonB.vertices[i] - m_v1);
if (s < axis.separation)
{
axis.separation = s;
}
}
return axis;
}
b2EPAxis b2EPCollider::ComputePolygonSeparation()
{
b2EPAxis axis;
axis.type = b2EPAxis::e_unknown;
axis.index = -1;
axis.separation = -FLT_MAX;
b2Vec2 perp(-m_normal.y, m_normal.x);
for (int32 i = 0; i < m_polygonB.count; ++i)
{
b2Vec2 n = -m_polygonB.normals[i];
float32 s1 = b2Dot(n, m_polygonB.vertices[i] - m_v1);
float32 s2 = b2Dot(n, m_polygonB.vertices[i] - m_v2);
float32 s = b2Min(s1, s2);
if (s > m_radius)
{
// No collision
axis.type = b2EPAxis::e_edgeB;
axis.index = i;
axis.separation = s;
return axis;
}
// Adjacency
if (b2Dot(n, perp) >= 0.0f)
{
if (b2Dot(n - m_upperLimit, m_normal) < -b2_angularSlop)
{
continue;
}
}
else
{
if (b2Dot(n - m_lowerLimit, m_normal) < -b2_angularSlop)
{
continue;
}
}
if (s > axis.separation)
{
axis.type = b2EPAxis::e_edgeB;
axis.index = i;
axis.separation = s;
}
}
return axis;
}
void b2CollideEdgeAndPolygon( b2Manifold* manifold,
const b2EdgeShape* edgeA, const b2Transform& xfA,
const b2PolygonShape* polygonB, const b2Transform& xfB)
{
b2EPCollider collider;
collider.Collide(manifold, edgeA, xfA, polygonB, xfB);
}
+239
View File
@@ -0,0 +1,239 @@
/*
* Copyright (c) 2006-2009 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/Collision/b2Collision.h>
#include <Box2D/Collision/Shapes/b2PolygonShape.h>
// Find the max separation between poly1 and poly2 using edge normals from poly1.
static float32 b2FindMaxSeparation(int32* edgeIndex,
const b2PolygonShape* poly1, const b2Transform& xf1,
const b2PolygonShape* poly2, const b2Transform& xf2)
{
int32 count1 = poly1->m_count;
int32 count2 = poly2->m_count;
const b2Vec2* n1s = poly1->m_normals;
const b2Vec2* v1s = poly1->m_vertices;
const b2Vec2* v2s = poly2->m_vertices;
b2Transform xf = b2MulT(xf2, xf1);
int32 bestIndex = 0;
float32 maxSeparation = -b2_maxFloat;
for (int32 i = 0; i < count1; ++i)
{
// Get poly1 normal in frame2.
b2Vec2 n = b2Mul(xf.q, n1s[i]);
b2Vec2 v1 = b2Mul(xf, v1s[i]);
// Find deepest point for normal i.
float32 si = b2_maxFloat;
for (int32 j = 0; j < count2; ++j)
{
float32 sij = b2Dot(n, v2s[j] - v1);
if (sij < si)
{
si = sij;
}
}
if (si > maxSeparation)
{
maxSeparation = si;
bestIndex = i;
}
}
*edgeIndex = bestIndex;
return maxSeparation;
}
static void b2FindIncidentEdge(b2ClipVertex c[2],
const b2PolygonShape* poly1, const b2Transform& xf1, int32 edge1,
const b2PolygonShape* poly2, const b2Transform& xf2)
{
const b2Vec2* normals1 = poly1->m_normals;
int32 count2 = poly2->m_count;
const b2Vec2* vertices2 = poly2->m_vertices;
const b2Vec2* normals2 = poly2->m_normals;
b2Assert(0 <= edge1 && edge1 < poly1->m_count);
// Get the normal of the reference edge in poly2's frame.
b2Vec2 normal1 = b2MulT(xf2.q, b2Mul(xf1.q, normals1[edge1]));
// Find the incident edge on poly2.
int32 index = 0;
float32 minDot = b2_maxFloat;
for (int32 i = 0; i < count2; ++i)
{
float32 dot = b2Dot(normal1, normals2[i]);
if (dot < minDot)
{
minDot = dot;
index = i;
}
}
// Build the clip vertices for the incident edge.
int32 i1 = index;
int32 i2 = i1 + 1 < count2 ? i1 + 1 : 0;
c[0].v = b2Mul(xf2, vertices2[i1]);
c[0].id.cf.indexA = (uint8)edge1;
c[0].id.cf.indexB = (uint8)i1;
c[0].id.cf.typeA = b2ContactFeature::e_face;
c[0].id.cf.typeB = b2ContactFeature::e_vertex;
c[1].v = b2Mul(xf2, vertices2[i2]);
c[1].id.cf.indexA = (uint8)edge1;
c[1].id.cf.indexB = (uint8)i2;
c[1].id.cf.typeA = b2ContactFeature::e_face;
c[1].id.cf.typeB = b2ContactFeature::e_vertex;
}
// Find edge normal of max separation on A - return if separating axis is found
// Find edge normal of max separation on B - return if separation axis is found
// Choose reference edge as min(minA, minB)
// Find incident edge
// Clip
// The normal points from 1 to 2
void b2CollidePolygons(b2Manifold* manifold,
const b2PolygonShape* polyA, const b2Transform& xfA,
const b2PolygonShape* polyB, const b2Transform& xfB)
{
manifold->pointCount = 0;
float32 totalRadius = polyA->m_radius + polyB->m_radius;
int32 edgeA = 0;
float32 separationA = b2FindMaxSeparation(&edgeA, polyA, xfA, polyB, xfB);
if (separationA > totalRadius)
return;
int32 edgeB = 0;
float32 separationB = b2FindMaxSeparation(&edgeB, polyB, xfB, polyA, xfA);
if (separationB > totalRadius)
return;
const b2PolygonShape* poly1; // reference polygon
const b2PolygonShape* poly2; // incident polygon
b2Transform xf1, xf2;
int32 edge1; // reference edge
uint8 flip;
const float32 k_tol = 0.1f * b2_linearSlop;
if (separationB > separationA + k_tol)
{
poly1 = polyB;
poly2 = polyA;
xf1 = xfB;
xf2 = xfA;
edge1 = edgeB;
manifold->type = b2Manifold::e_faceB;
flip = 1;
}
else
{
poly1 = polyA;
poly2 = polyB;
xf1 = xfA;
xf2 = xfB;
edge1 = edgeA;
manifold->type = b2Manifold::e_faceA;
flip = 0;
}
b2ClipVertex incidentEdge[2];
b2FindIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2);
int32 count1 = poly1->m_count;
const b2Vec2* vertices1 = poly1->m_vertices;
int32 iv1 = edge1;
int32 iv2 = edge1 + 1 < count1 ? edge1 + 1 : 0;
b2Vec2 v11 = vertices1[iv1];
b2Vec2 v12 = vertices1[iv2];
b2Vec2 localTangent = v12 - v11;
localTangent.Normalize();
b2Vec2 localNormal = b2Cross(localTangent, 1.0f);
b2Vec2 planePoint = 0.5f * (v11 + v12);
b2Vec2 tangent = b2Mul(xf1.q, localTangent);
b2Vec2 normal = b2Cross(tangent, 1.0f);
v11 = b2Mul(xf1, v11);
v12 = b2Mul(xf1, v12);
// Face offset.
float32 frontOffset = b2Dot(normal, v11);
// Side offsets, extended by polytope skin thickness.
float32 sideOffset1 = -b2Dot(tangent, v11) + totalRadius;
float32 sideOffset2 = b2Dot(tangent, v12) + totalRadius;
// Clip incident edge against extruded edge1 side edges.
b2ClipVertex clipPoints1[2];
b2ClipVertex clipPoints2[2];
int np;
// Clip to box side 1
np = b2ClipSegmentToLine(clipPoints1, incidentEdge, -tangent, sideOffset1, iv1);
if (np < 2)
return;
// Clip to negative box side 1
np = b2ClipSegmentToLine(clipPoints2, clipPoints1, tangent, sideOffset2, iv2);
if (np < 2)
{
return;
}
// Now clipPoints2 contains the clipped points.
manifold->localNormal = localNormal;
manifold->localPoint = planePoint;
int32 pointCount = 0;
for (int32 i = 0; i < b2_maxManifoldPoints; ++i)
{
float32 separation = b2Dot(normal, clipPoints2[i].v) - frontOffset;
if (separation <= totalRadius)
{
b2ManifoldPoint* cp = manifold->points + pointCount;
cp->localPoint = b2MulT(xf2, clipPoints2[i].v);
cp->id = clipPoints2[i].id;
if (flip)
{
// Swap features
b2ContactFeature cf = cp->id.cf;
cp->id.cf.indexA = cf.indexB;
cp->id.cf.indexB = cf.indexA;
cp->id.cf.typeA = cf.typeB;
cp->id.cf.typeB = cf.typeA;
}
++pointCount;
}
}
manifold->pointCount = pointCount;
}
+252
View File
@@ -0,0 +1,252 @@
/*
* Copyright (c) 2007-2009 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/Collision/b2Collision.h>
#include <Box2D/Collision/b2Distance.h>
void b2WorldManifold::Initialize(const b2Manifold* manifold,
const b2Transform& xfA, float32 radiusA,
const b2Transform& xfB, float32 radiusB)
{
if (manifold->pointCount == 0)
{
return;
}
switch (manifold->type)
{
case b2Manifold::e_circles:
{
normal.Set(1.0f, 0.0f);
b2Vec2 pointA = b2Mul(xfA, manifold->localPoint);
b2Vec2 pointB = b2Mul(xfB, manifold->points[0].localPoint);
if (b2DistanceSquared(pointA, pointB) > b2_epsilon * b2_epsilon)
{
normal = pointB - pointA;
normal.Normalize();
}
b2Vec2 cA = pointA + radiusA * normal;
b2Vec2 cB = pointB - radiusB * normal;
points[0] = 0.5f * (cA + cB);
separations[0] = b2Dot(cB - cA, normal);
}
break;
case b2Manifold::e_faceA:
{
normal = b2Mul(xfA.q, manifold->localNormal);
b2Vec2 planePoint = b2Mul(xfA, manifold->localPoint);
for (int32 i = 0; i < manifold->pointCount; ++i)
{
b2Vec2 clipPoint = b2Mul(xfB, manifold->points[i].localPoint);
b2Vec2 cA = clipPoint + (radiusA - b2Dot(clipPoint - planePoint, normal)) * normal;
b2Vec2 cB = clipPoint - radiusB * normal;
points[i] = 0.5f * (cA + cB);
separations[i] = b2Dot(cB - cA, normal);
}
}
break;
case b2Manifold::e_faceB:
{
normal = b2Mul(xfB.q, manifold->localNormal);
b2Vec2 planePoint = b2Mul(xfB, manifold->localPoint);
for (int32 i = 0; i < manifold->pointCount; ++i)
{
b2Vec2 clipPoint = b2Mul(xfA, manifold->points[i].localPoint);
b2Vec2 cB = clipPoint + (radiusB - b2Dot(clipPoint - planePoint, normal)) * normal;
b2Vec2 cA = clipPoint - radiusA * normal;
points[i] = 0.5f * (cA + cB);
separations[i] = b2Dot(cA - cB, normal);
}
// Ensure normal points from A to B.
normal = -normal;
}
break;
}
}
void b2GetPointStates(b2PointState state1[b2_maxManifoldPoints], b2PointState state2[b2_maxManifoldPoints],
const b2Manifold* manifold1, const b2Manifold* manifold2)
{
for (int32 i = 0; i < b2_maxManifoldPoints; ++i)
{
state1[i] = b2_nullState;
state2[i] = b2_nullState;
}
// Detect persists and removes.
for (int32 i = 0; i < manifold1->pointCount; ++i)
{
b2ContactID id = manifold1->points[i].id;
state1[i] = b2_removeState;
for (int32 j = 0; j < manifold2->pointCount; ++j)
{
if (manifold2->points[j].id.key == id.key)
{
state1[i] = b2_persistState;
break;
}
}
}
// Detect persists and adds.
for (int32 i = 0; i < manifold2->pointCount; ++i)
{
b2ContactID id = manifold2->points[i].id;
state2[i] = b2_addState;
for (int32 j = 0; j < manifold1->pointCount; ++j)
{
if (manifold1->points[j].id.key == id.key)
{
state2[i] = b2_persistState;
break;
}
}
}
}
// From Real-time Collision Detection, p179.
bool b2AABB::RayCast(b2RayCastOutput* output, const b2RayCastInput& input) const
{
float32 tmin = -b2_maxFloat;
float32 tmax = b2_maxFloat;
b2Vec2 p = input.p1;
b2Vec2 d = input.p2 - input.p1;
b2Vec2 absD = b2Abs(d);
b2Vec2 normal;
for (int32 i = 0; i < 2; ++i)
{
if (absD(i) < b2_epsilon)
{
// Parallel.
if (p(i) < lowerBound(i) || upperBound(i) < p(i))
{
return false;
}
}
else
{
float32 inv_d = 1.0f / d(i);
float32 t1 = (lowerBound(i) - p(i)) * inv_d;
float32 t2 = (upperBound(i) - p(i)) * inv_d;
// Sign of the normal vector.
float32 s = -1.0f;
if (t1 > t2)
{
b2Swap(t1, t2);
s = 1.0f;
}
// Push the min up
if (t1 > tmin)
{
normal.SetZero();
normal(i) = s;
tmin = t1;
}
// Pull the max down
tmax = b2Min(tmax, t2);
if (tmin > tmax)
{
return false;
}
}
}
// Does the ray start inside the box?
// Does the ray intersect beyond the max fraction?
if (tmin < 0.0f || input.maxFraction < tmin)
{
return false;
}
// Intersection.
output->fraction = tmin;
output->normal = normal;
return true;
}
// Sutherland-Hodgman clipping.
int32 b2ClipSegmentToLine(b2ClipVertex vOut[2], const b2ClipVertex vIn[2],
const b2Vec2& normal, float32 offset, int32 vertexIndexA)
{
// Start with no output points
int32 numOut = 0;
// Calculate the distance of end points to the line
float32 distance0 = b2Dot(normal, vIn[0].v) - offset;
float32 distance1 = b2Dot(normal, vIn[1].v) - offset;
// If the points are behind the plane
if (distance0 <= 0.0f) vOut[numOut++] = vIn[0];
if (distance1 <= 0.0f) vOut[numOut++] = vIn[1];
// If the points are on different sides of the plane
if (distance0 * distance1 < 0.0f)
{
// Find intersection point of edge and plane
float32 interp = distance0 / (distance0 - distance1);
vOut[numOut].v = vIn[0].v + interp * (vIn[1].v - vIn[0].v);
// VertexA is hitting edgeB.
vOut[numOut].id.cf.indexA = static_cast<uint8>(vertexIndexA);
vOut[numOut].id.cf.indexB = vIn[0].id.cf.indexB;
vOut[numOut].id.cf.typeA = b2ContactFeature::e_vertex;
vOut[numOut].id.cf.typeB = b2ContactFeature::e_face;
++numOut;
}
return numOut;
}
bool b2TestOverlap( const b2Shape* shapeA, int32 indexA,
const b2Shape* shapeB, int32 indexB,
const b2Transform& xfA, const b2Transform& xfB)
{
b2DistanceInput input;
input.proxyA.Set(shapeA, indexA);
input.proxyB.Set(shapeB, indexB);
input.transformA = xfA;
input.transformB = xfB;
input.useRadii = true;
b2SimplexCache cache;
cache.count = 0;
b2DistanceOutput output;
b2Distance(&output, &cache, &input);
return output.distance < 10.0f * b2_epsilon;
}
+277
View File
@@ -0,0 +1,277 @@
/*
* Copyright (c) 2006-2009 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.
*/
#ifndef B2_COLLISION_H
#define B2_COLLISION_H
#include <Box2D/Common/b2Math.h>
#include <limits.h>
/// @file
/// Structures and functions used for computing contact points, distance
/// queries, and TOI queries.
class b2Shape;
class b2CircleShape;
class b2EdgeShape;
class b2PolygonShape;
const uint8 b2_nullFeature = UCHAR_MAX;
/// The features that intersect to form the contact point
/// This must be 4 bytes or less.
struct b2ContactFeature
{
enum Type
{
e_vertex = 0,
e_face = 1
};
uint8 indexA; ///< Feature index on shapeA
uint8 indexB; ///< Feature index on shapeB
uint8 typeA; ///< The feature type on shapeA
uint8 typeB; ///< The feature type on shapeB
};
/// Contact ids to facilitate warm starting.
union b2ContactID
{
b2ContactFeature cf;
uint32 key; ///< Used to quickly compare contact ids.
};
/// A manifold point is a contact point belonging to a contact
/// manifold. It holds details related to the geometry and dynamics
/// of the contact points.
/// The local point usage depends on the manifold type:
/// -e_circles: the local center of circleB
/// -e_faceA: the local center of cirlceB or the clip point of polygonB
/// -e_faceB: the clip point of polygonA
/// This structure is stored across time steps, so we keep it small.
/// Note: the impulses are used for internal caching and may not
/// provide reliable contact forces, especially for high speed collisions.
struct b2ManifoldPoint
{
b2Vec2 localPoint; ///< usage depends on manifold type
float32 normalImpulse; ///< the non-penetration impulse
float32 tangentImpulse; ///< the friction impulse
b2ContactID id; ///< uniquely identifies a contact point between two shapes
};
/// A manifold for two touching convex shapes.
/// Box2D supports multiple types of contact:
/// - clip point versus plane with radius
/// - point versus point with radius (circles)
/// The local point usage depends on the manifold type:
/// -e_circles: the local center of circleA
/// -e_faceA: the center of faceA
/// -e_faceB: the center of faceB
/// Similarly the local normal usage:
/// -e_circles: not used
/// -e_faceA: the normal on polygonA
/// -e_faceB: the normal on polygonB
/// We store contacts in this way so that position correction can
/// account for movement, which is critical for continuous physics.
/// All contact scenarios must be expressed in one of these types.
/// This structure is stored across time steps, so we keep it small.
struct b2Manifold
{
enum Type
{
e_circles,
e_faceA,
e_faceB
};
b2ManifoldPoint points[b2_maxManifoldPoints]; ///< the points of contact
b2Vec2 localNormal; ///< not use for Type::e_points
b2Vec2 localPoint; ///< usage depends on manifold type
Type type;
int32 pointCount; ///< the number of manifold points
};
/// This is used to compute the current state of a contact manifold.
struct b2WorldManifold
{
/// Evaluate the manifold with supplied transforms. This assumes
/// modest motion from the original state. This does not change the
/// point count, impulses, etc. The radii must come from the shapes
/// that generated the manifold.
void Initialize(const b2Manifold* manifold,
const b2Transform& xfA, float32 radiusA,
const b2Transform& xfB, float32 radiusB);
b2Vec2 normal; ///< world vector pointing from A to B
b2Vec2 points[b2_maxManifoldPoints]; ///< world contact point (point of intersection)
float32 separations[b2_maxManifoldPoints]; ///< a negative value indicates overlap, in meters
};
/// This is used for determining the state of contact points.
enum b2PointState
{
b2_nullState, ///< point does not exist
b2_addState, ///< point was added in the update
b2_persistState, ///< point persisted across the update
b2_removeState ///< point was removed in the update
};
/// Compute the point states given two manifolds. The states pertain to the transition from manifold1
/// to manifold2. So state1 is either persist or remove while state2 is either add or persist.
void b2GetPointStates(b2PointState state1[b2_maxManifoldPoints], b2PointState state2[b2_maxManifoldPoints],
const b2Manifold* manifold1, const b2Manifold* manifold2);
/// Used for computing contact manifolds.
struct b2ClipVertex
{
b2Vec2 v;
b2ContactID id;
};
/// Ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
struct b2RayCastInput
{
b2Vec2 p1, p2;
float32 maxFraction;
};
/// Ray-cast output data. The ray hits at p1 + fraction * (p2 - p1), where p1 and p2
/// come from b2RayCastInput.
struct b2RayCastOutput
{
b2Vec2 normal;
float32 fraction;
};
/// An axis aligned bounding box.
struct b2AABB
{
/// Verify that the bounds are sorted.
bool IsValid() const;
/// Get the center of the AABB.
b2Vec2 GetCenter() const
{
return 0.5f * (lowerBound + upperBound);
}
/// Get the extents of the AABB (half-widths).
b2Vec2 GetExtents() const
{
return 0.5f * (upperBound - lowerBound);
}
/// Get the perimeter length
float32 GetPerimeter() const
{
float32 wx = upperBound.x - lowerBound.x;
float32 wy = upperBound.y - lowerBound.y;
return 2.0f * (wx + wy);
}
/// Combine an AABB into this one.
void Combine(const b2AABB& aabb)
{
lowerBound = b2Min(lowerBound, aabb.lowerBound);
upperBound = b2Max(upperBound, aabb.upperBound);
}
/// Combine two AABBs into this one.
void Combine(const b2AABB& aabb1, const b2AABB& aabb2)
{
lowerBound = b2Min(aabb1.lowerBound, aabb2.lowerBound);
upperBound = b2Max(aabb1.upperBound, aabb2.upperBound);
}
/// Does this aabb contain the provided AABB.
bool Contains(const b2AABB& aabb) const
{
bool result = true;
result = result && lowerBound.x <= aabb.lowerBound.x;
result = result && lowerBound.y <= aabb.lowerBound.y;
result = result && aabb.upperBound.x <= upperBound.x;
result = result && aabb.upperBound.y <= upperBound.y;
return result;
}
bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input) const;
b2Vec2 lowerBound; ///< the lower vertex
b2Vec2 upperBound; ///< the upper vertex
};
/// Compute the collision manifold between two circles.
void b2CollideCircles(b2Manifold* manifold,
const b2CircleShape* circleA, const b2Transform& xfA,
const b2CircleShape* circleB, const b2Transform& xfB);
/// Compute the collision manifold between a polygon and a circle.
void b2CollidePolygonAndCircle(b2Manifold* manifold,
const b2PolygonShape* polygonA, const b2Transform& xfA,
const b2CircleShape* circleB, const b2Transform& xfB);
/// Compute the collision manifold between two polygons.
void b2CollidePolygons(b2Manifold* manifold,
const b2PolygonShape* polygonA, const b2Transform& xfA,
const b2PolygonShape* polygonB, const b2Transform& xfB);
/// Compute the collision manifold between an edge and a circle.
void b2CollideEdgeAndCircle(b2Manifold* manifold,
const b2EdgeShape* polygonA, const b2Transform& xfA,
const b2CircleShape* circleB, const b2Transform& xfB);
/// Compute the collision manifold between an edge and a circle.
void b2CollideEdgeAndPolygon(b2Manifold* manifold,
const b2EdgeShape* edgeA, const b2Transform& xfA,
const b2PolygonShape* circleB, const b2Transform& xfB);
/// Clipping for contact manifolds.
int32 b2ClipSegmentToLine(b2ClipVertex vOut[2], const b2ClipVertex vIn[2],
const b2Vec2& normal, float32 offset, int32 vertexIndexA);
/// Determine if two generic shapes overlap.
bool b2TestOverlap( const b2Shape* shapeA, int32 indexA,
const b2Shape* shapeB, int32 indexB,
const b2Transform& xfA, const b2Transform& xfB);
// ---------------- Inline Functions ------------------------------------------
inline bool b2AABB::IsValid() const
{
b2Vec2 d = upperBound - lowerBound;
bool valid = d.x >= 0.0f && d.y >= 0.0f;
valid = valid && lowerBound.IsValid() && upperBound.IsValid();
return valid;
}
inline bool b2TestOverlap(const b2AABB& a, const b2AABB& b)
{
b2Vec2 d1, d2;
d1 = b.lowerBound - a.upperBound;
d2 = a.lowerBound - b.upperBound;
if (d1.x > 0.0f || d1.y > 0.0f)
return false;
if (d2.x > 0.0f || d2.y > 0.0f)
return false;
return true;
}
#endif
+614
View File
@@ -0,0 +1,614 @@
/*
* Copyright (c) 2007-2009 Erin Catto http://www.box2d.org
* Copyright (c) 2014 Google, Inc.
*
* 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 <string.h>
#include <memory.h>
#include <Box2D/Collision/b2Distance.h>
#include <Box2D/Collision/Shapes/b2CircleShape.h>
#include <Box2D/Collision/Shapes/b2EdgeShape.h>
#include <Box2D/Collision/Shapes/b2ChainShape.h>
#include <Box2D/Collision/Shapes/b2PolygonShape.h>
// GJK using Voronoi regions (Christer Ericson) and Barycentric coordinates.
int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
void b2DistanceProxy::Set(const b2Shape* shape, int32 index)
{
switch (shape->GetType())
{
case b2Shape::e_circle:
{
const b2CircleShape* circle = static_cast<const b2CircleShape*>(shape);
m_vertices = &circle->m_p;
m_count = 1;
m_radius = circle->m_radius;
}
break;
case b2Shape::e_polygon:
{
const b2PolygonShape* polygon = static_cast<const b2PolygonShape*>(shape);
m_vertices = polygon->m_vertices;
m_count = polygon->m_count;
m_radius = polygon->m_radius;
}
break;
case b2Shape::e_chain:
{
const b2ChainShape* chain = static_cast<const b2ChainShape*>(shape);
b2Assert(0 <= index && index < chain->m_count);
m_buffer[0] = chain->m_vertices[index];
if (index + 1 < chain->m_count)
{
m_buffer[1] = chain->m_vertices[index + 1];
}
else
{
m_buffer[1] = chain->m_vertices[0];
}
m_vertices = m_buffer;
m_count = 2;
m_radius = chain->m_radius;
}
break;
case b2Shape::e_edge:
{
const b2EdgeShape* edge = static_cast<const b2EdgeShape*>(shape);
m_vertices = &edge->m_vertex1;
m_count = 2;
m_radius = edge->m_radius;
}
break;
default:
b2Assert(false);
}
}
struct b2SimplexVertex
{
b2Vec2 wA; // support point in proxyA
b2Vec2 wB; // support point in proxyB
b2Vec2 w; // wB - wA
float32 a; // barycentric coordinate for closest point
int32 indexA; // wA index
int32 indexB; // wB index
};
struct b2Simplex
{
void ReadCache( const b2SimplexCache* cache,
const b2DistanceProxy* proxyA, const b2Transform& transformA,
const b2DistanceProxy* proxyB, const b2Transform& transformB)
{
b2Assert(cache->count <= 3);
// Copy data from cache.
m_count = cache->count;
b2SimplexVertex* vertices = &m_v1;
for (int32 i = 0; i < m_count; ++i)
{
b2SimplexVertex* v = vertices + i;
v->indexA = cache->indexA[i];
v->indexB = cache->indexB[i];
b2Vec2 wALocal = proxyA->GetVertex(v->indexA);
b2Vec2 wBLocal = proxyB->GetVertex(v->indexB);
v->wA = b2Mul(transformA, wALocal);
v->wB = b2Mul(transformB, wBLocal);
v->w = v->wB - v->wA;
v->a = 0.0f;
}
// Compute the new simplex metric, if it is substantially different than
// old metric then flush the simplex.
if (m_count > 1)
{
float32 metric1 = cache->metric;
float32 metric2 = GetMetric();
if (metric2 < 0.5f * metric1 || 2.0f * metric1 < metric2 || metric2 < b2_epsilon)
{
// Reset the simplex.
m_count = 0;
}
}
// If the cache is empty or invalid ...
if (m_count == 0)
{
b2SimplexVertex* v = vertices + 0;
v->indexA = 0;
v->indexB = 0;
b2Vec2 wALocal = proxyA->GetVertex(0);
b2Vec2 wBLocal = proxyB->GetVertex(0);
v->wA = b2Mul(transformA, wALocal);
v->wB = b2Mul(transformB, wBLocal);
v->w = v->wB - v->wA;
v->a = 1.0f;
m_count = 1;
}
}
void WriteCache(b2SimplexCache* cache) const
{
cache->metric = GetMetric();
cache->count = uint16(m_count);
const b2SimplexVertex* vertices = &m_v1;
for (int32 i = 0; i < m_count; ++i)
{
cache->indexA[i] = uint8(vertices[i].indexA);
cache->indexB[i] = uint8(vertices[i].indexB);
}
}
b2Vec2 GetSearchDirection() const
{
switch (m_count)
{
case 1:
return -m_v1.w;
case 2:
{
b2Vec2 e12 = m_v2.w - m_v1.w;
float32 sgn = b2Cross(e12, -m_v1.w);
if (sgn > 0.0f)
{
// Origin is left of e12.
return b2Cross(1.0f, e12);
}
else
{
// Origin is right of e12.
return b2Cross(e12, 1.0f);
}
}
default:
b2Assert(false);
return b2Vec2_zero;
}
}
b2Vec2 GetClosestPoint() const
{
switch (m_count)
{
case 0:
b2Assert(false);
return b2Vec2_zero;
case 1:
return m_v1.w;
case 2:
return m_v1.a * m_v1.w + m_v2.a * m_v2.w;
case 3:
return b2Vec2_zero;
default:
b2Assert(false);
return b2Vec2_zero;
}
}
void GetWitnessPoints(b2Vec2* pA, b2Vec2* pB) const
{
switch (m_count)
{
case 0:
b2Assert(false);
break;
case 1:
*pA = m_v1.wA;
*pB = m_v1.wB;
break;
case 2:
*pA = m_v1.a * m_v1.wA + m_v2.a * m_v2.wA;
*pB = m_v1.a * m_v1.wB + m_v2.a * m_v2.wB;
break;
case 3:
*pA = m_v1.a * m_v1.wA + m_v2.a * m_v2.wA + m_v3.a * m_v3.wA;
*pB = *pA;
break;
default:
b2Assert(false);
break;
}
}
float32 GetMetric() const
{
switch (m_count)
{
case 0:
b2Assert(false);
return 0.0f;
case 1:
return 0.0f;
case 2:
return b2Distance(m_v1.w, m_v2.w);
case 3:
return b2Cross(m_v2.w - m_v1.w, m_v3.w - m_v1.w);
default:
b2Assert(false);
return 0.0f;
}
}
void Solve2();
void Solve3();
b2SimplexVertex m_v1, m_v2, m_v3;
int32 m_count;
};
// Solve a line segment using barycentric coordinates.
//
// p = a1 * w1 + a2 * w2
// a1 + a2 = 1
//
// The vector from the origin to the closest point on the line is
// perpendicular to the line.
// e12 = w2 - w1
// dot(p, e) = 0
// a1 * dot(w1, e) + a2 * dot(w2, e) = 0
//
// 2-by-2 linear system
// [1 1 ][a1] = [1]
// [w1.e12 w2.e12][a2] = [0]
//
// Define
// d12_1 = dot(w2, e12)
// d12_2 = -dot(w1, e12)
// d12 = d12_1 + d12_2
//
// Solution
// a1 = d12_1 / d12
// a2 = d12_2 / d12
void b2Simplex::Solve2()
{
b2Vec2 w1 = m_v1.w;
b2Vec2 w2 = m_v2.w;
b2Vec2 e12 = w2 - w1;
// w1 region
float32 d12_2 = -b2Dot(w1, e12);
if (d12_2 <= 0.0f)
{
// a2 <= 0, so we clamp it to 0
m_v1.a = 1.0f;
m_count = 1;
return;
}
// w2 region
float32 d12_1 = b2Dot(w2, e12);
if (d12_1 <= 0.0f)
{
// a1 <= 0, so we clamp it to 0
m_v2.a = 1.0f;
m_count = 1;
m_v1 = m_v2;
return;
}
// Must be in e12 region.
float32 inv_d12 = 1.0f / (d12_1 + d12_2);
m_v1.a = d12_1 * inv_d12;
m_v2.a = d12_2 * inv_d12;
m_count = 2;
}
// Possible regions:
// - points[2]
// - edge points[0]-points[2]
// - edge points[1]-points[2]
// - inside the triangle
void b2Simplex::Solve3()
{
b2Vec2 w1 = m_v1.w;
b2Vec2 w2 = m_v2.w;
b2Vec2 w3 = m_v3.w;
// Edge12
// [1 1 ][a1] = [1]
// [w1.e12 w2.e12][a2] = [0]
// a3 = 0
b2Vec2 e12 = w2 - w1;
float32 w1e12 = b2Dot(w1, e12);
float32 w2e12 = b2Dot(w2, e12);
float32 d12_1 = w2e12;
float32 d12_2 = -w1e12;
// Edge13
// [1 1 ][a1] = [1]
// [w1.e13 w3.e13][a3] = [0]
// a2 = 0
b2Vec2 e13 = w3 - w1;
float32 w1e13 = b2Dot(w1, e13);
float32 w3e13 = b2Dot(w3, e13);
float32 d13_1 = w3e13;
float32 d13_2 = -w1e13;
// Edge23
// [1 1 ][a2] = [1]
// [w2.e23 w3.e23][a3] = [0]
// a1 = 0
b2Vec2 e23 = w3 - w2;
float32 w2e23 = b2Dot(w2, e23);
float32 w3e23 = b2Dot(w3, e23);
float32 d23_1 = w3e23;
float32 d23_2 = -w2e23;
// Triangle123
float32 n123 = b2Cross(e12, e13);
float32 d123_1 = n123 * b2Cross(w2, w3);
float32 d123_2 = n123 * b2Cross(w3, w1);
float32 d123_3 = n123 * b2Cross(w1, w2);
// w1 region
if (d12_2 <= 0.0f && d13_2 <= 0.0f)
{
m_v1.a = 1.0f;
m_count = 1;
return;
}
// e12
if (d12_1 > 0.0f && d12_2 > 0.0f && d123_3 <= 0.0f)
{
float32 inv_d12 = 1.0f / (d12_1 + d12_2);
m_v1.a = d12_1 * inv_d12;
m_v2.a = d12_2 * inv_d12;
m_count = 2;
return;
}
// e13
if (d13_1 > 0.0f && d13_2 > 0.0f && d123_2 <= 0.0f)
{
float32 inv_d13 = 1.0f / (d13_1 + d13_2);
m_v1.a = d13_1 * inv_d13;
m_v3.a = d13_2 * inv_d13;
m_count = 2;
m_v2 = m_v3;
return;
}
// w2 region
if (d12_1 <= 0.0f && d23_2 <= 0.0f)
{
m_v2.a = 1.0f;
m_count = 1;
m_v1 = m_v2;
return;
}
// w3 region
if (d13_1 <= 0.0f && d23_1 <= 0.0f)
{
m_v3.a = 1.0f;
m_count = 1;
m_v1 = m_v3;
return;
}
// e23
if (d23_1 > 0.0f && d23_2 > 0.0f && d123_1 <= 0.0f)
{
float32 inv_d23 = 1.0f / (d23_1 + d23_2);
m_v2.a = d23_1 * inv_d23;
m_v3.a = d23_2 * inv_d23;
m_count = 2;
m_v1 = m_v3;
return;
}
// Must be in triangle123
float32 inv_d123 = 1.0f / (d123_1 + d123_2 + d123_3);
m_v1.a = d123_1 * inv_d123;
m_v2.a = d123_2 * inv_d123;
m_v3.a = d123_3 * inv_d123;
m_count = 3;
}
void b2Distance(b2DistanceOutput* output,
b2SimplexCache* cache,
const b2DistanceInput* input)
{
++b2_gjkCalls;
const b2DistanceProxy* proxyA = &input->proxyA;
const b2DistanceProxy* proxyB = &input->proxyB;
b2Transform transformA = input->transformA;
b2Transform transformB = input->transformB;
// Initialize the simplex.
b2Simplex simplex;
simplex.ReadCache(cache, proxyA, transformA, proxyB, transformB);
// Get simplex vertices as an array.
b2SimplexVertex* vertices = &simplex.m_v1;
const int32 k_maxIters = 20;
// These store the vertices of the last simplex so that we
// can check for duplicates and prevent cycling.
int32 saveA[3], saveB[3];
int32 saveCount = 0;
// Work around spurious gcc-4.8.2 warnings when -Wmaybe-uninitialized is
// enabled by initializing saveA / saveB arrays when they're referenced
// at the end of the main iteration loop below even though saveCount
// entries of each array are initialized at the start of the main
// iteration loop.
memset(saveA, 0, sizeof(saveA));
memset(saveB, 0, sizeof(saveB));
float32 distanceSqr1 = b2_maxFloat;
float32 distanceSqr2;
// Main iteration loop.
int32 iter = 0;
while (iter < k_maxIters)
{
// Copy simplex so we can identify duplicates.
saveCount = simplex.m_count;
for (int32 i = 0; i < saveCount; ++i)
{
saveA[i] = vertices[i].indexA;
saveB[i] = vertices[i].indexB;
}
switch (simplex.m_count)
{
case 1:
break;
case 2:
simplex.Solve2();
break;
case 3:
simplex.Solve3();
break;
default:
b2Assert(false);
}
// If we have 3 points, then the origin is in the corresponding triangle.
if (simplex.m_count == 3)
{
break;
}
// Compute closest point.
b2Vec2 p = simplex.GetClosestPoint();
distanceSqr2 = p.LengthSquared();
// Ensure progress
if (distanceSqr2 >= distanceSqr1)
{
//break;
}
distanceSqr1 = distanceSqr2;
// Get search direction.
b2Vec2 d = simplex.GetSearchDirection();
// Ensure the search direction is numerically fit.
if (d.LengthSquared() < b2_epsilon * b2_epsilon)
{
// The origin is probably contained by a line segment
// or triangle. Thus the shapes are overlapped.
// We can't return zero here even though there may be overlap.
// In case the simplex is a point, segment, or triangle it is difficult
// to determine if the origin is contained in the CSO or very close to it.
break;
}
// Compute a tentative new simplex vertex using support points.
b2SimplexVertex* vertex = vertices + simplex.m_count;
vertex->indexA = proxyA->GetSupport(b2MulT(transformA.q, -d));
vertex->wA = b2Mul(transformA, proxyA->GetVertex(vertex->indexA));
b2Vec2 wBLocal;
vertex->indexB = proxyB->GetSupport(b2MulT(transformB.q, d));
vertex->wB = b2Mul(transformB, proxyB->GetVertex(vertex->indexB));
vertex->w = vertex->wB - vertex->wA;
// Iteration count is equated to the number of support point calls.
++iter;
++b2_gjkIters;
// Check for duplicate support points. This is the main termination criteria.
bool duplicate = false;
for (int32 i = 0; i < saveCount; ++i)
{
if (vertex->indexA == saveA[i] && vertex->indexB == saveB[i])
{
duplicate = true;
break;
}
}
// If we found a duplicate support point we must exit to avoid cycling.
if (duplicate)
{
break;
}
// New vertex is ok and needed.
++simplex.m_count;
}
b2_gjkMaxIters = b2Max(b2_gjkMaxIters, iter);
// Prepare output.
simplex.GetWitnessPoints(&output->pointA, &output->pointB);
output->distance = b2Distance(output->pointA, output->pointB);
output->iterations = iter;
// Cache the simplex.
simplex.WriteCache(cache);
// Apply radii if requested.
if (input->useRadii)
{
float32 rA = proxyA->m_radius;
float32 rB = proxyB->m_radius;
if (output->distance > rA + rB && output->distance > b2_epsilon)
{
// Shapes are still no overlapped.
// Move the witness points to the outer surface.
output->distance -= rA + rB;
b2Vec2 normal = output->pointB - output->pointA;
normal.Normalize();
output->pointA += rA * normal;
output->pointB -= rB * normal;
}
else
{
// Shapes are overlapped when radii are considered.
// Move the witness points to the middle.
b2Vec2 p = 0.5f * (output->pointA + output->pointB);
output->pointA = p;
output->pointB = p;
output->distance = 0.0f;
}
}
}
+141
View File
@@ -0,0 +1,141 @@
/*
* Copyright (c) 2006-2009 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.
*/
#ifndef B2_DISTANCE_H
#define B2_DISTANCE_H
#include <Box2D/Common/b2Math.h>
class b2Shape;
/// A distance proxy is used by the GJK algorithm.
/// It encapsulates any shape.
struct b2DistanceProxy
{
b2DistanceProxy() : m_vertices(NULL), m_count(0), m_radius(0.0f) {}
/// Initialize the proxy using the given shape. The shape
/// must remain in scope while the proxy is in use.
void Set(const b2Shape* shape, int32 index);
/// Get the supporting vertex index in the given direction.
int32 GetSupport(const b2Vec2& d) const;
/// Get the supporting vertex in the given direction.
const b2Vec2& GetSupportVertex(const b2Vec2& d) const;
/// Get the vertex count.
int32 GetVertexCount() const;
/// Get a vertex by index. Used by b2Distance.
const b2Vec2& GetVertex(int32 index) const;
b2Vec2 m_buffer[2];
const b2Vec2* m_vertices;
int32 m_count;
float32 m_radius;
};
/// Used to warm start b2Distance.
/// Set count to zero on first call.
struct b2SimplexCache
{
float32 metric; ///< length or area
uint16 count;
uint8 indexA[3]; ///< vertices on shape A
uint8 indexB[3]; ///< vertices on shape B
};
/// Input for b2Distance.
/// You have to option to use the shape radii
/// in the computation. Even
struct b2DistanceInput
{
b2DistanceProxy proxyA;
b2DistanceProxy proxyB;
b2Transform transformA;
b2Transform transformB;
bool useRadii;
};
/// Output for b2Distance.
struct b2DistanceOutput
{
b2Vec2 pointA; ///< closest point on shapeA
b2Vec2 pointB; ///< closest point on shapeB
float32 distance;
int32 iterations; ///< number of GJK iterations used
};
/// Compute the closest points between two shapes. Supports any combination of:
/// b2CircleShape, b2PolygonShape, b2EdgeShape. The simplex cache is input/output.
/// On the first call set b2SimplexCache.count to zero.
void b2Distance(b2DistanceOutput* output,
b2SimplexCache* cache,
const b2DistanceInput* input);
//////////////////////////////////////////////////////////////////////////
inline int32 b2DistanceProxy::GetVertexCount() const
{
return m_count;
}
inline const b2Vec2& b2DistanceProxy::GetVertex(int32 index) const
{
b2Assert(0 <= index && index < m_count);
return m_vertices[index];
}
inline int32 b2DistanceProxy::GetSupport(const b2Vec2& d) const
{
int32 bestIndex = 0;
float32 bestValue = b2Dot(m_vertices[0], d);
for (int32 i = 1; i < m_count; ++i)
{
float32 value = b2Dot(m_vertices[i], d);
if (value > bestValue)
{
bestIndex = i;
bestValue = value;
}
}
return bestIndex;
}
inline const b2Vec2& b2DistanceProxy::GetSupportVertex(const b2Vec2& d) const
{
int32 bestIndex = 0;
float32 bestValue = b2Dot(m_vertices[0], d);
for (int32 i = 1; i < m_count; ++i)
{
float32 value = b2Dot(m_vertices[i], d);
if (value > bestValue)
{
bestIndex = i;
bestValue = value;
}
}
return m_vertices[bestIndex];
}
#endif
+784
View File
@@ -0,0 +1,784 @@
/*
* Copyright (c) 2009 Erin Catto http://www.box2d.org
* Copyright (c) 2014 Google, Inc.
*
* 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/Collision/b2DynamicTree.h>
#include <memory.h>
#include <string.h>
b2DynamicTree::b2DynamicTree()
{
m_root = b2_nullNode;
m_nodeCapacity = 16;
m_nodeCount = 0;
m_nodes = (b2TreeNode*)b2Alloc(m_nodeCapacity * sizeof(b2TreeNode));
memset(m_nodes, 0, m_nodeCapacity * sizeof(b2TreeNode));
// Build a linked list for the free list.
for (int32 i = 0; i < m_nodeCapacity - 1; ++i)
{
m_nodes[i].next = i + 1;
m_nodes[i].height = -1;
}
m_nodes[m_nodeCapacity-1].next = b2_nullNode;
m_nodes[m_nodeCapacity-1].height = -1;
m_freeList = 0;
m_path = 0;
m_insertionCount = 0;
}
b2DynamicTree::~b2DynamicTree()
{
// This frees the entire tree in one shot.
b2Free(m_nodes);
}
// Allocate a node from the pool. Grow the pool if necessary.
int32 b2DynamicTree::AllocateNode()
{
// Expand the node pool as needed.
if (m_freeList == b2_nullNode)
{
b2Assert(m_nodeCount == m_nodeCapacity);
// The free list is empty. Rebuild a bigger pool.
b2TreeNode* oldNodes = m_nodes;
m_nodeCapacity *= 2;
m_nodes = (b2TreeNode*)b2Alloc(m_nodeCapacity * sizeof(b2TreeNode));
memcpy(m_nodes, oldNodes, m_nodeCount * sizeof(b2TreeNode));
b2Free(oldNodes);
// Build a linked list for the free list. The parent
// pointer becomes the "next" pointer.
for (int32 i = m_nodeCount; i < m_nodeCapacity - 1; ++i)
{
m_nodes[i].next = i + 1;
m_nodes[i].height = -1;
}
m_nodes[m_nodeCapacity-1].next = b2_nullNode;
m_nodes[m_nodeCapacity-1].height = -1;
m_freeList = m_nodeCount;
}
// Peel a node off the free list.
int32 nodeId = m_freeList;
m_freeList = m_nodes[nodeId].next;
m_nodes[nodeId].parent = b2_nullNode;
m_nodes[nodeId].child1 = b2_nullNode;
m_nodes[nodeId].child2 = b2_nullNode;
m_nodes[nodeId].height = 0;
m_nodes[nodeId].userData = NULL;
++m_nodeCount;
return nodeId;
}
// Return a node to the pool.
void b2DynamicTree::FreeNode(int32 nodeId)
{
b2Assert(0 <= nodeId && nodeId < m_nodeCapacity);
b2Assert(0 < m_nodeCount);
m_nodes[nodeId].next = m_freeList;
m_nodes[nodeId].height = -1;
m_freeList = nodeId;
--m_nodeCount;
}
// Create a proxy in the tree as a leaf node. We return the index
// of the node instead of a pointer so that we can grow
// the node pool.
int32 b2DynamicTree::CreateProxy(const b2AABB& aabb, void* userData)
{
int32 proxyId = AllocateNode();
// Fatten the aabb.
b2Vec2 r(b2_aabbExtension, b2_aabbExtension);
m_nodes[proxyId].aabb.lowerBound = aabb.lowerBound - r;
m_nodes[proxyId].aabb.upperBound = aabb.upperBound + r;
m_nodes[proxyId].userData = userData;
m_nodes[proxyId].height = 0;
InsertLeaf(proxyId);
return proxyId;
}
void b2DynamicTree::DestroyProxy(int32 proxyId)
{
b2Assert(0 <= proxyId && proxyId < m_nodeCapacity);
b2Assert(m_nodes[proxyId].IsLeaf());
RemoveLeaf(proxyId);
FreeNode(proxyId);
}
bool b2DynamicTree::MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement)
{
b2Assert(0 <= proxyId && proxyId < m_nodeCapacity);
b2Assert(m_nodes[proxyId].IsLeaf());
if (m_nodes[proxyId].aabb.Contains(aabb))
{
return false;
}
RemoveLeaf(proxyId);
// Extend AABB.
b2AABB b = aabb;
b2Vec2 r(b2_aabbExtension, b2_aabbExtension);
b.lowerBound = b.lowerBound - r;
b.upperBound = b.upperBound + r;
// Predict AABB displacement.
b2Vec2 d = b2_aabbMultiplier * displacement;
if (d.x < 0.0f)
{
b.lowerBound.x += d.x;
}
else
{
b.upperBound.x += d.x;
}
if (d.y < 0.0f)
{
b.lowerBound.y += d.y;
}
else
{
b.upperBound.y += d.y;
}
m_nodes[proxyId].aabb = b;
InsertLeaf(proxyId);
return true;
}
void b2DynamicTree::InsertLeaf(int32 leaf)
{
++m_insertionCount;
if (m_root == b2_nullNode)
{
m_root = leaf;
m_nodes[m_root].parent = b2_nullNode;
return;
}
// Find the best sibling for this node
b2AABB leafAABB = m_nodes[leaf].aabb;
int32 index = m_root;
while (m_nodes[index].IsLeaf() == false)
{
int32 child1 = m_nodes[index].child1;
int32 child2 = m_nodes[index].child2;
float32 area = m_nodes[index].aabb.GetPerimeter();
b2AABB combinedAABB;
combinedAABB.Combine(m_nodes[index].aabb, leafAABB);
float32 combinedArea = combinedAABB.GetPerimeter();
// Cost of creating a new parent for this node and the new leaf
float32 cost = 2.0f * combinedArea;
// Minimum cost of pushing the leaf further down the tree
float32 inheritanceCost = 2.0f * (combinedArea - area);
// Cost of descending into child1
float32 cost1;
if (m_nodes[child1].IsLeaf())
{
b2AABB aabb;
aabb.Combine(leafAABB, m_nodes[child1].aabb);
cost1 = aabb.GetPerimeter() + inheritanceCost;
}
else
{
b2AABB aabb;
aabb.Combine(leafAABB, m_nodes[child1].aabb);
float32 oldArea = m_nodes[child1].aabb.GetPerimeter();
float32 newArea = aabb.GetPerimeter();
cost1 = (newArea - oldArea) + inheritanceCost;
}
// Cost of descending into child2
float32 cost2;
if (m_nodes[child2].IsLeaf())
{
b2AABB aabb;
aabb.Combine(leafAABB, m_nodes[child2].aabb);
cost2 = aabb.GetPerimeter() + inheritanceCost;
}
else
{
b2AABB aabb;
aabb.Combine(leafAABB, m_nodes[child2].aabb);
float32 oldArea = m_nodes[child2].aabb.GetPerimeter();
float32 newArea = aabb.GetPerimeter();
cost2 = newArea - oldArea + inheritanceCost;
}
// Descend according to the minimum cost.
if (cost < cost1 && cost < cost2)
{
break;
}
// Descend
if (cost1 < cost2)
{
index = child1;
}
else
{
index = child2;
}
}
int32 sibling = index;
// Create a new parent.
int32 oldParent = m_nodes[sibling].parent;
int32 newParent = AllocateNode();
m_nodes[newParent].parent = oldParent;
m_nodes[newParent].userData = NULL;
m_nodes[newParent].aabb.Combine(leafAABB, m_nodes[sibling].aabb);
m_nodes[newParent].height = m_nodes[sibling].height + 1;
if (oldParent != b2_nullNode)
{
// The sibling was not the root.
if (m_nodes[oldParent].child1 == sibling)
{
m_nodes[oldParent].child1 = newParent;
}
else
{
m_nodes[oldParent].child2 = newParent;
}
m_nodes[newParent].child1 = sibling;
m_nodes[newParent].child2 = leaf;
m_nodes[sibling].parent = newParent;
m_nodes[leaf].parent = newParent;
}
else
{
// The sibling was the root.
m_nodes[newParent].child1 = sibling;
m_nodes[newParent].child2 = leaf;
m_nodes[sibling].parent = newParent;
m_nodes[leaf].parent = newParent;
m_root = newParent;
}
// Walk back up the tree fixing heights and AABBs
index = m_nodes[leaf].parent;
while (index != b2_nullNode)
{
index = Balance(index);
int32 child1 = m_nodes[index].child1;
int32 child2 = m_nodes[index].child2;
b2Assert(child1 != b2_nullNode);
b2Assert(child2 != b2_nullNode);
m_nodes[index].height = 1 + b2Max(m_nodes[child1].height, m_nodes[child2].height);
m_nodes[index].aabb.Combine(m_nodes[child1].aabb, m_nodes[child2].aabb);
index = m_nodes[index].parent;
}
//Validate();
}
void b2DynamicTree::RemoveLeaf(int32 leaf)
{
if (leaf == m_root)
{
m_root = b2_nullNode;
return;
}
int32 parent = m_nodes[leaf].parent;
int32 grandParent = m_nodes[parent].parent;
int32 sibling;
if (m_nodes[parent].child1 == leaf)
{
sibling = m_nodes[parent].child2;
}
else
{
sibling = m_nodes[parent].child1;
}
if (grandParent != b2_nullNode)
{
// Destroy parent and connect sibling to grandParent.
if (m_nodes[grandParent].child1 == parent)
{
m_nodes[grandParent].child1 = sibling;
}
else
{
m_nodes[grandParent].child2 = sibling;
}
m_nodes[sibling].parent = grandParent;
FreeNode(parent);
// Adjust ancestor bounds.
int32 index = grandParent;
while (index != b2_nullNode)
{
index = Balance(index);
int32 child1 = m_nodes[index].child1;
int32 child2 = m_nodes[index].child2;
m_nodes[index].aabb.Combine(m_nodes[child1].aabb, m_nodes[child2].aabb);
m_nodes[index].height = 1 + b2Max(m_nodes[child1].height, m_nodes[child2].height);
index = m_nodes[index].parent;
}
}
else
{
m_root = sibling;
m_nodes[sibling].parent = b2_nullNode;
FreeNode(parent);
}
//Validate();
}
// Perform a left or right rotation if node A is imbalanced.
// Returns the new root index.
int32 b2DynamicTree::Balance(int32 iA)
{
b2Assert(iA != b2_nullNode);
b2TreeNode* A = m_nodes + iA;
if (A->IsLeaf() || A->height < 2)
{
return iA;
}
int32 iB = A->child1;
int32 iC = A->child2;
b2Assert(0 <= iB && iB < m_nodeCapacity);
b2Assert(0 <= iC && iC < m_nodeCapacity);
b2TreeNode* B = m_nodes + iB;
b2TreeNode* C = m_nodes + iC;
int32 balance = C->height - B->height;
// Rotate C up
if (balance > 1)
{
int32 iF = C->child1;
int32 iG = C->child2;
b2TreeNode* F = m_nodes + iF;
b2TreeNode* G = m_nodes + iG;
b2Assert(0 <= iF && iF < m_nodeCapacity);
b2Assert(0 <= iG && iG < m_nodeCapacity);
// Swap A and C
C->child1 = iA;
C->parent = A->parent;
A->parent = iC;
// A's old parent should point to C
if (C->parent != b2_nullNode)
{
if (m_nodes[C->parent].child1 == iA)
{
m_nodes[C->parent].child1 = iC;
}
else
{
b2Assert(m_nodes[C->parent].child2 == iA);
m_nodes[C->parent].child2 = iC;
}
}
else
{
m_root = iC;
}
// Rotate
if (F->height > G->height)
{
C->child2 = iF;
A->child2 = iG;
G->parent = iA;
A->aabb.Combine(B->aabb, G->aabb);
C->aabb.Combine(A->aabb, F->aabb);
A->height = 1 + b2Max(B->height, G->height);
C->height = 1 + b2Max(A->height, F->height);
}
else
{
C->child2 = iG;
A->child2 = iF;
F->parent = iA;
A->aabb.Combine(B->aabb, F->aabb);
C->aabb.Combine(A->aabb, G->aabb);
A->height = 1 + b2Max(B->height, F->height);
C->height = 1 + b2Max(A->height, G->height);
}
return iC;
}
// Rotate B up
if (balance < -1)
{
int32 iD = B->child1;
int32 iE = B->child2;
b2TreeNode* D = m_nodes + iD;
b2TreeNode* E = m_nodes + iE;
b2Assert(0 <= iD && iD < m_nodeCapacity);
b2Assert(0 <= iE && iE < m_nodeCapacity);
// Swap A and B
B->child1 = iA;
B->parent = A->parent;
A->parent = iB;
// A's old parent should point to B
if (B->parent != b2_nullNode)
{
if (m_nodes[B->parent].child1 == iA)
{
m_nodes[B->parent].child1 = iB;
}
else
{
b2Assert(m_nodes[B->parent].child2 == iA);
m_nodes[B->parent].child2 = iB;
}
}
else
{
m_root = iB;
}
// Rotate
if (D->height > E->height)
{
B->child2 = iD;
A->child1 = iE;
E->parent = iA;
A->aabb.Combine(C->aabb, E->aabb);
B->aabb.Combine(A->aabb, D->aabb);
A->height = 1 + b2Max(C->height, E->height);
B->height = 1 + b2Max(A->height, D->height);
}
else
{
B->child2 = iE;
A->child1 = iD;
D->parent = iA;
A->aabb.Combine(C->aabb, D->aabb);
B->aabb.Combine(A->aabb, E->aabb);
A->height = 1 + b2Max(C->height, D->height);
B->height = 1 + b2Max(A->height, E->height);
}
return iB;
}
return iA;
}
int32 b2DynamicTree::GetHeight() const
{
if (m_root == b2_nullNode)
{
return 0;
}
return m_nodes[m_root].height;
}
//
float32 b2DynamicTree::GetAreaRatio() const
{
if (m_root == b2_nullNode)
{
return 0.0f;
}
const b2TreeNode* root = m_nodes + m_root;
float32 rootArea = root->aabb.GetPerimeter();
float32 totalArea = 0.0f;
for (int32 i = 0; i < m_nodeCapacity; ++i)
{
const b2TreeNode* node = m_nodes + i;
if (node->height < 0)
{
// Free node in pool
continue;
}
totalArea += node->aabb.GetPerimeter();
}
return totalArea / rootArea;
}
// Compute the height of a sub-tree.
int32 b2DynamicTree::ComputeHeight(int32 nodeId) const
{
b2Assert(0 <= nodeId && nodeId < m_nodeCapacity);
b2TreeNode* node = m_nodes + nodeId;
if (node->IsLeaf())
{
return 0;
}
int32 height1 = ComputeHeight(node->child1);
int32 height2 = ComputeHeight(node->child2);
return 1 + b2Max(height1, height2);
}
int32 b2DynamicTree::ComputeHeight() const
{
int32 height = ComputeHeight(m_root);
return height;
}
void b2DynamicTree::ValidateStructure(int32 index) const
{
if (index == b2_nullNode)
{
return;
}
if (index == m_root)
{
b2Assert(m_nodes[index].parent == b2_nullNode);
}
const b2TreeNode* node = m_nodes + index;
#if B2_ASSERT_ENABLED || DEBUG
int32 child1 = node->child1;
int32 child2 = node->child2;
#endif // B2_ASSERT_ENABLED || DEBUG
if (node->IsLeaf())
{
b2Assert(child1 == b2_nullNode);
b2Assert(child2 == b2_nullNode);
b2Assert(node->height == 0);
return;
}
b2Assert(0 <= child1 && child1 < m_nodeCapacity);
b2Assert(0 <= child2 && child2 < m_nodeCapacity);
b2Assert(m_nodes[child1].parent == index);
b2Assert(m_nodes[child2].parent == index);
B2_DEBUG_STATEMENT(ValidateStructure(child1));
B2_DEBUG_STATEMENT(ValidateStructure(child2));
}
void b2DynamicTree::ValidateMetrics(int32 index) const
{
if (index == b2_nullNode)
{
return;
}
const b2TreeNode* node = m_nodes + index;
int32 child1 = node->child1;
int32 child2 = node->child2;
if (node->IsLeaf())
{
b2Assert(child1 == b2_nullNode);
b2Assert(child2 == b2_nullNode);
b2Assert(node->height == 0);
return;
}
b2Assert(0 <= child1 && child1 < m_nodeCapacity);
b2Assert(0 <= child2 && child2 < m_nodeCapacity);
#if B2_ASSERT_ENABLED
int32 height1 = m_nodes[child1].height;
int32 height2 = m_nodes[child2].height;
int32 height;
height = 1 + b2Max(height1, height2);
#endif // B2_ASSERT_ENABLED
b2Assert(node->height == height);
b2AABB aabb;
aabb.Combine(m_nodes[child1].aabb, m_nodes[child2].aabb);
b2Assert(aabb.lowerBound == node->aabb.lowerBound);
b2Assert(aabb.upperBound == node->aabb.upperBound);
ValidateMetrics(child1);
ValidateMetrics(child2);
}
void b2DynamicTree::Validate() const
{
B2_DEBUG_STATEMENT(ValidateStructure(m_root));
B2_DEBUG_STATEMENT(ValidateMetrics(m_root));
int32 freeCount = 0;
int32 freeIndex = m_freeList;
while (freeIndex != b2_nullNode)
{
b2Assert(0 <= freeIndex && freeIndex < m_nodeCapacity);
freeIndex = m_nodes[freeIndex].next;
++freeCount;
}
b2Assert(GetHeight() == ComputeHeight());
b2Assert(m_nodeCount + freeCount == m_nodeCapacity);
}
int32 b2DynamicTree::GetMaxBalance() const
{
int32 maxBalance = 0;
for (int32 i = 0; i < m_nodeCapacity; ++i)
{
const b2TreeNode* node = m_nodes + i;
if (node->height <= 1)
{
continue;
}
b2Assert(node->IsLeaf() == false);
int32 child1 = node->child1;
int32 child2 = node->child2;
int32 balance = b2Abs(m_nodes[child2].height - m_nodes[child1].height);
maxBalance = b2Max(maxBalance, balance);
}
return maxBalance;
}
void b2DynamicTree::RebuildBottomUp()
{
int32* nodes = (int32*)b2Alloc(m_nodeCount * sizeof(int32));
int32 count = 0;
// Build array of leaves. Free the rest.
for (int32 i = 0; i < m_nodeCapacity; ++i)
{
if (m_nodes[i].height < 0)
{
// free node in pool
continue;
}
if (m_nodes[i].IsLeaf())
{
m_nodes[i].parent = b2_nullNode;
nodes[count] = i;
++count;
}
else
{
FreeNode(i);
}
}
while (count > 1)
{
float32 minCost = b2_maxFloat;
int32 iMin = -1, jMin = -1;
for (int32 i = 0; i < count; ++i)
{
b2AABB aabbi = m_nodes[nodes[i]].aabb;
for (int32 j = i + 1; j < count; ++j)
{
b2AABB aabbj = m_nodes[nodes[j]].aabb;
b2AABB b;
b.Combine(aabbi, aabbj);
float32 cost = b.GetPerimeter();
if (cost < minCost)
{
iMin = i;
jMin = j;
minCost = cost;
}
}
}
int32 index1 = nodes[iMin];
int32 index2 = nodes[jMin];
b2TreeNode* child1 = m_nodes + index1;
b2TreeNode* child2 = m_nodes + index2;
int32 parentIndex = AllocateNode();
b2TreeNode* parent = m_nodes + parentIndex;
parent->child1 = index1;
parent->child2 = index2;
parent->height = 1 + b2Max(child1->height, child2->height);
parent->aabb.Combine(child1->aabb, child2->aabb);
parent->parent = b2_nullNode;
child1->parent = parentIndex;
child2->parent = parentIndex;
nodes[jMin] = nodes[count-1];
nodes[iMin] = parentIndex;
--count;
}
m_root = nodes[0];
b2Free(nodes);
B2_DEBUG_STATEMENT(Validate());
}
void b2DynamicTree::ShiftOrigin(const b2Vec2& newOrigin)
{
// Build array of leaves. Free the rest.
for (int32 i = 0; i < m_nodeCapacity; ++i)
{
m_nodes[i].aabb.lowerBound -= newOrigin;
m_nodes[i].aabb.upperBound -= newOrigin;
}
}
+289
View File
@@ -0,0 +1,289 @@
/*
* Copyright (c) 2009 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.
*/
#ifndef B2_DYNAMIC_TREE_H
#define B2_DYNAMIC_TREE_H
#include <Box2D/Collision/b2Collision.h>
#include <Box2D/Common/b2GrowableStack.h>
#define b2_nullNode (-1)
/// A node in the dynamic tree. The client does not interact with this directly.
struct b2TreeNode
{
bool IsLeaf() const
{
return child1 == b2_nullNode;
}
/// Enlarged AABB
b2AABB aabb;
void* userData;
union
{
int32 parent;
int32 next;
};
int32 child1;
int32 child2;
// leaf = 0, free node = -1
int32 height;
};
/// A dynamic AABB tree broad-phase, inspired by Nathanael Presson's btDbvt.
/// A dynamic tree arranges data in a binary tree to accelerate
/// queries such as volume queries and ray casts. Leafs are proxies
/// with an AABB. In the tree we expand the proxy AABB by b2_fatAABBFactor
/// so that the proxy AABB is bigger than the client object. This allows the client
/// object to move by small amounts without triggering a tree update.
///
/// Nodes are pooled and relocatable, so we use node indices rather than pointers.
class b2DynamicTree
{
public:
/// Constructing the tree initializes the node pool.
b2DynamicTree();
/// Destroy the tree, freeing the node pool.
~b2DynamicTree();
/// Create a proxy. Provide a tight fitting AABB and a userData pointer.
int32 CreateProxy(const b2AABB& aabb, void* userData);
/// Destroy a proxy. This asserts if the id is invalid.
void DestroyProxy(int32 proxyId);
/// Move a proxy with a swepted AABB. If the proxy has moved outside of its fattened AABB,
/// then the proxy is removed from the tree and re-inserted. Otherwise
/// the function returns immediately.
/// @return true if the proxy was re-inserted.
bool MoveProxy(int32 proxyId, const b2AABB& aabb1, const b2Vec2& displacement);
/// Get proxy user data.
/// @return the proxy user data or 0 if the id is invalid.
void* GetUserData(int32 proxyId) const;
/// Get the fat AABB for a proxy.
const b2AABB& GetFatAABB(int32 proxyId) const;
/// Query an AABB for overlapping proxies. The callback class
/// is called for each proxy that overlaps the supplied AABB.
template <typename T>
void Query(T* callback, const b2AABB& aabb) const;
/// Ray-cast against the proxies in the tree. This relies on the callback
/// to perform a exact ray-cast in the case were the proxy contains a shape.
/// The callback also performs the any collision filtering. This has performance
/// roughly equal to k * log(n), where k is the number of collisions and n is the
/// number of proxies in the tree.
/// @param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
/// @param callback a callback class that is called for each proxy that is hit by the ray.
template <typename T>
void RayCast(T* callback, const b2RayCastInput& input) const;
/// Validate this tree. For testing.
void Validate() const;
/// Compute the height of the binary tree in O(N) time. Should not be
/// called often.
int32 GetHeight() const;
/// Get the maximum balance of an node in the tree. The balance is the difference
/// in height of the two children of a node.
int32 GetMaxBalance() const;
/// Get the ratio of the sum of the node areas to the root area.
float32 GetAreaRatio() const;
/// Build an optimal tree. Very expensive. For testing.
void RebuildBottomUp();
/// Shift the world origin. Useful for large worlds.
/// The shift formula is: position -= newOrigin
/// @param newOrigin the new origin with respect to the old origin
void ShiftOrigin(const b2Vec2& newOrigin);
private:
int32 AllocateNode();
void FreeNode(int32 node);
void InsertLeaf(int32 node);
void RemoveLeaf(int32 node);
int32 Balance(int32 index);
int32 ComputeHeight() const;
int32 ComputeHeight(int32 nodeId) const;
void ValidateStructure(int32 index) const;
void ValidateMetrics(int32 index) const;
int32 m_root;
b2TreeNode* m_nodes;
int32 m_nodeCount;
int32 m_nodeCapacity;
int32 m_freeList;
/// This is used to incrementally traverse the tree for re-balancing.
uint32 m_path;
int32 m_insertionCount;
};
inline void* b2DynamicTree::GetUserData(int32 proxyId) const
{
b2Assert(0 <= proxyId && proxyId < m_nodeCapacity);
return m_nodes[proxyId].userData;
}
inline const b2AABB& b2DynamicTree::GetFatAABB(int32 proxyId) const
{
b2Assert(0 <= proxyId && proxyId < m_nodeCapacity);
return m_nodes[proxyId].aabb;
}
template <typename T>
inline void b2DynamicTree::Query(T* callback, const b2AABB& aabb) const
{
b2GrowableStack<int32, 256> stack;
stack.Push(m_root);
while (stack.GetCount() > 0)
{
int32 nodeId = stack.Pop();
if (nodeId == b2_nullNode)
{
continue;
}
const b2TreeNode* node = m_nodes + nodeId;
if (b2TestOverlap(node->aabb, aabb))
{
if (node->IsLeaf())
{
bool proceed = callback->QueryCallback(nodeId);
if (proceed == false)
{
return;
}
}
else
{
stack.Push(node->child1);
stack.Push(node->child2);
}
}
}
}
template <typename T>
inline void b2DynamicTree::RayCast(T* callback, const b2RayCastInput& input) const
{
b2Vec2 p1 = input.p1;
b2Vec2 p2 = input.p2;
b2Vec2 r = p2 - p1;
b2Assert(r.LengthSquared() > 0.0f);
r.Normalize();
// v is perpendicular to the segment.
b2Vec2 v = b2Cross(1.0f, r);
b2Vec2 abs_v = b2Abs(v);
// Separating axis for segment (Gino, p80).
// |dot(v, p1 - c)| > dot(|v|, h)
float32 maxFraction = input.maxFraction;
// Build a bounding box for the segment.
b2AABB segmentAABB;
{
b2Vec2 t = p1 + maxFraction * (p2 - p1);
segmentAABB.lowerBound = b2Min(p1, t);
segmentAABB.upperBound = b2Max(p1, t);
}
b2GrowableStack<int32, 256> stack;
stack.Push(m_root);
while (stack.GetCount() > 0)
{
int32 nodeId = stack.Pop();
if (nodeId == b2_nullNode)
{
continue;
}
const b2TreeNode* node = m_nodes + nodeId;
if (b2TestOverlap(node->aabb, segmentAABB) == false)
{
continue;
}
// Separating axis for segment (Gino, p80).
// |dot(v, p1 - c)| > dot(|v|, h)
b2Vec2 c = node->aabb.GetCenter();
b2Vec2 h = node->aabb.GetExtents();
float32 separation = b2Abs(b2Dot(v, p1 - c)) - b2Dot(abs_v, h);
if (separation > 0.0f)
{
continue;
}
if (node->IsLeaf())
{
b2RayCastInput subInput;
subInput.p1 = input.p1;
subInput.p2 = input.p2;
subInput.maxFraction = maxFraction;
float32 value = callback->RayCastCallback(subInput, nodeId);
if (value == 0.0f)
{
// The client has terminated the ray cast.
return;
}
if (value > 0.0f)
{
// Update segment bounding box.
maxFraction = value;
b2Vec2 t = p1 + maxFraction * (p2 - p1);
segmentAABB.lowerBound = b2Min(p1, t);
segmentAABB.upperBound = b2Max(p1, t);
}
}
else
{
stack.Push(node->child1);
stack.Push(node->child2);
}
}
}
#endif
+487
View File
@@ -0,0 +1,487 @@
/*
* Copyright (c) 2007-2009 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/Collision/b2Collision.h>
#include <Box2D/Collision/b2Distance.h>
#include <Box2D/Collision/b2TimeOfImpact.h>
#include <Box2D/Collision/Shapes/b2CircleShape.h>
#include <Box2D/Collision/Shapes/b2PolygonShape.h>
#include <Box2D/Common/b2Timer.h>
#include <stdio.h>
float32 b2_toiTime, b2_toiMaxTime;
int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters;
int32 b2_toiRootIters, b2_toiMaxRootIters;
//
struct b2SeparationFunction
{
enum Type
{
e_points,
e_faceA,
e_faceB
};
// TODO_ERIN might not need to return the separation
float32 Initialize(const b2SimplexCache* cache,
const b2DistanceProxy* proxyA, const b2Sweep& sweepA,
const b2DistanceProxy* proxyB, const b2Sweep& sweepB,
float32 t1)
{
m_proxyA = proxyA;
m_proxyB = proxyB;
int32 count = cache->count;
b2Assert(0 < count && count < 3);
m_sweepA = sweepA;
m_sweepB = sweepB;
b2Transform xfA, xfB;
m_sweepA.GetTransform(&xfA, t1);
m_sweepB.GetTransform(&xfB, t1);
if (count == 1)
{
m_type = e_points;
b2Vec2 localPointA = m_proxyA->GetVertex(cache->indexA[0]);
b2Vec2 localPointB = m_proxyB->GetVertex(cache->indexB[0]);
b2Vec2 pointA = b2Mul(xfA, localPointA);
b2Vec2 pointB = b2Mul(xfB, localPointB);
m_axis = pointB - pointA;
float32 s = m_axis.Normalize();
m_localPoint = b2Vec2_zero;
return s;
}
else if (cache->indexA[0] == cache->indexA[1])
{
// Two points on B and one on A.
m_type = e_faceB;
b2Vec2 localPointB1 = proxyB->GetVertex(cache->indexB[0]);
b2Vec2 localPointB2 = proxyB->GetVertex(cache->indexB[1]);
m_axis = b2Cross(localPointB2 - localPointB1, 1.0f);
m_axis.Normalize();
b2Vec2 normal = b2Mul(xfB.q, m_axis);
m_localPoint = 0.5f * (localPointB1 + localPointB2);
b2Vec2 pointB = b2Mul(xfB, m_localPoint);
b2Vec2 localPointA = proxyA->GetVertex(cache->indexA[0]);
b2Vec2 pointA = b2Mul(xfA, localPointA);
float32 s = b2Dot(pointA - pointB, normal);
if (s < 0.0f)
{
m_axis = -m_axis;
s = -s;
}
return s;
}
else
{
// Two points on A and one or two points on B.
m_type = e_faceA;
b2Vec2 localPointA1 = m_proxyA->GetVertex(cache->indexA[0]);
b2Vec2 localPointA2 = m_proxyA->GetVertex(cache->indexA[1]);
m_axis = b2Cross(localPointA2 - localPointA1, 1.0f);
m_axis.Normalize();
b2Vec2 normal = b2Mul(xfA.q, m_axis);
m_localPoint = 0.5f * (localPointA1 + localPointA2);
b2Vec2 pointA = b2Mul(xfA, m_localPoint);
b2Vec2 localPointB = m_proxyB->GetVertex(cache->indexB[0]);
b2Vec2 pointB = b2Mul(xfB, localPointB);
float32 s = b2Dot(pointB - pointA, normal);
if (s < 0.0f)
{
m_axis = -m_axis;
s = -s;
}
return s;
}
}
//
float32 FindMinSeparation(int32* indexA, int32* indexB, float32 t) const
{
b2Transform xfA, xfB;
m_sweepA.GetTransform(&xfA, t);
m_sweepB.GetTransform(&xfB, t);
switch (m_type)
{
case e_points:
{
b2Vec2 axisA = b2MulT(xfA.q, m_axis);
b2Vec2 axisB = b2MulT(xfB.q, -m_axis);
*indexA = m_proxyA->GetSupport(axisA);
*indexB = m_proxyB->GetSupport(axisB);
b2Vec2 localPointA = m_proxyA->GetVertex(*indexA);
b2Vec2 localPointB = m_proxyB->GetVertex(*indexB);
b2Vec2 pointA = b2Mul(xfA, localPointA);
b2Vec2 pointB = b2Mul(xfB, localPointB);
float32 separation = b2Dot(pointB - pointA, m_axis);
return separation;
}
case e_faceA:
{
b2Vec2 normal = b2Mul(xfA.q, m_axis);
b2Vec2 pointA = b2Mul(xfA, m_localPoint);
b2Vec2 axisB = b2MulT(xfB.q, -normal);
*indexA = -1;
*indexB = m_proxyB->GetSupport(axisB);
b2Vec2 localPointB = m_proxyB->GetVertex(*indexB);
b2Vec2 pointB = b2Mul(xfB, localPointB);
float32 separation = b2Dot(pointB - pointA, normal);
return separation;
}
case e_faceB:
{
b2Vec2 normal = b2Mul(xfB.q, m_axis);
b2Vec2 pointB = b2Mul(xfB, m_localPoint);
b2Vec2 axisA = b2MulT(xfA.q, -normal);
*indexB = -1;
*indexA = m_proxyA->GetSupport(axisA);
b2Vec2 localPointA = m_proxyA->GetVertex(*indexA);
b2Vec2 pointA = b2Mul(xfA, localPointA);
float32 separation = b2Dot(pointA - pointB, normal);
return separation;
}
default:
b2Assert(false);
*indexA = -1;
*indexB = -1;
return 0.0f;
}
}
//
float32 Evaluate(int32 indexA, int32 indexB, float32 t) const
{
b2Transform xfA, xfB;
m_sweepA.GetTransform(&xfA, t);
m_sweepB.GetTransform(&xfB, t);
switch (m_type)
{
case e_points:
{
b2Vec2 localPointA = m_proxyA->GetVertex(indexA);
b2Vec2 localPointB = m_proxyB->GetVertex(indexB);
b2Vec2 pointA = b2Mul(xfA, localPointA);
b2Vec2 pointB = b2Mul(xfB, localPointB);
float32 separation = b2Dot(pointB - pointA, m_axis);
return separation;
}
case e_faceA:
{
b2Vec2 normal = b2Mul(xfA.q, m_axis);
b2Vec2 pointA = b2Mul(xfA, m_localPoint);
b2Vec2 localPointB = m_proxyB->GetVertex(indexB);
b2Vec2 pointB = b2Mul(xfB, localPointB);
float32 separation = b2Dot(pointB - pointA, normal);
return separation;
}
case e_faceB:
{
b2Vec2 normal = b2Mul(xfB.q, m_axis);
b2Vec2 pointB = b2Mul(xfB, m_localPoint);
b2Vec2 localPointA = m_proxyA->GetVertex(indexA);
b2Vec2 pointA = b2Mul(xfA, localPointA);
float32 separation = b2Dot(pointA - pointB, normal);
return separation;
}
default:
b2Assert(false);
return 0.0f;
}
}
const b2DistanceProxy* m_proxyA;
const b2DistanceProxy* m_proxyB;
b2Sweep m_sweepA, m_sweepB;
Type m_type;
b2Vec2 m_localPoint;
b2Vec2 m_axis;
};
// CCD via the local separating axis method. This seeks progression
// by computing the largest time at which separation is maintained.
void b2TimeOfImpact(b2TOIOutput* output, const b2TOIInput* input)
{
b2Timer timer;
++b2_toiCalls;
output->state = b2TOIOutput::e_unknown;
output->t = input->tMax;
const b2DistanceProxy* proxyA = &input->proxyA;
const b2DistanceProxy* proxyB = &input->proxyB;
b2Sweep sweepA = input->sweepA;
b2Sweep sweepB = input->sweepB;
// Large rotations can make the root finder fail, so we normalize the
// sweep angles.
sweepA.Normalize();
sweepB.Normalize();
float32 tMax = input->tMax;
float32 totalRadius = proxyA->m_radius + proxyB->m_radius;
float32 target = b2Max(b2_linearSlop, totalRadius - 3.0f * b2_linearSlop);
float32 tolerance = 0.25f * b2_linearSlop;
b2Assert(target > tolerance);
float32 t1 = 0.0f;
const int32 k_maxIterations = 20; // TODO_ERIN b2Settings
int32 iter = 0;
// Prepare input for distance query.
b2SimplexCache cache;
cache.count = 0;
b2DistanceInput distanceInput;
distanceInput.proxyA = input->proxyA;
distanceInput.proxyB = input->proxyB;
distanceInput.useRadii = false;
// The outer loop progressively attempts to compute new separating axes.
// This loop terminates when an axis is repeated (no progress is made).
for(;;)
{
b2Transform xfA, xfB;
sweepA.GetTransform(&xfA, t1);
sweepB.GetTransform(&xfB, t1);
// Get the distance between shapes. We can also use the results
// to get a separating axis.
distanceInput.transformA = xfA;
distanceInput.transformB = xfB;
b2DistanceOutput distanceOutput;
b2Distance(&distanceOutput, &cache, &distanceInput);
// If the shapes are overlapped, we give up on continuous collision.
if (distanceOutput.distance <= 0.0f)
{
// Failure!
output->state = b2TOIOutput::e_overlapped;
output->t = 0.0f;
break;
}
if (distanceOutput.distance < target + tolerance)
{
// Victory!
output->state = b2TOIOutput::e_touching;
output->t = t1;
break;
}
// Initialize the separating axis.
b2SeparationFunction fcn;
fcn.Initialize(&cache, proxyA, sweepA, proxyB, sweepB, t1);
#if 0
// Dump the curve seen by the root finder
{
const int32 N = 100;
float32 dx = 1.0f / N;
float32 xs[N+1];
float32 fs[N+1];
float32 x = 0.0f;
for (int32 i = 0; i <= N; ++i)
{
sweepA.GetTransform(&xfA, x);
sweepB.GetTransform(&xfB, x);
float32 f = fcn.Evaluate(xfA, xfB) - target;
printf("%g %g\n", x, f);
xs[i] = x;
fs[i] = f;
x += dx;
}
}
#endif
// Compute the TOI on the separating axis. We do this by successively
// resolving the deepest point. This loop is bounded by the number of vertices.
bool done = false;
float32 t2 = tMax;
int32 pushBackIter = 0;
for (;;)
{
// Find the deepest point at t2. Store the witness point indices.
int32 indexA, indexB;
float32 s2 = fcn.FindMinSeparation(&indexA, &indexB, t2);
// Is the final configuration separated?
if (s2 > target + tolerance)
{
// Victory!
output->state = b2TOIOutput::e_separated;
output->t = tMax;
done = true;
break;
}
// Has the separation reached tolerance?
if (s2 > target - tolerance)
{
// Advance the sweeps
t1 = t2;
break;
}
// Compute the initial separation of the witness points.
float32 s1 = fcn.Evaluate(indexA, indexB, t1);
// Check for initial overlap. This might happen if the root finder
// runs out of iterations.
if (s1 < target - tolerance)
{
output->state = b2TOIOutput::e_failed;
output->t = t1;
done = true;
break;
}
// Check for touching
if (s1 <= target + tolerance)
{
// Victory! t1 should hold the TOI (could be 0.0).
output->state = b2TOIOutput::e_touching;
output->t = t1;
done = true;
break;
}
// Compute 1D root of: f(x) - target = 0
int32 rootIterCount = 0;
float32 a1 = t1, a2 = t2;
for (;;)
{
// Use a mix of the secant rule and bisection.
float32 t;
if (rootIterCount & 1)
{
// Secant rule to improve convergence.
t = a1 + (target - s1) * (a2 - a1) / (s2 - s1);
}
else
{
// Bisection to guarantee progress.
t = 0.5f * (a1 + a2);
}
++rootIterCount;
++b2_toiRootIters;
float32 s = fcn.Evaluate(indexA, indexB, t);
if (b2Abs(s - target) < tolerance)
{
// t2 holds a tentative value for t1
t2 = t;
break;
}
// Ensure we continue to bracket the root.
if (s > target)
{
a1 = t;
s1 = s;
}
else
{
a2 = t;
s2 = s;
}
if (rootIterCount == 50)
{
break;
}
}
b2_toiMaxRootIters = b2Max(b2_toiMaxRootIters, rootIterCount);
++pushBackIter;
if (pushBackIter == b2_maxPolygonVertices)
{
break;
}
}
++iter;
++b2_toiIters;
if (done)
{
break;
}
if (iter == k_maxIterations)
{
// Root finder got stuck. Semi-victory.
output->state = b2TOIOutput::e_failed;
output->t = t1;
break;
}
}
b2_toiMaxIters = b2Max(b2_toiMaxIters, iter);
float32 time = timer.GetMilliseconds();
b2_toiMaxTime = b2Max(b2_toiMaxTime, time);
b2_toiTime += time;
}
+58
View File
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2006-2009 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.
*/
#ifndef B2_TIME_OF_IMPACT_H
#define B2_TIME_OF_IMPACT_H
#include <Box2D/Common/b2Math.h>
#include <Box2D/Collision/b2Distance.h>
/// Input parameters for b2TimeOfImpact
struct b2TOIInput
{
b2DistanceProxy proxyA;
b2DistanceProxy proxyB;
b2Sweep sweepA;
b2Sweep sweepB;
float32 tMax; // defines sweep interval [0, tMax]
};
// Output parameters for b2TimeOfImpact.
struct b2TOIOutput
{
enum State
{
e_unknown,
e_failed,
e_overlapped,
e_touching,
e_separated
};
State state;
float32 t;
};
/// Compute the upper bound on time before two shapes penetrate. Time is represented as
/// a fraction between [0,tMax]. This uses a swept separating axis and may miss some intermediate,
/// non-tunneling collision. If you change the time interval, you should call this function
/// again.
/// Note: use b2Distance to compute the contact point and normal at the time of impact.
void b2TimeOfImpact(b2TOIOutput* output, const b2TOIInput* input);
#endif
+225
View File
@@ -0,0 +1,225 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
* Copyright (c) 2013 Google, Inc.
*
* 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/Common/b2BlockAllocator.h>
#include <limits.h>
#include <memory.h>
#include <stddef.h>
#include <string.h>
#include <new> // For placement new
int32 b2BlockAllocator::s_blockSizes[b2_blockSizes] =
{
16, // 0
32, // 1
64, // 2
96, // 3
128, // 4
160, // 5
192, // 6
224, // 7
256, // 8
320, // 9
384, // 10
448, // 11
512, // 12
640, // 13
};
uint8 b2BlockAllocator::s_blockSizeLookup[b2_maxBlockSize + 1];
bool b2BlockAllocator::s_blockSizeLookupInitialized;
struct b2Chunk
{
int32 blockSize;
b2Block* blocks;
};
struct b2Block
{
b2Block* next;
};
b2BlockAllocator::b2BlockAllocator()
{
b2Assert((uint32)b2_blockSizes < UCHAR_MAX);
m_chunkSpace = b2_chunkArrayIncrement;
m_chunkCount = 0;
m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk));
memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk));
memset(m_freeLists, 0, sizeof(m_freeLists));
if (s_blockSizeLookupInitialized == false)
{
int32 j = 0;
for (int32 i = 1; i <= b2_maxBlockSize; ++i)
{
b2Assert(j < b2_blockSizes);
if (i <= s_blockSizes[j])
{
s_blockSizeLookup[i] = (uint8)j;
}
else
{
++j;
s_blockSizeLookup[i] = (uint8)j;
}
}
s_blockSizeLookupInitialized = true;
}
}
b2BlockAllocator::~b2BlockAllocator()
{
for (int32 i = 0; i < m_chunkCount; ++i)
{
b2Free(m_chunks[i].blocks);
}
b2Free(m_chunks);
}
uint32 b2BlockAllocator::GetNumGiantAllocations() const
{
return m_giants.GetList().GetLength();
}
void* b2BlockAllocator::Allocate(int32 size)
{
if (size == 0)
return NULL;
b2Assert(0 < size);
if (size > b2_maxBlockSize)
{
return m_giants.Allocate(size);
}
int32 index = s_blockSizeLookup[size];
b2Assert(0 <= index && index < b2_blockSizes);
if (m_freeLists[index])
{
b2Block* block = m_freeLists[index];
m_freeLists[index] = block->next;
return block;
}
else
{
if (m_chunkCount == m_chunkSpace)
{
b2Chunk* oldChunks = m_chunks;
m_chunkSpace += b2_chunkArrayIncrement;
m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk));
memcpy(m_chunks, oldChunks, m_chunkCount * sizeof(b2Chunk));
memset(m_chunks + m_chunkCount, 0, b2_chunkArrayIncrement * sizeof(b2Chunk));
b2Free(oldChunks);
}
b2Chunk* chunk = m_chunks + m_chunkCount;
chunk->blocks = (b2Block*)b2Alloc(b2_chunkSize);
#if DEBUG
memset(chunk->blocks, 0xcd, b2_chunkSize);
#endif
int32 blockSize = s_blockSizes[index];
chunk->blockSize = blockSize;
int32 blockCount = b2_chunkSize / blockSize;
b2Assert(blockCount * blockSize <= b2_chunkSize);
for (int32 i = 0; i < blockCount - 1; ++i)
{
b2Block* block = (b2Block*)((int8*)chunk->blocks + blockSize * i);
b2Block* next = (b2Block*)((int8*)chunk->blocks + blockSize * (i + 1));
block->next = next;
}
b2Block* last = (b2Block*)((int8*)chunk->blocks + blockSize * (blockCount - 1));
last->next = NULL;
m_freeLists[index] = chunk->blocks->next;
++m_chunkCount;
return chunk->blocks;
}
}
void b2BlockAllocator::Free(void* p, int32 size)
{
if (size == 0)
{
return;
}
b2Assert(0 < size);
if (size > b2_maxBlockSize)
{
m_giants.Free(p);
return;
}
int32 index = s_blockSizeLookup[size];
b2Assert(0 <= index && index < b2_blockSizes);
#if B2_ASSERT_ENABLED
// Verify the memory address and size is valid.
int32 blockSize = s_blockSizes[index];
bool found = false;
for (int32 i = 0; i < m_chunkCount; ++i)
{
b2Chunk* chunk = m_chunks + i;
if (chunk->blockSize != blockSize)
{
b2Assert( (int8*)p + blockSize <= (int8*)chunk->blocks ||
(int8*)chunk->blocks + b2_chunkSize <= (int8*)p);
}
else
{
if ((int8*)chunk->blocks <= (int8*)p && (int8*)p + blockSize <= (int8*)chunk->blocks + b2_chunkSize)
{
found = true;
}
}
}
b2Assert(found);
#endif // B2_ASSERT_ENABLED
#if DEBUG
memset(p, 0xfd, s_blockSizes[index]);
#endif
b2Block* block = (b2Block*)p;
block->next = m_freeLists[index];
m_freeLists[index] = block;
}
void b2BlockAllocator::Clear()
{
for (int32 i = 0; i < m_chunkCount; ++i)
{
b2Free(m_chunks[i].blocks);
}
m_chunkCount = 0;
memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk));
memset(m_freeLists, 0, sizeof(m_freeLists));
}
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2006-2009 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.
*/
#ifndef B2_BLOCK_ALLOCATOR_H
#define B2_BLOCK_ALLOCATOR_H
#include <Box2D/Common/b2Settings.h>
#include <Box2D/Common/b2TrackedBlock.h>
const int32 b2_chunkSize = 16 * 1024;
const int32 b2_maxBlockSize = 640;
const int32 b2_blockSizes = 14;
const int32 b2_chunkArrayIncrement = 128;
struct b2Block;
struct b2Chunk;
/// This is a small object allocator used for allocating small
/// objects that persist for more than one time step.
/// See: http://www.codeproject.com/useritems/Small_Block_Allocator.asp
class b2BlockAllocator
{
public:
b2BlockAllocator();
~b2BlockAllocator();
/// Allocate memory. This uses b2Alloc if the size is larger than b2_maxBlockSize.
void* Allocate(int32 size);
/// Free memory. This uses b2Free if the size is larger than b2_maxBlockSize.
void Free(void* p, int32 size);
void Clear();
/// Returns the number of allocations larger than the max block size.
uint32 GetNumGiantAllocations() const;
private:
b2Chunk* m_chunks;
int32 m_chunkCount;
int32 m_chunkSpace;
b2Block* m_freeLists[b2_blockSizes];
// Record giant allocations--ones bigger than the max block size
b2TrackedBlockAllocator m_giants;
static int32 s_blockSizes[b2_blockSizes];
static uint8 s_blockSizeLookup[b2_maxBlockSize + 1];
static bool s_blockSizeLookupInitialized;
};
#endif
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2011 Erin Catto http://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/Common/b2Draw.h>
b2Draw::b2Draw()
{
m_drawFlags = 0;
}
void b2Draw::SetFlags(uint32 flags)
{
m_drawFlags = flags;
}
uint32 b2Draw::GetFlags() const
{
return m_drawFlags;
}
void b2Draw::AppendFlags(uint32 flags)
{
m_drawFlags |= flags;
}
void b2Draw::ClearFlags(uint32 flags)
{
m_drawFlags &= ~flags;
}
+92
View File
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2011 Erin Catto http://box2d.org
* Copyright (c) 2014 Google, Inc.
*
* 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.
*/
#ifndef B2_DRAW_H
#define B2_DRAW_H
#include <Box2D/Common/b2Math.h>
#include <Box2D/Particle/b2Particle.h>
/// Color for debug drawing. Each value has the range [0,1].
struct b2Color
{
b2Color() {}
b2Color(float32 r, float32 g, float32 b) : r(r), g(g), b(b) {}
void Set(float32 ri, float32 gi, float32 bi) { r = ri; g = gi; b = bi; }
float32 r, g, b;
};
/// Implement and register this class with a b2World to provide debug drawing of physics
/// entities in your game.
class b2Draw
{
public:
b2Draw();
virtual ~b2Draw() {}
enum
{
e_shapeBit = 0x0001, ///< draw shapes
e_jointBit = 0x0002, ///< draw joint connections
e_aabbBit = 0x0004, ///< draw axis aligned bounding boxes
e_pairBit = 0x0008, ///< draw broad-phase pairs
e_centerOfMassBit = 0x0010, ///< draw center of mass frame
e_particleBit = 0x0020 ///< draw particles
};
/// Set the drawing flags.
void SetFlags(uint32 flags);
/// Get the drawing flags.
uint32 GetFlags() const;
/// Append flags to the current flags.
void AppendFlags(uint32 flags);
/// Clear flags from the current flags.
void ClearFlags(uint32 flags);
/// Draw a closed polygon provided in CCW order.
virtual void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) = 0;
/// Draw a solid closed polygon provided in CCW order.
virtual void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) = 0;
/// Draw a circle.
virtual void DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color) = 0;
/// Draw a solid circle.
virtual void DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color) = 0;
/// Draw a particle array
virtual void DrawParticles(const b2Vec2 *centers, float32 radius, const b2ParticleColor *colors, int32 count) = 0;
/// Draw a line segment.
virtual void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) = 0;
/// Draw a transform. Choose your own length scale.
/// @param xf a transform.
virtual void DrawTransform(const b2Transform& xf) = 0;
protected:
uint32 m_drawFlags;
};
#endif
+57
View File
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2014 Google, Inc.
*
* 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/Common/b2FreeList.h>
#include <Box2D/Common/b2IntrusiveList.h>
#include <Box2D/Common/b2Settings.h>
/// Allocate an item from the freelist.
b2IntrusiveListNode* b2FreeList::Allocate()
{
if (m_free.IsEmpty()) return NULL;
b2IntrusiveListNode * const node = m_free.GetNext();
node->Remove();
m_allocated.InsertBefore(node);
return node;
}
void b2FreeList::Free(b2IntrusiveListNode* node)
{
b2Assert(node);
#if B2_FREE_LIST_CHECK_ALLOCATED_ON_FREE
b2Assert(m_allocated.FindNodeInList(node));
#endif // B2_FREE_LIST_CHECK_ALLOCATED_ON_FREE
node->Remove();
m_free.InsertAfter(node);
}
void b2FreeList::AddToFreeList(b2IntrusiveListNode* node)
{
b2Assert(node);
b2Assert(!node->InList());
m_free.InsertBefore(node);
}
void b2FreeList::RemoveAll()
{
while (!m_allocated.IsEmpty()) {
m_allocated.GetNext()->Remove();
}
while (!m_free.IsEmpty()) {
m_free.GetNext()->Remove();
}
}
+113
View File
@@ -0,0 +1,113 @@
/*
* Copyright (c) 2014 Google, Inc.
*
* 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.
*/
#ifndef B2_FREE_LIST_H
#define B2_FREE_LIST_H
#include <Box2D/Common/b2IntrusiveList.h>
#include <Box2D/Common/b2Settings.h>
/// When B2_FREE_LIST_CHECK_ALLOCATED_ON_FREE is 1, b2FreeList::Free() will
/// check that the deallocated node was allocated from the freelist.
#ifndef B2_FREE_LIST_CHECK_ALLOCATED_ON_FREE
#define B2_FREE_LIST_CHECK_ALLOCATED_ON_FREE 0
#endif // B2_FREE_LIST_CHECK_ALLOCATED_ON_FREE
/// Fast - O(1) - list based allocator for items that can be inserted into
/// b2IntrusiveListNode lists.
class b2FreeList
{
public:
/// Construct the free list.
b2FreeList() { }
/// Destroy the free list.
~b2FreeList() { }
/// Allocate an item from the freelist.
b2IntrusiveListNode* Allocate();
/// Free an item from the freelist.
void Free(b2IntrusiveListNode* node);
/// Add an item to the freelist so that it can be allocated using
/// b2FreeList::Allocate().
void AddToFreeList(b2IntrusiveListNode* node);
/// Remove all items (allocated and free) from the freelist.
void RemoveAll();
/// Get the list which tracks allocated items.
const b2IntrusiveListNode& GetAllocatedList() const {
return m_allocated;
}
/// Get the list which tracks free items.
const b2IntrusiveListNode& GetFreeList() const {
return m_free;
}
protected:
/// List of allocated items.
b2IntrusiveListNode m_allocated;
/// List of free items.
b2IntrusiveListNode m_free;
};
/// Typed b2FreeList which manages items of type T assuming T implements
/// the GetInstanceFromListNode() and GetListNode() methods.
template<typename T>
class b2TypedFreeList {
public:
/// Construct the free list.
b2TypedFreeList() { }
/// Destroy the free list.
~b2TypedFreeList() { }
/// Allocate an item from the free list.
T* Allocate() {
b2IntrusiveListNode* const node = m_freeList.Allocate();
if (!node) return NULL;
return T::GetInstanceFromListNode(node);
}
/// Free an item.
void Free(T* instance) {
b2Assert(instance);
m_freeList.Free(instance->GetListNode());
}
/// Add an item to the freelist so that it can be allocated with
/// b2TypedFreeList::Allocate().
void AddToFreeList(T* instance)
{
b2Assert(instance);
m_freeList.AddToFreeList(instance->GetListNode());
}
// Get the underlying b2FreeList.
b2FreeList* GetFreeList() { return &m_freeList; }
const b2FreeList* GetFreeList() const { return &m_freeList; }
protected:
b2FreeList m_freeList;
};
#endif // B2_FREE_LIST_H
+200
View File
@@ -0,0 +1,200 @@
/*
* Copyright (c) 2014 Google, Inc.
*
* 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.
*/
#ifndef B2_GROWABLE_BUFFER_H
#define B2_GROWABLE_BUFFER_H
#include <Box2D/Common/b2BlockAllocator.h>
#include <string.h>
#include <memory.h>
#include <algorithm>
/// A simple array-like container, similar to std::vector.
/// If we ever start using stl, we should replace this with std::vector.
template <typename T>
class b2GrowableBuffer
{
public:
b2GrowableBuffer(b2BlockAllocator& allocator) :
data(NULL),
count(0),
capacity(0),
allocator(&allocator)
{
#if defined(LIQUIDFUN_SIMD_NEON)
// b2ParticleAssembly.neon.s assumes these values are at fixed offsets.
// If this assert fails, be sure to update the assembly offsets!
// ldr r3, [r9, #0] @ r3 = out = contacts.data
// ldr r6, [r9, #8] @ r6 = contacts.capacity
b2Assert((intptr_t)&data - (intptr_t)(this) == 0
&& (intptr_t)&capacity - (intptr_t)(this) == 8);
#endif // defined(LIQUIDFUN_SIMD_NEON)
}
b2GrowableBuffer(const b2GrowableBuffer<T>& rhs) :
data(NULL),
count(rhs.count),
capacity(rhs.capacity),
allocator(rhs.allocator)
{
if (rhs.data != NULL)
{
data = (T*) allocator->Allocate(sizeof(T) * capacity);
memcpy(data, rhs.data, sizeof(T) * count);
}
}
~b2GrowableBuffer()
{
Free();
}
T& Append()
{
if (count >= capacity)
{
Grow();
}
return data[count++];
}
void Reserve(int32 newCapacity)
{
if (capacity >= newCapacity)
return;
// Reallocate and copy.
T* newData = (T*) allocator->Allocate(sizeof(T) * newCapacity);
if (data)
{
memcpy(newData, data, sizeof(T) * count);
allocator->Free(data, sizeof(T) * capacity);
}
// Update pointer and capacity.
capacity = newCapacity;
data = newData;
}
void Grow()
{
// Double the capacity.
int32 newCapacity = capacity ? 2 * capacity
: b2_minParticleSystemBufferCapacity;
b2Assert(newCapacity > capacity);
Reserve(newCapacity);
}
void Free()
{
if (data == NULL)
return;
allocator->Free(data, sizeof(data[0]) * capacity);
data = NULL;
capacity = 0;
count = 0;
}
void Shorten(const T* newEnd)
{
b2Assert(newEnd >= data);
count = (int32) (newEnd - data);
}
T& operator[](int i)
{
return data[i];
}
const T& operator[](int i) const
{
return data[i];
}
T* Data()
{
return data;
}
const T* Data() const
{
return data;
}
T* Begin()
{
return data;
}
const T* Begin() const
{
return data;
}
T* End()
{
return &data[count];
}
const T* End() const
{
return &data[count];
}
int32 GetCount() const
{
return count;
}
void SetCount(int32 newCount)
{
b2Assert(0 <= newCount && newCount <= capacity);
count = newCount;
}
int32 GetCapacity() const
{
return capacity;
}
template<class UnaryPredicate>
T* RemoveIf(UnaryPredicate pred)
{
T* newEnd = std::remove_if(data, data + count, pred);
Shorten(newEnd);
return newEnd;
}
template<class BinaryPredicate>
T* Unique(BinaryPredicate pred)
{
T* newEnd = std::unique(data, data + count, pred);
Shorten(newEnd);
return newEnd;
}
private:
T* data;
int32 count;
int32 capacity;
b2BlockAllocator* allocator;
};
#endif // B2_GROWABLE_BUFFER_H
+87
View File
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2010 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.
*/
#ifndef B2_GROWABLE_STACK_H
#define B2_GROWABLE_STACK_H
#include <Box2D/Common/b2Settings.h>
#include <string.h>
#include <memory.h>
/// This is a growable LIFO stack with an initial capacity of N.
/// If the stack size exceeds the initial capacity, the heap is used
/// to increase the size of the stack.
template <typename T, int32 N>
class b2GrowableStack
{
public:
b2GrowableStack()
{
m_stack = m_array;
m_count = 0;
m_capacity = N;
}
~b2GrowableStack()
{
if (m_stack != m_array)
{
b2Free(m_stack);
m_stack = NULL;
}
}
void Push(const T& element)
{
if (m_count == m_capacity)
{
T* old = m_stack;
m_capacity *= 2;
m_stack = (T*)b2Alloc(m_capacity * sizeof(T));
memcpy(m_stack, old, m_count * sizeof(T));
if (old != m_array)
{
b2Free(old);
}
}
m_stack[m_count] = element;
++m_count;
}
T Pop()
{
b2Assert(m_count > 0);
--m_count;
return m_stack[m_count];
}
int32 GetCount()
{
return m_count;
}
private:
T* m_stack;
T m_array[N];
int32 m_count;
int32 m_capacity;
};
#endif
+369
View File
@@ -0,0 +1,369 @@
/*
* Copyright (c) 2014 Google, Inc.
*
* 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.
*/
#ifndef B2_INTRUSIVE_LIST
#define B2_INTRUSIVE_LIST
#include <Box2D/Common/b2Settings.h>
// Whether to enable b2IntrusiveList::ValidateList().
// Be careful when enabling this since this changes the size of
// b2IntrusiveListNode so make sure *all* projects that include Box2D.h
// also define this value in the same way to avoid data corruption.
#ifndef B2_INTRUSIVE_LIST_VALIDATE
#define B2_INTRUSIVE_LIST_VALIDATE 0
#endif // B2_INTRUSIVE_LIST_VALIDATE
/// b2IntrusiveListNode is used to implement an intrusive doubly-linked
/// list.
///
/// For example:
///
/// class MyClass {
/// public:
/// MyClass(const char *msg) : m_msg(msg) {}
/// const char* GetMessage() const { return m_msg; }
/// B2_INTRUSIVE_LIST_GET_NODE(m_node);
/// B2_INTRUSIVE_LIST_NODE_GET_CLASS(MyClass, m_node);
/// private:
/// b2IntrusiveListNode m_node;
/// const char *m_msg;
/// };
///
/// int main(int argc, char *argv[]) {
/// b2IntrusiveListNode list; // NOTE: type is NOT MyClass
/// MyClass a("this");
/// MyClass b("is");
/// MyClass c("a");
/// MyClass d("test");
/// list.InsertBefore(a.GetListNode());
/// list.InsertBefore(b.GetListNode());
/// list.InsertBefore(c.GetListNode());
/// list.InsertBefore(d.GetListNode());
/// for (b2IntrusiveListNode* node = list.GetNext();
/// node != list.GetTerminator(); node = node->GetNext()) {
/// MyClass *cls = MyClass::GetInstanceFromListNode(node);
/// printf("%s\n", cls->GetMessage());
/// }
/// return 0;
/// }
class b2IntrusiveListNode
{
public:
/// Initialize the node.
b2IntrusiveListNode()
{
Initialize();
#if B2_INTRUSIVE_LIST_VALIDATE
m_magic = k_magic;
#endif // B2_INTRUSIVE_LIST_VALIDATE
}
/// If the node is in a list, remove it from the list.
~b2IntrusiveListNode()
{
Remove();
#if B2_INTRUSIVE_LIST_VALIDATE
m_magic = 0;
#endif // B2_INTRUSIVE_LIST_VALIDATE
}
/// Insert this node after the specified node.
void InsertAfter(b2IntrusiveListNode* const node)
{
b2Assert(!node->InList());
node->m_next = m_next;
node->m_prev = this;
m_next->m_prev = node;
m_next = node;
}
/// Insert this node before the specified node.
void InsertBefore(b2IntrusiveListNode* const node)
{
b2Assert(!node->InList());
node->m_next = this;
node->m_prev = m_prev;
m_prev->m_next = node;
m_prev = node;
}
/// Get the terminator of the list.
const b2IntrusiveListNode* GetTerminator() const
{
return this;
}
/// Remove this node from the list it's currently in.
b2IntrusiveListNode* Remove()
{
m_prev->m_next = m_next;
m_next->m_prev = m_prev;
Initialize();
return this;
}
/// Determine whether this list is empty or the node isn't in a list.
bool IsEmpty() const
{
return GetNext() == this;
}
/// Determine whether this node is in a list or the list contains nodes.
bool InList() const
{
return !IsEmpty();
}
/// Calculate the length of the list.
uint32 GetLength() const
{
uint32 length = 0;
const b2IntrusiveListNode * const terminator = GetTerminator();
for (const b2IntrusiveListNode* node = GetNext();
node != terminator; node = node->GetNext())
{
length++;
}
return length;
}
/// Get the next node in the list.
b2IntrusiveListNode* GetNext() const
{
return m_next;
}
/// Get the previous node in the list.
b2IntrusiveListNode* GetPrevious() const
{
return m_prev;
}
/// If B2_INTRUSIVE_LIST_VALIDATE is 1 perform a very rough validation
/// of all nodes in the list.
bool ValidateList() const
{
#if B2_INTRUSIVE_LIST_VALIDATE
if (m_magic != k_magic) return false;
const b2IntrusiveListNode * const terminator = GetTerminator();
for (b2IntrusiveListNode *node = GetNext(); node != terminator;
node = node->GetNext()) {
if (node->m_magic != k_magic) return false;
}
#endif // B2_INTRUSIVE_LIST_VALIDATE
return true;
}
/// Determine whether the specified node is present in this list.
bool FindNodeInList(b2IntrusiveListNode* const nodeToFind) const
{
const b2IntrusiveListNode * const terminator = GetTerminator();
for (b2IntrusiveListNode *node = GetNext(); node != terminator;
node = node->GetNext())
{
if (nodeToFind == node) return true;
}
return false;
}
private:
/// Initialize the list node.
void Initialize()
{
m_next = this;
m_prev = this;
}
private:
#if B2_INTRUSIVE_LIST_VALIDATE
uint32 m_magic;
#endif // B2_INTRUSIVE_LIST_VALIDATE
/// The next node in the list.
b2IntrusiveListNode *m_prev;
/// The previous node in the list.
b2IntrusiveListNode *m_next;
private:
#if B2_INTRUSIVE_LIST_VALIDATE
static const uint32 k_magic = 0x7157ac01;
#endif // B2_INTRUSIVE_LIST_VALIDATE
};
/// Declares the member function GetListNode() of Class to retrieve a pointer
/// to NodeMemberName.
/// See #B2_INTRUSIVE_LIST_NODE_GET_CLASS_ACCESSOR()
#define B2_INTRUSIVE_LIST_GET_NODE(NodeMemberName) \
b2IntrusiveListNode* GetListNode() { return &NodeMemberName; } \
const b2IntrusiveListNode* GetListNode() const { return &NodeMemberName; }
/// Declares the member function FunctionName of Class to retrieve a pointer
/// to a Class instance from a list node pointer. NodeMemberName references
/// the name of the b2IntrusiveListNode member of Class.
#define B2_INTRUSIVE_LIST_NODE_GET_CLASS_ACCESSOR( \
Class, NodeMemberName, FunctionName) \
static Class* FunctionName(b2IntrusiveListNode *node) \
{ \
Class *cls = NULL; \
/* This effectively performs offsetof(Class, NodeMemberName) */ \
/* which ends up in the undefined behavior realm of C++ but in */ \
/* practice this works with most compilers. */ \
return reinterpret_cast<Class*>((uint8*)(node) - \
(uint8*)(&cls->NodeMemberName)); \
} \
\
static const Class* FunctionName(const b2IntrusiveListNode *node) \
{ \
return FunctionName(const_cast<b2IntrusiveListNode*>(node)); \
}
/// Declares the member function GetInstanceFromListNode() of Class to retrieve
/// a pointer to a Class instance from a list node pointer. NodeMemberName
/// reference the name of the b2IntrusiveListNode member of Class.
#define B2_INTRUSIVE_LIST_NODE_GET_CLASS(Class, NodeMemberName) \
B2_INTRUSIVE_LIST_NODE_GET_CLASS_ACCESSOR(Class, NodeMemberName, \
GetInstanceFromListNode)
/// b2TypedIntrusiveListNode which supports inserting an object into a single
/// doubly linked list. For objects that need to be inserted in multiple
/// doubly linked lists, use b2IntrusiveListNode.
///
/// For example:
///
/// class IntegerItem : public b2TypedIntrusiveListNode<IntegerItem>
/// {
/// public:
/// IntegerItem(int32 value) : m_value(value) { }
/// ~IntegerItem() { }
/// int32 GetValue() const { return m_value; }
/// private:
/// int32 m_value;
/// };
///
/// int main(int argc, const char *arvg[]) {
/// b2TypedIntrusiveListNode<IntegerItem> list;
/// IntegerItem a(1);
/// IntegerItem b(2);
/// IntegerItem c(3);
/// list.InsertBefore(&a);
/// list.InsertBefore(&b);
/// list.InsertBefore(&c);
/// for (IntegerItem* item = list.GetNext();
/// item != list.GetTerminator(); item = item->GetNext())
/// {
/// printf("%d\n", item->GetValue());
/// }
/// }
template<typename T>
class b2TypedIntrusiveListNode
{
public:
b2TypedIntrusiveListNode() { }
~b2TypedIntrusiveListNode() { }
/// Insert this object after the specified object.
void InsertAfter(T* const obj)
{
b2Assert(obj);
GetListNode()->InsertAfter(obj->GetListNode());
}
/// Insert this object before the specified object.
void InsertBefore(T* const obj)
{
b2Assert(obj);
GetListNode()->InsertBefore(obj->GetListNode());
}
/// Get the next object in the list.
/// Check against GetTerminator() before deferencing the object.
T* GetNext() const
{
return GetInstanceFromListNode(GetListNode()->GetNext());
}
/// Get the previous object in the list.
/// Check against GetTerminator() before deferencing the object.
T* GetPrevious() const
{
return GetInstanceFromListNode(GetListNode()->GetPrevious());
}
/// Get the terminator of the list.
/// This should not be dereferenced as it is a pointer to
/// b2TypedIntrusiveListNode<T> *not* T.
T* GetTerminator() const
{
return (T*)GetListNode();
}
/// Remove this object from the list it's currently in.
T* Remove()
{
GetListNode()->Remove();
return GetInstanceFromListNode(GetListNode());
}
/// Determine whether this object is in a list.
bool InList() const
{
return GetListNode()->InList();
}
// Determine whether this list is empty.
bool IsEmpty() const
{
return GetListNode()->IsEmpty();
}
/// Calculate the length of the list.
uint32 GetLength() const
{
return GetListNode()->GetLength();
}
B2_INTRUSIVE_LIST_GET_NODE(m_node);
private:
// Node within an intrusive list.
b2IntrusiveListNode m_node;
public:
/// Get a pointer to the instance of T that contains "node".
static T* GetInstanceFromListNode(b2IntrusiveListNode* const node)
{
b2Assert(node);
// Calculate the pointer to T from the offset.
return (T*)((uint8*)node - GetNodeOffset(node));
}
private:
// Get the offset of m_node within this class.
static int32 GetNodeOffset(b2IntrusiveListNode* const node)
{
b2Assert(node);
// Perform some type punning to calculate the offset of m_node in T.
// WARNING: This could result in undefined behavior with some C++
// compilers.
T* obj = (T*)node;
int32 nodeOffset = (int32)((uint8*)&obj->m_node - (uint8*)obj);
return nodeOffset;
}
};
#endif // B2_INTRUSIVE_LIST
+94
View File
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2007-2009 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/Common/b2Math.h>
const b2Vec2 b2Vec2_zero(0.0f, 0.0f);
/// Solve A * x = b, where b is a column vector. This is more efficient
/// than computing the inverse in one-shot cases.
b2Vec3 b2Mat33::Solve33(const b2Vec3& b) const
{
float32 det = b2Dot(ex, b2Cross(ey, ez));
if (det != 0.0f)
{
det = 1.0f / det;
}
b2Vec3 x;
x.x = det * b2Dot(b, b2Cross(ey, ez));
x.y = det * b2Dot(ex, b2Cross(b, ez));
x.z = det * b2Dot(ex, b2Cross(ey, b));
return x;
}
/// Solve A * x = b, where b is a column vector. This is more efficient
/// than computing the inverse in one-shot cases.
b2Vec2 b2Mat33::Solve22(const b2Vec2& b) const
{
float32 a11 = ex.x, a12 = ey.x, a21 = ex.y, a22 = ey.y;
float32 det = a11 * a22 - a12 * a21;
if (det != 0.0f)
{
det = 1.0f / det;
}
b2Vec2 x;
x.x = det * (a22 * b.x - a12 * b.y);
x.y = det * (a11 * b.y - a21 * b.x);
return x;
}
///
void b2Mat33::GetInverse22(b2Mat33* M) const
{
float32 a = ex.x, b = ey.x, c = ex.y, d = ey.y;
float32 det = a * d - b * c;
if (det != 0.0f)
{
det = 1.0f / det;
}
M->ex.x = det * d; M->ey.x = -det * b; M->ex.z = 0.0f;
M->ex.y = -det * c; M->ey.y = det * a; M->ey.z = 0.0f;
M->ez.x = 0.0f; M->ez.y = 0.0f; M->ez.z = 0.0f;
}
/// Returns the zero matrix if singular.
void b2Mat33::GetSymInverse33(b2Mat33* M) const
{
float32 det = b2Dot(ex, b2Cross(ey, ez));
if (det != 0.0f)
{
det = 1.0f / det;
}
float32 a11 = ex.x, a12 = ey.x, a13 = ez.x;
float32 a22 = ey.y, a23 = ez.y;
float32 a33 = ez.z;
M->ex.x = det * (a22 * a33 - a23 * a23);
M->ex.y = det * (a13 * a23 - a12 * a33);
M->ex.z = det * (a12 * a23 - a13 * a22);
M->ey.x = M->ex.y;
M->ey.y = det * (a11 * a33 - a13 * a13);
M->ey.z = det * (a13 * a12 - a11 * a23);
M->ez.x = M->ex.z;
M->ez.y = M->ey.z;
M->ez.z = det * (a11 * a22 - a12 * a12);
}
+800
View File
@@ -0,0 +1,800 @@
/*
* Copyright (c) 2006-2009 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.
*/
#ifndef B2_MATH_H
#define B2_MATH_H
#include <Box2D/Common/b2Settings.h>
#include <math.h>
/// This function is used to ensure that a floating point number is not a NaN or infinity.
inline bool b2IsValid(float32 x)
{
union {
float32 f;
int32 i;
} v = { x };
return (v.i & 0x7f800000) != 0x7f800000;
}
/// This is a approximate yet fast inverse square-root.
inline float32 b2InvSqrt(float32 x)
{
union
{
float32 x;
int32 i;
} convert;
convert.x = x;
float32 xhalf = 0.5f * x;
convert.i = 0x5f3759df - (convert.i >> 1);
x = convert.x;
x = x * (1.5f - xhalf * x * x);
return x;
}
#define b2Sqrt(x) sqrtf(x)
#define b2Atan2(y, x) atan2f(y, x)
/// A 2D column vector.
struct b2Vec2
{
/// Default constructor does nothing (for performance).
b2Vec2() {}
/// Construct using coordinates.
b2Vec2(float32 x, float32 y) : x(x), y(y) {}
/// Set this vector to all zeros.
void SetZero() { x = 0.0f; y = 0.0f; }
/// Set this vector to some specified coordinates.
void Set(float32 x_, float32 y_) { x = x_; y = y_; }
/// Negate this vector.
b2Vec2 operator -() const { b2Vec2 v; v.Set(-x, -y); return v; }
/// Read from and indexed element.
float32 operator () (int32 i) const
{
return (&x)[i];
}
/// Write to an indexed element.
float32& operator () (int32 i)
{
return (&x)[i];
}
/// Add a vector to this vector.
void operator += (const b2Vec2& v)
{
x += v.x; y += v.y;
}
/// Subtract a vector from this vector.
void operator -= (const b2Vec2& v)
{
x -= v.x; y -= v.y;
}
/// Multiply this vector by a scalar.
void operator *= (float32 a)
{
x *= a; y *= a;
}
/// Get the length of this vector (the norm).
float32 Length() const
{
return b2Sqrt(x * x + y * y);
}
/// Get the length squared. For performance, use this instead of
/// b2Vec2::Length (if possible).
float32 LengthSquared() const
{
return x * x + y * y;
}
/// Convert this vector into a unit vector. Returns the length.
float32 Normalize()
{
float32 length = Length();
if (length < b2_epsilon)
{
return 0.0f;
}
float32 invLength = 1.0f / length;
x *= invLength;
y *= invLength;
return length;
}
/// Does this vector contain finite coordinates?
bool IsValid() const
{
return b2IsValid(x) && b2IsValid(y);
}
/// Get the skew vector such that dot(skew_vec, other) == cross(vec, other)
b2Vec2 Skew() const
{
return b2Vec2(-y, x);
}
float32 x, y;
};
/// Add a float to a vector.
inline b2Vec2 operator + (const b2Vec2& v, float f)
{
return b2Vec2(v.x + f, v.y + f);
}
/// Substract a float from a vector.
inline b2Vec2 operator - (const b2Vec2& v, float f)
{
return b2Vec2(v.x - f, v.y - f);
}
/// Multiply a float with a vector.
inline b2Vec2 operator * (const b2Vec2& v, float f)
{
return b2Vec2(v.x * f, v.y * f);
}
/// Divide a vector by a float.
inline b2Vec2 operator / (const b2Vec2& v, float f)
{
return b2Vec2(v.x / f, v.y / f);
}
/// A 3D column vector with 3 elements.
struct b2Vec3
{
/// Default constructor does nothing (for performance).
b2Vec3() {}
/// Construct using coordinates.
b2Vec3(float32 x, float32 y, float32 z) : x(x), y(y), z(z) {}
/// Set this vector to all zeros.
void SetZero() { x = 0.0f; y = 0.0f; z = 0.0f; }
/// Set this vector to some specified coordinates.
void Set(float32 x_, float32 y_, float32 z_) { x = x_; y = y_; z = z_; }
/// Negate this vector.
b2Vec3 operator -() const { b2Vec3 v; v.Set(-x, -y, -z); return v; }
/// Add a vector to this vector.
void operator += (const b2Vec3& v)
{
x += v.x; y += v.y; z += v.z;
}
/// Subtract a vector from this vector.
void operator -= (const b2Vec3& v)
{
x -= v.x; y -= v.y; z -= v.z;
}
/// Multiply this vector by a scalar.
void operator *= (float32 s)
{
x *= s; y *= s; z *= s;
}
/// Get the length of this vector (the norm).
float32 Length() const
{
return b2Sqrt(x * x + y * y + z * z);
}
/// Convert this vector into a unit vector. Returns the length.
float32 Normalize()
{
float32 length = Length();
if (length < b2_epsilon)
{
return 0.0f;
}
float32 invLength = 1.0f / length;
x *= invLength;
y *= invLength;
z *= invLength;
return length;
}
float32 x, y, z;
};
/// A 4D column vector with 4 elements.
struct b2Vec4
{
/// Default constructor does nothing (for performance).
b2Vec4() {}
/// Construct using coordinates.
b2Vec4(float32 x, float32 y, float32 z, float32 w) : x(x), y(y), z(z), w(w) {}
float32 x, y, z, w;
};
/// A 2-by-2 matrix. Stored in column-major order.
struct b2Mat22
{
/// The default constructor does nothing (for performance).
b2Mat22() {}
/// Construct this matrix using columns.
b2Mat22(const b2Vec2& c1, const b2Vec2& c2)
{
ex = c1;
ey = c2;
}
/// Construct this matrix using scalars.
b2Mat22(float32 a11, float32 a12, float32 a21, float32 a22)
{
ex.x = a11; ex.y = a21;
ey.x = a12; ey.y = a22;
}
/// Initialize this matrix using columns.
void Set(const b2Vec2& c1, const b2Vec2& c2)
{
ex = c1;
ey = c2;
}
/// Set this to the identity matrix.
void SetIdentity()
{
ex.x = 1.0f; ey.x = 0.0f;
ex.y = 0.0f; ey.y = 1.0f;
}
/// Set this matrix to all zeros.
void SetZero()
{
ex.x = 0.0f; ey.x = 0.0f;
ex.y = 0.0f; ey.y = 0.0f;
}
b2Mat22 GetInverse() const
{
float32 a = ex.x, b = ey.x, c = ex.y, d = ey.y;
b2Mat22 B;
float32 det = a * d - b * c;
if (det != 0.0f)
{
det = 1.0f / det;
}
B.ex.x = det * d; B.ey.x = -det * b;
B.ex.y = -det * c; B.ey.y = det * a;
return B;
}
/// Solve A * x = b, where b is a column vector. This is more efficient
/// than computing the inverse in one-shot cases.
b2Vec2 Solve(const b2Vec2& b) const
{
float32 a11 = ex.x, a12 = ey.x, a21 = ex.y, a22 = ey.y;
float32 det = a11 * a22 - a12 * a21;
if (det != 0.0f)
{
det = 1.0f / det;
}
b2Vec2 x;
x.x = det * (a22 * b.x - a12 * b.y);
x.y = det * (a11 * b.y - a21 * b.x);
return x;
}
b2Vec2 ex, ey;
};
/// A 3-by-3 matrix. Stored in column-major order.
struct b2Mat33
{
/// The default constructor does nothing (for performance).
b2Mat33() {}
/// Construct this matrix using columns.
b2Mat33(const b2Vec3& c1, const b2Vec3& c2, const b2Vec3& c3)
{
ex = c1;
ey = c2;
ez = c3;
}
/// Set this matrix to all zeros.
void SetZero()
{
ex.SetZero();
ey.SetZero();
ez.SetZero();
}
/// Solve A * x = b, where b is a column vector. This is more efficient
/// than computing the inverse in one-shot cases.
b2Vec3 Solve33(const b2Vec3& b) const;
/// Solve A * x = b, where b is a column vector. This is more efficient
/// than computing the inverse in one-shot cases. Solve only the upper
/// 2-by-2 matrix equation.
b2Vec2 Solve22(const b2Vec2& b) const;
/// Get the inverse of this matrix as a 2-by-2.
/// Returns the zero matrix if singular.
void GetInverse22(b2Mat33* M) const;
/// Get the symmetric inverse of this matrix as a 3-by-3.
/// Returns the zero matrix if singular.
void GetSymInverse33(b2Mat33* M) const;
b2Vec3 ex, ey, ez;
};
/// Rotation
struct b2Rot
{
b2Rot() {}
/// Initialize from an angle in radians
explicit b2Rot(float32 angle)
{
/// TODO_ERIN optimize
s = sinf(angle);
c = cosf(angle);
}
/// Set using an angle in radians.
void Set(float32 angle)
{
/// TODO_ERIN optimize
s = sinf(angle);
c = cosf(angle);
}
/// Set to the identity rotation
void SetIdentity()
{
s = 0.0f;
c = 1.0f;
}
/// Get the angle in radians
float32 GetAngle() const
{
return b2Atan2(s, c);
}
/// Get the x-axis
b2Vec2 GetXAxis() const
{
return b2Vec2(c, s);
}
/// Get the u-axis
b2Vec2 GetYAxis() const
{
return b2Vec2(-s, c);
}
/// Sine and cosine
float32 s, c;
};
/// A transform contains translation and rotation. It is used to represent
/// the position and orientation of rigid frames.
struct b2Transform
{
/// The default constructor does nothing.
b2Transform() {}
/// Initialize using a position vector and a rotation.
b2Transform(const b2Vec2& position, const b2Rot& rotation) : p(position), q(rotation) {}
/// Set this to the identity transform.
void SetIdentity()
{
p.SetZero();
q.SetIdentity();
}
/// Set this based on the position and angle.
void Set(const b2Vec2& position, float32 angle)
{
p = position;
q.Set(angle);
}
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
/// Get x-coordinate of p.
float32 GetPositionX() const { return p.x; }
/// Get y-coordinate of p.
float32 GetPositionY() const { return p.y; }
/// Get sine-component of q.
float32 GetRotationSin() const { return q.s; }
/// Get cosine-component of q.
float32 GetRotationCos() const { return q.c; }
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
b2Vec2 p;
b2Rot q;
};
/// This describes the motion of a body/shape for TOI computation.
/// Shapes are defined with respect to the body origin, which may
/// no coincide with the center of mass. However, to support dynamics
/// we must interpolate the center of mass position.
struct b2Sweep
{
/// Get the interpolated transform at a specific time.
/// @param beta is a factor in [0,1], where 0 indicates alpha0.
void GetTransform(b2Transform* xfb, float32 beta) const;
/// Advance the sweep forward, yielding a new initial state.
/// @param alpha the new initial time.
void Advance(float32 alpha);
/// Normalize the angles.
void Normalize();
b2Vec2 localCenter; ///< local center of mass position
b2Vec2 c0, c; ///< center world positions
float32 a0, a; ///< world angles
/// Fraction of the current time step in the range [0,1]
/// c0 and a0 are the positions at alpha0.
float32 alpha0;
};
/// Useful constant
extern const b2Vec2 b2Vec2_zero;
/// Perform the dot product on two vectors.
inline float32 b2Dot(const b2Vec2& a, const b2Vec2& b)
{
return a.x * b.x + a.y * b.y;
}
/// Perform the cross product on two vectors. In 2D this produces a scalar.
inline float32 b2Cross(const b2Vec2& a, const b2Vec2& b)
{
return a.x * b.y - a.y * b.x;
}
/// Perform the cross product on a vector and a scalar. In 2D this produces
/// a vector.
inline b2Vec2 b2Cross(const b2Vec2& a, float32 s)
{
return b2Vec2(s * a.y, -s * a.x);
}
/// Perform the cross product on a scalar and a vector. In 2D this produces
/// a vector.
inline b2Vec2 b2Cross(float32 s, const b2Vec2& a)
{
return b2Vec2(-s * a.y, s * a.x);
}
/// Multiply a matrix times a vector. If a rotation matrix is provided,
/// then this transforms the vector from one frame to another.
inline b2Vec2 b2Mul(const b2Mat22& A, const b2Vec2& v)
{
return b2Vec2(A.ex.x * v.x + A.ey.x * v.y, A.ex.y * v.x + A.ey.y * v.y);
}
/// Multiply a matrix transpose times a vector. If a rotation matrix is provided,
/// then this transforms the vector from one frame to another (inverse transform).
inline b2Vec2 b2MulT(const b2Mat22& A, const b2Vec2& v)
{
return b2Vec2(b2Dot(v, A.ex), b2Dot(v, A.ey));
}
/// Add two vectors component-wise.
inline b2Vec2 operator + (const b2Vec2& a, const b2Vec2& b)
{
return b2Vec2(a.x + b.x, a.y + b.y);
}
/// Subtract two vectors component-wise.
inline b2Vec2 operator - (const b2Vec2& a, const b2Vec2& b)
{
return b2Vec2(a.x - b.x, a.y - b.y);
}
inline b2Vec2 operator * (float32 s, const b2Vec2& a)
{
return b2Vec2(s * a.x, s * a.y);
}
inline bool operator == (const b2Vec2& a, const b2Vec2& b)
{
return a.x == b.x && a.y == b.y;
}
inline bool operator != (const b2Vec2& a, const b2Vec2& b)
{
return !operator==(a, b);
}
inline float32 b2Distance(const b2Vec2& a, const b2Vec2& b)
{
b2Vec2 c = a - b;
return c.Length();
}
inline float32 b2DistanceSquared(const b2Vec2& a, const b2Vec2& b)
{
b2Vec2 c = a - b;
return b2Dot(c, c);
}
inline b2Vec3 operator * (float32 s, const b2Vec3& a)
{
return b2Vec3(s * a.x, s * a.y, s * a.z);
}
/// Add two vectors component-wise.
inline b2Vec3 operator + (const b2Vec3& a, const b2Vec3& b)
{
return b2Vec3(a.x + b.x, a.y + b.y, a.z + b.z);
}
/// Subtract two vectors component-wise.
inline b2Vec3 operator - (const b2Vec3& a, const b2Vec3& b)
{
return b2Vec3(a.x - b.x, a.y - b.y, a.z - b.z);
}
/// Perform the dot product on two vectors.
inline float32 b2Dot(const b2Vec3& a, const b2Vec3& b)
{
return a.x * b.x + a.y * b.y + a.z * b.z;
}
/// Perform the cross product on two vectors.
inline b2Vec3 b2Cross(const b2Vec3& a, const b2Vec3& b)
{
return b2Vec3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
}
inline b2Mat22 operator + (const b2Mat22& A, const b2Mat22& B)
{
return b2Mat22(A.ex + B.ex, A.ey + B.ey);
}
// A * B
inline b2Mat22 b2Mul(const b2Mat22& A, const b2Mat22& B)
{
return b2Mat22(b2Mul(A, B.ex), b2Mul(A, B.ey));
}
// A^T * B
inline b2Mat22 b2MulT(const b2Mat22& A, const b2Mat22& B)
{
b2Vec2 c1(b2Dot(A.ex, B.ex), b2Dot(A.ey, B.ex));
b2Vec2 c2(b2Dot(A.ex, B.ey), b2Dot(A.ey, B.ey));
return b2Mat22(c1, c2);
}
/// Multiply a matrix times a vector.
inline b2Vec3 b2Mul(const b2Mat33& A, const b2Vec3& v)
{
return v.x * A.ex + v.y * A.ey + v.z * A.ez;
}
/// Multiply a matrix times a vector.
inline b2Vec2 b2Mul22(const b2Mat33& A, const b2Vec2& v)
{
return b2Vec2(A.ex.x * v.x + A.ey.x * v.y, A.ex.y * v.x + A.ey.y * v.y);
}
/// Multiply two rotations: q * r
inline b2Rot b2Mul(const b2Rot& q, const b2Rot& r)
{
// [qc -qs] * [rc -rs] = [qc*rc-qs*rs -qc*rs-qs*rc]
// [qs qc] [rs rc] [qs*rc+qc*rs -qs*rs+qc*rc]
// s = qs * rc + qc * rs
// c = qc * rc - qs * rs
b2Rot qr;
qr.s = q.s * r.c + q.c * r.s;
qr.c = q.c * r.c - q.s * r.s;
return qr;
}
/// Transpose multiply two rotations: qT * r
inline b2Rot b2MulT(const b2Rot& q, const b2Rot& r)
{
// [ qc qs] * [rc -rs] = [qc*rc+qs*rs -qc*rs+qs*rc]
// [-qs qc] [rs rc] [-qs*rc+qc*rs qs*rs+qc*rc]
// s = qc * rs - qs * rc
// c = qc * rc + qs * rs
b2Rot qr;
qr.s = q.c * r.s - q.s * r.c;
qr.c = q.c * r.c + q.s * r.s;
return qr;
}
/// Rotate a vector
inline b2Vec2 b2Mul(const b2Rot& q, const b2Vec2& v)
{
return b2Vec2(q.c * v.x - q.s * v.y, q.s * v.x + q.c * v.y);
}
/// Inverse rotate a vector
inline b2Vec2 b2MulT(const b2Rot& q, const b2Vec2& v)
{
return b2Vec2(q.c * v.x + q.s * v.y, -q.s * v.x + q.c * v.y);
}
inline b2Vec2 b2Mul(const b2Transform& T, const b2Vec2& v)
{
float32 x = (T.q.c * v.x - T.q.s * v.y) + T.p.x;
float32 y = (T.q.s * v.x + T.q.c * v.y) + T.p.y;
return b2Vec2(x, y);
}
inline b2Vec2 b2MulT(const b2Transform& T, const b2Vec2& v)
{
float32 px = v.x - T.p.x;
float32 py = v.y - T.p.y;
float32 x = (T.q.c * px + T.q.s * py);
float32 y = (-T.q.s * px + T.q.c * py);
return b2Vec2(x, y);
}
// v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p
// = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p
inline b2Transform b2Mul(const b2Transform& A, const b2Transform& B)
{
b2Transform C;
C.q = b2Mul(A.q, B.q);
C.p = b2Mul(A.q, B.p) + A.p;
return C;
}
// v2 = A.q' * (B.q * v1 + B.p - A.p)
// = A.q' * B.q * v1 + A.q' * (B.p - A.p)
inline b2Transform b2MulT(const b2Transform& A, const b2Transform& B)
{
b2Transform C;
C.q = b2MulT(A.q, B.q);
C.p = b2MulT(A.q, B.p - A.p);
return C;
}
template <typename T>
inline T b2Abs(T a)
{
return a > T(0) ? a : -a;
}
inline b2Vec2 b2Abs(const b2Vec2& a)
{
return b2Vec2(b2Abs(a.x), b2Abs(a.y));
}
inline b2Mat22 b2Abs(const b2Mat22& A)
{
return b2Mat22(b2Abs(A.ex), b2Abs(A.ey));
}
template <typename T>
inline T b2Min(T a, T b)
{
return a < b ? a : b;
}
inline b2Vec2 b2Min(const b2Vec2& a, const b2Vec2& b)
{
return b2Vec2(b2Min(a.x, b.x), b2Min(a.y, b.y));
}
template <typename T>
inline T b2Max(T a, T b)
{
return a > b ? a : b;
}
inline b2Vec2 b2Max(const b2Vec2& a, const b2Vec2& b)
{
return b2Vec2(b2Max(a.x, b.x), b2Max(a.y, b.y));
}
template <typename T>
inline T b2Clamp(T a, T low, T high)
{
return b2Max(low, b2Min(a, high));
}
inline b2Vec2 b2Clamp(const b2Vec2& a, const b2Vec2& low, const b2Vec2& high)
{
return b2Max(low, b2Min(a, high));
}
template<typename T> inline void b2Swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
}
/// "Next Largest Power of 2
/// Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm
/// that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with
/// the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next
/// largest power of 2. For a 32-bit value:"
inline uint32 b2NextPowerOfTwo(uint32 x)
{
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return x + 1;
}
inline bool b2IsPowerOfTwo(uint32 x)
{
bool result = x > 0 && (x & (x - 1)) == 0;
return result;
}
inline void b2Sweep::GetTransform(b2Transform* xf, float32 beta) const
{
xf->p = (1.0f - beta) * c0 + beta * c;
float32 angle = (1.0f - beta) * a0 + beta * a;
xf->q.Set(angle);
// Shift to origin
xf->p -= b2Mul(xf->q, localCenter);
}
inline void b2Sweep::Advance(float32 alpha)
{
b2Assert(alpha0 < 1.0f);
float32 beta = (alpha - alpha0) / (1.0f - alpha0);
c0 += beta * (c - c0);
a0 += beta * (a - a0);
alpha0 = alpha;
}
/// Normalize an angle in radians to be between -pi and pi
inline void b2Sweep::Normalize()
{
float32 twoPi = 2.0f * b2_pi;
float32 d = twoPi * floorf(a0 / twoPi);
a0 -= d;
a -= d;
}
#endif
+136
View File
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
* Copyright (c) 2013 Google, Inc.
*
* 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/Common/b2Settings.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
b2Version b2_version = {2, 3, 0};
#define LIQUIDFUN_VERSION_MAJOR 1
#define LIQUIDFUN_VERSION_MINOR 1
#define LIQUIDFUN_VERSION_REVISION 0
#define LIQUIDFUN_STRING_EXPAND(X) #X
#define LIQUIDFUN_STRING(X) LIQUIDFUN_STRING_EXPAND(X)
static void* b2AllocDefault(int32 size, void* callbackData);
static void b2FreeDefault(void* mem, void* callbackData);
const b2Version b2_liquidFunVersion = {
LIQUIDFUN_VERSION_MAJOR, LIQUIDFUN_VERSION_MINOR,
LIQUIDFUN_VERSION_REVISION,
};
const char *b2_liquidFunVersionString =
"LiquidFun "
LIQUIDFUN_STRING(LIQUIDFUN_VERSION_MAJOR) "."
LIQUIDFUN_STRING(LIQUIDFUN_VERSION_MINOR) "."
LIQUIDFUN_STRING(LIQUIDFUN_VERSION_REVISION);
static int32 b2_numAllocs = 0;
// Initialize default allocator.
static b2AllocFunction b2_allocCallback = b2AllocDefault;
static b2FreeFunction b2_freeCallback = b2FreeDefault;
static void *b2_callbackData = NULL;
// Default implementation of b2AllocFunction.
static void* b2AllocDefault(int32 size, void* callbackData)
{
B2_NOT_USED(callbackData);
return malloc(size);
}
// Default implementation of b2FreeFunction.
static void b2FreeDefault(void* mem, void* callbackData)
{
B2_NOT_USED(callbackData);
free(mem);
}
/// Set alloc and free callbacks to override the default behavior of using
/// malloc() and free() for dynamic memory allocation.
/// Set allocCallback and freeCallback to NULL to restore the default
/// allocator (malloc / free).
void b2SetAllocFreeCallbacks(b2AllocFunction allocCallback,
b2FreeFunction freeCallback, void* callbackData)
{
b2Assert((allocCallback && freeCallback) ||
(!allocCallback && !freeCallback));
b2Assert(0 == b2GetNumAllocs());
if (allocCallback && freeCallback)
{
b2_allocCallback = allocCallback;
b2_freeCallback = freeCallback;
b2_callbackData = callbackData;
}
else
{
b2_allocCallback = b2AllocDefault;
b2_freeCallback = b2FreeDefault;
b2_callbackData = NULL;
}
}
// Memory allocators. Modify these to use your own allocator.
void* b2Alloc(int32 size)
{
b2_numAllocs++;
return b2_allocCallback(size, b2_callbackData);
}
void b2Free(void* mem)
{
b2_numAllocs--;
b2_freeCallback(mem, b2_callbackData);
}
void b2SetNumAllocs(const int32 numAllocs)
{
b2_numAllocs = numAllocs;
}
int32 b2GetNumAllocs()
{
return b2_numAllocs;
}
// You can modify this to use your logging facility.
void b2Log(const char* string, ...)
{
#if DEBUG
va_list args;
va_start(args, string);
vprintf(string, args);
va_end(args);
#else
B2_NOT_USED(string);
#endif
}
class Validator
{
public:
Validator()
{
b2Assert(sizeof(uint64)==8);
b2Assert(sizeof(int64)==8);
}
} validate;
+267
View File
@@ -0,0 +1,267 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
* Copyright (c) 2013 Google, Inc.
*
* 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.
*/
#ifndef B2_SETTINGS_H
#define B2_SETTINGS_H
#include <stddef.h>
#include <assert.h>
#include <float.h>
#define B2_NOT_USED(x) ((void)(x))
#if DEBUG && !defined(NDEBUG)
#define b2Assert(A) assert(A)
#define B2_ASSERT_ENABLED 1
#else
#define b2Assert(A)
#define B2_ASSERT_ENABLED 0
#endif
// Statement which is compiled out when DEBUG isn't defined.
#if DEBUG
#define B2_DEBUG_STATEMENT(A) A
#else
#define B2_DEBUG_STATEMENT(A)
#endif // DEBUG
// Calculate the size of a static array.
#define B2_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
typedef signed char int8;
typedef signed short int16;
typedef signed int int32;
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef float float32;
typedef double float64;
#ifdef WIN32
typedef __int64 int64;
typedef unsigned __int64 uint64;
#else // !WIN32
typedef long long int64;
typedef unsigned long long uint64;
#endif
#define b2_maxFloat FLT_MAX
#define b2_epsilon FLT_EPSILON
#define b2_pi 3.14159265359f
#if !defined(b2Inline)
#if defined(__GNUC__)
#define b2Inline __attribute__((always_inline))
#else
#define b2Inline inline
#endif // defined(__GNUC__)
#endif // !defined(b2Inline)
// We expand the API so that other languages (e.g. Java) can call into
// our C++ more easily. Only set if when the flag is not externally defined.
#if !defined(LIQUIDFUN_EXTERNAL_LANGUAGE_API)
#if SWIG || LIQUIDFUN_UNIT_TESTS
#define LIQUIDFUN_EXTERNAL_LANGUAGE_API 1
#else
#define LIQUIDFUN_EXTERNAL_LANGUAGE_API 0
#endif
#endif
/// @file
/// Global tuning constants based on meters-kilograms-seconds (MKS) units.
///
// Collision
/// The maximum number of contact points between two convex shapes. Do
/// not change this value.
#define b2_maxManifoldPoints 2
/// The maximum number of vertices on a convex polygon. You cannot increase
/// this too much because b2BlockAllocator has a maximum object size.
#define b2_maxPolygonVertices 8
/// This is used to fatten AABBs in the dynamic tree. This allows proxies
/// to move by a small amount without triggering a tree adjustment.
/// This is in meters.
#define b2_aabbExtension 0.1f
/// This is used to fatten AABBs in the dynamic tree. This is used to predict
/// the future position based on the current displacement.
/// This is a dimensionless multiplier.
#define b2_aabbMultiplier 2.0f
/// A small length used as a collision and constraint tolerance. Usually it is
/// chosen to be numerically significant, but visually insignificant.
#define b2_linearSlop 0.005f
/// A small angle used as a collision and constraint tolerance. Usually it is
/// chosen to be numerically significant, but visually insignificant.
#define b2_angularSlop (2.0f / 180.0f * b2_pi)
/// The radius of the polygon/edge shape skin. This should not be modified. Making
/// this smaller means polygons will have an insufficient buffer for continuous collision.
/// Making it larger may create artifacts for vertex collision.
#define b2_polygonRadius (2.0f * b2_linearSlop)
/// Maximum number of sub-steps per contact in continuous physics simulation.
#define b2_maxSubSteps 8
// Dynamics
/// Maximum number of contacts to be handled to solve a TOI impact.
#define b2_maxTOIContacts 32
/// A velocity threshold for elastic collisions. Any collision with a relative linear
/// velocity below this threshold will be treated as inelastic.
#define b2_velocityThreshold 1.0f
/// The maximum linear position correction used when solving constraints. This helps to
/// prevent overshoot.
#define b2_maxLinearCorrection 0.2f
/// The maximum angular position correction used when solving constraints. This helps to
/// prevent overshoot.
#define b2_maxAngularCorrection (8.0f / 180.0f * b2_pi)
/// The maximum linear velocity of a body. This limit is very large and is used
/// to prevent numerical problems. You shouldn't need to adjust this.
#define b2_maxTranslation 2.0f
#define b2_maxTranslationSquared (b2_maxTranslation * b2_maxTranslation)
/// The maximum angular velocity of a body. This limit is very large and is used
/// to prevent numerical problems. You shouldn't need to adjust this.
#define b2_maxRotation (0.5f * b2_pi)
#define b2_maxRotationSquared (b2_maxRotation * b2_maxRotation)
/// This scale factor controls how fast overlap is resolved. Ideally this would be 1 so
/// that overlap is removed in one time step. However using values close to 1 often lead
/// to overshoot.
#define b2_baumgarte 0.2f
#define b2_toiBaugarte 0.75f
// Particle
/// NEON SIMD requires 16-bit particle indices
#if !defined(B2_USE_16_BIT_PARTICLE_INDICES) && defined(LIQUIDFUN_SIMD_NEON)
#define B2_USE_16_BIT_PARTICLE_INDICES
#endif
/// A symbolic constant that stands for particle allocation error.
#define b2_invalidParticleIndex (-1)
#ifdef B2_USE_16_BIT_PARTICLE_INDICES
#define b2_maxParticleIndex 0x7FFF
#else
#define b2_maxParticleIndex 0x7FFFFFFF
#endif
/// The default distance between particles, multiplied by the particle diameter.
#define b2_particleStride 0.75f
/// The minimum particle weight that produces pressure.
#define b2_minParticleWeight 1.0f
/// The upper limit for particle pressure.
#define b2_maxParticlePressure 0.25f
/// The upper limit for force between particles.
#define b2_maxParticleForce 0.5f
/// The maximum distance between particles in a triad, multiplied by the
/// particle diameter.
#define b2_maxTriadDistance 2
#define b2_maxTriadDistanceSquared (b2_maxTriadDistance * b2_maxTriadDistance)
/// The initial size of particle data buffers.
#define b2_minParticleSystemBufferCapacity 256
/// The time into the future that collisions against barrier particles will be detected.
#define b2_barrierCollisionTime 2.5f
// Sleep
/// The time that a body must be still before it will go to sleep.
#define b2_timeToSleep 0.5f
/// A body cannot sleep if its linear velocity is above this tolerance.
#define b2_linearSleepTolerance 0.01f
/// A body cannot sleep if its angular velocity is above this tolerance.
#define b2_angularSleepTolerance (2.0f / 180.0f * b2_pi)
// Memory Allocation
/// Implement this function to use your own memory allocator.
void* b2Alloc(int32 size);
/// If you implement b2Alloc, you should also implement this function.
void b2Free(void* mem);
/// Use this function to override b2Alloc() without recompiling this library.
typedef void* (*b2AllocFunction)(int32 size, void* callbackData);
/// Use this function to override b2Free() without recompiling this library.
typedef void (*b2FreeFunction)(void* mem, void* callbackData);
/// Set alloc and free callbacks to override the default behavior of using
/// malloc() and free() for dynamic memory allocation.
/// Set allocCallback and freeCallback to NULL to restore the default
/// allocator (malloc / free).
void b2SetAllocFreeCallbacks(b2AllocFunction allocCallback,
b2FreeFunction freeCallback,
void* callbackData);
/// Set the number of calls to b2Alloc minus the number of calls to b2Free.
/// This can be used to disable the empty heap check in
/// b2SetAllocFreeCallbacks() which can be useful for testing.
void b2SetNumAllocs(const int32 numAllocs);
/// Get number of calls to b2Alloc minus number of calls to b2Free.
int32 b2GetNumAllocs();
/// Logging function.
void b2Log(const char* string, ...);
/// Version numbering scheme.
/// See http://en.wikipedia.org/wiki/Software_versioning
struct b2Version
{
int32 major; ///< significant changes
int32 minor; ///< incremental changes
int32 revision; ///< bug fixes
};
/// Current version.
/// Version of Box2D, LiquidFun is based upon.
extern b2Version b2_version;
/// Global variable is used to identify the version of LiquidFun.
extern const b2Version b2_liquidFunVersion;
/// String which identifies the current version of LiquidFun.
/// b2_liquidFunVersionString is used by Google developers to identify which
/// applications uploaded to Google Play are using this library. This allows
/// the development team at Google to determine the popularity of the library.
/// How it works: Applications that are uploaded to the Google Play Store are
/// scanned for this version string. We track which applications are using it
/// to measure popularity. You are free to remove it (of course) but we would
/// appreciate if you left it in.
extern const char *b2_liquidFunVersionString;
#endif
+244
View File
@@ -0,0 +1,244 @@
/*
* Copyright (c) 2014 Google, Inc.
*
* 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.
*/
#ifndef B2_SLAB_ALLOCATOR_H
#define B2_SLAB_ALLOCATOR_H
#include <stddef.h>
#include <stdint.h>
#include <new>
#include <Box2D/Common/b2IntrusiveList.h>
#include <Box2D/Common/b2FreeList.h>
#include <Box2D/Common/b2Settings.h>
#include <Box2D/Common/b2TrackedBlock.h>
/// Freelist based allocator for fixed sized items from slabs (memory
/// preallocated from the heap).
/// T should be a class which has a default constructor and implements the
/// member function "b2IntrusiveList* GetListNode()".
/// All objects in a slab are constructed when a slab is created and destructed
/// when a slab is freed.
template<typename T>
class b2SlabAllocator
{
private:
// Information about a slab.
class Slab
{
public:
/// Initialize a slab with the number of items it contains.
Slab(uint32 numberOfItems) :
m_numberOfItems(numberOfItems)
{
B2_NOT_USED(m_padding);
// This assumes that this class is packed on at least a 4-byte
// boundary with no padding. Verify the assumption.
b2Assert(sizeof(*this) == b2_mallocAlignment);
}
/// Empty destructor.
~Slab() { }
/// Get the number of items in this slab.
uint32 GetNumberOfItems() const { return m_numberOfItems; }
/// Get a pointer to the first item in the slab.
T* GetFirstItem() const
{
return (T*)((uint8*)(this + 1));
}
/// Get a pointer to the end of the slab.
/// NOTE: This is a pointer after the last byte of the slab not the
/// last item in the slab.
T* GetItemEnd() const { return GetFirstItem() + GetNumberOfItems(); }
private:
/// Number of items in the slab.
uint32 m_numberOfItems;
/// Padding to align the first item in the slab to b2_mallocAlignment.
uint8 m_padding[b2_mallocAlignment - sizeof(uint32)];
};
public:
/// Initialize the allocator to allocate itemsPerSlab of type T for each
/// slab that is allocated.
b2SlabAllocator(const uint32 itemsPerSlab) :
m_itemsPerSlab(itemsPerSlab)
{
}
/// Free all allocated slabs.
~b2SlabAllocator()
{
FreeAllSlabs();
}
/// Set size of the next allocated slab using the number of items per
/// slab. Setting this value to zero disables further slab allocation.
void SetItemsPerSlab(uint32 itemsPerSlab)
{
m_itemsPerSlab = itemsPerSlab;
}
// Get the size of the next allocated slab.
uint32 GetItemsPerSlab() const
{
return m_itemsPerSlab;
}
/// Allocate a item from the slab.
T* Allocate()
{
// Allocate a slab if needed here.
if (m_freeList.GetFreeList()->GetFreeList().IsEmpty() &&
!AllocateSlab())
return NULL;
return m_freeList.Allocate();
}
/// Free an item from the slab.
void Free(T *object)
{
m_freeList.Free(object);
}
/// Allocate a slab, construct instances of T and add them to the free
/// pool.
bool AllocateSlab()
{
if (!m_itemsPerSlab) return false;
const uint32 slabSize = sizeof(Slab) + (sizeof(T) * m_itemsPerSlab);
void* const memory = m_slabs.Allocate(slabSize);
if (!memory) return false;
Slab* const slab = new (BlockGetSlab(memory)) Slab(m_itemsPerSlab);
T* item = slab->GetFirstItem();
for (uint32 i = 0; i < m_itemsPerSlab; ++i, ++item)
{
m_freeList.AddToFreeList(new (item) T);
}
return true;
}
/// Free all slabs.
void FreeAllSlabs()
{
const b2TypedIntrusiveListNode<b2TrackedBlock>& slabList =
m_slabs.GetList();
while (!slabList.IsEmpty())
{
FreeSlab(BlockGetSlab(slabList.GetNext()->GetMemory()));
}
}
/// Free all empty slabs.
/// This method is slow - O(M^N) - since this class doesn't track
/// the association between each item and slab.
void FreeEmptySlabs()
{
const b2IntrusiveListNode& freeItemList =
m_freeList.GetFreeList()->GetFreeList();
const b2IntrusiveListNode* freeItemListTerminator =
freeItemList.GetTerminator();
const b2TypedIntrusiveListNode<b2TrackedBlock>& slabList =
m_slabs.GetList();
const b2TypedIntrusiveListNode<b2TrackedBlock>* slabListTerminator =
slabList.GetTerminator();
b2TrackedBlock* block = slabList.GetNext();
while (block != slabListTerminator)
{
// Get the Slab from the memory associated with the block.
Slab* const slab = BlockGetSlab(block->GetMemory());
block = block->GetNext();
// Determine the range of memory the Slab owns.
const uint8* const slabItemStart = (uint8*)slab->GetFirstItem();
const uint8* const slabItemEnd = (uint8*)slab->GetItemEnd();
// Count all free items that are owned by the current slab.
uint8 freeItems = 0;
bool empty = false;
for (b2IntrusiveListNode* itemNode = freeItemList.GetNext();
itemNode != freeItemListTerminator;
itemNode = itemNode->GetNext())
{
const uint8* itemNodeAddress = (uint8*)itemNode;
if (itemNodeAddress >= slabItemStart &&
itemNodeAddress <= slabItemEnd)
{
++freeItems;
if (slab->GetNumberOfItems() == freeItems)
{
empty = true;
break;
}
}
}
// If a slab is empty, free it.
if (empty)
{
FreeSlab(slab);
}
}
}
/// Get the item allocator freelist.
const b2TypedFreeList<T>& GetFreeList() const
{
return m_freeList;
}
private:
/// Destroy all objects in a slab and free the slab.
void FreeSlab(Slab * const slab)
{
b2Assert(slab);
const uint32 numberOfItems = slab->GetNumberOfItems();
T* item = slab->GetFirstItem();
for (uint32 i = 0; i < numberOfItems; ++i, ++item)
{
item->~T();
}
slab->~Slab();
m_slabs.Free(slab);
}
/// Get a pointer to a Slab from a block of memory in m_slabs.
Slab* BlockGetSlab(void *memory)
{
return (Slab*)memory;
}
/// Get a pointer to the first item in the array of items referenced by a
/// Slab.
T* SlabGetFirstItem(Slab* slab)
{
return (T*)(slab + 1);
}
private:
/// Contains a list of b2TrackedBlock instances where each b2TrackedBlock's
/// associated user memory contains a Slab followed by instances of T.
b2TrackedBlockAllocator m_slabs;
/// Number of items to allocate in the next allocated slab.
uint32 m_itemsPerSlab;
/// Freelist which contains instances of T.
b2TypedFreeList<T> m_freeList;
};
#endif // B2_SLAB_ALLOCATOR_H
+120
View File
@@ -0,0 +1,120 @@
/*
* Copyright (c) 2006-2009 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/Common/b2StackAllocator.h>
#include <Box2D/Common/b2Math.h>
#include <string.h>
b2StackAllocator::b2StackAllocator()
{
m_index = 0;
m_allocation = 0;
m_maxAllocation = 0;
m_entryCount = 0;
}
b2StackAllocator::~b2StackAllocator()
{
b2Assert(m_index == 0);
b2Assert(m_entryCount == 0);
}
void* b2StackAllocator::Allocate(int32 size)
{
b2Assert(m_entryCount < b2_maxStackEntries);
const int32 roundedSize = (size + ALIGN_MASK) & ~ALIGN_MASK;
b2StackEntry* entry = m_entries + m_entryCount;
entry->size = roundedSize;
if (m_index + roundedSize > b2_stackSize)
{
entry->data = (char*)b2Alloc(roundedSize);
entry->usedMalloc = true;
}
else
{
entry->data = m_data + m_index;
entry->usedMalloc = false;
m_index += roundedSize;
}
m_allocation += roundedSize;
m_maxAllocation = b2Max(m_maxAllocation, m_allocation);
++m_entryCount;
return entry->data;
}
void* b2StackAllocator::Reallocate(void* p, int32 size)
{
b2Assert(m_entryCount > 0);
b2StackEntry* entry = m_entries + m_entryCount - 1;
b2Assert(p == entry->data);
B2_NOT_USED(p);
int32 incrementSize = size - entry->size;
if (incrementSize > 0)
{
if (entry->usedMalloc)
{
void* data = b2Alloc(size);
memcpy(data, entry->data, entry->size);
b2Free(entry->data);
entry->data = (char*)data;
}
else if (m_index + incrementSize > b2_stackSize)
{
void* data = b2Alloc(size);
memcpy(data, entry->data, entry->size);
m_index -= entry->size;
entry->data = (char*)data;
entry->usedMalloc = true;
}
else
{
m_index += incrementSize;
m_allocation += incrementSize;
m_maxAllocation = b2Max(m_maxAllocation, m_allocation);
}
entry->size = size;
}
return entry->data;
}
void b2StackAllocator::Free(void* p)
{
b2Assert(m_entryCount > 0);
b2StackEntry* entry = m_entries + m_entryCount - 1;
b2Assert(p == entry->data);
if (entry->usedMalloc)
{
b2Free(p);
}
else
{
m_index -= entry->size;
}
m_allocation -= entry->size;
--m_entryCount;
p = NULL;
}
int32 b2StackAllocator::GetMaxAllocation() const
{
return m_maxAllocation;
}
+64
View File
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2006-2009 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.
*/
#ifndef B2_STACK_ALLOCATOR_H
#define B2_STACK_ALLOCATOR_H
#include <Box2D/Common/b2Settings.h>
const int32 b2_stackSize = 100 * 1024; // 100k
const int32 b2_maxStackEntries = 32;
struct b2StackEntry
{
char* data;
int32 size;
bool usedMalloc;
};
// This is a stack allocator used for fast per step allocations.
// You must nest allocate/free pairs. The code will assert
// if you try to interleave multiple allocate/free pairs.
class b2StackAllocator
{
public:
enum { MIN_ALIGNMENT = sizeof(void*) }; // Must be a power of 2
enum { ALIGN_MASK = MIN_ALIGNMENT - 1 };
b2StackAllocator();
~b2StackAllocator();
void* Allocate(int32 size);
void* Reallocate(void* p, int32 size);
void Free(void* p);
int32 GetMaxAllocation() const;
private:
char m_data[b2_stackSize];
int32 m_index;
int32 m_allocation;
int32 m_maxAllocation;
b2StackEntry m_entries[b2_maxStackEntries];
int32 m_entryCount;
};
#endif
+67
View File
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2013 Google, Inc.
*
* 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 "b2Stat.h"
#include <algorithm>
#include <cfloat>
b2Stat::b2Stat()
{
Clear();
}
void b2Stat::Record( float32 t )
{
m_total += t;
m_min = std::min(m_min,t);
m_max = std::max(m_max,t);
m_count++;
}
int b2Stat::GetCount() const
{
return m_count;
}
float32 b2Stat::GetMean() const
{
if (m_count == 0)
{
return 0.0f;
}
return (float32)(m_total / m_count);
}
float32 b2Stat::GetMin() const
{
return m_min;
}
float32 b2Stat::GetMax() const
{
return m_max;
}
void b2Stat::Clear()
{
m_count = 0;
m_total = 0;
m_min = FLT_MAX;
m_max = -FLT_MAX;
}
+57
View File
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2013 Google, Inc.
*
* 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.
*/
#ifndef B2_STAT
#define B2_STAT
#include <Box2D/Common/b2Settings.h>
/// Calculates min/max/mean of a set of samples
class b2Stat
{
public:
b2Stat();
/// Record a sample
void Record( float32 t );
/// Returns the number of recorded samples
int GetCount() const;
/// Returns the mean of all recorded samples,
/// Returns 0 if there are no recorded samples
float32 GetMean() const;
/// Returns the min of all recorded samples,
/// FLT_MAX if there are no recorded samples
float32 GetMin() const;
/// Returns the max of all recorded samples,
/// -FLT_MAX if there are no recorded samples
float32 GetMax() const;
/// Erase all recorded samples
void Clear();
private:
int m_count;
float64 m_total;
float32 m_min;
float32 m_max;
};
#endif
+134
View File
@@ -0,0 +1,134 @@
/*
* Copyright (c) 2011 Erin Catto http://box2d.org
* Copyright (c) 2014 Google, Inc.
*
* 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/Common/b2Timer.h>
#if defined(_WIN32)
float64 b2Timer::s_invFrequency = 0.0f;
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef BOOL (WINAPI *SystemGetTimeFunc)(_Out_ LARGE_INTEGER *lpFrequency);
SystemGetTimeFunc systemGetTimeFunc = ::QueryPerformanceCounter;
SystemGetTimeFunc systemGetFreqFunc = ::QueryPerformanceFrequency;
int64 b2Timer::GetTicks()
{
LARGE_INTEGER largeInteger;
systemGetTimeFunc(&largeInteger);
return largeInteger.QuadPart;
}
b2Timer::b2Timer()
{
LARGE_INTEGER largeInteger;
if (s_invFrequency == 0.0f)
{
systemGetFreqFunc(&largeInteger);
s_invFrequency = float64(largeInteger.QuadPart);
if (s_invFrequency > 0.0f)
{
s_invFrequency = 1000.0f / s_invFrequency;
}
}
m_start = GetTicks();
}
void b2Timer::Reset()
{
m_start = GetTicks();
}
float32 b2Timer::GetMilliseconds() const
{
int64 elapsed = GetTicks() - m_start;
return (float32)(s_invFrequency * elapsed);
}
#elif defined(__linux__) || defined (__APPLE__)
#include <sys/time.h>
#include <time.h>
// systemGetTimeFunc is defined with external linkage to allow unit
// test to mock out the system time function
#if defined(__linux__)
typedef int (*SystemGetTimeFunc)(clockid_t clk_id, struct timespec *tp);
SystemGetTimeFunc systemGetTimeFunc = ::clock_gettime;
#elif defined(__APPLE__)
typedef int (*SystemGetTimeFunc)(struct timeval * tp, void * tzp);
SystemGetTimeFunc systemGetTimeFunc = ::gettimeofday;
#endif
int64 b2Timer::GetTicks()
{
static const int NSEC_PER_SEC = 1000000000;
#ifdef __linux__
timespec ts;
systemGetTimeFunc(CLOCK_MONOTONIC,&ts);
return ((int64)ts.tv_sec) * NSEC_PER_SEC + ts.tv_nsec;
#else
timeval t;
systemGetTimeFunc(&t, 0);
return ((int64)t.tv_sec) * NSEC_PER_SEC + t.tv_usec * 1000;
#endif
}
b2Timer::b2Timer()
{
Reset();
}
void b2Timer::Reset()
{
m_start = GetTicks();
}
float32 b2Timer::GetMilliseconds() const
{
static const float32 kTicksToMs = 0.000001f;
return kTicksToMs * (float32)(GetTicks() - m_start);
}
#else
b2Timer::b2Timer()
{
}
void b2Timer::Reset()
{
}
float32 b2Timer::GetMilliseconds() const
{
return 0.0f;
}
#endif
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2011 Erin Catto http://box2d.org
* Copyright (c) 2014 Google, Inc.
*
* 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.
*/
#ifndef B2_TIMER_H
#define B2_TIMER_H
#include <Box2D/Common/b2Settings.h>
/// Timer for profiling. This has platform specific code and may
/// not work on every platform.
class b2Timer
{
public:
/// Constructor
b2Timer();
/// Reset the timer.
void Reset();
/// Get the time since construction or the last reset.
float32 GetMilliseconds() const;
private:
/// Get platform specific tick count
static int64 GetTicks();
#if defined(_WIN32)
static float64 s_invFrequency;
#endif
int64 m_start;
};
#endif
+111
View File
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2014 Google, Inc.
*
* 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/Common/b2TrackedBlock.h>
#include <stddef.h>
#include <stdint.h>
#include <new>
// Initialize this block with a reference to "this".
b2TrackedBlock::b2TrackedBlock()
{
b2TrackedBlock** pointerToThis =
(b2TrackedBlock**)((uint8*)GetMemory() - sizeof(b2TrackedBlock**));
*pointerToThis = this;
}
/// Get the allocated memory associated with this block.
void* b2TrackedBlock::GetMemory() const
{
// The size of data in this without padding.
static const uint32 kSizeOfThisWithNoPadding =
sizeof(*this) - sizeof(m_padding) + sizeof(b2TrackedBlock**);
// Make sure b2_mallocAlignment is base2.
b2Assert(((b2_mallocAlignment - 1) & b2_mallocAlignment) == 0);
// Round the pointer following data in this to b2_mallocAlignment.
uint8* const aligned = (uint8*)(
((uintptr_t)this + kSizeOfThisWithNoPadding + b2_mallocAlignment - 1) &
~((uintptr_t)b2_mallocAlignment - 1));
// Verify offset doesn't overlap data in this.
b2Assert((uintptr_t)aligned - (uintptr_t)this >= kSizeOfThisWithNoPadding);
return aligned;
}
/// Allocate a b2TrackedBlock returning a pointer to memory of size
/// bytes that can be used by the caller.
void* b2TrackedBlock::Allocate(uint32 size)
{
void* memory = (b2TrackedBlock*)b2Alloc(sizeof(b2TrackedBlock) +
size);
if (!memory)
{
return NULL;
}
return (new(memory) b2TrackedBlock)->GetMemory();
}
/// Get a b2TrackedBlock from a pointer to memory returned by
/// b2TrackedBlock::Allocate().
b2TrackedBlock* b2TrackedBlock::GetFromMemory(void *memory)
{
uint8* const aligned = (uint8*)memory;
b2Assert(memory);
b2TrackedBlock **blockPtr = (b2TrackedBlock**)(aligned -
sizeof(b2TrackedBlock**));
b2Assert(*blockPtr);
return *blockPtr;
}
/// Free a block of memory returned by b2TrackedBlock::Allocate()
void b2TrackedBlock::Free(void *memory)
{
Free(GetFromMemory(memory));
}
/// Free a b2TrackedBlock.
void b2TrackedBlock::Free(b2TrackedBlock *block)
{
b2Assert(block);
block->~b2TrackedBlock();
b2Free(block);
}
/// Allocate a block of size bytes using b2TrackedBlock::Allocate().
void* b2TrackedBlockAllocator::Allocate(uint32 size)
{
void *memory = b2TrackedBlock::Allocate(size);
m_blocks.InsertBefore(b2TrackedBlock::GetFromMemory(memory));
return memory;
}
/// Free a block returned by Allocate().
void b2TrackedBlockAllocator::Free(void *memory)
{
b2TrackedBlock::Free(memory);
}
/// Free all allocated blocks.
void b2TrackedBlockAllocator::FreeAll()
{
while (!m_blocks.IsEmpty())
{
b2TrackedBlock::Free(m_blocks.GetNext());
}
}
+92
View File
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2014 Google, Inc.
*
* 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.
*/
#ifndef B2_TRACKED_BLOCK_H
#define B2_TRACKED_BLOCK_H
#include <Box2D/Common/b2IntrusiveList.h>
#include <Box2D/Common/b2Settings.h>
/// Alignment (in bytes) of user memory associated with b2TrackedBlock.
const int32 b2_mallocAlignment = 32;
/// Allocated block of memory that can be tracked in a b2IntrusiveList.
class b2TrackedBlock : public b2TypedIntrusiveListNode<b2TrackedBlock>
{
private:
// Initialize this block with a reference to "this".
b2TrackedBlock();
// Remove the block from the list.
~b2TrackedBlock() { }
public:
/// Get the allocated memory associated with this block.
void* GetMemory() const;
private:
// Padding required to align the pointer to user memory in the block
// to b2_mallocAlignment.
uint8 m_padding[b2_mallocAlignment + sizeof(b2TrackedBlock**)];
public:
/// Allocate a b2TrackedBlock returning a pointer to memory of size
/// bytes that can be used by the caller.
static void* Allocate(uint32 size);
/// Get a b2TrackedBlock from a pointer to memory returned by
/// b2TrackedBlock::Allocate().
static b2TrackedBlock* GetFromMemory(void *memory);
/// Free a block of memory returned by b2TrackedBlock::Allocate()
static void Free(void *memory);
/// Free a b2TrackedBlock.
static void Free(b2TrackedBlock *block);
};
/// Allocator of blocks which are tracked in a list.
class b2TrackedBlockAllocator
{
public:
/// Initialize.
b2TrackedBlockAllocator() {}
/// Free all allocated blocks.
~b2TrackedBlockAllocator()
{
FreeAll();
}
/// Allocate a block of size bytes using b2TrackedBlock::Allocate().
void* Allocate(uint32 size);
/// Free a block returned by Allocate().
void Free(void *memory);
/// Free all allocated blocks.
void FreeAll();
// Get the list of allocated blocks.
const b2TypedIntrusiveListNode<b2TrackedBlock>& GetList() const
{
return m_blocks;
}
private:
b2TypedIntrusiveListNode<b2TrackedBlock> m_blocks;
};
#endif // B2_TRACKED_BLOCK_H
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
Building LiquidFun and Running Examples {#mainpage}
=======================================
LiquidFun is an extension of [Box2D](http://box2d.org), a 2D physics engine
for games.
API documentation is located in the Documentation/ folder and referenced
by our [landing page](../../index.html).
If your project already uses Box2D, please see these instructions for
[Porting from Box2D](md__porting_from_box2_d.html).
Instructions on how to build for:
- [Android](md__building_android.html)
- [iOS](md__buildingi_o_s.html)
- [Linux](md__building_linux.html)
- [Windows](md__building_windows.html)
- [OS X](md__building_o_s_x.html)
- [JavaScript version](md__building_java_script.html)
@@ -0,0 +1,117 @@
# Building for Android
### Version Requirements
Following are the minimum required versions for the tools and libraries you
need for building LiquidFun for Android:
- Android SDK: Android 2.3.3 (API Level 10)
- ADT: 20130917
- NDK: android-ndk-r9
- NDK plugn for Eclipse: bundled with ADT
- cmake (when building from source): 2.8.12.1
### Before Building
- Install the [Android SDK].
- Install the [Android NDK].
### Building
Each LiquidFun project for Android has an associated `AndroidManifest.xml`
file and `jni` subdirectory. For samples, `AndroidManifest.xml` contains
details about how to build an Android package (apk). For libraries, such as
LiquidFun and freeglut, the `AndroidManifest.xml` file informs the `ndk-build`
tool that the directory contains NDK makefiles under the `jni` subdirectory.
The following [Android NDK][] projects live in the liquidfun directory tree.
- `liquidfun/Box2D` - liquidfun dynamic and static libraries.
- `liquidfun/Box2D/HelloWorld` - Non-interactive Hello World sample
application.
- `liquidfun/Box2D/Testbed` - Interactive Testbed sample application.
To build an [Android NDK][] project:
- Open a command line window.
- Go to the working directory containing the project to build.
- Execute ndk-build.
For example, to build the LiquidFun dynamic and static libraries:
cd liquidfun/Box2D
ndk-build
### Executing a Sample
Running a sample requires the Android Development Tools ([ADT][]) plugin and
the [NDK Eclipse plugin][].
#### Running an application using Eclipse:
- Build a project using `ndk-build` (see above).
- Open [ADT][] Eclipse.
- Select "File->Import..." from the menu.
- Select "Android > Existing Android Code Into Workspace", and click "Next".
- Click the "Browse..." button next to `Root Directory:` and select the
project folder (e.g. `liquidfun/Box2D/Testbed`).
- Click "Finish". Eclipse imports the project, and displays it in the
Package Explorer pane.
- Right-click the project, and select "Run->Run As->Android Application"
from the menu.
- If you do not have a physical device, you must define a virtual one.
For details about how to define a virtual device, see [managing avds][].
We dont recommend a virtual device for development.
- If the target is a physical device, unlock the device and observe the application executing.
#### Build and Run from the Command Line:
It's also possible to use `build_apk.sh` to build, deploy, and execute an application.
`build_apk.sh` requires the installation of the [Android NDK][] and [Android SDK][].
On Windows the script requires that you install [Cygwin][], as it
makes use of Bash and common UNIX utilities. In addition, `build_apk.sh`
requires the addition of the [Android SDK][] `sdk/tools`, [Android SDK][]
`sdk/platform-tools` and [Android NDK][] root directories to the `PATH`
environment variable.
To build and run an application using `build_apk.sh`:
- Change into the directory containing the project to build.
- Run build\_apk.sh.
For example, the following will build the Testbed application, deploy it to
the connected device, and start it:
cd liquidfun/Box2D/Testbed
../AndroidUtil/build_apk.sh
### Code Generation
By default, code is generated for devices that support the `armeabi-v7a` ABI.
Alternatively, you can generate a fat `.apk` that includes code for all ABIs.
To do so, override APP\_ABI on ndk-build's command line:
ndk-build APP_ABI=all
### Running Unit Tests
Developers modifying the LiquidFun library can verify that it is still working
correctly by executing the provided unit tests.
Use the run\_tests\_android.sh script to execute unit tests:
./liquidfun/Box2D/Unittests/run_tests_android.sh
`run_tests_android.sh` uses `build_apk.sh` to build, deploy, and
execute each unit test on the connected device.
The "Build and Run from the Command Line" section above describes
the setup requirements for `build_apk.sh`.
[Android SDK]: http://developer.android.com/sdk/index.html
[Android NDK]: http://developer.android.com/tools/sdk/ndk/index.html
[NDK Eclipse plugin]: http://developer.android.com/sdk/index.html
[managing avds]: http://developer.android.com/tools/devices/managing-avds.html
[Cygwin installation]: http://www.cygwin.com/
[ADT]: http://developer.android.com/tools/sdk/eclipse-adt.html
@@ -0,0 +1,92 @@
# Building JavaScript version
### Background
LiquidFun can be translated from C++ into JavaScript by
[Emscripten](http://github.com/kripken/emscripten/wiki).
The entire JavaScript translation is in one file: liquidfun.js.
The LiquidFun source includes a pre-made version of liquidfun.js.
You can reference that liquidfun.js to start writing LiquidFun programs
in JavaScript right away.
However, if you make changes to the LiquidFun C++ code, or if you need
to expose more of the LiquidFun API, you will need to remake liquidfun.js
by following the steps on this page.
### Before Building
We use Emscripten on Linux, but you should be able to use the Emscripten SDK
on Mac or Windows too, if you prefer. Note that Mac and Windows build
environments have not been tested.
- Get Emscripten. See [Installing from Source](http://github.com/kripken/emscripten/wiki/Emscripten-SDK#installing-from-source).
- You also need [Fastcomp](http://github.com/kripken/emscripten/wiki/LLVM-Backend#getting-fastcomp)
- And [Node.js](http://nodejs.org/download/)
- Setup Emscripten.
- As suggested in the Emscripten docs, run "emcc -v"
- Update your ~/.emscripten file. You'll probably want something like this:
- LLVM_ROOT = os.path.expanduser('~/Code/emscripten-fastcomp/build/Release/bin')
- NODE_JS = os.path.expanduser('~/Code/node-v0.10.29-linux-x64/bin/node')
- Get the [Closure Compiler](http://developers.google.com/closure/compiler/).
- Set environment variables. Depending on your setup, you may want to add
some lines like this to your ~/.bashrc file.
- export EMSCRIPTEN=~/Code/emscripten
- export CLOSURE_JAR=~/Code/closure/compiler.jar
### Building
The following commands run Emscripten to translate C++ to lf_core.js,
then combine lf_core.js with the bindings in jsBindings into liquidfun.js.
cd Box2D/lfjs
make
./uglify.sh
### Executing the Testbed
For an example program, try openning `Box2D/lfjs/index.html' in your browser.
This will load a JavaScript version of the LiquidFun Testbed. It uses
the liquidfun.js file that you built above.
### Debugging
The default output of `make` and `uglify.sh` is whitespace-optimized
JavaScript that is difficult to debug. To create an un-optimized version
of the testbed, please follow the steps below.
+ In `lfjs/Makefile` change -O2 to -O0, to disable Emscripten optimizations.
+ In `lfjs/index.html` replace `<script src="liquidfun.js"></script>` with
a similar line for every .js file in `lfjs/uglify.sh`. It should look
something like,
<script src="lf_core.js"></script>
<script src="jsBindings/offsets.js"></script>
<script src="jsBindings/Common/b2Math.js"></script>
<script src="jsBindings/Collision/b2Collision.js"></script>
<script src="jsBindings/Collision/Shapes/b2EdgeShape.js"></script>
<script src="jsBindings/Collision/Shapes/b2PolygonShape.js"></script>
<script src="jsBindings/Collision/Shapes/b2Shape.js"></script>
<script src="jsBindings/Collision/Shapes/b2ChainShape.js"></script>
<script src="jsBindings/Collision/Shapes/b2CircleShape.js"></script>
<script src="jsBindings/Dynamics/b2Body.js"></script>
<script src="jsBindings/Dynamics/b2World.js"></script>
<script src="jsBindings/Dynamics/Joints/b2WheelJoint.js"></script>
<script src="jsBindings/Dynamics/Joints/b2WeldJoint.js"></script>
<script src="jsBindings/Dynamics/Joints/b2GearJoint.js"></script>
<script src="jsBindings/Dynamics/Joints/b2Joint.js"></script>
<script src="jsBindings/Dynamics/Joints/b2FrictionJoint.js"></script>
<script src="jsBindings/Dynamics/Joints/b2RevoluteJoint.js"></script>
<script src="jsBindings/Dynamics/Joints/b2MotorJoint.js"></script>
<script src="jsBindings/Dynamics/Joints/b2PulleyJoint.js"></script>
<script src="jsBindings/Dynamics/Joints/b2DistanceJoint.js"></script>
<script src="jsBindings/Dynamics/Joints/b2PrismaticJoint.js"></script>
<script src="jsBindings/Dynamics/Joints/b2RopeJoint.js"></script>
<script src="jsBindings/Dynamics/Joints/b2MouseJoint.js"></script>
<script src="jsBindings/Dynamics/Contacts/b2Contact.js"></script>
<script src="jsBindings/Dynamics/b2Fixture.js"></script>
<script src="jsBindings/Dynamics/b2WorldCallbacks.js"></script>
<script src="jsBindings/Particle/b2ParticleSystem.js"></script>
<script src="jsBindings/Particle/b2ParticleGroup.js"></script>
<script src="jsBindings/Particle/b2Particle.js"></script>
@@ -0,0 +1,79 @@
# Building for Linux
### Version Requirements
Following are the minimum required versions for the tools and libraries you
need for building LiquidFun for Linux:
- OpenGL: libglapi-mesa 8.0.4 (tested with libglapi-mesa 8.0.4-0ubuntu0)
- GLU: libglu1-mesa-dev 8.0.4 (tested with libglu1-mesa-dev 8.0.4.0ubuntu0)
- cmake (when building from source): 2.8.12.1
### Before Building
Prior to building, install the following components using the [Linux][]
distribution's package manager:
- [cmake][]. You can also manually install from [cmake.org]
(http://cmake.org).
- OpenGL (`libglapi-mesa`).
- GLU (`libglu1-mesa-dev`).
For example, on Ubuntu:
sudo apt-get install cmake
sudo apt-get install libglapi-mesa
sudo apt-get install libglu1-mesa-dev
The sample applications require OpenGL and GLU.
### Building
- Generate makefiles from the [cmake][] project in `liquidfun/Box2D`.
- Execute `make` to build the library and sample applications.
For example:
cd liquidfun/Box2D
cmake -G'Unix Makefiles'
make
To perform a debug build:
cd liquidfun/Box2D
cmake -G'Unix Makefiles' -DCMAKE_BUILD_TYPE=Debug
make
Build targets can be configured using options exposed in
`liquidfun/Box2D/CMakeLists.txt` by using cmake's `-D` option.
Build configuration set using the `-D` option is sticky across subsequent
builds.
For example, if a build is performed using:
cmake -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug
make
to switch to a release build CMAKE_BUILD_TYPE must be explicitly specified:
cmake -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=Release
make
### Executing a Sample
After building the project, you can execute the samples from the command line.
For example:
./liquidfun/Box2D/Testbed/Release/Testbed
### Running Unit Tests
Developers modifying the LiquidFun library can verify that it still working
correctly by executing the provided unit tests.
Use the run\_tests.sh script to execute unit tests:
./liquidfun/Box2D/Unittests/run_tests.sh
[cmake]: http://www.cmake.org
[Linux]: http://en.wikipedia.org/wiki/Linux
[Ubuntu]: http://www.ubuntu.com
@@ -0,0 +1,62 @@
# Building for OS X
You can use [cmake][] to generate an [Xcode][] project for LiquidFun's
Testbed and Unittests on [OS X][].
Alternatively, you can download a pre-generated Xcode
project from the [release page][]. The [Xcode][] project is free of
host-specific dependencies.
### Version Requirements
These are the minimum required versions for building LiquidFun on OS X:
- OS X: Mavericks 10.9.1.
- Xcode: 5.0.1
- Xquartz: 2.7.5 (xorg-server 1.14.4)
- cmake (when building from source) 2.8.12.1
### Before Building
- Install [Xquartz][] to run LiquidFun sample applications.
The Testbed uses [freeglut][], which requires [Xquartz][].
- Reboot your machine. Rebooting sets the `DISPLAY` environment variable for
[Xquartz][], which enables sample applications to run correctly.
### Creating the Xcode project using [cmake][]
When working directly with the source, you can generate the [Xcode][]
project using [cmake][]. [cmake][] version 2.8.12.1 or above is required to
build this project on [OS X][] 10.9 (Mavericks). For example, the following
generates the Xcode project in the liquidfun/Box2D directory.
cd liquidfun/Box2D
cmake -G "Xcode"
### Building with [Xcode][]
- Double-click on `liquidfun/Box2D/Box2D.xcodeproj` to open the project in
[Xcode][].
- Select "Product-->Build" from the menu.
### Executing a Sample
- Select a sample `Scheme`, for example "Testbed-->My Mac 64-bit", from the
combo box to the right of the "Run" button.
- Click the "Run" button.
### Running Unit Tests
Developers modifying the LiquidFun library can verify that it is still working
correctly by executing the provided unit tests.
Use the run\_tests.sh script to execute unit tests:
./liquidfun/Box2D/Unittests/run_tests.sh
[Xquartz]: http://xquartz.macosforge.org/
[cmake]: http://www.cmake.org
[Xcode]: http://developer.apple.com/xcode/
[OS X]: http://www.apple.com/osx/
[freeglut]: http://freeglut.sourceforge.net/
[release page]: http://github.com/google/liquidfun/releases
@@ -0,0 +1,64 @@
# Building for Windows
You can use [cmake][] to generate a [Visual Studio][] project for
LiquidFun's Testbed and Unittests on [Windows][].
Alternatively, you can download a pre-generated Visual Studio solution
from the [release page][]. The Visual Studio solution is free of
host-specific dependencies.
### Version Requirements
These are the minimum required versions for building LiquidFun for Windows:
- Windows: 7
- Visual Studio: 2010 or 2012
- cmake: 2.8.12.1
### Creating the Visual Studio solution using [cmake][]
When working directly with the source, use [cmake][] to generate the
[Visual Studio][] solution and project files. For example, the following
generates the [Visual Studio][] 2012 solution in the `liquidfun/Box2D`
directory:
cd liquidfun\Box2D
cmake -G "Visual Studio 11"
To generate a [Visual Studio][] 2010 solution, use this commend:
cd liquidfun\Box2D
cmake -G "Visual Studio 10"
Running [cmake][] under [cygwin][] requires empty TMP, TEMP, tmp and temp
variables. To generate a [Visual Studio][] solution from a [cygwin][]
bash shell use:
$ cd liquidfun/Box2D
$ ( unset {temp,tmp,TEMP,TMP} ; cmake -G "Visual Studio 11" )
### Building with [Visual Studio][]
- Double-click on `liquidfun/Box2D/Box2D.sln` to open the solution.
- Select "Build-->Build Solution" from the menu.
### Executing a Sample
- Right-click on an example project (e.g Testbed) in the Solution Explorer
pane, and select "Set as StartUp Project".
- Select "Debug-->Start Debugging" from the menu.
### Running Unit Tests
Developers modifying the LiquidFun library can verify that it is still working
correctly by executing the provided unit tests.
Use the run\_tests.bat batch file to execute unit tests:
liquidfun\Box2D\Unittests\run_tests.bat
[cmake]: http://www.cmake.org
[Visual Studio]: http://www.visualstudio.com/
[Windows]: http://windows.microsoft.com/
[cygwin]: http://www.cygwin.com/
[release page]: http://github.com/google/liquidfun/releases
@@ -0,0 +1,32 @@
# Building for iOS
The LiquidFun source contains [Xcode][] projects to build the Testbed
application and EyeCandy demo. Unlike the OS X projects, the iOS Xcode
projects are *not* generated using [cmake][]. They must be maintained
manually.
### Version Requirements
Following are the minimum tested versions for building the tools and
libraries you on iOS.
- OS X: Mavericks 10.9.3.
- Xcode: 5.1.1
### Building with [Xcode][]
- Double-click `liquidfun/Box2D/Testbed/ios/Testbed.xcodeproj` or
`liquidfun/Box2D/EyeCandy/ios/EyeCandy.xcodeproj` to open the project
in [Xcode][].
- Select "Product-->Build" from the menu.
### Executing a Testbed or EyeCandy
- Select a `Scheme`, for example "Testbed-->iPhone Retina (3.5-inch)",
from the combo box to the right of the "Run" button.
- Click the "Run" button.
[Xcode]: http://developer.apple.com/xcode/
[cmake]: http://www.cmake.org
[OS X]: http://www.apple.com/osx/
@@ -0,0 +1,40 @@
# Porting from Box2D
LiquidFun extends Erin Catto's popular Box2D physics engine. If your project
already uses Box2D, LiquidFun should be a simple drop-in replacement for you.
### Drop-in LiquidFun Code
To port from Box2D to LiquidFun, simply replace your Box2D directory with
liquidfun/Box2D/Box2D. Your code should compile and run as it did under Box2D,
and you will have access to LiquidFun's particle simulation API.
LiquidFun is based off of a specific version of Box2D. If your version of
Box2D is different from LiquidFun's you may have to adjust your code to
match API differences.
Please see the [Release Notes][] to find the version of Box2D that LiquidFun
is based on.
### Specify `particleIterations`
LiquidFun adds a `particleIterations` parameter to b2World::Step. If you don't
specify `particleIterations`, we calculate a default value based on the
physical constants in your b2ParticleSystems.
However, the number of `particleIterations` affects performance and stability
significantly. You should experiment with the number of iterations to find
a suitable balance for your situation.
### Start using b2ParticleSystem
`b2ParticleSystems` are created with `b2World::CreateParticleSystem`, the
same way `b2Bodys` are created with `b2World::CreateBody`.
For details, please see [Particle Module][] in the [Programmer's Guide][]
[Release Notes]: ../../ReleaseNotes.html
[Particle Module]: ../../Programmers-Guide/html/md__chapter11__particles.html
[Programmer's Guide]: ../../Programmers-Guide/html/index.html
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,274 @@
# Introduction
<img src="liquidfun-logo-square-small.png" alt="LiquidFun logo" style="float:right;"/>
[About](#about)<br/>
[Prerequisites](#pre)<br/>
[About this manual](#atm)<br/>
[Feedback and reporting bugs](#frb)<br/>
[Core concepts](#cc)<br/>
[Modules](#mo)<br/>
[Units](#un)<br/>
[Factories and definitions](#fd)<br/>
<a name="About"></a><br/>
## About
LiquidFun is based on Erin Catto's [Box2D library](http://www.box2d.org), which
provides 2D, rigid-body simulation in games. LiquidFun extends Box2D to provide
[particle physics and fluid dynamics](md__chapter11__particles.html).
Programmers can use LiquidFun in their games to make objects move in realistic
ways and make the game world more interactive. From the game engine's point of
view, a physics engine is just a system for procedural animation.
LiquidFun is written in portable C++. Most of the types defined in the engine
begin with the b2 prefix. Hopefully this is sufficient to avoid name clashing
with your game engine.
<a name="pre"></a><br/>
## Prerequisites
In this manual I'll assume you are familiar with basic physics concepts, such
as mass, force, torque, and impulses. If not, please first consult Google
search and Wikipedia.
LiquidFun is based on the Box2D library, which was created as part of a
physics tutorial at the Game Developer Conference. You can get these tutorials
from the download section of Box2D.org.
Since LiquidFun is written in C++, you are expected to be experienced in C++
programming. LiquidFun should not be your first C++ programming project! You
should be comfortable with compiling, linking, and debugging.
Caution
LiquidFun should not be your first C++ project. Please learn C++
programming, compiling, linking, and debugging before working with LiquidFun.
There are many resources for this on the net.
<a name="atm"></a><br/>
## About this manual
This manual covers the majority of the LiquidFun API. However, not every
aspect is covered. You are encouraged to look at the testbed included with
LiquidFun to learn more. Also, the LiquidFun code base has comments formatted
for Doxygen, so it is easy to create a hyper-linked API document.
This manual is only updated with new releases. The version in source control
is likely to be out of date.
<a name="frb"></a><br/>
## Feedback and Reporting Bugs
If you have a question or feedback about LiquidFun, please leave a comment in
the forum. This is also a great place for community discussion.
LiquidFun issues are tracked using a Google code project. This is a great way
to track issues and ensures that your issue will not be lost in the depths of
the forums.
Please file bugs and feature requests here:
[http://github.com/google/liquidfun/issues](http://github.com/google/liquidfun/issues)
You can help to ensure your issue gets fixed if you provide sufficient
detail. A testbed example that reproduces the problem is ideal. You can read
about the testbed later in this document.
<a name="cc"></a><br/>
## Core Concepts
LiquidFun works with several fundamental concepts and objects. We briefly
define these objects here and more details are given later in this
document.<br/>
<br/>
### shape
A shape is 2D geometrical object, such as a circle or polygon.<br/>
### rigid body
A chunk of matter that is so strong that the distance between any two bits of
matter on the chunk is constant. They are hard like a diamond. In the
following discussion we use body interchangeably with rigid body.<br/>
### fixture
A fixture binds a shape to a body and adds material properties such as
density, friction, and restitution. A fixture puts a shape into the collision
system (broad-phase) so that it can collide with other shapes.<br/>
### constraint
A constraint is a physical connection that removes degrees of freedom from
bodies. A 2D body has 3 degrees of freedom (two translation coordinates and
one rotation coordinate). If we take a body and pin it to the wall (like a
pendulum) we have constrained the body to the wall. At this point the body can
only rotate about the pin, so the constraint has removed 2 degrees of
freedom.<br/>
### contact constraint
A special constraint designed to prevent penetration of rigid bodies and to
simulate friction and restitution. You do not create contact constraints; they
are created automatically by LiquidFun.<br/>
### joint
This is a constraint used to hold two or more bodies together. LiquidFun
supports several joint types: revolute, prismatic, distance, and more. Some
joints may have limits and motors.<br/>
### joint limit
A joint limit restricts the range of motion of a joint. For example, the human
elbow only allows a certain range of angles.<br/>
### joint motor
A joint motor drives the motion of the connected bodies according to the
joint's degrees of freedom. For example, you can use a motor to drive the
rotation of an elbow.<br/>
### world
A physics world is a collection of bodies, fixtures, and constraints that
interact together. LiquidFun supports the creation of multiple worlds, but
this is usually not necessary or desirable.<br/>
### solver
The physics world has a solver that is used to advance time and to resolve
contact and joint constraints. The LiquidFun solver is a high performance
iterative solver that operates in order N time, where N is the number of
constraints.<br/>
### continuous collision
The solver advances bodies in time using discrete time steps. Without
intervention this can lead to tunneling.<br/>
<br/>
<img align="center" src="image_0.png" alt="Tunneling" height="293"
width="275"><br/>
LiquidFun contains specialized algorithms to deal with tunneling. First, the
collision algorithms can interpolate the motion of two bodies to find the
first time of impact (TOI). Second, there is a sub-stepping solver that moves
bodies to their first time of impact and then resolves the collision.
<a name="mo"></a><br/>
## Modules
LiquidFun is composed of three modules: Common, Collision, and Dynamics. The
Common module has code for allocation, math, and settings. The Collision
module defines shapes, a broad-phase, and collision functions/queries. Finally
the Dynamics module provides the simulation world, bodies, fixtures, and
joints.<br/>
<br/>
<img align="center" src="image_1.png" alt="Modules" height="229" width="217">
<a name="un"></a><br/>
## Units
LiquidFun works with floating point numbers and tolerances have to be used to
make LiquidFun perform well. These tolerances have been tuned to work well
with meters-kilogram-second (MKS) units. In particular, LiquidFun has been
tuned to work well with moving shapes between 0.1 and 10 meters. So this means
objects between soup cans and buses in size should work well. Static shapes
may be up to 50 meters long without trouble.
Being a 2D physics engine, it is tempting to use pixels as your units.
Unfortunately this will lead to a poor simulation and possibly weird behavior.
An object of length 200 pixels would be seen by LiquidFun as the size of a 45
story building.
Caution
LiquidFun is tuned for MKS units. Keep the size of moving objects
roughly between 0.1 and 10 meters. You'll need to use some scaling system when
you render your environment and actors. The LiquidFun testbed does this by
using an OpenGL viewport transform. DO NOT USE PIXELS.
It is best to think of LiquidFun bodies as moving billboards upon which you
attach your artwork. The billboard may move in a unit system of meters, but
you can convert that to pixel coordinates with a simple scaling factor. You
can then use those pixel coordinates to place your sprites, etc. You can also
account for flipped coordinate axes.
LiquidFun uses radians for angles. The body rotation is stored in radians and
may grow unbounded. Consider normalizing the angle of your bodies if the
magnitude of the angle becomes too large (use b2Body::SetAngle).
Caution
LiquidFun uses radians, not degrees.
<a name="fd"></a><br/>
## Factories and Definitions
Fast memory management plays a central role in the design of the LiquidFun
API. So when you create a b2Body or a b2Joint, you need to call the factory
functions on b2World. You should never try to allocate these types in another
manner.
There are creation functions:
&nbsp;&nbsp;&nbsp;`b2Body* b2World::CreateBody(const b2BodyDef* def)`<br/>
&nbsp;&nbsp;&nbsp;`b2Joint* b2World::CreateJoint(const b2JointDef* def)`<br/>
&nbsp;&nbsp;&nbsp;`And there are corresponding destruction functions:`<br/>
&nbsp;&nbsp;&nbsp;`void b2World::DestroyBody(b2Body* body)`<br/>
&nbsp;&nbsp;&nbsp;`void b2World::DestroyJoint(b2Joint* joint)`<br/>
When you create a body or joint, you need to provide a definition. These
definitions contain all the information needed to build the body or joint. By
using this approach we can prevent construction errors, keep the number of
function parameters small, provide sensible defaults, and reduce the number of
accessors.
Since fixtures (shapes) must be parented to a body, they are created and
destroyed using a factory method on b2Body:
&nbsp;&nbsp;&nbsp;`b2Fixture* b2Body::CreateFixture(const b2FixtureDef*
def)`<br/>
&nbsp;&nbsp;&nbsp;`void b2Body::DestroyFixture(b2Fixture* fixture)`<br/>
There is also shortcut to create a fixture directly from the shape and density.
&nbsp;&nbsp;&nbsp;`b2Fixture* b2Body::CreateFixture(const b2Shape* shape,
float32 density)`<br/>
Factories do not retain references to the definitions. So you can create
definitions on the stack and keep them in temporary resources.
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
[ ][Chapter02]
[ ][Chapter03]
[ ][Chapter04]
[ ][Chapter05]
[ ][Chapter06]
[ ][Chapter07]
[ ][Chapter08]
[ ][Chapter09]
[ ][Chapter10]
[ ][Chapter11]
[ ][Chapter12]
[ ][Chapter13]
[ ][Chapter14]
[ ][Chapter15]
[Chapter02]: md__chapter02__hello__box2_d.html
[Chapter03]: md__chapter03__common.html
[Chapter04]: md__chapter04__collision__module.html
[Chapter05]: md__chapter05__dynamics__module.html
[Chapter06]: md__chapter06__bodies.html
[Chapter07]: md__chapter07__fixtures.html
[Chapter08]: md__chapter08__joints.html
[Chapter09]: md__chapter09__contacts.html
[Chapter10]: md__chapter10__world.html
[Chapter11]: md__chapter11__particles.html
[Chapter12]: md__chapter12__loose__ends.html
[Chapter13]: md__chapter13__debug__drawing.html
[Chapter14]: md__chapter14__limitations.html
[Chapter15]: md__chapter15__references.html
@@ -0,0 +1,290 @@
# Hello LiquidFun
[About](#about)<br/>
[Creating a world](#cr)<br/>
[Creating a ground box](#cgb)<br/>
[Creating a dynamic body](#cdb)<br/>
[Simulating the World (of LiquidFun)](#stw)<br>
[Cleanup](#cl)<br>
[The testbed](#tb)<br>
<br/>
<a name="about">
## About
In the distribution of LiquidFun is a Hello World project. The program creates
a large ground box and a small dynamic box. This code does not contain any
graphics. All you will see is text output in the console of the box's position
over time.
This is a good example of how to get up and running with LiquidFun.
<a name="cr"></a><br/>
## Creating a World
Every LiquidFun program begins with the creation of a b2World object. b2World
is the physics hub that manages memory, objects, and simulation. You can
allocate the physics world on the stack, heap, or data section.
It is easy to create a LiquidFun world. First, we define the gravity vector.
b2Vec2 gravity(0.0f, -10.0f);
Now we create the world object. Note that we are creating the world on the
stack, so the world must remain in scope.
b2World world(gravity);
So now we have our physics world, let's start adding some stuff to it.
<a name="cgb"></a><br/>
## Creating a Ground Box
Bodies are built using the following steps:
1. Define a body with position, damping, etc.
2. Use the world object to create the body.
3. Define fixtures with a shape, friction, density, etc.
4. Create fixtures on the body.
For step 1 we create the ground body. For this we need a body definition. With
the body definition we specify the initial position of the ground body.
&nbsp;&nbsp;&nbsp;`b2BodyDef groundBodyDef;`<br/>
&nbsp;&nbsp;&nbsp;`groundBodyDef.position.Set(0.0f, -10.0f);`<br/>
For step 2 the body definition is passed to the world object to create the
ground body. The world object does not keep a reference to the body
definition. Bodies are static by default. Static bodies don't collide with
other static bodies and are immovable.
&nbsp;&nbsp;&nbsp;`b2Body* groundBody = world.CreateBody(&groundBodyDef);`<br/>
For step 3 we create a ground polygon. We use the SetAsBox shortcut to form
the ground polygon into a box shape, with the box centered on the origin of
the parent body.
&nbsp;&nbsp;&nbsp;`b2PolygonShape groundBox;`<br/>
&nbsp;&nbsp;&nbsp;`groundBox.SetAsBox(50.0f, 10.0f);`<br/>
The SetAsBox function takes the **half**-**width** and **half**-**height**
(extents). So in this case the ground box is 100 units wide (x-axis) and 20
units tall (y-axis). LiquidFun is tuned for meters, kilograms, and seconds. So
you can consider the extents to be in meters. LiquidFun generally works best
when objects are the size of typical real world objects. For example, a barrel
is about 1 meter tall. Due to the limitations of floating point arithmetic,
using LiquidFun to model the movement of glaciers or dust particles is not a
good idea.
We finish the ground body in step 4 by creating the shape fixture. For this
step we have a shortcut. We do not have a need to alter the default fixture
material properties, so we can pass the shape directly to the body without
creating a fixture definition. Later we will see how to use a fixture
definition for customized material properties. The second parameter is the
shape density in kilograms per meter squared. A static body has zero mass by
definition, so the density is not used in this case.
&nbsp;&nbsp;&nbsp;`groundBody->CreateFixture(&groundBox, 0.0f);`
LiquidFun does not keep a reference to the shape. It clones the data into a
new b2Shape object.
Note that every fixture must have a parent body, even fixtures that are
static. However, you can attach all static fixtures to a single static body.
When you attach a shape to a body using a fixture, the shapes coordinates
become local to the body. So when the body moves, so does the shape. A
fixtures world transform is inherited from the parent body. A fixture does
not have a transform independent of the body. So we dont move a shape
around on the body. Moving or modifying a shape that is on a body is not
supported. The reason is simple: a body with morphing shapes is not a rigid
body, but LiquidFun is a rigid body engine. Many of the assumptions made in
LiquidFun are based on the rigid body model. If this is violated many things
will break
<a name="cdb"></a><br/>
## Creating a Dynamic Body
So now we have a ground body. We can use the same technique to create a
dynamic body. The main difference, besides dimensions, is that we must
establish the dynamic body's mass properties.
First we create the body using CreateBody. By default bodies are static, so we
should set the b2BodyType at construction time to make the body dynamic.
&nbsp;&nbsp;&nbsp;`b2BodyDef bodyDef;`<br/>
&nbsp;&nbsp;&nbsp;`bodyDef.type = b2_dynamicBody;`<br/>
&nbsp;&nbsp;&nbsp;`bodyDef.position.Set(0.0f, 4.0f);`<br/>
&nbsp;&nbsp;&nbsp;`b2Body* body = world.CreateBody(&bodyDef);`<br/>
Caution
You must set the body type to b2_dynamicBody if you want the body to move in response to forces.
Next we create and attach a polygon shape using a fixture definition. First we
create a box shape:
&nbsp;&nbsp;&nbsp;`b2PolygonShape dynamicBox;`<br/>
&nbsp;&nbsp;&nbsp;`dynamicBox.SetAsBox(1.0f, 1.0f);`<br/>
Next we create a fixture definition using the box. Notice that we set density
to 1. The default density is zero. Also, the friction on the shape is set to
0.3.
&nbsp;&nbsp;&nbsp;`b2FixtureDef fixtureDef;`<br/>
&nbsp;&nbsp;&nbsp;`fixtureDef.shape = &dynamicBox;`<br/>
&nbsp;&nbsp;&nbsp;`fixtureDef.density = 1.0f;`<br/>
&nbsp;&nbsp;&nbsp;`fixtureDef.friction = 0.3f;`<br/>
Caution
A dynamic body should have at least one fixture with a non-zero density. Otherwise you will get strange behavior.
Using the fixture definition we can now create the fixture. This automatically
updates the mass of the body. You can add as many fixtures as you like to a
body. Each one contributes to the total mass.
&nbsp;&nbsp;&nbsp;`body->CreateFixture(&fixtureDef);`<br/>
That's it for initialization. We are now ready to begin simulating.
<a name="stw"></a><br/>
## Simulating the World (of LiquidFun)
So we have initialized the ground box and a dynamic box. Now we are ready to
set Newton loose to do his thing. We just have a couple more issues to
consider.
LiquidFun uses a computational algorithm called an integrator. Integrators
simulate the physics equations at discrete points of time. This goes along
with the traditional game loop where we essentially have a flip book of
movement on the screen. So we need to pick a time step for LiquidFun.
Generally physics engines for games like a time step at least as fast as 60Hz
or 1/60 seconds. You can get away with larger time steps, but you will have to
be more careful about setting up the definitions for your world. We also don't
like the time step to change much. A variable time step produces variable
results, which makes it difficult to debug. So don't tie the time step to your
frame rate (unless you really, really have to). Without further ado, here is
the time step.
&nbsp;&nbsp;&nbsp;`float32 timeStep = 1.0f / 60.0f;`<br/>
In addition to the integrator, LiquidFun also uses a larger bit of code called
a constraint solver. The constraint solver solves all the constraints in the
simulation, one at a time. A single constraint can be solved perfectly.
However, when we solve one constraint, we slightly disrupt other constraints.
To get a good solution, we need to iterate over all constraints a number of
times.
There are two phases in the constraint solver: a velocity phase and a position
phase. In the velocity phase the solver computes the impulses necessary for
the bodies to move correctly. In the position phase the solver adjusts the
positions of the bodies to reduce overlap and joint detachment. Each phase has
its own iteration count. In addition, the position phase may exit iterations
early if the errors are small.
The suggested iteration count for LiquidFun is 8 for velocity and 3 for
position. You can tune this number to your liking, just keep in mind that this
has a trade-off between performance and accuracy. Using fewer iterations
increases performance but accuracy suffers. Likewise, using more iterations
decreases performance but improves the quality of your simulation. For this
simple example, we don't need much iteration. Here are our chosen iteration
counts.
&nbsp;&nbsp;&nbsp;`int32 velocityIterations = 6;`<br/>
&nbsp;&nbsp;&nbsp;`int32 positionIterations = 2;`<br/>
Note that the time step and the iteration count are completely unrelated. An
iteration is not a sub-step. One solver iteration is a single pass over all
the constraints within a time step. You can have multiple passes over the
constraints within a single time step.
We are now ready to begin the simulation loop. In your game the simulation
loop can be merged with your game loop. In each pass through your game loop
you call b2World::Step. Just one call is usually enough, depending on your
frame rate and your physics time step.
The Hello World program was designed to be simple, so it has no graphical
output. The code prints out the position and rotation of the dynamic body.
Here is the simulation loop that simulates 60 time steps for a total of 1
second of simulated time.
&nbsp;&nbsp;&nbsp;`for (int32 i = 0; i < 60; ++i)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;` world.Step(timeStep,
velocityIterations, positionIterations);`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;` b2Vec2 position =
body->GetPosition();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;` float32 angle =
body->GetAngle();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`printf("%4.2f %4.2f %4.2f\n", position.x,
position.y, angle);`<br/>
&nbsp;&nbsp;&nbsp;`}`
The output shows the box falling and landing on the ground box. Your output
should look like this:
0.00 4.00 0.00
0.00 3.99 0.00
0.00 3.98 0.00
...
0.00 1.25 0.00
0.00 1.13 0.00
0.00 1.01 0.00
<a name="cl"></a><br/>
## Cleanup
When a world leaves scope or is deleted by calling delete on a pointer, all
the memory reserved for bodies, fixtures, and joints is freed. This is done to
improve performance and make your life easier. However, you will need to
nullify any body, fixture, or joint pointers you have because they will become
invalid.
<a name="tb"></a><br/>
## The Testbed
Once you have conquered the HelloWorld example, you should start looking at
LiquidFun's testbed. The testbed is a unit-testing framework and demo
environment. Here are some of the features:
* Camera with pan and zoom.
* Mouse picking of shapes attached to dynamic bodies.
* Extensible set of tests.
* GUI for selecting tests, parameter tuning, and debug drawing options.
* Pause and single step simulation.
* Text rendering
<img align="center" src="image_2.gif" alt="Modules" height="300"
width="336"><br/>
The testbed has many examples of LiquidFun usage in the test cases and the
framework itself. I encourage you to explore and tinker with the testbed as
you learn LiquidFun.
Note: the testbed is written using freeglut and GLUI. The testbed is not part
of the LiquidFun library. The LiquidFun library is agnostic about rendering.
As shown by the HelloWorld example, you don't need a renderer to use LiquidFun.
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,93 @@
# Common
[About](#about)<br/>
[Settings](#settings)<br/>
[Memory Management](#mm)<br/>
[Math](#math)
<a name="about">
## About
The Common module contains settings, memory management, and vector math.
<a name="settings">
## Settings
The header b2Settings.h contains:
* Types such as int32 and float32
* Constants
* Allocation wrappers
* The version number
### Types
LiquidFun defines various types such as float32, int8, etc. to make it easy to
determine the size of structures.
### Constants
LiquidFun defines several constants. These are all documented in b2Settings.h.
Normally you do not need to adjust these constants.
LiquidFun uses floating point math for collision and simulation. Due to
round-off error some numerical tolerances are defined. Some tolerances are
absolute and some are relative. Absolute tolerances use MKS units.
### Allocation wrappers
The settings file defines b2Alloc and b2Free for large allocations. You may
forward these calls to your own memory management system.
### Version
The b2Version structure holds the current version so you can query this at
run-time.
<a name="mm">
## Memory Management
A large number of the decisions about the design of LiquidFun were based on
the need for quick and efficient use of memory. In this section I will discuss
how and why LiquidFun allocates memory.
LiquidFun tends to allocate a large number of small objects (around 50-300
bytes). Using the system heap through malloc or new for small objects is
inefficient and can cause fragmentation. Many of these small objects may have
a short life span, such as contacts, but can persist for several time steps.
So we need an allocator that can efficiently provide heap memory for these
objects.
LiquidFun's solution is to use a small object allocator (SOA) called
b2BlockAllocator. The SOA keeps a number of growable pools of varying sizes.
When a request is made for memory, the SOA returns a block of memory that best
fits the requested size. When a block is freed, it is returned to the pool.
Both of these operations are fast and cause little heap traffic.
Since LiquidFun uses a SOA, you should never new or malloc a body, fixture, or
joint. However, you do have to allocate a b2World on your own. The b2World
class provides factories for you to create bodies, fixtures, and joints. This
allows LiquidFun to use the SOA and hide the gory details from you. Never,
call delete or free on a body, fixture, or joint.
While executing a time step, LiquidFun needs some temporary workspace memory.
For this, it uses a stack allocator called b2StackAllocator to avoid per-step
heap allocations. You don't need to interact with the stack allocator, but
it's good to know it's there.
<a name="math">
## Math
LiquidFun includes a simple small vector and matrix module. This has been
designed to suit the internal needs of LiquidFun and the API. All the members
are exposed, so you may use them freely in your application.
The math library is kept simple to make LiquidFun easy to port and maintain.
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,438 @@
# Collision Module
[About](#about)<br/>
[Shapes](#shapes)<br/>
[Unary Geometric Queries](#un)<br/>
[Binary Functions](#bf)<br/>
[Dynamic Tree](#dt)<br/>
[Broad-Phase](#bp)<br/>
<a name="about">
## About
The Collision module contains shapes and functions that operate on them. The
module also contains a dynamic tree and broad-phase to acceleration collision
processing of large systems.
The collision module is designed to be usable outside of the dynamic system.
For example, you can use the dynamic tree for other aspects of your game
besides physics.
However, the main purpose of LiquidFun is to provide a rigid body physics
engine, so the using the collision module by itself may feel limited for some
applications. Likewise, I will not make a strong effort to document it or
polish the APIs.
<a name="shapes">
## Shapes
Shapes describe collision geometry and may be used independently of physics
simulation. At a minimum, you should understand how to create shapes that can
be later attached to rigid bodies.
LiquidFun shapes implement the b2Shape base class. The base class defines
functions to:
* Test a point for overlap with the shape.
* Perform a ray cast against the shape.
* Compute the shape's AABB.
* Compute the mass properties of the shape.
In addition, each shape has a type member and a radius. The radius even
applies to polygons, as discussed below.
Keep in mind that a shape does not know about bodies and stand apart from the
dynamics system. Shapes are stored in a compact form that is optimized for
size and performance. As such, shapes are not easily moved around. You have to
manually set the shape vertex positions to move a shape. However, when a shape
is attached to a body using a fixture, the shapes move rigidly with the host
body. In summary:
* When a shape is **not** attached to a body, you can view its vertices as
being expressed in world-space.
* When a shape is attached to a body, you can view its vertices as being
expressed in local coordinates.
### Circle Shapes
Circle shapes have a position and radius. Circles are solid. You cannot make
a hollow circle using the circle shape.
&nbsp;&nbsp;&nbsp;`b2CircleShape circle;`<br/>
&nbsp;&nbsp;&nbsp;`circle.m_p.Set(2.0f, 3.0f);`<br/>
&nbsp;&nbsp;&nbsp;`circle.m_radius = 0.5f;`<br/>
### Polygon Shapes
Polygon shapes are solid convex polygons. A polygon is convex when all line
segments connecting two points in the interior do not cross any edge of the
polygon. Polygons are solid and never hollow. A polygon must have 3 or more
vertices.
<img align="center" src="image_3.gif" alt="Polygon shapes" height="125"
width="223"><br/>
Polygon vertices are stored with a counter clockwise winding (CCW). We must be
careful because the notion of CCW is with respect to a right-handed coordinate
system with the z-axis pointing out of the plane. This might turn out to be
clockwise on your screen, depending on your coordinate system conventions.
<img align="center" src="image_4.png" alt="Polygon vertices" height="176"
width="347"><br/>
The polygon members are public, but you should use initialization functions to
create a polygon. The initialization functions create normal vectors and
perform validation.
You can create a polygon shape by passing in a vertex array. The maximal size
of the array is controlled by b2_maxPolygonVertices which has a default value
of 8. This is sufficient to describe most convex polygons.
The b2PolygonShape::Set function automatically computes the convex hull and
establishes the proper winding order. This function is fast when the number of
vertices is low. If you increase b2_maxPolygonVertices, then the convex hull
computation might become slow. Also note that the convex hull function may
eliminate and/or re-order the points you provide.
&nbsp;&nbsp;&nbsp;`// This defines a triangle in CCW order.`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 vertices[3];`<br/>
&nbsp;&nbsp;&nbsp;`vertices[0].Set(0.0f, 0.0f);`<br/>
&nbsp;&nbsp;&nbsp;`vertices[1].Set(1.0f, 0.0f);`<br/>
&nbsp;&nbsp;&nbsp;`vertices[2].Set(0.0f, 1.0f);`<br/>
&nbsp;&nbsp;&nbsp;`int32 count = 3;`<br/>
&nbsp;&nbsp;&nbsp;`b2PolygonShape polygon;`<br/>
&nbsp;&nbsp;&nbsp;`polygon.Set(vertices, count);`<br/>
The polygon shape has some convenience functions to create boxes.
&nbsp;&nbsp;&nbsp;`void SetAsBox(float32 hx, float32 hy);`<br/>
&nbsp;&nbsp;&nbsp;`void SetAsBox(float32 hx, float32 hy, const b2Vec2& center,
float32 angle);`<br/>
Polygons inherit a radius from b2Shape. The radius creates a skin around the
polygon. The skin is used in stacking scenarios to keep polygons slightly
separated. This allows continuous collision to work against the core polygon.
<img align="center" src="image_5.png" alt="Polygon skin" height="270"
width="300"><br/>
The polygon skin helps prevent tunneling by keeping the polygons separated.
This results in small gaps between the shapes. Your visual representation can
be larger than the polygon to hide any gaps.
<img align="center" src="image_6.png" alt="Polygons collide" height="109"
width="322"><br/>
### Edge Shapes
Edge shapes are line segments. These are provided to assist in making a
free-form static environment for your game. A major limitation of edge shapes
is that they can collide with circles and polygons but not with themselves.
The collision algorithms used by LiquidFun require that at least one of two
colliding shapes have volume. Edge shapes have no volume, so edge-edge
collision is not possible.
&nbsp;&nbsp;&nbsp;`// This an edge shape.`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 v1(0.0f, 0.0f);`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 v2(1.0f, 0.0f);`<br/>
&nbsp;&nbsp;&nbsp;`b2EdgeShape edge;`<br/>
&nbsp;&nbsp;&nbsp;`edge.Set(v1, v2);`<br/>
In many cases a game environment is constructed by connecting several edge
shapes end-to-end. This can give rise to an unexpected artifact when a polygon
slides along the chain of edges. In the figure below we see a box colliding
with an internal vertex. These *ghost* collisions are caused when the polygon
collides with an internal vertex generating an internal collision normal.
<img align="center" src="image_7.png" alt="Ghost collision" height="154"
width="315"><br/>
If edge1 did not exist this collision would seem fine. With edge1 present, the
internal collision seems like a bug. But normally when LiquidFun collides two
shapes, it views them in isolation.
Fortunately, the edge shape provides a mechanism for eliminating ghost
collisions by storing the adjacent *ghost *vertices. LiquidFun uses these
ghost vertices to prevent internal collisions.
<img align="center" src="image_8.png" alt="Ghost vertices" height="153"
width="332"><br/>
&nbsp;&nbsp;&nbsp;`// This is an edge shape with ghost vertices.`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 v0(1.7f, 0.0f);`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 v1(1.0f, 0.25f);`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 v2(0.0f, 0.0f);`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 v3(-1.7f, 0.4f);`<br/>
&nbsp;&nbsp;&nbsp;`b2EdgeShape edge;`<br/>
&nbsp;&nbsp;&nbsp;`edge.Set(v1, v2);`<br/>
&nbsp;&nbsp;&nbsp;`edge.m_hasVertex0 = true;`<br/>
&nbsp;&nbsp;&nbsp;`edge.m_hasVertex3 = true;`<br/>
&nbsp;&nbsp;&nbsp;`edge.m_vertex0 = v0;`<br/>
&nbsp;&nbsp;&nbsp;`edge.m_vertex3 = v3;`<br/>
In general stitching edges together this way is a bit wasteful and tedious.
This brings us to chain shapes.
### Chain Shapes
The chain shape provides an efficient way to connect many edges together to
construct your static game worlds. Chain shapes automatically eliminate ghost
collisions and provide two-sided collision.
<img align="center" src="image_9.png" alt="Chain shape" height="165"
width="305"><br/>
&nbsp;&nbsp;&nbsp;`// This a chain shape with isolated vertices`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 vs[4];`<br/>
&nbsp;&nbsp;&nbsp;`vs[0].Set(1.7f, 0.0f);`<br/>
&nbsp;&nbsp;&nbsp;`vs[1].Set(1.0f, 0.25f);`<br/>
&nbsp;&nbsp;&nbsp;`vs[2].Set(0.0f, 0.0f);`<br/>
&nbsp;&nbsp;&nbsp;`vs[3].Set(-1.7f, 0.4f);`<br/>
&nbsp;&nbsp;&nbsp;`b2ChainShape chain;`<br/>
&nbsp;&nbsp;&nbsp;`chain.CreateChain(vs, 4);`<br/>
You may have a scrolling game world and would like to connect several chains
together. You can connect chains together using ghost vertices, like we did
with b2EdgeShape.
&nbsp;&nbsp;&nbsp;`// Install ghost vertices`<br/>
&nbsp;&nbsp;&nbsp;`chain.SetPrevVertex(b2Vec2(3.0f, 1.0f));`<br/>
&nbsp;&nbsp;&nbsp;`chain.SetNextVertex(b2Vec2(-2.0f, 0.0f));`<br/>
You may also create loops automatically.
&nbsp;&nbsp;&nbsp;`// Create a loop. The first and last vertices are
connected.`<br/>
&nbsp;&nbsp;&nbsp;`b2ChainShape chain;`<br/>
&nbsp;&nbsp;&nbsp;`chain.CreateLoop(vs, 4);`<br/>
Self-intersection of chain shapes is not supported. It might work, it might
not. The code that prevents ghost collisions assumes there are no
self-intersections of the chain. Also, very close vertices can cause problems.
Make sure all your edges are longer than b2_linearSlop (5mm).
<img align="center" src="image_10.png" alt="No self-intersection" height="178"
width="335"><br/>
Each edge in the chain is treated as a child shape and can be accessed by
index. When a chain shape is connected to a body, each edge gets its own
bounding box in the broad-phase collision tree.
&nbsp;&nbsp;&nbsp;`// Visit each child edge.`<br/>
&nbsp;&nbsp;&nbsp;`for (int32 i = 0; i < chain.GetChildCount(); ++i)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2EdgeShape edge;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`chain.GetChildEdge(&edge, i);`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`…`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
<a name="un">
## Unary Geometric Queries
You can perform a couple of geometric queries on a single shape.
### Shape Point Test
You can test a point for overlap with a shape. You provide a transform for the
shape and a world point.
&nbsp;&nbsp;&nbsp;`b2Transfrom transform;`<br/>
&nbsp;&nbsp;&nbsp;`transform.SetIdentity();`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 point(5.0f, 2.0f);`<br/>
&nbsp;&nbsp;&nbsp;`bool hit = shape->TestPoint(transform, point);`<br/>
Edge and chain shapes always return false, even if the chain is a loop.
### Shape Ray Cast
You can cast a ray at a shape to get the point of first intersection and
normal vector. No hit will register if the ray starts inside the shape. A
child index is included for chain shapes because the ray cast will only check
a single edge at a time.
&nbsp;&nbsp;&nbsp;`b2Transfrom transform;`<br/>
&nbsp;&nbsp;&nbsp;`transform.SetIdentity();`<br/>
&nbsp;&nbsp;&nbsp;`b2RayCastInput input;`<br/>
&nbsp;&nbsp;&nbsp;`input.p1.Set(0.0f, 0.0f, 0.0f);`<br/>
&nbsp;&nbsp;&nbsp;`input.p2.Set(1.0f, 0.0f, 0.0f);`<br/>
&nbsp;&nbsp;&nbsp;`input.maxFraction = 1.0f;`<br/>
&nbsp;&nbsp;&nbsp;`int32 childIndex = 0;`<br/>
&nbsp;&nbsp;&nbsp;`b2RayCastOutput output;`<br/>
&nbsp;&nbsp;&nbsp;`bool hit = shape->RayCast(&output, input, transform,
childIndex);`<br/>
&nbsp;&nbsp;&nbsp;`if (hit)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Vec2 hitPoint = input.p1 +
output.fraction * (input.p2 input.p1);`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`…`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
<a name="bf">
## Binary Functions
The Collision module contains bilateral functions that take a pair of shapes
and compute some results. These include:
* Overlap
* Contact manifolds
* Distance
* Time of impact
### Overlap
You can test two shapes for overlap using this function:
&nbsp;&nbsp;&nbsp;`b2Transform xfA = …, xfB = …;`<br/>
&nbsp;&nbsp;&nbsp;`bool overlap = b2TestOverlap(shapeA, indexA, shapeB,
indexB, xfA, xfB);`<br/>
Again you must provide child indices to for the case of chain shapes.
### Contact Manifolds
LiquidFun has functions to compute contact points for overlapping shapes. If
we consider circle-circle or circle-polygon, we can only get one contact point
and normal. In the case of polygon-polygon we can get two points. These points
share the same normal vector so LiquidFun groups them into a manifold
structure. The contact solver takes advantage of this to improve stacking
stability.
<img align="center" src="image_11.png" alt="Manifold structure" height="300"
width="419"><br/>
Normally you dont need to compute contact manifolds directly, however you
will likely use the results produced in the simulation.
The b2Manifold structure holds a normal vector and up to two contact points.
The normal and points are held in local coordinates. As a convenience for the
contact solver, each point stores the normal and tangential (friction)
impulses.
The data stored in b2Manifold is optimized for internal use. If you need this
data, it is usually best to use the b2WorldManifold structure to generate the
world coordinates of the contact normal and points. You need to provide a
b2Manifold and the shape transforms and radii.
&nbsp;&nbsp;&nbsp;`b2WorldManifold worldManifold;`<br/>
&nbsp;&nbsp;&nbsp;`worldManifold.Initialize(&manifold, transformA,
shapeA.m_radius, transformB, shapeB.m_radius);`<br/>
&nbsp;&nbsp;&nbsp;`for (int32 i = 0; i < manifold.pointCount; ++i)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Vec2 point =
worldManifold.points[i];`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`…`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
Notice that the world manifold uses the point count from the original manifold.
During simulation shapes may move and the manifolds may change. Points may be
added or removed. You can detect this using b2GetPointStates.
&nbsp;&nbsp;&nbsp;`b2PointState state1[2], state2[2];`<br/>
&nbsp;&nbsp;&nbsp;`b2GetPointStates(state1, state2, &manifold1,
&manifold2);`<br/>
&nbsp;&nbsp;&nbsp;`if (state1[0] == b2_removeState)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`// process event`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
### Distance
The b2Distance function can be used to compute the distance between two
shapes. The distance function needs both shapes to be converted into a
b2DistanceProxy. There is also some caching used to warm start the distance
function for repeated calls. You can see the details in b2Distance.h.
<img align="center" src="image_12.png" alt="Distance" height="134"
width="271"><br/>
### Time of Impact
If two shapes are moving fast, they may *tunnel* through each other in a
single time step.
<img align="center" src="image_13.png" alt="Time-of-impact tunneling"
height="133" width="234"><br/>
The b2TimeOfImpact function is used to determine the time when two moving
shapes collide. This is called the *time of impact *(TOI). The main purpose of
b2TimeOfImpact is for tunnel prevention. In particular, it is designed to
prevent moving objects from tunneling outside of static level geometry.
This function accounts for rotation and translation of both shapes, however if
the rotations are large enough, then the function may miss a collision.
However the function will still report a non-overlapped time and will capture
all translational collisions.
The time of impact function identities an initial separating axis and ensures
the shapes do not cross on that axis. This might miss collisions that are
clear at the final positions. While this approach may miss some collisions, it
is very fast and adequate for tunnel prevention.
<img align="center" src="image_14.png" alt="Captured collision" height="169"
width="297"><br/>
<img align="center" src="image_15.png" alt="Missed collision" height="172"
width="297"><br/>
It is difficult to put a restriction on the rotation magnitude. There may be
cases where collisions are missed for small rotations. Normally, these missed
rotational collisions should not harm game play. They tend to be glancing
collisions.
The function requires two shapes (converted to b2DistanceProxy) and two
b2Sweep structures. The sweep structure defines the initial and final
transforms of the shapes.
You can use fixed rotations to perform a *shape cast*. In this case, the time
of impact function will not miss any collisions.
<a name="dt">
## Dynamic Tree
The b2DynamicTree class is used by LiquidFun to organize large numbers of
shapes efficiently. The class does not know about shapes. Instead it operates
on axis-aligned bounding boxes (AABBs) with user data pointers.
The dynamic tree is a hierarchical AABB tree. Each internal node in the tree
has two children. A leaf node is a single user AABB. The tree uses rotations
to keep the tree balanced, even in the case of degenerate input.
The tree structure allows for efficient ray casts and region queries. For
example, you may have hundreds of shapes in your scene. You could perform a
ray cast against the scene in a brute force manner by ray casting each shape.
This would be inefficient because it does not take advantage of shapes being
spread out. Instead, you can maintain a dynamic tree and perform ray casts
against the tree. This traverses the ray through the tree skipping large
numbers of shapes.
A region query uses the tree to find all leaf AABBs that overlap a query AABB.
This is faster than a brute force approach because many shapes can be skipped.
<img align="center" src="image_16.png" alt="Ray cast" height="249"
width="248"><br/>
<img align="center" src="image_17.png" alt="Region query" height="210"
width="231"><br/>
Normally you will not use the dynamic tree directly. Rather you will go
through the b2World class for ray casts and region queries. If you plan to
instantiate your own dynamic tree, you can learn how to use it by looking at
how LiquidFun uses it.
<a name="bp">
## Broad-phase
Collision processing in a physics step can be divided into narrow-phase and
broad-phase. In the narrow-phase we compute contact points between pairs of
shapes. Imagine we have N shapes. Using brute force, we would need to perform
the narrow-phase for N*N/2 pairs.
The b2BroadPhase class reduces this load by using a dynamic tree for pair
management. This greatly reduces the number of narrow-phase calls.
Normally you do not interact with the broad-phase directly. Instead, LiquidFun
creates and manages a broad-phase internally. Also, b2BroadPhase is designed
with LiquidFuns simulation loop in mind, so it is likely not suited for
other use cases.
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,37 @@
# Dynamics Module
[Overview](#ov)<br/>
<a name="ov">
## Overview
The Dynamics module is the most complex part of LiquidFun and is the part you
likely interact with the most. The Dynamics module sits on top of the Common
and Collision modules, so you should be somewhat familiar with those by now.
The Dynamics module contains:
* fixture class
* rigid body class
* contact class
* joint classes
* world class
* listener classes
There are many dependencies between these classes so it is difficult to
describe one class without referring to another. In the following, you may see
some references to classes that have not been described yet. Therefore, you
may want to quickly skim this chapter before reading it closely.
The dynamics module is covered in the following chapters.
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,363 @@
# Bodies
[About](#about)<br/>
[Body Definition](#bd)<br/>
[Body Factory](#bf)<br/>
[Using a Body](#ub)<br/>
<a name="about">
## About
Bodies have position and velocity. You can apply forces, torques, and impulses
to bodies. Bodies can be static, kinematic, or dynamic. Here are the body type
definitions:
### b2_staticBody
A static body does not move under simulation and behaves as if it has infinite
mass. Internally, LiquidFun stores zero for the mass and the inverse mass.
Static bodies can be moved manually by the user. A static body has zero
velocity. Static bodies do not collide with other static or kinematic bodies.
### b2_kinematicBody
A kinematic body moves under simulation according to its velocity. Kinematic
bodies do not respond to forces. They can be moved manually by the user, but
normally a kinematic body is moved by setting its velocity. A kinematic body
behaves as if it has infinite mass, however, LiquidFun stores zero for the
mass and the inverse mass. Kinematic bodies do not collide with other
kinematic or static bodies.
### b2_dynamicBody
A dynamic body is fully simulated. They can be moved manually by the user, but
normally they move according to forces. A dynamic body can collide with all
body types. A dynamic body always has finite, non-zero mass. If you try to set
the mass of a dynamic body to zero, it will automatically acquire a mass of
one kilogram and it wont rotate.
Bodies are the backbone for fixtures (shapes). Bodies carry fixtures and move
them around in the world. Bodies are always rigid bodies in LiquidFun. That
means that two fixtures attached to the same rigid body never move relative to
each other and fixtures attached to the same body dont collide.
Fixtures have collision geometry and density. Normally, bodies acquire their
mass properties from the fixtures. However, you can override the mass
properties after a body is constructed.
You usually keep pointers to all the bodies you create. This way you can query
the body positions to update the positions of your graphical entities. You
should also keep body pointers so you can destroy them when you are done with
them.
<a name="bd">
## Body Definition
Before a body is created you must create a body definition (b2BodyDef). The
body definition holds the data needed to create and initialize a body.
LiquidFun copies the data out of the body definition; it does not keep a
pointer to the body definition. This means you can recycle a body definition
to create multiple bodies.
Lets go over some of the key members of the body definition.
### Body Type
As discussed at the beginning of this chapter, there are three different body
types: static, kinematic, and dynamic. You should establish the body type at
creation because changing the body type later is expensive.
&nbsp;&nbsp;&nbsp;`bodyDef.type = b2_dynamicBody;`<br/>
Setting the body type is mandatory.
### Position and Angle
The body definition gives you the chance to initialize the position of the
body on creation. This has far better performance than creating the body at
the world origin and then moving the body.
Caution
Do not create a body at the origin and then move it. If you create
several bodies at the origin, then performance will suffer.
A body has two main points of interest. The first point is the body's origin.
Fixtures and joints are attached relative to the body's origin. The second
point of interest is the center of mass. The center of mass is determined from
mass distribution of the attached shapes or is explicitly set with b2MassData.
Much of LiquidFun's internal computations use the center of mass position. For
example b2Body stores the linear velocity for the center of mass.
When you are building the body definition, you may not know where the center
of mass is located. Therefore you specify the position of the body's origin.
You may also specify the body's angle in radians, which is not affected by the
position of the center of mass. If you later change the mass properties of the
body, then the center of mass may move on the body, but the origin position
does not change and the attached shapes and joints do not move.
&nbsp;&nbsp;&nbsp;`bodyDef.position.Set(0.0f, 2.0f); // the body's origin
position.`<br/>
&nbsp;&nbsp;&nbsp;`bodyDef.angle = 0.25f * b2_pi; // the body's angle in
radians.`<br/>
A rigid body is also a frame of reference. You can define fixtures and joints
in that frame. Those fixtures and joint anchors never move in the local frame
of the body.
### Damping
Damping is used to reduce the world velocity of bodies. Damping is different
than friction because friction only occurs with contact. Damping is not a
replacement for friction and the two effects should be used together.
Damping parameters should be between 0 and infinity, with 0 meaning no
damping, and infinity meaning full damping. Normally you will use a damping
value between 0 and 0.1. I generally do not use linear damping because it
makes bodies look floaty.
&nbsp;&nbsp;&nbsp;`bodyDef.linearDamping = 0.0f;`<br/>
&nbsp;&nbsp;&nbsp;`bodyDef.angularDamping = 0.01f;`<br/>
Damping is approximated for stability and performance. At small damping values
the damping effect is mostly independent of the time step. At larger damping
values, the damping effect will vary with the time step. This is not an issue
if you use a fixed time step (recommended).
### Gravity Scale
You can use the gravity scale to adjust the gravity on a single body. Be
careful though, increased gravity can decrease stability.
&nbsp;&nbsp;&nbsp;`// Set the gravity scale to zero so this body will
float`<br/>
&nbsp;&nbsp;&nbsp;`bodyDef.gravityScale = 0.0f;`<br/>
### Sleep Parameters
What does sleep mean? Well it is expensive to simulate bodies, so the less we
have to simulate the better. When a body comes to rest we would like to stop
simulating it.
When LiquidFun determines that a body (or group of bodies) has come to rest,
the body enters a sleep state which has very little CPU overhead. If a body is
awake and collides with a sleeping body, then the sleeping body wakes up.
Bodies will also wake up if a joint or contact attached to them is destroyed.
You can also wake a body manually.
The body definition lets you specify whether a body can sleep and whether a
body is created sleeping.
&nbsp;&nbsp;&nbsp;`bodyDef.allowSleep = true;`<br/>
&nbsp;&nbsp;&nbsp;`bodyDef.awake = true;`<br/>
### Fixed Rotation
You may want a rigid body, such as a character, to have a fixed rotation. Such
a body should not rotate, even under load. You can use the fixed rotation
setting to achieve this:
&nbsp;&nbsp;&nbsp;`bodyDef.fixedRotation = true;`<br/>
The fixed rotation flag causes the rotational inertia and its inverse to be
set to zero.
### Bullets
Game simulation usually generates a sequence of images that are played at some
frame rate. This is called discrete simulation. In discrete simulation, rigid
bodies can move by a large amount in one time step. If a physics engine
doesn't account for the large motion, you may see some objects incorrectly
pass through each other. This effect is called tunneling.
By default, LiquidFun uses continuous collision detection (CCD) to prevent
dynamic bodies from tunneling through static bodies. This is done by sweeping
shapes from their old position to their new positions. The engine looks for
new collisions during the sweep and computes the time of impact (TOI) for
these collisions. Bodies are moved to their first TOI and then halted for the
remainder of the time step.
Normally CCD is not used between dynamic bodies. This is done to keep
performance reasonable. In some game scenarios you need dynamic bodies to use
CCD. For example, you may want to shoot a high speed bullet at a stack of
dynamic bricks. Without CCD, the bullet might tunnel through the bricks.
Fast moving objects in LiquidFun can be labeled as bullets. Bullets will
perform CCD with both static and dynamic bodies. You should decide what bodies
should be bullets based on your game design. If you decide a body should be
treated as a bullet, use the following setting.
&nbsp;&nbsp;&nbsp;`bodyDef.bullet = true;`<br/>
The bullet flag only affects dynamic bodies.
LiquidFun performs continuous collision sequentially, so bullets may miss fast
moving bodies.
### Activation
You may wish a body to be created but not participate in collision or
dynamics. This state is similar to sleeping except the body will not be woken
by other bodies and the body's fixtures will not be placed in the broad-phase.
This means the body will not participate in collisions, ray casts, etc.
You can create a body in an inactive state and later re-activate it.
&nbsp;&nbsp;&nbsp;`bodyDef.active = true;`<br/>
Joints may be connected to inactive bodies. These joints will not be
simulated. You should be careful when you activate a body that its joints are
not distorted.
### User Data
User data is a void pointer. This gives you a hook to link your application
objects to bodies. You should be consistent to use the same object type for
all body user data.
&nbsp;&nbsp;&nbsp;`b2BodyDef bodyDef;`<br/>
&nbsp;&nbsp;&nbsp;`bodyDef.userData = &myActor;`<br/>
<a name="bf">
## Body Factory
Bodies are created and destroyed using a body factory provided by the world
class. This lets the world create the body with an efficient allocator and add
the body to the world data structure.
Bodies can be dynamic or static depending on the mass properties. Both body
types use the same creation and destruction methods.
&nbsp;&nbsp;&nbsp;`b2Body* dynamicBody = myWorld->CreateBody(&bodyDef);`<br/>
&nbsp;&nbsp;&nbsp;`... do stuff …`<br/>
&nbsp;&nbsp;&nbsp;`myWorld->DestroyBody(dynamicBody);`<br/>
&nbsp;&nbsp;&nbsp;`dynamicBody = NULL;`<br/>
Caution
You should never use new or malloc to create a body. The world won't
know about the body and the body won't be properly initialized.
Static bodies do not move under the influence of other bodies. You may
manually move static bodies, but you should be careful so that you don't
squash dynamic bodies between two or more static bodies. Friction will not
work correctly if you move a static body. Static bodies never collide with
static or kinematic bodies. It is faster to attach several shapes to a static
body than to create several static bodies with a single shape on each one.
Internally, LiquidFun sets the mass and inverse mass of static bodies to zero.
This makes the math work out so that most algorithms don't need to treat
static bodies as a special case.
LiquidFun does not keep a reference to the body definition or any of the data
it holds (except user data pointers). So you can create temporary body
definitions and reuse the same body definitions.
LiquidFun allows you to avoid destroying bodies by deleting your b2World
object, which does all the cleanup work for you. However, you should be
mindful to nullify body pointers that you keep in your game engine.
When you destroy a body, the attached fixtures and joints are automatically
destroyed. This has important implications for how you manage shape and joint
pointers.
<a name="ub">
## Using a Body
After creating a body, there are many operations you can perform on the body.
These include setting mass properties, accessing position and velocity,
applying forces, and transforming points and vectors.
### Mass Data
Every body has a mass (scalar), center of mass (2-vector), and rotational
inertia (scalar). For static bodies, the mass and rotational inertia are set
to zero. When a body has fixed rotation, its rotational inertia is zero.
Normally the mass properties of a body are established automatically when
fixtures are added to the body. You can also adjust the mass of a body at
run-time. This is usually done when you have special game scenarios that
require altering the mass.
&nbsp;&nbsp;&nbsp;`void SetMassData(const b2MassData* data);`<br/>
After setting a body's mass directly, you may wish to revert to the natural
mass dictated by the fixtures. You can do this with:
&nbsp;&nbsp;&nbsp;`void ResetMassData();`<br/>
The body's mass data is available through the following functions:
&nbsp;&nbsp;&nbsp;`float32 GetMass() const;`
&nbsp;&nbsp;&nbsp;`float32 GetInertia() const;`
&nbsp;&nbsp;&nbsp;`const b2Vec2& GetLocalCenter() const;`
&nbsp;&nbsp;&nbsp;`void GetMassData(b2MassData* data) const;`
### State Information
There are many aspects to the body's state. You can access this state data
efficiently through the following functions:
&nbsp;&nbsp;&nbsp;`void SetType(b2BodyType type);`
&nbsp;&nbsp;&nbsp;`b2BodyType GetType();`
&nbsp;&nbsp;&nbsp;`void SetBullet(bool flag);`
&nbsp;&nbsp;&nbsp;`bool IsBullet() const;`
&nbsp;&nbsp;&nbsp;`void SetSleepingAllowed(bool flag);`
&nbsp;&nbsp;&nbsp;`bool IsSleepingAllowed() const;`
&nbsp;&nbsp;&nbsp;`void SetAwake(bool flag);`
&nbsp;&nbsp;&nbsp;`bool IsAwake() const;`
&nbsp;&nbsp;&nbsp;`void SetActive(bool flag);`
&nbsp;&nbsp;&nbsp;`bool IsActive() const;`
&nbsp;&nbsp;&nbsp;`void SetFixedRotation(bool flag);`
&nbsp;&nbsp;&nbsp;`bool IsFixedRotation() const;`
### Position and Velocity
You can access the position and rotation of a body. This is common when
rendering your associated game actor. You can also set the position, although
this is less common since you will normally use LiquidFun to simulate movement.
&nbsp;&nbsp;&nbsp;`bool SetTransform(const b2Vec2& position, float32
angle);`<br/>
&nbsp;&nbsp;&nbsp;`const b2Transform& GetTransform() const;`<br/>
&nbsp;&nbsp;&nbsp;`const b2Vec2& GetPosition() const;`<br/>
&nbsp;&nbsp;&nbsp;`float32 GetAngle() const;`<br/>
You can access the center of mass position in local and world coordinates.
Much of the internal simulation in LiquidFun uses the center of mass. However,
you should normally not need to access it. Instead you will usually work with
the body transform. For example, you may have a body that is square. The body
origin might be a corner of the square, while the center of mass is located at
the center of the square.
&nbsp;&nbsp;&nbsp;`const b2Vec2& GetWorldCenter() const;`<br/>
&nbsp;&nbsp;&nbsp;`const b2Vec2& GetLocalCenter() const;`<br/>
You can access the linear and angular velocity. The linear velocity is for the
center of mass. Therefore, the linear velocity may change if the mass
properties change.
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,201 @@
# Fixtures
[About](#about)<br/>
[Fixture Creation](#fc)<br/>
[Sensors](#se)<br/>
<a name="about">
## About
Recall that shapes dont know about bodies and may be used independently of
the physics simulation. Therefore LiquidFun provides the b2Fixture class to
attach shapes to bodies. A body may have zero or more fixtures. A body with
multiple fixtures is sometimes called a *compound body.*
Fixtures hold the following:
* a single shape
* broad-phase proxies
* density, friction, and restitution
* collision filtering flags
* back pointer to the parent body
* user data
* sensor flag
These are described in the following sections.
<a name="fc">
## Fixture Creation
Fixtures are created by initializing a fixture definition and then passing the
definition to the parent body.
&nbsp;&nbsp;&nbsp;`b2FixtureDef fixtureDef;`<br/>
&nbsp;&nbsp;&nbsp;`fixtureDef.shape = &myShape;`<br/>
&nbsp;&nbsp;&nbsp;`fixtureDef.density = 1.0f;`<br/>
&nbsp;&nbsp;&nbsp;`b2Fixture* myFixture =
myBody->CreateFixture(&fixtureDef);`<br/>
This creates the fixture and attaches it to the body. You do not need to store
the fixture pointer since the fixture will automatically be destroyed when the
parent body is destroyed. You can create multiple fixtures on a single body.
You can destroy a fixture on the parent body. You may do this to model a
breakable object. Otherwise you can just leave the fixture alone and let the
body destruction take care of destroying the attached fixtures.
&nbsp;&nbsp;&nbsp;`myBody->DestroyFixture(myFixture);`<br/>
### Density
The fixture density is used to compute the mass properties of the parent body.
The density can be zero or positive. You should generally use similar
densities for all your fixtures. This will improve stacking stability.
The mass of a body is not adjusted when you set the density. You must call
ResetMassData for this to occur.
&nbsp;&nbsp;&nbsp;`fixture->SetDensity(5.0f);`<br/>
&nbsp;&nbsp;&nbsp;`body->ResetMassData();`<br/>
### Friction
Friction is used to make objects slide along each other realistically.
LiquidFun supports static and dynamic friction, but uses the same parameter
for both. Friction is simulated accurately in LiquidFun and the friction
strength is proportional to the normal force (this is called Coulomb
friction). The friction parameter is usually set between 0 and 1, but can be
any non-negative value. A friction value of 0 turns off friction and a value
of 1 makes the friction strong. When the friction force is computed between
two shapes, LiquidFun must combine the friction parameters of the two parent
fixtures. This is done with the geometric mean:
&nbsp;&nbsp;&nbsp;`float32 friction;`<br/>
&nbsp;&nbsp;&nbsp;`friction = sqrtf(fixtureA->friction *
fixtureB->friction);`<br/>
So if one fixture has zero friction then the contact will have zero friction.
You can override the default mixed friction using b2Contact::SetFriction. This
is usually done in the b2ContactListener callback.
### Restitution
Restitution is used to make objects bounce. The restitution value is usually
set to be between 0 and 1. Consider dropping a ball on a table. A value of
zero means the ball won't bounce. This is called an inelastic collision. A
value of one means the ball's velocity will be exactly reflected. This is
called a perfectly elastic collision. Restitution is combined using the
following formula.
&nbsp;&nbsp;&nbsp;`float32 restitution;`<br/>
&nbsp;&nbsp;&nbsp;`restitution = b2Max(fixtureA->restitution,
fixtureB->restitution);`<br/>
Restitution is combined this way so that you can have a bouncy super ball
without having a bouncy floor.
You can override the default mixed restitution using
b2Contact::SetRestitution. This is usually done in the b2ContactListener
callback.
When a shape develops multiple contacts, restitution is simulated
approximately. This is because LiquidFun uses an iterative solver. LiquidFun
also uses inelastic collisions when the collision velocity is small. This is
done to prevent jitter. See b2_velocityThreshold in b2Settings.h.
### Filtering
Collision filtering allows you to prevent collision between fixtures. For
example, say you make a character that rides a bicycle. You want the bicycle
to collide with the terrain and the character to collide with the terrain, but
you don't want the character to collide with the bicycle (because they must
overlap). LiquidFun supports such collision filtering using categories and
groups.
LiquidFun supports 16 collision categories. For each fixture you can specify
which category it belongs to. You also specify what other categories this
fixture can collide with. For example, you could specify in a multiplayer game
that all players don't collide with each other and monsters don't collide with
each other, but players and monsters should collide. This is done with masking
bits. For example:
&nbsp;&nbsp;&nbsp;`playerFixtureDef.filter.categoryBits = 0x0002;`<br/>
&nbsp;&nbsp;&nbsp;`monsterFixtureDef.filter.categoryBits = 0x0004;`<br/>
&nbsp;&nbsp;&nbsp;`playerFixtureDef.filter.maskBits = 0x0004;`<br/>
&nbsp;&nbsp;&nbsp;`monsterFixtureDef.filter.maskBits = 0x0002;`<br/>
Here is the rule for a collision to occur:
&nbsp;&nbsp;&nbsp;`uint16 catA = fixtureA.filter.categoryBits;`<br/>
&nbsp;&nbsp;&nbsp;`uint16 maskA = fixtureA.filter.maskBits;`<br/>
&nbsp;&nbsp;&nbsp;`uint16 catB = fixtureB.filter.categoryBits;`<br/>
&nbsp;&nbsp;&nbsp;`uint16 maskB = fixtureB.filter.maskBits;`<br/>
&nbsp;&nbsp;&nbsp;`if ((catA & maskB) != 0 && (catB & maskA) != 0)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; `// fixtures can collide`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
Collision groups let you specify an integral group index. You can have all
fixtures with the same group index always collide (positive index) or never
collide (negative index). Group indices are usually used for things that are
somehow related, like the parts of a bicycle. In the following example,
fixture1 and fixture2 always collide, but fixture3 and fixture4 never collide.
&nbsp;&nbsp;&nbsp;`fixture1Def.filter.groupIndex = 2;`<br/>
&nbsp;&nbsp;&nbsp;`fixture2Def.filter.groupIndex = 2;`<br/>
&nbsp;&nbsp;&nbsp;`fixture3Def.filter.groupIndex = -8;`<br/>
&nbsp;&nbsp;&nbsp;`fixture4Def.filter.groupIndex = -8;`<br/>
Collisions between fixtures of different group indices are filtered according
the category and mask bits. In other words, group filtering has higher
precedence than category filtering.
Note that additional collision filtering occurs in LiquidFun. Here is a list:
* A fixture on a static body can only collide with a dynamic body.
* A fixture on a kinematic body can only collide with a dynamic body.
* Fixtures on the same body never collide with each other.
* You can optionally enable/disable collision between fixtures on bodies
connected by a joint.
Sometimes you might need to change collision filtering after a fixture has
already been created. You can get and set the b2Filter structure on an
existing fixture using b2Fixture::GetFilterData and b2Fixture::SetFilterData.
Note that changing the filter data will not add or remove contacts until the
next time step (see the World class).
<a name="se">
## Sensors
Sometimes game logic needs to know when two fixtures overlap yet there should
be no collision response. This is done by using sensors. A sensor is a fixture
that detects collision but does not produce a response.
You can flag any fixture as being a sensor. Sensors may be static, kinematic,
or dynamic. Remember that you may have multiple fixtures per body and you can
have any mix of sensors and solid fixtures. Also, sensors only form contacts
when at least one body is dynamic, so you will not get a contact for kinematic
versus kinematic, kinematic versus static, or static versus static.
Sensors do not generate contact points. There are two ways to get the state of
a sensor:
1. `b2Contact::IsTouching`
2. `b2ContactListener::BeginContact and EndContact`
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,434 @@
# Joints
[About](#about)<br/>
[Joint Definition](#jd)<br/>
[Joint Factory](#jf)<br/>
[Using Joints](#uj)<br/>
<a name="about">
## About
Joints are used to constrain bodies to the world or to each other. Typical
examples in games include ragdolls, teeters, and pulleys. Joints can be
combined in many different ways to create interesting motions.
Some joints provide limits so you can control the range of motion. Some joint
provide motors which can be used to drive the joint at a prescribed speed
until a prescribed force/torque is exceeded.
Joint motors can be used in many ways. You can use motors to control position
by specifying a joint velocity that is proportional to the difference between
the actual and desired position. You can also use motors to simulate joint
friction: set the joint velocity to zero and provide a small, but significant
maximum motor force/torque. Then the motor will attempt to keep the joint from
moving until the load becomes too strong.
<a name="jd">
## Joint Definition
Each joint type has a definition that derives from b2JointDef. All joints are
connected between two different bodies. One body may static. Joints between
static and/or kinematic bodies are allowed, but have no effect and use some
processing time.
You can specify user data for any joint type and you can provide a flag to
prevent the attached bodies from colliding with each other. This is actually
the default behavior and you must set the collideConnected Boolean to allow
collision between to connected bodies.
Many joint definitions require that you provide some geometric data. Often a
joint will be defined by anchor points. These are points fixed in the attached
bodies. LiquidFun requires these points to be specified in local coordinates.
This way the joint can be specified even when the current body transforms
violate the joint constraint --- a common occurrence when a game is saved and
reloaded. Additionally, some joint definitions need to know the default
relative angle between the bodies. This is necessary to constrain rotation
correctly.
Initializing the geometric data can be tedious, so many joints have
initialization functions that use the current body transforms to remove much
of the work. However, these initialization functions should usually only be
used for prototyping. Production code should define the geometry directly.
This will make joint behavior more robust.
The rest of the joint definition data depends on the joint type. We cover
these now.
<a name="jf">
## Joint Factory
Joints are created and destroyed using the world factory methods. This brings
up an old issue:
Caution
Don't try to create a joint on the stack or on the heap using new or malloc. You must create and destroy bodies and joints using the create and destroy methods of the b2World class.
Here's an example of the lifetime of a revolute joint:
&nbsp;&nbsp;&nbsp;`b2RevoluteJointDef jointDef;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.bodyA = myBodyA;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.bodyB = myBodyB;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.anchorPoint = myBodyA->GetCenterPosition();`<br/>
&nbsp;&nbsp;&nbsp;`b2RevoluteJoint* joint =
(b2RevoluteJoint*)myWorld->CreateJoint(&jointDef);`<br/>
&nbsp;&nbsp;&nbsp;`… do stuff …`<br/>
&nbsp;&nbsp;&nbsp;`myWorld->DestroyJoint(joint);`<br/>
&nbsp;&nbsp;&nbsp;`joint = NULL;`<br/>
It is always good to nullify your pointer after they are destroyed. This will
make the program crash in a controlled manner if you try to reuse the pointer.
The lifetime of a joint is not simple. Heed this warning well:
Caution
Joints are destroyed when an attached body is destroyed.
This precaution is not always necessary. You may organize your game engine so
that joints are always destroyed before the attached bodies. In this case you
don't need to implement the listener class. See the section on Implicit
Destruction for details.
<a name="uj">
## Using Joints
Many simulations create the joints and don't access them again until they are
destroyed. However, there is a lot of useful data contained in joints that you
can use to create a rich simulation.
First of all, you can get the bodies, anchor points, and user data from a
joint.
&nbsp;&nbsp;&nbsp;`b2Body* GetBodyA();`
&nbsp;&nbsp;&nbsp;`b2Body* GetBodyB();`
&nbsp;&nbsp;&nbsp;`b2Vec2 GetAnchorA();`
&nbsp;&nbsp;&nbsp;`b2Vec2 GetAnchorB();`
&nbsp;&nbsp;&nbsp;`void* GetUserData();`
All joints have a reaction force and torque. This the reaction force applied
to body 2 at the anchor point. You can use reaction forces to break joints or
trigger other game events. These functions may do some computations, so don't
call them if you don't need the result.
&nbsp;&nbsp;&nbsp;`b2Vec2 GetReactionForce();`<br/>
&nbsp;&nbsp;&nbsp;`float32 GetReactionTorque();`<br/>
### Distance Joint
One of the simplest joint is a distance joint which says that the distance
between two points on two bodies must be constant. When you specify a distance
joint the two bodies should already be in place. Then you specify the two
anchor points in world coordinates. The first anchor point is connected to
body 1, and the second anchor point is connected to body 2. These points imply
the length of the distance constraint.
<img align="center" src="image_18.gif" alt="Distance joint" height="118"
width="155"><br/>
Here is an example of a distance joint definition. In this case we decide to
allow the bodies to collide.
&nbsp;&nbsp;&nbsp;`b2DistanceJointDef jointDef;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.Initialize(myBodyA, myBodyB, worldAnchorOnBodyA,
worldAnchorOnBodyB);`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.collideConnected = true;`<br/>
The distance joint can also be made soft, like a spring-damper connection. See
the Web example in the testbed to see how this behaves.
Softness is achieved by tuning two constants in the definition: frequency and
damping ratio. Think of the frequency as the frequency of a harmonic
oscillator (like a guitar string). The frequency is specified in Hertz.
Typically the frequency should be less than a half the frequency of the time
step. So if you are using a 60Hz time step, the frequency of the distance
joint should be less than 30Hz. The reason is related to the Nyquist frequency.
The damping ratio is non-dimensional and is typically between 0 and 1, but can
be larger. At 1, the damping is critical (all oscillations should vanish).
&nbsp;&nbsp;&nbsp;`jointDef.frequencyHz = 4.0f;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.dampingRatio = 0.5f;`<br/>
### Revolute Joint
A revolute joint forces two bodies to share a common anchor point, often
called a hinge point. The revolute joint has a single degree of freedom: the
relative rotation of the two bodies. This is called the joint angle.
<img align="center" src="image_19.gif" alt="Revolute joint" height="97"
width="139"><br/>
To specify a revolute you need to provide two bodies and a single anchor point
in world space. The initialization function assumes that the bodies are
already in the correct position.
In this example, two bodies are connected by a revolute joint at the first
body's center of mass.
&nbsp;&nbsp;&nbsp;`b2RevoluteJointDef jointDef;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.Initialize(myBodyA, myBodyB,
myBodyA->GetWorldCenter());`<br/>
The revolute joint angle is positive when bodyB rotates CCW about the angle
point. Like all angles in LiquidFun, the revolute angle is measured in
radians. By convention the revolute joint angle is zero when the joint is
created using Initialize(), regardless of the current rotation of the two
bodies.
In some cases you might wish to control the joint angle. For this, the
revolute joint can optionally simulate a joint limit and/or a motor.
A joint limit forces the joint angle to remain between a lower and upper
bound. The limit will apply as much torque as needed to make this happen. The
limit range should include zero, otherwise the joint will lurch when the
simulation begins.
A joint motor allows you to specify the joint speed (the time derivative of
the angle). The speed can be negative or positive. A motor can have infinite
force, but this is usually not desirable. Recall the eternal question:
*"What happens when an irresistible force meets an immovable object?"*
I can tell you it's not pretty. So you can provide a maximum torque for the
joint motor. The joint motor will maintain the specified speed unless the
required torque exceeds the specified maximum. When the maximum torque is
exceeded, the joint will slow down and can even reverse.
You can use a joint motor to simulate joint friction. Just set the joint speed
to zero, and set the maximum torque to some small, but significant value. The
motor will try to prevent the joint from rotating, but will yield to a
significant load.
Here's a revision of the revolute joint definition above; this time the joint
has a limit and a motor enabled. The motor is setup to simulate joint friction.
b2RevoluteJointDef jointDef;
jointDef.Initialize(bodyA, bodyB, myBodyA->GetWorldCenter());
jointDef.lowerAngle = -0.5f * b2_pi; // -90 degrees
jointDef.upperAngle = 0.25f * b2_pi; // 45 degrees
jointDef.enableLimit = true;
jointDef.maxMotorTorque = 10.0f;
jointDef.motorSpeed = 0.0f;
jointDef.enableMotor = true;
You can access a revolute joint's angle, speed, and motor torque.
&nbsp;&nbsp;&nbsp;`float32 GetJointAngle() const;`<br/>
&nbsp;&nbsp;&nbsp;`float32 GetJointSpeed() const;`<br/>
&nbsp;&nbsp;&nbsp;`float32 GetMotorTorque() const;`<br/>
You also update the motor parameters each step.
&nbsp;&nbsp;&nbsp;`void SetMotorSpeed(float32 speed);`<br/>
&nbsp;&nbsp;&nbsp;`void SetMaxMotorTorque(float32 torque);`<br/>
Joint motors have some interesting abilities. You can update the joint speed
every time step so you can make the joint move back-and-forth like a sine-wave
or according to whatever function you want.
&nbsp;&nbsp;&nbsp;`... Game Loop Begin ...`<br/>
&nbsp;&nbsp;&nbsp;`myJoint->SetMotorSpeed(cosf(0.5f * time));`<br/>
&nbsp;&nbsp;&nbsp;`... Game Loop End ...`<br/>
You can also use joint motors to track a desired joint angle. For example:
&nbsp;&nbsp;&nbsp;`... Game Loop Begin ...`<br/>
&nbsp;&nbsp;&nbsp;`float32 angleError = myJoint->GetJointAngle() -
angleTarget;`<br/>
&nbsp;&nbsp;&nbsp;`float32 gain = 0.1f;`<br/>
&nbsp;&nbsp;&nbsp;`myJoint->SetMotorSpeed(-gain * angleError);`<br/>
&nbsp;&nbsp;&nbsp;`... Game Loop End ...`<br/>
Generally your gain parameter should not be too large. Otherwise your joint
may become unstable.
### Prismatic Joint
A prismatic joint allows for relative translation of two bodies along a
specified axis. A prismatic joint prevents relative rotation. Therefore, a
prismatic joint has a single degree of freedom.
<img align="center" src="image_20.gif" alt="Prismatic joint" height="134"
width="165"><br/>
The prismatic joint definition is similar to the revolute joint description;
just substitute translation for angle and force for torque. Using this analogy
provides an example prismatic joint definition with a joint limit and a
friction motor:
&nbsp;&nbsp;&nbsp;`b2PrismaticJointDef jointDef;`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 worldAxis(1.0f, 0.0f);`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.Initialize(myBodyA, myBodyB,
myBodyA->GetWorldCenter(), worldAxis);`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.lowerTranslation = -5.0f;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.upperTranslation = 2.5f;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.enableLimit = true;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.maxMotorForce = 1.0f;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.motorSpeed = 0.0f;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.enableMotor = true;`<br/>
The revolute joint has an implicit axis coming out of the screen. The
prismatic joint needs an explicit axis parallel to the screen. This axis is
fixed in the two bodies and follows their motion.
Like the revolute joint, the prismatic joint translation is zero when the
joint is created using Initialize(). So be sure zero is between your lower and
upper translation limits.
Using a prismatic joint is similar to using a revolute joint. Here are the
relevant member functions:
&nbsp;&nbsp;&nbsp;`float32 GetJointTranslation() const;`
&nbsp;&nbsp;&nbsp;`float32 GetJointSpeed() const;`
&nbsp;&nbsp;&nbsp;`float32 GetMotorForce() const;`
&nbsp;&nbsp;&nbsp;`void SetMotorSpeed(float32 speed);`
&nbsp;&nbsp;&nbsp;`void SetMotorForce(float32 force);`
### Pulley Joint
A pulley is used to create an idealized pulley. The pulley connects two bodies
to ground and to each other. As one body goes up, the other goes down. The
total length of the pulley rope is conserved according to the initial
configuration.
length1 + length2 == constant
You can supply a ratio that simulates a block and tackle. This causes one side
of the pulley to extend faster than the other. At the same time the constraint
force is smaller on one side than the other. You can use this to create
mechanical leverage.
length1 + ratio * length2 == constant
For example, if the ratio is 2, then length1 will vary at twice the rate of
length2. Also the force in the rope attached to body1 will have half the
constraint force as the rope attached to body2.
<img align="center" src="image_21.gif" alt="Pulley joint" height="213"
width="237"><br/>
Pulleys can be troublesome when one side is fully extended. The rope on the
other side will have zero length. At this point the constraint equations
become singular (bad). You should configure collision shapes to prevent this.
Here is an example pulley definition:
&nbsp;&nbsp;&nbsp;`b2Vec2 anchor1 = myBody1->GetWorldCenter();`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 anchor2 = myBody2->GetWorldCenter();`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 groundAnchor1(p1.x, p1.y + 10.0f);`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 groundAnchor2(p2.x, p2.y + 12.0f);`<br/>
&nbsp;&nbsp;&nbsp;`float32 ratio = 1.0f;`<br/>
&nbsp;&nbsp;&nbsp;`b2PulleyJointDef jointDef;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.Initialize(myBody1, myBody2, groundAnchor1,
groundAnchor2, anchor1, anchor2, ratio);`<br/>
Pulley joints provide the current lengths.
&nbsp;&nbsp;&nbsp;`float32 GetLengthA() const;`<br/>
&nbsp;&nbsp;&nbsp;`float32 GetLengthB() const;`<br/>
### Gear Joint
If you want to create a sophisticated mechanical contraption you might want to
use gears. In principle you can create gears in LiquidFun by using compound
shapes to model gear teeth. This is not very efficient and might be tedious to
author. You also have to be careful to line up the gears so the teeth mesh
smoothly. LiquidFun has a simpler method of creating gears: the gear joint.
<img align="center" src="image_22.gif" alt="Gear joint" height="188"
width="179"><br/>
The gear joint can only connect revolute and/or prismatic joints.
Like the pulley ratio, you can specify a gear ratio. However, in this case the
gear ratio can be negative. Also keep in mind that when one joint is a
revolute joint (angular) and the other joint is prismatic (translation), and
then the gear ratio will have units of length or one over length.
coordinate1 + ratio * coordinate2 == constant
Here is an example gear joint. The bodies myBodyA and myBodyB are any bodies
from the two joints, as long as they are not the same bodies.
&nbsp;&nbsp;&nbsp;`b2GearJointDef jointDef;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.bodyA = myBodyA;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.bodyB = myBodyB;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.joint1 = myRevoluteJoint;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.joint2 = myPrismaticJoint;`<br/>
&nbsp;&nbsp;&nbsp;`jointDef.ratio = 2.0f * b2_pi / myLength;`<br/>
Note that the gear joint depends on two other joints. This creates a fragile
situation. What happens if those joints are deleted?
Caution
Always delete gear joints before the revolute/prismatic joints on the gears. Otherwise your code will crash in a bad way due to the orphaned joint pointers in the gear joint. You should also delete the gear joint before you delete any of the bodies involved.
### Mouse Joint
The mouse joint is used in the testbed to manipulate bodies with the mouse. It
attempts to drive a point on a body towards the current position of the
cursor. There is no restriction on rotation.
The mouse joint definition has a target point, maximum force, frequency, and
damping ratio. The target point initially coincides with the bodys anchor
point. The maximum force is used to prevent violent reactions when multiple
dynamic bodies interact. You can make this as large as you like. The frequency
and damping ratio are used to create a spring/damper effect similar to the
distance joint.
Many users have tried to adapt the mouse joint for game play. Users often
want to achieve precise positioning and instantaneous response. The mouse
joint doesnt work very well in that context. You may wish to consider using
kinematic bodies instead.
### Wheel Joint
The wheel joint restricts a point on bodyB to a line on bodyA. The wheel joint
also provides a suspension spring. See b2WheelJoint.h and Car.h for details.
<img align="center" src="image_23.png" alt="Wheel joint" height="286"
width="157"><br/>
### Weld Joint
The weld joint attempts to constrain all relative motion between two bodies.
See the Cantilever.h in the testbed to see how the weld joint behaves.
It is tempting to use the weld joint to define breakable structures. However,
the LiquidFun solver is iterative so the joints are a bit soft. So chains of
bodies connected by weld joints will flex.
Instead it is better to create breakable bodies starting with a single body
with multiple fixtures. When the body breaks, you can destroy a fixture and
recreate it on a new body. See the Breakable example in the testbed.
### Rope Joint
The rope joint restricts the maximum distance between two points. This can be
useful to prevent chains of bodies from stretching, even under high load. See
b2RopeJoint.h and RopeJoint.h for details.
### Friction Joint
The friction joint is used for top-down friction. The joint provides 2D
translational friction and angular friction. See b2FrictionJoint.h and
ApplyForce.h for details.
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,364 @@
# Contacts
[About](#about)<br/>
[Contact Class](#cc)<br/>
[Accessing Contacts](#ac)<br/>
[Contact Listener](#cl)<br/>
[Contact Filtering](#cf)<br/>
<a name="about"></a>
## About
Contacts are objects created by LiquidFun to manage collision between two
fixtures. If the fixture has children, such as a chain shape, then a contact
exists for each relevant child. There are different kinds of contacts, derived
from b2Contact, for managing contact between different kinds of fixtures. For
example there is a contact class for managing polygon-polygon collision and
another contact class for managing circle-circle collision.
Here is some terminology associated with contacts.
### contact point
A contact point is a point where two shapes touch. LiquidFun approximates
contact with a small number of points.
### contact normal
A contact normal is a unit vector that points from one shape to another. By
convention, the normal points from fixtureA to fixtureB.
### contact separation
Separation is the opposite of penetration. Separation is negative when shapes
overlap. It is possible that future versions of LiquidFun will create contact
points with positive separation, so you may want to check the sign when
contact points are reported.
### contact manifold
Contact between two convex polygons may generate up to 2 contact points. Both
of these points use the same normal, so they are grouped into a contact
manifold, which is an approximation of a continuous region of contact.
### normal impulse
The normal force is the force applied at a contact point to prevent the shapes
from penetrating. For convenience, LiquidFun works with impulses. The normal
impulse is just the normal force multiplied by the time step.
### tangent impulse
The tangent force is generated at a contact point to simulate friction. For
convenience, this is stored as an impulse.
### contact ids
LiquidFun tries to re-use the contact force results from a time step as the
initial guess for the next time step. LiquidFun uses contact ids to match
contact points across time steps. The ids contain geometric features indices
that help to distinguish one contact point from another.
Contacts are created when two fixtures AABBs overlap. Sometimes collision
filtering will prevent the creation of contacts. Contacts are destroyed with
the AABBs cease to overlap.
So you might gather that there may be contacts created for fixtures that are
not touching (just their AABBs). Well, this is correct. It's a "chicken or egg"
problem. We don't know if we need a contact object until one is created
to analyze the collision. We could delete the contact right away if the shapes
are not touching, or we can just wait until the AABBs stop overlapping.
LiquidFun takes the latter approach because it lets the system cache
information to improve performance.
<a name="cc">
## Contact Class
As mentioned before, the contact class is created and destroyed by LiquidFun.
Contact objects are not created by the user. However, you are able to access
the contact class and interact with it.
You can access the raw contact manifold:
&nbsp;&nbsp;&nbsp;`b2Manifold* GetManifold();`<br/>
&nbsp;&nbsp;&nbsp;`const b2Manifold* GetManifold() const;`<br/>
You can potentially modify the manifold, but this is generally not supported
and is for advanced usage.
There is a helper function to get the b2WorldManifold:
&nbsp;&nbsp;&nbsp;`void GetWorldManifold(b2WorldManifold* worldManifold)
const;`<br/>
This uses the current positions of the bodies to compute world positions of
the contact points.
Sensors do not create manifolds, so for them use:
&nbsp;&nbsp;&nbsp;`bool touching = sensorContact->IsTouching();`<br/>
This function also works for non-sensors.
You can get the fixtures from a contact. From those you can get the bodies.
&nbsp;&nbsp;&nbsp;`b2Fixture* fixtureA = myContact->GetFixtureA();`<br/>
&nbsp;&nbsp;&nbsp;`b2Body* bodyA = fixtureA->GetBody();`<br/>
&nbsp;&nbsp;&nbsp;`MyActor* actorA = (MyActor*)bodyA->GetUserData();`<br/>
You can disable a contact. This only works inside the
b2ContactListener::PreSolve event, discussed below.
<a name="ac">
## Accessing Contacts
You can get access to contacts in several ways. You can access the contacts
directly on the world and body structures. You can also implement a contact
listener.
You can iterate over all contacts in the world:
&nbsp;&nbsp;&nbsp;`for (b2Contact* c = myWorld->GetContactList(); c; c =
c->GetNext())`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`// process c`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
You can also iterate over all the contacts on a body. These are stored in a
graph using a contact edge structure.
&nbsp;&nbsp;&nbsp;`for (b2ContactEdge* ce = myBody->GetContactList(); ce; ce =
ce->next)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;` b2Contact* c = ce->contact;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;` // process c`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
You can also access contacts using the contact listener that is described
below.
Caution
Accessing contacts off b2World and b2Body may miss some transient
contacts that occur in the middle of the time step. Use b2ContactListener to
get the most accurate results.
<a name="cl">
## Contact Listener
You can receive contact data by implementing b2ContactListener. The contact
listener supports several events: begin, end, pre-solve, and post-solve.
&nbsp;&nbsp;&nbsp;`class MyContactListener : public b2ContactListener`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`public:`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`void BeginContact(b2Contact*
contact)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{ /* handle begin event */ }`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`void EndContact(b2Contact* contact)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{ /* handle end event */ }`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`void PreSolve(b2Contact* contact, const
b2Manifold* oldManifold)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{ /* handle pre-solve event */ }`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`void PostSolve(b2Contact* contact, const
b2ContactImpulse* impulse)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{ /* handle post-solve event */ }`<br/>
&nbsp;&nbsp;&nbsp;`};`<br/>
Caution
Do not keep a reference to the pointers sent to b2ContactListener.
Instead make a deep copy of the contact point data into your own buffer. The
example below shows one way of doing this.
At run-time you can create an instance of the listener and register it
with b2World::SetContactListener. Be sure your listener remains in scope while
the world object exists.
### Begin Contact Event
This is called when two fixtures begin to overlap. This is called for sensors
and non-sensors. This event can only occur inside the time step.
### End Contact Event
This is called when two fixtures cease to overlap. This is called for sensors
and non-sensors. This may be called when a body is destroyed, so this event
can occur outside the time step.
### Pre-Solve Event
This is called after collision detection, but before collision resolution.
This gives you a chance to disable the contact based on the current
configuration. For example, you can implement a one-sided platform using this
callback and calling b2Contact::SetEnabled(false). The contact will be
re-enabled each time through collision processing, so you will need to disable
the contact every time-step. The pre-solve event may be fired multiple times
per time step per contact due to continuous collision detection.
&nbsp;&nbsp;&nbsp;`void PreSolve(b2Contact* contact, const b2Manifold*
oldManifold)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;`b2WorldManifold worldManifold;`<br/>
&nbsp;&nbsp;&nbsp;`contact->GetWorldManifold(&worldManifold);`<br/>
&nbsp;&nbsp;&nbsp;`if (worldManifold.normal.y < -0.5f)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`contact->SetEnabled(false);`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
The pre-solve event is also a good place to determine the point state and the
approach velocity of collisions.
&nbsp;&nbsp;&nbsp;`void PreSolve(b2Contact* contact, const b2Manifold*
oldManifold)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2WorldManifold worldManifold;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`contact->GetWorldManifold(&worldManifold);`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2PointState state1[2], state2[2];`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2GetPointStates(state1, state2,
oldManifold, contact->GetManifold());`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`if (state2[0] == b2_addState)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`const b2Body* bodyA =
contact->GetFixtureA()->GetBody();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`const b2Body* bodyB =
contact->GetFixtureB()->GetBody();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Vec2 point =
worldManifold.points[0];`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Vec2 vA =
bodyA->GetLinearVelocityFromWorldPoint(point);`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Vec2 vB =
bodyB->GetLinearVelocityFromWorldPoint(point);`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`float32
approachVelocity = b2Dot(vB vA, worldManifold.normal);`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`if (approachVelocity >
1.0f)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`MyPlayCollisionSound();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
### Post-Solve Event
The post solve event is where you can gather collision impulse results. If you
dont care about the impulses, you should probably just implement the
pre-solve event.
It is tempting to implement game logic that alters the physics world inside a
contact callback. For example, you may have a collision that applies damage
and try to destroy the associated actor and its rigid body. However, LiquidFun
does not allow you to alter the physics world inside a callback because you
might destroy objects that LiquidFun is currently processing, leading to
orphaned pointers.
The recommended practice for processing contact points is to buffer all
contact data that you care about and process it after the time step. You
should always process the contact points immediately after the time step;
otherwise some other client code might alter the physics world, invalidating
the contact buffer. When you process the contact buffer you can alter the
physics world, but you still need to be careful that you don't orphan pointers
stored in the contact point buffer. The testbed has example contact point
processing that is safe from orphaned pointers.
This code from the CollisionProcessing test shows how to handle orphaned
bodies when processing the contact buffer. Here is an excerpt. Be sure to read
the comments in the listing. This code assumes that all contact points have
been buffered in the b2ContactPoint array m_points.
&nbsp;&nbsp;&nbsp;`// We are going to destroy some bodies according to
contact`<br/>
&nbsp;&nbsp;&nbsp;`// points. We must buffer the bodies that should be
destroyed`<br/>
&nbsp;&nbsp;&nbsp;`// because they may belong to multiple contact points.`<br/>
&nbsp;&nbsp;&nbsp;`const int32 k_maxNuke = 6;`<br/>
&nbsp;&nbsp;&nbsp;`b2Body* nuke[k_maxNuke];`<br/>
&nbsp;&nbsp;&nbsp;`int32 nukeCount = 0;`<br/>
&nbsp;&nbsp;&nbsp;`// Traverse the contact buffer. Destroy bodies that`<br/>
&nbsp;&nbsp;&nbsp;`// are touching heavier bodies.`<br/>
&nbsp;&nbsp;&nbsp;`for (int32 i = 0; i < m_pointCount; ++i)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`ContactPoint* point = m_points + i;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Body* bodyA =
point->fixtureA->GetBody();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Body* bodyB =
point->FixtureB->GetBody();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`float32 massA = bodyA->GetMass();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`float32 massB = bodyB->GetMass();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`if (massA > 0.0f && massB > 0.0f)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`if (massB > massA)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`nuke[nukeCount++] = bodyA;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`else`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`nuke[nukeCount++] = bodyB;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`if (nukeCount == k_maxNuke)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`break;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;`// Sort the nuke array to group duplicates.`<br/>
&nbsp;&nbsp;&nbsp;`std::sort(nuke, nuke + nukeCount);`<br/>
&nbsp;&nbsp;&nbsp;`// Destroy the bodies, skipping duplicates.`<br/>
&nbsp;&nbsp;&nbsp;`int32 i = 0;`<br/>
&nbsp;&nbsp;&nbsp;`while (i < nukeCount)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Body* b = nuke[i++];`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`while (i < nukeCount && nuke[i] ==
b)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`++i;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`m_world->DestroyBody(b);`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
## Contact Filtering
Often in a game you don't want all objects to collide. For example, you may
want to create a door that only certain characters can pass through. This is
called contact filtering, because some interactions are filtered out.
LiquidFun allows you to achieve custom contact filtering by implementing a
b2ContactFilter class. This class requires you to implement a ShouldCollide
function that receives two b2Shape pointers. Your function returns true if the
shapes should collide.
The default implementation of ShouldCollide uses the b2FilterData defined in
Chapter 6, Fixtures.
&nbsp;&nbsp;&nbsp;`bool b2ContactFilter::ShouldCollide(b2Fixture* fixtureA,
b2Fixture* fixtureB)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`const b2Filter& filterA =
fixtureA->GetFilterData();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`const b2Filter& filterB =
fixtureB->GetFilterData();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`if (filterA.groupIndex ==
filterB.groupIndex && filterA.groupIndex != 0)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`return
filterA.groupIndex > 0;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`bool collide = (filterA.maskBits & filterB.categoryBits) != 0 && (filterA.categoryBits & filterB.maskBits) != 0;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`return collide;`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
At run-time you can create an instance of your contact filter and register it
with b2World::SetContactFilter. Make sure your filter stays in scope while the
world exists.
&nbsp;&nbsp;&nbsp;`MyContactFilter filter;`<br/>
&nbsp;&nbsp;&nbsp;`world->SetContactFilter(&filter);`<br/>
&nbsp;&nbsp;&nbsp;`// filter remains in scope …`<br/>
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,322 @@
# World Class
[About](#about)<br/>
[Creating and Destroying a World](#cdw)<br/>
[Using a World](#uw)<br/>
[Simulation](#sim)<br/>
[Exploring the World](#ew)<br/>
[AABB Queries](#ab)<br/>
[Ray Casts](#rc)<br/>
[Forces and Impulses](#fi)<br/>
[Coordinate Transformations](#ct)<br/>
[Lists](#lists)<br/>
<a name="about"></a><br/>
## About
The b2World class contains the bodies and joints. It manages all aspects of
the simulation and allows for asynchronous queries (like AABB queries and
ray-casts). Much of your interactions with LiquidFun will be with a b2World
object.
<a name="cdw"></a><br/>
## Creating and Destroying a World
Creating a world is fairly simple. You just need to provide a gravity vector
and a Boolean indicating if bodies can sleep. Usually you will create and
destroy a world using new and delete.
&nbsp;&nbsp;&nbsp;`b2World* myWorld = new b2World(gravity, doSleep);`<br/>
&nbsp;&nbsp;&nbsp;`... do stuff ...`<br/>
&nbsp;&nbsp;&nbsp;`delete myWorld;`<br/>
<a name="uw"></a><br/>
## Using a World
The world class contains factories for creating and destroying bodies and
joints. These factories are discussed later in the sections on bodies and
joints. There are some other interactions with b2World that I will cover now.
<a name="sim"></a><br/>
## Simulation
The world class is used to drive the simulation. You specify a time step and a
velocity and position iteration count. For example:
&nbsp;&nbsp;&nbsp;`float32 timeStep = 1.0f / 60.f;`<br/>
&nbsp;&nbsp;&nbsp;`int32 velocityIterations = 10;`<br/>
&nbsp;&nbsp;&nbsp;`int32 positionIterations = 8;`<br/>
&nbsp;&nbsp;&nbsp;`myWorld->Step(timeStep, velocityIterations,
positionIterations);`<br/>
After the time step you can examine your bodies and joints for information.
Most likely you will grab the position off the bodies so that you can update
your actors and render them. You can perform the time step anywhere in your
game loop, but you should be aware of the order of things. For example, you
must create bodies before the time step if you want to get collision results
for the new bodies in that frame.
As I discussed above in the HelloWorld tutorial, you should use a fixed time
step. By using a larger time step you can improve performance in low frame
rate scenarios. But generally you should use a time step no larger than 1/30
seconds. A time step of 1/60 seconds will usually deliver a high quality
simulation.
The iteration count controls how many times the constraint solver sweeps over
all the contacts and joints in the world. More iteration always yields a
better simulation. But don't trade a small time step for a large iteration
count. 60Hz and 10 iterations is far better than 30Hz and 20 iterations.
After stepping, you should clear any forces you have applied to your bodies.
This is done with the command b2World::ClearForces. This lets you take
multiple sub-steps with the same force field.
&nbsp;&nbsp;&nbsp;`myWorld->ClearForces();`<br/>
<a name="ew"></a><br/>
## Exploring the World
The world is a container for bodies, contacts, and joints. You can grab the
body, contact, and joint lists off the world and iterate over them. For
example, this code wakes up all the bodies in the world:
&nbsp;&nbsp;&nbsp;`for (b2Body* b = myWorld->GetBodyList(); b; b =
b->GetNext())`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;` b->SetAwake(true);`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
Unfortunately real programs can be more complicated. For example, the
following code is broken:
&nbsp;&nbsp;&nbsp;`for (b2Body* b = myWorld->GetBodyList(); b; b =
b->GetNext())`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`GameActor* myActor =
(GameActor*)b->GetUserData();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`if (myActor->IsDead())`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`myWorld->DestroyBody(b);
// ERROR: now GetNext returns garbage.`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
Everything goes ok until a body is destroyed. Once a body is destroyed, its
next pointer becomes invalid. So the call to b2Body::GetNext() will return
garbage. The solution to this is to copy the next pointer before destroying
the body.
&nbsp;&nbsp;&nbsp;`b2Body* node = myWorld->GetBodyList();`<br/>
&nbsp;&nbsp;&nbsp;`while (node)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Body* b = node;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`node = node->GetNext();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`GameActor* myActor =
(GameActor*)b->GetUserData();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`if (myActor->IsDead())`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`myWorld->DestroyBody(b);`
<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
This safely destroys the current body. However, you may want to call a game
function that may destroy multiple bodies. In this case you need to be very
careful. The solution is application specific, but for convenience I'll show
one method of solving the problem.
&nbsp;&nbsp;&nbsp;`b2Body* node = myWorld->GetBodyList();`<br/>
&nbsp;&nbsp;&nbsp;`while (node)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Body* b = node;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`node = node->GetNext();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`GameActor* myActor =
(GameActor*)b->GetUserData();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`if (myActor->IsDead())`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`bool
otherBodiesDestroyed = GameCrazyBodyDestroyer(b);`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`if
(otherBodiesDestroyed)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`node
= myWorld->GetBodyList();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
Obviously to make this work, GameCrazyBodyDestroyer must be honest about what
it has destroyed.
<a name="ab"></a><br/>
## AABB Queries
Sometimes you want to determine all the shapes in a region. The b2World class
has a fast log(N) method for this using the broad-phase data structure. You
provide an AABB in world coordinates and an implementation of b2QueryCallback.
The world calls your class with each fixture whose AABB overlaps the query
AABB. Return true to continue the query, otherwise return false. For example,
the following code finds all the fixtures that potentially intersect a
specified AABB and wakes up all of the associated bodies.
&nbsp;&nbsp;&nbsp;`class MyQueryCallback : public b2QueryCallback`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`public:`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`bool ReportFixture(b2Fixture*
fixture)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Body* body =
fixture->GetBody();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`body->SetAwake(true);`<br
/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`// Return true to
continue the query.`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`return true;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;`};`<br/>
&nbsp;&nbsp;&nbsp;`...`<br/>
&nbsp;&nbsp;&nbsp;`MyQueryCallback callback;`<br/>
&nbsp;&nbsp;&nbsp;`b2AABB aabb;`<br/>
&nbsp;&nbsp;&nbsp;`aabb.lowerBound.Set(-1.0f, -1.0f);`<br/>
&nbsp;&nbsp;&nbsp;`aabb.upperBound.Set(1.0f, 1.0f);`<br/>
&nbsp;&nbsp;&nbsp;`myWorld->Query(&callback, aabb);`<br/>
You cannot make any assumptions about the order of the callbacks.
<a name="rc"></a><br/>
## Ray Casts
You can use ray casts to do line-of-sight checks, fire guns, etc. You perform
a ray cast by implementing a callback class and providing the start and end
points. The world class calls your class with each fixture hit by the ray.
Your callback is provided with the fixture, the point of intersection, the
unit normal vector, and the fractional distance along the ray. You cannot make
any assumptions about the order of the callbacks.
You control the continuation of the ray cast by returning a fraction.
Returning a fraction of zero indicates the ray cast should be terminated. A
fraction of one indicates the ray cast should continue as if no hit occurred.
If you return the fraction from the argument list, the ray will be clipped to
the current intersection point. So you can ray cast any shape, ray cast all
shapes, or ray cast the closest shape by returning the appropriate fraction.
You may also return of fraction of -1 to filter the fixture. Then the ray cast
will proceed as if the fixture does not exist.
Here is an example:
&nbsp;&nbsp;&nbsp;`// This class captures the closest hit shape.`<br/>
&nbsp;&nbsp;&nbsp;`class MyRayCastCallback : public b2RayCastCallback`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`public:`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`MyRayCastCallback()`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`m_fixture = NULL;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`float32 ReportFixture(b2Fixture* fixture,
const b2Vec2& point, const b2Vec2& normal, float32 fraction)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`m_fixture =
fixture;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`m_point = point;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`m_normal = normal;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`m_fraction =
fraction;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`return fraction;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Fixture* m_fixture;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Vec2 m_point;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`b2Vec2 m_normal;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`float32 m_fraction;`<br/>
&nbsp;&nbsp;&nbsp;`};`<br/>
&nbsp;&nbsp;&nbsp;`MyRayCastCallback callback;`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 point1(-1.0f, 0.0f);`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 point2(3.0f, 1.0f);`<br/>
&nbsp;&nbsp;&nbsp;`myWorld->RayCast(&callback, point1, point2);`<br/>
Caution
Due to round-off errors, ray casts can sneak through small cracks
between polygons in your static environment. If this is not acceptable in your
application, please enlarge your polygons slightly.
&nbsp;&nbsp;&nbsp;`void SetLinearVelocity(const b2Vec2& v);`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 GetLinearVelocity() const;`<br/>
&nbsp;&nbsp;&nbsp;`void SetAngularVelocity(float32 omega);`<br/>
&nbsp;&nbsp;&nbsp;`float32 GetAngularVelocity() const;`<br/>
<a name="fi"></a><br/>
## Forces and Impulses
You can apply forces, torques, and impulses to a body. When you apply a force
or an impulse, you provide a world point where the load is applied. This often
results in a torque about the center of mass.
void ApplyForce(const b2Vec2& force, const b2Vec2& point);
void ApplyTorque(float32 torque);
void ApplyLinearImpulse(const b2Vec2& impulse, const b2Vec2& point);
void ApplyAngularImpulse(float32 impulse);
Applying a force, torque, or impulse wakes the body. Sometimes this is
undesirable. For example, you may be applying a steady force and want to allow
the body to sleep to improve performance. In this case you can use the
following code.
&nbsp;&nbsp;&nbsp;`if (myBody->IsAwake() == true)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`myBody->ApplyForce(myForce,
myPoint);`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
You can apply forces and impulses to particles and particle groups, as well.
Unlike with bodies, however, the load is not applied to an arbitrary world
point. Instead, it acts upon the center of each particle.
You can apply forces and impulses to individual particles or to particle groups.
The following example spreads an impulse of (0.7, 0.3) kg m/s across the
particles in myParticleGroup:
&nbsp;&nbsp;&nbsp;`const b2Vec2 impulse(0.7f, 0.3f);`
&nbsp;&nbsp;&nbsp;`myParticleGroup->ApplyLinearImpulse(impulse);`
<a name="ct"></a><br/>
## Coordinate Transformations
The body class has some utility functions to help you transform points and
vectors between local and world space. If you don't understand these concepts,
please read "Essential Mathematics for Games and Interactive Applications" by
Jim Van Verth and Lars Bishop. These functions are efficient (when inlined).
&nbsp;&nbsp;&nbsp;`b2Vec2 GetWorldPoint(const b2Vec2& localPoint);`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 GetWorldVector(const b2Vec2& localVector);`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 GetLocalPoint(const b2Vec2& worldPoint);`<br/>
&nbsp;&nbsp;&nbsp;`b2Vec2 GetLocalVector(const b2Vec2& worldVector);`<br/>
<a name="lists"></a><br/>
## Lists
You can iterate over a body's fixtures. This is mainly useful if you need to
access the fixture's user data.
&nbsp;&nbsp;&nbsp;`for (b2Fixture* f = body->GetFixtureList(); f; f =
f->GetNext())`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`MyFixtureData* data =
(MyFixtureData*)f->GetUserData();`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`… do something with data …`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
You can similarly iterate over the body's joint list.
The body also provides a list of associated contacts. You can use this to get
information about the current contacts. Be careful, because the contact list
may not contain all the contacts that existed during the previous time step.
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,729 @@
# Particle Module
[About](#About)<br/>
[Particles](#Particles)<br/>
[Particle Systems](#ps)<br/>
[Particle Groups](#pg)<br/>
[Discrete Particles vs. Particle Groups](#dp)<br/>
[Stepping the World](#stw)<br/>
[Maximum Velocity](#mv)<br/>
[Creating and Destroying Particles](#cdp)<br/>
[Creating and Destroying Particle Groups](#cdpg)<br/>
[Particle Behaviors](#pb)<br/>
[Particle Properties](#pp)<br/>
[Rendering with OpenGL](#gl)<br/>
[Sample Applications](#sa)<br/>
<a name="About">
## About
The Particle module offers the ability to create and manipulate liquid or soft
(deformable) bodies. It allows you to create (and destroy) particles with
various behaviors and properties, and provides various methods for
manipulating
them. The module permits you to define particles discretely or as groups. It
is
designed to allow you to manipulate large numbers of particles efficiently.
<a name="Particles">
## Particles
A particle is round, and the minimal unit of matter in a particle system. By
default, a particle behaves as a liquid. You can set behavioral flags,
however, to assign different behaviors (explained in
[Particle Behaviors](#pb)) to individual particles or groups of particles.
You can also set other particle properties including position, velocity, and
color.<br/>
The `b2Particle.h` file contains the enumerated behavior values, as well as
the
variables specifying other particle properties. The corresponding enum is
named `b2ParticleFlag.`
<a name="ps">
## Particle Systems
The "world" that particles inhabit is called a particle system. A particle
system describes a wide variety of physical coefficients that help dictate
how particles interact with the world around them. A few examples of these
conditions are default particle radius, elasticity, and viscosity. For more
detail, see the API Reference description of the b2ParticleSystemDef struct.
The following example creates a particle system:
&nbsp;&nbsp;&nbsp;`const b2ParticleSystemDef particleSystemDef;`<br/>
&nbsp;&nbsp;&nbsp;`m_particleSystems[0] =
`m_world->CreateParticleSystem(&particleSystemDef);`<br/>
You can also create more than one particle system: Thus, one "world's"
particles may have a certain default radius, elasticity, etc., while the other
"world" has different default values for these properties. The following sample
shows the creation of multiple particle systems:
&nbsp;&nbsp;&nbsp;`const b2ParticleSystemDef particleSystemDef;`<br/>
&nbsp;&nbsp;&nbsp;`for (int i = 0; i < NUM_PARTICLE_SYSTEMS; ++i) {`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
`m_particleSystems[i] = m_world->CreateParticleSystem(&particleSystemDef);`
<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
In many, if not most,
cases, it will not be necessary to adjust the default values or create multiple
particle systems. You may find it useful in some cases, however.
For example, dividing particles into multiple systems can yield a performance
gain by allowing you to simulate only the visible systems while putting all
other systems in a "paused" state using `b2ParticleSystem::SetPaused()`.
The "Multiple Systems" example in the Testbed provides an example of
two particle systems influencing a rigid body while not interacting with each
other.
<a name="pg">
## Particle Groups
Instead of creating particles individually, you can create a group of
particles to manipulate en masse. Some of the particle-group properties that
you can set are the same as those for discrete particles: behavior, position,
linear velocity, and color. There are also properties specific to groups:
rotational angle, rotational velocity, and strength.<br/>
The `b2ParticleGroup.h` file contains the declarations for all of these
variables, as well as the enum for particle-group behavior:
`b2ParticleGroupFlag`.
<a name="dp">
## Discrete Particles vs. Particle Groups
With one main exception, there is no functional difference between working
with individual particles and groups of particles. The exception is rigid
particles: Because of the internal algorithm used to make particles rigid, you
must define them as a group.
Particle groups do offer several conveniences. First, they allow you to create
and destroy large numbers of particles automatically. If you do not create a
group, you must create all of the particles individually. Also, a group allows
you to assign the same property, such as angle of rotation, to all of its
particles at once.
<a name="stw">
## Stepping the World (Particle Iterations)
The particle solver can iterate multiple times per step. Larger numbers of
steps improve the stability and fidelity of the particle simulation. However,
more steps also require more processor cycles.
The cycles cost is almost linear: double the number of particle iterations
will almost double the cycles cost of b2ParticleSystem::Solve.
Use the `particleIterations` parameter in `b2World::Step` to set the number
of iterations. The default value of `particleIterations` is 1.
You should experiment with `particleIterations` in your game to find the best
balance of stability versus cycles. Try calling `b2CalculateParticleIterations`
or `b2World::CalculateReasonableParticleIterations` to estimate a reasonable
value. Note that these functions are, necessarily, a simplification, and
should be used only as a starting point.
If your simulation seems overly bouncy or energetic, or if the particles in
your simulation are passing through contacts, try increasing the number of
particle iterations.
Note that, as particle iterations increases, the affect of pressure on
highly-compressed particles also increases. That is, particles get more
incompressible as you increase particle iterations.
<a name="mv">
## Maximum Velocity
The particle simulation enforces a maximum velocity on the particles, for
stability and to prevent excessive interpenetration. The maximum velocity is,
&nbsp;&nbsp;&nbsp;`particle diameter / (particle iterations *
b2World::Step's dt)`<br/>
<a name="cdp">
## Creating and Destroying Particles
To create individual particles, create a `b2ParticleDef`-struct object. Next,
specify the behavior and properties of the particle. Finally, call the method
to create the particle.<br/>
The following example creates an individual particle.
&nbsp;&nbsp;&nbsp;`b2ParticleDef pd;`<br>
&nbsp;&nbsp;&nbsp;`pd.flags = b2_elasticParticle;`<br/>
&nbsp;&nbsp;&nbsp;`pd.color.Set(0, 0, 255, 255);`<br/>
&nbsp;&nbsp;&nbsp;`pd.position.Set(i, 0);`<br/>
&nbsp;&nbsp;&nbsp;`int tempIndex = m_particleSystem->CreateParticle(pd);`<br/>
Particle lists are self-compacting. Therefore, the index returned by
CreateParticle is only valid until a lower-indexed particle, or a group
referencing a lower-indexed particle, is deleted.<br/>
To destroy an individual particle, invoke the function
&nbsp;&nbsp;&nbsp;`void DestroyParticle(int32 index);`
The following example destroys the particle created above.
&nbsp;&nbsp;&nbsp;`m_particleSystem->DestroyParticle(tempIndex);`<br/>
### Particle lifetimes
In addition to manual destruction of particles as described above, particles
can also expire and be destroyed due to age.
The following example tells the system to track particle ages for the purpose
of destroying them.
&nbsp;&nbsp;&nbsp;`m_particleSystem->SetParticleDestructionByAge(true);`
A particle can die one of two "age-related" deaths. First, you can set a
lifetime for a particle--a period of time after which it expires. The following
example does this:
&nbsp;&nbsp;&nbsp;`m_particleSystem->SetParticleLifetime(`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`index, Random() *`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
`(k_particleLifetimeMax - k_particleLifetimeMin) +`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`k_particleLifetimeMin);`<br/>
where `index` specifies the number of the particle whose lifetime is being
assigned, and the `Random()` function generates a random value for that
lifetime.
You do not need to set a specific lifetime for a particle for it to have an
age-related death. If you set a maximum number of particles that can exist in a
particle system, and you have have told the system to track particle ages, the
system clamps particle count by culling "excess" particles. Particle culling
takes place in age order, with the oldest ones destroyed first.
The following example sets a maximum particle count for a particle system.
&nbsp;&nbsp;&nbsp;`m_particleSystem->SetMaxParticleCount(k_maxParticleCount);`
The Faucet example in the Testbed provides an example of both types of
lifetime-driven particle destruction.
### Stuck Particles
Particles may get stuck and become obstructions that need to be destroyed or
relocated. A particle is identified as possibly stuck if it remains in contact
with two or more surfaces for a user-specified number (threshold) of particle
iterations. Once "candidates" are identified, you can implement your own logic
to decide whether they are actually stuck, and how to deal with them.
The ability to implement your own logic gives you flexibility in deciding
when you want to consider a particle stuck. For instance, a ball may
be traveling down a chute, making contact with walls on multiple sides. This
state satisfies the "possibly stuck" condition described in the previous
paragraph. But you could implement logic judging the ball not stuck as long
as it keeps traveling down the chute.
On the other hand, you could also decide that not only an immobile particle,
but even a mobile one trapped in a certain spatial range, is stuck. The system
relies on you to judge the candidates.
The following example shows one possible implementation for such a case.
<pre>
// This code example of app logic deciding whether or not to eliminate stuck
// particles shows a user who set up a global array of sensor fixtures
// covering areas they know to be "problematic" for stuck particles in
// their geometry, and then at each step testing any stuck particles against
// those sensors, eliminating any stuck particles that lie inside a known
// problem region.
void DestroyStuckParticlesInSensors(
const b2Fixture * const *sensors, int32 num)
{
const int32 stuck = gParticleSystem->GetStuckCandidateCount();
if (stuck > 0)
{
const int32 *candidates = gParticleSystem->GetStuckCandidates();
const b2Vec2 *positions = gParticleSystem->GetPositionBuffer();
for (int32 i = 0; i < stuck; ++i)
{
const int32 particle = candidates[i];
const b2Vec2 &position = positions[particle];
for (int32 j = 0; j < num; ++j)
{
if(sensors[j]->TestPoint(position))
{
gParticleSystem->DestroyParticle(particle);
}
}
}
}
}
// particles in multiple contacts for 5 or more iterations are
// candidates
gParticleSystem->SetStuckThreshold(5);
// step the world (assuming the timestep, velocity iterations,
// and position iterations have been set globally).
gWorld->Step(gTimeStep, gVelocityIterations, gPositionIterations);
// Perform the above check for stuck particles against sensors
// in this global array.
DestroyStuckParticlesInSensors(gProblemAreaSensors, gNumSensors);
</pre>
<a name="cdpg">
## Creating and Destroying Particle Groups
A particle group begins life in a shaped container. You must therefore start a
particle group definition by specifying a shape. Next, create a
b2ParticleGroupDef-struct object. Then, specify the behavior and properties of
the particles themselves. Finally, call the method to create a particle
group.<br/>
The following example creates five differently colored, box-shaped groups of
particles.
&nbsp;&nbsp;&nbsp;`b2ParticleGroupDef pd;`<br/>
&nbsp;&nbsp;&nbsp;`b2PolygonShape shape;`<br/>
&nbsp;&nbsp;&nbsp;`shape.SetAsBox(10, 5);`<br/>
&nbsp;&nbsp;&nbsp;`pd.shape = &shape;`<br/>
&nbsp;&nbsp;&nbsp;`pd.flags = b2_elasticParticle;`<br/>
&nbsp;&nbsp;&nbsp;`pd.angle = -0.5f;`<br/>
&nbsp;&nbsp;&nbsp;`pd.angularVelocity = 2.0f;`<br/>
&nbsp;&nbsp;&nbsp;`for (int32 i = 0; i < 5; i++)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`pd.position.Set(10 + 20 * i, 40);`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`pd.color.Set(i * 255 / 5, 255 - i * 255 /
5, 128, 255);`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
`m_particleSystem->CreateParticleGroup(pd);`<br/>
&nbsp;&nbsp;&nbsp;`}`<br/>
To destroy a particles in a group, invoke the function
&nbsp;&nbsp;&nbsp;`DestroyParticles(bool callDestructionListener);`<br/>
Groups are automatically destroyed when they contain no particles if the
`b2_particleGroupCanBeEmpty` is not set in the group's flags.
The following example destroys all particle groups in the particle system.
<pre>
b2ParticleGroup* group = m_particleSystem->GetParticleGroupList();
while (group)
{
m_particleSystem->SetGroupFlags(
m_particleSystem->GetGroupFlags() & ~b2_particleGroupCanBeEmpty);
group->DestroyParticles(false);
// The destruction of particle groups are deferred to the next call of
// Step() so it's safe to reference the group here.
group = group->GetNext();
}
</pre>
The next several sections provide more information on how to define particle
behaviors and properties.
<a name="pb">
## Particle Behaviors
Particle behaviors are defined either for entire groups of, or individual,
particles.
For a group of particles, use the `b2ParticleGroupFlag` enum, which provides
two types of particle groups:
###Solid
A solid particle group prevents other bodies from lodging inside of it. Should
anything penetrate it, the solid particle group pushes the offending body back
out to its surface.
A solid particle group also possesses an especially strong repulsive force. It
is useful, for example, in a case where:
* Something should be expected to bounce with unusual vigor
** As when a racquetball strikes the wall of a court
Use the `b2_SolidParticleGroup` flag of the `b2ParticleGroupFlag` enum to
specify a solid particle group. For example:
&nbsp;&nbsp;&nbsp;`pd.groupFlags = b2_solidParticleGroup;`
###Rigid
Rigid particle groups are ones whose shape does not change, even when they
collide
with other bodies. Working with rigid particle groups confers a few advantages
over simply
working with rigid bodies: With a rigid particle group, you can:<br/>
* Delete part of the group (i.e., some of its particles).
* For example, firing a bullet that leaves a hole in a box-shaped group of
particles.
* Merge it with other groups.
* For example, creating a snowman from three round particle groups, and
then merging them into a single particle group.
Use the `b2_rigidParticleGroup` flag of the `b2ParticleGroupFlag` enum to
specify a rigid particle group. For example:
&nbsp;&nbsp;&nbsp;`pd.groupFlags = b2_rigidParticleGroup;`
For individual particles, use the b2ParticleFlag enum. The b2ParticleFlag enum
provides the flags described in the following sections. Note that different
particle behaviors may exact different performance costs.
### Elastic
Elastic particles deform and may also bounce when they collide with rigid
bodies.<br/>
Set particle behavior as elastic using the statement
&nbsp;&nbsp;&nbsp;`pd.flags = b2_elasticParticle;`
The green circle and the blue box in the "Elastic Particles" demo of the
Testbed application comprise elastic particles.
### Color-mixing
Color-mixing particles take on some of the color of other particles with which
they collide. If only one of the two colliding particles is a color-mixing
one,
the other particle retains its pre-collision color.<br/>
<br/>
The following example shows how color mixture is calculated. It shows the
collision of two color-mixing particles: one red ("R") and one green ("G").
1. First, the system calculates deltaColor, which is the value by which each
color will change.
deltaColor = colorMixingStrength * (B's color - A's color).<br/>
= 0.5 * ((0,255,0,255) - (255,0,0,255))<br/>
= 0.5 * (-255,255,0,0)<br/>
= (-127.5,127.5,0,0)
2. Then, it applies the delta to each particle
R's color += deltaColor<br/>
G's color -= deltaColor
3. As a result, both particles are now yellow:
A's color = (127.5,127.5,0,255)<br/>
B's color = (127.5,127.5,0,255)<br/>
<br/>
Note that when one of the operations in step 2 results in a negative number,
the
system uses the absolute value of that number. When it results in a value over
255, it rolls over from zero.<br/>
Set particle behavior as color-mixing using the statement<br/>
&nbsp;&nbsp;&nbsp;`pd.flags = b2_colorMixingParticle;`
The "Surface Tension" demo of the Testbed application uses color-mixing
particles.
### Powder
Powder particles produce a scattering effect such as you might see with sand
or
dust.<br/>
Set particle behavior as powder using the statement<br/>
&nbsp;&nbsp;&nbsp;`pd.flags = b2_powderParticle;`
The "Sparky" demo of the Testbed application uses powder particles.
### Spring
Spring particles produce the effect of being attached to one another, as by a
spring. Particles are "connected" in pairs. Each particle is connected to the
one that was closest to it at time of creation. Once paired, particles do not
change "partners." The farther an external force pulls them from one another,
the greater the power with which they collide when that external force is
removed. No matter how far particles get from one another, the connection
between them does not "snap."<br/>
Set spring behavior using the statement<br/>
&nbsp;&nbsp;&nbsp;`pd.flags = b2_springParticle;`
The red circle in the "Elastic Particles" demo of the Testbed application
comprises spring particles.
### Tensile
Tensile particles are used to produce the effect of surface tension, or the
taut
curvature on the surface of a body of liquid. They might be used, for example,
to create the surface tension you would see on a drop of water.<br/>
Once the tension is broken, the particles bounce as if they were elastic, but
also continue to attract each other. As a result, particles tend to form
clusters as they bounce.<br/>
Set tensile behavior using the statement
&nbsp;&nbsp;&nbsp;`pd.flags = b2_tensileParticle;`
The "Surface Tension" demo of the Testbed application uses tensile particles.
### Viscous
Viscous particles exhibit clinginess or stickiness, like oil.<br/>
Set viscous behavior using the statement
&nbsp;&nbsp;&nbsp;`pd.flags = b2_viscousParticle;`
The "Liquid Timer" demo of the Testbed application uses viscous particles.
### Static Pressure
Particles are subject to compression when pressure acts upon them. For example,
when particles pour into a container, the ones at the bottom of the container
are "crushed" under the weight of those above them and packed more tightly
together than the ones at the top of the pile.
The static-pressure particle eliminates this differential; the same amount of
pressure acts upon each particle in the group.
The following example sets static-pressure behavior.
&nbsp;&nbsp;&nbsp;`pd.flags = b2_staticPressureParticle;`
### Wall
Wall particles are static. They are permanently stationary, even if something
collides with them. <br/>
Set wall behavior using the statement
&nbsp;&nbsp;&nbsp;`pd.flags = b2_wallParticle;`
### Barrier
Solid or rigid particle groups are not inherently tunneling-proof. Particles
traveling at high enough velocities may penetrate them. Barrier particles,
used in conjunction with other particle types, provide particle groups
with protection against tunneling. This functionality is useful when, for
example, you want to ensure that liquid particles will not leak out of a
container formed of wall particles.
Barrier particles only prevent penetration of the particle groups they inhabit.
They cannot prevent particles from getting between groups of particles, even if
the groups' positions make them look as if they are contiguous.
You can use barrier particles with elastic, spring, or wall particles.
The following example creates an impermeable group of wall particles:
&nbsp;&nbsp;&nbsp;`pd.flags = b2_wallParticle | b_barrierParticle;`
&nbsp;&nbsp;&nbsp;`pd.groupFlags = b2_solidParticleGroup;`
### Zombie
Zombie particles are useful when you want efficiently to destroy multiple
particles in a single step. All of the particles that you designate as zombies
are destroyed at the same time, in a single iteration of the solver.
Destroying
particles in a batch, after designating them as zombies, yields better
performance than destroying them one by one: Whereas destroying particles
one-by-one takes (number of parti`cles) * (time per particle) to complete,
destroying them all in a batch takes the same time as it would to destroy a
single particle.<br/>
In the following example, every other particle in a group is designated as a
zombie, and will be destroyed in the next step of the solver. (For more
information on the LiquidFun solver, see Chapter 1. Introduction.)
&nbsp;&nbsp;&nbsp;`b2ParticleGroup*group=
m_particleSystem->CreateParticleGroup(pd);`<br/>
&nbsp;&nbsp;&nbsp;`for (int32 i=0;i<group->GetParticleCount();i+=2)`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`group->GetFlagsBuffer()[i] |=`
`b2_zombieParticle;`<br/>
&nbsp;&nbsp;&nbsp;`}`
Note that you can assign multiple behaviors to a group or particle. Use
the | ("bitwise OR") operator to chain behavior flags. For example, for a group:
&nbsp;&nbsp;&nbsp;`pd.groupFlags = b2_solidParticleGroup |
b2_rigidParticleGroup;`
And for particles:
&nbsp;&nbsp;&nbsp;`pd.flags = b2_elasticParticle | b2_viscousParticle;`
To define a group combining a specific group behavior with a specific particle
behavior, use two statements. For example:
&nbsp;&nbsp;&nbsp;`pd.flags = b2_elasticParticle;`<br/>
&nbsp;&nbsp;&nbsp;`pd.groupFlags = b2_solidParticleGroup;`<br/>
<a name="pp">
## Particle Properties
### Color
Set particle or particle-group color using the statement
&nbsp;&nbsp;&nbsp;`pd.color.Set(r, g, b, a);`
whose parameters set red, green, blue, and opacity, respectively. Each
parameter takes a value of 0-255.
### Size
There are two points to keep in mind when using small particles. First, in the
case of particle groups, particle size can affect performance. This is
because particle size is inversely proportional to the number of particles
generated to constitute a group. Having a large number of particles, in turn,
can diminish performance.
Set particle size using the statement
&nbsp;&nbsp;&nbsp;`m_particleSystem->SetRadius(r);`
where `r` is a float32 value greater than 0.0f. Default particle radius is
1.0f.
Small particles may also behave unpredictably (i.e., break conservation of
momentum) in scenarios such as explosions. Slowing these particles down by
reducing gravity scale can stabilize their behavior.
Set gravity scale using the statement
&nbsp;&nbsp;&nbsp;`m_particleSystem->SetGravityScale(g);`
where `g` is a `float32` value greater than 0.0f. Default gravity scale is
1.0f.
It is worth noting that adjusting the number of particle iterations per solver
step can also affect the effect of gravity on particles. Larger iteration sizes
confer greater resistance to gravity. A common reason for increasing the number
of particle-iterations is to prevent volume loss (i.e. compression) due to
gravity.
### Position
Set particle or particle-group position using the statement
&nbsp;&nbsp;&nbsp;`pd.position.Set(x, y);`
where `x` and `y` are the world-coordinates of the translation of the
particle
group.
### Velocity
For discrete particles, set velocity using the statement
&nbsp;&nbsp;&nbsp;`pd.velocity.Set(x,y);`
where `x` is velocity along the x-axis, and `y` is velocity along the
y-axis.<br/>
For particle groups, set velocity using the statements
&nbsp;&nbsp;&nbsp;`pd.linearVelocity.Set(x,y);`<br/>
&nbsp;&nbsp;&nbsp;`pd.angularVelocity = aV;`<br/>
where `x` is the group's velocity along the x-axis, `y` is velocity along the
y-axis, and `aV` is the group's angular (i.e., rotational) velocity (expressed
as radians per second).
### Angle (Groups Only)
This property applies only to rigid particle groups. It indicates the angle at
which a group is tilted. Set angle with the statement
&nbsp;&nbsp;&nbsp;`pd.angle =checkout a;`
where `a` is the angle of tilt, expressed in radians. Left unspecified, the
value defaults to 0.
### Strength (Groups Only)
Strength describes the cohesion of a group of particles. Set strength with the
statement
&nbsp;&nbsp;&nbsp;`pd.strength = s;`<br/>
where `s` is a float32 value between 0.0 (least cohesive) and 1.0 (most
cohesive). The default value is 1.0.
<a name="gl">
## Rendering with OpenGL
The Particle module provides particularly efficient rendering via OpenGL.
Each type of particle property lives in a contiguous memory buffer. For
example,
all particles' position data live next door to one another, all color data
live
next door to one another, and so forth. Table 1 provides a visual
representation
of this storage.
**_Table 1. Memory Map of Particle Buffers_**
<table>
<tr>
<td></td>
<td>Particle 1</td>
<td>Particle 2</td>
<td>Particle 3</td>
</tr>
<tr>
<td>Position</td>
<td>x1,y1</td>
<td>x2,y2</td>
<td>x3,y3</td>
</tr>
<tr>
<td>Address</td>
<td>0x00001000</td>
<td>0x00001008</td>
<td>0x00001010</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Color</td>
<td>r1,g1,b1,a1</td>
<td>r2,g2,b2,a2</td>
<td>r3,g3,b3,a3</td>
</tr>
<tr>
<td>Address</td>
<td>0x00002000</td>
<td>0x00002004</td>
<td>0x00002008</td>
</tr>
</table>
OpenGL can use these buffers directly in rendering.<br/>
In this example, OpenGL 1.1 would use glVertexPointer and glColorPointer to
get
the values from memory. OpenGL 2.0 would use glVertexAttribPointer.<br/>
OpenGL can be used to render either individual particles or particle groups.
<a name="sa">
## Sample Applications
Among the samples included in the LiquidFun distribution are two applications
that offer a quick look into the capabilities of the library.
Testbed includes a large number of demos that provide examples of different
types of particle behavior. While some of the demos are "look only," others are
interactive, allowing you to use your mouse or touchscreen to affect the
behavior on screen.
Experimenting with each of the demos, and comparing their behavior against the
source code, can provide useful insights into how different particles behave
under various conditions. Testbed builds and runs on Android, MacOSX, Linux,
and Windows.
EyeCandy is an Android-only application and is twofold in purpose: It provides
a simple Android example of how to use LiquidFun; and, it seeks to inspire
developers with its demonstration of the powerful liquid shaders it brings to
mobile hardware.
When running the program, you can slosh the fluid around by changing the
orientation of the Android device. You can also toggle bewteen shaders by
tapping the screen.
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,178 @@
# Loose Ends
[User Data](#ud)<br/>
[Implicit Destruction](#id)<br/>
[Pixels and Coordinate Systems](#pcs)<br/>
<a name="ud"></a>
## User Data
The b2Fixture, b2Body, and b2Joint classes allow you to attach user data as a
void pointer. This is handy when you are examining LiquidFun data structures
and you want to determine how they relate to the objects in your game engine.
For example, it is typical to attach an actor pointer to the rigid body on
that actor. This sets up a circular reference. If you have the actor, you can
get the body. If you have the body, you can get the actor.
&nbsp;&nbsp;&nbsp;`GameActor* actor = GameCreateActor();`<br/>
&nbsp;&nbsp;&nbsp;`b2BodyDef bodyDef;`<br/>
&nbsp;&nbsp;&nbsp;`bodyDef.userData = actor;`<br/>
&nbsp;&nbsp;&nbsp;`actor->body = box2Dworld->CreateBody(&bodyDef);`<br/>
Here are some examples of cases where you would need the user data:
* Applying damage to an actor using a collision result.
* Playing a scripted event if the player is inside an axis-aligned box.
* Accessing a game structure when LiquidFun notifies you that a joint is going
to be destroyed.
Keep in mind that user data is optional and you can put anything in it.
However, you should be consistent. For example, if you want to store an actor
pointer on one body, you should keep an actor pointer on all bodies. Don't
store an actor pointer on one body, and a foo pointer on another body. Casting
an actor pointer to a foo pointer may lead to a crash.
User data pointers are NULL by default.
For fixtures you might consider defining a user data structure that lets you
store game specific information, such as material type, effects hooks, sound
hooks, etc.
&nbsp;&nbsp;&nbsp;`struct FixtureUserData`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;` int materialIndex;`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;` . . .`<br/>
&nbsp;&nbsp;&nbsp;`};`<br/>
&nbsp;&nbsp;&nbsp;`FixtureUserData myData = new FixtureUserData;`<br/>
&nbsp;&nbsp;&nbsp;`myData->materialIndex = 2;`<br/>
&nbsp;&nbsp;&nbsp;`b2FixtureDef fixtureDef;`<br/>
&nbsp;&nbsp;&nbsp;`fixtureDef.shape = &someShape;`<br/>
&nbsp;&nbsp;&nbsp;`fixtureDef.userData = myData;`<br/>
&nbsp;&nbsp;&nbsp;`b2Fixture* fixture = body->CreateFixture(&fixtureDef);`<br/>
&nbsp;&nbsp;&nbsp;`. . .`<br/>
&nbsp;&nbsp;&nbsp;`delete fixture->GetUserData();`<br/>
&nbsp;&nbsp;&nbsp;`fixture->SetUserData(NULL);`<br/>
&nbsp;&nbsp;&nbsp;`body->DestroyFixture(fixture);`<br/>
<a name="id"></a>
## Implicit Destruction
LiquidFun doesn't use reference counting. So if you destroy a body it is
really gone. Accessing a pointer to a destroyed body has undefined behavior.
In other words, your program will likely crash and burn. To help fix these
problems, the debug build memory manager fills destroyed entities with
FDFDFDFD. This can help find problems more easily in some cases.
If you destroy a LiquidFun entity, it is up to you to make sure you remove all
references to the destroyed object. This is easy if you only have a single
reference to the entity. If you have multiple references, you might consider
implementing a handle class to wrap the raw pointer.
Often when using LiquidFun you will create and destroy many bodies, shapes,
and joints. Managing these entities is somewhat automated by LiquidFun. If you
destroy a body then all associated shapes and joints are automatically
destroyed. This is called implicit destruction.
When you destroy a body, all its attached shapes, joints, and contacts are
destroyed. This is called implicit destruction. Any body connected to one of
those joints and/or contacts is woken. This process is usually convenient.
However, you must be aware of one crucial issue:
Caution
When a body is destroyed, all fixtures and joints attached to the body
are automatically destroyed. You must nullify any pointers you have to those
shapes and joints. Otherwise, your program will die horribly if you try to
access or destroy those shapes or joints later.
To help you nullify your joint pointers, LiquidFun provides a listener class
named b2DestructionListener that you can implement and provide to your world
object. Then the world object will notify you when a joint is going to be
implicitly destroyed
Note that there no notification when a joint or fixture is explicitly
destroyed. In this case ownership is clear and you can perform the necessary
cleanup on the spot. If you like, you can call your own implementation of
b2DestructionListener to keep cleanup code centralized.
Implicit destruction is a great convenience in many cases. It can also make
your program fall apart. You may store pointers to shapes and joints somewhere
in your code. These pointers become orphaned when an associated body is
destroyed. The situation becomes worse when you consider that joints are often
created by a part of the code unrelated to management of the associated body.
For example, the testbed creates a b2MouseJoint for interactive manipulation
of bodies on the screen.
LiquidFun provides a callback mechanism to inform your application when
implicit destruction occurs. This gives your application a chance to nullify
the orphaned pointers. This callback mechanism is described later in this
manual.
You can implement a b2DestructionListener that allows b2World to inform you
when a shape or joint is implicitly destroyed because an associated body was
destroyed. This will help prevent your code from accessing orphaned pointers.
&nbsp;&nbsp;&nbsp;`class MyDestructionListener : public
b2DestructionListener`<br/>
&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`void SayGoodbye(b2Joint* joint)`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`{`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`// remove all
references to joint.`<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`}`<br/>
&nbsp;&nbsp;&nbsp;`};`<br/>
You can then register an instance of your destruction listener with your world
object. You should do this during world initialization.
&nbsp;&nbsp;&nbsp;`myWorld->SetListener(myDestructionListener);`
<a name="pcs"></a>
## Pixels and Coordinate Systems
Recall that LiquidFun uses MKS (meters, kilograms, and seconds) units and
radians for angles. You may have trouble working with meters because your game
is expressed in terms of pixels. To deal with this in the testbed I have the
whole *game* work in meters and just use an OpenGL viewport transformation to
scale the world into screen space.
&nbsp;&nbsp;&nbsp;`float lowerX = -25.0f, upperX = 25.0f, lowerY = -5.0f,
upperY = 25.0f;`<br/>
&nbsp;&nbsp;&nbsp;`gluOrtho2D(lowerX, upperX, lowerY, upperY);`<br/>
If your game must work in pixel units then you should convert your length
units from pixels to meters when passing values from LiquidFun. Likewise you
should convert the values received from LiquidFun from meters to pixels. This
will improve the stability of the physics simulation.
You have to come up with a reasonable conversion factor. I suggest making this
choice based on the size of your characters. Suppose you have determined to
use 50 pixels per meter (because your character is 75 pixels tall). Then you
can convert from pixels to meters using these formulas:
&nbsp;&nbsp;&nbsp;`xMeters = 0.02f * xPixels;`<br/>
&nbsp;&nbsp;&nbsp;`yMeters = 0.02f * yPixels;`<br/>
In reverse:
&nbsp;&nbsp;&nbsp;`xPixels = 50.0f * xMeters;`<br/>
&nbsp;&nbsp;&nbsp;`yPixels = 50.0f * yMeters;`<br/>
You should consider using MKS units in your game code and just convert to
pixels when you render. This will simplify your game logic and reduce the
chance for errors since the rendering conversion can be isolated to a small
amount of code.
If you use a conversion factor, you should try tweaking it globally to make
sure nothing breaks. You can also try adjusting it to improve stability.
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,28 @@
# Debug Drawing
You can implement the b2DebugDraw class to get detailed drawing of the physics
world. Here are the available entities:
* shape outlines
* joint connectivity
* broad-phase axis-aligned bounding boxes (AABBs)
* center of mass
<img align="center" src="image_24.png" alt="Debug drawing" height="306"
width="431"><br/>
This is the preferred method of drawing these physics entities, rather than
accessing the data directly. The reason is that much of the necessary data is
internal and subject to change.
The testbed draws physics entities using the debug draw facility and the
contact listener, so it serves as the primary example of how to implement
debug drawing as well as how to draw contact points.
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,33 @@
## Limitations
LiquidFun uses several approximations to simulate rigid body physics
efficiently. This brings some limitations.
Here are the current limitations:
1. Stacking heavy bodies on top of much lighter bodies is not stable.
Stability degrades as the mass ratio passes 10:1.
2. Chains of bodies connected by joints may stretch if a lighter body is
supporting a heavier body. For example, a wrecking ball connect to a chain of
light weight bodies may not be stable. Stability degrades as the mass ratio
passes 10:1.
3. There is typically around 0.5cm of slop in shape versus shape collision.
4. Continuous collision does not handle joints. So you may see joint
stretching on fast moving objects.
5. LiquidFun uses the symplectic Euler integration scheme. It does not
reproduce parabolic motion of projectiles and has only first-order accuracy.
However it is fast and has good stability.
6. LiquidFun uses an iterative solver to provide real-time performance. You
will not get precisely rigid collisions or pixel perfect accuracy. Increasing
the iterations will improve accuracy.
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,13 @@
# References
Erin Catto's GDC Tutorials:<br/>
[http://code.google.com/p/box2d/downloads/list](http://code.google.com/p/box2d/downloads/list)<br/>
_Collision Detection in Interactive 3D Environments,_ Gino van den Bergen,
2004<br/>
_Real-Time Collision Detection,_ Christer Ericson, 2005
*This content is licensed under
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
For details and restrictions, please see the
[Content License](md__content_license.html).*
@@ -0,0 +1,11 @@
# Content License
Portions of this documentation are modifications based on work
created and shared by Erin Catto and used according to terms
described in the
[Creative Commons 4.0 Attribution License](http://creativecommons.org/licenses/by/4.0/legalcode)
Copyright © 2007-2011 Erin Catto
Copyright © 2013-2014 Fun Propulsion Labs at Google
@@ -0,0 +1,17 @@
# Logo
Please feel free to make fair use the LiquidFun logo in connection with any
implementations (in your splash screens or documentation, for example).
There is no requirement to include the logo, but we appreciate your
acknowledgement. We only ask that you avoid changing the proportions of the
logo or otherwise modifying it, and that you avoid using the logo in a way
that suggests your implementation is developed by, sponsored by, or affiliated
with Fun Propulsion Labs or Google. (For example, you shouldn't use the
LiquidFun logo as your app icon, and you shouldn't use it more prominently
than your own logos or icons.)
![LiquidFun Logo bitmap](liquidfun-logo-small.png)
* [Logo in bitmap format](liquidfun-logo.png)
* [Logo in vector format](liquidfun-logo.ai)
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Some files were not shown because too many files have changed in this diff Show More