Пример #1
0
        bool f2PolygontoPolygon(f2Manifold* m, f2PolygonShape* A, f2PolygonShape* B)
        {
            // 初始化接触点
            m->touchPointsNum = 0;

            // 最短深度(数值最大)的面序号
            uint32 faceA;
            // 通过a上的法向量寻找b上的支点
            float32 penetrationA = f2FindMaxSeparation(&faceA, A, B);
            if(penetrationA >= 0.0f)
                // 碰撞没有发生
                return false;

            uint32 faceB;
            float32 penetrationB = f2FindMaxSeparation(&faceB, B, A);
            if(penetrationB >= 0.0f)
                // 碰撞没有发生
                return false;

            // 碰撞检测完毕 之后生成碰撞的深度及其他信息
            uint32 referenceIndex;
            bool flip;

            // --!
            f2PolygonShape *referencePoly;
            f2PolygonShape *incidentPoly;

            // 决定哪个多边形包含了参照面(深度大的包含参考面) 深度较小(数值较大--小于0)的可以作为后期用来分离两个多边形
            // 这里的深度一定小于0 在面法线上的投影越长越小
            // A>B?提供一定容忍度
            //const float32 tolerance = 0.1f * F2_LINEARSLOP;
            //if(penetrationA > penetrationB + tolerance)
            if(f2Greater(penetrationA, penetrationB))
            {
                referencePoly = A;
                incidentPoly = B;
                referenceIndex = faceA;
                flip = false;
            }
            else
            {
                referencePoly = B;
                incidentPoly = A;
                referenceIndex = faceB;
                flip = true;
            }

            // 找到碰撞发生面
            f2Vector2f incidentFace[2];
            f2FindIncidentFace(incidentFace, referencePoly, incidentPoly, referenceIndex);

            // 参考面顶点
            f2Vector2f v1 = referencePoly->getVertice(referenceIndex);
            // 循环
            referenceIndex = referenceIndex + 1 == referencePoly->getVertexCount() ? 0 : referenceIndex + 1;
            f2Vector2f v2 = referencePoly->getVertice(referenceIndex);

            // 转换到参考多边形的世界坐标系中
            v1 = referencePoly->getOrient() * v1 + referencePoly->getPosition();
            v2 = referencePoly->getOrient() * v2 + referencePoly->getPosition();

            // 在世界坐标系中计算参照多边形的边向量
            f2Vector2f sidePlaneNormal = (v2 - v1);
            // 这里的normal表示后期计算时操作的是垂直于sidePlane的直线而其本身代表的还是边(v2 - v1)
            sidePlaneNormal.normalize();

            //得到边的法线向量(以当前向量方向为基准 顺时针旋转90度得到refNormal)
            f2Vector2f refFaceNormal(sidePlaneNormal.y, -sidePlaneNormal.x);

            // ax + by = c
            // 联立两点式直线方程
            // c = x2y1 - x1y2
            // a = y2 - y1
            // b = x1 - x2
            // 这条直线过v1 v2点
            float32 referenceC = refFaceNormal * v1;

            //           ->n       ^
            //      +---c ------positivePlane--clip
            //      | i |\
                    //      +---+ c-----negetivePlane--clip
            //             \       v
            //              r
            //
            //  r : 参考面
            //  i : 事件多边形
            //  c : 裁剪点
            //  n : 事件面法线

            // 裁剪线直线方程的c
            float32 negetiveC = -(sidePlaneNormal * v1);
            float32 positiveC = sidePlaneNormal * v2;

            // 在相对面上裁剪
            if(f2Clip(-sidePlaneNormal, negetiveC, incidentFace) < 2)
                return false;

            // 相向面裁剪
            if(f2Clip(sidePlaneNormal, positiveC, incidentFace) < 2)
                return false;

            // 是否翻转参考面面法线
            m->normal = flip ? -refFaceNormal : refFaceNormal;

            // 保持点在参考面后方
            uint32 contactNum = 0;
            // 这里得到的实际方程式为ax+by+c 也就是经过v1 v2点的直线 含义为d = ax+by+c尽管这里的d 还应该除以(a^2+b^2)^1/2不过猜测应该是为了提高计算速度有关系
            float32 separation = refFaceNormal * incidentFace[0] - referenceC;
            if(separation <= 0.0f)
            {
                m->touchPoints[contactNum] = incidentFace[0];
                // 深度为负值
                m->penetration = -separation;
                ++contactNum;
            }
            else
                m->penetration = 0;

            separation = refFaceNormal * incidentFace[1] - referenceC;
            if(separation <= 0.0f)
            {
                m->touchPoints[contactNum] = incidentFace[1];

                m->penetration += -separation;
                ++contactNum;

                // 平均两接触点的接触深度
                m->penetration /= (float32) contactNum;
            }

            m->touchPointsNum = contactNum;

            return true;
        }
