feat: BatchRenderer for optimized ball rendering

This commit is contained in:
Florian Sylvain
2025-05-11 05:19:18 +02:00
parent f81eb7e810
commit d1c4f36963
7 changed files with 128 additions and 11 deletions
+2
View File
@@ -28,4 +28,6 @@ class Ball : public PhysicalObject {
sf::Vector2f getVelocity() const; sf::Vector2f getVelocity() const;
bool isAtRest() const; bool isAtRest() const;
sf::Vector2f getPixelVelocity() const { return m_pixelVelocity; } sf::Vector2f getPixelVelocity() const { return m_pixelVelocity; }
float getRadius() const { return m_radius; }
sf::Color getColor() const { return m_shape.getFillColor(); }
}; };
+85
View File
@@ -0,0 +1,85 @@
#include "BatchRenderer.hpp"
#include <SFML/Graphics/Image.hpp>
#include <SFML/OpenGL.hpp>
#include <cmath>
BatchRenderer::BatchRenderer() : m_vertices(sf::PrimitiveType::Triangles) {
const unsigned int size = 64;
sf::Image image(sf::Vector2u(size, size), sf::Color::Transparent);
unsigned int radius = size / 2;
sf::Vector2u center(radius, radius);
for (unsigned int y = 0; y < size; ++y) {
for (unsigned int x = 0; x < size; ++x) {
sf::Vector2u pixel(x, y);
int dx = static_cast<int>(center.x) - static_cast<int>(pixel.x);
int dy = static_cast<int>(center.y) - static_cast<int>(pixel.y);
float distance = std::sqrt(dx * dx + dy * dy);
if (distance <= radius) {
float alpha = 255.0f;
if (distance > radius - 2.0f) {
alpha =
255.0f * (1.0f - (distance - (radius - 2.0f)) / 2.0f);
}
image.setPixel(
sf::Vector2u(x, y),
sf::Color(255, 255, 255, static_cast<std::uint8_t>(alpha)));
}
}
}
if (!m_circleTexture.loadFromImage(image)) {
throw std::runtime_error("Failed to load circle texture from image");
}
}
void BatchRenderer::clear() { m_vertices.clear(); }
void BatchRenderer::addBall(const Ball& ball) {
sf::Vector2f position = ball.getPosition();
float radius = ball.getRadius();
sf::Color color = ball.getColor();
sf::Vertex topLeft;
sf::Vertex topRight;
sf::Vertex bottomRight;
sf::Vertex bottomLeft;
topLeft.position = sf::Vector2f(position.x - radius, position.y - radius);
topRight.position = sf::Vector2f(position.x + radius, position.y - radius);
bottomRight.position =
sf::Vector2f(position.x + radius, position.y + radius);
bottomLeft.position =
sf::Vector2f(position.x - radius, position.y + radius);
topLeft.texCoords = sf::Vector2f(0, 0);
topRight.texCoords = sf::Vector2f(m_circleTexture.getSize().x, 0);
bottomRight.texCoords =
sf::Vector2f(m_circleTexture.getSize().x, m_circleTexture.getSize().y);
bottomLeft.texCoords = sf::Vector2f(0, m_circleTexture.getSize().y);
topLeft.color = color;
topRight.color = color;
bottomRight.color = color;
bottomLeft.color = color;
m_vertices.append(topLeft);
m_vertices.append(topRight);
m_vertices.append(bottomRight);
m_vertices.append(topLeft);
m_vertices.append(bottomRight);
m_vertices.append(bottomLeft);
}
void BatchRenderer::draw(sf::RenderWindow& window) {
if (m_vertices.getVertexCount() == 0) return;
sf::RenderStates states;
states.texture = &m_circleTexture;
window.draw(m_vertices, states);
}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/VertexArray.hpp>
#include <vector>
#include "Ball.hpp"
class BatchRenderer {
private:
sf::VertexArray m_vertices;
sf::Texture m_circleTexture;
public:
BatchRenderer();
void clear();
void addBall(const Ball& ball);
void draw(sf::RenderWindow& window);
};
+6 -6
View File
@@ -1,13 +1,13 @@
#pragma once #pragma once
namespace Constants { namespace Constants {
constexpr unsigned WIDTH = 1280; constexpr unsigned WIDTH = 1920;
constexpr unsigned HEIGHT = 720; constexpr unsigned HEIGHT = 1080;
constexpr float GRAVITY = 8000.f; constexpr float GRAVITY = 781.f;
constexpr float RESTITUTION = 0.8f; constexpr float RESTITUTION = 0.8f;
constexpr float FRICTION = 0.9f; constexpr float FRICTION = 0.9f;
constexpr float BALL_RADIUS = 20.f; constexpr float IMPULSE = 2000.f;
constexpr float IMPULSE = 2500.f;
constexpr float REST_PIXEL_VELOCITY = 2.0f; constexpr float REST_PIXEL_VELOCITY = 2.0f;
constexpr unsigned BALL_QUANTITY = 100; constexpr float BALL_RADIUS = 15.f;
constexpr unsigned BALL_QUANTITY = 500;
} // namespace Constants } // namespace Constants
+1 -1
View File
@@ -4,7 +4,7 @@
DebugOverlay::DebugOverlay(const std::string& fontPath) DebugOverlay::DebugOverlay(const std::string& fontPath)
: m_font(fontPath), m_text(m_font) { : m_font(fontPath), m_text(m_font) {
m_text.setCharacterSize(16); m_text.setCharacterSize(18);
m_text.setFillColor(sf::Color::White); m_text.setFillColor(sf::Color::White);
m_text.setPosition(sf::Vector2f(5.f, 5.f)); m_text.setPosition(sf::Vector2f(5.f, 5.f));
} }
+9
View File
@@ -126,9 +126,15 @@ void Game::render() {
m_debugLines.clear(); m_debugLines.clear();
m_drawCallCount = 0; m_drawCallCount = 0;
m_batchRenderer.clear();
for (const auto &object : m_objects) { for (const auto &object : m_objects) {
if (auto *ball = dynamic_cast<Ball *>(object.get())) {
m_batchRenderer.addBall(*ball);
} else {
object->draw(m_window); object->draw(m_window);
++m_drawCallCount; ++m_drawCallCount;
}
if (!m_toggleDebug) continue; if (!m_toggleDebug) continue;
if (auto *ball = dynamic_cast<Ball *>(object.get())) { if (auto *ball = dynamic_cast<Ball *>(object.get())) {
@@ -137,6 +143,9 @@ void Game::render() {
} }
} }
m_batchRenderer.draw(m_window);
++m_drawCallCount;
if (m_toggleDebug) { if (m_toggleDebug) {
DebugDraw::drawBatchedLines(m_window, m_debugLines); DebugDraw::drawBatchedLines(m_window, m_debugLines);
m_debugOverlay.update(m_drawCallCount + 2, m_timeScale, m_window); m_debugOverlay.update(m_drawCallCount + 2, m_timeScale, m_window);
+4 -2
View File
@@ -11,6 +11,7 @@
#include <vector> #include <vector>
#include "Ball.hpp" #include "Ball.hpp"
#include "BatchRenderer.hpp"
#include "DebugOverlay.hpp" #include "DebugOverlay.hpp"
#include "InputManager.hpp" #include "InputManager.hpp"
#include "PhysicalObject.hpp" #include "PhysicalObject.hpp"
@@ -20,7 +21,7 @@ class PhysicalObject;
class Game { class Game {
private: private:
float m_timeScale = 1.0f; float m_timeScale = 1.f;
sf::RenderWindow m_window; sf::RenderWindow m_window;
sf::Vector2f m_windowSize; sf::Vector2f m_windowSize;
std::vector<std::unique_ptr<PhysicalObject>> m_objects; std::vector<std::unique_ptr<PhysicalObject>> m_objects;
@@ -29,7 +30,8 @@ class Game {
sf::VertexArray m_debugLines; sf::VertexArray m_debugLines;
int m_drawCallCount = 0; int m_drawCallCount = 0;
DebugOverlay m_debugOverlay; DebugOverlay m_debugOverlay;
bool m_toggleDebug = false; bool m_toggleDebug = true;
BatchRenderer m_batchRenderer;
void processKeyPressed(const sf::Event::KeyPressed& keyPressed); void processKeyPressed(const sf::Event::KeyPressed& keyPressed);
void processMousePressed(const sf::Event::MouseButtonPressed& mousePressed); void processMousePressed(const sf::Event::MouseButtonPressed& mousePressed);