1
0
Fork 0
Univerxel/src/client/render/gl/api/Images.cpp

192 lines
7.0 KiB
C++

#include "Images.hpp"
#include "../../../../core/utils/logger.hpp"
#include <algorithm>
#include <string>
#include <array>
using namespace render::gl;
Image::~Image() {
glDeleteTextures(1, &id);
}
GLenum getGLFormat(render::Image::Format format) {
switch (format) {
case render::Image::Format::BC2:
case render::Image::Format::BC2_UNORM:
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
case render::Image::Format::BC3:
case render::Image::Format::BC3_UNORM:
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
default:
return 0;
}
}
GLuint createImage(const Image::requirement& req, render::data_view data) {
GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glTextureParameteri(textureID, GL_TEXTURE_MAX_LEVEL, req.mipmapLevels-1);
GLenum format = getGLFormat(req.format);
unsigned int blockSize = /*(format == GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) ? 8 :*/ 16;
unsigned int offset = 0;
int width = req.size.width;
int height = req.size.height;
/* load the mipmaps */
for (unsigned int level = 0; level < req.mipmapLevels && (width || height); ++level) {
unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;
assert(data.size >= offset + size);
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, size, static_cast<const uint8_t*>(data.ptr) + offset);
offset += size;
width /= 2;
height /= 2;
// Deal with Non-Power-Of-Two textures. This code is not included in the webpage to reduce clutter.
if(width < 1) width = 1;
if(height < 1) height = 1;
}
return textureID;
}
void applySampler(GLuint textureID, const Texture::sampling& props) {
auto getFilter = [](bool linear, bool mipmap) { return linear ?
(mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR) : (mipmap ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST); };
auto wrap = [](Texture::Wrap wrap) {
switch (wrap) {
case Texture::Wrap::CLAMP_TO_EDGE:
return GL_CLAMP_TO_EDGE;
case Texture::Wrap::CLAMP_TO_BORDER:
return GL_CLAMP_TO_BORDER;
case Texture::Wrap::MIRRORED_REPEAT:
return GL_MIRRORED_REPEAT;
case Texture::Wrap::REPEAT:
default:
return GL_REPEAT;
}
}(props.wrap);
glTextureParameteri(textureID, GL_TEXTURE_MAG_FILTER, getFilter(props.magLinear, props.mipmap));
glTextureParameteri(textureID, GL_TEXTURE_MIN_FILTER, getFilter(props.minLinear, props.mipmap));
glTextureParameterf(textureID, GL_TEXTURE_LOD_BIAS, props.mipmapLod);
#if !GL_OLD
glTextureParameterf(textureID, GL_TEXTURE_MAX_ANISOTROPY, props.anisotropy);
#endif
glTextureParameteri(textureID, GL_TEXTURE_WRAP_S, wrap);
glTextureParameteri(textureID, GL_TEXTURE_WRAP_T, wrap);
glTextureParameteri(textureID, GL_TEXTURE_WRAP_R, wrap);
}
std::unique_ptr<Texture> Texture::LoadFromFile(const std::string& path, const sampling& props) {
std::vector<unsigned char> data;
auto header = [&] {
if (auto header = render::Image::Read(path, data)) {
return header.value();
}
FATAL("Cannot read texture");
}();
auto id = createImage(requirement::Texture(header), data);
if(!id) {
FATAL("Cannot create texture image");
}
applySampler(id, props);
return std::unique_ptr<Texture>(new Texture(id));
}
std::unique_ptr<TextureCube> TextureCube::LoadFromFiles(const std::array<std::string, 6>& paths, const sampling& props) {
std::vector<unsigned char> data;
auto header = [&] {
if (auto header = render::Image::Read(paths.front(), data)) {
return header.value();
}
FATAL("Cannot read first texture");
}();
auto req = requirement::Texture(header);
GLuint textureID;
{
GLenum format = getGLFormat(header.format);
glCreateTextures(GL_TEXTURE_CUBE_MAP, 1, &textureID);
glTextureStorage2D(textureID, 1, format, header.size.width, header.size.height);
uint16_t layer = 0;
for (auto imagepath = paths.begin(); imagepath != paths.end(); ++imagepath, ++layer) {
data.clear();
if (!render::Image::Read(*imagepath, data).has_value()) {
FATAL("Cannot read texture");
}
GLuint subTextureID = createImage(req, data);
glCopyImageSubData(subTextureID, GL_TEXTURE_2D, 0, 0, 0, 0, textureID, GL_TEXTURE_CUBE_MAP, 0, 0, 0, layer, header.size.width, header.size.height, 1);
glDeleteTextures(1, &subTextureID);
}
}
glTextureParameteri(textureID, GL_TEXTURE_MAX_LEVEL, header.mipmapLevels-1);
applySampler(textureID, props);
return std::unique_ptr<TextureCube>(new TextureCube(textureID));
}
std::unique_ptr<TextureCube> TextureCube::LoadFromFiles(const std::string& prefix, const sampling& props) {
const std::array<std::string, 6> faces {
"right",
"left",
"top",
"bottom",
"front",
"back"
};
std::array<std::string, 6> paths;
std::transform(faces.begin(), faces.end(), paths.begin(),
[prefix](const std::string &face) -> std::string { return prefix + "." + face + ".dds"; });
return LoadFromFiles(paths, props);
}
std::unique_ptr<TextureArray> TextureArray::LoadFromFiles(const std::vector<std::string>& paths, const sampling& props) {
std::vector<unsigned char> data;
auto header = [&] {
if (auto header = render::Image::Read(paths.front(), data)) {
return header.value();
}
FATAL("Cannot read first texture");
}();
auto req = requirement::Texture(header);
GLuint textureID;
{
GLenum format = getGLFormat(header.format);
glCreateTextures(GL_TEXTURE_2D_ARRAY, 1, &textureID);
glTextureStorage3D(textureID, header.mipmapLevels, format, header.size.width, header.size.height, paths.size());
uint16_t layer = 0;
for (auto imagepath = paths.begin(); imagepath != paths.end(); ++imagepath, ++layer) {
data.clear();
if (!render::Image::Read(*imagepath, data).has_value()) {
FATAL("Cannot read texture");
}
GLuint subTextureID = createImage(req, data);
auto width = header.size.width;
auto height = header.size.height;
for (uint32_t level = 0; level < header.mipmapLevels; level++) {
glCopyImageSubData(subTextureID, GL_TEXTURE_2D, level, 0, 0, 0, textureID, GL_TEXTURE_2D_ARRAY, level, 0, 0, layer, width, height, 1);
width /= 2;
height /= 2;
if(width < 1) width = 1;
if(height < 1) height = 1;
}
glDeleteTextures(1, &subTextureID);
}
}
glTextureParameteri(textureID, GL_TEXTURE_MAX_LEVEL, header.mipmapLevels-1);
applySampler(textureID, props);
return std::unique_ptr<TextureArray>(new TextureArray(paths.size(), textureID));
}