// IAEngine: 2D Game Engine by IA // Copyright (C) 2025 IASoft (PVT) LTD (oss@iasoft.dev) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include #include #include #include #include #define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_RESIZE_IMPLEMENTATION #include #include namespace ia::iae { Map ResourceManager::s_images; Map ResourceManager::s_sounds; Map> ResourceManager::s_tileSets; Vector ResourceManager::s_imageHandles; VOID ResourceManager::Initialize() { { // Set Texture Handle 0 to a pure white image const auto data = new UINT8[100 * 100 * 4]; ia_memset(data, 0xFF, 100 * 100 * 4); CreateImage(Engine::GetUniqueResourceName(), data, 100, 100); delete[] data; } } VOID ResourceManager::Terminate() { for (const auto &t : s_images) GPUResourceManager::DestroyTexture(s_imageHandles[t->Value].Handle); for (const auto &t : s_sounds) AudioManager::DestoryAudio(t->Value); } Handle ResourceManager::CreateImage(IN CONST String &name, IN PCUINT8 encodedData, IN SIZE_T encodedDataSize) { INT32 w, h, n; const auto rgbaData = stbi_load_from_memory(encodedData, encodedDataSize, &w, &h, &n, STBI_rgb_alpha); const auto result = CreateImage(name, rgbaData, w, h); STBI_FREE(rgbaData); return result; } Handle ResourceManager::CreateImage(IN CONST String &name, IN PCUINT8 rgbaData, IN INT32 width, IN INT32 height, IN INT32 stride) { const auto texture = GPUResourceManager::CreateTexture( SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET, width, height, (stride == -1) ? width : stride, rgbaData, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, true); s_imageHandles.pushBack(ImageResource{.OriginalWidth = width, .OriginalHeight = height, .OriginalPixelData = new UINT8[width * height * 4], .Width = width, .Height = height, .Handle = texture}); ia_memcpy(s_imageHandles.back().OriginalPixelData, rgbaData, width * height * 4); Handle handle = s_imageHandles.size() - 1; s_images[name] = handle; return handle; } Handle ResourceManager::CreateSound(IN CONST String &name, IN PCUINT8 encodedData, IN SIZE_T encodedDataSize) { const auto handle = AudioManager::CreateAudio(encodedData, encodedDataSize); s_sounds[name] = handle; return handle; } Handle ResourceManager::GetImage(IN CONST String &name) { return s_images[name]; } Handle ResourceManager::GetSound(IN CONST String &name) { return s_sounds[name]; } String ResourceManager::GetImageName(IN Handle handle) { for (const auto &t : s_images) { if (handle == t->Value) return t->Key; } THROW_NO_SUCH_KEY(); return ""; } String ResourceManager::GetSoundName(IN Handle handle) { for (const auto &t : s_sounds) { if (handle == t->Value) return t->Key; } THROW_NO_SUCH_KEY(); return ""; } VOID ResourceManager::DestroyImage(IN Handle image) { GPUResourceManager::DestroyTexture(s_imageHandles[image].Handle); s_imageHandles[image] = {}; } VOID ResourceManager::DestroySound(IN Handle sound) { AudioManager::DestoryAudio(sound); } IVec2 ResourceManager::GetImageExtent(IN Handle image) { const auto t = s_imageHandles[image]; return {t.Width, t.Height}; } IVec2 ResourceManager::GetImageOriginalExtent(IN Handle image) { const auto t = s_imageHandles[image]; return {t.OriginalWidth, t.OriginalHeight}; } Handle ResourceManager::RescaleImage(IN Handle image, IN Vec2 factor, IN BOOL makeOriginal) { if (!s_imageHandles[image].OriginalPixelData) return image; const auto newWidth = (INT32) (s_imageHandles[image].OriginalWidth * factor.x); const auto newHeight = (INT32) (s_imageHandles[image].OriginalHeight * factor.y); if (!newWidth || !newHeight) return image; const auto newPixelData = stbir_resize_uint8_linear(s_imageHandles[image].OriginalPixelData, s_imageHandles[image].OriginalWidth, s_imageHandles[image].OriginalHeight, s_imageHandles[image].OriginalWidth * 4, nullptr, newWidth, newHeight, newWidth * 4, stbir_pixel_layout::STBIR_RGBA); const auto texture = GPUResourceManager::CreateTexture( SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET, newWidth, newHeight, newWidth, newPixelData, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, true); GPUResourceManager::DestroyTexture(s_imageHandles[image].Handle); s_imageHandles[image].Handle = texture; s_imageHandles[image].Width = newWidth; s_imageHandles[image].Height = newHeight; if (!makeOriginal) { free(newPixelData); return image; } free(s_imageHandles[image].OriginalPixelData); s_imageHandles[image].OriginalPixelData = newPixelData; s_imageHandles[image].OriginalWidth = newWidth; s_imageHandles[image].OriginalHeight = newHeight; return image; } Handle ResourceManager::CombineImages(IN CONST Vector &images, IN INT32 unitWidth, IN INT32 unitHeight, IN INT32 unitCountX, IN INT32 unitCountY) { const auto w = unitWidth * unitCountX; const auto h = unitHeight * unitCountY; Vector textures; for (const auto &t : images) textures.pushBack(s_imageHandles[t].Handle); const auto texture = GPUResourceManager::CombineTextures(textures.data(), unitWidth, unitHeight, unitCountX, unitCountY); s_imageHandles.pushBack(ImageResource{.OriginalWidth = w, .OriginalHeight = h, .OriginalPixelData = nullptr, .Width = w, .Height = h, .Handle = texture}); Handle handle = s_imageHandles.size() - 1; s_images[Engine::GetUniqueResourceName()] = handle; return handle; } VOID ResourceManager::RescaleAllImages(IN Vec2 factor) { for (auto &t : s_images) t->Value = RescaleImage(t->Value, factor); } Handle ResourceManager::CreateTileSet(IN CONST String &name, IN Handle image, IN INT32 tileWidth, IN INT32 tileHeight) { Vector tileImages; const auto extent = GetImageExtent(image); for (INT32 y = 0; y < extent.y; y += tileHeight) { for (INT32 x = 0; x < extent.x; x += tileWidth) { const auto w = s_imageHandles[image].OriginalWidth; tileImages.pushBack(CreateImage(BuildString(name, "_Tile", x, y), &s_imageHandles[image].OriginalPixelData[(x + y * w) * 4], tileWidth, tileHeight, w)); } } return CreateTileSet(name, tileImages, tileWidth, tileHeight); } Handle ResourceManager::CreateTileSet(IN CONST String &name, IN CONST Vector &images, IN INT32 tileWidth, IN INT32 tileHeight) { auto result = MakeRefPtr(images, tileWidth, tileHeight); s_tileSets[name] = result; return (Handle) (result.get()); } Handle ResourceManager::GetTileSet(IN CONST String &name) { return (Handle) (s_tileSets[name].get()); } VOID ResourceManager::DestroyTileSet(IN CONST String &name) { s_tileSets[name].reset(); } } // namespace ia::iae namespace ia::iae { Handle Engine::CreateImage(IN CONST String &name, IN PCUINT8 encodedData, IN SIZE_T encodedDataSize) { return ResourceManager::CreateImage(name, encodedData, encodedDataSize); } Handle Engine::CreateImage(IN CONST String &name, IN PCUINT8 rgbaData, IN INT32 width, IN INT32 height) { return ResourceManager::CreateImage(name, rgbaData, width, height); } Handle Engine::CreateSound(IN CONST String &name, IN PCUINT8 encodedData, IN SIZE_T encodedDataSize) { return ResourceManager::CreateSound(name, encodedData, encodedDataSize); } Handle Engine::GetImage(IN CONST String &name) { return ResourceManager::GetImage(name); } Handle Engine::GetSound(IN CONST String &name) { return ResourceManager::GetSound(name); } VOID Engine::DestroyImage(IN Handle image) { ResourceManager::DestroyImage(image); } VOID Engine::DestroySound(IN Handle sound) { ResourceManager::DestroySound(sound); } IVec2 Engine::GetImageExtent(IN Handle image) { return ResourceManager::GetImageExtent(image); } IVec2 Engine::GetImageOriginalExtent(IN Handle image) { return ResourceManager::GetImageOriginalExtent(image); } Handle Engine::RescaleImage(IN Handle image, IN Vec2 factor) { return ResourceManager::RescaleImage(image, factor); } VOID Engine::RescaleAllImages(IN Vec2 factor) { ResourceManager::RescaleAllImages(factor); } Handle Engine::CombineImages(IN CONST Vector &images, IN INT32 unitWidth, IN INT32 unitHeight, IN INT32 unitCountX, IN INT32 unitCountY) { return ResourceManager::CombineImages(images, unitWidth, unitHeight, unitCountX, unitCountY); } Handle Engine::CreateTileSet(IN CONST String &name, IN Handle image, IN INT32 tileWidth, IN INT32 tileHeight) { return ResourceManager::CreateTileSet(name, image, tileWidth, tileHeight); } Handle Engine::CreateTileSet(IN CONST String &name, IN CONST Vector &images, IN INT32 tileWidth, IN INT32 tileHeight) { return ResourceManager::CreateTileSet(name, images, tileWidth, tileHeight); } Handle Engine::GetTileSet(IN CONST String &name) { return ResourceManager::GetTileSet(name); } VOID Engine::DestroyTileSet(IN CONST String &name) { ResourceManager::DestroyTileSet(name); } } // namespace ia::iae