2025-10-24 09:15:00 +02:00
|
|
|
/*
|
|
|
|
|
MIT License
|
|
|
|
|
|
|
|
|
|
Copyright (c) 2025 tobid7
|
|
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
|
|
|
copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
|
SOFTWARE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <d7rc/atlas.hpp>
|
|
|
|
|
|
|
|
|
|
namespace D7RC {
|
|
|
|
|
Atlas::Atlas(int size) {
|
|
|
|
|
Size = PD::ivec2(size, size);
|
|
|
|
|
Space.push_back(PD::ivec4(0, 0, size, size));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Atlas::AppendImage(PD::Image::Ref img, const std::string& name) {
|
|
|
|
|
for (size_t i = 0; i < Space.size(); i++) {
|
|
|
|
|
auto& it = Space[i];
|
|
|
|
|
PD::ivec2 size = PD::ivec2(it.z, it.w);
|
|
|
|
|
if (img->Width() <= size.x && img->Height() <= size.y) {
|
|
|
|
|
Entry e;
|
|
|
|
|
e.Name = name;
|
|
|
|
|
e.iImg = img;
|
|
|
|
|
e.iPos = PD::ivec2(it.x, it.y);
|
|
|
|
|
e.Size = PD::ivec2(img->Width(), img->Height());
|
|
|
|
|
Entries.push_back(e);
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
std::cout << std::format("Created Image {} ({})\n", name,
|
|
|
|
|
PD::ivec4(e.iPos, e.Size));
|
|
|
|
|
#endif
|
|
|
|
|
if (e.iPos.x + e.Size.x > Max.x) {
|
|
|
|
|
Max.x = e.iPos.x + e.Size.x;
|
|
|
|
|
}
|
|
|
|
|
if (e.iPos.y + e.Size.y > Max.y) {
|
|
|
|
|
Max.y = e.iPos.y + e.Size.y;
|
|
|
|
|
}
|
|
|
|
|
pSplit(i, PD::ivec4(it.x, it.y, img->Width(), img->Height()));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* TODO: Need a smarter splitting algo
|
|
|
|
|
* Does not really find free spaces yet
|
|
|
|
|
*/
|
|
|
|
|
void Atlas::pSplit(int i, PD::ivec4 area) {
|
|
|
|
|
auto s = Space[i];
|
|
|
|
|
Space.erase(Space.begin() + i);
|
|
|
|
|
|
|
|
|
|
if (area.x + area.z < s.x + s.z)
|
|
|
|
|
Space.emplace_back(area.x + area.z, s.y, (s.x + s.z) - (area.x + area.z),
|
|
|
|
|
area.w);
|
|
|
|
|
if (area.y + area.w < s.y + s.w)
|
|
|
|
|
Space.emplace_back(s.x, area.y + area.w, s.z,
|
|
|
|
|
(s.y + s.w) - (area.y + area.w));
|
|
|
|
|
std::sort(Space.begin(), Space.end(),
|
|
|
|
|
[](PD::ivec4& a, PD::ivec4& b) { return a.y < b.y && a.x < b.x; });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Atlas::Pack() {
|
|
|
|
|
if (!PD::BitUtil::IsSingleBit(Max.x)) {
|
|
|
|
|
Size.x = PD::BitUtil::GetPow2(Max.x);
|
|
|
|
|
}
|
|
|
|
|
if (!PD::BitUtil::IsSingleBit(Max.y)) {
|
|
|
|
|
Size.y = PD::BitUtil::GetPow2(Max.y);
|
|
|
|
|
}
|
2025-10-25 17:37:04 +02:00
|
|
|
pCreateUVs();
|
2025-10-24 09:15:00 +02:00
|
|
|
Img.resize(Size.x * Size.y * 4);
|
|
|
|
|
for (auto& it : Entries) {
|
|
|
|
|
for (int i = 0; i < it.Size.x; i++) {
|
|
|
|
|
for (int j = 0; j < it.Size.y; j++) {
|
|
|
|
|
Img[((j + it.iPos.y) * Size.x + (it.iPos.x + i)) * 4] =
|
|
|
|
|
it.iImg->pBuffer[(j * it.iImg->pWidth + i) * 4];
|
|
|
|
|
Img[((j + it.iPos.y) * Size.x + (it.iPos.x + i)) * 4 + 1] =
|
|
|
|
|
it.iImg->pBuffer[(j * it.iImg->pWidth + i) * 4 + 1];
|
|
|
|
|
Img[((j + it.iPos.y) * Size.x + (it.iPos.x + i)) * 4 + 2] =
|
|
|
|
|
it.iImg->pBuffer[(j * it.iImg->pWidth + i) * 4 + 2];
|
|
|
|
|
Img[((j + it.iPos.y) * Size.x + (it.iPos.x + i)) * 4 + 3] =
|
|
|
|
|
it.iImg->pBuffer[(j * it.iImg->pWidth + i) * 4 + 3];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
std::cout << std::format("Created Texture ({})\n", Size);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Atlas::LoadTextures() {
|
|
|
|
|
pTex = PD::Gfx::LoadTex(Img, Size.x, Size.y);
|
|
|
|
|
for (auto& it : Entries) {
|
|
|
|
|
it.iTex = PD::Li::Texture::New();
|
|
|
|
|
it.iTex->Address = pTex->Address;
|
|
|
|
|
it.iTex->Size = it.Size;
|
|
|
|
|
it.iTex->UV = it.UV;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PD::Li::Texture::Ref Atlas::Get(const std::string& name) {
|
|
|
|
|
if (!pTex) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
for (auto& it : Entries) {
|
|
|
|
|
if (it.Name == name) {
|
|
|
|
|
return it.iTex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2025-10-25 17:37:04 +02:00
|
|
|
|
|
|
|
|
void Atlas::pCreateUVs() {
|
|
|
|
|
for (auto& e : Entries) {
|
|
|
|
|
e.UV = PD::fvec4((float)e.iPos.x / (float)Size.x,
|
|
|
|
|
1.f - (float)e.iPos.y / (float)Size.y,
|
|
|
|
|
(float)(e.iPos.x + e.Size.x) / (float)Size.x,
|
|
|
|
|
1.f - (float)(e.iPos.y + e.Size.y) / (float)Size.y);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-24 09:15:00 +02:00
|
|
|
} // namespace D7RC
|