diff --git a/include/Nazara/Renderer/OpenGL.hpp b/include/Nazara/Renderer/OpenGL.hpp index cd4c1a4d2..8b2761c84 100644 --- a/include/Nazara/Renderer/OpenGL.hpp +++ b/include/Nazara/Renderer/OpenGL.hpp @@ -74,6 +74,7 @@ NAZARA_API extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; NAZARA_API extern PFNGLCOLORMASKPROC glColorMask; NAZARA_API extern PFNGLCULLFACEPROC glCullFace; NAZARA_API extern PFNGLCOMPILESHADERPROC glCompileShader; +NAZARA_API extern PFNGLCOPYTEXSUBIMAGE2DPROC glCopyTexSubImage2D; NAZARA_API extern PFNGLDEBUGMESSAGECONTROLARBPROC glDebugMessageControl; NAZARA_API extern PFNGLDEBUGMESSAGEINSERTARBPROC glDebugMessageInsert; NAZARA_API extern PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallback; @@ -130,6 +131,7 @@ NAZARA_API extern PFNGLLINKPROGRAMPROC glLinkProgram; NAZARA_API extern PFNGLMAPBUFFERPROC glMapBuffer; NAZARA_API extern PFNGLMAPBUFFERRANGEPROC glMapBufferRange; NAZARA_API extern PFNGLPOLYGONMODEPROC glPolygonMode; +NAZARA_API extern PFNGLREADPIXELSPROC glReadPixels; NAZARA_API extern PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage; NAZARA_API extern PFNGLSCISSORPROC glScissor; NAZARA_API extern PFNGLSHADERSOURCEPROC glShaderSource; diff --git a/include/Nazara/Renderer/RenderWindow.hpp b/include/Nazara/Renderer/RenderWindow.hpp index 908cea023..2c43579ec 100644 --- a/include/Nazara/Renderer/RenderWindow.hpp +++ b/include/Nazara/Renderer/RenderWindow.hpp @@ -19,10 +19,14 @@ #endif class NzContext; +class NzImage; +class NzTexture; struct NzContextParameters; class NAZARA_API NzRenderWindow : public NzRenderTarget, public NzWindow { + friend class NzTexture; + public: NzRenderWindow(); NzRenderWindow(NzVideoMode mode, const NzString& title, nzUInt32 style = NzWindow::Default, const NzContextParameters& parameters = NzContextParameters()); @@ -31,6 +35,9 @@ class NAZARA_API NzRenderWindow : public NzRenderTarget, public NzWindow bool CanActivate() const; + bool CopyToImage(NzImage* image); ///TODO: Const + bool CopyToTexture(NzTexture* texture); ///TODO: Const + bool Create(NzVideoMode mode, const NzString& title, nzUInt32 style = NzWindow::Default, const NzContextParameters& parameters = NzContextParameters()); bool Create(NzWindowHandle handle, const NzContextParameters& parameters = NzContextParameters()); diff --git a/include/Nazara/Renderer/Renderer.hpp b/include/Nazara/Renderer/Renderer.hpp index 86cee1cba..aab95ab7c 100644 --- a/include/Nazara/Renderer/Renderer.hpp +++ b/include/Nazara/Renderer/Renderer.hpp @@ -13,6 +13,34 @@ #define NazaraRenderer NzRenderer::Instance() +enum nzBlendFunc +{ + nzBlendFunc_DestAlpha, + nzBlendFunc_DestColor, + nzBlendFunc_SrcAlpha, + nzBlendFunc_SrcColor, + nzBlendFunc_InvDestAlpha, + nzBlendFunc_InvDestColor, + nzBlendFunc_InvSrcAlpha, + nzBlendFunc_InvSrcColor, + nzBlendFunc_One, + nzBlendFunc_Zero +}; + +enum nzFaceCulling +{ + nzFaceCulling_Back, + nzFaceCulling_Front, + nzFaceCulling_FrontAndBack +}; + +enum nzFaceFilling +{ + nzFaceFilling_Point, + nzFaceFilling_Line, + nzFaceFilling_Fill +}; + enum nzPrimitiveType { nzPrimitiveType_LineList, @@ -46,6 +74,40 @@ enum nzRendererClear nzRendererClear_Stencil = 0x04 }; +enum nzRendererComparison +{ + nzRendererComparison_Always, + nzRendererComparison_Equal, + nzRendererComparison_Greater, + nzRendererComparison_GreaterOrEqual, + nzRendererComparison_Less, + nzRendererComparison_LessOrEqual, + nzRendererComparison_Never +}; + +enum nzRendererParameter +{ + nzRendererParameter_AlphaTest, + nzRendererParameter_Blend, + nzRendererParameter_ColorWrite, + nzRendererParameter_DepthTest, + nzRendererParameter_DepthWrite, + nzRendererParameter_FaceCulling, + nzRendererParameter_Stencil +}; + +enum nzStencilOperation +{ + nzStencilOperation_Decrement, + nzStencilOperation_DecrementToSaturation, + nzStencilOperation_Increment, + nzStencilOperation_IncrementToSaturation, + nzStencilOperation_Invert, + nzStencilOperation_Keep, + nzStencilOperation_Replace, + nzStencilOperation_Zero +}; + class NzColor; class NzContext; class NzIndexBuffer; @@ -66,6 +128,10 @@ class NAZARA_API NzRenderer void DrawIndexedPrimitives(nzPrimitiveType primitive, unsigned int firstIndex, unsigned int indexCount); void DrawPrimitives(nzPrimitiveType primitive, unsigned int firstVertex, unsigned int vertexCount); + void Enable(nzRendererParameter parameter, bool enable); + + unsigned int GetMaxAnisotropyLevel() const; + unsigned int GetMaxRenderTargets() const; unsigned int GetMaxTextureUnits() const; NzShader* GetShader() const; NzRenderTarget* GetTarget() const; @@ -77,8 +143,16 @@ class NAZARA_API NzRenderer void SetClearColor(nzUInt8 r, nzUInt8 g, nzUInt8 b, nzUInt8 a = 255); void SetClearDepth(double depth); void SetClearStencil(unsigned int value); + void SetFaceCulling(nzFaceCulling cullingMode); + void SetFaceFilling(nzFaceFilling fillingMode); bool SetIndexBuffer(const NzIndexBuffer* indexBuffer); bool SetShader(NzShader* shader); + void SetStencilCompareFunction(nzRendererComparison compareFunc); + void SetStencilFailOperation(nzStencilOperation failOperation); + void SetStencilMask(nzUInt32 mask); + void SetStencilPassOperation(nzStencilOperation passOperation); + void SetStencilReferenceValue(unsigned int refValue); + void SetStencilZFailOperation(nzStencilOperation zfailOperation); bool SetTarget(NzRenderTarget* target); bool SetVertexBuffer(const NzVertexBuffer* vertexBuffer); bool SetVertexDeclaration(const NzVertexDeclaration* vertexDeclaration); @@ -89,19 +163,30 @@ class NAZARA_API NzRenderer static bool IsInitialized(); private: - bool UpdateStates(); + bool EnsureStateUpdate(); typedef std::tuple VAO_Key; std::map m_vaos; + nzRendererComparison m_stencilCompare; + nzStencilOperation m_stencilFail; + nzStencilOperation m_stencilPass; + nzStencilOperation m_stencilZFail; + nzUInt32 m_stencilMask; const NzIndexBuffer* m_indexBuffer; NzRenderTarget* m_target; NzShader* m_shader; NzUtility* m_utilityModule; const NzVertexBuffer* m_vertexBuffer; const NzVertexDeclaration* m_vertexDeclaration; + bool m_vaoUpdated; bool m_capabilities[nzRendererCap_Count]; - bool m_statesUpdated; + bool m_stencilFuncUpdated; + bool m_stencilOpUpdated; + unsigned int m_maxAnisotropyLevel; + unsigned int m_maxRenderTarget; + unsigned int m_maxTextureUnit; + unsigned int m_stencilReference; static NzRenderer* s_instance; static bool s_initialized; diff --git a/include/Nazara/Renderer/Texture.hpp b/include/Nazara/Renderer/Texture.hpp index f038a86e9..01b69b115 100644 --- a/include/Nazara/Renderer/Texture.hpp +++ b/include/Nazara/Renderer/Texture.hpp @@ -27,7 +27,7 @@ enum nzTextureWrap nzTextureWrap_Unknown }; -class NzRenderWindow; ///TODO: Screenshot +class NzShader; struct NzTextureImpl; class NAZARA_API NzTexture : public NzResource, NzNonCopyable diff --git a/include/Nazara/Utility/Window.hpp b/include/Nazara/Utility/Window.hpp index e6b202df3..acd694f50 100644 --- a/include/Nazara/Utility/Window.hpp +++ b/include/Nazara/Utility/Window.hpp @@ -61,7 +61,7 @@ class NAZARA_API NzWindow : NzNonCopyable NzWindowHandle GetHandle() const; unsigned int GetHeight() const; NzVector2i GetPosition() const; - NzVector2i GetSize() const; + NzVector2ui GetSize() const; NzString GetTitle() const; unsigned int GetWidth() const; diff --git a/src/Nazara/Renderer/OpenGL.cpp b/src/Nazara/Renderer/OpenGL.cpp index 610e8c891..502cc1066 100644 --- a/src/Nazara/Renderer/OpenGL.cpp +++ b/src/Nazara/Renderer/OpenGL.cpp @@ -223,6 +223,7 @@ bool NzOpenGL::Initialize() glColorMask = reinterpret_cast(LoadEntry("glColorMask")); glCullFace = reinterpret_cast(LoadEntry("glCullFace")); glCompileShader = reinterpret_cast(LoadEntry("glCompileShader")); + glCopyTexSubImage2D = reinterpret_cast(LoadEntry("glCopyTexSubImage2D")); glDeleteBuffers = reinterpret_cast(LoadEntry("glDeleteBuffers")); glDeleteQueries = reinterpret_cast(LoadEntry("glDeleteQueries")); glDeleteProgram = reinterpret_cast(LoadEntry("glDeleteProgram")); @@ -263,6 +264,7 @@ bool NzOpenGL::Initialize() glLinkProgram = reinterpret_cast(LoadEntry("glLinkProgram")); glMapBuffer = reinterpret_cast(LoadEntry("glMapBuffer")); glPolygonMode = reinterpret_cast(LoadEntry("glPolygonMode")); + glReadPixels = reinterpret_cast(LoadEntry("glReadPixels")); glScissor = reinterpret_cast(LoadEntry("glScissor")); glShaderSource = reinterpret_cast(LoadEntry("glShaderSource")); glStencilFunc = reinterpret_cast(LoadEntry("glStencilFunc")); @@ -545,6 +547,7 @@ PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus = nullptr; PFNGLCOLORMASKPROC glColorMask = nullptr; PFNGLCULLFACEPROC glCullFace = nullptr; PFNGLCOMPILESHADERPROC glCompileShader = nullptr; +PFNGLCOPYTEXSUBIMAGE2DPROC glCopyTexSubImage2D = nullptr; PFNGLDEBUGMESSAGECONTROLARBPROC glDebugMessageControl = nullptr; PFNGLDEBUGMESSAGEINSERTARBPROC glDebugMessageInsert = nullptr; PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallback = nullptr; @@ -601,6 +604,7 @@ PFNGLLINKPROGRAMPROC glLinkProgram = nullptr; PFNGLMAPBUFFERPROC glMapBuffer = nullptr; PFNGLMAPBUFFERRANGEPROC glMapBufferRange = nullptr; PFNGLPOLYGONMODEPROC glPolygonMode = nullptr; +PFNGLREADPIXELSPROC glReadPixels = nullptr; PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = nullptr; PFNGLSCISSORPROC glScissor = nullptr; PFNGLSHADERSOURCEPROC glShaderSource = nullptr; diff --git a/src/Nazara/Renderer/RenderWindow.cpp b/src/Nazara/Renderer/RenderWindow.cpp index 6ce72547d..bc8c91db8 100644 --- a/src/Nazara/Renderer/RenderWindow.cpp +++ b/src/Nazara/Renderer/RenderWindow.cpp @@ -8,15 +8,10 @@ #include #include #include +#include #include #include -namespace -{ - NzContextParameters invalidContextParameters; - NzRenderTargetParameters invalidRTParameters; -} - NzRenderWindow::NzRenderWindow() : m_context(nullptr) { @@ -59,6 +54,82 @@ bool NzRenderWindow::CanActivate() const return m_impl != nullptr && m_context != nullptr; } +bool NzRenderWindow::CopyToImage(NzImage* image) +{ + #if NAZARA_RENDERER_SAFE + if (!m_context) + { + NazaraError("Window has not been created"); + return false; + } + + if (!image) + { + NazaraError("Image must be valid"); + return false; + } + #endif + + if (!m_context->SetActive(true)) + { + NazaraError("Failed to activate context"); + return false; + } + + NzVector2ui size = GetSize(); + + if (!image->Create(nzImageType_2D, nzPixelFormat_RGBA8, size.x, size.y, 1, 1)) + { + NazaraError("Failed to create image"); + return false; + } + + nzUInt8* pixels = image->GetPixels(); + glReadPixels(0, 0, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + for (unsigned int j = 0; j < size.y/2; ++j) + std::swap_ranges(&pixels[j*size.x*4], &pixels[(j+1)*size.x*4-1], &pixels[(size.y-j-1)*size.x*4]); + + return true; +} + +bool NzRenderWindow::CopyToTexture(NzTexture* texture) +{ + #if NAZARA_RENDERER_SAFE + if (!m_context) + { + NazaraError("Window has not been created"); + return false; + } + + if (!texture) + { + NazaraError("Texture must be valid"); + return false; + } + #endif + + if (!m_context->SetActive(true)) + { + NazaraError("Failed to activate context"); + return false; + } + + NzVector2ui size = GetSize(); + + if (!texture->Create(nzImageType_2D, nzPixelFormat_RGBA8, size.x, size.y, 1, 1, true)) + { + NazaraError("Failed to create texture"); + return false; + } + + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.x, size.y); + + texture->Unlock(); + + return true; +} + bool NzRenderWindow::Create(NzVideoMode mode, const NzString& title, nzUInt32 style, const NzContextParameters& parameters) { m_parameters = parameters; @@ -81,29 +152,29 @@ void NzRenderWindow::EnableVerticalSync(bool enabled) { if (m_context) { -#if defined(NAZARA_PLATFORM_WINDOWS) + #if defined(NAZARA_PLATFORM_WINDOWS) if (!m_context->SetActive(true)) - { - NazaraError("Unable to activate context"); - return; - } + { + NazaraError("Failed to activate context"); + return; + } if (wglSwapInterval) wglSwapInterval(enabled ? 1 : 0); else -#elif defined(NAZARA_PLATFORM_LINUX) + #elif defined(NAZARA_PLATFORM_LINUX) if (!m_context->SetActive(true)) { - NazaraError("Unable to activate context"); + NazaraError("Failed to activate context"); return; } if (glXSwapInterval) glXSwapInterval(enabled ? 1 : 0); else -#else - #error Vertical Sync is not supported on this platform -#endif + #else + #error Vertical Sync is not supported on this platform + #endif NazaraError("Vertical Sync is not supported on this platform"); } else diff --git a/src/Nazara/Renderer/Renderer.cpp b/src/Nazara/Renderer/Renderer.cpp index 05627ef61..11e18926e 100644 --- a/src/Nazara/Renderer/Renderer.cpp +++ b/src/Nazara/Renderer/Renderer.cpp @@ -30,6 +30,21 @@ namespace 4 // nzElementUsage_TexCoord }; + const GLenum faceCullingMode[] = + { + GL_BACK, // nzFaceCulling_Back + GL_FRONT, // nzFaceCulling_Front + GL_FRONT_AND_BACK // nzFaceCulling_FrontAndBack + }; + + const GLenum faceFillingMode[] = + { + GL_POINT, // nzFaceFilling_Point + GL_LINE, // nzFaceFilling_Line + GL_FILL // nzFaceFilling_Fill + }; + + const GLenum openglPrimitive[] = { GL_LINES, // nzPrimitiveType_LineList, @@ -65,15 +80,42 @@ namespace GL_FLOAT, // nzElementType_Float3 GL_FLOAT // nzElementType_Float4 }; + + const GLenum rendererComparison[] = + { + GL_ALWAYS, // nzRendererComparison_Always + GL_EQUAL, // nzRendererComparison_Equal + GL_GREATER, // nzRendererComparison_Greater + GL_GEQUAL, // nzRendererComparison_GreaterOrEqual + GL_LESS, // nzRendererComparison_Less + GL_LEQUAL, // nzRendererComparison_LessOrEqual + GL_NEVER // nzRendererComparison_Never + }; + + const GLenum rendererParameter[] = + { + GL_BLEND, // nzRendererParameter_Blend + GL_NONE, // nzRendererParameter_ColorWrite + GL_DEPTH_TEST, // nzRendererParameter_DepthTest + GL_NONE, // nzRendererParameter_DepthWrite + GL_CULL_FACE, // nzRendererParameter_FaceCulling + GL_STENCIL_TEST // nzRendererParameter_Stencil + }; + + const GLenum stencilOperation[] = + { + GL_DECR, // nzStencilOperation_Decrement + GL_DECR_WRAP, // nzStencilOperation_DecrementToSaturation + GL_INCR, // nzStencilOperation_Increment + GL_INCR_WRAP, // nzStencilOperation_IncrementToSaturation + GL_INVERT, // nzStencilOperation_Invert + GL_KEEP, // nzStencilOperation_Keep + GL_REPLACE, // nzStencilOperation_Replace + GL_ZERO // nzStencilOperation_Zero + }; } -NzRenderer::NzRenderer() : -m_indexBuffer(nullptr), -m_target(nullptr), -m_shader(nullptr), -m_vertexBuffer(nullptr), -m_vertexDeclaration(nullptr), -m_statesUpdated(false) +NzRenderer::NzRenderer() { #if NAZARA_RENDERER_SAFE if (s_instance) @@ -121,6 +163,12 @@ void NzRenderer::Clear(unsigned long flags) void NzRenderer::DrawIndexedPrimitives(nzPrimitiveType primitive, unsigned int firstIndex, unsigned int indexCount) { #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + if (!m_indexBuffer) { NazaraError("No index buffer"); @@ -128,13 +176,10 @@ void NzRenderer::DrawIndexedPrimitives(nzPrimitiveType primitive, unsigned int f } #endif - if (!m_statesUpdated) + if (!EnsureStateUpdate()) { - if (!UpdateStates()) - { - NazaraError("Failed to update states"); - return; - } + NazaraError("Failed to update states"); + return; } nzUInt8 indexSize = m_indexBuffer->GetIndexSize(); @@ -164,30 +209,58 @@ void NzRenderer::DrawIndexedPrimitives(nzPrimitiveType primitive, unsigned int f void NzRenderer::DrawPrimitives(nzPrimitiveType primitive, unsigned int firstVertex, unsigned int vertexCount) { - if (!m_statesUpdated) + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) { - if (!UpdateStates()) - { - NazaraError("Failed to update states"); - return; - } + NazaraError("No active context"); + return; + } + #endif + + if (!EnsureStateUpdate()) + { + NazaraError("Failed to update states"); + return; } glDrawArrays(openglPrimitive[primitive], firstVertex, vertexCount); } +void NzRenderer::Enable(nzRendererParameter parameter, bool enable) +{ + switch (parameter) + { + case nzRendererParameter_ColorWrite: + glColorMask(enable, enable, enable, enable); + break; + + case nzRendererParameter_DepthWrite: + glDepthMask(enable); + break; + + default: + if (enable) + glEnable(rendererParameter[parameter]); + else + glDisable(rendererParameter[parameter]); + + break; + } +} + +unsigned int NzRenderer::GetMaxAnisotropyLevel() const +{ + return m_maxAnisotropyLevel; +} + +unsigned int NzRenderer::GetMaxRenderTargets() const +{ + return m_maxRenderTarget; +} + unsigned int NzRenderer::GetMaxTextureUnits() const { - static int maxTextureUnits = -1; - if (maxTextureUnits == -1) - { - if (m_capabilities[nzRendererCap_TextureMulti]) - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); - else - maxTextureUnits = 1; - } - - return maxTextureUnits; + return m_maxTextureUnit; } NzShader* NzRenderer::GetShader() const @@ -224,6 +297,22 @@ bool NzRenderer::Initialize() if (NzOpenGL::Initialize()) { + m_vaoUpdated = false; + m_indexBuffer = nullptr; + m_shader = nullptr; + m_stencilCompare = nzRendererComparison_Always; + m_stencilFail = nzStencilOperation_Keep; + m_stencilFuncUpdated = true; + m_stencilMask = 0xFFFFFFFF; + m_stencilOpUpdated = true; + m_stencilPass = nzStencilOperation_Keep; + m_stencilReference = 0; + m_stencilZFail = nzStencilOperation_Keep; + m_target = nullptr; + m_vertexBuffer = nullptr; + m_vertexDeclaration = nullptr; + + // Récupération des capacités m_capabilities[nzRendererCap_AnisotropicFilter] = NzOpenGL::IsSupported(NzOpenGL::AnisotropicFilter); m_capabilities[nzRendererCap_FP64] = NzOpenGL::IsSupported(NzOpenGL::FP64); m_capabilities[nzRendererCap_HardwareBuffer] = true; // Natif depuis OpenGL 1.5 @@ -235,6 +324,40 @@ bool NzRenderer::Initialize() m_capabilities[nzRendererCap_TextureMulti] = true; // Natif depuis OpenGL 1.3 m_capabilities[nzRendererCap_TextureNPOT] = true; // Natif depuis OpenGL 2.0 + if (m_capabilities[nzRendererCap_AnisotropicFilter]) + { + GLint maxAnisotropy; + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy); + + m_maxAnisotropyLevel = static_cast(maxAnisotropy); + } + else + m_maxAnisotropyLevel = 1; + + if (m_capabilities[nzRendererCap_MultipleRenderTargets]) + { + // Permettre de gérer plus de targets que de nombre de sorties dans le shader ne servirait à rien + GLint maxDrawBuffers; + glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); + + GLint maxRenderTextureTargets; + glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxRenderTextureTargets); + + m_maxRenderTarget = static_cast(std::min(maxDrawBuffers, maxRenderTextureTargets)); + } + else + m_maxRenderTarget = 1; + + if (m_capabilities[nzRendererCap_TextureMulti]) + { + GLint maxTextureUnits; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); + + m_maxTextureUnit = static_cast(maxTextureUnits); + } + else + m_maxTextureUnit = 1; + s_initialized = true; return true; @@ -295,12 +418,39 @@ void NzRenderer::SetClearStencil(unsigned int value) glClearStencil(value); } +void NzRenderer::SetFaceCulling(nzFaceCulling cullingMode) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + glCullFace(faceCullingMode[cullingMode]); +} + +void NzRenderer::SetFaceFilling(nzFaceFilling fillingMode) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + glPolygonMode(GL_FRONT_AND_BACK, faceFillingMode[fillingMode]); +} + + bool NzRenderer::SetIndexBuffer(const NzIndexBuffer* indexBuffer) { if (indexBuffer != m_indexBuffer) { m_indexBuffer = indexBuffer; - m_statesUpdated = false; + m_vaoUpdated = false; } return true; @@ -332,13 +482,67 @@ bool NzRenderer::SetShader(NzShader* shader) NazaraError("Failed to bind shader"); return false; } - - m_shader = shader; } + m_shader = shader; + return true; } +void NzRenderer::SetStencilCompareFunction(nzRendererComparison compareFunc) +{ + if (compareFunc != m_stencilCompare) + { + m_stencilCompare = compareFunc; + m_stencilFuncUpdated = false; + } +} + +void NzRenderer::SetStencilFailOperation(nzStencilOperation failOperation) +{ + if (failOperation != m_stencilFail) + { + m_stencilFail = failOperation; + m_stencilOpUpdated = false; + } +} + +void NzRenderer::SetStencilMask(nzUInt32 mask) +{ + if (mask != m_stencilMask) + { + m_stencilMask = mask; + m_stencilFuncUpdated = false; + } +} + +void NzRenderer::SetStencilPassOperation(nzStencilOperation passOperation) +{ + if (passOperation != m_stencilPass) + { + m_stencilPass = passOperation; + m_stencilOpUpdated = false; + } +} + +void NzRenderer::SetStencilReferenceValue(unsigned int refValue) +{ + if (refValue != m_stencilReference) + { + m_stencilReference = refValue; + m_stencilFuncUpdated = false; + } +} + +void NzRenderer::SetStencilZFailOperation(nzStencilOperation zfailOperation) +{ + if (zfailOperation != m_stencilZFail) + { + m_stencilZFail = zfailOperation; + m_stencilOpUpdated = false; + } +} + bool NzRenderer::SetTarget(NzRenderTarget* target) { if (target == m_target) @@ -378,7 +582,7 @@ bool NzRenderer::SetVertexBuffer(const NzVertexBuffer* vertexBuffer) if (m_vertexBuffer != vertexBuffer) { m_vertexBuffer = vertexBuffer; - m_statesUpdated = false; + m_vaoUpdated = false; } return true; @@ -389,7 +593,7 @@ bool NzRenderer::SetVertexDeclaration(const NzVertexDeclaration* vertexDeclarati if (m_vertexDeclaration != vertexDeclaration) { m_vertexDeclaration = vertexDeclaration; - m_statesUpdated = false; + m_vaoUpdated = false; } return true; @@ -438,98 +642,113 @@ bool NzRenderer::IsInitialized() return s_initialized; } -bool NzRenderer::UpdateStates() +bool NzRenderer::EnsureStateUpdate() { - #if NAZARA_RENDERER_SAFE - if (!m_vertexBuffer) + if (!m_stencilFuncUpdated) { - NazaraError("No vertex buffer"); - return false; + glStencilFunc(rendererComparison[m_stencilCompare], m_stencilReference, m_stencilMask); + m_stencilFuncUpdated = true; } - if (!m_vertexDeclaration) + if (!m_stencilOpUpdated) { - NazaraError("No vertex declaration"); - return false; + glStencilOp(stencilOperation[m_stencilFail], stencilOperation[m_stencilZFail], stencilOperation[m_stencilPass]); + m_stencilOpUpdated = true; } - #endif - static const bool vaoSupported = NzOpenGL::IsSupported(NzOpenGL::VertexArrayObject); - bool update; - GLuint vao; - - // Si les VAOs sont supportés, on entoure nos appels par ceux-ci - if (vaoSupported) + if (!m_vaoUpdated) { - // On recherche si un VAO existe déjà avec notre configuration - // Note: Les VAOs ne sont pas partagés entre les contextes, ces derniers 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()) + #if NAZARA_RENDERER_SAFE + if (!m_vertexBuffer) { - // On créé notre VAO - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); + NazaraError("No vertex buffer"); + return false; + } - // On l'ajoute à notre liste - m_vaos.insert(std::make_pair(key, static_cast(vao))); + if (!m_vertexDeclaration) + { + NazaraError("No vertex declaration"); + return false; + } + #endif - // Et on indique qu'on veut le programmer - update = true; + static const bool vaoSupported = NzOpenGL::IsSupported(NzOpenGL::VertexArrayObject); + bool update; + GLuint vao; + + // 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, ces derniers 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()) + { + // On créé notre VAO + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + // On l'ajoute à notre liste + m_vaos.insert(std::make_pair(key, static_cast(vao))); + + // Et on indique qu'on veut le programmer + update = true; + } + else + { + // Notre VAO existe déjà, il est donc inutile de le reprogrammer + vao = it->second; + + update = false; + } } else - { - // Notre VAO existe déjà, il est donc inutile de le reprogrammer - vao = it->second; + update = true; // Fallback si les VAOs ne sont pas supportés - 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); + { + m_vertexBuffer->GetBuffer()->GetImpl()->Bind(); - // Nous (re)bindons le VAO pour définir les attributs de vertice - glBindVertexArray(vao); + 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_vaoUpdated = true; } - m_statesUpdated = true; - return true; } diff --git a/src/Nazara/Renderer/Texture.cpp b/src/Nazara/Renderer/Texture.cpp index 36dad7d8f..a69dca501 100644 --- a/src/Nazara/Renderer/Texture.cpp +++ b/src/Nazara/Renderer/Texture.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -459,7 +460,7 @@ bool NzTexture::Download(NzImage* image) const if (!image) { - NazaraError("Cannot download to a null image"); + NazaraError("Image must be valid"); return false; } #endif @@ -479,12 +480,40 @@ bool NzTexture::Download(NzImage* image) const LockTexture(m_impl); + unsigned int width = m_impl->width; + unsigned int height = m_impl->height; + unsigned int depth = m_impl->depth; + nzUInt8 bpp = NzPixelFormat::GetBPP(m_impl->format); + + nzUInt8* mirrored = new nzUInt8[width*height*depth*bpp]; + // Téléchargement... for (nzUInt8 level = 0; level < m_impl->levelCount; ++level) - glGetTexImage(openglTarget[m_impl->type], level, format.dataFormat, format.dataType, image->GetPixels(level)); + { + glGetTexImage(openglTarget[m_impl->type], level, format.dataFormat, format.dataType, mirrored); + + // Inversion de la texture pour le repère d'OpenGL + ///FIXME: Gérer l'inversion dans NzImage, et gérer également les images compressées + unsigned int faceSize = width*height*bpp; + + nzUInt8* ptr = mirrored; + for (unsigned int d = 0; d < depth; ++d) + { + for (unsigned int j = 0; j < height/2; ++j) + std::swap_ranges(&ptr[j*width*bpp], &ptr[(j+1)*width*bpp-1], &ptr[(height-j-1)*width*bpp]); + + ptr += faceSize; + } + + width = std::max(width >> 1, 1U); + height = std::max(height >> 1, 1U); + depth = std::max(depth >> 1, 1U); + } UnlockTexture(m_impl); + delete[] mirrored; + return true; } @@ -534,12 +563,12 @@ unsigned int NzTexture::GetAnisotropyLevel() const LockTexture(m_impl); - GLfloat anisotropyLevel; - glGetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropyLevel); + GLint anisotropyLevel; + glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropyLevel); UnlockTexture(m_impl); - return static_cast(anisotropyLevel); + return anisotropyLevel; } nzUInt8 NzTexture::GetBPP() const @@ -853,7 +882,7 @@ bool NzTexture::SetAnisotropyLevel(unsigned int anistropyLevel) LockTexture(m_impl); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, static_cast(anistropyLevel)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anistropyLevel); UnlockTexture(m_impl); @@ -1058,56 +1087,16 @@ bool NzTexture::Update(const nzUInt8* pixels, nzUInt8 level) NazaraError("Texture must be valid"); return false; } - - if (m_impl->type == nzImageType_Cubemap) - { - NazaraError("Update is not designed for cubemaps, use UpdateFace instead"); - return false; - } - - if (!pixels) - { - NazaraError("Invalid pixel source"); - return false; - } - - if (level >= m_impl->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_impl->levelCount) + ')'); - return false; - } #endif - OpenGLFormat format; - if (!GetOpenGLFormat(m_impl->format, &format)) + if (m_impl->type == nzImageType_3D) { - NazaraError("Failed to get OpenGL format"); + NazaraInternalError("Not implemented yet, sorry"); return false; + //return Update(pixels, NzCube(0, 0, 0, std::max(m_impl->width >> level, 1U), std::max(m_impl->height >> level, 1U), std::max(m_impl->depth >> level, 1U)), level); } - - LockTexture(m_impl); - - switch (m_impl->type) - { - case nzImageType_1D: - glTexSubImage1D(GL_TEXTURE_1D, level, 0, std::max(m_impl->width >> level, 1U), format.dataFormat, format.dataType, pixels); - break; - - case nzImageType_2D: - glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, std::max(m_impl->width >> level, 1U), std::max(m_impl->height >> level, 1U), format.dataFormat, format.dataType, pixels); - break; - - case nzImageType_3D: - glTexSubImage3D(GL_TEXTURE_3D, level, 0, 0, 0, std::max(m_impl->width >> level, 1U), std::max(m_impl->height >> level, 1U), std::max(m_impl->depth >> level, 1U), format.dataFormat, format.dataType, pixels); - break; - - default: - NazaraInternalError("Image type not handled (0x" + NzString::Number(m_impl->type, 16) + ')'); - } - - UnlockTexture(m_impl); - - return true; + else + return Update(pixels, NzRectui(0, 0, std::max(m_impl->width >> level, 1U), std::max(m_impl->height >> level, 1U)), 0, level); } bool NzTexture::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z, nzUInt8 level) @@ -1163,20 +1152,32 @@ bool NzTexture::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int return false; } + nzUInt8 bpp = NzPixelFormat::GetBPP(m_impl->format); + + // Inversion de la texture pour le repère d'OpenGL + ///FIXME: Gérer l'inversion dans NzImage, et gérer également les images compressées + unsigned int size = rect.width*rect.height*bpp; + nzUInt8* mirrored = new nzUInt8[size]; + std::memcpy(mirrored, pixels, size); + + nzUInt8* ptr = &mirrored[size*z]; + for (unsigned int j = 0; j < rect.height/2; ++j) + std::swap_ranges(&ptr[j*rect.width*bpp], &ptr[(j+1)*rect.width*bpp-1], &ptr[(rect.height-j-1)*rect.width*bpp]); + LockTexture(m_impl); switch (m_impl->type) { case nzImageType_1D: - glTexSubImage1D(GL_TEXTURE_1D, level, rect.x, rect.width, format.dataFormat, format.dataType, pixels); + glTexSubImage1D(GL_TEXTURE_1D, level, rect.x, rect.width, format.dataFormat, format.dataType, mirrored); break; case nzImageType_2D: - glTexSubImage2D(GL_TEXTURE_2D, level, rect.x, rect.y, rect.width, rect.height, format.dataFormat, format.dataType, pixels); + glTexSubImage2D(GL_TEXTURE_2D, level, rect.x, rect.y, rect.width, rect.height, format.dataFormat, format.dataType, mirrored); break; case nzImageType_3D: - glTexSubImage3D(GL_TEXTURE_3D, level, rect.x, rect.y, z, rect.width, rect.height, 1, format.dataFormat, format.dataType, pixels); + glTexSubImage3D(GL_TEXTURE_3D, level, rect.x, rect.y, z, rect.width, rect.height, 1, format.dataFormat, format.dataType, mirrored); break; default: @@ -1185,6 +1186,8 @@ bool NzTexture::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int UnlockTexture(m_impl); + delete[] mirrored; + return true; } /* @@ -1237,20 +1240,38 @@ bool NzTexture::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 leve return false; } + nzUInt8 bpp = NzPixelFormat::GetBPP(m_impl->format); + + // Inversion de la texture pour le repère d'OpenGL + ///FIXME: Gérer l'inversion dans NzImage, et gérer également les images compressées + unsigned int faceSize = cube.width*cube.height*bpp; + unsigned int size = faceSize*cube.depth; + nzUInt8* mirrored = new nzUInt8[size]; + std::memcpy(mirrored, pixels, size); + + nzUInt8* ptr = mirrored; + for (unsigned int d = 0; d < cube.depth; ++d) + { + for (unsigned int j = 0; j < cube.height/2; ++j) + std::swap_ranges(&ptr[j*cube.width*bpp], &ptr[(j+1)*cube.width*bpp-1], &ptr[(cube.height-j-1)*cube.width*bpp]); + + ptr += faceSize; + } + LockTexture(m_impl); switch (m_impl->type) { case nzImageType_1D: - glTexSubImage1D(GL_TEXTURE_1D, level, cube.x, cube.width, format->dataFormat, format->dataType, pixels); + glTexSubImage1D(GL_TEXTURE_1D, level, cube.x, cube.width, format->dataFormat, format->dataType, mirrored); break; case nzImageType_2D: - glTexSubImage1D(GL_TEXTURE_2D, level, cube.x, cube.y, cube.width, cube.height, format->dataFormat, format->dataType, pixels); + glTexSubImage1D(GL_TEXTURE_2D, level, cube.x, cube.y, cube.width, cube.height, format->dataFormat, format->dataType, mirrored); break; case nzImageType_3D: - glTexSubImage1D(GL_TEXTURE_3D, level, cube.x, cube.y, cube.z, cube.width, cube.height, cube.depth, format->dataFormat, format->dataType, pixels); + glTexSubImage1D(GL_TEXTURE_3D, level, cube.x, cube.y, cube.z, cube.width, cube.height, cube.depth, format->dataFormat, format->dataType, mirrored); break; default: @@ -1259,6 +1280,8 @@ bool NzTexture::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 leve UnlockTexture(m_impl); + delete[] mirrored; + return true; } */ @@ -1278,7 +1301,7 @@ bool NzTexture::UpdateFace(nzCubemapFace face, const NzImage& image, nzUInt8 lev } #endif - return UpdateFace(face, image.GetConstPixels(level), level); + return UpdateFace(face, image.GetConstPixels(level), NzRectui(0, 0, image.GetWidth(), image.GetHeight()), level); } bool NzTexture::UpdateFace(nzCubemapFace face, const NzImage& image, const NzRectui& rect, nzUInt8 level) @@ -1308,40 +1331,9 @@ bool NzTexture::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, nzUInt8 le NazaraError("Texture must be valid"); return false; } - - if (m_impl->type != nzImageType_Cubemap) - { - NazaraError("UpdateFace is designed for cubemaps, use Update instead"); - return false; - } - - if (!pixels) - { - NazaraError("Invalid pixel source"); - return false; - } - - if (level >= m_impl->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_impl->levelCount) + ')'); - return false; - } #endif - OpenGLFormat format; - if (!GetOpenGLFormat(m_impl->format, &format)) - { - NazaraError("Failed to get OpenGL format"); - return false; - } - - LockTexture(m_impl); - - glTexSubImage2D(cubemapFace[face], level, 0, 0, m_impl->width, m_impl->height, format.dataFormat, format.dataType, pixels); - - UnlockTexture(m_impl); - - return true; + return UpdateFace(face, pixels, NzRectui(0, 0, m_impl->width, m_impl->height), level); } bool NzTexture::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, const NzRectui& rect, nzUInt8 level) @@ -1391,12 +1383,24 @@ bool NzTexture::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, const NzRe return false; } + nzUInt8 bpp = NzPixelFormat::GetBPP(m_impl->format); + + // Inversion de la texture pour le repère d'OpenGL + ///FIXME: Gérer l'inversion dans NzImage, et gérer également les images compressées + unsigned int size = rect.width*rect.height*bpp; + nzUInt8* mirrored = new nzUInt8[size]; + std::memcpy(mirrored, pixels, size); + for (unsigned int j = 0; j < rect.height/2; ++j) + std::swap_ranges(&mirrored[j*rect.width*bpp], &mirrored[(j+1)*rect.width*bpp-1], &mirrored[(rect.height-j-1)*rect.width*bpp]); + LockTexture(m_impl); - glTexSubImage2D(cubemapFace[face], level, rect.x, rect.y, rect.width, rect.height, format.dataFormat, format.dataType, pixels); + glTexSubImage2D(cubemapFace[face], level, rect.x, rect.y, rect.width, rect.height, format.dataFormat, format.dataType, mirrored); UnlockTexture(m_impl); + delete[] mirrored; + return true; } diff --git a/src/Nazara/Utility/Image.cpp b/src/Nazara/Utility/Image.cpp index b9cb4d8dd..aeca004a7 100644 --- a/src/Nazara/Utility/Image.cpp +++ b/src/Nazara/Utility/Image.cpp @@ -729,8 +729,7 @@ bool NzImage::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z EnsureOwnership(); nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); - - nzUInt8* dstPixels = &m_sharedImage->pixels[level][(m_sharedImage->height*(m_sharedImage->width*z + rect.y) + rect.x) * NzPixelFormat::GetBPP(m_sharedImage->format)]; + nzUInt8* dstPixels = &m_sharedImage->pixels[level][(m_sharedImage->height*(m_sharedImage->width*z + rect.y) + rect.x) * bpp]; unsigned int srcStride = rect.width * bpp; unsigned int blockSize = m_sharedImage->width * bpp; for (unsigned int i = 0; i < rect.height; ++i) @@ -822,8 +821,7 @@ bool NzImage::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, const NzRect EnsureOwnership(); nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); - - nzUInt8* dstPixels = &m_sharedImage->pixels[level][(m_sharedImage->height*(m_sharedImage->width*(face-nzCubemapFace_PositiveX) + rect.y) + rect.x) * NzPixelFormat::GetBPP(m_sharedImage->format)]; + nzUInt8* dstPixels = &m_sharedImage->pixels[level][(m_sharedImage->height*(m_sharedImage->width*(face-nzCubemapFace_PositiveX) + rect.y) + rect.x) * bpp]; unsigned int srcStride = rect.width * bpp; unsigned int blockSize = m_sharedImage->width * bpp; for (unsigned int i = 0; i < rect.height; ++i) diff --git a/src/Nazara/Utility/Win32/WindowImpl.cpp b/src/Nazara/Utility/Win32/WindowImpl.cpp index f020cbdfa..946045a5c 100644 --- a/src/Nazara/Utility/Win32/WindowImpl.cpp +++ b/src/Nazara/Utility/Win32/WindowImpl.cpp @@ -202,11 +202,11 @@ NzVector2i NzWindowImpl::GetPosition() const return NzVector2i(rect.left, rect.top); } -NzVector2i NzWindowImpl::GetSize() const +NzVector2ui NzWindowImpl::GetSize() const { RECT rect; GetClientRect(m_handle, &rect); - return NzVector2i(rect.right-rect.left, rect.bottom-rect.top); + return NzVector2ui(rect.right-rect.left, rect.bottom-rect.top); } NzString NzWindowImpl::GetTitle() const @@ -689,7 +689,7 @@ bool NzWindowImpl::HandleMessage(HWND window, UINT message, WPARAM wParam, LPARA { if (wParam != SIZE_MINIMIZED) { - NzVector2i size = GetSize(); // On récupère uniquement la taille de la zone client + NzVector2ui size = GetSize(); // On récupère uniquement la taille de la zone client NzEvent event; event.type = NzEvent::Resized; diff --git a/src/Nazara/Utility/Win32/WindowImpl.hpp b/src/Nazara/Utility/Win32/WindowImpl.hpp index 48791c68c..2cc727ab0 100644 --- a/src/Nazara/Utility/Win32/WindowImpl.hpp +++ b/src/Nazara/Utility/Win32/WindowImpl.hpp @@ -45,7 +45,7 @@ class NzWindowImpl : NzNonCopyable NzWindowHandle GetHandle() const; unsigned int GetHeight() const; NzVector2i GetPosition() const; - NzVector2i GetSize() const; + NzVector2ui GetSize() const; NzString GetTitle() const; unsigned int GetWidth() const; diff --git a/src/Nazara/Utility/Window.cpp b/src/Nazara/Utility/Window.cpp index dbe29b835..1ef9c3de8 100644 --- a/src/Nazara/Utility/Window.cpp +++ b/src/Nazara/Utility/Window.cpp @@ -222,12 +222,12 @@ NzVector2i NzWindow::GetPosition() const return NzVector2i(0); } -NzVector2i NzWindow::GetSize() const +NzVector2ui NzWindow::GetSize() const { if (m_impl) return m_impl->GetSize(); else - return NzVector2i(0); + return NzVector2ui(0U); } NzString NzWindow::GetTitle() const