summaryrefslogtreecommitdiff
path: root/gfx/models/texture.cpp
blob: e014f80e8d6cf90c74fcca4b09477987821ac4e1 (plain)
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include "texture.h"
#include "glArrays.h"
#include "tga.h"
#include <GL/glew.h>
#include <cache.h>
#include <fcntl.h>
#include <filesystem.h>
#include <gfx/image.h>
#include <glm/geometric.hpp>
#include <resource.h>
#include <stb/stb_image.h>
#include <sys/mman.h>

Cache<Texture, std::filesystem::path> Texture::cachedTexture;

GLint
TextureOptions::glMapMode(TextureOptions::MapMode mm)
{
	switch (mm) {
		case MapMode::Repeat:
			return GL_REPEAT;
		case MapMode::Clamp:
			return GL_CLAMP_TO_EDGE;
		case MapMode::Mirror:
			return GL_MIRRORED_REPEAT;
		default:
			throw std::domain_error("Invalid MapMode value");
	}
}

Texture::Texture(const std::filesystem::path & fileName, TextureOptions to) :
	Texture {Image {Resource::mapPath(fileName).c_str(), STBI_rgb_alpha}, to}
{
}

Texture::Texture(const Image & tex, TextureOptions to) :
	Texture {static_cast<GLsizei>(tex.width), static_cast<GLsizei>(tex.height), tex.data.data(), to}
{
}

Texture::Texture(GLsizei width, GLsizei height, TextureOptions to) : Texture {width, height, nullptr, to} { }

Texture::Texture(GLsizei width, GLsizei height, const void * data, TextureOptions to) : type {to.type}
{
	glBindTexture(type, m_texture);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	glTexParameteri(type, GL_TEXTURE_WRAP_S, TextureOptions::glMapMode(to.wrapU));
	glTexParameteri(type, GL_TEXTURE_WRAP_T, TextureOptions::glMapMode(to.wrapV));

	glTexParameteri(type, GL_TEXTURE_MIN_FILTER, to.minFilter);
	glTexParameteri(type, GL_TEXTURE_MAG_FILTER, to.magFilter);
	glTexImage2D(type, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
}

void
Texture::bind(GLenum unit) const
{
	glActiveTexture(unit);
	glBindTexture(type, m_texture);
}

glm::ivec2
Texture::getSize(const glTexture & texture)
{
	glm::ivec2 size;
	glGetTextureLevelParameteriv(texture, 0, GL_TEXTURE_WIDTH, &size.x);
	glGetTextureLevelParameteriv(texture, 0, GL_TEXTURE_HEIGHT, &size.y);
	return size;
}

void
Texture::save(
		const glTexture & texture, GLenum format, GLenum type, uint8_t channels, const char * path, uint8_t tgaFormat)
{
	const auto size = getSize(texture);
	const size_t dataSize = (static_cast<size_t>(size.x * size.y * channels));
	const size_t fileSize = dataSize + sizeof(TGAHead);

	filesystem::fh out {path, O_RDWR | O_CREAT, 0660};
	out.truncate(fileSize);
	auto tga = out.mmap(fileSize, 0, PROT_WRITE, MAP_SHARED);
	*tga.get<TGAHead>() = {
			.format = tgaFormat,
			.size = size,
			.pixelDepth = static_cast<uint8_t>(8 * channels),
	};
	glPixelStorei(GL_PACK_ALIGNMENT, 1);
	glGetTextureImage(texture, 0, format, type, static_cast<GLsizei>(dataSize), tga.get<TGAHead>() + 1);
	tga.msync(MS_ASYNC);
}

void
Texture::save(const char * path) const
{
	save(m_texture, GL_BGR, GL_UNSIGNED_BYTE, 3, path, 2);
}

void
Texture::save(const glTexture & texture, const char * path)
{
	save(texture, GL_BGR, GL_UNSIGNED_BYTE, 3, path, 2);
}

void
Texture::saveDepth(const glTexture & texture, const char * path)
{
	save(texture, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 1, path, 3);
}

void
Texture::saveNormal(const glTexture & texture, const char * path)
{
	save(texture, GL_BGR, GL_BYTE, 3, path, 2);
}

TextureAtlas::TextureAtlas(GLsizei width, GLsizei height, GLuint count) : Texture(width, height, nullptr, {})
{
	glBindTexture(GL_TEXTURE_RECTANGLE, m_atlas);

	glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA16UI, 2, static_cast<GLsizei>(count), 0, GL_RGBA_INTEGER,
			GL_UNSIGNED_BYTE, nullptr);
}

void
TextureAtlas::bind(GLenum unit) const
{
	Texture::bind(unit);
	glActiveTexture(unit + 1);
	glBindTexture(GL_TEXTURE_RECTANGLE, m_atlas);
}

GLuint
TextureAtlas::add(glm::ivec2 position, glm::ivec2 size, void * data, TextureOptions to)
{
	glTextureSubImage2D(m_texture, 0, position.x, position.y, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, data);
	struct Material {
		glm::vec<2, uint16_t> position, size;
		TextureOptions::MapMode wrapU;
		TextureOptions::MapMode wrapV;
	} material {position, size, to.wrapU, to.wrapV};
	static_assert(sizeof(Material) <= 32);
	glTextureSubImage2D(m_atlas, 0, 0, static_cast<GLsizei>(used), 2, 1, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, &material);
	return ++used;
}