Пример #1
0
void Labels::update(const View& _view, float _dt, const std::vector<std::unique_ptr<Style>>& _styles,
                    const std::vector<std::shared_ptr<Tile>>& _tiles) {

    m_needUpdate = false;

    // float zoom = _view.getZoom();
    // int lodDiscard = LODDiscardFunc(View::s_maxZoom, zoom);
    // logMsg("loddiscard %f %d\n", zoom, lodDiscard);

    std::set<std::pair<Label*, Label*>> occlusions;

    // Could clear this at end of function unless debug draw is active
    m_labels.clear();
    m_aabbs.clear();

    glm::vec2 screenSize = glm::vec2(_view.getWidth(), _view.getHeight());

    //// Collect labels from visible tiles

    for (const auto& tile : _tiles) {

        // discard based on level of detail
        // if ((zoom - tile->getID().z) > lodDiscard) {
        //     logMsg("discard %d %d %d\n", tile->getID().z, tile->getID().x, tile->getID().y);
        //     continue;
        // }

        glm::mat4 mvp = _view.getViewProjectionMatrix() * tile->getModelMatrix();

        for (const auto& style : _styles) {
            const auto& mesh = tile->getMesh(*style);
            if (!mesh) { continue; }

            const LabelMesh* labelMesh = dynamic_cast<const LabelMesh*>(mesh.get());
            if (!labelMesh) { continue; }

            for (auto& label : labelMesh->getLabels()) {
                m_needUpdate |= label->update(mvp, screenSize, _dt);

                if (label->canOcclude()) {
                    m_aabbs.push_back(label->getAABB());
                    m_aabbs.back().m_userData = (void*)label.get();
                }
                m_labels.push_back(label.get());
            }
        }
    }

    //// Manage occlusions

    // broad phase
    auto pairs = intersect(m_aabbs, {4, 4}, {_view.getWidth(), _view.getHeight()});

    for (auto pair : pairs) {
        const auto& aabb1 = m_aabbs[pair.first];
        const auto& aabb2 = m_aabbs[pair.second];

        auto l1 = (Label*)aabb1.m_userData;
        auto l2 = (Label*)aabb2.m_userData;

        // narrow phase
        if (intersect(l1->getOBB(), l2->getOBB())) { occlusions.insert({l1, l2}); }
    }

    for (auto& pair : occlusions) {
        if (!pair.first->occludedLastFrame() || !pair.second->occludedLastFrame()) {
            // lower numeric priority means higher priority
            if (pair.first->getOptions().priority < pair.second->getOptions().priority) {
                pair.second->setOcclusion(true);
            } else {
                pair.first->setOcclusion(true);
            }
        }
    }

    //// Update label meshes

    for (auto label : m_labels) {
        label->occlusionSolved();
        label->pushTransform();
    }

    // Request for render if labels are in fading in/out states
    if (m_needUpdate) {
        requestRender();
    }
}
Пример #2
0
MTV Collision::getCollision(const sf::Sprite& object1,Entity::ENTITY_SHAPE shape1,const sf::Sprite& object2,Entity::ENTITY_SHAPE shape2){
	//Use Separating Axis Theorem to determine whether two objects are overlapping
	//See documentation for full explanation of this algorithm

	//Get the oriented bounding box in world coordinates (includes rotation) of objects
	OBB obb1 = getOBB(object1);
	OBB obb2 = getOBB(object2);
	float rot = object1.getRotation();

	double overlap = LONG_MAX;
	maths::Vector2 smallest(0,0);

	std::vector<maths::Vector2> axis1;
	std::vector<maths::Vector2> axis2;

	maths::Vector2 circleCentre1;
	maths::Vector2 circleCentre2;

	sf::FloatRect gbounds1 = object1.getGlobalBounds();
	sf::FloatRect gbounds2 = object2.getGlobalBounds();
	 
	//Find all the axes to check using SAT based on shapes of objects
	//Works with squares/rectangles or circles
	//Rectangles only need 2 axes because they have 2 sets of parallel lines
	if(shape1 == Entity::SHAPE_CIRCLE && shape2 == Entity::SHAPE_CIRCLE){
		//If both shapes are circles, the only axis needed is the axis between centres of circles
		circleCentre1 = maths::Vector2(gbounds1.left + gbounds1.width / 2, gbounds1.top + gbounds1.height / 2);
		circleCentre2 = maths::Vector2(gbounds2.left + gbounds2.width / 2, gbounds2.top + gbounds2.height / 2);
		axis1.push_back(maths::Vector2(circleCentre1 - circleCentre2).normalise());

	}else if(shape1 != shape2){ //if one shape is circle and one shape is rectangle
		maths::Vector2 circleCentre;
		sf::FloatRect squareRect;
		float rotation;

		//First get the unrotated bounding box and centre of the circle of the 2 objects
		if(shape1 == Entity::SHAPE_CIRCLE){
			circleCentre = maths::Vector2(gbounds1.left + gbounds1.width / 2, gbounds1.top + gbounds1.height / 2);
			circleCentre1 = circleCentre;
			squareRect = getOriginalBoundingBox(object2);
			rotation = object2.getRotation();
		}else if(shape2 == Entity::SHAPE_CIRCLE){
			circleCentre = maths::Vector2(gbounds2.left + gbounds2.width / 2, gbounds2.top + gbounds2.height / 2);
			circleCentre2 = circleCentre;
			squareRect = getOriginalBoundingBox(object1);
			rotation = object1.getRotation();
		}

		maths::Vector2 squareCentre(squareRect.left + squareRect.width / 2, squareRect.top + squareRect.height / 2);
		OBB* square = (shape1 == Entity::SHAPE_SQUARE ? &obb1 : &obb2);
		maths::Vector2 relativeCircleCentre = circleCentre.rotate(-rotation, squareCentre); //get circle centre in relation to the rotated square
		bool vertice = false;
		maths::Vector2 axis;

		maths::Vector2 topLeft(squareRect.left, squareRect.top);
		maths::Vector2 topRight(squareRect.left + squareRect.width, squareRect.top);
		maths::Vector2 botLeft(squareRect.left, squareRect.top + squareRect.height);
		maths::Vector2 botRight(squareRect.left + squareRect.width, squareRect.top + squareRect.height);

		//Get the closest vertex of the rectangle to the circle centre.
		//The axis to check is the vector between these 2 points.
		if(circleCentre.x < topLeft.x){
			if(circleCentre.y < topLeft.y){
				vertice = true;
				axis = topLeft;
			}else if(circleCentre.y >  botLeft.y){
				vertice = true;
				axis = botLeft;
			}else{
				axis = maths::Vector2(topLeft - botLeft).normalise();
			}
		}else if(circleCentre.x > topRight.x){
			if(circleCentre.y < topLeft.y){
				vertice = true;
				axis = topRight;
			}else if(circleCentre.y >  botLeft.y){
				vertice = true;
				axis = botRight;
			}else{
				axis = maths::Vector2(topRight - botRight).normalise();
			}
		}else{
			if(circleCentre.y < topLeft.y){
				axis = maths::Vector2(topRight - topLeft).normalise();
			}else if(circleCentre.y >  botLeft.y){
				axis = maths::Vector2(botLeft - botRight).normalise();
			}else{
				//contains point!
			}
		}

		if(vertice){
			axis1.push_back(maths::Vector2(circleCentre - axis).normalise());
		}else{
			axis1.push_back(maths::Vector2(topRight - topLeft).normalise());
			axis1.push_back(maths::Vector2(topRight - botRight).normalise());
		}
	}else{ //If both shapes are rectangles
		//Get vectors for sides of shapes
		maths::Vector2 Xside1(obb1.bot_left - obb1.bot_right);
		maths::Vector2 Yside1(obb1.top_left - obb1.bot_left);
		maths::Vector2 Xside2(obb2.bot_left - obb2.bot_right);
		maths::Vector2 Yside2(obb2.top_left - obb2.bot_left);

		//Axes requires are perpendicular to the sides of the shape
		//Vector2.perpendicular() normalises for greater accuracy
		axis1.push_back(Xside1.perpendicular());
		axis1.push_back(Yside1.perpendicular());
		axis2.push_back(Xside2.perpendicular());
		axis2.push_back(Yside2.perpendicular());
	}

	//We have all the axes to check.
	//Now find details on collisions with projections.

	for(int i=0;i<axis1.size();i++){
		//Get projection of axis for both shapes
		maths::Vector2 axis = axis1[i];
		Projection projection1 = project(obb1, axis);
		if(shape1 == Entity::SHAPE_CIRCLE){
			float radius = gbounds1.width / 2;
			projection1 = projectCircle(circleCentre1, radius, axis);
		}
		Projection projection2 = project(obb2, axis);
		if (shape2 == Entity::SHAPE_CIRCLE){
			float radius = gbounds2.width / 2;
			projection2 = projectCircle(circleCentre2, radius, axis);
		}

		//If a projection does not overlap, we know the objects do not collide so we can exit the function.
		//Otherwise, the MTV (minimum translation vector required to make the objects not collide) is calculated.

		if(!projection1.overlap(projection2)){
			return MTV::NONE;
		}else{
			//The axis with the smallest overlap is the axis used to calculate MTV, so record it.
			double o = projection1.getOverlap(projection2);
			if(o < overlap){
				overlap = o; //set smallest overlap
				smallest = axis; //set smallest separation vector
			}
		}
	}
	
	//Repeat the same process as above with the other set of axes.
	for(int i=0;i<axis2.size();i++){
		maths::Vector2 axis = axis2[i];
		Projection projection1 = project(obb1, axis);
		if(shape1 == Entity::SHAPE_CIRCLE){
			float radius = gbounds1.width/2;
			projection1 = projectCircle(circleCentre1, radius, axis);
		}
		Projection projection2 = project(obb2,axis);
		if(shape2 == Entity::SHAPE_CIRCLE){
			float radius = gbounds2.width / 2;
			projection2 = projectCircle(circleCentre2, radius, axis);
		}

		if(!projection1.overlap(projection2)){
			return MTV::NONE;
		}else{
			double o = projection1.getOverlap(projection2);
			if(o < overlap){
				overlap = o;
				smallest = axis;
			}
		}
	}
	//Get the vector from the centre of object 2 to the centre of object 1
	maths::Vector2 centre1 = maths::Vector2(gbounds1.left + gbounds1.width / 2, gbounds1.top + gbounds1.height / 2);
	maths::Vector2 centre2 = maths::Vector2(gbounds2.left + gbounds2.width / 2, gbounds2.top + gbounds2.height / 2);
	maths::Vector2 between = centre1 - centre2;
	//If the separation vector is in the opposite direction of 'between', flip it round by negating it
	if(between.dot(smallest) < 0){
		smallest = -smallest;
	}
	MTV mtv(overlap, smallest);
	return mtv;
}