From d311a65e0c86aaec94f6d9a61d5f9b374f90aa5c Mon Sep 17 00:00:00 2001 From: LaG1924 <12997935+LaG1924@users.noreply.github.com> Date: Sat, 4 Aug 2018 19:57:19 +0500 Subject: Implemented texture atlas --- src/AssetManager.cpp | 15 +++++++- src/AssetManager.hpp | 15 +++++++- src/TextureAtlas.cpp | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/TextureAtlas.hpp | 35 +++++++++++++++++ 4 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 src/TextureAtlas.cpp create mode 100644 src/TextureAtlas.hpp diff --git a/src/AssetManager.cpp b/src/AssetManager.cpp index 25e9f24..9e360c0 100644 --- a/src/AssetManager.cpp +++ b/src/AssetManager.cpp @@ -603,7 +603,20 @@ AssetTreeNode * AssetManager::GetAssetByAssetName(const std::string & assetName) } void AssetManager::LoadTextures() { - + std::vector textureData; + size_t id = 0; + RecursiveWalkAsset("/minecraft/textures/", [&](AssetTreeNode &node) { + TextureData data; + AssetTexture *textureAsset = dynamic_cast(node.asset.get()); + if (!textureAsset) + return; + data.data = std::move(textureAsset->textureData); + data.width = textureAsset->realWidth; + data.height = textureAsset->realHeight; + textureData.push_back(data); + textureAsset->id = id++; + }); + atlas = std::make_unique(textureData); } void AssetManager::ParseAssetTexture(AssetTreeNode &node) { diff --git a/src/AssetManager.hpp b/src/AssetManager.hpp index b8748ae..1b64215 100644 --- a/src/AssetManager.hpp +++ b/src/AssetManager.hpp @@ -11,6 +11,7 @@ #include "Vector.hpp" #include "Block.hpp" +#include "TextureAtlas.hpp" class Texture; @@ -160,8 +161,8 @@ struct AssetBlockModel : Asset { struct AssetTexture : Asset { std::vector textureData; - double x, y, w, h; unsigned int realWidth, realHeight; + size_t id; }; class AssetManager { @@ -171,6 +172,7 @@ class AssetManager { std::map textureAtlasIndexes; std::map blockIdToBlockName; std::unique_ptr assetTree; + std::unique_ptr atlas; public: AssetManager(); @@ -219,4 +221,15 @@ public: void LoadTextures(); void ParseAssetTexture(AssetTreeNode &node); + + inline GLuint GetTextureAtlasId() { + return atlas->GetRawTextureId(); + } + + inline TextureCoord GetTexture(const std::string assetName) { + AssetTexture *asset = GetAsset(assetName); + if (!asset) + return {}; + return atlas->GetTexture(asset->id); + } }; diff --git a/src/TextureAtlas.cpp b/src/TextureAtlas.cpp new file mode 100644 index 0000000..23ab92d --- /dev/null +++ b/src/TextureAtlas.cpp @@ -0,0 +1,104 @@ +#include "TextureAtlas.hpp" + +#define STB_RECT_PACK_IMPLEMENTATION + +#include +#include + +#include "Utility.hpp" + +TextureAtlas::TextureAtlas(std::vector &textures) { + LOG(INFO) << "Initializing texture atlas..."; + LOG(INFO) << "Textures count: " << textures.size(); + + //Texture packing + const int textureSize = 1024; + + std::vector totalRects; + for (int i = 0; i < textures.size(); i++) { + stbrp_rect rect; + rect.id = i; + rect.x = 0; + rect.y = 0; + rect.w = textures[i].width; + rect.h = textures[i].height; + rect.was_packed = 0; + totalRects.push_back(rect); + } + + textureCoords.resize(textures.size()); + + int layer = 0; + for (;;layer++) { + stbrp_context context; + std::vector nodes; + int nodesCount = textureSize * 2; + nodes.resize(nodesCount); + stbrp_init_target(&context, textureSize, textureSize, nodes.data(), nodesCount); + + std::vector rects; + for (const auto &it : totalRects) { + if (it.was_packed != 0) + continue; + + rects.push_back(it); + } + stbrp_pack_rects(&context, rects.data(), rects.size()); + + int unpackedTextures = 0; + for (auto &it : rects) { + if (!it.was_packed) { + unpackedTextures++; + continue; + } + textureCoords[it.id].pixelX = it.x; + textureCoords[it.id].pixelY = it.y; + textureCoords[it.id].pixelW = it.w; + textureCoords[it.id].pixelH = it.h; + textureCoords[it.id].layer = layer; + textureCoords[it.id].x = (double)it.x / textureSize; + textureCoords[it.id].y = (double)it.y / textureSize; + textureCoords[it.id].w = (double)it.w / textureSize; + textureCoords[it.id].h = (double)it.h / textureSize; + totalRects[it.id].was_packed = 1; + } + if (unpackedTextures == 0) + break; + } + LOG(INFO) << "Texture atlas size is " << textureSize << "x" << textureSize << "x" << layer; + + //OpenGL + int mipLevelCount = 1; + + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D_ARRAY, texture); + glTexStorage3D(GL_TEXTURE_2D_ARRAY, mipLevelCount, GL_RGBA8, textureSize, textureSize, layer+1); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glCheckError(); + + //Uploading texture data + for (int i = 0; i < textureCoords.size(); i++) { + size_t bytesPerLine = textureCoords[i].pixelW * 4; + for (int y = 0; y < textureCoords[i].pixelH / 2; y++) { + int invY = textureCoords[i].pixelH - y - 1; + unsigned char *src = textures[i].data.data() + y * bytesPerLine; + unsigned char *dst = textures[i].data.data() + invY * bytesPerLine; + for (int j = 0; j < bytesPerLine; j++) { + std::swap(*(src + j), *(dst + j)); + } + } + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, textureCoords[i].pixelX, textureSize - textureCoords[i].pixelY - textureCoords[i].pixelH, textureCoords[i].layer, + textureCoords[i].pixelW, textureCoords[i].pixelH, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, textures[i].data.data()); + glCheckError(); + } + + LOG(INFO) << "Texture atlas initialized"; +} + +TextureAtlas::~TextureAtlas() { + glDeleteTextures(1, &texture); +} diff --git a/src/TextureAtlas.hpp b/src/TextureAtlas.hpp new file mode 100644 index 0000000..76a6c49 --- /dev/null +++ b/src/TextureAtlas.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include + +struct TextureData { + std::vector data; //expected format RGBA8888 + int width, height; +}; + +struct TextureCoord { + double x, y, w, h; + int pixelX, pixelY, pixelW, pixelH; + size_t layer; +}; + +class TextureAtlas { + GLuint texture; + std::vector textureCoords; +public: + TextureAtlas(std::vector &textures); + + TextureAtlas(const TextureAtlas &) = delete; + + ~TextureAtlas(); + + inline GLuint GetRawTextureId() { + return texture; + } + + TextureCoord GetTexture(int id) { + return textureCoords[id]; + } +}; \ No newline at end of file -- cgit v1.2.3