libdonut  2.3.2
Application framework for cross-platform game development in C++20
SpriteAtlas.hpp
Go to the documentation of this file.
1 #ifndef DONUT_GRAPHICS_SPRITE_ATLAS_HPP
2 #define DONUT_GRAPHICS_SPRITE_ATLAS_HPP
3 
4 #include <donut/AtlasPacker.hpp>
5 #include <donut/Color.hpp>
8 #include <donut/math.hpp>
9 
10 #include <cassert> // assert
11 #include <cstddef> // std::size_t
12 #include <cstdint> // std::uint8_t
13 #include <vector> // std::vector
14 
15 namespace donut::graphics {
16 
17 class Renderer; // Forward declaration, to avoid including Renderer.hpp.
18 
27  bool useLinearFiltering = false;
28 };
29 
34 class SpriteAtlas {
35 public:
39  using Flip = std::uint8_t;
40 
44  enum FlipAxis : Flip {
45  NO_FLIP = 0,
46  FLIP_HORIZONTALLY = 1 << 0,
47  FLIP_VERTICALLY = 1 << 1,
48  };
49 
53  struct SpriteId {
54  private:
55  friend SpriteAtlas;
56 
57  constexpr explicit SpriteId(std::size_t index) noexcept
58  : index(index) {}
59 
60  std::size_t index;
61  };
62 
66  struct Sprite {
67  vec2 position{};
68  vec2 size{};
70  };
71 
75  SpriteAtlas() = default;
76 
82  explicit SpriteAtlas(const SpriteAtlasOptions& options)
83  : options(options) {}
84 
103  [[nodiscard]] SpriteId insert(Renderer& renderer, const ImageView& image, Flip flip = NO_FLIP) {
104  const auto [x, y, resized] = atlasPacker.insertRectangle(image.getWidth(), image.getHeight());
105  prepareAtlasTexture(renderer, resized);
106 
107  atlasTexture.pasteImage2D(image, x, y);
108 
109  const vec2 position{static_cast<float>(x), static_cast<float>(y)};
110  const vec2 size{static_cast<float>(image.getWidth()), static_cast<float>(image.getHeight())};
111 
112  const std::size_t index = sprites.size();
113  sprites.push_back(Sprite{.position = position, .size = size, .flip = flip});
114  return SpriteId{index};
115  }
116 
147  [[nodiscard]] SpriteId createSubSprite(SpriteId baseSpriteId, std::size_t offsetX, std::size_t offsetY, std::size_t width, std::size_t height, Flip flip = NO_FLIP) {
148  const Sprite& baseSprite = getSprite(baseSpriteId);
149  assert(static_cast<float>(offsetX) <= baseSprite.size.x);
150  assert(static_cast<float>(offsetY) <= baseSprite.size.y);
151  assert(static_cast<float>(width) <= baseSprite.size.x - static_cast<float>(offsetX));
152  assert(static_cast<float>(height) <= baseSprite.size.y - static_cast<float>(offsetY));
153 
154  const vec2 position = baseSprite.position + vec2{static_cast<float>(offsetX), static_cast<float>(offsetY)};
155  const vec2 size{static_cast<float>(width), static_cast<float>(height)};
156 
157  const std::size_t index = sprites.size();
158  sprites.push_back(Sprite{.position = position, .size = size, .flip = flip});
159  return SpriteId{index};
160  }
161 
174  [[nodiscard]] const Sprite& getSprite(SpriteId id) const {
175  assert(id.index < sprites.size());
176  return sprites[id.index];
177  }
178 
186  [[nodiscard]] const Texture& getAtlasTexture() const noexcept {
187  return atlasTexture;
188  }
189 
190 private:
191  static constexpr std::size_t INITIAL_RESOLUTION = 128;
192  static constexpr std::size_t PADDING = 6;
193 
194  void prepareAtlasTexture(Renderer& renderer, bool resized) {
195  if (atlasTexture) {
196  if (resized) {
197  atlasTexture.grow2D(renderer, atlasPacker.getResolution(), atlasPacker.getResolution(), Color::INVISIBLE);
198  }
199  } else {
200  atlasTexture = {
202  atlasPacker.getResolution(),
203  atlasPacker.getResolution(),
204  {.repeat = false, .useLinearFiltering = options.useLinearFiltering, .useMipmap = false},
205  };
206  atlasTexture.fill2D(renderer, Color::INVISIBLE);
207  }
208  }
209 
210  AtlasPacker<INITIAL_RESOLUTION, PADDING> atlasPacker{};
211  Texture atlasTexture{};
212  std::vector<Sprite> sprites{};
213  SpriteAtlasOptions options{};
214 };
215 
216 } // namespace donut::graphics
217 
218 #endif
InsertRectangleResult insertRectangle(std::size_t width, std::size_t height)
Find and reserve a suitable space for a new axis-aligned rectangle to be inserted into the atlas.
Definition: AtlasPacker.hpp:79
std::size_t getResolution() const noexcept
Get the current required resolution of the atlas.
Definition: AtlasPacker.hpp:116
static const Color INVISIBLE
Definition: Color.hpp:13
Read-only non-owning view over a 2D image.
Definition: Image.hpp:40
constexpr std::size_t getHeight() const noexcept
Get the height of the image referenced by this view.
Definition: Image.hpp:101
constexpr std::size_t getWidth() const noexcept
Get the width of the image referenced by this view.
Definition: Image.hpp:88
Persistent system for rendering the batched draw commands of a RenderPass onto a Framebuffer,...
Definition: Renderer.hpp:38
Expandable texture atlas for packing 2D images into a spritesheet to enable batch rendering.
Definition: SpriteAtlas.hpp:34
FlipAxis
Flag values for Flip that describe how a sprite is flipped when rendered.
Definition: SpriteAtlas.hpp:44
@ FLIP_HORIZONTALLY
Flip the sprite along the X axis.
Definition: SpriteAtlas.hpp:46
@ NO_FLIP
Do not flip the sprite.
Definition: SpriteAtlas.hpp:45
@ FLIP_VERTICALLY
Flip the sprite along the Y axis.
Definition: SpriteAtlas.hpp:47
const Texture & getAtlasTexture() const noexcept
Get a reference to the internal texture atlas.
Definition: SpriteAtlas.hpp:186
SpriteAtlas(const SpriteAtlasOptions &options)
Construct an empty sprite atlas.
Definition: SpriteAtlas.hpp:82
const Sprite & getSprite(SpriteId id) const
Get information about a specific image in the spritesheet.
Definition: SpriteAtlas.hpp:174
SpriteId insert(Renderer &renderer, const ImageView &image, Flip flip=NO_FLIP)
Add a new image to the spritesheet, possibly expanding the texture atlas in order to make space for i...
Definition: SpriteAtlas.hpp:103
std::uint8_t Flip
Flags that describe how a sprite is flipped when rendered.
Definition: SpriteAtlas.hpp:39
SpriteId createSubSprite(SpriteId baseSpriteId, std::size_t offsetX, std::size_t offsetY, std::size_t width, std::size_t height, Flip flip=NO_FLIP)
Add a new sprite that is defined as a sub-region of an existing sprite.
Definition: SpriteAtlas.hpp:147
SpriteAtlas()=default
Construct an empty sprite atlas.
Storage for multidimensional data, such as 2D images, on the GPU, combined with a sampler configurati...
Definition: Texture.hpp:75
void grow2D(Renderer &renderer, std::size_t newWidth, std::size_t newHeight, std::optional< Color > backgroundColor={})
Expand the allocated 2D texture data by allocating larger texture storage and copying the old texture...
void pasteImage2D(std::size_t width, std::size_t height, PixelFormat pixelFormat, PixelComponentType pixelComponentType, const void *pixels, std::size_t x, std::size_t y)
Copy 2D image data into the 2D texture at a specific position.
void fill2D(Renderer &renderer, Color color)
Fill the entire allocated 2D texture data with pixels of the given color.
Definition: Buffer.hpp:7
@ R8G8B8A8_UNORM
Each texel comprises 4 normalized 8-bit unsigned integer components: red, green, blue,...
Configuration options for a SpriteAtlas.
Definition: SpriteAtlas.hpp:22
bool useLinearFiltering
Use bilinear filtering rather than nearest-neighbor interpolation when rendering sprites from this sp...
Definition: SpriteAtlas.hpp:27
Identifier for a specific image in the spritesheet.
Definition: SpriteAtlas.hpp:53
Information about a specific image in the spritesheet.
Definition: SpriteAtlas.hpp:66
Flip flip
Flags that describe how the sprite should be flipped when rendered.
Definition: SpriteAtlas.hpp:69
vec2 size
Size of the image in the texture atlas, in texels.
Definition: SpriteAtlas.hpp:68
vec2 position
Position of the image in the texture atlas, in texels.
Definition: SpriteAtlas.hpp:67