Пример #2
0
void gridToPolygon(phys::Collision* c, phys::RigidBody *a, phys::RigidBody *b)
{
    c->addManifold();
    c->getLastManifold()->contactCount = 0;

    GridShape* grid = reinterpret_cast<GridShape*>(a->getShape());
    phys::PolygonShape* poly = reinterpret_cast<phys::PolygonShape*>(b->getShape());

    sf::Vector2f topLeft(FLT_MAX, FLT_MAX);
    sf::Vector2f botRight(-FLT_MAX, -FLT_MAX);

    for (unsigned int i = 0; i < poly->getVertices().size(); i++)
    {
        // Retrieve a face normal from A
        sf::Vector2f n = poly->getNormals()[i];
        sf::Vector2f nw = poly->getU() * n;

        // Transform face normal into B's model space
        phys::Mat2 buT = grid->getU().transpose( );
        n = buT * nw;

        // Retrieve vertex on face from A, transform into
        // B's model space
        sf::Vector2f v = poly->getVertices()[i];
        v = (poly->getU() * v) + b->getPosition();
        v -= a->getPosition();
        v = buT * v;

        poly->transformedVertices[i] = v;
        poly->transformedNormals[i] = n;

        if (v.x < topLeft.x)
            topLeft.x = v.x;
        if (v.y < topLeft.y)
            topLeft.y = v.y;
        if (v.x > botRight.x)
            botRight.x = v.x;
        if (v.y > botRight.y)
            botRight.y = v.y;
    }

    int left = floor(topLeft.x/(PTU/TILE_SIZE))-1;
    int top = floor(topLeft.y/(PTU/TILE_SIZE))-1;
    int right = ceil(botRight.x/(PTU/TILE_SIZE))+1;
    int bot = ceil(botRight.y/(PTU/TILE_SIZE))+1;

    if (!grid->getGrid()->mWrapX)
    {
        left = std::max(left, 0);
        right = std::min(right, grid->getGrid()->mSizeX-1);
    }
	top = std::max(top, 0);
    bot = std::min(bot, grid->getGrid()->mSizeY-1);

    // Tile size
    float tileSize = PTU/TILE_SIZE;

    // Find the tile with the least overlap in the collision
    sf::Vector2f tileVerts[4];
    int tileVertCount;
    float tilePenetration = 0.f;
    float polyPenetration = 0.f;
    int tileFace;
    int polyFace;

    sf::Vector2f tileNormals[4];

    sf::Vector2f usedNormals[20];
    int usedNormalCount = 0;

    for (int y = top; y <= bot; y++)
    {
        for (int _x = left; _x <= right; _x++)
        {
            int x = _x;
            if (grid->getGrid()->mWrapX)
                x = grid->getGrid()->wrapX(x);

            if (!grid->getGrid()->mTiles[y][x].isSolid())
                continue;

            int left = x-1;
            int right = x+1;
            int up = y-1;
            int down = y+1;

            if (grid->getGrid()->mWrapX)
            {
                left = grid->getGrid()->wrapX(left);
                right = grid->getGrid()->wrapX(right);
            }

            bool leftT = false;
            bool rightT = false;
            bool upT = false;
            bool downT = false;

            if (left >= 0 && grid->getGrid()->mTiles[y][left].isSolid())
                leftT = true;
            if (right < grid->getGrid()->mSizeX && grid->getGrid()->mTiles[y][right].isSolid())
                rightT = true;
            if (up >= 0 && grid->getGrid()->mTiles[up][x].isSolid())
                upT = true;
            if (down < grid->getGrid()->mSizeY && grid->getGrid()->mTiles[down][x].isSolid())
                downT = true;

            sf::Vector2f start(_x*tileSize, y*tileSize);

            if (!leftT && !rightT && !upT && downT)
            {
                tileVertCount = 3;
                tileVerts[0] = start + sf::Vector2f(tileSize/2, 0);
                tileVerts[1] = start + sf::Vector2f(0, tileSize);
                tileVerts[2] = start + sf::Vector2f(tileSize, tileSize);

                tileNormals[0] = normalize(sf::Vector2f(-0.5, -1.f));
                tileNormals[1] = sf::Vector2f(0, 1);
                tileNormals[2] = normalize(sf::Vector2f(0.5, -1.f));
            }
            else if (!leftT && rightT && !upT && downT)
            {
                tileVertCount = 3;
                tileVerts[0] = start + sf::Vector2f(tileSize, 0);
                tileVerts[1] = start + sf::Vector2f(0, tileSize);
                tileVerts[2] = start + sf::Vector2f(tileSize, tileSize);

                tileNormals[0] = normalize(sf::Vector2f(-1.f, -1.f));
                tileNormals[1] = sf::Vector2f(0, 1);
                tileNormals[2] = sf::Vector2f(1.f, 0.f);
            }
            else if (leftT && !rightT && !upT && downT)
            {
                tileVertCount = 3;
                tileVerts[0] = start;
                tileVerts[1] = start + sf::Vector2f(0, tileSize);
                tileVerts[2] = start + sf::Vector2f(tileSize, tileSize);

                tileNormals[0] = sf::Vector2f(-1.f, 0.f);
                tileNormals[1] = sf::Vector2f(0, 1);
                tileNormals[2] = normalize(sf::Vector2f(1.f, -1.f));
            }
            else
            {
                tileVertCount = 4;
                tileVerts[0] = start;
                tileVerts[1] = start + sf::Vector2f(0, tileSize);
                tileVerts[2] = start + sf::Vector2f(tileSize, tileSize);
                tileVerts[3] = start + sf::Vector2f(tileSize, 0);

                tileNormals[0] = sf::Vector2f(-1, 0);
                tileNormals[1] = sf::Vector2f(0, 1);
                tileNormals[2] = sf::Vector2f(1, 0);
                tileNormals[3] = sf::Vector2f(0, -1);
            }


            // Check for a separating axis with A's face planes
            int faceA;
            float penetrationA = findAxisLeastPenetration(&faceA, poly->transformedVertices.data(),
                                                          poly->transformedNormals.data(), poly->transformedVertices.size(), tileVerts, tileVertCount);
            if(penetrationA >= 0.0f || phys::equal(penetrationA, 0.f))
                continue;

            // Check for a separating axis with B's face planes
            int faceB;
            float penetrationB = findAxisLeastPenetration(&faceB, tileVerts, tileNormals, tileVertCount,
                                                          poly->transformedVertices.data(), poly->transformedVertices.size());
            if(penetrationB >= 0.0f || phys::equal(penetrationB, 0.f))
                continue;

            tilePenetration = penetrationB;
            polyPenetration = penetrationA;
            tileFace = faceB;
            polyFace = faceA;

            sf::Uint32 referenceIndex;
            bool flip; // Always point from a to b

            phys::RigidBody* ref;
            sf::Vector2f* refVerts;
            sf::Vector2f* refNormals;
            int refCount;
            phys::RigidBody* inc;
            sf::Vector2f* incVerts;
            sf::Vector2f* incNormals;
            int incCount;

            // Determine which shape contains reference face
            if(phys::biasGreaterThan( tilePenetration, polyPenetration ))
            {
                ref = a;
                refVerts = tileVerts;
                refNormals = tileNormals;
                refCount = tileVertCount;

                inc = b;
                incVerts = poly->transformedVertices.data();
                incNormals = poly->transformedNormals.data();
                incCount = poly->transformedVertices.size();

                referenceIndex = tileFace;
                flip = false;
            }
            else
            {
                ref = b;
                refVerts = poly->transformedVertices.data();
                refNormals = poly->transformedNormals.data();
                refCount = poly->transformedVertices.size();

                inc = a;
                incVerts = tileVerts;
                incNormals = tileNormals;
                incCount = tileVertCount;

                referenceIndex = polyFace;
                flip = true;
            }

            bool used = false;
            for (int i = 0; i < usedNormalCount; i++)
            {
                if (refNormals[referenceIndex] == usedNormals[i])
                {
                    used = true;
                    break;
                }
            }

            if (used)
                continue;

            usedNormals[usedNormalCount] = refNormals[referenceIndex];
            usedNormalCount++;

            // World space incident face
            sf::Vector2f incidentFace[2];
            findIncidentFace(incidentFace, refVerts, refNormals, refCount, incVerts, incNormals, incCount, referenceIndex);

            //        y
            //        ^  ->n       ^
            //      +---c ------posPlane--
            //  x < | i |\
            //      +---+ c-----negPlane--
            //             \       v
            //              r
            //
            //  r : reference face
            //  i : incident poly
            //  c : clipped point
            //  n : incident normal

            // Setup reference face vertices
            sf::Vector2f v1 = refVerts[referenceIndex];
            referenceIndex = referenceIndex + 1 == refCount ? 0 : referenceIndex + 1;
            sf::Vector2f v2 = refVerts[referenceIndex];

            // Calculate reference face side normal in world space
            sf::Vector2f sidePlaneNormal = (v2 - v1);
            sidePlaneNormal = phys::normalize(sidePlaneNormal);

            // Orthogonalize
            sf::Vector2f refFaceNormal( sidePlaneNormal.y, -sidePlaneNormal.x );

            // ax + by = c
            // c is distance from origin
            float refC = phys::dot( refFaceNormal, v1 );
            float negSide = -phys::dot( sidePlaneNormal, v1 );
            float posSide =  phys::dot( sidePlaneNormal, v2 );

            // Clip incident face to reference face side planes
            if(clip( -sidePlaneNormal, negSide, incidentFace ) < 2)
                return; // Due to floating point error, possible to not have required points

            if(clip(  sidePlaneNormal, posSide, incidentFace ) < 2)
                return; // Due to floating point error, possible to not have required points

            // Flip
            c->getLastManifold()->normal = flip ? -refFaceNormal : refFaceNormal;

            // Keep points behind reference face
            sf::Uint32 cp = 0; // clipped points behind reference face
            float separation = phys::dot( refFaceNormal, incidentFace[0] ) - refC;
            if(separation <= 0.0f)
            {
                c->getLastManifold()->contacts[cp] = incidentFace[0];
                c->getLastManifold()->penetration = -separation;
                ++cp;
            }
            else
                c->getLastManifold()->penetration = 0;

            separation = phys::dot( refFaceNormal, incidentFace[1] ) - refC;
            if(separation <= 0.0f)
            {
                c->getLastManifold()->contacts[cp] = incidentFace[1];

                c->getLastManifold()->penetration += -separation;
                ++cp;

                // Average penetration
                c->getLastManifold()->penetration /= (float)cp;
            }

            c->getLastManifold()->contactCount = cp;

            c->addManifold();
            c->getLastManifold()->contactCount = 0;
        }
    }
}
Пример #3
0
	void polygontoPolygon(Manifold* m, RigidBody* a, RigidBody* b) {
		Polygon* A = reinterpret_cast<Polygon *>(a->shape);
		Polygon* B = reinterpret_cast<Polygon *>(b->shape);
		m->contactCount = 0;

		unsigned int faceA;
		float penetrationA = findAxisLeastPenetration(&faceA, A, B);
		if (penetrationA >= 0.0f)
			return;

		unsigned int faceB;
		float penetrationB = findAxisLeastPenetration(&faceB, B, A);
		if (penetrationB >= 0.0f)
			return;

		unsigned int referenceIndex;
		bool flip; 

		Polygon* RefPoly; 
		Polygon* IncPoly; 

		if (BiasGreaterThan(penetrationA, penetrationB)) {
			RefPoly = A;
			IncPoly = B;
			referenceIndex = faceA;
			flip = false;
		} 
		else {
			RefPoly = B;
			IncPoly = A;
			referenceIndex = faceB;
			flip = true;
		}

		vec2 incidentFace[2];
		findIncidentFace(incidentFace, RefPoly, IncPoly, referenceIndex);

		vec2 v1 = RefPoly->m_vertices[referenceIndex];
		referenceIndex = referenceIndex + 1 == RefPoly->m_vertexCount ? 0 : referenceIndex + 1;
		vec2 v2 = RefPoly->m_vertices[referenceIndex];

		v1 = RefPoly->m_orientation * v1 + RefPoly->m_body->getPosition();
		v2 = RefPoly->m_orientation * v2 + RefPoly->m_body->getPosition();

		vec2 sidePlaneNormal = (v2 - v1);
		sidePlaneNormal.normalise();

		vec2 refFaceNormal(sidePlaneNormal.y, -sidePlaneNormal.x);

		float refC = dot(refFaceNormal, v1);
		float negSide = -dot(sidePlaneNormal, v1);
		float posSide = dot(sidePlaneNormal, v2);

		if (clip(-sidePlaneNormal, negSide, incidentFace) < 2)
			return;

		if (clip(sidePlaneNormal, posSide, incidentFace) < 2)
			return;

		m->normal = flip ? -refFaceNormal : refFaceNormal;

		unsigned int cp = 0; 
		float separation = dot(refFaceNormal, incidentFace[0]) - refC;
		if (separation <= 0.0f)	{
			m->contacts[cp] = incidentFace[0];
			m->penetration = -separation;
			++cp;
		}
		else
			m->penetration = 0;

		separation = dot(refFaceNormal, incidentFace[1]) - refC;
		if (separation <= 0.0f)	{
			m->contacts[cp] = incidentFace[1];

			m->penetration += -separation;
			++cp;

			m->penetration /= (float)cp;
		}

		m->contactCount = cp;
	}
