summaryrefslogtreecommitdiff
path: root/gfx/models/texture.cpp
blob: 7e413d4b5e480a1342620384f68b507ac4eb6230 (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
#include "texture.h"
#include "config/types.h"
#include <fcntl.h>
#include <filesystem.h>
#include <gfx/image.h>
#include <gl_traits.h>
#include <glad/gl.h>
#include <glm/geometric.hpp>
#include <resource.h>
#include <stb/stb_image.h>
#include <sys/mman.h>

using std::ceil;

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)
{
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	const auto levels = static_cast<GLsizei>(ceil(std::log2(std::max(width, height))));
	m_texture.storage(levels, GL_RGBA8, {width, height});
	m_texture.parameter(GL_TEXTURE_WRAP_S, TextureOptions::glMapMode(to.wrapU));
	m_texture.parameter(GL_TEXTURE_WRAP_T, TextureOptions::glMapMode(to.wrapV));
	m_texture.parameter(GL_TEXTURE_MIN_FILTER, to.minFilter);
	m_texture.parameter(GL_TEXTURE_MAG_FILTER, to.magFilter);
	m_texture.image({width, height}, GL_RGBA, GL_UNSIGNED_BYTE, data);
	auto isMimmap = [](auto value) {
		auto eqAnyOf = [value](auto... test) {
			return (... || (value == test));
		};
		return eqAnyOf(
				GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR);
	};
	if (levels > 1 && (isMimmap(to.minFilter) || isMimmap(to.magFilter))) {
		m_texture.generateMipmap();
	}
}

void
Texture::bind(GLenum unit) const
{
	m_texture.bind(unit);
}

TextureAtlas::TextureAtlas(GLsizei width, GLsizei height, GLuint count) : Texture(width, height)
{
	m_atlas.parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	m_atlas.parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	m_atlas.parameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	m_atlas.parameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	m_atlas.storage(1, GL_RGBA16UI, {2, count});
}

void
TextureAtlas::bind(GLenum unit) const
{
	Texture::bind(unit);
	m_atlas.bind(unit + 1);
}

GLuint
TextureAtlas::add(TextureAbsCoord position, TextureAbsCoord size, void * data, TextureOptions to)
{
	m_texture.subImage(position, size, 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);
	m_atlas.subImage({0, used}, {2, 1}, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, &material);
	return ++used;
}

void
TextureAtlas::complete()
{
	m_texture.generateMipmap();
}