void addStaticShapes(b2World& world) { ShapeFactory factory { constants::RENDER_SCALE }; // Add the nondestructible screen edges std::vector<b2Vec2> boundaryPoints = { b2Vec2{ 0.0f, 0.0f }, b2Vec2{ 0.0f, screenHeight }, b2Vec2{ screenWidth, screenHeight }, b2Vec2{ screenWidth, 0.0f } }; auto boundaryShape = factory.chain(boundaryPoints.data(), boundaryPoints.size()); b2BodyDef boundaryDef; b2Body* boundaryBody = world.CreateBody(&boundaryDef); auto boundaryFixture = boundaryBody->CreateFixture(boundaryShape.get(), 0.0f); auto filter = boundaryFixture->GetFilterData(); filter.categoryBits = Shape::normal; boundaryFixture->SetFilterData(filter); // Add a destructible polygon std::vector<b2Vec2> polygonPoints = { b2Vec2{ screenWidth * 0.1f, screenHeight * 0.4f }, b2Vec2{ screenWidth * 0.1f, screenHeight * 0.95f }, b2Vec2{ screenWidth * 0.9f, screenHeight * 0.95f }, b2Vec2{ screenWidth * 0.9f, screenHeight * 0.7f }, b2Vec2{ screenWidth * 0.4f, screenHeight * 0.4f } }; auto polygonShape = factory.chain(polygonPoints.data(), polygonPoints.size()); b2BodyDef polygonDef; b2Body* polygonBody = world.CreateBody(&polygonDef); auto polygonFixture = polygonBody->CreateFixture(polygonShape.get(), 0.0f); filter.categoryBits = Shape::destructible; polygonFixture->SetFilterData(filter); }
//////////////////////////////////////////////////////////////////////// /// /// @fn bool BonusModifierChangeZone::Revert() /// /// Reverts the effect on the node, /// return unsed for now /// /// /// @return bool /// //////////////////////////////////////////////////////////////////////// bool BonusModifierChangeZone::Revert() { #if WIN32 SoundFMOD::obtenirInstance()->playEffect(BONUS_CHANGE_SIDE_OUT_EFFECT); #endif #if BOX2D_PLAY for(int i=0; i<(int)mFixtures.size(); ++i) { auto fixture = mFixtures[i]; b2Filter filter = fixture->GetFilterData(); // adds the wall as categories that can block the node filter.maskBits |= CATEGORY_MIDLANE; fixture->SetFilterData(filter); } #endif NoeudMaillet* maillet = (NoeudMaillet*)mOwner; if(maillet) { auto pos = maillet->getPosition(); auto initialPos = maillet->getOriginalPosition(); // verification si le maillet n'est plus dans sa zone if(pos[VX]*initialPos[VX] <= 0) { maillet->setPosition(initialPos); } } return true; }
void processRemoval(b2Vec2 removalPosition, float removalRadius, b2World& world, bool simplifyGeometry) { auto foundBodies = queryDestructibleBodies(removalPosition, removalRadius, world); auto batch = matchBodiesToRings(foundBodies.begin(), foundBodies.end()); // Partition the shapes by area, so that elements to be processed are at the beginning auto borderIt = std::partition(batch.begin(), batch.end(), [](const match_t& m) { const double areaEpsilon = 0.02; return bg::area(m.second) > areaEpsilon; }); // Remove small shapes std::for_each(borderIt, batch.end(), [&](const match_t& m) { world.DestroyBody(m.first); }); // Subtract the input polygon from each shape returned from the query ring_t diff = makeConvexRing(removalPosition, removalRadius, 16); boost::geometry::correct(diff); typedef std::pair<std::unique_ptr<b2ChainShape>, b2Filter> shape_property_t; std::vector<shape_property_t> resultShapes; std::for_each(batch.begin(), borderIt, [&](const match_t& m) { auto subtractionResult = subtract(m.second, diff); // Simplify the results if (simplifyGeometry) { simplify(subtractionResult); } // Convert the rings to b2ChainShapes and add to result shapes auto converted = convertGeometry(subtractionResult); auto moveBegin = std::make_move_iterator(converted.begin()); auto moveEnd = std::make_move_iterator(converted.end()); std::transform(moveBegin, moveEnd, std::back_inserter(resultShapes), [&](std::unique_ptr<b2ChainShape> converted) { auto filter = m.first->GetFixtureList()->GetFilterData(); return std::make_pair(std::move(converted), filter); }); if (!subtractionResult.empty()) { world.DestroyBody(m.first); } }); for (auto&& s : resultShapes) { b2BodyDef bd; b2Body* body = world.CreateBody(&bd); auto fixture = body->CreateFixture(s.first.get(), 0.0f); fixture->SetFilterData(s.second); } }
//////////////////////////////////////////////////////////////////////// /// /// @fn bool BonusModifierChangeZone::Apply() /// /// Applies the real effect on the node /// returns false if the bonus finished the execution (no time to live) /// in case it returns false, complete its execution /// /// /// @return bool /// //////////////////////////////////////////////////////////////////////// bool BonusModifierChangeZone::Apply() { if(mOwner) { auto existingModifiers = mOwner->GetModifiers(); // recherche pour un bonus déjà présent for(auto it = existingModifiers.begin(); it != existingModifiers.end(); ++it) { BonusModifierChangeZone* modifier = dynamic_cast<BonusModifierChangeZone*>(*it); if(modifier && modifier != this) { // resets time to live on the modifier modifier->mTimeToLive = mTimeToLive; /// return false to indicate that the bonus was not added to the node, even though the /// bonus itself was applied. return false; } } #if BOX2D_PLAY auto body = mOwner->getPhysicBody(); if(body) { for(auto fixture = body->GetFixtureList(); fixture; fixture = fixture->GetNext()) { b2Filter filter = fixture->GetFilterData(); if(filter.maskBits & CATEGORY_MIDLANE) { // removes the wall as categorie that can block the node filter.maskBits &= ~CATEGORY_MIDLANE; fixture->SetFilterData(filter); mFixtures.push_back(fixture); } } } #endif } // if any fixtures were found, it means that this node had a modifiation applied return mFixtures.size() != 0; }
int main() { sf::Font font; sf::Text overlayText; const std::string fontFile = "am.ttf"; if (font.loadFromFile(fontFile)) { overlayText.setFont(font); } else { std::cerr << "Could not find " << fontFile << "\n"; return 1; } overlayText.setCharacterSize(10); overlayText.setString("Hold left mouse button to modify\nRight mouse button to add objects"); sf::VideoMode videoMode{ static_cast<unsigned int>(screenWidth), static_cast<unsigned int>(screenHeight) }; sf::ContextSettings settings; settings.antialiasingLevel = 8; sf::RenderWindow window(videoMode, "Box2D modifiable geometry", sf::Style::Default, settings); std::unique_ptr<b2World> physicsWorld{ new b2World{ b2Vec2{ 0.0f, 18.0f } } }; physicsWorld->SetAutoClearForces(false); physicsWorld->SetContactListener(nullptr); DebugDraw debugDraw(&window, physicsWorld.get()); addStaticShapes(*physicsWorld); sf::Clock clock; bool simplifyGeometry = true; while (window.isOpen()) { float elapsed = clock.restart().asSeconds(); sf::Event event; auto mousePos = sf::Mouse::getPosition(window); auto worldPos = window.mapPixelToCoords(mousePos); const float removalRadius = 25.f; b2Vec2 position = { worldPos.x, worldPos.y }; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) { window.close(); } if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Button::Right) { ShapeFactory factory{ constants::RENDER_SCALE }; auto ballShape = factory.circle(position, removalRadius / 2.f); b2BodyDef ballDef; b2Body* ballBody = physicsWorld->CreateBody(&ballDef); auto ballFixture = ballBody->CreateFixture(ballShape.get(), 0.0f); ballBody->SetType(b2BodyType::b2_dynamicBody); auto filter = ballFixture->GetFilterData(); filter.categoryBits = Shape::normal; filter.maskBits = Shape::normal | Shape::destructible; ballFixture->SetFilterData(filter); } if (event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::S) { simplifyGeometry = !simplifyGeometry; } } window.clear(); step(*physicsWorld, elapsed); b2Vec2 scaledPos = position; scaledPos *= (1.f / constants::RENDER_SCALE); float scaledRadius = removalRadius / constants::RENDER_SCALE; if (sf::Mouse::isButtonPressed(sf::Mouse::Button::Left)) { processRemoval(scaledPos, scaledRadius, *physicsWorld, simplifyGeometry); drawMouseIndicator(worldPos, removalRadius, window); } physicsWorld->DrawDebugData(); window.draw(overlayText); window.display(); } return 0; }