main
Brett 2023-12-17 19:37:11 -05:00
parent 6a5b06ac70
commit e04f4b555d
5 changed files with 8591 additions and 103 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -19,168 +19,190 @@
#ifndef BLT_TEXTURE_H
#define BLT_TEXTURE_H
#ifdef __EMSCRIPTEN__
#else
#endif
#include <blt/gfx/gl_includes.h>
#include <blt/gfx/stb/stb_image.h>
#include <blt/std/logging.h>
#include <string>
#include <vector>
#include <utility>
namespace blt::gfx
{
class file_texture {
class texture_file;
class texture_data
{
friend texture_file;
private:
std::string m_Name;
std::string m_Path;
int width = 0, height = 0, channels = 0;
unsigned char* m_Data = nullptr;
unsigned char* m_data = nullptr;
int m_width = 0, m_height = 0, m_channels = 0;
public:
texture_data(unsigned char* data, int width, int height, int channels):
m_data(data), m_width(width), m_height(height), m_channels(channels)
{}
texture_data(int width, int height, int channels = 4): m_width(width), m_height(height), m_channels(channels)
{
m_data = static_cast<unsigned char*>(STBI_MALLOC(width * height * channels));
}
texture_data() = default;
unsigned char* data()
{
return m_data;
}
[[nodiscard]] unsigned char* data() const
{
return m_data;
}
[[nodiscard]] int width() const
{
return m_width;
}
[[nodiscard]] int height() const
{
return m_height;
}
[[nodiscard]] int channels() const
{
return m_channels;
}
~texture_data()
{
STBI_FREE(m_data);
}
};
class texture_file
{
private:
std::string m_name;
std::string m_path;
mutable texture_data m_texture;
public:
/**
* @param path path to the texture file
* @param name reference name for this texture. If empty the texture will use path as its identifier
*/
explicit file_texture(const std::string& path, const std::string& name = ""):
m_Name(name.empty() ? path : name), m_Path(path) {}
explicit texture_file(const std::string& path, const std::string& name = "");
static file_texture* load(file_texture*& texture) {
// we want to load every texture as if it has transparency,
// otherwise textures won't be correctly resized and loaded to the gpu
constexpr int channel_count = 4;
texture->m_Data = stbi_load(
texture->m_Path.c_str(), &texture->width, &texture->height,
&texture->channels, channel_count
);
texture->channels = channel_count;
return texture;
texture_file& resize(int target_width, int target_height);
texture_data& texture() const
{
return m_texture;
}
static file_texture* resize(
file_texture* texture, int target_width, int target_height
) {
if (target_width == texture->width && target_height == texture->height)
return texture;
// since we will be replacing the loaded data pointer, is it wise to use the allocator
// that matches with what stb image uses, which is malloc, since we unload with stbi_image_free -> (free)
auto* output_Data = (unsigned char*) malloc(
target_width * target_height * texture->channels
);
// resize the texture
if (stbir_resize_uint8(
// input
texture->m_Data, texture->width, texture->height, 0,
// output
output_Data, target_width, target_height, 0,
// channels
texture->channels
)) {
BLT_WARN("Error resizing block texture image!");
[[nodiscard]] int channels() const
{
return m_texture.m_channels;
}
// free up the old data
stbi_image_free(texture->m_Data);
texture->m_Data = output_Data;
texture->width = target_width;
texture->height = target_height;
return texture;
[[nodiscard]] int width() const
{
return m_texture.m_width;
}
unsigned char* data() {
return m_Data;
[[nodiscard]] int height() const
{
return m_texture.m_height;
}
[[nodiscard]] int getChannels() const {
return channels;
}
[[nodiscard]] int getWidth() const {
return width;
}
[[nodiscard]] int getHeight() const {
return height;
}
[[nodiscard]] const std::string& getName() {
return m_Name;
}
~file_texture() {
stbi_image_free(m_Data);
[[nodiscard]] const std::string& getName() const
{
return m_name;
}
};
struct gl_texture {
struct texture_gl
{
protected:
unsigned int textureID = 0;
GLint textureBindType;
GLint textureColorMode;
int m_width, m_height;
gl_texture(
texture_gl(
int width, int height, GLint bind_type = GL_TEXTURE_2D,
GLint color_mode = GL_RGBA
):
textureBindType(bind_type), textureColorMode(color_mode), m_width(width),
m_height(height) {
m_height(height)
{
glGenTextures(1, &textureID);
}
public:
inline void bind() const {
inline void bind() const
{
glBindTexture(textureBindType, textureID);
}
inline void unbind() const {
inline void unbind() const
{
glBindTexture(textureBindType, 0);
}
void setDefaults() const {
void setDefaults() const
{
bind();
glTexParameteri(textureBindType, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(textureBindType, GL_TEXTURE_WRAP_T, GL_REPEAT);
// nearest preserves the pixely look
glTexParameteri(textureBindType, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
glTexParameteri(textureBindType, GL_TEXTURE_MAG_FILTER, GL_NEAREST_MIPMAP_LINEAR);
#ifdef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
// Anisotropy helps preserve textures at oblique angles
float a = 0;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &a);
glTexParameterf(textureBindType, GL_TEXTURE_MAX_ANISOTROPY_EXT, a);
#endif
unbind();
}
inline void generateMipmaps() const {
inline void generateMipmaps() const
{
// it's a little inefficient binding and unbinding for these small calls, they really should be done in the constructor or data upload
bind();
glGenerateMipmap(textureBindType);
unbind();
}
[[nodiscard]] inline unsigned int getTextureID() const {
[[nodiscard]] inline unsigned int getTextureID() const
{
return textureID;
}
virtual ~gl_texture() {
virtual ~texture_gl()
{
glDeleteTextures(1, &textureID);
}
};
struct gl_texture2D : public gl_texture {
struct texture_gl2D : public texture_gl
{
public:
gl_texture2D(int width, int height, GLint colorMode = GL_RGBA):
gl_texture(width, height, GL_TEXTURE_2D, colorMode) {
texture_gl2D(int width, int height, GLint colorMode = GL_RGBA):
texture_gl(width, height, GL_TEXTURE_2D, colorMode)
{
bind();
// TODO:
const int MIPMAP_LEVELS = 4;
glTexStorage2D(
textureBindType, std::stoi(fp::settings::get("MIPMAP_LEVELS")), colorMode,
textureBindType, MIPMAP_LEVELS, colorMode,
width, height
);
}
void upload(
void* data, GLint dataColorMode = GL_RGBA, int level = 0, int x_offset = 0,
int y_offset = 0, int sub_width = -1,
int sub_height = -1
) const {
void upload(void* data, GLint dataColorMode = GL_RGBA, int level = 0, int x_offset = 0, int y_offset = 0, int sub_width = -1,
int sub_height = -1) const
{
if (sub_width < 0)
sub_width = m_width;
if (sub_height < 0)
@ -193,14 +215,16 @@ namespace blt::gfx
unbind();
}
void upload(file_texture* texture) const {
upload(texture->data(), texture->getChannels() == 4 ? GL_RGBA : GL_RGB);
void upload(const texture_file& tex_file) const
{
upload(tex_file.texture().data(), tex_file.channels() == 4 ? GL_RGBA : GL_RGB, 0, 0, 0, tex_file.width(), tex_file.height());
}
/**
* Resizes the internal memory for the texture but does NOT resize the texture image stored
*/
inline void resize(int width, int height) {
inline void resize(int width, int height)
{
m_width = width;
m_height = height;
bind();
@ -209,23 +233,23 @@ namespace blt::gfx
}
};
struct gl_texture2D_array : public gl_texture {
struct gl_texture2D_array : public texture_gl
{
protected:
int m_layers;
public:
gl_texture2D_array(int width, int height, int layers, GLint colorMode = GL_RGBA8):
gl_texture(width, height, GL_TEXTURE_2D_ARRAY, colorMode), m_layers(layers) {
texture_gl(width, height, GL_TEXTURE_2D_ARRAY, colorMode), m_layers(layers)
{
bind();
// 6+ mipmaps is about where I stop noticing any difference (size is 4x4 pixels, so that makes sense)
glTexStorage3D(textureBindType, 6, colorMode, width, height, layers);
BLT_DEBUG("Creating 2D Texture Array with ID: %d", textureID);
}
void upload(
void* data, int index, GLint dataColorMode = GL_RGBA, int level = 0,
int x_offset = 0, int y_offset = 0, int sub_width = -1,
int sub_height = -1
) const {
void upload(void* data, int index, GLint dataColorMode = GL_RGBA, int level = 0, int x_offset = 0, int y_offset = 0, int sub_width = -1,
int sub_height = -1) const
{
if (sub_width < 0)
sub_width = m_width;
if (sub_height < 0)
@ -239,7 +263,8 @@ namespace blt::gfx
}
};
class gl_buffer_texture : public gl_texture2D {
class gl_buffer_texture : public texture_gl2D
{
private:
public:
@ -247,7 +272,8 @@ namespace blt::gfx
gl_buffer_texture(
int width, int height, GLint format = GL_RGB32F,
GLint colorAttachment = GL_COLOR_ATTACHMENT0
): gl_texture2D(width, height, format) {
): texture_gl2D(width, height, format)
{
bind();
// no mipmaping and no interpolation to position textures!
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

View File

@ -15,3 +15,54 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_PERLIN_IMPLEMENTATION
#include <blt/gfx/stb/stb_image_write.h>
#include <blt/gfx/stb/stb_image_resize2.h>
#include <blt/gfx/stb/stb_perlin.h>
#include <blt/gfx/texture.h>
blt::gfx::texture_file::texture_file(const std::string& path, const std::string& name): m_name(name.empty() ? path : name), m_path(path)
{
m_texture.m_data = stbi_load(
m_path.c_str(),
reinterpret_cast<int*>(&m_texture.m_width), reinterpret_cast<int*>(&m_texture.m_height),
reinterpret_cast<int*>(&m_texture.m_channels),
0
);
}
blt::gfx::texture_file& blt::gfx::texture_file::resize(int target_width, int target_height)
{
if (target_width == m_texture.width() && target_height == m_texture.height())
return *this;
// since we will be replacing the loaded data pointer, is it wise to use the allocator
// that matches with what stb image uses, which is malloc, since we unload with stbi_image_free -> (free)
auto* output_Data = (unsigned char*) malloc(
target_width * target_height * m_texture.channels()
);
// resize the texture
if (stbir_resize_uint8_linear(
// input
m_texture.m_data, m_texture.width(), m_texture.height(), 0,
// output
output_Data, target_width, target_height, 0,
// channels
static_cast<stbir_pixel_layout>(m_texture.channels())
))
{
BLT_WARN("Error resizing block texture image!");
}
// free up the old data
stbi_image_free(m_texture.m_data);
m_texture.m_data = output_Data;
m_texture.m_width = target_width;
m_texture.m_height = target_height;
return *this;
}