Exemplo n.º 1
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;
	}
Exemplo n.º 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;
        }
    }
}