Botcraft 1.21.4
Loading...
Searching...
No Matches
Atlas.cpp
Go to the documentation of this file.
3
5
6#define STB_IMAGE_IMPLEMENTATION
7#define STBI_ONLY_PNG
8#include <stb_image/stb_image.h>
9
10// rectpack2D
11#include "finders_interface.h"
12
13#include <fstream>
14#include <string>
15
16namespace Botcraft
17{
18 namespace Renderer
19 {
20 // A basic struct to hold a named image data
21 struct Texture
22 {
23 int width;
24 int height;
25 int depth;
26 std::string identifier;
28 std::vector<unsigned char> data;
29
30 const unsigned char Get(const int x, const int y, const int c) const
31 {
32 return data[(y * width + x) * depth + c];
33 }
34 };
35
37 {
38 height = 0;
39 width = 0;
40 }
41
43 {
44
45 }
46
47 void Atlas::Reset(const int height_, const int width_)
48 {
49 height = height_;
50 width = width_;
51 data = std::vector<unsigned char>(height * width * 4, 0);
52 textures_map.clear();
53
54 //Fill with "undefined" texture
55 for (int row = 0; row < height; ++row)
56 {
57 for (int col = 0; col < width; ++col)
58 {
59 unsigned char* pixel_pointer = Get(row, col);
60
61 bool is_magenta = !((row % 16 < 8 && col % 16 < 8) || (row % 16 >= 8 && col % 16 >= 8));
62
63 *(pixel_pointer + 0) = is_magenta ? 255 : 0;
64 *(pixel_pointer + 1) = 0;
65 *(pixel_pointer + 2) = is_magenta ? 255 : 0;
66 *(pixel_pointer + 3) = 255;
67 }
68 }
69 }
70
71 void Atlas::LoadData(const std::vector<std::pair<std::string, std::string> >& textures_path_names)
72 {
73 if (textures_path_names.size() == 0)
74 {
75 Reset(16, 16);
76 return;
77 }
78
79 std::vector<Texture> textures;
80 textures.reserve(textures_path_names.size());
81
82 for (int i = 0; i < textures_path_names.size(); ++i)
83 {
84 if (textures_path_names[i].first.empty())
85 {
86 continue;
87 }
88 Texture tex;
89 unsigned char* data = stbi_load(textures_path_names[i].first.c_str(), &tex.width, &tex.height, &tex.depth, 0);
90 if (data != nullptr)
91 {
92 tex.data = std::vector<unsigned char>(data, data + tex.width * tex.height * tex.depth);
93 tex.identifier = textures_path_names[i].second;
94 std::ifstream animation_file((textures_path_names[i].first + ".mcmeta").c_str());
95 tex.animated = animation_file.good();
96
97 textures.push_back(tex);
98 }
99 stbi_image_free(data);
100 }
101
102
103 // Copy the textures with correct size (crop if it's an animated texture)
104 std::vector<Texture> kept_textures;
105 kept_textures.reserve(textures.size());
106 for (int i = 0; i < textures.size(); ++i)
107 {
108 // If the depth is not good, don't keep the texture
109 if (textures[i].depth < 1 || textures[i].depth > 4)
110 {
111 LOG_WARNING("Unknown depth format(" << textures[i].depth << ") for texture " << textures[i].identifier);
112 continue;
113 }
114 kept_textures.push_back(textures[i]);
115 }
116
117 //Compute atlas dimensions using rectpack2D
118 using spaces_type = rectpack2D::empty_spaces<false>;
119 using rect_type = rectpack2D::output_rect_t<spaces_type>;
120
121 std::vector<rect_type> rectangles;
122 rectangles.emplace_back(rectpack2D::rect_xywh(0, 0, 16, 16));
123 for (int i = 0; i < kept_textures.size(); ++i)
124 {
125 rectangles.emplace_back(rectpack2D::rect_xywh(0, 0, kept_textures[i].width, kept_textures[i].height));
126 }
127
128 const auto result_size = rectpack2D::find_best_packing<spaces_type>(
129 rectangles,
130 make_finder_input(
131 10000,
132 1,
133 [](rect_type&) {
134 return rectpack2D::callback_result::CONTINUE_PACKING;
135 },
136 [](rect_type&) {
137 LOG_ERROR("Error packing the textures, aborting");
138 return rectpack2D::callback_result::ABORT_PACKING;
139 },
140 rectpack2D::flipping_option::DISABLED
141 )
142 );
143
144 LOG_INFO("All textures packed, resultant atlas size: " << result_size.h << "x" << result_size.w);
145
146 //Compute the global texture image
147 Reset(result_size.h, result_size.w);
148
149 //Set the default texture
151 { rectangles[0].w, rectangles[0].h }, // size
152 { rectangles[0].x, rectangles[0].y }, // position
155 };
156
157 for (int i = 0; i < kept_textures.size(); ++i)
158 {
159 const rect_type& rectangle = rectangles[i + 1];
160
161 textures_map[kept_textures[i].identifier] = TextureData{
162 { rectangle.w, rectangle.h }, // size
163 { rectangle.x, rectangle.y }, // position
165 kept_textures[i].animated ? Animation::Animated : Animation::Static
166 };
167
168 for (int row = 0; row < rectangle.h; ++row)
169 {
170 for (int col = 0; col < rectangle.w; ++col)
171 {
172 int r = 0;
173 int g = 0;
174 int b = 0;
175 int a = 0;
176
177 switch (kept_textures[i].depth)
178 {
179 case 1: //Grayscale image
180 r = kept_textures[i].Get(col, row, 0);
181 g = kept_textures[i].Get(col, row, 0);
182 b = kept_textures[i].Get(col, row, 0);
183 a = 255;
184 break;
185 case 2: //Grayscale + Alpha image
186 r = kept_textures[i].Get(col, row, 0);
187 g = kept_textures[i].Get(col, row, 0);
188 b = kept_textures[i].Get(col, row, 0);
189 a = kept_textures[i].Get(col, row, 1);
190 break;
191 case 3: //RGB image
192 r = kept_textures[i].Get(col, row, 0);
193 g = kept_textures[i].Get(col, row, 1);
194 b = kept_textures[i].Get(col, row, 2);
195 a = 255;
196 break;
197 case 4: //RGBA image
198 r = kept_textures[i].Get(col, row, 0);
199 g = kept_textures[i].Get(col, row, 1);
200 b = kept_textures[i].Get(col, row, 2);
201 a = kept_textures[i].Get(col, row, 3);
202 break;
203 }
204
205 *(Get(rectangle.y + row, rectangle.x + col, 0)) = r;
206 *(Get(rectangle.y + row, rectangle.x + col, 1)) = g;
207 *(Get(rectangle.y + row, rectangle.x + col, 2)) = b;
208 *(Get(rectangle.y + row, rectangle.x + col, 3)) = a;
209
210 TextureData& data = textures_map[kept_textures[i].identifier];
211 if (a == 0 && data.transparency != Transparency::Partial)
212 {
214 }
215 else if (a < 255)
216 {
217 data.transparency = Transparency::Partial;
218 }
219 }
220 }
221 }
222
223 //In case we want to save the atlas to check the data
224 //WriteImage("atlas.png", height, width, 4, data.data(), false);
225 }
226
227 int Atlas::GetWidth() const
228 {
229 return width;
230 }
231
233 {
234 return height;
235 }
236
237 const TextureData& Atlas::GetData(const std::string& name) const
238 {
239 auto it = textures_map.find(name);
240 if (it != textures_map.end())
241 {
242 return it->second;
243 }
244
245 return textures_map.at("");
246 }
247
248 const unsigned char* Atlas::Get(const int row, const int col, const int depth) const
249 {
250 return data.data() + ((row * width + col) * 4 + depth);
251 }
252
253 unsigned char* Atlas::Get(const int row, const int col, const int depth)
254 {
255 return data.data() + ((row * width + col) * 4 + depth);
256 }
257 } // Renderer
258} // Botcraft
#define LOG_ERROR(osstream)
Definition Logger.hpp:45
#define LOG_WARNING(osstream)
Definition Logger.hpp:44
#define LOG_INFO(osstream)
Definition Logger.hpp:43
const unsigned char * Get(const int row=0, const int col=0, const int depth=0) const
Definition Atlas.cpp:248
std::vector< unsigned char > data
Definition Atlas.hpp:44
void Reset(const int height_, const int width_)
Definition Atlas.cpp:47
std::unordered_map< std::string, TextureData > textures_map
Definition Atlas.hpp:45
void LoadData(const std::vector< std::pair< std::string, std::string > > &textures_path)
Definition Atlas.cpp:71
const TextureData & GetData(const std::string &name) const
Definition Atlas.cpp:237
const unsigned char Get(const int x, const int y, const int c) const
Definition Atlas.cpp:30
std::vector< unsigned char > data
Definition Atlas.cpp:28