summaryrefslogtreecommitdiffstats
path: root/src/TextureAtlas.cpp
blob: 406418f7171488aecc117fbe509f822f183dc968 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include "TextureAtlas.hpp"

#define STB_RECT_PACK_IMPLEMENTATION

#include <stb_rect_pack.h>
#include <easylogging++.h>

#include "Utility.hpp"

TextureAtlas::TextureAtlas(std::vector<TextureData> &textures) {
	LOG(INFO) << "Initializing texture atlas...";
	LOG(INFO) << "Textures count: " << textures.size();

	//Texture packing
	const int textureSize = 1024;
	const int padding = 1;
	const int paddingLimit = 128;

	std::vector<stbrp_rect> totalRects;
	for (int i = 0; i < textures.size(); i++) {
		stbrp_rect rect;
		rect.id = i;
		rect.x = 0;
		rect.y = 0;
		if (textures[i].width >= paddingLimit || textures[i].height >= paddingLimit) {
			rect.w = textures[i].width;
			rect.h = textures[i].height;
		} else {
			rect.w = textures[i].width + padding * 2;
			rect.h = textures[i].height + padding * 2;
		}
		rect.was_packed = 0;
		totalRects.push_back(rect);
	}

	textureCoords.resize(textures.size());

	int layer = 0;
	for (;;layer++) {
		stbrp_context context;
		std::vector<stbrp_node> nodes;
		int nodesCount = textureSize * 2;
		nodes.resize(nodesCount);
		stbrp_init_target(&context, textureSize, textureSize, nodes.data(), nodesCount);

		std::vector<stbrp_rect> 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;
			}
			if (it.w >= paddingLimit || it.h >= paddingLimit) {
				textureCoords[it.id].pixelX = it.x;
				textureCoords[it.id].pixelY = it.y;
				textureCoords[it.id].pixelW = it.w;
				textureCoords[it.id].pixelH = it.h;
			} else {
				textureCoords[it.id].pixelX = it.x + padding;
				textureCoords[it.id].pixelY = it.y + padding;
				textureCoords[it.id].pixelW = it.w - padding * 2;
				textureCoords[it.id].pixelH = it.h - padding * 2;
			}
			textureCoords[it.id].x = (double)textureCoords[it.id].pixelX / textureSize;
			textureCoords[it.id].y = (double)textureCoords[it.id].pixelY / textureSize;
			textureCoords[it.id].w = (double)textureCoords[it.id].pixelW / textureSize;
			textureCoords[it.id].h = (double)textureCoords[it.id].pixelH / textureSize;
			textureCoords[it.id].layer = layer;
			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_REV, textures[i].data.data());
		glCheckError();
	}

	LOG(INFO) << "Texture atlas initialized";
}

TextureAtlas::~TextureAtlas() {
	glDeleteTextures(1, &texture);
}