/* world: b2world对象 pos:受力点 body:被切分的box2d对象 csx:被切分的cocos2dx对象 */ void BodyExplosion(b2World *world, b2Vec2 &pos, b2Body* body, CCSprite* csx)
#ifndef __BodyExplosion_h__ #define __BodyExplosion_h__ #include <Box2D/Box2D.h> #include "jb2d.h" #include "cocos2d.h" #include "zarray.h" class JoySplitRayCastCallback:public b2RayCastCallback { public: JoySplitRayCastCallback(b2World* pworld, b2Body* body=NULL); void FloatCompareAccuracy() { m_minx-=0.5; m_miny-=0.5; m_maxx+=0.5; m_maxy+=0.5; } virtual float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction); public: void splitObj(b2Body* sliceBody, b2Vec2& A, b2Vec2& B); void resetCB(); void addPoint(b2Body* body, const b2Vec2& p); void removePoint(b2Body* body); void CreateBody( b2Body* sliceBody, zArrayT<b2Vec2> &vecs); bool PointInBody(const b2Vec2& point); bool CheckClockwise(zArrayT<b2Vec2> &vecs); public: b2World* world; b2Body* clickBody; bool destroyed; float m_minx; float m_miny; float m_maxx; float m_maxy; zArrayT<b2Body*> explodingBodies; zArrayT<b2Vec2> enterPointsVec; zArrayT<b2Body*> enterPointsVecBody; zArrayT<b2Body*> slicedBodies; }; #endif
#define PI 3.141295f
float32 JoySplitRayCastCallback::ReportFixture( b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction ) { b2Body* body=fixture->GetBody(); if (explodingBodies.find(body) == -1) { return 1; } int idx=enterPointsVecBody.find(body); if (idx==-1) { addPoint(body, point); } else { splitObj(fixture->GetBody(), enterPointsVec[idx], (b2Vec2&)point); } return 1; } float det(float x1, float y1, float x2, float y2, float x3, float y3) { // This is a function which finds the determinant of a 3x3 matrix. // If you studied matrices, you'd know that it returns a positive number if three given points are in clockwise order, negative if they are in anti-clockwise order and zero if they lie on the same line. // Another useful thing about determinants is that their absolute value is two times the face of the triangle, formed by the three given points. return x1*y2+x2*y3+x3*y1-y1*x2-y2*x3-y3*x1; } int comp1(const void* a1,const void* b1) { b2Vec2& a=*(b2Vec2*)a1; b2Vec2& b=*(b2Vec2*)b1; // This is a compare function, used in the arrangeClockwise() method - a fast way to arrange the points in ascending order, according to their x-coordinate. if (a.x*10000>b.x*10000) { return 1; } else if (a.x*10000<b.x*10000) { return -1; } return 0; } void arrangeClockwise(zArrayT<b2Vec2>& vec) { // The algorithm is simple: // First, it arranges all given points in ascending order, according to their x-coordinate. // Secondly, it takes the leftmost and rightmost points (lets call them C and D), and creates tempVec, where the points arranged in clockwise order will be stored. // Then, it iterates over the vertices vector, and uses the det() method I talked about earlier. It starts putting the points above CD from the beginning of the vector, and the points below CD from the end of the vector. // That was it! int n=vec.count(); float d; int i1=1,i2=n-1; zArrayT<b2Vec2> tempVec; b2Vec2 C; b2Vec2 D; qsort(vec.getDataPtr(), vec.count(), sizeof(b2Vec2), comp1); tempVec.setSize(n); tempVec[0]=vec[0]; C=vec[0]; D=vec[n-1]; for (int i=1; i<n-1; i++) { d=det(C.x,C.y,D.x,D.y,vec[i].x,vec[i].y); if (d<0) { tempVec[i1++]=vec[i]; } else { tempVec[i2--]=vec[i]; } } tempVec[i1]=vec[n-1]; for (int i=0; i<n; i++) { vec[i]=tempVec[i]; } return ; } float getArea(zArrayT<b2Vec2>& vs) { float area=0.0; float p1X=0.0, p1Y=0.0; float inv3=1.0/3.0; int n=vs.count(); for (int i=0; i < n; i++) { b2Vec2 p2=vs[i]; b2Vec2 p3=vs[i+1<n?i+1:0]; float e1X=p2.x-p1X; float e1Y=p2.y-p1Y; float e2X=p3.x-p1X; float e2Y=p3.y-p1Y; float D=(e1X * e2Y - e1Y * e2X); float triangleArea=0.5*D; area+=triangleArea; } return area; } JoySplitRayCastCallback::JoySplitRayCastCallback( b2World* pworld, b2Body* body/*=NULL*/ ): world(pworld),clickBody(body),destroyed(false) ,m_minx(0xffff),m_miny(0xffff),m_maxx(-0xffff),m_maxy(-0xffff) { if (body) { explodingBodies.push(body); b2Fixture* fixture=body->GetFixtureList(); while(fixture) { if (fixture->GetType()==b2Shape::e_polygon) { b2PolygonShape* poly=(b2PolygonShape*)fixture->GetShape(); int numVertices=poly->GetVertexCount(); b2Vec2 v; for(int i=0; i < poly->GetVertexCount(); i++) { v=poly->GetVertex(i); m_minx=MIN(m_minx, v.x); m_miny=MIN(m_miny, v.y); m_maxx=MAX(m_maxx, v.x); m_maxy=MAX(m_maxy, v.y); } } fixture=fixture->GetNext(); } } FloatCompareAccuracy(); } float32 sb2Cross(const b2Vec2& a, const b2Vec2& b) { return a.x * b.y - a.y * b.x; } void ShowVect(const char* name, zArrayT<b2Vec2> &vecs) { printf("%s\n",name); for (unsigned int i=0; i < vecs.count(); i++) { printf("(%f %f) ",vecs[i].x, vecs[i].y); } printf("\n"); } bool JoySplitRayCastCallback::CheckClockwise(zArrayT<b2Vec2> &vecs) { int cnt=vecs.count(); for (int32 i = 0; i < cnt; ++i) { int32 i1 = i; int32 i2 = i + 1 < cnt ? i + 1 : 0; b2Vec2 edge = vecs[i2] - vecs[i1]; if (edge.LengthSquared() < b2_epsilon * b2_epsilon) { return false; } if(!PointInBody(vecs[i])) { printf("illeage data (%f,%f)\n",vecs[i].x, vecs[i].y); return false; } for (int32 j = 0; j < cnt; ++j) { // Don't check vertices on the current edge. if (j == i1 || j == i2) { continue; } b2Vec2 r = vecs[j] - vecs[i1]; // If this crashes, your polygon is non-convex, has colinear edges, // or the winding order is wrong. float32 s = sb2Cross(edge, r); if (s<0) { return false; } } } return true; } void JoySplitRayCastCallback::splitObj( b2Body* sliceBody, b2Vec2& A, b2Vec2& B ) { if (clickBody==sliceBody && destroyed) { return; } b2Fixture* origFixture=sliceBody->GetFixtureList(); b2PolygonShape* poly=(b2PolygonShape*)origFixture->GetShape(); int numVertices=poly->GetVertexCount(); zArrayT<b2Vec2> shape1Vertices; zArrayT<b2Vec2> shape2Vertices; float d; A=sliceBody->GetLocalPoint(A); B=sliceBody->GetLocalPoint(B); shape1Vertices.push(A); shape1Vertices.push(B); shape2Vertices.push(A); shape2Vertices.push(B); for (int i=0; i < numVertices; i++) { b2Vec2 v=poly->GetVertex(i); d=det(A.x,A.y,B.x,B.y,v.x, v.y); if (d>0) { shape1Vertices.push(v); } else { shape2Vertices.push(v); } } //ShowVect("shap1", shape1Vertices); //ShowVect("shap2", shape2Vertices); arrangeClockwise(shape1Vertices); arrangeClockwise(shape2Vertices); // creating the first shape, if big enough if (getArea(shape1Vertices)>=0.05 && CheckClockwise(shape1Vertices)) { CreateBody( sliceBody, shape1Vertices); } // creating the second shape, if big enough if (getArea(shape2Vertices)>=0.05 && CheckClockwise(shape2Vertices)) { CreateBody( sliceBody, shape2Vertices); } destroyed=true; if (clickBody!=sliceBody) { world->DestroyBody(sliceBody); int idx=slicedBodies.find(sliceBody); if (idx!=-1) { slicedBodies.remove(idx); } } } void JoySplitRayCastCallback::resetCB() { enterPointsVec.setSize(0); enterPointsVecBody.setSize(0); } void JoySplitRayCastCallback::addPoint( b2Body* body, const b2Vec2& p ) { enterPointsVecBody.push(body); enterPointsVec.push(p); } void JoySplitRayCastCallback::removePoint( b2Body* body ) { int n=enterPointsVecBody.find(body); if (n>=0) { enterPointsVecBody.remove(n); enterPointsVec.remove(n); } } void JoySplitRayCastCallback::CreateBody( b2Body* sliceBody, zArrayT<b2Vec2> &vecs) { b2Fixture* origFixture=sliceBody->GetFixtureList(); b2BodyDef bodyDef; bodyDef.type=b2_dynamicBody; bodyDef.position=sliceBody->GetPosition(); b2FixtureDef fixtureDef; fixtureDef.density=origFixture->GetDensity(); fixtureDef.friction=origFixture->GetFriction(); fixtureDef.restitution=origFixture->GetRestitution(); b2PolygonShape polyShape; b2Body* body=NULL; polyShape.Set(vecs.getDataPtr(), vecs.count()); fixtureDef.shape=&polyShape; removePoint(body); body=world->CreateBody(&bodyDef); //body->SetAngularVelocity(sliceBody->GetAngle()); body->CreateFixture(&fixtureDef); // setting a velocity for the debris b2Vec2 v(1,1); body->SetLinearVelocity(v); // the shape will be also part of the explosion and can explode too explodingBodies.push(body); slicedBodies.push(body); return ; } bool JoySplitRayCastCallback::PointInBody( const b2Vec2& point ) { return (point.x<=m_maxx && point.x >=m_minx && point.y <=m_maxy && point.y>=m_miny); }
/* world: b2world对象 pos:受力点 body:被切分的box2d对象 csx:被切分的cocos2dx对象 */ void BodyExplosion(b2World *world, b2Vec2 &pos, b2Body* body, CCSprite* csx) { if (body==NULL) { return; } float cutAngle=0.0; b2Vec2 p1,p2; float scale=32; JoySplitRayCastCallback cb(world, body); int i; for (i=0; i<5; i++) { cutAngle=rand()*PI*2; p1.x=(pos.x + i/10.0 - 2000*cos(cutAngle))/scale; p1.y=(pos.y - 2000*sin(cutAngle))/scale; p2.x=(pos.x + 2000*cos(cutAngle))/scale; p2.y=(pos.y + 2000*sin(cutAngle))/scale; world->RayCast(&cb, p1, p2); world->RayCast(&cb, p2, p1); cb.resetCB(); } for(i=0; i<cb.slicedBodies.count(); i++) { b2Body* bd=cb.slicedBodies[i]; CCSprite* sp=CreateSprintByBody(bd, csx, scale); } } CCSprite* CreateSprintByBody(b2Body* body, CCSprite* csx, float metor) { b2Fixture* fixture=body->GetFixtureList(); b2PolygonShape* poly=(b2PolygonShape*)fixture->GetShape(); int numVertices=poly->GetVertexCount(); CCPoint points[10]; float scalex=csx->getScaleX(); float scaley=csx->getScaleY(); CCPoint ap=csx->getAnchorPoint(); unsigned long ow=csx->getTexture()->getContentSize().width; ow*=ap.x; unsigned long oh=csx->getTexture()->getContentSize().height; oh*=ap.y; for (int i=0; i<numVertices; i++) { b2Vec2 v=poly->GetVertex(i); points[i].x=v.x*metor/scalex+ow; points[i].y=v.y*metor/scaley+oh; } CCSprite* sp=CCSprite::spriteWithTexture(csx->getTexture()); sp->setPoly(numVertices, points); sp->setScaleX(scalex); sp->setScaleY(scaley); sp->setAnchorPoint(CCPointMake(0.5,0.5)); return sp; }
int m_polyVertexCount; ccV2F_C4F_T2F m_polyVertexData[10]; public: void setPoly(int count,CCPoint* vertex); void drawPoly();
4.2 源文件添加
void CCSprite::setPoly( int count,CCPoint* vertex ) { if (count<=0 || count>10) { m_polyVertexCount=0; return; } m_polyVertexCount=count; float w=getTexture()->getPixelsWide(); float h=getTexture()->getPixelsHigh(); float x,y; for (int i=0; i<count; i++) { x=vertex[i].x; y=vertex[i].y; m_polyVertexData[i].vertices.x = x; m_polyVertexData[i].vertices.y = y; m_polyVertexData[i].texCoords.u = x/w; m_polyVertexData[i].texCoords.v = y/h; m_polyVertexData[i].colors.r=255; m_polyVertexData[i].colors.g=255; m_polyVertexData[i].colors.b=255; m_polyVertexData[i].colors.a=255; } } void CCSprite::drawPoly() { CCNode::draw(); CCAssert(! m_bUsesBatchNode, ""); // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY // Unneeded states: - bool newBlend = m_sBlendFunc.src != CC_BLEND_SRC || m_sBlendFunc.dst != CC_BLEND_DST; if (newBlend) { glBlendFunc(m_sBlendFunc.src, m_sBlendFunc.dst); } /// ======================================================================== // Replaced [texture_ drawAtPoint:CGPointZero] with my own vertexData // Everything above me and below me is copied from CCTextureNode's draw if (m_pobTexture) { glBindTexture(GL_TEXTURE_2D, m_pobTexture->getName()); } else { glBindTexture(GL_TEXTURE_2D, 0); } glVertexPointer(2, GL_FLOAT, sizeof(ccV2F_C4F_T2F), &m_polyVertexData[0].vertices); glTexCoordPointer(2, GL_FLOAT, sizeof(ccV2F_C4F_T2F), &m_polyVertexData[0].texCoords); //glColorPointer(4, GL_FLOAT, sizeof(ccV2F_C4F_T2F), &m_polyVertexData[0].colors); glDrawArrays(GL_TRIANGLE_FAN, 0, m_polyVertexCount); }4.3 修改CCSprite类的draw函数
void CCSprite::draw(void) { CCNode::draw(); CCAssert(! m_bUsesBatchNode, ""); if(m_polyVertexCount!=0) { drawPoly(); return; }