604 lines
18 KiB
C++
604 lines
18 KiB
C++
/*
|
|
* Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/* Testbed example showing deformable and breakable bodies using the soft
|
|
* b2DistanceJoint and a small,liteweight triangle mesher.
|
|
* 2008-05-09 / nimodo
|
|
*/
|
|
#ifndef BREAKABLE_BODY_H
|
|
#define BREAKABLE_BODY_H
|
|
|
|
#include "TriangleMesh.h"
|
|
|
|
/// utility macro
|
|
#define H(x) (x)/2.0f
|
|
#define N_MAXVERTEX 256
|
|
|
|
class BreakableBody : public Test
|
|
{
|
|
|
|
public:
|
|
BreakableBody()
|
|
{
|
|
/// geometries
|
|
float32 gx = 100.0f, gy = 1.0f,
|
|
dx = 34.0f, br = 0.3f;
|
|
float32 sx=-dx-H(dx), sy = 30.f;
|
|
/// break joint, if the reactionforce exceeds:
|
|
maxAllowableForce = 100.0f;
|
|
m_drawMode = m_staticBodies = false;
|
|
m_drawCount = 0;
|
|
/// ground
|
|
{
|
|
b2PolygonDef sd;
|
|
b2BodyDef bd;
|
|
b2Body* ground;
|
|
bd.position.Set(0.0f, 0.0f);
|
|
ground = m_world->CreateBody(&bd);
|
|
/// bottom
|
|
sd.SetAsBox( H(gx), H(gy) );
|
|
ground->CreateFixture(&sd);
|
|
sd.SetAsBox( H(dx), H(gy), b2Vec2(-dx,sy-1.0f), 0.0f );
|
|
ground->CreateFixture(&sd);
|
|
}
|
|
/// dyn bodies
|
|
{
|
|
b2PolygonDef pd;
|
|
b2DistanceJointDef dj;
|
|
|
|
dj.dampingRatio = 0.0f;
|
|
dj.collideConnected = true;
|
|
|
|
ExampleData('B');
|
|
dj.frequencyHz = 20.f;
|
|
pd.density = 1.0f/70.0f;
|
|
pd.friction = 0.4f;
|
|
pd.restitution = 0.01f;
|
|
CreateSoftBody( b2Vec2(sx,sy), 0, 0, pd, dj,
|
|
nodes,n_nodes, segments,n_segments, holes,n_holes) ;
|
|
|
|
ExampleData('@');
|
|
dj.frequencyHz = 20.f;
|
|
pd.density = 1.0f/36.0f;
|
|
pd.friction = 0.1f;
|
|
pd.restitution = 0.5f;
|
|
CreateSoftBody( b2Vec2(sx+6.f,sy), 0, 0, pd, dj,
|
|
nodes,n_nodes, segments,n_segments, holes,n_holes) ;
|
|
|
|
ExampleData('x');
|
|
dj.frequencyHz = 20.0f;
|
|
pd.density = 1.0f/60.0f;
|
|
pd.friction = 0.6f;
|
|
pd.restitution = 0.0f;
|
|
CreateSoftBody( b2Vec2(sx+13.f,sy), 0, 0, pd, dj,
|
|
nodes,n_nodes, segments,n_segments, holes,n_holes) ;
|
|
|
|
ExampleData('2');
|
|
pd.density = 0.01f;
|
|
pd.friction = 0.3f;
|
|
pd.restitution = 0.3f;
|
|
CreateSoftBody( b2Vec2(sx+20.f,sy), 0, 0, pd, dj,
|
|
nodes,n_nodes, segments,n_segments, holes,n_holes) ;
|
|
|
|
ExampleData('D');
|
|
CreateSoftBody( b2Vec2(sx+28.f,sy), 0, 0, pd, dj,
|
|
nodes,n_nodes, segments,n_segments, holes,n_holes) ;
|
|
|
|
ExampleData('b');
|
|
dj.frequencyHz = 10.0f;
|
|
dj.dampingRatio = 20.0f;
|
|
pd.friction = 0.9f;
|
|
pd.restitution = 0.01f;
|
|
pd.density = 0.01f;
|
|
CreateSoftBody( b2Vec2(-5.f,5.f*gy), 0, 0, pd, dj,
|
|
nodes,n_nodes, segments,n_segments, holes,n_holes) ;
|
|
|
|
b2CircleDef cd;
|
|
b2BodyDef bd;
|
|
b2Body* b;
|
|
cd.radius = br;
|
|
cd.density= 0.001f;
|
|
bd.position.Set(0.0f,10.0f*gy);
|
|
for (int32 i=0; i<60; i++ )
|
|
{
|
|
b = m_world->CreateBody(&bd);
|
|
b->CreateFixture (&cd);
|
|
b->SetMassFromShapes();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/// Create compound (soft) body using a triangle mesh
|
|
/// If meshDensity is 0, a minimal grid is generated.
|
|
/// Actually pd and dj define the behaviour for all triangles
|
|
void CreateSoftBody(b2Vec2 pos, int32 meshDensity,int32 options,
|
|
b2PolygonDef pd, b2DistanceJointDef dj,
|
|
tmVertex* nodes,int32 n_nodes,
|
|
tmSegmentId *segments=NULL, int32 n_segments=0,
|
|
tmVertex* holes=NULL, int32 n_holes=0)
|
|
{
|
|
int32 i;
|
|
/// TriangleMesh defs
|
|
tmTriangle *triangles;
|
|
TriangleMesh md;
|
|
/// box2d defs
|
|
b2BodyDef bd;
|
|
b2Body *b;
|
|
/// in case of meshDensit>3 ...
|
|
md.SetMaxVertexCount(meshDensity);
|
|
if (options>0) md.SetOptions(options);
|
|
/// triangulator main
|
|
md.Mesh( nodes, n_nodes, segments,n_segments, holes, n_holes );
|
|
md.PrintData();
|
|
/// bodies (triangles)
|
|
triangles = md.GetTriangles();
|
|
if ( triangles==NULL ) return;
|
|
pd.vertexCount = 3;
|
|
for ( i=0; i<md.GetTriangleCount(); i++ )
|
|
{
|
|
if ( triangles[i].inside )
|
|
{
|
|
/// triangle -> b2PolygonDef
|
|
pd.vertices[0].Set(triangles[i].v[0]->x, triangles[i].v[0]->y);
|
|
pd.vertices[1].Set(triangles[i].v[1]->x, triangles[i].v[1]->y);
|
|
pd.vertices[2].Set(triangles[i].v[2]->x, triangles[i].v[2]->y);
|
|
bd.position.Set(pos.x,pos.y);
|
|
b = m_world->CreateBody(&bd);
|
|
b->CreateFixture(&pd);
|
|
b->SetMassFromShapes();
|
|
/// we need the body pointer in the triangles for the joints later
|
|
triangles[i].userData = (void *)b;
|
|
}
|
|
}
|
|
/// joints
|
|
/// for each triangle-pair in edges, connect with a distance joint
|
|
tmEdge *edges;
|
|
tmTriangle *t0,*t1;
|
|
b2Body *b1,*b2;
|
|
edges = md.GetEdges();
|
|
for ( i=0; i<md.GetEdgeCount(); i++ )
|
|
{
|
|
t0 = edges[i].t[0];
|
|
t1 = edges[i].t[1];
|
|
if ( (t0->inside==false) || (t1->inside==false) ) continue;
|
|
|
|
/// Get bodies
|
|
b1 = (b2Body*)t0->userData;
|
|
b2 = (b2Body*)t1->userData;
|
|
if ( b1==NULL || b2==NULL ) continue;
|
|
|
|
dj.Initialize( b1,b2, b1->GetWorldCenter(), b2->GetWorldCenter());
|
|
m_world->CreateJoint(&dj);
|
|
}
|
|
/// clean TriangleMesh
|
|
md.FreeMemory();
|
|
}
|
|
|
|
/// maybe here to check for maximal reaction forces to break a body
|
|
void Step(Settings* settings)
|
|
{
|
|
b2Joint *jStressed=NULL;
|
|
float32 F=0.0f, tmp;
|
|
|
|
Test::Step(settings);
|
|
|
|
for (b2Joint* j = m_world->GetJointList(); j; j = j->GetNext())
|
|
{
|
|
tmp = j->GetReactionForce(settings->hz).Length();
|
|
if ( tmp>F )
|
|
{
|
|
F = tmp;
|
|
jStressed = j;
|
|
}
|
|
}
|
|
if ( jStressed && (F>maxAllowableForce) )
|
|
{
|
|
m_world->DestroyJoint(jStressed);
|
|
}
|
|
|
|
m_debugDraw.DrawString(1, m_textLine,"max.reactionforce=%.0f allowable=%.0f change:-+", (float)F,(float)maxAllowableForce);
|
|
m_textLine += 12;
|
|
|
|
m_debugDraw.DrawString(1, m_textLine,"drawmode(%s):d mesh:m static(%s):s", (m_drawMode)?"on":"off", (m_staticBodies)?"on":"off");
|
|
m_textLine += 12;
|
|
|
|
for ( int32 i=0; i<m_drawCount-1; i++ )
|
|
{
|
|
b2Vec2 p1,p2;
|
|
p1.Set(m_drawVertices[i].x,m_drawVertices[i].y);
|
|
p2.Set(m_drawVertices[i+1].x,m_drawVertices[i+1].y);
|
|
m_debugDraw.DrawSegment(p1,p2,b2Color(0.6f,0.2f,0.2f));
|
|
}
|
|
}
|
|
|
|
/// default constructor for TestEntries.cpp
|
|
static Test* Create()
|
|
{
|
|
return new BreakableBody;
|
|
}
|
|
|
|
void Keyboard(unsigned char key)
|
|
{
|
|
switch (key)
|
|
{
|
|
case '-':
|
|
maxAllowableForce -= 5.0f;
|
|
break;
|
|
|
|
case '+':
|
|
maxAllowableForce += 5.0f;
|
|
break;
|
|
|
|
case 'd':
|
|
m_drawMode = !m_drawMode;
|
|
break;
|
|
|
|
case 's':
|
|
m_staticBodies = !m_staticBodies;
|
|
break;
|
|
|
|
case 'm':
|
|
if ( m_drawCount>0 )
|
|
{
|
|
b2PolygonDef pd;
|
|
b2DistanceJointDef dj;
|
|
dj.collideConnected = true;
|
|
dj.frequencyHz = 20.f;
|
|
dj.dampingRatio = 10.0f;
|
|
pd.density = (m_staticBodies) ? 0.0f : 1.0f/32.0f;
|
|
pd.friction = 0.99f;
|
|
pd.restitution = 0.01f;
|
|
CreateSoftBody( b2Vec2(0.0f,0.0f), 0, tmO_SEGMENTBOUNDARY|tmO_GRADING,
|
|
pd, dj, m_drawVertices, m_drawCount) ;
|
|
|
|
m_drawCount = 0;
|
|
m_drawMode = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MouseDown(const b2Vec2& p)
|
|
{
|
|
if ( m_drawMode && (m_drawCount<N_MAXVERTEX) )
|
|
{
|
|
m_drawVertices[m_drawCount].x = p.x;
|
|
m_drawVertices[m_drawCount].y = p.y;
|
|
m_drawCount++;
|
|
}
|
|
else Test::MouseDown(p);
|
|
}
|
|
/*
|
|
void MouseMove(const b2Vec2& p)
|
|
{
|
|
m_lastPoint = p;
|
|
if (m_drawMode)
|
|
{
|
|
}
|
|
}
|
|
*/
|
|
void MouseUp(const b2Vec2& p)
|
|
{
|
|
Test::MouseUp(p);
|
|
}
|
|
|
|
/// examples
|
|
void ExampleData(char which)
|
|
{
|
|
/// @ - ring
|
|
static tmVertex ring_nodes[] = {
|
|
{ 6.00f, 3.00f},
|
|
{ 5.12f, 5.12f},
|
|
{ 3.00f, 6.00f},
|
|
{ 0.88f, 5.12f},
|
|
{ 0.00f, 3.00f},
|
|
{ 0.88f, 0.88f},
|
|
{ 3.00f, 0.00f},
|
|
{ 5.12f, 0.88f},
|
|
{ 4.50f, 3.00f},
|
|
{ 4.06f, 4.06f},
|
|
{ 3.00f, 4.50f},
|
|
{ 1.94f, 4.06f},
|
|
{ 1.50f, 3.00f},
|
|
{ 1.94f, 1.94f},
|
|
{ 3.00f, 1.50f},
|
|
{ 4.06f, 1.94f}
|
|
};
|
|
static tmSegmentId ring_segments[] = {
|
|
{ 9, 10 },
|
|
{ 10, 11 },
|
|
{ 11, 12 },
|
|
{ 12, 13 },
|
|
{ 13, 14 },
|
|
{ 14, 15 },
|
|
{ 15, 16 },
|
|
{ 16, 9 }
|
|
};
|
|
static tmVertex ring_holes[] = {
|
|
{ 3.00f, 3.00f}
|
|
};
|
|
/// 'B'
|
|
static tmVertex B_nodes[] = {
|
|
{ 0.00f, 0.00f},
|
|
{ 4.00f, 0.00f},
|
|
{ 5.00f, 2.00f},
|
|
{ 5.00f, 4.00f},
|
|
{ 4.00f, 5.00f},
|
|
{ 5.00f, 6.00f},
|
|
{ 5.00f, 8.00f},
|
|
{ 4.00f, 9.00f},
|
|
{ 0.00f, 9.00f},
|
|
{ 0.00f, 5.00f},
|
|
{ 1.50f, 1.50f},
|
|
{ 3.50f, 1.50f},
|
|
{ 3.50f, 4.00f},
|
|
{ 1.50f, 4.00f},
|
|
{ 1.50f, 6.00f},
|
|
{ 3.50f, 6.00f},
|
|
{ 3.50f, 8.50f},
|
|
{ 1.50f, 8.50f}
|
|
};
|
|
static tmSegmentId B_segments[] = {
|
|
{ 1, 2 },
|
|
{ 2, 3 },
|
|
{ 3, 4 },
|
|
{ 4, 5 },
|
|
{ 5, 6 },
|
|
{ 6, 7 },
|
|
{ 7, 8 },
|
|
{ 8, 9 },
|
|
{ 9, 10 },
|
|
{ 10, 1 },
|
|
{ 11, 12 },
|
|
{ 12, 13 },
|
|
{ 13, 14 },
|
|
{ 14, 11 },
|
|
{ 15, 16 },
|
|
{ 16, 17 },
|
|
{ 17, 18 },
|
|
{ 18, 15 }
|
|
};
|
|
static tmVertex B_holes[] = {
|
|
{ 5.00f, 5.00f},
|
|
{ 2.50f, 2.50f},
|
|
{ 2.50f, 7.00f}
|
|
};
|
|
/// 'D'
|
|
static tmVertex D_nodes[] = {
|
|
{ 0.00f, 0.00f},
|
|
{ 4.00f, 0.00f},
|
|
{ 5.00f, 2.50f},
|
|
{ 5.00f, 7.00f},
|
|
{ 4.00f, 9.00f},
|
|
{ 0.00f, 9.00f},
|
|
{ 0.00f, 5.00f},
|
|
{ 1.50f, 2.50f},
|
|
{ 3.50f, 2.50f},
|
|
{ 3.50f, 7.00f},
|
|
{ 1.50f, 7.00f},
|
|
};
|
|
static tmSegmentId D_segments[] = {
|
|
{ 1, 2 },
|
|
{ 2, 3 },
|
|
{ 3, 4 },
|
|
{ 4, 5 },
|
|
{ 5, 6 },
|
|
{ 6, 7 },
|
|
{ 7, 1 },
|
|
{ 8, 9 },
|
|
{ 9, 10 },
|
|
{ 10, 11 },
|
|
{ 11, 8 },
|
|
};
|
|
static tmVertex D_holes[] = {
|
|
{ 2.50f, 5.00f},
|
|
};
|
|
/// 'x'
|
|
static tmVertex x_nodes[] = {
|
|
{ 0.00f, 0.00f},
|
|
{ 1.00f, 0.00f},
|
|
{ 5.00f, 0.00f},
|
|
{ 6.00f, 0.00f},
|
|
{ 6.00f, 1.00f},
|
|
{ 6.00f, 5.00f},
|
|
{ 6.00f, 6.00f},
|
|
{ 1.00f, 6.00f},
|
|
{ 5.00f, 6.00f},
|
|
{ 0.00f, 6.00f},
|
|
{ 0.00f, 5.00f},
|
|
{ 0.00f, 1.00f},
|
|
{ 3.00f, 2.00f},
|
|
{ 4.00f, 3.00f},
|
|
{ 3.00f, 4.00f},
|
|
{ 2.00f, 3.00f}
|
|
};
|
|
static tmSegmentId x_segments[] = {
|
|
{ 2, 13 },
|
|
{ 3, 13 },
|
|
{ 5, 14 },
|
|
{ 6, 14 },
|
|
{ 8, 15 },
|
|
{ 9, 15 },
|
|
{ 11, 16 },
|
|
{ 12, 16 }
|
|
};
|
|
static tmVertex x_holes[] = {
|
|
{ 3.00f, 1.00f},
|
|
{ 5.00f, 3.00f},
|
|
{ 3.00f, 5.00f},
|
|
{ 1.00f, 3.00f},
|
|
};
|
|
/// '2'
|
|
static tmVertex two_nodes[] = {
|
|
{ 0.00f, 0.00f},
|
|
{ 6.00f, 0.00f},
|
|
{ 6.00f, 1.00f},
|
|
{ 2.00f, 1.00f},
|
|
{ 2.00f, 2.00f},
|
|
{ 6.00f, 6.00f},
|
|
{ 6.00f, 8.00f},
|
|
{ 5.00f, 9.00f},
|
|
{ 2.00f, 9.00f},
|
|
{ 1.00f, 7.50f},
|
|
{ 0.00f, 2.50f},
|
|
{ 5.00f, 6.50f},
|
|
{ 5.00f, 8.00f},
|
|
{ 2.50f, 8.00f},
|
|
{ 2.00f, 7.50f},
|
|
};
|
|
static tmSegmentId two_segments[] = {
|
|
{ 1, 2 },
|
|
{ 2, 3 },
|
|
{ 3, 4 },
|
|
{ 4, 5 },
|
|
{ 5, 6 },
|
|
{ 6, 7 },
|
|
{ 7, 8 },
|
|
{ 8, 9 },
|
|
{ 9, 10 },
|
|
{ 10, 15 },
|
|
{ 11, 12 },
|
|
{ 12, 13 },
|
|
{ 13, 14 },
|
|
{ 14, 15 },
|
|
};
|
|
static tmVertex two_holes[] = {
|
|
{ 3.00f, 5.00f},
|
|
{ 4.00f, 3.00f},
|
|
};
|
|
/// '-' beam
|
|
static tmVertex beam_nodes[] = {
|
|
{ 0.00f, 0.00f},
|
|
{ 32.00f, 0.00f},
|
|
{ 32.00f, 3.00f},
|
|
{ 0.00f, 3.00f},
|
|
};
|
|
static tmSegmentId *beam_segments = NULL;
|
|
static tmVertex *beam_holes = NULL;
|
|
/// 'b' a box
|
|
static tmVertex b_nodes[] = {
|
|
{ 0.00f, 0.00f},
|
|
{ 10.00f, 0.00f},
|
|
{ 10.00f, 10.00f},
|
|
{ 0.00f, 10.00f},
|
|
{ 2.00f, 2.00f},
|
|
{ 8.00f, 2.00f},
|
|
{ 8.00f, 8.00f},
|
|
{ 2.00f, 8.00f},
|
|
};
|
|
static tmSegmentId b_segments[] = {
|
|
{ 5, 6 },
|
|
{ 6, 7 },
|
|
{ 7, 8 },
|
|
{ 8, 5 },
|
|
};
|
|
static tmVertex b_holes[] = {
|
|
{ 5.0f, 5.0f},
|
|
};
|
|
/// choose...
|
|
switch( which )
|
|
{
|
|
case 'B':
|
|
nodes = B_nodes;
|
|
segments = B_segments;
|
|
holes = B_holes;
|
|
|
|
n_nodes = sizeof(B_nodes)/sizeof(tmVertex);
|
|
n_segments = (segments) ? sizeof(B_segments)/sizeof(tmSegmentId) : 0;
|
|
n_holes = (holes) ? sizeof(B_holes)/sizeof(tmVertex) : 0;
|
|
break;
|
|
|
|
case 'D':
|
|
nodes = D_nodes;
|
|
segments = D_segments;
|
|
holes = D_holes;
|
|
|
|
n_nodes = sizeof(D_nodes)/sizeof(tmVertex);
|
|
n_segments = (segments) ? sizeof(D_segments)/sizeof(tmSegmentId) : 0;
|
|
n_holes = (holes) ? sizeof(D_holes)/sizeof(tmVertex) : 0;
|
|
break;
|
|
|
|
case 'x':
|
|
nodes = x_nodes;
|
|
segments = x_segments;
|
|
holes = x_holes;
|
|
|
|
n_nodes = sizeof(x_nodes)/sizeof(tmVertex);
|
|
n_segments = (segments) ? sizeof(x_segments)/sizeof(tmSegmentId) : 0;
|
|
n_holes = (holes) ? sizeof(x_holes)/sizeof(tmVertex) : 0;
|
|
break;
|
|
|
|
case '@':
|
|
nodes = ring_nodes;
|
|
segments = ring_segments;
|
|
holes = ring_holes ;
|
|
|
|
n_nodes = sizeof(ring_nodes)/sizeof(tmVertex);
|
|
n_segments = (segments) ? sizeof(ring_segments)/sizeof(tmSegmentId) : 0;
|
|
n_holes = (holes) ? sizeof(ring_holes)/sizeof(tmVertex) : 0;
|
|
break;
|
|
|
|
case '2':
|
|
nodes = two_nodes;
|
|
segments = two_segments;
|
|
holes = two_holes ;
|
|
|
|
n_nodes = sizeof(two_nodes)/sizeof(tmVertex);
|
|
n_segments = (segments) ? sizeof(two_segments)/sizeof(tmSegmentId) : 0;
|
|
n_holes = (holes) ? sizeof(two_holes)/sizeof(tmVertex) : 0;
|
|
break;
|
|
case '-':
|
|
nodes = beam_nodes;
|
|
segments = beam_segments;
|
|
holes = beam_holes ;
|
|
|
|
n_nodes = sizeof(beam_nodes)/sizeof(tmVertex);
|
|
n_segments = (segments) ? sizeof(beam_segments)/sizeof(tmSegmentId) : 0;
|
|
n_holes = (holes) ? sizeof(beam_holes)/sizeof(tmVertex) : 0;
|
|
break;
|
|
case 'b':
|
|
nodes = b_nodes;
|
|
segments = b_segments;
|
|
holes = b_holes;
|
|
|
|
n_nodes = sizeof(b_nodes)/sizeof(tmVertex);
|
|
n_segments = (segments) ? sizeof(b_segments)/sizeof(tmSegmentId) : 0;
|
|
n_holes = (holes) ? sizeof(b_holes)/sizeof(tmVertex) : 0;
|
|
break;
|
|
}
|
|
}
|
|
///
|
|
bool m_drawMode, m_staticBodies;
|
|
tmVertex m_drawVertices[N_MAXVERTEX];
|
|
int32 m_drawCount;
|
|
///
|
|
float32 maxAllowableForce;
|
|
/// temporary vars to hold the examples
|
|
tmVertex *nodes;
|
|
int32 n_nodes;
|
|
tmVertex *holes;
|
|
int32 n_holes;
|
|
tmSegmentId *segments;
|
|
int32 n_segments;
|
|
};
|
|
|
|
#undef H
|
|
|
|
#endif
|