Add SpriteBatch
@ -26,6 +26,7 @@ if(SFML_BUILD_GRAPHICS)
|
||||
add_subdirectory(shader)
|
||||
add_subdirectory(island)
|
||||
add_subdirectory(vulkan)
|
||||
add_subdirectory(sprite_batch)
|
||||
endif()
|
||||
|
||||
if(SFML_OS_WINDOWS)
|
||||
|
435
examples/sprite_batch/Batching.cpp
Normal file
@ -0,0 +1,435 @@
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Headers
|
||||
////////////////////////////////////////////////////////////
|
||||
#include <SFML/Audio.hpp>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#ifdef SFML_SYSTEM_IOS
|
||||
#include <SFML/Main.hpp>
|
||||
#endif
|
||||
|
||||
std::filesystem::path resourcesDir()
|
||||
{
|
||||
#ifdef SFML_SYSTEM_IOS
|
||||
return "";
|
||||
#else
|
||||
return "resources";
|
||||
#endif
|
||||
}
|
||||
|
||||
enum class BatchDrawables
|
||||
{
|
||||
Sprite,
|
||||
Text,
|
||||
Shape,
|
||||
ShapeNoTexture
|
||||
};
|
||||
|
||||
std::string getRandomString(std::mt19937& mt)
|
||||
{
|
||||
static const std::array<std::string, 7> texts =
|
||||
{"",
|
||||
"Hello World!",
|
||||
"London is the capital \nof Great Britain.",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ut augue in dui \n"
|
||||
"posuere sodales nec a orci. Phasellus sit amet tellus lobortis, scelerisque \n"
|
||||
"dui dictum, posuere risus. Nam pellentesque gravida elit, id convallis tortor \n"
|
||||
"aliquet sed. Duis vitae cursus risus, fermentum condimentum ex. Proin mattis \n"
|
||||
"orci a malesuada commodo. Nunc egestas erat non volutpat elementum. Vivamus \n"
|
||||
"pulvinar tortor eleifend libero convallis, at volutpat turpis lobortis. \n",
|
||||
"'Tis but a flesh wound.",
|
||||
"SFML Test Demo Thingy Stuff (Epic)",
|
||||
"?!?!?!111(one)"};
|
||||
|
||||
return texts[std::uniform_int_distribution<std::size_t>(0, texts.size() - 1)(mt)];
|
||||
}
|
||||
|
||||
std::string getDisplayString(sf::SpriteBatch::BatchMode batchMode)
|
||||
{
|
||||
static const std::map<sf::SpriteBatch::BatchMode, std::string> modeDisplay =
|
||||
{{sf::SpriteBatch::BatchMode::Deferred, "Deferred"},
|
||||
{sf::SpriteBatch::BatchMode::TextureSort, "TextureSort"},
|
||||
{sf::SpriteBatch::BatchMode::DepthSort, "DepthSort"}};
|
||||
|
||||
if (modeDisplay.find(batchMode) != modeDisplay.end())
|
||||
return modeDisplay.at(batchMode);
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
std::string getDisplayString(BatchDrawables drawableMode)
|
||||
{
|
||||
static const std::map<BatchDrawables, std::string> modeDisplay =
|
||||
{{BatchDrawables::Sprite, "Sprite"},
|
||||
{BatchDrawables::Text, "Text"},
|
||||
{BatchDrawables::Shape, "Shape"},
|
||||
{BatchDrawables::ShapeNoTexture, "Shape (No Texture)"}};
|
||||
|
||||
if (modeDisplay.find(drawableMode) != modeDisplay.end())
|
||||
return modeDisplay.at(drawableMode);
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// Entry point of application
|
||||
///
|
||||
/// \return Application exit code
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
int main()
|
||||
{
|
||||
std::random_device rd;
|
||||
std::mt19937 mt(rd());
|
||||
|
||||
// Define some constants used within the demo
|
||||
const int windowWidth = 1600;
|
||||
const int windowHeight = 900;
|
||||
const int sameTextureChance = 85;
|
||||
const std::size_t drawableCountChange = 100;
|
||||
const std::size_t maxTimesSaved = 60;
|
||||
const std::size_t drawablesPerLayer = 250;
|
||||
|
||||
// Create the window of the application
|
||||
sf::VideoMode videMode({windowWidth, windowHeight}, 32);
|
||||
sf::RenderWindow window(videMode, "SFML SpriteBatch", sf::Style::Titlebar | sf::Style::Close);
|
||||
window.setVerticalSyncEnabled(true);
|
||||
|
||||
// Load texture assets
|
||||
std::vector<sf::Texture> textures;
|
||||
sf::Texture tempTexture;
|
||||
|
||||
int currentTex = 0;
|
||||
while (tempTexture.loadFromFile(resourcesDir() / ("Tex" + std::to_string(currentTex) + ".png")))
|
||||
{
|
||||
textures.push_back(std::move(tempTexture));
|
||||
tempTexture = sf::Texture();
|
||||
++currentTex;
|
||||
}
|
||||
|
||||
// Load font for debug text
|
||||
sf::Font font;
|
||||
if (!font.loadFromFile(resourcesDir() / "tuffy.ttf"))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
// Different types of drawables
|
||||
// For simplicity, we keep N objects of each type
|
||||
std::vector<sf::Sprite> sprites;
|
||||
std::vector<sf::CircleShape> shapes;
|
||||
std::vector<sf::CircleShape> shapesNoTexture;
|
||||
std::vector<sf::Text> texts;
|
||||
|
||||
// Text used for debug info
|
||||
sf::Text debugInfoText("", font);
|
||||
debugInfoText.setOutlineColor(sf::Color::Blue);
|
||||
debugInfoText.setOutlineThickness(4);
|
||||
debugInfoText.setFillColor(sf::Color::White);
|
||||
|
||||
// Define some state used in the demo
|
||||
std::size_t drawableCount = 100;
|
||||
sf::SpriteBatch batch;
|
||||
sf::SpriteBatch::BatchMode batchMode = sf::SpriteBatch::BatchMode::Deferred;
|
||||
bool batchEnabled = false;
|
||||
bool useStaticBatch = false;
|
||||
std::vector<sf::Time> timeHistory;
|
||||
sf::Time timeReference;
|
||||
std::size_t framesTillRefUpdate = 0;
|
||||
sf::Clock clock;
|
||||
BatchDrawables benchmarkedDrawable = BatchDrawables::Sprite;
|
||||
|
||||
while (window.isOpen())
|
||||
{
|
||||
// Handle events
|
||||
for (sf::Event event; window.pollEvent(event);)
|
||||
{
|
||||
// Window closed or escape key pressed: exit
|
||||
if (event.type == sf::Event::Closed ||
|
||||
(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape))
|
||||
{
|
||||
window.close();
|
||||
break;
|
||||
}
|
||||
|
||||
// W key pressed: increase drawable count
|
||||
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Up)
|
||||
{
|
||||
drawableCount += drawableCountChange;
|
||||
}
|
||||
|
||||
// S key pressed: decrease drawable count
|
||||
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Down)
|
||||
{
|
||||
drawableCount -= std::min(drawableCount, drawableCountChange);
|
||||
}
|
||||
|
||||
// R key pressed: cycle through the drawables to render
|
||||
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::D)
|
||||
{
|
||||
switch (benchmarkedDrawable)
|
||||
{
|
||||
case BatchDrawables::Sprite:
|
||||
benchmarkedDrawable = BatchDrawables::Text;
|
||||
break;
|
||||
|
||||
case BatchDrawables::Text:
|
||||
benchmarkedDrawable = BatchDrawables::Shape;
|
||||
break;
|
||||
|
||||
case BatchDrawables::Shape:
|
||||
benchmarkedDrawable = BatchDrawables::ShapeNoTexture;
|
||||
break;
|
||||
|
||||
case BatchDrawables::ShapeNoTexture:
|
||||
benchmarkedDrawable = BatchDrawables::Sprite;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// F or G key pressed: cycle through batch modes
|
||||
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::F)
|
||||
{
|
||||
switch (batchMode)
|
||||
{
|
||||
case sf::SpriteBatch::BatchMode::Deferred:
|
||||
batchMode = sf::SpriteBatch::BatchMode::TextureSort;
|
||||
break;
|
||||
|
||||
case sf::SpriteBatch::BatchMode::TextureSort:
|
||||
batchMode = sf::SpriteBatch::BatchMode::DepthSort;
|
||||
break;
|
||||
|
||||
case sf::SpriteBatch::BatchMode::DepthSort:
|
||||
batchMode = sf::SpriteBatch::BatchMode::Deferred;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// G key pressed: toggle batching
|
||||
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::G)
|
||||
{
|
||||
batchEnabled = !batchEnabled;
|
||||
}
|
||||
|
||||
// H key pressed: toggle static batch
|
||||
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::H)
|
||||
{
|
||||
useStaticBatch = !useStaticBatch;
|
||||
}
|
||||
|
||||
// Window size changed, adjust view appropriately
|
||||
if (event.type == sf::Event::Resized)
|
||||
{
|
||||
sf::View view;
|
||||
view.setSize({windowWidth, windowHeight});
|
||||
view.setCenter({windowWidth / 2.f, windowHeight / 2.f});
|
||||
window.setView(view);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the window
|
||||
window.clear(sf::Color(50, 50, 50));
|
||||
|
||||
// Delete excess drawables
|
||||
while (drawableCount < sprites.size())
|
||||
{
|
||||
sprites.pop_back();
|
||||
texts.pop_back();
|
||||
shapes.pop_back();
|
||||
shapesNoTexture.pop_back();
|
||||
}
|
||||
|
||||
// Add new drawables
|
||||
while (drawableCount > sprites.size())
|
||||
{
|
||||
float x = std::uniform_real_distribution<float>(0, windowWidth)(mt);
|
||||
float y = std::uniform_real_distribution<float>(0, windowHeight)(mt);
|
||||
float degrees = std::uniform_real_distribution<float>(0, 360)(mt);
|
||||
|
||||
const sf::Texture* texture = &textures[std::uniform_int_distribution<std::size_t>(0, textures.size() - 1)(mt)];
|
||||
|
||||
// X% chance to use the previous texture instead
|
||||
// The ordered batcher performs badly when textures change extremely often.
|
||||
if (!sprites.empty() && std::uniform_int_distribution<int>(0, 99)(mt) < sameTextureChance)
|
||||
texture = (sprites.end() - 1)->getTexture();
|
||||
|
||||
auto& sprite = sprites.emplace_back();
|
||||
|
||||
sprite.setPosition(sf::Vector2f(x, y));
|
||||
sprite.setRotation(sf::degrees(degrees));
|
||||
sprite.setTexture(*texture);
|
||||
|
||||
auto& shape = shapes.emplace_back(60.0f, 30);
|
||||
|
||||
shape.setPosition(sf::Vector2f(x, y));
|
||||
shape.setRotation(sf::degrees(degrees));
|
||||
shape.setTexture(texture);
|
||||
shape.setOutlineColor(sf::Color::Blue);
|
||||
shape.setOutlineThickness(2);
|
||||
|
||||
auto& shapeNoTexture = shapesNoTexture.emplace_back(60.0f, 30);
|
||||
|
||||
shapeNoTexture.setPosition(sf::Vector2f(x, y));
|
||||
shapeNoTexture.setRotation(sf::degrees(degrees));
|
||||
shapeNoTexture.setOutlineColor(sf::Color::Blue);
|
||||
shapeNoTexture.setOutlineThickness(2);
|
||||
|
||||
auto& text = texts.emplace_back(getRandomString(mt), font, 60);
|
||||
|
||||
text.setPosition(sf::Vector2f(x, y));
|
||||
text.setRotation(sf::degrees(degrees));
|
||||
text.setFillColor(sf::Color::Black);
|
||||
text.setOutlineColor(sf::Color::White);
|
||||
text.setOutlineThickness(8);
|
||||
text.setStyle(sf::Text::Bold);
|
||||
}
|
||||
|
||||
// Update drawables
|
||||
for (std::size_t i = 0; i < sprites.size(); i++)
|
||||
{
|
||||
const sf::Angle amount = sf::degrees(2);
|
||||
|
||||
sprites[i].setRotation(sprites[i].getRotation() + amount);
|
||||
texts[i].setRotation(texts[i].getRotation() + amount / 8);
|
||||
shapes[i].setRotation(shapes[i].getRotation() + amount);
|
||||
shapesNoTexture[i].setRotation(shapesNoTexture[i].getRotation() + amount);
|
||||
}
|
||||
|
||||
// Batch drawables
|
||||
clock.restart();
|
||||
|
||||
float depth = 1.f;
|
||||
std::size_t count = 0;
|
||||
|
||||
if (batchEnabled && !useStaticBatch)
|
||||
{
|
||||
batch.clear();
|
||||
batch.setBatchMode(batchMode);
|
||||
|
||||
for (std::size_t i = 0; i < sprites.size(); i++)
|
||||
{
|
||||
const sf::Batchable* batchable = nullptr;
|
||||
|
||||
switch (benchmarkedDrawable)
|
||||
{
|
||||
case BatchDrawables::Sprite:
|
||||
batchable = &sprites[i];
|
||||
break;
|
||||
|
||||
case BatchDrawables::Shape:
|
||||
batchable = &shapes[i];
|
||||
break;
|
||||
|
||||
case BatchDrawables::ShapeNoTexture:
|
||||
batchable = &shapesNoTexture[i];
|
||||
break;
|
||||
|
||||
case BatchDrawables::Text:
|
||||
batchable = &texts[i];
|
||||
break;
|
||||
}
|
||||
|
||||
batch.batch(*batchable, depth);
|
||||
|
||||
count++;
|
||||
|
||||
// Move to another layer for DepthSort case
|
||||
// In a
|
||||
if (count > drawablesPerLayer)
|
||||
{
|
||||
count -= drawablesPerLayer;
|
||||
depth -= 0.01f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!batchEnabled)
|
||||
{
|
||||
for (std::size_t i = 0; i < sprites.size(); i++)
|
||||
{
|
||||
const sf::Drawable* drawable = nullptr;
|
||||
|
||||
switch (benchmarkedDrawable)
|
||||
{
|
||||
case BatchDrawables::Sprite:
|
||||
drawable = &sprites[i];
|
||||
break;
|
||||
|
||||
case BatchDrawables::Shape:
|
||||
drawable = &shapes[i];
|
||||
break;
|
||||
|
||||
case BatchDrawables::ShapeNoTexture:
|
||||
drawable = &shapesNoTexture[i];
|
||||
break;
|
||||
|
||||
case BatchDrawables::Text:
|
||||
drawable = &texts[i];
|
||||
break;
|
||||
}
|
||||
|
||||
window.draw(*drawable);
|
||||
}
|
||||
}
|
||||
|
||||
if (batchEnabled)
|
||||
batch.draw(window, sf::RenderStates(sf::BlendAlpha, sf::Transform::Identity, nullptr, nullptr));
|
||||
|
||||
sf::Time time = clock.restart();
|
||||
|
||||
timeHistory.push_back(time);
|
||||
|
||||
if (timeHistory.size() > maxTimesSaved)
|
||||
timeHistory.erase(timeHistory.begin(), timeHistory.end() - static_cast<int>(maxTimesSaved));
|
||||
|
||||
const sf::Time average = std::accumulate(timeHistory.begin(), timeHistory.end(), sf::Time::Zero) /
|
||||
static_cast<float>(timeHistory.size());
|
||||
|
||||
if (framesTillRefUpdate <= 0)
|
||||
{
|
||||
framesTillRefUpdate = 18;
|
||||
timeReference = time;
|
||||
}
|
||||
|
||||
framesTillRefUpdate--;
|
||||
|
||||
// clang-format off
|
||||
debugInfoText.setPosition(sf::Vector2f(0, 0));
|
||||
debugInfoText.setString(
|
||||
"Batching enabled:\n"
|
||||
"Current drawable:\n"
|
||||
"Time:\n"
|
||||
"Time (average):\n"
|
||||
"Drawables:\n"
|
||||
"Batch mode:\n");
|
||||
|
||||
window.draw(debugInfoText);
|
||||
|
||||
debugInfoText.setPosition(sf::Vector2f(250, 0));
|
||||
debugInfoText.setString(
|
||||
std::string(batchEnabled ? "true" : "false") + "\n" +
|
||||
getDisplayString(benchmarkedDrawable) + "\n" +
|
||||
std::to_string(timeReference.asSeconds() * 1000.0f) + "\n" +
|
||||
std::to_string(average.asSeconds() * 1000.0f) + "\n" +
|
||||
std::to_string(drawableCount) + "\n" +
|
||||
getDisplayString(batchMode) + "\n" +
|
||||
(useStaticBatch ? "Static batch is active" : ""));
|
||||
// clang-format on
|
||||
|
||||
window.draw(debugInfoText);
|
||||
|
||||
// Display things on screen
|
||||
window.display();
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
22
examples/sprite_batch/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
# all source files
|
||||
set(SRC Batching.cpp)
|
||||
if (SFML_OS_IOS)
|
||||
set(RESOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources/Tex0.png
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources/Tex1.png
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources/Tex2.png
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources/Tex3.png
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources/Tex4.png
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources/Tex5.png
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources/Tex6.png
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources/Tex7.png
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources/Tex8.png)
|
||||
set_source_files_properties(${RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
endif()
|
||||
|
||||
# define the sprite batch demo target
|
||||
sfml_add_example(sprite_batch GUI_APP
|
||||
SOURCES ${SRC}
|
||||
BUNDLE_RESOURCES ${RESOURCES}
|
||||
DEPENDS SFML::Graphics
|
||||
RESOURCES_DIR resources)
|
BIN
examples/sprite_batch/resources/Tex0.png
Normal file
After Width: | Height: | Size: 528 B |
BIN
examples/sprite_batch/resources/Tex1.png
Normal file
After Width: | Height: | Size: 732 B |
BIN
examples/sprite_batch/resources/Tex2.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
examples/sprite_batch/resources/Tex3.png
Normal file
After Width: | Height: | Size: 191 B |
BIN
examples/sprite_batch/resources/Tex4.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
examples/sprite_batch/resources/Tex5.png
Normal file
After Width: | Height: | Size: 842 B |
BIN
examples/sprite_batch/resources/Tex6.png
Normal file
After Width: | Height: | Size: 940 B |
BIN
examples/sprite_batch/resources/Tex7.png
Normal file
After Width: | Height: | Size: 994 B |
BIN
examples/sprite_batch/resources/Tex8.png
Normal file
After Width: | Height: | Size: 1015 B |
BIN
examples/sprite_batch/resources/tuffy.ttf
Normal file
@ -46,6 +46,7 @@
|
||||
#include <SFML/Graphics/Shader.hpp>
|
||||
#include <SFML/Graphics/Shape.hpp>
|
||||
#include <SFML/Graphics/Sprite.hpp>
|
||||
#include <SFML/Graphics/SpriteBatch.hpp>
|
||||
#include <SFML/Graphics/Text.hpp>
|
||||
#include <SFML/Graphics/Texture.hpp>
|
||||
#include <SFML/Graphics/Transform.hpp>
|
||||
|
77
include/SFML/Graphics/Batchable.hpp
Normal file
@ -0,0 +1,77 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SFML - Simple and Fast Multimedia Library
|
||||
// Copyright (C) 2007-2022 Laurent Gomila (laurent@sfml-dev.org)
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it freely,
|
||||
// subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented;
|
||||
// you must not claim that you wrote the original software.
|
||||
// If you use this software in a product, an acknowledgment
|
||||
// in the product documentation would be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such,
|
||||
// and must not be misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Headers
|
||||
////////////////////////////////////////////////////////////
|
||||
#include <SFML/Graphics/Export.hpp>
|
||||
|
||||
namespace sf
|
||||
{
|
||||
class SpriteBatch;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Abstract base class for objects that can be batched
|
||||
/// to a sf::SpriteBatch
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
class SFML_GRAPHICS_API Batchable
|
||||
{
|
||||
public:
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Virtual destructor
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
virtual ~Batchable() = default;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Batch the object using the given SpriteBatch
|
||||
///
|
||||
/// This is a pure virtual function that has to be implemented
|
||||
/// by the derived class to define how the drawable should be
|
||||
/// batched.
|
||||
///
|
||||
/// \param spriteBatch The SpriteBatch to use
|
||||
/// \param depth The depth at which to render the object
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
virtual void batch(SpriteBatch& spriteBatch, float depth = 0.f) const = 0;
|
||||
};
|
||||
|
||||
} // namespace sf
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \class sf::Batchable
|
||||
/// \ingroup graphics
|
||||
///
|
||||
/// sf::Batchable is an interface for drawables that can be
|
||||
/// batched using an sf::SpriteBatch. To implement the interface,
|
||||
/// you need to define a batch method that makes use of
|
||||
/// sf::SpriteBatch::batch(), and supplies it the vertices, texture,
|
||||
/// transform and other data for drawing.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
@ -29,6 +29,7 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
#include <SFML/Graphics/Export.hpp>
|
||||
|
||||
#include <SFML/Graphics/Batchable.hpp>
|
||||
#include <SFML/Graphics/Drawable.hpp>
|
||||
#include <SFML/Graphics/Transformable.hpp>
|
||||
#include <SFML/Graphics/VertexArray.hpp>
|
||||
@ -43,7 +44,7 @@ class Texture;
|
||||
/// \brief Base class for textured shapes with outline
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
class SFML_GRAPHICS_API Shape : public Drawable, public Transformable
|
||||
class SFML_GRAPHICS_API Shape : public Drawable, public Transformable, public Batchable
|
||||
{
|
||||
public:
|
||||
////////////////////////////////////////////////////////////
|
||||
@ -248,6 +249,15 @@ public:
|
||||
////////////////////////////////////////////////////////////
|
||||
FloatRect getGlobalBounds() const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Batch the Shape using the given SpriteBatch
|
||||
///
|
||||
/// \param spriteBatch The SpriteBatch to use
|
||||
/// \param depth The depth at which to render the shape
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void batch(SpriteBatch& spriteBatch, float depth = 0.f) const override;
|
||||
|
||||
protected:
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Default constructor
|
||||
|
@ -29,6 +29,7 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
#include <SFML/Graphics/Export.hpp>
|
||||
|
||||
#include <SFML/Graphics/Batchable.hpp>
|
||||
#include <SFML/Graphics/Drawable.hpp>
|
||||
#include <SFML/Graphics/Rect.hpp>
|
||||
#include <SFML/Graphics/Transformable.hpp>
|
||||
@ -44,7 +45,7 @@ class Texture;
|
||||
/// own transformations, color, etc.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
class SFML_GRAPHICS_API Sprite : public Drawable, public Transformable
|
||||
class SFML_GRAPHICS_API Sprite : public Drawable, public Transformable, public Batchable
|
||||
{
|
||||
public:
|
||||
////////////////////////////////////////////////////////////
|
||||
@ -206,6 +207,15 @@ public:
|
||||
////////////////////////////////////////////////////////////
|
||||
FloatRect getGlobalBounds() const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Batch the Sprite using the given SpriteBatch
|
||||
///
|
||||
/// \param spriteBatch The SpriteBatch to use
|
||||
/// \param depth The depth at which to render the sprite
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void batch(SpriteBatch& spriteBatch, float depth = 0.f) const override;
|
||||
|
||||
private:
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Draw the sprite to a render target
|
||||
|
256
include/SFML/Graphics/SpriteBatch.hpp
Normal file
@ -0,0 +1,256 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SFML - Simple and Fast Multimedia Library
|
||||
// Copyright (C) 2007-2022 Laurent Gomila (laurent@sfml-dev.org)
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it freely,
|
||||
// subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented;
|
||||
// you must not claim that you wrote the original software.
|
||||
// If you use this software in a product, an acknowledgment
|
||||
// in the product documentation would be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such,
|
||||
// and must not be misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Headers
|
||||
////////////////////////////////////////////////////////////
|
||||
#include <SFML/Graphics/Export.hpp>
|
||||
|
||||
#include <SFML/Graphics/Batchable.hpp>
|
||||
#include <SFML/Graphics/RenderStates.hpp>
|
||||
#include <SFML/Graphics/Sprite.hpp>
|
||||
#include <SFML/Graphics/Text.hpp>
|
||||
#include <SFML/Graphics/Texture.hpp>
|
||||
#include <SFML/Graphics/Vertex.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace sf
|
||||
{
|
||||
class RenderTarget;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Batching utility for drawables, meant for increasing
|
||||
/// rendering performance
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
class SFML_GRAPHICS_API SpriteBatch : public Drawable
|
||||
{
|
||||
public:
|
||||
enum class BatchMode
|
||||
{
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Creates a batch for each contiguous groups of drawables
|
||||
/// that use the same texture.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
Deferred,
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Organizes objects into a single layer, then reorders
|
||||
/// them by texture before drawing.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
TextureSort,
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Organizes objects into multiple layers. Groups objects
|
||||
/// into layers based on depth, then reorders objects in
|
||||
/// each layer by texture before drawing the layers from
|
||||
/// furthest to closest.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
DepthSort,
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Sets the batch mode to use
|
||||
///
|
||||
/// \param batchMode The new batch mode to use
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void setBatchMode(BatchMode batchMode);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Batches the given drawable
|
||||
///
|
||||
/// \param drawable The drawable to be batched
|
||||
/// \param depth The depth at which the drawable will be
|
||||
/// rendererd. This value is used only when
|
||||
/// BatchMode::DepthSort mode is used
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void batch(const Batchable& drawable, float depth = 0.f);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Batches an array of vertices
|
||||
///
|
||||
/// \param vertices The array of vertices to be batched
|
||||
/// \param count How many vertices to batch
|
||||
/// \param type The primitive formed by the vertices.
|
||||
/// Only triangle primitives are supported
|
||||
/// \param texture The texture to use for rendering
|
||||
/// \param transform The transform to apply on the vertices
|
||||
/// \param depth The depth at which the drawable will be
|
||||
/// rendererd. This value is used only when
|
||||
/// BatchMode::DepthSort mode is used.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void batch(const Vertex* vertices,
|
||||
std::size_t count,
|
||||
PrimitiveType type,
|
||||
const Texture* texture,
|
||||
const Transform& transform = Transform::Identity,
|
||||
float depth = 0.f);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Renders the batch to the render target
|
||||
///
|
||||
/// \param target The RenderTarget to draw to
|
||||
/// \param states The RenderStates to use. The texture is ignored
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void draw(RenderTarget& target, const RenderStates& states) const override;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Clears the batch, removing all drawables that were
|
||||
/// added
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void clear();
|
||||
|
||||
private:
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Pushes a triangle into internal storage
|
||||
///
|
||||
/// \param a First vertex
|
||||
/// \param b Second vertex
|
||||
/// \param c Third vertex
|
||||
/// \param transform Transform to apply before inserting
|
||||
/// \param texture Texture to apply to triangle
|
||||
/// \param depth The depth to use for depth sorting
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void pushTriangle(const Vertex& a,
|
||||
const Vertex& b,
|
||||
const Vertex& c,
|
||||
const Transform& transform,
|
||||
const Texture* texture,
|
||||
float depth);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Updates the batch, preparing its internal state for
|
||||
/// efficient usage
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void updateBatch() const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Holds information about a batched triangle
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
struct TriangleInfo
|
||||
{
|
||||
const Texture* texture{}; //!< The triangle's texture
|
||||
float depth{}; //!< The depth of the triangle
|
||||
|
||||
TriangleInfo() = default;
|
||||
TriangleInfo(const Texture* theTexture, float theDepth) : texture(theTexture), depth(theDepth)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Holds information for rendering a batch
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
struct BatchInfo
|
||||
{
|
||||
const Texture* texture{}; //!< The texture used to render the batch
|
||||
std::size_t vertexCount{}; //!< The number of contiguous vertices to render
|
||||
|
||||
BatchInfo() = default;
|
||||
BatchInfo(const Texture* theTexture, std::size_t theVertexCount) :
|
||||
texture(theTexture),
|
||||
vertexCount(theVertexCount)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Member data
|
||||
////////////////////////////////////////////////////////////
|
||||
// Batched Triangles
|
||||
std::vector<TriangleInfo> m_triangles; //!< Info about batched triangles
|
||||
std::vector<Vertex> m_unsortedVertices; //!< Vertices currently batched
|
||||
mutable std::vector<BatchInfo> m_batches; //!< Prepared batch information, ready for rendering
|
||||
mutable std::vector<Vertex> m_vertices; //!< Prepared vertices, ready for rendering
|
||||
|
||||
// Batch Settings
|
||||
BatchMode m_batchMode{BatchMode::Deferred}; //!< The current batch strategy
|
||||
mutable bool m_updateRequired{true}; //!< If true, batch must be sorted before rendering
|
||||
};
|
||||
|
||||
} // namespace sf
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \class sf::SpriteBatch
|
||||
/// \ingroup graphics
|
||||
///
|
||||
/// sf::SpriteBatch is a batching utility for drawables. It
|
||||
/// increases rendering performance by reordering and combining
|
||||
/// drawables, such that they are rendered using fewer draw calls
|
||||
/// and state switches.
|
||||
///
|
||||
/// sf::SpriteBatch supports multiple batching strategies. Each
|
||||
/// comes with its benefits and drawbacks, so the optimal choice
|
||||
/// depends on your specific needs.
|
||||
///
|
||||
/// \c sf::BatchMode::Deferred is best used when rendering contiguous
|
||||
/// groups ofdrawables that use the same texture. This mode creates
|
||||
/// batches for each such group, improving performance when many
|
||||
/// drawables with few vertices are drawn (such as hundreds to
|
||||
/// thousands of sf::Sprite drawables).
|
||||
/// The render order of drawables is the same as the order they
|
||||
/// were batched in. This is similar how rendering works with
|
||||
/// sf::RenderTarget.
|
||||
///
|
||||
/// \c sf::BatchMode::TextureSort is best used when drawing objects
|
||||
/// that are positioned on a "layer", and do not overlap each other.
|
||||
/// This mode assumes that the draw order is not important within
|
||||
/// a layer, and sorts all objects by texture before batching them.
|
||||
/// This provides great performance improvements, especially when
|
||||
/// rendering objects using different textures (since it will reorder
|
||||
/// them back in contiguous groups).
|
||||
/// Care should be taken when when drawing overlapping objects, since
|
||||
/// it is not guaranteed that they will be ordered correctly.
|
||||
///
|
||||
/// \c sf::BatchMode::DepthSort is best used when drawing objects
|
||||
/// that are positioned on multiple "layers" based on depth, and objects
|
||||
/// within a layer do not overlap each other.
|
||||
/// Within each layer, objects are sorted by texture before drawing,
|
||||
/// similar to \c TextureSort. However, objects in a layer with a
|
||||
/// higher "depth" value will be drawn behind objects with a lower
|
||||
/// "depth" value.
|
||||
/// You can also use this mode to render objects in an order that is
|
||||
/// different from the order they were batched in, since objects
|
||||
/// will be reordered by depth before rendering.
|
||||
///
|
||||
/// An example of how to use sf::SpriteBatch can be found in the
|
||||
/// examples/sprite_batch project.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
@ -29,6 +29,7 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
#include <SFML/Graphics/Export.hpp>
|
||||
|
||||
#include <SFML/Graphics/Batchable.hpp>
|
||||
#include <SFML/Graphics/Drawable.hpp>
|
||||
#include <SFML/Graphics/Rect.hpp>
|
||||
#include <SFML/Graphics/Transformable.hpp>
|
||||
@ -47,7 +48,7 @@ class Font;
|
||||
/// \brief Graphical text that can be drawn to a render target
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
class SFML_GRAPHICS_API Text : public Drawable, public Transformable
|
||||
class SFML_GRAPHICS_API Text : public Drawable, public Transformable, public Batchable
|
||||
{
|
||||
public:
|
||||
////////////////////////////////////////////////////////////
|
||||
@ -417,6 +418,15 @@ public:
|
||||
////////////////////////////////////////////////////////////
|
||||
FloatRect getGlobalBounds() const;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Batch the Text using the given SpriteBatch
|
||||
///
|
||||
/// \param spriteBatch The SpriteBatch to use
|
||||
/// \param depth The depth at which to render the text
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void batch(SpriteBatch& spriteBatch, float depth = 0.f) const override;
|
||||
|
||||
private:
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Draw the text to a render target
|
||||
|
@ -4,6 +4,7 @@ set(SRCROOT ${PROJECT_SOURCE_DIR}/src/SFML/Graphics)
|
||||
|
||||
# all source files
|
||||
set(SRC
|
||||
${INCROOT}/Batchable.hpp
|
||||
${SRCROOT}/BlendMode.cpp
|
||||
${INCROOT}/BlendMode.hpp
|
||||
${INCROOT}/Color.hpp
|
||||
@ -36,6 +37,8 @@ set(SRC
|
||||
${INCROOT}/RenderWindow.hpp
|
||||
${SRCROOT}/Shader.cpp
|
||||
${INCROOT}/Shader.hpp
|
||||
${SRCROOT}/SpriteBatch.cpp
|
||||
${INCROOT}/SpriteBatch.hpp
|
||||
${SRCROOT}/Texture.cpp
|
||||
${INCROOT}/Texture.hpp
|
||||
${SRCROOT}/TextureSaver.cpp
|
||||
|
@ -27,6 +27,7 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
#include <SFML/Graphics/RenderTarget.hpp>
|
||||
#include <SFML/Graphics/Shape.hpp>
|
||||
#include <SFML/Graphics/SpriteBatch.hpp>
|
||||
#include <SFML/Graphics/Texture.hpp>
|
||||
|
||||
#include <cmath>
|
||||
@ -148,6 +149,28 @@ FloatRect Shape::getGlobalBounds() const
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void Shape::batch(SpriteBatch& spriteBatch, float depth) const
|
||||
{
|
||||
// Draw the shape itself
|
||||
{
|
||||
const std::size_t shapeCount = m_vertices.getVertexCount();
|
||||
const Vertex* shapePtr = shapeCount > 0 ? &m_vertices[0] : nullptr;
|
||||
|
||||
spriteBatch.batch(shapePtr, shapeCount, m_vertices.getPrimitiveType(), m_texture, getTransform(), depth);
|
||||
}
|
||||
|
||||
// Draw outline
|
||||
if (m_outlineThickness != 0)
|
||||
{
|
||||
const std::size_t outlineCount = m_outlineVertices.getVertexCount();
|
||||
const Vertex* outlinePtr = outlineCount > 0 ? &m_outlineVertices[0] : nullptr;
|
||||
|
||||
spriteBatch.batch(outlinePtr, outlineCount, m_outlineVertices.getPrimitiveType(), nullptr, getTransform(), depth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
Shape::Shape() = default;
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
#include <SFML/Graphics/RenderTarget.hpp>
|
||||
#include <SFML/Graphics/Sprite.hpp>
|
||||
#include <SFML/Graphics/SpriteBatch.hpp>
|
||||
#include <SFML/Graphics/Texture.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
@ -130,6 +131,14 @@ FloatRect Sprite::getGlobalBounds() const
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void Sprite::batch(SpriteBatch& spriteBatch, float depth) const
|
||||
{
|
||||
if (m_texture)
|
||||
spriteBatch.batch(m_vertices, 4, PrimitiveType::TriangleStrip, m_texture, getTransform(), depth);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void Sprite::draw(RenderTarget& target, const RenderStates& states) const
|
||||
{
|
||||
|
248
src/SFML/Graphics/SpriteBatch.cpp
Normal file
@ -0,0 +1,248 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SFML - Simple and Fast Multimedia Library
|
||||
// Copyright (C) 2007-2022 Laurent Gomila (laurent@sfml-dev.org)
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it freely,
|
||||
// subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented;
|
||||
// you must not claim that you wrote the original software.
|
||||
// If you use this software in a product, an acknowledgment
|
||||
// in the product documentation would be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such,
|
||||
// and must not be misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Headers
|
||||
////////////////////////////////////////////////////////////
|
||||
#include <SFML/Graphics/Font.hpp>
|
||||
#include <SFML/Graphics/RenderStates.hpp>
|
||||
#include <SFML/Graphics/RenderTarget.hpp>
|
||||
#include <SFML/Graphics/SpriteBatch.hpp>
|
||||
#include <SFML/System/Err.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <numeric>
|
||||
|
||||
namespace sf
|
||||
{
|
||||
////////////////////////////////////////////////////////////
|
||||
void SpriteBatch::setBatchMode(BatchMode batchMode)
|
||||
{
|
||||
m_batchMode = batchMode;
|
||||
m_updateRequired = true;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void SpriteBatch::batch(const Batchable& drawable, float depth)
|
||||
{
|
||||
drawable.batch(*this, depth);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void SpriteBatch::batch(const Vertex* vertices,
|
||||
std::size_t count,
|
||||
PrimitiveType type,
|
||||
const Texture* texture,
|
||||
const Transform& transform,
|
||||
float depth)
|
||||
{
|
||||
const bool unsupportedType = type != PrimitiveType::TriangleFan && type != PrimitiveType::TriangleStrip &&
|
||||
type != PrimitiveType::Triangles;
|
||||
|
||||
if (unsupportedType)
|
||||
{
|
||||
err() << "SpriteBatch supports only triangle-based primitive types.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Anything to batch?
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
// Split into triangles
|
||||
switch (type)
|
||||
{
|
||||
case PrimitiveType::TriangleStrip:
|
||||
for (std::size_t i = 2; i < count; i++)
|
||||
pushTriangle(vertices[i - 2], vertices[i - 1], vertices[i], transform, texture, depth);
|
||||
break;
|
||||
|
||||
case PrimitiveType::TriangleFan:
|
||||
for (std::size_t i = 2; i < count; i++)
|
||||
pushTriangle(vertices[0], vertices[i - 1], vertices[i], transform, texture, depth);
|
||||
break;
|
||||
|
||||
case PrimitiveType::Triangles:
|
||||
for (std::size_t i = 2; i < count; i += 3)
|
||||
pushTriangle(vertices[i - 2], vertices[i - 1], vertices[i], transform, texture, depth);
|
||||
break;
|
||||
|
||||
default:
|
||||
err() << "A non-triangle primitive type was encountered when decomposing into triangles." << std::endl;
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void SpriteBatch::draw(RenderTarget& target, const RenderStates& states) const
|
||||
{
|
||||
if (m_updateRequired)
|
||||
{
|
||||
updateBatch();
|
||||
m_updateRequired = false;
|
||||
}
|
||||
|
||||
RenderStates statesCopy = states;
|
||||
|
||||
std::size_t startTriangle = 0;
|
||||
for (const auto& batch : m_batches)
|
||||
{
|
||||
statesCopy.texture = batch.texture;
|
||||
target.draw(&m_vertices[startTriangle], batch.vertexCount, PrimitiveType::Triangles, statesCopy);
|
||||
startTriangle += batch.vertexCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void SpriteBatch::pushTriangle(const Vertex& a,
|
||||
const Vertex& b,
|
||||
const Vertex& c,
|
||||
const Transform& transform,
|
||||
const Texture* texture,
|
||||
float depth)
|
||||
{
|
||||
m_triangles.emplace_back(texture, depth);
|
||||
|
||||
m_unsortedVertices.emplace_back(transform * a.position, a.color, a.texCoords);
|
||||
m_unsortedVertices.emplace_back(transform * b.position, b.color, b.texCoords);
|
||||
m_unsortedVertices.emplace_back(transform * c.position, c.color, c.texCoords);
|
||||
|
||||
m_updateRequired = true;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void SpriteBatch::updateBatch() const
|
||||
{
|
||||
if (m_batchMode == BatchMode::Deferred)
|
||||
{
|
||||
// Batch based on texture change
|
||||
std::size_t startIndex = 0;
|
||||
std::size_t nextIndex = 0;
|
||||
const Texture* lastTexture = nullptr;
|
||||
|
||||
while (nextIndex < m_triangles.size())
|
||||
{
|
||||
const Texture* nextTexture = m_triangles[nextIndex].texture;
|
||||
if (nextTexture != lastTexture)
|
||||
{
|
||||
m_batches.emplace_back(lastTexture, (nextIndex - startIndex) * 3);
|
||||
lastTexture = nextTexture;
|
||||
startIndex = nextIndex;
|
||||
}
|
||||
|
||||
nextIndex++;
|
||||
}
|
||||
|
||||
// Deal with leftovers
|
||||
if (startIndex != m_triangles.size())
|
||||
m_batches.emplace_back(lastTexture, (m_triangles.size() - startIndex) * 3);
|
||||
|
||||
m_vertices = m_unsortedVertices;
|
||||
}
|
||||
|
||||
else if (m_batchMode == BatchMode::TextureSort || m_batchMode == BatchMode::DepthSort)
|
||||
{
|
||||
std::vector<std::size_t> indices(m_triangles.size());
|
||||
|
||||
std::iota(indices.begin(), indices.end(), 0);
|
||||
|
||||
if (m_batchMode == BatchMode::TextureSort)
|
||||
{
|
||||
const auto comp = [this](const std::size_t a, const std::size_t b)
|
||||
{ return m_triangles[a].texture < m_triangles[b].texture; };
|
||||
|
||||
std::stable_sort(indices.begin(), indices.end(), comp);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
const auto comp = [this](const std::size_t a, const std::size_t b)
|
||||
{
|
||||
if (m_triangles[a].depth != m_triangles[b].depth)
|
||||
return m_triangles[a].depth > m_triangles[b].depth;
|
||||
else
|
||||
return m_triangles[a].texture < m_triangles[b].texture;
|
||||
};
|
||||
|
||||
std::stable_sort(indices.begin(), indices.end(), comp);
|
||||
}
|
||||
|
||||
// Create the array of sorted vertices
|
||||
// We need them sorted so that we feed chunks to RenderTarget
|
||||
m_vertices.resize(m_unsortedVertices.size());
|
||||
|
||||
for (std::size_t i = 0; i < indices.size(); i++)
|
||||
{
|
||||
const std::size_t newPos = i * 3;
|
||||
const std::size_t oldPos = indices[i] * 3;
|
||||
m_vertices[newPos] = m_unsortedVertices[oldPos];
|
||||
m_vertices[newPos + 1] = m_unsortedVertices[oldPos + 1];
|
||||
m_vertices[newPos + 2] = m_unsortedVertices[oldPos + 2];
|
||||
}
|
||||
|
||||
// Generate batches
|
||||
std::size_t startIndex = 0;
|
||||
std::size_t nextIndex = 0;
|
||||
const Texture* lastTexture = nullptr;
|
||||
|
||||
while (nextIndex < m_triangles.size())
|
||||
{
|
||||
const Texture* nextTexture = m_triangles[indices[nextIndex]].texture;
|
||||
|
||||
if (nextTexture != lastTexture)
|
||||
{
|
||||
m_batches.emplace_back(lastTexture, (nextIndex - startIndex) * 3);
|
||||
lastTexture = nextTexture;
|
||||
startIndex = nextIndex;
|
||||
}
|
||||
|
||||
nextIndex++;
|
||||
}
|
||||
|
||||
// Deal with leftovers
|
||||
if (startIndex != m_triangles.size())
|
||||
m_batches.emplace_back(m_triangles[indices[startIndex]].texture, (m_triangles.size() - startIndex) * 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void SpriteBatch::clear()
|
||||
{
|
||||
m_unsortedVertices.clear();
|
||||
m_triangles.clear();
|
||||
m_vertices.clear();
|
||||
m_batches.clear();
|
||||
|
||||
m_updateRequired = false;
|
||||
}
|
||||
|
||||
} // namespace sf
|
@ -27,6 +27,7 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
#include <SFML/Graphics/Font.hpp>
|
||||
#include <SFML/Graphics/RenderTarget.hpp>
|
||||
#include <SFML/Graphics/SpriteBatch.hpp>
|
||||
#include <SFML/Graphics/Text.hpp>
|
||||
#include <SFML/Graphics/Texture.hpp>
|
||||
|
||||
@ -369,6 +370,33 @@ FloatRect Text::getGlobalBounds() const
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void Text::batch(SpriteBatch& spriteBatch, float depth) const
|
||||
{
|
||||
if (m_font)
|
||||
{
|
||||
ensureGeometryUpdate();
|
||||
const Texture* texture = &m_font->getTexture(m_characterSize);
|
||||
|
||||
// Draw outline
|
||||
if (m_outlineThickness != 0)
|
||||
{
|
||||
const std::size_t outlineCount = m_outlineVertices.getVertexCount();
|
||||
const Vertex* outlinePtr = outlineCount > 0 ? &m_outlineVertices[0] : nullptr;
|
||||
|
||||
spriteBatch.batch(outlinePtr, outlineCount, m_outlineVertices.getPrimitiveType(), texture, getTransform(), depth);
|
||||
}
|
||||
|
||||
// Draw the text itself
|
||||
{
|
||||
const std::size_t textCount = m_vertices.getVertexCount();
|
||||
const Vertex* textPtr = textCount > 0 ? &m_vertices[0] : nullptr;
|
||||
|
||||
spriteBatch.batch(textPtr, textCount, m_vertices.getPrimitiveType(), texture, getTransform(), depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void Text::draw(RenderTarget& target, const RenderStates& states) const
|
||||
{
|
||||
|