diff --git a/include/Nazara/Renderer/OpenGL.hpp b/include/Nazara/Renderer/OpenGL.hpp index 0a30c89eb..fa3a0cef6 100644 --- a/include/Nazara/Renderer/OpenGL.hpp +++ b/include/Nazara/Renderer/OpenGL.hpp @@ -28,8 +28,10 @@ enum nzOpenGLExtension { nzOpenGLExtension_AnisotropicFilter, nzOpenGLExtension_DebugOutput, + nzOpenGLExtension_DrawInstanced, nzOpenGLExtension_FP64, nzOpenGLExtension_FrameBufferObject, + nzOpenGLExtension_InstancedArray, nzOpenGLExtension_PixelBufferObject, nzOpenGLExtension_SamplerObjects, nzOpenGLExtension_SeparateShaderObjects, @@ -139,9 +141,11 @@ NAZARA_API extern PFNGLDEPTHMASKPROC glDepthMask; NAZARA_API extern PFNGLDISABLEPROC glDisable; NAZARA_API extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; NAZARA_API extern PFNGLDRAWARRAYSPROC glDrawArrays; +NAZARA_API extern PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced; NAZARA_API extern PFNGLDRAWBUFFERPROC glDrawBuffer; NAZARA_API extern PFNGLDRAWBUFFERSPROC glDrawBuffers; NAZARA_API extern PFNGLDRAWELEMENTSPROC glDrawElements; +NAZARA_API extern PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced; NAZARA_API extern PFNGLENABLEPROC glEnable; NAZARA_API extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; NAZARA_API extern PFNGLENDQUERYPROC glEndQuery; @@ -234,6 +238,7 @@ NAZARA_API extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; NAZARA_API extern PFNGLUNMAPBUFFERPROC glUnmapBuffer; NAZARA_API extern PFNGLUSEPROGRAMPROC glUseProgram; NAZARA_API extern PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f; +NAZARA_API extern PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor; NAZARA_API extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; NAZARA_API extern PFNGLVIEWPORTPROC glViewport; #if defined(NAZARA_PLATFORM_WINDOWS) diff --git a/include/Nazara/Renderer/Renderer.hpp b/include/Nazara/Renderer/Renderer.hpp index 09e84eae3..9be165780 100644 --- a/include/Nazara/Renderer/Renderer.hpp +++ b/include/Nazara/Renderer/Renderer.hpp @@ -27,16 +27,27 @@ class NzVertexDeclaration; class NAZARA_API NzRenderer { public: + struct InstancingData + { + NzMatrix4f worldMatrix; + }; + NzRenderer() = delete; ~NzRenderer() = delete; static void Clear(unsigned long flags = nzRendererClear_Color | nzRendererClear_Depth); static void DrawIndexedPrimitives(nzPrimitiveType primitive, unsigned int firstIndex, unsigned int indexCount); + static void DrawIndexedPrimitivesInstanced(unsigned int instanceCount, nzPrimitiveType primitive, unsigned int firstIndex, unsigned int indexCount); static void DrawPrimitives(nzPrimitiveType primitive, unsigned int firstVertex, unsigned int vertexCount); + static void DrawPrimitivesInstanced(unsigned int instanceCount, nzPrimitiveType primitive, unsigned int firstVertex, unsigned int vertexCount); static void Enable(nzRendererParameter parameter, bool enable); + static void FillInstancingBuffer(const InstancingData* instancingData, unsigned int instanceCount); + + static void Flush(); + static float GetLineWidth(); //static NzMatrix4f GetMatrix(nzMatrixCombination combination); static NzMatrix4f GetMatrix(nzMatrixType type); diff --git a/src/Nazara/Renderer/GLSLShader.cpp b/src/Nazara/Renderer/GLSLShader.cpp index e7431722e..417d687f4 100644 --- a/src/Nazara/Renderer/GLSLShader.cpp +++ b/src/Nazara/Renderer/GLSLShader.cpp @@ -111,24 +111,25 @@ bool NzGLSLShader::Create() return false; } + glBindAttribLocation(m_program, NzOpenGL::AttributeIndex[nzElementUsage_TexCoord]+8, "InstanceMatrix"); + glBindAttribLocation(m_program, NzOpenGL::AttributeIndex[nzElementUsage_Position], "VertexPosition"); glBindAttribLocation(m_program, NzOpenGL::AttributeIndex[nzElementUsage_Normal], "VertexNormal"); glBindAttribLocation(m_program, NzOpenGL::AttributeIndex[nzElementUsage_Diffuse], "VertexDiffuse"); glBindAttribLocation(m_program, NzOpenGL::AttributeIndex[nzElementUsage_Tangent], "VertexTangent"); - NzString uniform; - uniform.Reserve(16); // 14 + 2 - uniform = "VertexTexCoord"; + char texCoordsUniform[] = "VertexTexCoord*"; - unsigned int maxTexCoords = NzRenderer::GetMaxTextureUnits(); + unsigned int maxTexCoords = std::min(8U, NzRenderer::GetMaxTextureUnits()); for (unsigned int i = 0; i < maxTexCoords; ++i) { - NzString uniformName = uniform + NzString::Number(i); - glBindAttribLocation(m_program, NzOpenGL::AttributeIndex[nzElementUsage_TexCoord]+i, uniformName.GetConstBuffer()); + texCoordsUniform[14] = '0' + i; + glBindAttribLocation(m_program, NzOpenGL::AttributeIndex[nzElementUsage_TexCoord]+i, texCoordsUniform); } if (NzRenderer::HasCapability(nzRendererCap_MultipleRenderTargets)) { + NzString uniform; uniform.Reserve(14); // 12 + 2 uniform = "RenderTarget"; diff --git a/src/Nazara/Renderer/OpenGL.cpp b/src/Nazara/Renderer/OpenGL.cpp index 2e7dbe4b4..3a63806ed 100644 --- a/src/Nazara/Renderer/OpenGL.cpp +++ b/src/Nazara/Renderer/OpenGL.cpp @@ -413,6 +413,37 @@ bool NzOpenGL::Initialize() } } + // DrawInstanced + if (s_openglVersion >= 330) + { + try + { + glDrawArraysInstanced = reinterpret_cast(LoadEntry("glDrawArraysInstanced")); + glDrawElementsInstanced = reinterpret_cast(LoadEntry("glDrawElementsInstanced")); + + s_openGLextensions[nzOpenGLExtension_DrawInstanced] = true; + } + catch (const std::exception& e) + { + NazaraWarning("Failed to load GL_ARB_draw_instanced: " + NzString(e.what())); + } + } + + if (!s_openGLextensions[nzOpenGLExtension_DrawInstanced] && IsSupported("GL_ARB_draw_instanced")) + { + try + { + glDrawArraysInstanced = reinterpret_cast(LoadEntry("glDrawArraysInstancedARB")); + glDrawElementsInstanced = reinterpret_cast(LoadEntry("glDrawElementsInstancedARB")); + + s_openGLextensions[nzOpenGLExtension_DrawInstanced] = true; + } + catch (const std::exception& e) + { + NazaraWarning("Failed to load GL_ARB_draw_instanced: " + NzString(e.what())); + } + } + // FP64 if (s_openglVersion >= 400 || IsSupported("GL_ARB_gpu_shader_fp64")) { @@ -460,6 +491,35 @@ bool NzOpenGL::Initialize() } } + // InstancedArray + if (s_openglVersion >= 330) + { + try + { + glVertexAttribDivisor = reinterpret_cast(LoadEntry("glVertexAttribDivisor")); + + s_openGLextensions[nzOpenGLExtension_InstancedArray] = true; + } + catch (const std::exception& e) + { + NazaraWarning("Failed to load GL_ARB_instanced_arrays: " + NzString(e.what())); + } + } + + if (!s_openGLextensions[nzOpenGLExtension_InstancedArray] && IsSupported("GL_ARB_instanced_arrays")) + { + try + { + glVertexAttribDivisor = reinterpret_cast(LoadEntry("glVertexAttribDivisorARB")); + + s_openGLextensions[nzOpenGLExtension_InstancedArray] = true; + } + catch (const std::exception& e) + { + NazaraWarning("Failed to load GL_ARB_instanced_arrays: " + NzString(e.what())); + } + } + // PixelBufferObject s_openGLextensions[nzOpenGLExtension_PixelBufferObject] = (s_openglVersion >= 210 || IsSupported("GL_ARB_pixel_buffer_object")); @@ -980,9 +1040,11 @@ PFNGLDEPTHMASKPROC glDepthMask = nullptr; PFNGLDISABLEPROC glDisable = nullptr; PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray = nullptr; PFNGLDRAWARRAYSPROC glDrawArrays = nullptr; +PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced = nullptr; PFNGLDRAWBUFFERPROC glDrawBuffer = nullptr; PFNGLDRAWBUFFERSPROC glDrawBuffers = nullptr; PFNGLDRAWELEMENTSPROC glDrawElements = nullptr; +PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced = nullptr; PFNGLENABLEPROC glEnable = nullptr; PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr; PFNGLENDQUERYPROC glEndQuery = nullptr; @@ -1075,6 +1137,7 @@ PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = nullptr; PFNGLUNMAPBUFFERPROC glUnmapBuffer = nullptr; PFNGLUSEPROGRAMPROC glUseProgram = nullptr; PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f = nullptr; +PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor = nullptr; PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr; PFNGLVIEWPORTPROC glViewport = nullptr; #if defined(NAZARA_PLATFORM_WINDOWS) diff --git a/src/Nazara/Renderer/Renderer.cpp b/src/Nazara/Renderer/Renderer.cpp index 75be0abe2..5adab18aa 100644 --- a/src/Nazara/Renderer/Renderer.cpp +++ b/src/Nazara/Renderer/Renderer.cpp @@ -60,9 +60,8 @@ namespace std::map s_vaos; std::vector s_textureUnits; + NzBuffer* s_instancingBuffer = nullptr; NzMatrix4f s_matrix[totalMatrixCount]; - int s_matrixLocation[totalMatrixCount]; - bool s_matrixUpdated[totalMatrixCount]; nzBlendFunc s_srcBlend; nzBlendFunc s_dstBlend; nzFaceCulling s_faceCulling; @@ -79,12 +78,15 @@ namespace const NzShader* s_shader; const NzVertexBuffer* s_vertexBuffer; const NzVertexDeclaration* s_vertexDeclaration; - bool s_vaoUpdated; bool s_capabilities[nzRendererCap_Max+1]; + bool s_instancingEnabled; + bool s_matrixUpdated[totalMatrixCount]; bool s_stencilFuncUpdated; bool s_stencilOpUpdated; bool s_useSamplerObjects; bool s_useVertexArrayObjects; + bool s_vaoUpdated; + int s_matrixLocation[totalMatrixCount]; unsigned int s_maxRenderTarget; unsigned int s_maxTextureUnit; unsigned int s_stencilReference; @@ -147,6 +149,12 @@ void NzRenderer::DrawIndexedPrimitives(nzPrimitiveType primitive, unsigned int f return; } + if (s_instancingEnabled) + { + glDisableVertexAttribArray(NzOpenGL::AttributeIndex[nzElementUsage_TexCoord]+8); + s_instancingEnabled = false; + } + if (s_indexBuffer->IsSequential()) glDrawArrays(NzOpenGL::PrimitiveType[primitive], s_indexBuffer->GetStartIndex(), s_indexBuffer->GetIndexCount()); else @@ -168,6 +176,81 @@ void NzRenderer::DrawIndexedPrimitives(nzPrimitiveType primitive, unsigned int f } } +void NzRenderer::DrawIndexedPrimitivesInstanced(unsigned int instanceCount, nzPrimitiveType primitive, unsigned int firstIndex, unsigned int indexCount) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + + if (primitive > nzPrimitiveType_Max) + { + NazaraError("Primitive type 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; + } + + if (instanceCount > NAZARA_RENDERER_INSTANCING_MAX) + { + NazaraError("Instance count is over maximum instance count (" + NzString::Number(instanceCount) + " >= " + NzString::Number(NAZARA_RENDERER_INSTANCING_MAX) + ')'); + return; + } + #endif + + if (!EnsureStateUpdate()) + { + NazaraError("Failed to update states"); + return; + } + + if (!s_instancingEnabled) + { + glEnableVertexAttribArray(NzOpenGL::AttributeIndex[nzElementUsage_TexCoord]+8); + s_instancingEnabled = true; + } + + if (s_indexBuffer->IsSequential()) + glDrawArraysInstanced(NzOpenGL::PrimitiveType[primitive], s_indexBuffer->GetStartIndex(), s_indexBuffer->GetIndexCount(), instanceCount); + else + { + GLenum type; + const nzUInt8* ptr = reinterpret_cast(s_indexBuffer->GetPointer()); + if (s_indexBuffer->HasLargeIndices()) + { + ptr += firstIndex*sizeof(nzUInt32); + type = GL_UNSIGNED_INT; + } + else + { + ptr += firstIndex*sizeof(nzUInt16); + type = GL_UNSIGNED_SHORT; + } + + glDrawElementsInstanced(NzOpenGL::PrimitiveType[primitive], indexCount, type, ptr, instanceCount); + } +} + void NzRenderer::DrawPrimitives(nzPrimitiveType primitive, unsigned int firstVertex, unsigned int vertexCount) { #ifdef NAZARA_DEBUG @@ -190,9 +273,66 @@ void NzRenderer::DrawPrimitives(nzPrimitiveType primitive, unsigned int firstVer return; } + if (s_instancingEnabled) + { + glDisableVertexAttribArray(NzOpenGL::AttributeIndex[nzElementUsage_TexCoord]+8); + s_instancingEnabled = false; + } + glDrawArrays(NzOpenGL::PrimitiveType[primitive], firstVertex, vertexCount); } +void NzRenderer::DrawPrimitivesInstanced(unsigned int instanceCount, nzPrimitiveType primitive, unsigned int firstVertex, unsigned int vertexCount) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + + if (primitive > nzPrimitiveType_Max) + { + NazaraError("Primitive type 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; + } + + if (instanceCount > NAZARA_RENDERER_INSTANCING_MAX) + { + NazaraError("Instance count is over maximum instance count (" + NzString::Number(instanceCount) + " >= " + NzString::Number(NAZARA_RENDERER_INSTANCING_MAX) + ')'); + return; + } + #endif + + if (!EnsureStateUpdate()) + { + NazaraError("Failed to update states"); + return; + } + + if (!s_instancingEnabled) + { + glEnableVertexAttribArray(NzOpenGL::AttributeIndex[nzElementUsage_TexCoord]+8); + s_instancingEnabled = true; + } + + glDrawArraysInstanced(NzOpenGL::PrimitiveType[primitive], firstVertex, vertexCount, instanceCount); +} + void NzRenderer::Enable(nzRendererParameter parameter, bool enable) { #ifdef NAZARA_DEBUG @@ -229,6 +369,43 @@ void NzRenderer::Enable(nzRendererParameter parameter, bool enable) } } +void NzRenderer::FillInstancingBuffer(const NzRenderer::InstancingData* instancingData, unsigned int instanceCount) +{ + #if NAZARA_RENDERER_SAFE + if (!s_capabilities[nzRendererCap_Instancing]) + { + NazaraError("Instancing not supported"); + return; + } + + if (!instancingData) + { + NazaraError("Instancing data must be valid"); + return; + } + + if (instanceCount == 0) + { + NazaraError("Instance count must be over 0"); + return; + } + + if (instanceCount > NAZARA_RENDERER_INSTANCING_MAX) + { + NazaraError("Instance count is over maximum instance count (" + NzString::Number(instanceCount) + " >= " + NzString::Number(NAZARA_RENDERER_INSTANCING_MAX) + ')'); + return; + } + #endif + + if (!s_instancingBuffer->Fill(instancingData, 0, instanceCount)) + NazaraError("Failed to fill instancing buffer"); +} + +void NzRenderer::Flush() +{ + glFlush(); +} + float NzRenderer::GetLineWidth() { #ifdef NAZARA_DEBUG @@ -387,6 +564,8 @@ bool NzRenderer::Initialize(bool initializeDebugDrawer) NzContext::EnsureContext(); + NzBuffer::SetBufferFunction(nzBufferStorage_Hardware, HardwareBufferFunction); + for (unsigned int i = 0; i < totalMatrixCount; ++i) { s_matrix[i].MakeIdentity(); @@ -398,8 +577,8 @@ bool NzRenderer::Initialize(bool initializeDebugDrawer) s_capabilities[nzRendererCap_AnisotropicFilter] = NzOpenGL::IsSupported(nzOpenGLExtension_AnisotropicFilter); s_capabilities[nzRendererCap_FP64] = NzOpenGL::IsSupported(nzOpenGLExtension_FP64); s_capabilities[nzRendererCap_HardwareBuffer] = true; // Natif depuis OpenGL 1.5 - // MultipleRenderTargets (Techniquement natif depuis OpenGL 2.0 mais inutile sans glBindFragDataLocation) - s_capabilities[nzRendererCap_MultipleRenderTargets] = (glBindFragDataLocation != nullptr); + 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); @@ -418,6 +597,35 @@ bool NzRenderer::Initialize(bool initializeDebugDrawer) else s_maxAnisotropyLevel = 1; + if (s_capabilities[nzRendererCap_Instancing]) + { + s_instancingBuffer = new NzBuffer(nzBufferType_Vertex); + if (s_instancingBuffer->Create(NAZARA_RENDERER_INSTANCING_MAX, sizeof(InstancingData), nzBufferStorage_Hardware, nzBufferUsage_Dynamic)) + { + static_cast(s_instancingBuffer->GetImpl())->Bind(); + + unsigned int instanceMatrixIndex = NzOpenGL::AttributeIndex[nzElementUsage_TexCoord] + 8; + for (unsigned int i = 0; i < 4; ++i) + { + glVertexAttribPointer(instanceMatrixIndex, 4, GL_FLOAT, GL_FALSE, sizeof(InstancingData), reinterpret_cast(offsetof(InstancingData, worldMatrix) + sizeof(float)*4*i)); + glVertexAttribDivisor(instanceMatrixIndex, 1); + + instanceMatrixIndex++; + } + + s_instancingEnabled = false; + } + else + { + s_capabilities[nzRendererCap_Instancing] = false; + + delete s_instancingBuffer; + s_instancingBuffer = nullptr; + + NazaraWarning("Failed to create instancing buffer, disabled instancing."); + } + } + if (s_capabilities[nzRendererCap_MultipleRenderTargets]) { GLint maxDrawBuffers; @@ -467,8 +675,6 @@ bool NzRenderer::Initialize(bool initializeDebugDrawer) s_vertexBuffer = nullptr; s_vertexDeclaration = nullptr; - NzBuffer::SetBufferFunction(nzBufferStorage_Hardware, HardwareBufferFunction); - if (initializeDebugDrawer && !NzDebugDrawer::Initialize()) NazaraWarning("Failed to initialize debug drawer"); // Non-critique @@ -1040,6 +1246,13 @@ void NzRenderer::Uninitialize() NzContext::EnsureContext(); + // Libération du buffer d'instancing + if (s_instancingBuffer) + { + delete s_instancingBuffer; + s_instancingBuffer = nullptr; + } + // Libération des VAOs for (auto it = s_vaos.begin(); it != s_vaos.end(); ++it) { diff --git a/src/Nazara/Renderer/ShaderBuilder.cpp b/src/Nazara/Renderer/ShaderBuilder.cpp index fca57cb86..1b6c1587f 100644 --- a/src/Nazara/Renderer/ShaderBuilder.cpp +++ b/src/Nazara/Renderer/ShaderBuilder.cpp @@ -303,14 +303,22 @@ namespace sourceCode += '\n'; /********************Uniformes********************/ - if (flags & nzShaderBuilder_Lighting) - sourceCode += "uniform mat4 WorldMatrix;\n"; + if (flags & nzShaderBuilder_Instancing) + sourceCode += "uniform mat4 ViewProjMatrix;\n"; + else + { + if (flags & nzShaderBuilder_Lighting) + sourceCode += "uniform mat4 WorldMatrix;\n"; - sourceCode += "uniform mat4 WorldViewProjMatrix;\n"; + sourceCode += "uniform mat4 WorldViewProjMatrix;\n"; + } sourceCode += '\n'; /********************Entrant********************/ + if (flags & nzShaderBuilder_Instancing) + sourceCode += inKW + " mat4 InstanceMatrix;\n"; + sourceCode += inKW + " vec3 VertexPosition;\n"; if (flags & nzShaderBuilder_Lighting) @@ -343,31 +351,43 @@ namespace /********************Code********************/ sourceCode += "void main()\n" - "{\n" - "gl_Position = WorldViewProjMatrix * vec4(VertexPosition, 1.0);\n"; + "{\n"; + + if (flags & nzShaderBuilder_Instancing) + sourceCode += "gl_Position = InstanceMatrix * ViewProjMatrix * vec4(VertexPosition, 1.0);\n"; + else + sourceCode += "gl_Position = WorldViewProjMatrix * vec4(VertexPosition, 1.0);\n"; if (flags & nzShaderBuilder_Lighting) { - sourceCode += "mat3 RotationMatrix = mat3(WorldMatrix);\n"; + if (flags & nzShaderBuilder_Instancing) + sourceCode += "mat3 rotationMatrix = mat3(InstanceMatrix);\n"; + else + sourceCode += "mat3 rotationMatrix = mat3(WorldMatrix);\n"; if (flags & nzShaderBuilder_NormalMapping) { sourceCode += "\n" "vec3 binormal = cross(VertexNormal, VertexTangent);\n" - "vLightToWorld[0] = normalize(VertexTangent * RotationMatrix);\n" - "vLightToWorld[1] = normalize(binormal * RotationMatrix);\n" - "vLightToWorld[2] = normalize(VertexNormal * RotationMatrix);\n" + "vLightToWorld[0] = normalize(VertexTangent * rotationMatrix);\n" + "vLightToWorld[1] = normalize(binormal * rotationMatrix);\n" + "vLightToWorld[2] = normalize(VertexNormal * rotationMatrix);\n" "\n"; } else - sourceCode += "vNormal = normalize(RotationMatrix * VertexNormal);\n"; + sourceCode += "vNormal = normalize(rotationMatrix * VertexNormal);\n"; } if (flags & nzShaderBuilder_DiffuseMapping || flags & nzShaderBuilder_NormalMapping) sourceCode += "vTexCoord = VertexTexCoord0;\n"; if (flags & nzShaderBuilder_Lighting) - sourceCode += "vWorldPos = vec3(WorldMatrix * vec4(VertexPosition, 1.0));\n"; + { + if (flags & nzShaderBuilder_Instancing) + sourceCode += "vWorldPos = vec3(InstanceMatrix * vec4(VertexPosition, 1.0));\n"; + else + sourceCode += "vWorldPos = vec3(WorldMatrix * vec4(VertexPosition, 1.0));\n"; + } sourceCode += "}\n";