1#include "hellfire/graphics/texture/Texture.h"
5#define STB_IMAGE_IMPLEMENTATION
8#include <stb/stb_image.h>
10#include "hellfire/graphics/material/Material.h"
89 if (
const std::ifstream file(path_); !file.good()) {
90 std::cerr <<
"Texture file does not exist: " <<
path_ << std::endl;
96 int desired_channels = 0;
101 desired_channels = 1;
106 if (desired_channels > 0) {
111 const char *error = stbi_failure_reason();
112 std::cerr <<
"STBI failed to load: " <<
path_
113 <<
" - " << (error ? error :
"Unknown error") << std::endl;
119 std::cerr <<
"Invalid texture data: " <<
width <<
"x" <<
height
121 stbi_image_free(data);
128 std::cerr <<
"Failed to generate OpenGL texture" << std::endl;
129 stbi_image_free(data);
133 glBindTexture(GL_TEXTURE_2D, texture_id_);
134 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
137 GLenum format, internal_format;
141 internal_format = GL_R8;
145 internal_format = GL_RGB8;
149 internal_format = GL_RGBA8;
152 std::cerr <<
"Unsupported channel count: " <<
nr_channels << std::endl;
153 stbi_image_free(data);
164 internal_format = GL_R8;
169 glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
171 GLenum gl_error = glGetError();
172 if (gl_error != GL_NO_ERROR) {
173 std::cerr <<
"OpenGL error uploading texture: " << gl_error << std::endl;
174 stbi_image_free(data);
182 glGenerateMipmap(GL_TEXTURE_2D);
185 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, get_gl_wrap_mode(settings_.wrap_s));
186 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, get_gl_wrap_mode(settings_.wrap_t));
187 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, get_gl_filter_mode(settings_.min_filter));
188 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, get_gl_filter_mode(settings_.mag_filter));
191 stbi_image_free(data);
203 glActiveTexture(GL_TEXTURE0 + slot);
204 glBindTexture(GL_TEXTURE_2D, texture_id_);
208 glBindTexture(GL_TEXTURE_2D, 0);
222 default:
return "Unknown";
237 default:
return "uTexture";
243 case TextureWrap::REPEAT:
return GL_REPEAT;
244 case TextureWrap::CLAMP_TO_EDGE:
return GL_CLAMP_TO_EDGE;
245 case TextureWrap::CLAMP_TO_BORDER:
return GL_CLAMP_TO_BORDER;
246 case TextureWrap::MIRRORED_REPEAT:
return GL_MIRRORED_REPEAT;
247 default:
return GL_REPEAT;
253 case TextureFilter::NEAREST:
return GL_NEAREST;
254 case TextureFilter::LINEAR:
return GL_LINEAR;
255 case TextureFilter::LINEAR_MIPMAP_LINEAR:
return GL_LINEAR_MIPMAP_LINEAR;
256 case TextureFilter::NEAREST_MIPMAP_NEAREST:
return GL_NEAREST_MIPMAP_NEAREST;
257 default:
return GL_LINEAR;
263 std::string cache_key = path +
"_" + std::to_string(
static_cast<
int>(type));
264 const auto it = cache_.find(cache_key);
265 if (it != cache_.end()) {
266 auto shared_texture = it->second.lock();
267 if (shared_texture) {
268 return shared_texture;
274 auto texture = std::make_shared<
Texture>(path, type);
275 if (texture->is_valid()) {
276 cache_[cache_key] = texture;
279 std::cerr <<
"Failed to create valid texture from: " << path << std::endl;
289 for (
auto it = cache_.begin(); it != cache_.end();) {
290 if (it->second.expired()) {
291 it = cache_.erase(it);
296 return cache_.size();
300 textures_[TextureType::DIFFUSE] = TextureCache::load(path, TextureType::DIFFUSE);
305 textures_[TextureType::NORMAL] = TextureCache::load(path, TextureType::NORMAL);
310 textures_[TextureType::SPECULAR] = TextureCache::load(path, TextureType::SPECULAR);
315 textures_[TextureType::ROUGHNESS] = TextureCache::load(path, TextureType::ROUGHNESS);
320 textures_[TextureType::METALNESS] = TextureCache::load(path, TextureType::METALNESS);
325 textures_[type] = TextureCache::load(path, type);
330 textures_[TextureType::AMBIENT_OCCLUSION] = TextureCache::load(path, TextureType::AMBIENT_OCCLUSION);
335 textures_[TextureType::EMISSIVE] = TextureCache::load(path, TextureType::EMISSIVE);
340 auto it = textures_.find(type);
341 return (it != textures_.end()) ? it->second :
nullptr;
345 auto it = textures_.find(type);
346 return (it != textures_.end()) && (it->second !=
nullptr) && (it->second->is_valid());
350 int texture_unit = 0;
351 for (
const auto &texture: textures_ | std::views::values) {
352 if (texture && texture->is_valid()) {
353 texture->bind(texture_unit);
360 for (
const auto &[type, texture]: textures_) {
361 if (texture && texture->is_valid()) {
362 std::string uniform_name = Texture::get_uniform_name(type);
363 material.set_property(uniform_name, texture.get(), uniform_name);
369 return std::filesystem::exists(filename);
373 const std::string &material_name) {
377 std::vector<std::pair<TextureType, std::vector<std::string> > > naming_patterns = {
379 TextureType::DIFFUSE, {
380 material_name +
"_diffuse.jpg", material_name +
"_diffuse.png",
381 material_name +
"_albedo.jpg", material_name +
"_albedo.png",
382 material_name +
"_color.jpg", material_name +
"_color.png",
383 material_name +
"_basecolor.jpg", material_name +
"_basecolor.png"
387 TextureType::NORMAL, {
388 material_name +
"_normal.jpg", material_name +
"_normal.png",
389 material_name +
"_nrm.jpg", material_name +
"_nrm.png",
390 material_name +
"_norm.jpg", material_name +
"_norm.png",
391 material_name +
"_normalmap.jpg", material_name +
"_normalmap.png"
395 TextureType::SPECULAR, {
396 material_name +
"_specular.jpg", material_name +
"_specular.png",
397 material_name +
"_spec.jpg", material_name +
"_spec.png",
398 material_name +
"_gloss.jpg", material_name +
"_gloss.png"
402 TextureType::ROUGHNESS, {
403 material_name +
"_roughness.jpg", material_name +
"_roughness.png",
404 material_name +
"_rough.jpg", material_name +
"_rough.png"
408 TextureType::METALNESS, {
409 material_name +
"_metalness.jpg", material_name +
"_metalness.png",
410 material_name +
"_metal.jpg", material_name +
"_metal.png",
411 material_name +
"_metallic.jpg", material_name +
"_metallic.png"
415 TextureType::AMBIENT_OCCLUSION, {
416 material_name +
"_ao.jpg", material_name +
"_ao.png",
417 material_name +
"_occlusion.jpg", material_name +
"_occlusion.png",
418 material_name +
"_ambient.jpg", material_name +
"_ambient.png"
422 TextureType::EMISSIVE, {
423 material_name +
"_emissive.jpg", material_name +
"_emissive.png",
424 material_name +
"_emission.jpg", material_name +
"_emission.png",
425 material_name +
"_glow.jpg", material_name +
"_glow.png"
429 TextureType::HEIGHT, {
430 material_name +
"_height.jpg", material_name +
"_height.png",
431 material_name +
"_displacement.jpg", material_name +
"_displacement.png",
432 material_name +
"_disp.jpg", material_name +
"_disp.png"
436 TextureType::OPACITY, {
437 material_name +
"_opacity.jpg", material_name +
"_opacity.png",
438 material_name +
"_alpha.jpg", material_name +
"_alpha.png",
439 material_name +
"_mask.jpg", material_name +
"_mask.png"
445 for (
const auto &[type, patterns]: naming_patterns) {
446 for (
const auto &pattern: patterns) {
447 std::string full_path = base_path;
450 if (!base_path.empty() && base_path.back() !=
'/' && base_path.back() !=
'\\') {
453 full_path += pattern;
455 if (file_exists(full_path)) {
457 texture_set.texture(type, full_path);
void apply_to_material(Material &material) const
bool has(TextureType type) const
std::shared_ptr< Texture > get(TextureType type) const
static std::unordered_map< std::string, std::weak_ptr< Texture > > cache_
static void clear_cache()
static size_t get_cache_size()
static std::string type_to_string(TextureType type)
Texture(Texture &&other) noexcept
TextureSettings settings_
static std::string get_uniform_name(TextureType type)
GLint get_gl_filter_mode(TextureFilter filter) const
void bind(unsigned int slot=0) const
Texture & operator=(Texture &&other) noexcept
bool file_exists(const std::string &filename)
static TextureSettings for_type(TextureType type)