// Copyright (C) 2014 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { enum ResourceType { ResourceType_Context, ResourceType_IndexBuffer, ResourceType_VertexBuffer, ResourceType_VertexDeclaration }; enum UpdateFlags { Update_None = 0, Update_Matrices = 0x1, Update_Shader = 0x2, Update_Textures = 0x4, Update_VAO = 0x8 }; struct MatrixUnit { NzMatrix4f matrix; bool updated; int location; }; struct TextureUnit { NzTextureSampler sampler; const NzTexture* texture = nullptr; bool samplerUpdated = false; }; using VAO_Key = std::tuple; using VAO_Map = std::map; using Context_Map = std::unordered_map; Context_Map s_vaos; std::vector s_dirtyTextureUnits; std::vector s_textureUnits; GLuint s_currentVAO = 0; NzVertexBuffer s_instanceBuffer; NzVertexBuffer s_fullscreenQuadBuffer; MatrixUnit s_matrices[nzMatrixType_Max+1]; NzRenderStates s_states; NzVector2ui s_targetSize; nzUInt8 s_maxAnisotropyLevel; nzUInt32 s_updateFlags; const NzIndexBuffer* s_indexBuffer; const NzRenderTarget* s_target; const NzShader* s_shader; const NzVertexBuffer* s_vertexBuffer; bool s_capabilities[nzRendererCap_Max+1]; bool s_instancing; bool s_useSamplerObjects; bool s_useVertexArrayObjects; unsigned int s_maxColorAttachments; unsigned int s_maxRenderTarget; unsigned int s_maxTextureUnit; unsigned int s_maxVertexAttribs; class ResourceListener : public NzResourceListener { public: void OnResourceReleased(const NzResource* resource, int index) override { switch (index) { case ResourceType_Context: { const NzContext* context = static_cast(resource); s_vaos.erase(context); break; } case ResourceType_IndexBuffer: { const NzIndexBuffer* indexBuffer = static_cast(resource); for (auto& pair : s_vaos) { const NzContext* context = pair.first; VAO_Map& vaos = pair.second; auto it = vaos.begin(); while (it != vaos.end()) { const VAO_Key& key = it->first; const NzIndexBuffer* vaoIndexBuffer = std::get<0>(key); if (vaoIndexBuffer == indexBuffer) { // Suppression du VAO: // Comme celui-ci est local à son contexte de création, sa suppression n'est possible que si // son contexte d'origine est actif, sinon il faudra le mettre en file d'attente // Ceci est géré par la méthode OpenGL::DeleteVertexArray NzOpenGL::DeleteVertexArray(context, it->second); vaos.erase(it++); } else ++it; } } break; } case ResourceType_VertexBuffer: { const NzVertexBuffer* vertexBuffer = static_cast(resource); for (auto& pair : s_vaos) { const NzContext* context = pair.first; VAO_Map& vaos = pair.second; auto it = vaos.begin(); while (it != vaos.end()) { const VAO_Key& key = it->first; const NzVertexBuffer* vaoVertexBuffer = std::get<1>(key); if (vaoVertexBuffer == vertexBuffer) { // Suppression du VAO: // Comme celui-ci est local à son contexte de création, sa suppression n'est possible que si // son contexte d'origine est actif, sinon il faudra le mettre en file d'attente // Ceci est géré par la méthode OpenGL::DeleteVertexArray NzOpenGL::DeleteVertexArray(context, it->second); vaos.erase(it++); } else ++it; } } break; } case ResourceType_VertexDeclaration: { const NzVertexDeclaration* vertexDeclaration = static_cast(resource); for (auto& pair : s_vaos) { const NzContext* context = pair.first; VAO_Map& vaos = pair.second; auto it = vaos.begin(); while (it != vaos.end()) { const VAO_Key& key = it->first; const NzVertexDeclaration* vaoVertexDeclaration = std::get<2>(key); const NzVertexDeclaration* vaoInstancingDeclaration = std::get<3>(key); if (vaoVertexDeclaration == vertexDeclaration || vaoInstancingDeclaration == vertexDeclaration) { // Suppression du VAO: // Comme celui-ci est local à son contexte de création, sa suppression n'est possible que si // son contexte d'origine est actif, sinon il faudra le mettre en file d'attente // Ceci est géré par la méthode OpenGL::DeleteVertexArray NzOpenGL::DeleteVertexArray(context, it->second); vaos.erase(it++); } else ++it; } } break; } default: NazaraInternalError("Unknown resource type"); break; } } }; ResourceListener s_listener; } void NzRenderer::BeginCondition(const NzGpuQuery& query, nzGpuQueryCondition condition) { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } #endif #if NAZARA_RENDERER_SAFE if (!s_capabilities[nzRendererCap_ConditionalRendering]) { NazaraError("Conditional rendering is not supported"); return; } #endif glBeginConditionalRender(query.GetOpenGLID(), NzOpenGL::QueryCondition[condition]); } void NzRenderer::Clear(nzUInt32 flags) { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } #endif if (flags) { // On n'oublie pas de mettre à jour la cible s_target->EnsureTargetUpdated(); // Les états du rendu sont suceptibles d'influencer glClear NzOpenGL::ApplyStates(s_states); GLenum mask = 0; if (flags & nzRendererBuffer_Color) mask |= GL_COLOR_BUFFER_BIT; if (flags & nzRendererBuffer_Depth) mask |= GL_DEPTH_BUFFER_BIT; if (flags & nzRendererBuffer_Stencil) mask |= GL_STENCIL_BUFFER_BIT; glClear(mask); } } void NzRenderer::DrawFullscreenQuad() { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } #endif EnableInstancing(false); SetIndexBuffer(nullptr); SetVertexBuffer(&s_fullscreenQuadBuffer); if (!EnsureStateUpdate()) { NazaraError("Failed to update states: " + NzError::GetLastError()); return; } glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); if (s_useVertexArrayObjects) glBindVertexArray(0); } void NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode mode, unsigned int firstIndex, unsigned int indexCount) { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } if (mode > nzPrimitiveMode_Max) { NazaraError("Primitive mode out of enum"); return; } #endif #if NAZARA_RENDERER_SAFE if (!s_indexBuffer) { NazaraError("No index buffer"); return; } #endif EnableInstancing(false); if (!EnsureStateUpdate()) { NazaraError("Failed to update states: " + NzError::GetLastError()); return; } GLenum type; nzUInt8* offset = reinterpret_cast(s_indexBuffer->GetStartOffset()); if (s_indexBuffer->HasLargeIndices()) { offset += firstIndex*sizeof(nzUInt32); type = GL_UNSIGNED_INT; } else { offset += firstIndex*sizeof(nzUInt16); type = GL_UNSIGNED_SHORT; } glDrawElements(NzOpenGL::PrimitiveMode[mode], indexCount, type, offset); if (s_useVertexArrayObjects) glBindVertexArray(0); } void NzRenderer::DrawIndexedPrimitivesInstanced(unsigned int instanceCount, nzPrimitiveMode mode, unsigned int firstIndex, unsigned int indexCount) { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } if (mode > nzPrimitiveMode_Max) { NazaraError("Primitive mode out of enum"); return; } #endif #if NAZARA_RENDERER_SAFE if (!s_capabilities[nzRendererCap_Instancing]) { NazaraError("Instancing not supported"); return; } if (!s_indexBuffer) { NazaraError("No index buffer"); return; } if (instanceCount == 0) { NazaraError("Instance count must be over 0"); return; } unsigned int maxInstanceCount = s_instanceBuffer.GetVertexCount(); if (instanceCount > maxInstanceCount) { NazaraError("Instance count is over maximum instance count (" + NzString::Number(instanceCount) + " >= " NazaraStringifyMacro(NAZARA_RENDERER_MAX_INSTANCES) ")" ); return; } #endif EnableInstancing(true); if (!EnsureStateUpdate()) { NazaraError("Failed to update states: " + NzError::GetLastError()); return; } GLenum type; nzUInt8* offset = reinterpret_cast(s_indexBuffer->GetStartOffset()); if (s_indexBuffer->HasLargeIndices()) { offset += firstIndex*sizeof(nzUInt32); type = GL_UNSIGNED_INT; } else { offset += firstIndex*sizeof(nzUInt16); type = GL_UNSIGNED_SHORT; } glDrawElementsInstanced(NzOpenGL::PrimitiveMode[mode], indexCount, type, offset, instanceCount); if (s_useVertexArrayObjects) glBindVertexArray(0); } void NzRenderer::DrawPrimitives(nzPrimitiveMode mode, unsigned int firstVertex, unsigned int vertexCount) { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } if (mode > nzPrimitiveMode_Max) { NazaraError("Primitive mode out of enum"); return; } #endif EnableInstancing(false); if (!EnsureStateUpdate()) { NazaraError("Failed to update states: " + NzError::GetLastError()); return; } glDrawArrays(NzOpenGL::PrimitiveMode[mode], firstVertex, vertexCount); if (s_useVertexArrayObjects) glBindVertexArray(0); } void NzRenderer::DrawPrimitivesInstanced(unsigned int instanceCount, nzPrimitiveMode mode, unsigned int firstVertex, unsigned int vertexCount) { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } if (mode > nzPrimitiveMode_Max) { NazaraError("Primitive mode out of enum"); return; } #endif #if NAZARA_RENDERER_SAFE if (!s_capabilities[nzRendererCap_Instancing]) { NazaraError("Instancing not supported"); return; } if (instanceCount == 0) { NazaraError("Instance count must be over 0"); return; } unsigned int maxInstanceCount = s_instanceBuffer.GetVertexCount(); if (instanceCount > maxInstanceCount) { NazaraError("Instance count is over maximum instance count (" + NzString::Number(instanceCount) + " >= " NazaraStringifyMacro(NAZARA_RENDERER_MAX_INSTANCES) ")" ); return; } #endif EnableInstancing(true); if (!EnsureStateUpdate()) { NazaraError("Failed to update states: " + NzError::GetLastError()); return; } glDrawArraysInstanced(NzOpenGL::PrimitiveMode[mode], firstVertex, vertexCount, instanceCount); if (s_useVertexArrayObjects) glBindVertexArray(0); } void NzRenderer::Enable(nzRendererParameter parameter, bool enable) { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } if (parameter > nzRendererParameter_Max) { NazaraError("Renderer parameter out of enum"); return; } #endif s_states.parameters[parameter] = enable; } void NzRenderer::EndCondition() { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } #endif #if NAZARA_RENDERER_SAFE if (!s_capabilities[nzRendererCap_ConditionalRendering]) { NazaraError("Conditional rendering is not supported"); return; } #endif glEndConditionalRender(); } void NzRenderer::Flush() { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } #endif glFlush(); } nzRendererComparison NzRenderer::GetDepthFunc() { return s_states.depthFunc; } NzVertexBuffer* NzRenderer::GetInstanceBuffer() { #if NAZARA_RENDERER_SAFE if (!s_capabilities[nzRendererCap_Instancing]) { NazaraError("Instancing not supported"); return nullptr; } #endif s_updateFlags |= Update_VAO; return &s_instanceBuffer; } float NzRenderer::GetLineWidth() { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return 0.f; } #endif return s_states.lineWidth; } NzMatrix4f NzRenderer::GetMatrix(nzMatrixType type) { #ifdef NAZARA_DEBUG if (type > nzMatrixType_Max) { NazaraError("Matrix type out of enum"); return NzMatrix4f(); } #endif if (!s_matrices[type].updated) UpdateMatrix(type); return s_matrices[type].matrix; } nzUInt8 NzRenderer::GetMaxAnisotropyLevel() { return s_maxAnisotropyLevel; } unsigned int NzRenderer::GetMaxColorAttachments() { return s_maxColorAttachments; } unsigned int NzRenderer::GetMaxRenderTargets() { return s_maxRenderTarget; } unsigned int NzRenderer::GetMaxTextureUnits() { return s_maxTextureUnit; } unsigned int NzRenderer::GetMaxVertexAttribs() { return s_maxVertexAttribs; } float NzRenderer::GetPointSize() { return s_states.pointSize; } const NzRenderStates& NzRenderer::GetRenderStates() { return s_states; } NzRecti NzRenderer::GetScissorRect() { return NzOpenGL::GetCurrentScissorBox(); } const NzShader* NzRenderer::GetShader() { return s_shader; } const NzRenderTarget* NzRenderer::GetTarget() { return s_target; } NzRecti NzRenderer::GetViewport() { return NzOpenGL::GetCurrentViewport(); } bool NzRenderer::HasCapability(nzRendererCap capability) { #ifdef NAZARA_DEBUG if (capability > nzRendererCap_Max) { NazaraError("Renderer capability out of enum"); return false; } #endif return s_capabilities[capability]; } bool NzRenderer::Initialize() { if (s_moduleReferenceCounter > 0) { s_moduleReferenceCounter++; return true; // Déjà initialisé } // Initialisation des dépendances if (!NzUtility::Initialize()) { NazaraError("Failed to initialize Utility module"); return false; } s_moduleReferenceCounter++; // Initialisation du module NzCallOnExit onExit(NzRenderer::Uninitialize); // Initialisation d'OpenGL if (!NzOpenGL::Initialize()) { NazaraError("Failed to initialize OpenGL"); return false; } NzBuffer::SetBufferFunction(nzBufferStorage_Hardware, [](NzBuffer* parent, nzBufferType type) -> NzAbstractBuffer* { return new NzHardwareBuffer(parent, type); } ); for (unsigned int i = 0; i <= nzMatrixType_Max; ++i) { MatrixUnit& unit = s_matrices[i]; unit.location = -1; unit.matrix.MakeIdentity(); unit.updated = true; } // Récupération des capacités d'OpenGL s_capabilities[nzRendererCap_AnisotropicFilter] = NzOpenGL::IsSupported(nzOpenGLExtension_AnisotropicFilter); s_capabilities[nzRendererCap_ConditionalRendering] = NzOpenGL::IsSupported(nzOpenGLExtension_ConditionalRender); s_capabilities[nzRendererCap_FP64] = NzOpenGL::IsSupported(nzOpenGLExtension_FP64); s_capabilities[nzRendererCap_HardwareBuffer] = true; // Natif depuis OpenGL 1.5 s_capabilities[nzRendererCap_Instancing] = NzOpenGL::IsSupported(nzOpenGLExtension_DrawInstanced) && NzOpenGL::IsSupported(nzOpenGLExtension_InstancedArray); s_capabilities[nzRendererCap_MultipleRenderTargets] = (glBindFragDataLocation != nullptr); // Natif depuis OpenGL 2.0 mais inutile sans glBindFragDataLocation s_capabilities[nzRendererCap_OcclusionQuery] = true; // Natif depuis OpenGL 1.5 s_capabilities[nzRendererCap_PixelBufferObject] = NzOpenGL::IsSupported(nzOpenGLExtension_PixelBufferObject); s_capabilities[nzRendererCap_RenderTexture] = NzOpenGL::IsSupported(nzOpenGLExtension_FrameBufferObject); s_capabilities[nzRendererCap_Texture3D] = true; // Natif depuis OpenGL 1.2 s_capabilities[nzRendererCap_TextureCubemap] = true; // Natif depuis OpenGL 1.3 s_capabilities[nzRendererCap_TextureMulti] = true; // Natif depuis OpenGL 1.3 s_capabilities[nzRendererCap_TextureNPOT] = true; // Natif depuis OpenGL 2.0 NzContext::EnsureContext(); if (s_capabilities[nzRendererCap_AnisotropicFilter]) { GLfloat maxAnisotropy; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy); s_maxAnisotropyLevel = static_cast(maxAnisotropy); } else s_maxAnisotropyLevel = 1; if (s_capabilities[nzRendererCap_RenderTexture]) { GLint maxColorAttachments; glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments); s_maxColorAttachments = static_cast(maxColorAttachments); } else s_maxColorAttachments = 1; if (s_capabilities[nzRendererCap_MultipleRenderTargets]) { GLint maxDrawBuffers; glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); s_maxRenderTarget = static_cast(maxDrawBuffers); } else s_maxRenderTarget = 1; if (s_capabilities[nzRendererCap_TextureMulti]) { GLint maxTextureUnits; glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); s_maxTextureUnit = static_cast(maxTextureUnits); } else s_maxTextureUnit = 1; GLint maxVertexAttribs; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs); s_maxVertexAttribs = static_cast(maxVertexAttribs); s_states = NzRenderStates(); s_indexBuffer = nullptr; s_shader = nullptr; s_target = nullptr; s_targetSize.Set(0U); s_textureUnits.resize(s_maxTextureUnit); s_useSamplerObjects = NzOpenGL::IsSupported(nzOpenGLExtension_SamplerObjects); s_useVertexArrayObjects = NzOpenGL::IsSupported(nzOpenGLExtension_VertexArrayObjects); s_updateFlags = Update_Matrices | Update_Shader | Update_VAO; s_vertexBuffer = nullptr; s_fullscreenQuadBuffer.Reset(NzVertexDeclaration::Get(nzVertexLayout_XY), 4, nzBufferStorage_Hardware, nzBufferUsage_Static); float vertices[4*2] = { -1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f, }; if (!s_fullscreenQuadBuffer.Fill(vertices, 0, 4)) { NazaraError("Failed to fill fullscreen quad buffer"); return false; } if (s_capabilities[nzRendererCap_Instancing]) { try { NzErrorFlags errFlags(nzErrorFlag_ThrowException, true); s_instanceBuffer.Reset(nullptr, NAZARA_RENDERER_INSTANCE_BUFFER_SIZE, nzBufferStorage_Hardware, nzBufferUsage_Dynamic); } catch (const std::exception& e) { s_capabilities[nzRendererCap_Instancing] = false; NzErrorFlags flags(nzErrorFlag_ThrowExceptionDisabled); NazaraError("Failed to create instancing buffer: " + NzString(e.what())); } } if (!NzShaderLibrary::Initialize()) { NazaraError("Failed to initialize shader library"); return false; } if (!NzTextureSampler::Initialize()) { NazaraError("Failed to initialize texture sampler"); return false; } if (!NzUberShaderLibrary::Initialize()) { NazaraError("Failed to initialize uber shader library"); return false; } // Création du shader de Debug std::unique_ptr shader(new NzShader); shader->SetPersistent(false); if (!shader->Create()) { NazaraError("Failed to create debug shader"); return false; } const nzUInt8 coreFragmentShader[] = { #include }; const nzUInt8 coreVertexShader[] = { #include }; const nzUInt8 compatibilityFragmentShader[] = { #include }; const nzUInt8 compatibilityVertexShader[] = { #include }; const char* fragmentShader; const char* vertexShader; unsigned int fragmentShaderLength; unsigned int vertexShaderLength; if (NzOpenGL::GetGLSLVersion() >= 140) { fragmentShader = reinterpret_cast(coreFragmentShader); fragmentShaderLength = sizeof(coreFragmentShader); vertexShader = reinterpret_cast(coreVertexShader); vertexShaderLength = sizeof(coreVertexShader); } else { fragmentShader = reinterpret_cast(compatibilityFragmentShader); fragmentShaderLength = sizeof(compatibilityFragmentShader); vertexShader = reinterpret_cast(compatibilityVertexShader); vertexShaderLength = sizeof(compatibilityVertexShader); } if (!shader->AttachStageFromSource(nzShaderStage_Fragment, fragmentShader, fragmentShaderLength)) { NazaraError("Failed to attach fragment stage"); return false; } if (!shader->AttachStageFromSource(nzShaderStage_Vertex, vertexShader, vertexShaderLength)) { NazaraError("Failed to attach vertex stage"); return false; } if (!shader->Link()) { NazaraError("Failed to link shader"); return false; } NzShaderLibrary::Register("DebugSimple", shader.get()); shader.release(); onExit.Reset(); NazaraNotice("Initialized: Renderer module"); return true; } bool NzRenderer::IsComponentTypeSupported(nzComponentType type) { switch (type) { case nzComponentType_Color: case nzComponentType_Float1: case nzComponentType_Float2: case nzComponentType_Float3: case nzComponentType_Float4: return true; // Supportés nativement case nzComponentType_Double1: case nzComponentType_Double2: case nzComponentType_Double3: case nzComponentType_Double4: return glVertexAttribLPointer != nullptr; // Fonction requise pour envoyer des doubles case nzComponentType_Int1: case nzComponentType_Int2: case nzComponentType_Int3: case nzComponentType_Int4: return glVertexAttribIPointer != nullptr; // Fonction requise pour envoyer des entiers case nzComponentType_Quaternion: return false; } NazaraError("Attribute type out of enum (0x" + NzString::Number(type, 16) + ')'); return false; } bool NzRenderer::IsEnabled(nzRendererParameter parameter) { #ifdef NAZARA_DEBUG if (parameter > nzRendererParameter_Max) { NazaraError("Renderer parameter out of enum"); return false; } #endif return s_states.parameters[parameter]; } bool NzRenderer::IsInitialized() { return s_moduleReferenceCounter != 0; } void NzRenderer::SetBlendFunc(nzBlendFunc srcBlend, nzBlendFunc dstBlend) { #ifdef NAZARA_DEBUG if (srcBlend > nzBlendFunc_Max) { NazaraError("Blend func out of enum"); return; } if (dstBlend > nzBlendFunc_Max) { NazaraError("Blend func out of enum"); return; } #endif s_states.srcBlend = srcBlend; s_states.dstBlend = dstBlend; } void NzRenderer::SetClearColor(const NzColor& color) { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } #endif glClearColor(color.r/255.f, color.g/255.f, color.b/255.f, color.a/255.f); } void NzRenderer::SetClearColor(nzUInt8 r, nzUInt8 g, nzUInt8 b, nzUInt8 a) { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } #endif glClearColor(r/255.f, g/255.f, b/255.f, a/255.f); } void NzRenderer::SetClearDepth(double depth) { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } #endif glClearDepth(depth); } void NzRenderer::SetClearStencil(unsigned int value) { #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return; } #endif glClearStencil(value); } void NzRenderer::SetDepthFunc(nzRendererComparison compareFunc) { #ifdef NAZARA_DEBUG if (compareFunc > nzRendererComparison_Max) { NazaraError("Renderer comparison out of enum"); return; } #endif s_states.depthFunc = compareFunc; } void NzRenderer::SetFaceCulling(nzFaceSide faceSide) { #ifdef NAZARA_DEBUG if (faceSide > nzFaceSide_Max) { NazaraError("Face side out of enum"); return; } #endif s_states.faceCulling = faceSide; } void NzRenderer::SetFaceFilling(nzFaceFilling fillingMode) { #ifdef NAZARA_DEBUG if (fillingMode > nzFaceFilling_Max) { NazaraError("Face filling out of enum"); return; } #endif s_states.faceFilling = fillingMode; } void NzRenderer::SetIndexBuffer(const NzIndexBuffer* indexBuffer) { #if NAZARA_RENDERER_SAFE if (indexBuffer && !indexBuffer->IsHardware()) { NazaraError("Buffer must be hardware"); return; } #endif if (s_indexBuffer != indexBuffer) { s_indexBuffer = indexBuffer; s_updateFlags |= Update_VAO; } } void NzRenderer::SetLineWidth(float width) { #if NAZARA_RENDERER_SAFE if (width <= 0.f) { NazaraError("Width must be over zero"); return; } #endif s_states.lineWidth = width; } void NzRenderer::SetMatrix(nzMatrixType type, const NzMatrix4f& matrix) { #ifdef NAZARA_DEBUG if (type > nzMatrixType_Max) { NazaraError("Matrix type out of enum"); return; } #endif s_matrices[type].matrix = matrix; s_matrices[type].updated = true; // Invalidation des combinaisons switch (type) { // Matrices de base case nzMatrixType_Projection: s_matrices[nzMatrixType_InvProjection].updated = false; s_matrices[nzMatrixType_InvViewProj].updated = false; s_matrices[nzMatrixType_InvWorldViewProj].updated = false; s_matrices[nzMatrixType_ViewProj].updated = false; s_matrices[nzMatrixType_WorldViewProj].updated = false; break; case nzMatrixType_View: s_matrices[nzMatrixType_InvView].updated = false; s_matrices[nzMatrixType_InvViewProj].updated = false; s_matrices[nzMatrixType_InvWorld].updated = false; s_matrices[nzMatrixType_InvWorldViewProj].updated = false; s_matrices[nzMatrixType_ViewProj].updated = false; s_matrices[nzMatrixType_World].updated = false; s_matrices[nzMatrixType_WorldViewProj].updated = false; break; case nzMatrixType_World: s_matrices[nzMatrixType_InvWorld].updated = false; s_matrices[nzMatrixType_InvWorldView].updated = false; s_matrices[nzMatrixType_InvWorldViewProj].updated = false; s_matrices[nzMatrixType_WorldView].updated = false; s_matrices[nzMatrixType_WorldViewProj].updated = false; break; // Matrices combinées case nzMatrixType_ViewProj: s_matrices[nzMatrixType_InvViewProj].updated = false; break; case nzMatrixType_WorldView: s_matrices[nzMatrixType_InvWorldView].updated = false; s_matrices[nzMatrixType_WorldViewProj].updated = false; break; case nzMatrixType_WorldViewProj: s_matrices[nzMatrixType_InvWorldViewProj].updated = false; break; case nzMatrixType_InvProjection: case nzMatrixType_InvView: case nzMatrixType_InvViewProj: case nzMatrixType_InvWorld: case nzMatrixType_InvWorldView: case nzMatrixType_InvWorldViewProj: break; } s_updateFlags |= Update_Matrices; } void NzRenderer::SetPointSize(float size) { #if NAZARA_RENDERER_SAFE if (size <= 0.f) { NazaraError("Size must be over zero"); return; } #endif s_states.pointSize = size; } void NzRenderer::SetRenderStates(const NzRenderStates& states) { s_states = states; } void NzRenderer::SetScissorRect(const NzRecti& rect) { NzOpenGL::BindScissorBox(rect); } void NzRenderer::SetShader(const NzShader* shader) { #if NAZARA_RENDERER_SAFE if (shader) { if (!shader->IsValid() || !shader->IsLinked()) { NazaraError("Invalid shader"); return; } } #endif if (s_shader != shader) { s_shader = shader; s_updateFlags |= Update_Shader; } } void NzRenderer::SetStencilCompareFunction(nzRendererComparison compareFunc, nzFaceSide faceSide) { #ifdef NAZARA_DEBUG if (compareFunc > nzRendererComparison_Max) { NazaraError("Renderer comparison out of enum"); return; } if (faceSide > nzFaceSide_Max) { NazaraError("Face side out of enum"); return; } #endif switch (faceSide) { case nzFaceSide_Back: s_states.backFace.stencilCompare = compareFunc; break; case nzFaceSide_Front: s_states.frontFace.stencilCompare = compareFunc; break; case nzFaceSide_FrontAndBack: s_states.backFace.stencilCompare = compareFunc; s_states.frontFace.stencilCompare = compareFunc; break; } } void NzRenderer::SetStencilFailOperation(nzStencilOperation failOperation, nzFaceSide faceSide) { #ifdef NAZARA_DEBUG if (failOperation > nzStencilOperation_Max) { NazaraError("Stencil fail operation out of enum"); return; } if (faceSide > nzFaceSide_Max) { NazaraError("Face side out of enum"); return; } #endif switch (faceSide) { case nzFaceSide_Back: s_states.backFace.stencilFail = failOperation; break; case nzFaceSide_Front: s_states.frontFace.stencilFail = failOperation; break; case nzFaceSide_FrontAndBack: s_states.backFace.stencilFail = failOperation; s_states.frontFace.stencilFail = failOperation; break; } } void NzRenderer::SetStencilMask(nzUInt32 mask, nzFaceSide faceSide) { #ifdef NAZARA_DEBUG if (faceSide > nzFaceSide_Max) { NazaraError("Face side out of enum"); return; } #endif switch (faceSide) { case nzFaceSide_Back: s_states.backFace.stencilMask = mask; break; case nzFaceSide_Front: s_states.frontFace.stencilMask = mask; break; case nzFaceSide_FrontAndBack: s_states.backFace.stencilMask = mask; s_states.frontFace.stencilMask = mask; break; } } void NzRenderer::SetStencilPassOperation(nzStencilOperation passOperation, nzFaceSide faceSide) { #ifdef NAZARA_DEBUG if (passOperation > nzStencilOperation_Max) { NazaraError("Stencil pass operation out of enum"); return; } if (faceSide > nzFaceSide_Max) { NazaraError("Face side out of enum"); return; } #endif switch (faceSide) { case nzFaceSide_Back: s_states.backFace.stencilPass = passOperation; break; case nzFaceSide_Front: s_states.frontFace.stencilPass = passOperation; break; case nzFaceSide_FrontAndBack: s_states.backFace.stencilPass = passOperation; s_states.frontFace.stencilPass = passOperation; break; } } void NzRenderer::SetStencilReferenceValue(unsigned int refValue, nzFaceSide faceSide) { #ifdef NAZARA_DEBUG if (faceSide > nzFaceSide_Max) { NazaraError("Face side out of enum"); return; } #endif switch (faceSide) { case nzFaceSide_Back: s_states.backFace.stencilReference = refValue; break; case nzFaceSide_Front: s_states.frontFace.stencilReference = refValue; break; case nzFaceSide_FrontAndBack: s_states.backFace.stencilReference = refValue; s_states.frontFace.stencilReference = refValue; break; } } void NzRenderer::SetStencilZFailOperation(nzStencilOperation zfailOperation, nzFaceSide faceSide) { #ifdef NAZARA_DEBUG if (zfailOperation > nzStencilOperation_Max) { NazaraError("Stencil pass operation out of enum"); return; } if (faceSide > nzFaceSide_Max) { NazaraError("Face side out of enum"); return; } #endif switch (faceSide) { case nzFaceSide_Back: s_states.backFace.stencilZFail = zfailOperation; break; case nzFaceSide_Front: s_states.frontFace.stencilZFail = zfailOperation; break; case nzFaceSide_FrontAndBack: s_states.backFace.stencilZFail = zfailOperation; s_states.frontFace.stencilZFail = zfailOperation; break; } } bool NzRenderer::SetTarget(const NzRenderTarget* target) { if (s_target == target) return true; if (s_target) { if (!s_target->HasContext()) s_target->Desactivate(); s_target = nullptr; } if (target) { #if NAZARA_RENDERER_SAFE if (!target->IsRenderable()) { NazaraError("Target not renderable"); return false; } #endif if (!target->Activate()) { NazaraError("Failed to activate target"); return false; } s_target = target; } NzOpenGL::SetTarget(s_target); return true; } void NzRenderer::SetTexture(nzUInt8 unit, const NzTexture* texture) { #if NAZARA_RENDERER_SAFE if (unit >= s_maxTextureUnit) { NazaraError("Texture unit out of range (" + NzString::Number(unit) + " >= " + NzString::Number(s_maxTextureUnit) + ')'); return; } #endif if (s_textureUnits[unit].texture != texture) { s_textureUnits[unit].texture = texture; if (texture) { if (s_textureUnits[unit].sampler.UseMipmaps(texture->HasMipmaps())) s_textureUnits[unit].samplerUpdated = false; } s_dirtyTextureUnits.push_back(unit); s_updateFlags |= Update_Textures; } } void NzRenderer::SetTextureSampler(nzUInt8 unit, const NzTextureSampler& sampler) { #if NAZARA_RENDERER_SAFE if (unit >= s_maxTextureUnit) { NazaraError("Texture unit out of range (" + NzString::Number(unit) + " >= " + NzString::Number(s_maxTextureUnit) + ')'); return; } #endif s_textureUnits[unit].sampler = sampler; s_textureUnits[unit].samplerUpdated = false; if (s_textureUnits[unit].texture) s_textureUnits[unit].sampler.UseMipmaps(s_textureUnits[unit].texture->HasMipmaps()); s_dirtyTextureUnits.push_back(unit); s_updateFlags |= Update_Textures; } void NzRenderer::SetVertexBuffer(const NzVertexBuffer* vertexBuffer) { #if NAZARA_RENDERER_SAFE if (vertexBuffer && !vertexBuffer->IsHardware()) { NazaraError("Buffer must be hardware"); return; } #endif if (vertexBuffer && s_vertexBuffer != vertexBuffer) { s_vertexBuffer = vertexBuffer; s_updateFlags |= Update_VAO; } } void NzRenderer::SetViewport(const NzRecti& viewport) { NzOpenGL::BindViewport(viewport); } void NzRenderer::Uninitialize() { if (s_moduleReferenceCounter != 1) { // Le module est soit encore utilisé, soit pas initialisé if (s_moduleReferenceCounter > 1) s_moduleReferenceCounter--; return; } // Libération du module s_moduleReferenceCounter = 0; NzShaderLibrary::Unregister("DebugSimple"); NzUberShaderLibrary::Uninitialize(); NzTextureSampler::Uninitialize(); NzShaderLibrary::Uninitialize(); NzDebugDrawer::Uninitialize(); s_textureUnits.clear(); // Libération des buffers s_fullscreenQuadBuffer.Reset(); s_instanceBuffer.Reset(); // Libération des VAOs for (auto& pair : s_vaos) { const NzContext* context = pair.first; for (auto& pair2 : pair.second) { const VAO_Key& key = pair2.first; const NzIndexBuffer* indexBuffer = std::get<0>(key); const NzVertexBuffer* vertexBuffer = std::get<1>(key); const NzVertexDeclaration* vertexDeclaration = std::get<2>(key); const NzVertexDeclaration* instancingDeclaration = std::get<3>(key); if (indexBuffer) indexBuffer->RemoveResourceListener(&s_listener); vertexBuffer->RemoveResourceListener(&s_listener); vertexDeclaration->RemoveResourceListener(&s_listener); if (instancingDeclaration) instancingDeclaration->RemoveResourceListener(&s_listener); NzOpenGL::DeleteVertexArray(context, pair2.second); } } s_vaos.clear(); NzOpenGL::Uninitialize(); NazaraNotice("Uninitialized: Renderer module"); // Libération des dépendances NzUtility::Uninitialize(); } void NzRenderer::EnableInstancing(bool instancing) { if (s_instancing != instancing) { s_updateFlags |= Update_VAO; s_instancing = instancing; } } bool NzRenderer::EnsureStateUpdate() { // Toutes les erreurs sont silencieuses car l'erreur est gérée par la fonction appelante NzErrorFlags flags(nzErrorFlag_Silent | nzErrorFlag_ThrowExceptionDisabled); #ifdef NAZARA_DEBUG if (NzContext::GetCurrent() == nullptr) { NazaraError("No active context"); return false; } #endif #if NAZARA_RENDERER_SAFE if (!s_shader) { NazaraError("No shader"); return false; } if (!s_target) { NazaraError("No target"); return false; } #endif s_target->EnsureTargetUpdated(); s_shader->Bind(); // Active le programme si ce n'est pas déjà le cas // Si le programme a été changé depuis la dernière fois if (s_updateFlags & Update_Shader) { // Récupération des indices des variables uniformes (-1 si la variable n'existe pas) s_matrices[nzMatrixType_Projection].location = s_shader->GetUniformLocation(nzShaderUniform_ProjMatrix); s_matrices[nzMatrixType_View].location = s_shader->GetUniformLocation(nzShaderUniform_ViewMatrix); s_matrices[nzMatrixType_World].location = s_shader->GetUniformLocation(nzShaderUniform_WorldMatrix); s_matrices[nzMatrixType_ViewProj].location = s_shader->GetUniformLocation(nzShaderUniform_ViewProjMatrix); s_matrices[nzMatrixType_WorldView].location = s_shader->GetUniformLocation(nzShaderUniform_WorldViewMatrix); s_matrices[nzMatrixType_WorldViewProj].location = s_shader->GetUniformLocation(nzShaderUniform_WorldViewProjMatrix); s_matrices[nzMatrixType_InvProjection].location = s_shader->GetUniformLocation(nzShaderUniform_InvProjMatrix); s_matrices[nzMatrixType_InvView].location = s_shader->GetUniformLocation(nzShaderUniform_InvViewMatrix); s_matrices[nzMatrixType_InvViewProj].location = s_shader->GetUniformLocation(nzShaderUniform_InvViewProjMatrix); s_matrices[nzMatrixType_InvWorld].location = s_shader->GetUniformLocation(nzShaderUniform_InvWorldMatrix); s_matrices[nzMatrixType_InvWorldView].location = s_shader->GetUniformLocation(nzShaderUniform_InvWorldViewMatrix); s_matrices[nzMatrixType_InvWorldViewProj].location = s_shader->GetUniformLocation(nzShaderUniform_InvWorldViewProjMatrix); s_targetSize.Set(0U); // On force l'envoi des uniformes s_updateFlags |= Update_Matrices; // Changement de programme, on renvoie toutes les matrices demandées s_updateFlags &= ~Update_Shader; } // Envoi des uniformes liées au Renderer NzVector2ui targetSize(s_target->GetWidth(), s_target->GetHeight()); if (s_targetSize != targetSize) { int location; location = s_shader->GetUniformLocation(nzShaderUniform_InvTargetSize); if (location != -1) s_shader->SendVector(location, 1.f/NzVector2f(targetSize)); location = s_shader->GetUniformLocation(nzShaderUniform_TargetSize); if (location != -1) s_shader->SendVector(location, NzVector2f(targetSize)); s_targetSize.Set(targetSize); } if (s_updateFlags != Update_None) { if (s_updateFlags & Update_Textures) { if (s_useSamplerObjects) { for (unsigned int i : s_dirtyTextureUnits) { TextureUnit& unit = s_textureUnits[i]; if (unit.texture && !unit.samplerUpdated) { unit.sampler.Bind(i); unit.samplerUpdated = true; } } } else { for (unsigned int i : s_dirtyTextureUnits) { TextureUnit& unit = s_textureUnits[i]; if (unit.texture && !unit.samplerUpdated) { NzOpenGL::BindTextureUnit(i); unit.sampler.Apply(unit.texture); unit.samplerUpdated = true; } } } s_dirtyTextureUnits.clear(); // Ne change pas la capacité s_updateFlags &= ~Update_Textures; } if (s_updateFlags & Update_Matrices) { for (unsigned int i = 0; i <= nzMatrixType_Max; ++i) { MatrixUnit& unit = s_matrices[i]; if (unit.location != -1) // On ne traite que les matrices existant dans le programme { if (!unit.updated) UpdateMatrix(static_cast(i)); s_shader->SendMatrix(unit.location, unit.matrix); } } s_updateFlags &= ~Update_Matrices; } if (s_updateFlags & Update_VAO) { #if NAZARA_RENDERER_SAFE if (!s_vertexBuffer) { NazaraError("No vertex buffer"); return false; } #endif bool update; VAO_Map::iterator vaoIt; // Si les VAOs sont supportés, on entoure nos appels par ceux-ci if (s_useVertexArrayObjects) { // Note: Les VAOs ne sont pas partagés entre les contextes, nous avons donc un tableau de VAOs par contexte const NzContext* context = NzContext::GetCurrent(); VAO_Map* vaos; auto it = s_vaos.find(context); if (it == s_vaos.end()) { context->AddResourceListener(&s_listener, ResourceType_Context); auto pair = s_vaos.insert(std::make_pair(context, Context_Map::mapped_type())); vaos = &pair.first->second; } else vaos = &it->second; // Notre clé est composée de ce qui définit un VAO const NzVertexDeclaration* vertexDeclaration = s_vertexBuffer->GetVertexDeclaration(); const NzVertexDeclaration* instancingDeclaration = (s_instancing) ? s_instanceBuffer.GetVertexDeclaration() : nullptr; VAO_Key key(s_indexBuffer, s_vertexBuffer, vertexDeclaration, instancingDeclaration); // On recherche un VAO existant avec notre configuration vaoIt = vaos->find(key); if (vaoIt == vaos->end()) { // On créé notre VAO glGenVertexArrays(1, &s_currentVAO); glBindVertexArray(s_currentVAO); // On l'ajoute à notre liste vaoIt = vaos->insert(std::make_pair(key, s_currentVAO)).first; if (s_indexBuffer) s_indexBuffer->AddResourceListener(&s_listener, ResourceType_IndexBuffer); s_vertexBuffer->AddResourceListener(&s_listener, ResourceType_VertexBuffer); vertexDeclaration->AddResourceListener(&s_listener, ResourceType_VertexDeclaration); if (instancingDeclaration) instancingDeclaration->AddResourceListener(&s_listener, ResourceType_VertexDeclaration); // Et on indique qu'on veut le programmer update = true; } else { // Notre VAO existe déjà, il est donc inutile de le reprogrammer s_currentVAO = vaoIt->second; update = false; } } else update = true; // Fallback si les VAOs ne sont pas supportés bool updateFailed = false; if (update) { const NzVertexDeclaration* vertexDeclaration; unsigned int bufferOffset; unsigned int stride; // Pour éviter la duplication de code, on va utiliser une astuce via une boucle for for (unsigned int i = 0; i < (s_instancing ? 2 : 1); ++i) { // Selon l'itération nous choisissons un buffer différent const NzVertexBuffer* vertexBuffer = (i == 0) ? s_vertexBuffer : &s_instanceBuffer; NzHardwareBuffer* vertexBufferImpl = static_cast(vertexBuffer->GetBuffer()->GetImpl()); glBindBuffer(NzOpenGL::BufferTarget[nzBufferType_Vertex], vertexBufferImpl->GetOpenGLID()); bufferOffset = vertexBuffer->GetStartOffset(); vertexDeclaration = vertexBuffer->GetVertexDeclaration(); stride = vertexDeclaration->GetStride(); // On définit les bornes (une fois de plus selon l'itération) unsigned int start = (i == 0) ? nzVertexComponent_FirstVertexData : nzVertexComponent_FirstInstanceData; unsigned int end = (i == 0) ? nzVertexComponent_LastVertexData : nzVertexComponent_LastInstanceData; for (unsigned int j = start; j <= end; ++j) { nzComponentType type; bool enabled; unsigned int offset; vertexDeclaration->GetComponent(static_cast(j), &enabled, &type, &offset); if (enabled) { if (!IsComponentTypeSupported(type)) { NazaraError("Invalid vertex declaration " + NzString::Pointer(vertexDeclaration) + ": Vertex component 0x" + NzString::Number(j, 16) + " (type: 0x" + NzString::Number(type, 16) + ") is not supported"); updateFailed = true; break; } glEnableVertexAttribArray(NzOpenGL::VertexComponentIndex[j]); switch (type) { case nzComponentType_Color: { glVertexAttribPointer(NzOpenGL::VertexComponentIndex[j], NzUtility::ComponentCount[type], NzOpenGL::ComponentType[type], GL_TRUE, stride, reinterpret_cast(bufferOffset + offset)); break; } case nzComponentType_Double1: case nzComponentType_Double2: case nzComponentType_Double3: case nzComponentType_Double4: { glVertexAttribLPointer(NzOpenGL::VertexComponentIndex[j], NzUtility::ComponentCount[type], NzOpenGL::ComponentType[type], stride, reinterpret_cast(bufferOffset + offset)); break; } case nzComponentType_Float1: case nzComponentType_Float2: case nzComponentType_Float3: case nzComponentType_Float4: { glVertexAttribPointer(NzOpenGL::VertexComponentIndex[j], NzUtility::ComponentCount[type], NzOpenGL::ComponentType[type], GL_FALSE, stride, reinterpret_cast(bufferOffset + offset)); break; } case nzComponentType_Int1: case nzComponentType_Int2: case nzComponentType_Int3: case nzComponentType_Int4: { glVertexAttribIPointer(NzOpenGL::VertexComponentIndex[j], NzUtility::ComponentCount[type], NzOpenGL::ComponentType[type], stride, reinterpret_cast(bufferOffset + offset)); break; } default: { NazaraInternalError("Unsupported component type"); break; } } // Les attributs d'instancing ont un diviseur spécifique (pour dépendre de l'instance en cours) if (i == 1) glVertexAttribDivisor(NzOpenGL::VertexComponentIndex[j], 1); } else glDisableVertexAttribArray(NzOpenGL::VertexComponentIndex[j]); } } if (!s_instancing) { // Je ne sais pas si c'est vraiment nécessaire de désactiver les attributs, sur mon ordinateur ça ne pose aucun problème // mais dans le doute, je laisse ça comme ça. for (unsigned int i = nzVertexComponent_FirstInstanceData; i <= nzVertexComponent_LastInstanceData; ++i) glDisableVertexAttribArray(NzOpenGL::VertexComponentIndex[i]); } // Et on active l'index buffer (Un seul index buffer par VAO) if (s_indexBuffer) { NzHardwareBuffer* indexBufferImpl = static_cast(s_indexBuffer->GetBuffer()->GetImpl()); glBindBuffer(NzOpenGL::BufferTarget[nzBufferType_Index], indexBufferImpl->GetOpenGLID()); } else glBindBuffer(NzOpenGL::BufferTarget[nzBufferType_Index], 0); // On invalide les bindings des buffers (car nous les avons défini manuellement) NzOpenGL::SetBuffer(nzBufferType_Index, 0); NzOpenGL::SetBuffer(nzBufferType_Vertex, 0); } if (s_useVertexArrayObjects) { if (update) { if (updateFailed) { // La création de notre VAO a échoué, libérons-le et marquons-le comme problématique glDeleteVertexArrays(1, &vaoIt->second); vaoIt->second = 0; s_currentVAO = 0; } else glBindVertexArray(0); // On marque la fin de la construction du VAO en le débindant } // En cas de non-support des VAOs, les attributs doivent être respécifiés à chaque frame s_updateFlags &= ~Update_VAO; } } #ifdef NAZARA_DEBUG if (s_updateFlags != Update_None && !s_useVertexArrayObjects && s_updateFlags != Update_VAO) NazaraWarning("Update flags not fully cleared"); #endif } // On bind notre VAO if (s_useVertexArrayObjects) { if (!s_currentVAO) { NazaraError("Failed to create VAO"); return false; } glBindVertexArray(s_currentVAO); } // On vérifie que les textures actuellement bindées sont bien nos textures // Ceci à cause du fait qu'il est possible que des opérations sur les textures aient eu lieu // entre le dernier rendu et maintenant for (unsigned int i = 0; i < s_maxTextureUnit; ++i) { const NzTexture* texture = s_textureUnits[i].texture; if (texture) { NzOpenGL::BindTexture(i, texture->GetType(), texture->GetOpenGLID()); texture->EnsureMipmapsUpdate(); } } // Et on termine par envoyer nos états au driver NzOpenGL::ApplyStates(s_states); return true; } void NzRenderer::OnShaderReleased(const NzShader* shader) { if (s_shader == shader) { s_shader = nullptr; s_updateFlags |= Update_Shader; } } void NzRenderer::OnTextureReleased(const NzTexture* texture) { for (TextureUnit& unit : s_textureUnits) { if (unit.texture == texture) unit.texture = nullptr; // Inutile de changer le flag pour une texture désactivée } } void NzRenderer::UpdateMatrix(nzMatrixType type) { #ifdef NAZARA_DEBUG if (type > nzMatrixType_Max) { NazaraError("Matrix type out of enum"); return; } #endif switch (type) { // Matrices de base case nzMatrixType_Projection: case nzMatrixType_View: case nzMatrixType_World: s_matrices[type].updated = true; break; // Matrices combinées case nzMatrixType_ViewProj: s_matrices[nzMatrixType_ViewProj].matrix = s_matrices[nzMatrixType_View].matrix * s_matrices[nzMatrixType_Projection].matrix; s_matrices[nzMatrixType_ViewProj].updated = true; break; case nzMatrixType_WorldView: s_matrices[nzMatrixType_WorldView].matrix = s_matrices[nzMatrixType_World].matrix; s_matrices[nzMatrixType_WorldView].matrix.ConcatenateAffine(s_matrices[nzMatrixType_View].matrix); s_matrices[nzMatrixType_WorldView].updated = true; break; case nzMatrixType_WorldViewProj: if (!s_matrices[nzMatrixType_WorldView].updated) UpdateMatrix(nzMatrixType_WorldView); s_matrices[nzMatrixType_WorldViewProj].matrix = s_matrices[nzMatrixType_WorldView].matrix; s_matrices[nzMatrixType_WorldViewProj].matrix.Concatenate(s_matrices[nzMatrixType_Projection].matrix); s_matrices[nzMatrixType_WorldViewProj].updated = true; break; // Matrices inversées case nzMatrixType_InvProjection: if (!s_matrices[nzMatrixType_Projection].updated) UpdateMatrix(nzMatrixType_Projection); if (!s_matrices[nzMatrixType_Projection].matrix.GetInverse(&s_matrices[nzMatrixType_InvProjection].matrix)) NazaraWarning("Failed to inverse Proj matrix"); s_matrices[nzMatrixType_InvProjection].updated = true; break; case nzMatrixType_InvView: if (!s_matrices[nzMatrixType_View].updated) UpdateMatrix(nzMatrixType_View); if (!s_matrices[nzMatrixType_View].matrix.GetInverse(&s_matrices[nzMatrixType_InvView].matrix)) NazaraWarning("Failed to inverse View matrix"); s_matrices[nzMatrixType_InvView].updated = true; break; case nzMatrixType_InvViewProj: if (!s_matrices[nzMatrixType_ViewProj].updated) UpdateMatrix(nzMatrixType_ViewProj); if (!s_matrices[nzMatrixType_ViewProj].matrix.GetInverse(&s_matrices[nzMatrixType_InvViewProj].matrix)) NazaraWarning("Failed to inverse ViewProj matrix"); s_matrices[nzMatrixType_InvViewProj].updated = true; break; case nzMatrixType_InvWorld: if (!s_matrices[nzMatrixType_World].updated) UpdateMatrix(nzMatrixType_World); if (!s_matrices[nzMatrixType_World].matrix.GetInverse(&s_matrices[nzMatrixType_InvWorld].matrix)) NazaraWarning("Failed to inverse World matrix"); s_matrices[nzMatrixType_InvWorld].updated = true; break; case nzMatrixType_InvWorldView: if (!s_matrices[nzMatrixType_WorldView].updated) UpdateMatrix(nzMatrixType_WorldView); if (!s_matrices[nzMatrixType_WorldView].matrix.GetInverse(&s_matrices[nzMatrixType_InvWorldView].matrix)) NazaraWarning("Failed to inverse WorldView matrix"); s_matrices[nzMatrixType_InvWorldView].updated = true; break; case nzMatrixType_InvWorldViewProj: if (!s_matrices[nzMatrixType_WorldViewProj].updated) UpdateMatrix(nzMatrixType_WorldViewProj); if (!s_matrices[nzMatrixType_WorldViewProj].matrix.GetInverse(&s_matrices[nzMatrixType_InvWorldViewProj].matrix)) NazaraWarning("Failed to inverse WorldViewProj matrix"); s_matrices[nzMatrixType_InvWorldViewProj].updated = true; break; } } unsigned int NzRenderer::s_moduleReferenceCounter = 0;