From ad0028a20860098e234b8d10bca25ac69c096e6e Mon Sep 17 00:00:00 2001 From: tobid7 Date: Fri, 24 Oct 2025 09:15:00 +0200 Subject: [PATCH] Initial Commit --- .gitignore | 3 + .gitmodules | 3 + CMakeLists.txt | 29 +++++++++ LICENSE | 21 +++++++ README.md | Bin 0 -> 378 bytes include/d7rc.hpp | 28 +++++++++ include/d7rc/atlas.hpp | 68 +++++++++++++++++++++ include/d7rc/file.hpp | 59 +++++++++++++++++++ source/lib/atlas.cpp | 131 +++++++++++++++++++++++++++++++++++++++++ source/lib/file.cpp | 129 ++++++++++++++++++++++++++++++++++++++++ source/tool/main.cpp | 48 +++++++++++++++ 11 files changed, 519 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 README.md create mode 100644 include/d7rc.hpp create mode 100644 include/d7rc/atlas.hpp create mode 100644 include/d7rc/file.hpp create mode 100644 source/lib/atlas.cpp create mode 100644 source/lib/file.cpp create mode 100644 source/tool/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d0f45cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +compile_commands.json +.vscode/settings.json \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c986e29 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "palladium"] + path = vendor/palladium + url = https://dev.npid7.de/tobid7/palladium diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c1c35d6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.22) + +project(d7rc VERSION 0.1.0) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED true) + +add_subdirectory(vendor/palladium) + +add_library(${PROJECT_NAME} + source/lib/file.cpp + source/lib/atlas.cpp +) + +target_include_directories(${PROJECT_NAME} PRIVATE palladium) +target_include_directories(${PROJECT_NAME} PUBLIC include) +target_link_libraries(${PROJECT_NAME} PRIVATE palladium) +target_compile_definitions(${PROJECT_NAME} PRIVATE +-DVERSION="${PROJECT_VERSION}" +-DDEBUG=1 +) + +add_executable(${PROJECT_NAME}-make + source/tool/main.cpp +) + +target_include_directories(${PROJECT_NAME}-make PRIVATE palladium include) +target_link_libraries(${PROJECT_NAME}-make PRIVATE d7rc palladium) +target_compile_definitions(${PROJECT_NAME}-make PRIVATE -DVERSION="${PROJECT_VERSION}") \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ac06ff0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d319970e0ac4e03b41dc34d839046c0d5678ca7f GIT binary patch literal 378 zcmZ9I%?<%U5QOV2@eU(y5@(5S;ng=5|^r;*O{{d{|%b)0DaF!(*z(Xg&hElEoUWs}8=pK`++Pdv7!6K*Ov} u{o?H}^uM#>4oa1eYG-p%q8PA}&DYfIFS{sCP`B-!hGch6Mn}$F_?b7`d^gPi literal 0 HcmV?d00001 diff --git a/include/d7rc.hpp b/include/d7rc.hpp new file mode 100644 index 0000000..6e893f7 --- /dev/null +++ b/include/d7rc.hpp @@ -0,0 +1,28 @@ +/* +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. +*/ + +#pragma once + +#include +#include \ No newline at end of file diff --git a/include/d7rc/atlas.hpp b/include/d7rc/atlas.hpp new file mode 100644 index 0000000..a6f872e --- /dev/null +++ b/include/d7rc/atlas.hpp @@ -0,0 +1,68 @@ +/* +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. +*/ + +#pragma once + +#include + +namespace D7RC { +class Atlas { + public: + Atlas(int size = 1024); + ~Atlas() {} + + /** + * Function that tries to find a free place and if found + * it will place the Image there and create a UV Entry + * + * @param img Uses Palladium Image Ref + */ + bool AppendImage(PD::Image::Ref img, const std::string& name); + void Pack(); + void LoadTextures(); + /** + * Get a Texture from the Atlas + * Warning: Palladium Backend needs to be init!!! + */ + PD::Li::Texture::Ref Get(const std::string& name); + + void pSplit(int i, PD::ivec4 pos); + + struct Entry { + PD::Li::Rect UV; + PD::ivec2 Size; + std::string Name; + PD::ivec2 iPos; // data that does not get written + PD::Image::Ref iImg; // Temp ref + PD::Li::Texture::Ref iTex; // tmp (on load) + }; + + std::vector Entries; + PD::ivec2 Size; + PD::ivec2 Max; + std::vector Space; + std::vector Img; + PD::Li::Texture::Ref pTex; +}; +} // namespace D7RC \ No newline at end of file diff --git a/include/d7rc/file.hpp b/include/d7rc/file.hpp new file mode 100644 index 0000000..ab61678 --- /dev/null +++ b/include/d7rc/file.hpp @@ -0,0 +1,59 @@ +/* +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. +*/ + +#pragma once + +#include +#include + +namespace D7RC { +class File { + public: + File() {} + ~File() {} + + void Write(Atlas& atlas, const std::string& path); + void pWriteTab(std::ofstream& off, Atlas& atlas); + size_t pCalcTabLen(Atlas& atlas); + void Read(Atlas& atlas, const std::string& path); + void pReadTab(std::ifstream& off, Atlas& atlas); + + struct Header { + PD::u32 Magic; + PD::u16 Width; + PD::u16 Height; + PD::u32 DataSize; + PD::u32 TabLen; + PD::u8 Format; + PD::u8 pUnk; + PD::u16 NumEntries; + + static Header New(); + void Write(std::ofstream& off); + void Read(std::ifstream& iff); + }; + + Header hdr; +}; +} // namespace D7RC \ No newline at end of file diff --git a/source/lib/atlas.cpp b/source/lib/atlas.cpp new file mode 100644 index 0000000..2c369ac --- /dev/null +++ b/source/lib/atlas.cpp @@ -0,0 +1,131 @@ +/* +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 + +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()); + e.UV = PD::fvec4((float)it.x / (float)Size.x, (float)it.y / (float)Size.y, + (float)(it.x + img->Width()) / (float)Size.x, + (float)(it.x + img->Height()) / (float)Size.y); + 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); + } + 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; +} +} // namespace D7RC \ No newline at end of file diff --git a/source/lib/file.cpp b/source/lib/file.cpp new file mode 100644 index 0000000..b67149c --- /dev/null +++ b/source/lib/file.cpp @@ -0,0 +1,129 @@ +/* +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 + +namespace D7RC { +File::Header File::Header::New() { + Header ret; + ret.Magic = 0; + ret.Width = 0; + ret.Height = 0; + ret.DataSize = 0; + ret.TabLen = 0; + ret.Format = 0; + ret.pUnk = 0; + ret.NumEntries = 0; + return ret; +} + +void File::Header::Write(std::ofstream& off) { + off.write(reinterpret_cast(&Magic), sizeof(Magic)); + off.write(reinterpret_cast(&Width), sizeof(Width)); + off.write(reinterpret_cast(&Height), sizeof(Height)); + off.write(reinterpret_cast(&DataSize), sizeof(DataSize)); + off.write(reinterpret_cast(&TabLen), sizeof(TabLen)); + off.write(reinterpret_cast(&Format), sizeof(Format)); + off.write(reinterpret_cast(&pUnk), sizeof(pUnk)); + off.write(reinterpret_cast(&NumEntries), sizeof(NumEntries)); +} + +void File::Header::Read(std::ifstream& iff) { + iff.read(reinterpret_cast(&Magic), sizeof(Magic)); + iff.read(reinterpret_cast(&Width), sizeof(Width)); + iff.read(reinterpret_cast(&Height), sizeof(Height)); + iff.read(reinterpret_cast(&DataSize), sizeof(DataSize)); + iff.read(reinterpret_cast(&TabLen), sizeof(TabLen)); + iff.read(reinterpret_cast(&Format), sizeof(Format)); + iff.read(reinterpret_cast(&pUnk), sizeof(pUnk)); + iff.read(reinterpret_cast(&NumEntries), sizeof(NumEntries)); +} + +void File::Write(Atlas& atlas, const std::string& path) { + hdr = Header::New(); + hdr.Magic = 0x43523744; + hdr.Width = atlas.Size.x; + hdr.Height = atlas.Size.y; + hdr.DataSize = atlas.Img.size(); + hdr.TabLen = pCalcTabLen(atlas); + hdr.Format = 0; + hdr.NumEntries = atlas.Entries.size(); + std::ofstream off(path, std::ios::out | std::ios::binary); + hdr.Write(off); + pWriteTab(off, atlas); + off.write(reinterpret_cast(atlas.Img.data()), atlas.Img.size()); + off.close(); +} + +size_t File::pCalcTabLen(Atlas& atlas) { + size_t ret = 0; + size_t s_uv = sizeof(PD::Li::Rect); + size_t s_size = sizeof(PD::u16) * 2; + for (auto& it : atlas.Entries) { + ret += s_uv + s_size + sizeof(PD::u8) + it.Name.size(); + } + return ret; +} + +void File::pWriteTab(std::ofstream& off, Atlas& atlas) { + for (auto& it : atlas.Entries) { + if (it.Name.size() > 255) { + continue; + } + off.write(reinterpret_cast(&it.UV), sizeof(it.UV)); + PD::u16 w = it.Size.x; + PD::u16 h = it.Size.y; + off.write(reinterpret_cast(&w), sizeof(w)); + off.write(reinterpret_cast(&h), sizeof(h)); + PD::u32 size = (PD::u32)it.Name.size(); + off.write(reinterpret_cast(&size), sizeof(PD::u32)); + off.write(it.Name.data(), it.Name.size()); + } +} + +void File::Read(Atlas& atlas, const std::string& path) { + std::ifstream iff(path, std::ios::in | std::ios::binary); + hdr.Read(iff); + pReadTab(iff, atlas); + atlas.Img.resize(hdr.DataSize); + iff.read(reinterpret_cast(atlas.Img.data()), hdr.DataSize); + iff.close(); +} + +void File::pReadTab(std::ifstream& iff, Atlas& atlas) { + for (PD::u32 i = 0; i < hdr.NumEntries; i++) { + Atlas::Entry e; + PD::u16 w = 0, h = 0; + PD::u32 namelen = 0; + iff.read(reinterpret_cast(&e.UV), sizeof(e.UV)); + iff.read(reinterpret_cast(&w), sizeof(w)); + iff.read(reinterpret_cast(&w), sizeof(h)); + iff.read(reinterpret_cast(&namelen), sizeof(PD::u32)); + e.Name.resize(namelen); + iff.read(e.Name.data(), namelen); + e.Size = PD::ivec2(w, h); + } +} + +} // namespace D7RC \ No newline at end of file diff --git a/source/tool/main.cpp b/source/tool/main.cpp new file mode 100644 index 0000000..31c3445 --- /dev/null +++ b/source/tool/main.cpp @@ -0,0 +1,48 @@ +/* +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 + +#ifndef VERSION +#define VERSION "" +#endif + +int main() { + std::cout << "d7rc-make v" VERSION << std::endl; + int szs = 1024; + D7RC::Atlas a(szs); + std::map imgs; + for (auto& it : std::filesystem::directory_iterator("images")) { + auto img = PD::Image::New(it.path().string()); + PD::Image::Convert(img, PD::Image::RGBA); + imgs[img] = it.path().filename().string(); + } + for (auto& it : imgs) { + a.AppendImage(it.first, it.second); + } + a.Pack(); + D7RC::File f; + f.Write(a, "test.d7rc"); + return 0; +} \ No newline at end of file