diff --git a/include/Nazara/Prerequesites.hpp b/include/Nazara/Prerequesites.hpp index ff66261cc..2a3fe5140 100644 --- a/include/Nazara/Prerequesites.hpp +++ b/include/Nazara/Prerequesites.hpp @@ -62,24 +62,18 @@ #if NAZARA_CORE_WINDOWS_VISTA // Version de Windows minimale : Vista - #if defined(_WIN32_WINNT) - #if _WIN32_WINNT < 0x0600 - #undef _WIN32_WINNT - #define _WIN32_WINNT 0x0600 - #endif - #else - #define _WIN32_WINNT 0x0600 + #define NAZARA_WINNT 0x0600 + #else + #define NAZARA_WINNT 0x0501 + #endif + + #if defined(_WIN32_WINNT) + #if _WIN32_WINNT < NAZARA_WINNT + #undef _WIN32_WINNT + #define _WIN32_WINNT NAZARA_WINNT #endif #else - // Version de Windows minimale : XP - #if defined(_WIN32_WINNT) - #if _WIN32_WINNT < 0x0501 - #undef _WIN32_WINNT - #define _WIN32_WINNT 0x0501 - #endif - #else - #define _WIN32_WINNT 0x0501 - #endif + #define _WIN32_WINNT NAZARA_WINNT #endif #endif #elif defined(linux) || defined(__linux) diff --git a/include/Nazara/Renderer/OpenGL.hpp b/include/Nazara/Renderer/OpenGL.hpp index fc229991f..832d558a6 100644 --- a/include/Nazara/Renderer/OpenGL.hpp +++ b/include/Nazara/Renderer/OpenGL.hpp @@ -35,8 +35,9 @@ class NAZARA_API NzOpenGL { AnisotropicFilter, FP64, - Framebuffer_Object, + FrameBufferObject, Texture3D, + VertexArrayObject, Count }; @@ -56,6 +57,7 @@ NAZARA_API extern PFNGLBINDBUFFERPROC glBindBuffer; NAZARA_API extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; NAZARA_API extern PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer; NAZARA_API extern PFNGLBINDTEXTUREPROC glBindTexture; +NAZARA_API extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray; NAZARA_API extern PFNGLBLENDFUNCPROC glBlendFunc; NAZARA_API extern PFNGLBUFFERDATAPROC glBufferData; NAZARA_API extern PFNGLBUFFERSUBDATAPROC glBufferSubData; @@ -76,6 +78,7 @@ NAZARA_API extern PFNGLDELETEQUERIESPROC glDeleteQueries; NAZARA_API extern PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers; NAZARA_API extern PFNGLDELETESHADERPROC glDeleteShader; NAZARA_API extern PFNGLDELETETEXTURESPROC glDeleteTextures; +NAZARA_API extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays; NAZARA_API extern PFNGLDEPTHFUNCPROC glDepthFunc; NAZARA_API extern PFNGLDEPTHMASKPROC glDepthMask; NAZARA_API extern PFNGLDISABLEPROC glDisable; @@ -96,6 +99,7 @@ NAZARA_API extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; NAZARA_API extern PFNGLGENQUERIESPROC glGenQueries; NAZARA_API extern PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers; NAZARA_API extern PFNGLGENTEXTURESPROC glGenTextures; +NAZARA_API extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; NAZARA_API extern PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv; NAZARA_API extern PFNGLGETERRORPROC glGetError; NAZARA_API extern PFNGLGETINTEGERVPROC glGetIntegerv; diff --git a/include/Nazara/Renderer/Renderer.hpp b/include/Nazara/Renderer/Renderer.hpp index 6b509ece0..86cee1cba 100644 --- a/include/Nazara/Renderer/Renderer.hpp +++ b/include/Nazara/Renderer/Renderer.hpp @@ -8,6 +8,8 @@ #define NAZARA_RENDERER_HPP #include +#include +#include #define NazaraRenderer NzRenderer::Instance() @@ -45,6 +47,7 @@ enum nzRendererClear }; class NzColor; +class NzContext; class NzIndexBuffer; class NzRenderTarget; class NzShader; @@ -86,8 +89,11 @@ class NAZARA_API NzRenderer static bool IsInitialized(); private: - bool UpdateVertexBuffer(); + bool UpdateStates(); + typedef std::tuple VAO_Key; + + std::map m_vaos; const NzIndexBuffer* m_indexBuffer; NzRenderTarget* m_target; NzShader* m_shader; @@ -95,7 +101,7 @@ class NAZARA_API NzRenderer const NzVertexBuffer* m_vertexBuffer; const NzVertexDeclaration* m_vertexDeclaration; bool m_capabilities[nzRendererCap_Count]; - bool m_vertexBufferUpdated; + bool m_statesUpdated; static NzRenderer* s_instance; static bool s_initialized; diff --git a/src/Nazara/Renderer/GLSLShader.cpp b/src/Nazara/Renderer/GLSLShader.cpp index eda9240e8..af9403f89 100644 --- a/src/Nazara/Renderer/GLSLShader.cpp +++ b/src/Nazara/Renderer/GLSLShader.cpp @@ -6,14 +6,13 @@ #include #include #include -#include -#include #include #include namespace { - nzUInt8 attribIndex[] = + ///FIXME: Déclaré deux fois (ici et dans Renderer.cpp) + const nzUInt8 attribIndex[] = { 2, // nzElementUsage_Diffuse 1, // nzElementUsage_Normal @@ -27,32 +26,6 @@ namespace GL_GEOMETRY_SHADER, // nzShaderType_Geometry GL_VERTEX_SHADER // nzShaderType_Vertex }; - - const nzUInt8 size[] = - { - 4, // nzElementType_Color - 1, // nzElementType_Double1 - 2, // nzElementType_Double2 - 3, // nzElementType_Double3 - 4, // nzElementType_Double4 - 1, // nzElementType_Float1 - 2, // nzElementType_Float2 - 3, // nzElementType_Float3 - 4 // nzElementType_Float4 - }; - - const GLenum type[] = - { - GL_UNSIGNED_BYTE, // nzElementType_Color - GL_DOUBLE, // nzElementType_Double1 - GL_DOUBLE, // nzElementType_Double2 - GL_DOUBLE, // nzElementType_Double3 - GL_DOUBLE, // nzElementType_Double4 - GL_FLOAT, // nzElementType_Float1 - GL_FLOAT, // nzElementType_Float2 - GL_FLOAT, // nzElementType_Float3 - GL_FLOAT // nzElementType_Float4 - }; } NzGLSLShader::NzGLSLShader(NzShader* parent) : @@ -331,29 +304,3 @@ void NzGLSLShader::Unlock() const if (--m_lockedLevel == 0 && m_lockedPrevious != m_program) glUseProgram(m_lockedPrevious); } - -bool NzGLSLShader::UpdateVertexBuffer(const NzVertexBuffer* vertexBuffer, const NzVertexDeclaration* vertexDeclaration) -{ - vertexBuffer->GetBuffer()->GetImpl()->Bind(); - const nzUInt8* buffer = reinterpret_cast(vertexBuffer->GetBufferPtr()); - - ///FIXME: Améliorer les déclarations pour permettre de faire ça plus simplement - for (int i = 0; i < 12; ++i) // Solution temporaire, à virer - glDisableVertexAttribArray(i); // Chaque itération tue un chaton :( - - unsigned int stride = vertexDeclaration->GetStride(); - unsigned int elementCount = vertexDeclaration->GetElementCount(); - for (unsigned int i = 0; i < elementCount; ++i) - { - const NzVertexDeclaration::Element* element = vertexDeclaration->GetElement(i); - glEnableVertexAttribArray(attribIndex[element->usage]+element->usageIndex); - glVertexAttribPointer(attribIndex[element->usage]+element->usageIndex, - size[element->type], - type[element->type], - (element->type == nzElementType_Color) ? GL_TRUE : GL_FALSE, - stride, - &buffer[element->offset]); - } - - return true; -} diff --git a/src/Nazara/Renderer/GLSLShader.hpp b/src/Nazara/Renderer/GLSLShader.hpp index 86efaabaf..653955999 100644 --- a/src/Nazara/Renderer/GLSLShader.hpp +++ b/src/Nazara/Renderer/GLSLShader.hpp @@ -47,8 +47,6 @@ class NzGLSLShader : public NzShaderImpl void Unlock() const; private: - bool UpdateVertexBuffer(const NzVertexBuffer* vertexBuffer, const NzVertexDeclaration* vertexDeclaration); - mutable std::map m_idCache; mutable GLuint m_lockedPrevious; GLuint m_program; diff --git a/src/Nazara/Renderer/OpenGL.cpp b/src/Nazara/Renderer/OpenGL.cpp index e76b224c6..3dde42994 100644 --- a/src/Nazara/Renderer/OpenGL.cpp +++ b/src/Nazara/Renderer/OpenGL.cpp @@ -314,7 +314,7 @@ bool NzOpenGL::Initialize() } } - // Framebuffer_Object + // FrameBufferObject try { glBindFramebuffer = reinterpret_cast(LoadEntry("glBindFramebuffer")); @@ -329,7 +329,7 @@ bool NzOpenGL::Initialize() glGenRenderbuffers = reinterpret_cast(LoadEntry("glGenRenderbuffers")); glRenderbufferStorage = reinterpret_cast(LoadEntry("glRenderbufferStorage")); - openGLextensions[NzOpenGL::Framebuffer_Object] = true; + openGLextensions[NzOpenGL::FrameBufferObject] = true; } catch (const std::exception& e) { @@ -352,7 +352,7 @@ bool NzOpenGL::Initialize() glGenRenderbuffers = reinterpret_cast(LoadEntry("glGenRenderbuffersEXT")); glRenderbufferStorage = reinterpret_cast(LoadEntry("glRenderbufferStorageEXT")); - openGLextensions[NzOpenGL::Framebuffer_Object] = true; + openGLextensions[NzOpenGL::FrameBufferObject] = true; } catch (const std::exception& e) { @@ -377,8 +377,24 @@ bool NzOpenGL::Initialize() } } - /****************************************Contextes****************************************/ + // VertexArrayObject + if (openGLversion >= 300 || IsSupported("GL_ARB_vertex_array_object")) + { + try + { + glBindVertexArray = reinterpret_cast(LoadEntry("glBindVertexArray", false)); + glDeleteVertexArrays = reinterpret_cast(LoadEntry("glDeleteVertexArrays", false)); + glGenVertexArrays = reinterpret_cast(LoadEntry("glGenVertexArrays", false)); + openGLextensions[NzOpenGL::VertexArrayObject] = true; + } + catch (const std::exception& e) + { + NazaraError("Failed to load ARB_vertex_array_object: " + NzString(e.what())); + } + } + + /****************************************Contextes****************************************/ NzContextParameters::defaultMajorVersion = openGLversion/100; NzContextParameters::defaultMinorVersion = (openGLversion%100)/10; @@ -427,6 +443,7 @@ PFNGLBINDBUFFERPROC glBindBuffer = nullptr; PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = nullptr; PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer = nullptr; PFNGLBINDTEXTUREPROC glBindTexture = nullptr; +PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr; PFNGLBLENDFUNCPROC glBlendFunc = nullptr; PFNGLBUFFERDATAPROC glBufferData = nullptr; PFNGLBUFFERSUBDATAPROC glBufferSubData = nullptr; @@ -447,6 +464,7 @@ PFNGLDELETEQUERIESPROC glDeleteQueries = nullptr; PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers = nullptr; PFNGLDELETESHADERPROC glDeleteShader = nullptr; PFNGLDELETETEXTURESPROC glDeleteTextures = nullptr; +PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = nullptr; PFNGLDEPTHFUNCPROC glDepthFunc = nullptr; PFNGLDEPTHMASKPROC glDepthMask = nullptr; PFNGLDISABLEPROC glDisable = nullptr; @@ -467,6 +485,7 @@ PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = nullptr; PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers = nullptr; PFNGLGENQUERIESPROC glGenQueries = nullptr; PFNGLGENTEXTURESPROC glGenTextures = nullptr; +PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr; PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv = nullptr; PFNGLGETERRORPROC glGetError = nullptr; PFNGLGETINTEGERVPROC glGetIntegerv = nullptr; diff --git a/src/Nazara/Renderer/Renderer.cpp b/src/Nazara/Renderer/Renderer.cpp index 6daae067c..7de849aff 100644 --- a/src/Nazara/Renderer/Renderer.cpp +++ b/src/Nazara/Renderer/Renderer.cpp @@ -14,13 +14,23 @@ #include #include #include +#include #include #include #include namespace { - GLenum openglPrimitive[] = { + const nzUInt8 attribIndex[] = + { + 2, // nzElementUsage_Diffuse + 1, // nzElementUsage_Normal + 0, // nzElementUsage_Position + 3, // nzElementUsage_Tangent + 4 // nzElementUsage_TexCoord + }; + + const GLenum openglPrimitive[] = { GL_LINES, // nzPrimitiveType_LineList, GL_LINE_STRIP, // nzPrimitiveType_LineStrip, GL_POINTS, // nzPrimitiveType_PointList, @@ -28,6 +38,32 @@ namespace GL_TRIANGLE_STRIP, // nzPrimitiveType_TriangleStrip, GL_TRIANGLE_FAN // nzPrimitiveType_TriangleFan }; + + const nzUInt8 openglSize[] = + { + 4, // nzElementType_Color + 1, // nzElementType_Double1 + 2, // nzElementType_Double2 + 3, // nzElementType_Double3 + 4, // nzElementType_Double4 + 1, // nzElementType_Float1 + 2, // nzElementType_Float2 + 3, // nzElementType_Float3 + 4 // nzElementType_Float4 + }; + + const GLenum openglType[] = + { + GL_UNSIGNED_BYTE, // nzElementType_Color + GL_DOUBLE, // nzElementType_Double1 + GL_DOUBLE, // nzElementType_Double2 + GL_DOUBLE, // nzElementType_Double3 + GL_DOUBLE, // nzElementType_Double4 + GL_FLOAT, // nzElementType_Float1 + GL_FLOAT, // nzElementType_Float2 + GL_FLOAT, // nzElementType_Float3 + GL_FLOAT // nzElementType_Float4 + }; } NzRenderer::NzRenderer() : @@ -36,7 +72,7 @@ m_target(nullptr), m_shader(nullptr), m_vertexBuffer(nullptr), m_vertexDeclaration(nullptr), -m_vertexBufferUpdated(false) +m_statesUpdated(false) { #if NAZARA_RENDERER_SAFE if (s_instance) @@ -91,11 +127,11 @@ void NzRenderer::DrawIndexedPrimitives(nzPrimitiveType primitive, unsigned int f } #endif - if (!m_vertexBufferUpdated) + if (!m_statesUpdated) { - if (!UpdateVertexBuffer()) + if (!UpdateStates()) { - NazaraError("Failed to update vertex buffer"); + NazaraError("Failed to update states"); return; } } @@ -127,11 +163,11 @@ void NzRenderer::DrawIndexedPrimitives(nzPrimitiveType primitive, unsigned int f void NzRenderer::DrawPrimitives(nzPrimitiveType primitive, unsigned int firstVertex, unsigned int vertexCount) { - if (!m_vertexBufferUpdated) + if (!m_statesUpdated) { - if (!UpdateVertexBuffer()) + if (!UpdateStates()) { - NazaraError("Failed to update vertex buffer"); + NazaraError("Failed to update states"); return; } } @@ -191,7 +227,7 @@ bool NzRenderer::Initialize() m_capabilities[nzRendererCap_FP64] = NzOpenGL::IsSupported(NzOpenGL::FP64); m_capabilities[nzRendererCap_HardwareBuffer] = true; // Natif depuis OpenGL 1.5 m_capabilities[nzRendererCap_MultipleRenderTargets] = true; // Natif depuis OpenGL 2.0 - m_capabilities[nzRendererCap_OcclusionQuery] = // Natif depuis OpenGL 1.5 + m_capabilities[nzRendererCap_OcclusionQuery] = true; // Natif depuis OpenGL 1.5 m_capabilities[nzRendererCap_SoftwareBuffer] = NzOpenGL::GetVersion() <= 300; // Déprécié en OpenGL 3 m_capabilities[nzRendererCap_Texture3D] = NzOpenGL::IsSupported(NzOpenGL::Texture3D); m_capabilities[nzRendererCap_TextureCubemap] = true; // Natif depuis OpenGL 1.3 @@ -260,12 +296,11 @@ void NzRenderer::SetClearStencil(unsigned int value) bool NzRenderer::SetIndexBuffer(const NzIndexBuffer* indexBuffer) { - if (indexBuffer == m_indexBuffer) - return true; - - // OpenGL ne nécessite pas de débinder un index buffer pour ne pas l'utiliser - if (indexBuffer) - indexBuffer->GetBuffer()->m_impl->Bind(); + if (indexBuffer != m_indexBuffer) + { + m_indexBuffer = indexBuffer; + m_statesUpdated = false; + } return true; } @@ -292,7 +327,6 @@ bool NzRenderer::SetShader(NzShader* shader) } m_shader = shader; - m_vertexBufferUpdated = false; } else if (m_shader) { @@ -339,24 +373,22 @@ bool NzRenderer::SetTarget(NzRenderTarget* target) bool NzRenderer::SetVertexBuffer(const NzVertexBuffer* vertexBuffer) { - if (m_vertexBuffer == vertexBuffer) - return true; - - if (m_vertexBuffer && vertexBuffer) + if (m_vertexBuffer != vertexBuffer) { - // Si l'ancien buffer et le nouveau sont hardware, pas besoin de mettre à jour la déclaration - if (!m_vertexBuffer->IsHardware() || !vertexBuffer->IsHardware()) - m_vertexBufferUpdated = false; + m_vertexBuffer = vertexBuffer; + m_statesUpdated = false; } - m_vertexBuffer = vertexBuffer; return true; } bool NzRenderer::SetVertexDeclaration(const NzVertexDeclaration* vertexDeclaration) { - m_vertexDeclaration = vertexDeclaration; - m_vertexBufferUpdated = false; + if (m_vertexDeclaration != vertexDeclaration) + { + m_vertexDeclaration = vertexDeclaration; + m_statesUpdated = false; + } return true; } @@ -371,10 +403,17 @@ void NzRenderer::Uninitialize() } #endif - NzOpenGL::Uninitialize(); - s_initialized = false; + // Libération des VAOs + for (auto it = m_vaos.begin(); it != m_vaos.end(); ++it) + { + GLuint vao = it->second; + glDeleteVertexArrays(1, &vao); + } + + NzOpenGL::Uninitialize(); + if (m_utilityModule) { delete m_utilityModule; @@ -397,15 +436,9 @@ bool NzRenderer::IsInitialized() return s_initialized; } -bool NzRenderer::UpdateVertexBuffer() +bool NzRenderer::UpdateStates() { #if NAZARA_RENDERER_SAFE - if (!m_shader) - { - NazaraError("No shader"); - return false; - } - if (!m_vertexBuffer) { NazaraError("No vertex buffer"); @@ -419,10 +452,78 @@ bool NzRenderer::UpdateVertexBuffer() } #endif - if (!m_shader->m_impl->UpdateVertexBuffer(m_vertexBuffer, m_vertexDeclaration)) - return false; + static const bool vaoSupported = NzOpenGL::IsSupported(NzOpenGL::VertexArrayObject); + bool update; + GLuint vao; - m_vertexBufferUpdated = true; + // Si les VAOs sont supportés, on entoure nos appels par ceux-ci + if (vaoSupported) + { + // On recherche si un VAO existe déjà avec notre configuration + // Note: Les VAOs ne sont pas partagés entre les contextes, ils font donc partie de notre configuration + + auto key = std::make_tuple(NzContext::GetCurrent(), m_indexBuffer, m_vertexBuffer, m_vertexDeclaration); + auto it = m_vaos.find(key); + if (it == m_vaos.end()) + { + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + m_vaos.insert(std::make_pair(key, static_cast(vao))); + + update = true; + } + else + { + // Notre VAO existe déjà, il est donc inutile de le reprogrammer + vao = it->second; + + update = false; + } + } + else + update = true; // Fallback si les VAOs ne sont pas supportés + + if (update) + { + m_vertexBuffer->GetBuffer()->GetImpl()->Bind(); + + const nzUInt8* buffer = reinterpret_cast(m_vertexBuffer->GetBufferPtr()); + + ///FIXME: Améliorer les déclarations pour permettre de faire ça plus simplement + for (int i = 0; i < 12; ++i) // Solution temporaire, à virer + glDisableVertexAttribArray(i); // Chaque itération tue un chaton :( + + unsigned int stride = m_vertexDeclaration->GetStride(); + unsigned int elementCount = m_vertexDeclaration->GetElementCount(); + for (unsigned int i = 0; i < elementCount; ++i) + { + const NzVertexDeclaration::Element* element = m_vertexDeclaration->GetElement(i); + + glEnableVertexAttribArray(attribIndex[element->usage]+element->usageIndex); + glVertexAttribPointer(attribIndex[element->usage]+element->usageIndex, + openglSize[element->type], + openglType[element->type], + (element->type == nzElementType_Color) ? GL_TRUE : GL_FALSE, + stride, + &buffer[element->offset]); + } + + if (m_indexBuffer) + m_indexBuffer->GetBuffer()->GetImpl()->Bind(); + } + + if (vaoSupported) + { + // Si nous venons de définir notre VAO, nous devons le débinder pour indiquer la fin de sa construction + if (update) + glBindVertexArray(0); + + // Nous (re)bindons le VAO pour définir les attributs de vertice + glBindVertexArray(vao); + } + + m_statesUpdated = true; return true; } diff --git a/src/Nazara/Renderer/ShaderImpl.hpp b/src/Nazara/Renderer/ShaderImpl.hpp index 9d30cedbc..b5b1316ec 100644 --- a/src/Nazara/Renderer/ShaderImpl.hpp +++ b/src/Nazara/Renderer/ShaderImpl.hpp @@ -47,9 +47,6 @@ class NzShaderImpl virtual void Unbind() = 0; virtual void Unlock() const = 0; - - protected: - virtual bool UpdateVertexBuffer(const NzVertexBuffer* vertexBuffer, const NzVertexDeclaration* vertexDeclaration) = 0; }; #endif // NAZARA_SHADERIMPL_HPP diff --git a/src/Nazara/Utility/Image.cpp b/src/Nazara/Utility/Image.cpp index 4e80eae9a..5e76b5ce3 100644 --- a/src/Nazara/Utility/Image.cpp +++ b/src/Nazara/Utility/Image.cpp @@ -271,6 +271,14 @@ const nzUInt8* NzImage::GetConstPixels(nzUInt8 level) const unsigned int NzImage::GetDepth(nzUInt8 level) const { + #if NAZARA_UTILITY_SAFE + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return 0; + } + #endif + return std::max(m_sharedImage->depth/(1 << level), 1U); } @@ -281,6 +289,14 @@ nzPixelFormat NzImage::GetFormat() const unsigned int NzImage::GetHeight(nzUInt8 level) const { + #if NAZARA_UTILITY_SAFE + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return 0; + } + #endif + return std::max(m_sharedImage->height/(1 << level), 1U); } @@ -422,6 +438,14 @@ unsigned int NzImage::GetSize() const unsigned int NzImage::GetSize(nzUInt8 level) const { + #if NAZARA_UTILITY_SAFE + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return 0; + } + #endif + return (std::max(m_sharedImage->width/(1 << level), 1U)) * (std::max(m_sharedImage->height/(1 << level), 1U)) * ((m_sharedImage->type == nzImageType_Cubemap) ? 6 : std::min(m_sharedImage->depth/(1 << level), 1U)) * @@ -435,6 +459,14 @@ nzImageType NzImage::GetType() const unsigned int NzImage::GetWidth(nzUInt8 level) const { + #if NAZARA_UTILITY_SAFE + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return 0; + } + #endif + return std::max(m_sharedImage->width/(1 << level), 1U); } @@ -905,4 +937,4 @@ void NzImage::ReleaseImage() m_sharedImage = &emptyImage; } -NzImage::SharedImage NzImage::emptyImage(0, nzImageType_2D, nzPixelFormat_Undefined, 0, nullptr, 0, 0, 0); +NzImage::SharedImage NzImage::emptyImage(0, nzImageType_2D, nzPixelFormat_Undefined, 1, nullptr, 0, 0, 0);