189 lines
6.9 KiB
C++
189 lines
6.9 KiB
C++
#include "Images.hpp"
|
|
#include "../../../../core/utils/logger.hpp"
|
|
#include <algorithm>
|
|
#include <string>
|
|
|
|
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);
|
|
glTextureParameterf(textureID, GL_TEXTURE_MAX_ANISOTROPY, props.anisotropy);
|
|
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));
|
|
} |