libdonut  2.3.2
Application framework for cross-platform game development in C++20
AtlasPacker.hpp
Go to the documentation of this file.
1 #ifndef DONUT_ATLAS_PACKER_HPP
2 #define DONUT_ATLAS_PACKER_HPP
3 
4 #include <cstddef> // std::size_t
5 #include <vector> // std::vector
6 
7 namespace donut {
8 
17 template <std::size_t InitialResolution, std::size_t Padding>
18 class AtlasPacker {
19 public:
24  static constexpr std::size_t INITIAL_RESOLUTION = InitialResolution;
25 
30  static constexpr std::size_t GROWTH_FACTOR = 2;
31 
35  static constexpr std::size_t PADDING = Padding;
36 
42  static constexpr float MINIMUM_ROW_HEIGHT_RATIO = 0.7f;
43 
52  std::size_t x;
53 
58  std::size_t y;
59 
65  bool resized;
66  };
67 
79  [[nodiscard]] InsertRectangleResult insertRectangle(std::size_t width, std::size_t height) {
80  const std::size_t paddedWidth = width + PADDING * std::size_t{2};
81  const std::size_t paddedHeight = height + PADDING * std::size_t{2};
82 
83  Row* rowPointer = nullptr;
84  for (Row& row : rows) {
85  if (const float heightRatio = static_cast<float>(paddedHeight) / static_cast<float>(row.height);
86  heightRatio >= MINIMUM_ROW_HEIGHT_RATIO && heightRatio <= 1.0f && paddedWidth <= resolution - row.width) {
87  rowPointer = &row;
88  break;
89  }
90  }
91 
92  bool resized = false;
93  if (!rowPointer) {
94  const std::size_t newRowTop = (rows.empty()) ? std::size_t{0} : rows.back().top + rows.back().height;
95  const std::size_t newRowHeight = paddedHeight + paddedHeight / std::size_t{10};
96  while (resolution < newRowTop + newRowHeight || resolution < paddedWidth) {
97  resolution *= GROWTH_FACTOR;
98  resized = true;
99  }
100  rowPointer = &rows.emplace_back(newRowTop, paddedHeight);
101  }
102 
103  const std::size_t x = rowPointer->width + PADDING;
104  const std::size_t y = rowPointer->top + PADDING;
105 
106  rowPointer->width += paddedWidth;
107 
108  return InsertRectangleResult{x, y, resized};
109  }
110 
116  [[nodiscard]] std::size_t getResolution() const noexcept {
117  return resolution;
118  }
119 
120 private:
121  struct Row {
122  Row(std::size_t top, std::size_t height) noexcept
123  : top(top)
124  , height(height) {}
125 
126  std::size_t top;
127  std::size_t width = 0;
128  std::size_t height;
129  };
130 
131  std::vector<Row> rows{};
132  std::size_t resolution = INITIAL_RESOLUTION;
133 };
134 
135 } // namespace donut
136 
137 #endif
Axis-aligned rectangle packer for expandable square texture atlases.
Definition: AtlasPacker.hpp:18
static constexpr std::size_t INITIAL_RESOLUTION
The initial resolution that was passed to the InitialResolution template parameter.
Definition: AtlasPacker.hpp:24
static constexpr std::size_t GROWTH_FACTOR
The factor by which the resolution of the atlas will grow when it needs to make more space for a new ...
Definition: AtlasPacker.hpp:30
static constexpr float MINIMUM_ROW_HEIGHT_RATIO
The minimum ratio between the height of a new rectangle and the size of an existing row in the atlas ...
Definition: AtlasPacker.hpp:42
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
static constexpr std::size_t PADDING
The padding that was passed to the Padding template parameter.
Definition: AtlasPacker.hpp:35
std::size_t getResolution() const noexcept
Get the current required resolution of the atlas.
Definition: AtlasPacker.hpp:116
Definition: Application.hpp:9
Result of the insertRectangle() function.
Definition: AtlasPacker.hpp:47
std::size_t x
The horizontal offset, in pixels, from the left edge of the atlas where the new rectangle was inserte...
Definition: AtlasPacker.hpp:52
std::size_t y
The vertical offset, in pixels, from the bottom edge of the atlas where the new rectangle was inserte...
Definition: AtlasPacker.hpp:58
bool resized
Whether the atlas needed to grow in order to accommodate the new rectangle or not.
Definition: AtlasPacker.hpp:65