394 lines
11 KiB
C++
394 lines
11 KiB
C++
// Copyright (C) 2017 Jérôme Leclercq
|
|
// This file is part of the "Nazara Engine - Renderer module"
|
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
|
|
|
#include <Nazara/Renderer/TextureSampler.hpp>
|
|
#include <Nazara/Core/Error.hpp>
|
|
#include <Nazara/Renderer/Config.hpp>
|
|
#include <Nazara/Renderer/Context.hpp>
|
|
#include <Nazara/Renderer/OpenGL.hpp>
|
|
#include <Nazara/Renderer/Renderer.hpp>
|
|
#include <Nazara/Renderer/Texture.hpp>
|
|
#include <unordered_map>
|
|
#include <Nazara/Renderer/Debug.hpp>
|
|
|
|
namespace Nz
|
|
{
|
|
namespace
|
|
{
|
|
std::unordered_map<UInt32, GLuint> s_samplers;
|
|
UInt8 s_maxAnisotropyLevel;
|
|
bool s_useAnisotropicFilter;
|
|
}
|
|
|
|
TextureSampler::TextureSampler() :
|
|
m_filterMode(SamplerFilter_Default),
|
|
m_wrapMode(SamplerWrap_Default),
|
|
m_anisotropicLevel(0),
|
|
m_mipmaps(true),
|
|
m_samplerId(0)
|
|
{
|
|
}
|
|
|
|
UInt8 TextureSampler::GetAnisotropicLevel() const
|
|
{
|
|
return m_anisotropicLevel;
|
|
}
|
|
|
|
SamplerFilter TextureSampler::GetFilterMode() const
|
|
{
|
|
return m_filterMode;
|
|
}
|
|
|
|
SamplerWrap TextureSampler::GetWrapMode() const
|
|
{
|
|
return m_wrapMode;
|
|
}
|
|
|
|
void TextureSampler::SetAnisotropyLevel(UInt8 anisotropyLevel)
|
|
{
|
|
#ifdef NAZARA_DEBUG
|
|
if (!Renderer::IsInitialized())
|
|
{
|
|
NazaraError("Renderer module must be initialized");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (m_anisotropicLevel != anisotropyLevel)
|
|
{
|
|
if (anisotropyLevel > s_maxAnisotropyLevel)
|
|
{
|
|
NazaraWarning("Anisotropy level is over maximum anisotropy level (" + String::Number(anisotropyLevel) + " > " + String::Number(s_maxAnisotropyLevel) + ')');
|
|
anisotropyLevel = s_maxAnisotropyLevel;
|
|
}
|
|
|
|
m_anisotropicLevel = anisotropyLevel;
|
|
m_samplerId = 0;
|
|
}
|
|
}
|
|
|
|
void TextureSampler::SetFilterMode(SamplerFilter filterMode)
|
|
{
|
|
if (m_filterMode != filterMode)
|
|
{
|
|
m_filterMode = filterMode;
|
|
m_samplerId = 0;
|
|
}
|
|
}
|
|
|
|
void TextureSampler::SetWrapMode(SamplerWrap wrapMode)
|
|
{
|
|
if (m_wrapMode != wrapMode)
|
|
{
|
|
m_wrapMode = wrapMode;
|
|
m_samplerId = 0;
|
|
}
|
|
}
|
|
|
|
UInt8 TextureSampler::GetDefaultAnisotropicLevel()
|
|
{
|
|
return s_defaultAnisotropyLevel;
|
|
}
|
|
|
|
SamplerFilter TextureSampler::GetDefaultFilterMode()
|
|
{
|
|
return s_defaultFilterMode;
|
|
}
|
|
|
|
SamplerWrap TextureSampler::GetDefaultWrapMode()
|
|
{
|
|
return s_defaultWrapMode;
|
|
}
|
|
|
|
void TextureSampler::SetDefaultAnisotropyLevel(UInt8 anisotropyLevel)
|
|
{
|
|
#if NAZARA_RENDERER_SAFE
|
|
if (anisotropyLevel == 0)
|
|
{
|
|
NazaraError("Default anisotropy level mode cannot be set to default value (0)");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#ifdef NAZARA_DEBUG
|
|
if (!Renderer::IsInitialized())
|
|
{
|
|
NazaraError("Renderer module must be initialized");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (anisotropyLevel > s_maxAnisotropyLevel)
|
|
{
|
|
NazaraWarning("Anisotropy level is over maximum anisotropy level (" + String::Number(anisotropyLevel) + " > " + String::Number(s_maxAnisotropyLevel));
|
|
anisotropyLevel = s_maxAnisotropyLevel;
|
|
}
|
|
|
|
s_defaultAnisotropyLevel = anisotropyLevel;
|
|
|
|
if (s_useAnisotropicFilter)
|
|
{
|
|
for (const std::pair<const UInt32, GLuint>& pair : s_samplers)
|
|
{
|
|
if (((pair.first >> 5) & 0xFF) == 0)
|
|
glSamplerParameterf(pair.second, GL_TEXTURE_MAX_ANISOTROPY_EXT, static_cast<float>(anisotropyLevel));
|
|
}
|
|
}
|
|
}
|
|
|
|
void TextureSampler::SetDefaultFilterMode(SamplerFilter filterMode)
|
|
{
|
|
#if NAZARA_RENDERER_SAFE
|
|
if (filterMode == SamplerFilter_Default)
|
|
{
|
|
NazaraError("Default filter mode cannot be set to default enum value (SamplerFilter_Default)");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
s_defaultFilterMode = filterMode;
|
|
|
|
for (const std::pair<const UInt32, GLuint>& pair : s_samplers)
|
|
{
|
|
if (((pair.first >> 1) & 0x3) == SamplerFilter_Default)
|
|
{
|
|
bool mipmaps = pair.first & 0x1;
|
|
switch (filterMode)
|
|
{
|
|
case SamplerFilter_Bilinear:
|
|
if (mipmaps)
|
|
glSamplerParameteri(pair.second, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
|
else
|
|
glSamplerParameteri(pair.second, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glSamplerParameteri(pair.second, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
break;
|
|
|
|
case SamplerFilter_Nearest:
|
|
if (mipmaps)
|
|
glSamplerParameteri(pair.second, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
|
|
else
|
|
glSamplerParameteri(pair.second, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
|
glSamplerParameteri(pair.second, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
break;
|
|
|
|
case SamplerFilter_Trilinear:
|
|
if (mipmaps)
|
|
glSamplerParameteri(pair.second, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
else
|
|
glSamplerParameteri(pair.second, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Filtrage bilinéaire
|
|
|
|
glSamplerParameteri(pair.second, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
break;
|
|
|
|
default:
|
|
NazaraError("Texture filter not handled (0x" + String::Number(filterMode, 16) + ')');
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TextureSampler::SetDefaultWrapMode(SamplerWrap wrapMode)
|
|
{
|
|
#if NAZARA_RENDERER_SAFE
|
|
if (wrapMode == SamplerWrap_Default)
|
|
{
|
|
NazaraError("Default wrap mode cannot be set to default enum value (SamplerWrap_Default)");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
s_defaultWrapMode = wrapMode;
|
|
|
|
GLenum wrapEnum = OpenGL::SamplerWrapMode[wrapMode];
|
|
for (const std::pair<const UInt32, GLuint>& pair : s_samplers)
|
|
{
|
|
if (((pair.first >> 3) & 0x3) == SamplerWrap_Default)
|
|
{
|
|
glSamplerParameteri(pair.second, GL_TEXTURE_WRAP_R, wrapEnum);
|
|
glSamplerParameteri(pair.second, GL_TEXTURE_WRAP_T, wrapEnum);
|
|
glSamplerParameteri(pair.second, GL_TEXTURE_WRAP_S, wrapEnum);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TextureSampler::Apply(const Texture* texture) const
|
|
{
|
|
ImageType type = texture->GetType();
|
|
GLenum target = OpenGL::TextureTarget[type];
|
|
|
|
OpenGL::BindTexture(type, texture->GetOpenGLID());
|
|
|
|
if (s_useAnisotropicFilter)
|
|
{
|
|
UInt8 anisotropyLevel = (m_anisotropicLevel == 0) ? s_defaultAnisotropyLevel : m_anisotropicLevel;
|
|
glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, static_cast<float>(anisotropyLevel));
|
|
}
|
|
|
|
SamplerFilter filterMode = (m_filterMode == SamplerFilter_Default) ? s_defaultFilterMode : m_filterMode;
|
|
switch (filterMode)
|
|
{
|
|
case SamplerFilter_Bilinear:
|
|
if (m_mipmaps)
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
|
else
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
break;
|
|
|
|
case SamplerFilter_Nearest:
|
|
if (m_mipmaps)
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
|
|
else
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
break;
|
|
|
|
case SamplerFilter_Trilinear:
|
|
if (m_mipmaps)
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
else
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Filtrage bilinéaire
|
|
|
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
break;
|
|
|
|
default:
|
|
NazaraError("Texture filter not handled (0x" + String::Number(filterMode, 16) + ')');
|
|
break;
|
|
}
|
|
|
|
GLenum wrapMode = OpenGL::SamplerWrapMode[(m_wrapMode == SamplerWrap_Default) ? s_defaultWrapMode : m_wrapMode];
|
|
switch (type)
|
|
{
|
|
// Notez l'absence de "break" ici
|
|
case ImageType_3D:
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_R, wrapMode);
|
|
case ImageType_2D:
|
|
case ImageType_2D_Array:
|
|
case ImageType_Cubemap:
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_T, wrapMode);
|
|
case ImageType_1D:
|
|
case ImageType_1D_Array:
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_S, wrapMode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TextureSampler::Bind(unsigned int unit) const
|
|
{
|
|
static_assert(SamplerFilter_Max < 0x4, "Maximum sampler filter mode takes more than 2 bits");
|
|
static_assert(SamplerWrap_Max < 0x4, "Maximum sampler wrap mode takes more than 2 bits");
|
|
|
|
if (!m_samplerId)
|
|
UpdateSamplerId();
|
|
|
|
OpenGL::BindSampler(unit, m_samplerId);
|
|
}
|
|
|
|
unsigned int TextureSampler::GetOpenGLID() const
|
|
{
|
|
if (!m_samplerId)
|
|
UpdateSamplerId();
|
|
|
|
return m_samplerId;
|
|
}
|
|
|
|
void TextureSampler::UpdateSamplerId() const
|
|
{
|
|
UInt32 key = (((m_mipmaps) ? 1 : 0) << 0) | // 1 bit
|
|
(m_filterMode << 1) | // 2 bits
|
|
(m_wrapMode << 3) | // 2 bits
|
|
(m_anisotropicLevel << 5); // 8 bits
|
|
|
|
auto it = s_samplers.find(key);
|
|
if (it == s_samplers.end())
|
|
{
|
|
GLuint sampler;
|
|
glGenSamplers(1, &sampler);
|
|
|
|
if (s_useAnisotropicFilter)
|
|
{
|
|
UInt8 anisotropyLevel = (m_anisotropicLevel == 0) ? s_defaultAnisotropyLevel : m_anisotropicLevel;
|
|
glSamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, static_cast<float>(anisotropyLevel));
|
|
}
|
|
|
|
SamplerFilter filterMode = (m_filterMode == SamplerFilter_Default) ? s_defaultFilterMode : m_filterMode;
|
|
switch (filterMode)
|
|
{
|
|
case SamplerFilter_Bilinear:
|
|
glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, (m_mipmaps) ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR);
|
|
glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
break;
|
|
|
|
case SamplerFilter_Nearest:
|
|
glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, (m_mipmaps) ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
|
|
glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
break;
|
|
|
|
case SamplerFilter_Trilinear:
|
|
// Équivalent au filtrage bilinéaire si les mipmaps sont absentes
|
|
glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, (m_mipmaps) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
|
|
glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
break;
|
|
|
|
default:
|
|
NazaraError("Texture filter not handled (0x" + String::Number(filterMode, 16) + ')');
|
|
break;
|
|
}
|
|
|
|
GLenum wrapMode = OpenGL::SamplerWrapMode[(m_wrapMode == SamplerWrap_Default) ? s_defaultWrapMode : m_wrapMode];
|
|
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_R, wrapMode);
|
|
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, wrapMode);
|
|
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, wrapMode);
|
|
|
|
s_samplers[key] = sampler;
|
|
m_samplerId = sampler;
|
|
}
|
|
else
|
|
m_samplerId = it->second;
|
|
}
|
|
|
|
bool TextureSampler::UseMipmaps(bool mipmaps)
|
|
{
|
|
if (m_mipmaps != mipmaps)
|
|
{
|
|
m_mipmaps = mipmaps;
|
|
m_samplerId = 0;
|
|
|
|
return true; // Renvoie true si la valeur a été changée (Donc s'il faut réappliquer le sampler)
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool TextureSampler::Initialize()
|
|
{
|
|
s_maxAnisotropyLevel = Renderer::GetMaxAnisotropyLevel();
|
|
s_useAnisotropicFilter = OpenGL::IsSupported(OpenGLExtension_AnisotropicFilter);
|
|
|
|
return true;
|
|
}
|
|
|
|
void TextureSampler::Uninitialize()
|
|
{
|
|
if (!s_samplers.empty())
|
|
{
|
|
Context::EnsureContext();
|
|
for (const std::pair<const UInt32, GLuint>& pair : s_samplers)
|
|
OpenGL::DeleteSampler(pair.second);
|
|
|
|
s_samplers.clear();
|
|
}
|
|
}
|
|
|
|
UInt8 TextureSampler::s_defaultAnisotropyLevel = 1;
|
|
SamplerFilter TextureSampler::s_defaultFilterMode = SamplerFilter_Trilinear;
|
|
SamplerWrap TextureSampler::s_defaultWrapMode = SamplerWrap_Repeat;
|
|
}
|