Пример #4
0
		void Collider::PolygonToPolygon( Manifold *m, Body *a, Body *b )
		{
			PolygonShape *A=reinterpret_cast<PolygonShape *>(a->m_shape);
			PolygonShape *B=reinterpret_cast<PolygonShape *>(b->m_shape);
			m->m_contactCount=0;

			int faceA;
			float penetrationA=FindAxisLeastPenetration(&faceA,A,B);
			if(penetrationA>=0.0f)
			{
				return;
			}

			int faceB;
			float penetrationB=FindAxisLeastPenetration(&faceB,B,A);
			if(penetrationB>=0.0f)
			{
				return;
			}

			int referenceIndex;
			bool flip;

			PolygonShape *RefPoly;
			PolygonShape *IncPoly;

			if(MPACK::Math::Misc<float>::BiasGreaterThan(penetrationA,penetrationB))
			{
				RefPoly = A;
				IncPoly = B;
				referenceIndex = faceA;
				flip = false;
			}
			else
			{
				RefPoly = B;
				IncPoly = A;
				referenceIndex = faceB;
				flip = true;
			}

			Vector2f incidentFace[2];
			FindIncidentFace(incidentFace,RefPoly,IncPoly,referenceIndex);

			Vector2f v1=RefPoly->m_vertices[referenceIndex];
			referenceIndex=referenceIndex+1==RefPoly->m_vertexCount?0:referenceIndex+1;
			Vector2f v2=RefPoly->m_vertices[referenceIndex];

			v1 = RefPoly->u * v1 + RefPoly->body->m_position;
			v2 = RefPoly->u * v2 + RefPoly->body->m_position;

			Vector2f sidePlaneNormal=(v2-v1);
			sidePlaneNormal.Normalize();

			Vector2f refFaceNormal(sidePlaneNormal.y,-sidePlaneNormal.x);

			float refC=Dot(refFaceNormal,v1);
			float negSide=-Dot(sidePlaneNormal,v1);
			float posSide=Dot(sidePlaneNormal,v2);

			if(Clip(-sidePlaneNormal,negSide,incidentFace )<2)
			{
				return;
			}

			if(Clip(sidePlaneNormal,posSide,incidentFace )<2)
			{
				return;
			}

			m->m_normal=flip?-refFaceNormal:refFaceNormal;

			int contactPoints=0;
			float separation=Dot(refFaceNormal,incidentFace[0])-refC;
			if(separation<=0.0f)
			{
				m->m_contacts[contactPoints]=incidentFace[0];
				m->m_penetration=-separation;
				++contactPoints;
			}
			else
			{
				m->m_penetration=0;
			}

			separation=Dot(refFaceNormal,incidentFace[1])-refC;
			if(separation<=0.0f)
			{
				m->m_contacts[contactPoints]=incidentFace[1];

				m->m_penetration+=-separation;
				++contactPoints;

				m->m_penetration/=(float)contactPoints;
			}

			m->m_contactCount=contactPoints;
		}