/* * 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