internal override void Draw(GameObject g, ref Matrix4 viewProjection, ref Matrix4 viewProjectionShadowBiased, ref Matrix4 viewProjectionShadowBiased2, HelperFrustum frustum, ref float[] lightColors, ref float[] lightTargets, ref float[] lightPositions, int lightCount, ref int lightShadow)
        {
            if (g == null || !g.HasModel || g.CurrentWorld == null || g.Opacity <= 0)
            {
                return;
            }

            g.IsInsideScreenSpace = frustum.VolumeVsFrustum(g.GetCenterPointForAllHitboxes(), g.GetMaxDimensions().X, g.GetMaxDimensions().Y, g.GetMaxDimensions().Z);
            if (!g.IsInsideScreenSpace)
            {
                return;
            }

            GL.UseProgram(mProgramId);

            lock (g)
            {
                GL.Uniform1(mUniform_BiasCoefficient, KWEngine.ShadowMapCoefficient);

                GL.Uniform4(mUniform_Glow, g.Glow);
                GL.Uniform4(mUniform_Outline, g.ColorOutline);
                GL.Uniform3(mUniform_TintColor, g.Color);

                // How many lights are there?
                GL.Uniform1(mUniform_LightCount, lightCount);
                GL.Uniform4(mUniform_LightsColors, KWEngine.MAX_LIGHTS, lightColors);
                GL.Uniform4(mUniform_LightsTargets, KWEngine.MAX_LIGHTS, lightTargets);
                GL.Uniform4(mUniform_LightsPositions, KWEngine.MAX_LIGHTS, lightPositions);

                // Sun
                GL.Uniform4(mUniform_SunIntensity, g.CurrentWorld.GetSunColor());
                GL.Uniform3(mUniform_SunPosition, g.CurrentWorld.GetSunPosition().X, g.CurrentWorld.GetSunPosition().Y, g.CurrentWorld.GetSunPosition().Z);
                Vector3 sunDirection = g.CurrentWorld.GetSunPosition() - g.CurrentWorld.GetSunTarget();
                sunDirection.NormalizeFast();
                GL.Uniform3(mUniform_SunDirection, ref sunDirection);
                GL.Uniform1(mUniform_SunAmbient, g.CurrentWorld.SunAmbientFactor);

                GL.Uniform1(mUniform_SunAffection, g.IsAffectedBySun ? 1 : 0);
                GL.Uniform1(mUniform_LightAffection, g.IsAffectedByLight ? 1 : 0);

                // Camera
                if (!CurrentWorld.IsFirstPersonMode)
                {
                    GL.Uniform3(mUniform_uCameraPos, g.CurrentWorld.GetCameraPosition().X, g.CurrentWorld.GetCameraPosition().Y, g.CurrentWorld.GetCameraPosition().Z);
                    GL.Uniform3(mUniform_uCameraDirection, g.CurrentWorld.GetCameraLookAtVector());
                }
                else
                {
                    GL.Uniform3(mUniform_uCameraPos, g.CurrentWorld.GetFirstPersonObject().Position.X, g.CurrentWorld.GetFirstPersonObject().Position.Y + g.CurrentWorld.GetFirstPersonObject().FPSEyeOffset, g.CurrentWorld.GetFirstPersonObject().Position.Z);
                    GL.Uniform3(mUniform_uCameraDirection, HelperCamera.GetLookAtVector());
                }



                // Upload depth texture (shadow mapping)
                GL.ActiveTexture(TextureUnit.Texture3);
                GL.BindTexture(TextureTarget.Texture2D, GLWindow.CurrentWindow.TextureShadowMap);
                GL.Uniform1(mUniform_TextureShadowMap, 3);

                if (lightShadow >= 0)
                {
                    GL.ActiveTexture(TextureUnit.Texture5);
                    GL.BindTexture(TextureTarget.Texture2D, GLWindow.CurrentWindow.TextureShadowMap2);
                    GL.Uniform1(mUniform_TextureShadowMap2, 5);

                    GL.Uniform1(mUniform_ShadowLightPosition, lightShadow);
                    GL.Uniform1(mUniform_BiasCoefficient2, CurrentWorld.GetLightObjects().ElementAt(lightShadow).ShadowMapBiasCoefficient);
                }
                else
                {
                    GL.ActiveTexture(TextureUnit.Texture5);
                    GL.BindTexture(TextureTarget.Texture2D, GLWindow.CurrentWindow.TextureShadowMap2);
                    GL.Uniform1(mUniform_TextureShadowMap2, 5);
                    GL.Uniform1(mUniform_ShadowLightPosition, -1);
                }

                int index = 0;
                foreach (string meshName in g.Model.Meshes.Keys)
                {
                    if (g._cubeModel is GeoModelCube6)
                    {
                        index = 0;
                    }

                    // Matrices:
                    try
                    {
                        Matrix4.Mult(ref g.ModelMatrixForRenderPass[index], ref viewProjection, out _modelViewProjection);
                        Matrix4.Transpose(ref g.ModelMatrixForRenderPass[index], out _normalMatrix);
                        Matrix4.Invert(ref _normalMatrix, out _normalMatrix);
                    }
                    catch (Exception)
                    {
                        continue;
                    }

                    GL.UniformMatrix4(mUniform_ModelMatrix, false, ref g.ModelMatrixForRenderPass[index]);
                    GL.UniformMatrix4(mUniform_NormalMatrix, false, ref _normalMatrix);
                    GL.UniformMatrix4(mUniform_MVP, false, ref _modelViewProjection);

                    // Shadow mapping
                    Matrix4 modelViewProjectionMatrixBiased = g.ModelMatrixForRenderPass[index] * viewProjectionShadowBiased;
                    GL.UniformMatrix4(mUniform_MVPShadowMap, false, ref modelViewProjectionMatrixBiased);

                    if (lightShadow >= 0)
                    {
                        Matrix4 modelViewProjectionMatrixBiased2 = g.ModelMatrixForRenderPass[index] * viewProjectionShadowBiased2;
                        GL.UniformMatrix4(mUniform_MVPShadowMap2, false, ref modelViewProjectionMatrixBiased2);
                    }
                    index++;


                    GL.Disable(EnableCap.Blend);
                    GeoMesh mesh = g.Model.Meshes[meshName];
                    if (mesh.Material.Opacity <= 0)
                    {
                        continue;
                    }

                    if (mesh.Material.Opacity < 1 || g.Opacity < 1)
                    {
                        GL.Enable(EnableCap.Blend);
                    }
                    if (g.Opacity < mesh.Material.Opacity)
                    {
                        GL.Uniform1(mUniform_Opacity, g.Opacity);
                    }
                    else
                    {
                        GL.Uniform1(mUniform_Opacity, mesh.Material.Opacity);
                    }

                    Dictionary <GameObject.Override, object> overrides = null;
                    if (g._overrides.ContainsKey(mesh.Name))
                    {
                        overrides = g._overrides[mesh.Name];
                    }

                    if (mesh.BoneNames.Count > 0 && g.AnimationID >= 0 && g.Model.Animations != null && g.Model.Animations.Count > 0)
                    {
                        GL.Uniform1(mUniform_UseAnimations, 1);
                        for (int i = 0; i < g.BoneTranslationMatrices[meshName].Length; i++)
                        {
                            Matrix4 tmp = g.BoneTranslationMatrices[meshName][i];
                            GL.UniformMatrix4(mUniform_BoneTransforms + i, false, ref tmp);
                        }
                    }
                    else
                    {
                        GL.Uniform1(mUniform_UseAnimations, 0);
                    }

                    if (g._cubeModel != null)
                    {
                        GL.Uniform1(mUniform_SpecularPower, g._cubeModel.SpecularPower);
                        GL.Uniform1(mUniform_SpecularArea, g._cubeModel.SpecularArea);
                    }
                    else
                    {
                        if (overrides == null || overrides.Count == 0)
                        {
                            GL.Uniform1(mUniform_SpecularPower, mesh.Material.SpecularPower);
                        }
                        else
                        {
                            bool found = overrides.TryGetValue(GameObject.Override.SpecularPower, out object value);
                            if (found)
                            {
                                GL.Uniform1(mUniform_SpecularPower, (float)value);
                            }
                            else
                            {
                                GL.Uniform1(mUniform_SpecularPower, mesh.Material.SpecularPower);
                            }
                        }

                        if (overrides == null || overrides.Count == 0)
                        {
                            GL.Uniform1(mUniform_SpecularArea, mesh.Material.SpecularArea);
                        }
                        else
                        {
                            bool found = overrides.TryGetValue(GameObject.Override.SpecularArea, out object value);
                            if (found)
                            {
                                GL.Uniform1(mUniform_SpecularArea, (float)value);
                            }
                            else
                            {
                                GL.Uniform1(mUniform_SpecularArea, mesh.Material.SpecularArea);
                            }
                        }
                    }



                    if (g._cubeModel != null)
                    {
                        UploadMaterialForKWCube(g._cubeModel, mesh, g);
                        GL.Uniform1(mUniform_TextureUseLightMap, 0);
                    }
                    else
                    {
                        if (mesh.Material.TextureLight.OpenGLID > 0)
                        {
                            GL.ActiveTexture(TextureUnit.Texture8);
                            GL.BindTexture(TextureTarget.Texture2D, mesh.Material.TextureLight.OpenGLID);
                            GL.Uniform1(mUniform_TextureLightMap, 8);
                            GL.Uniform1(mUniform_TextureUseLightMap, 1);
                        }
                        else
                        {
                            GL.Uniform1(mUniform_TextureUseLightMap, 0);
                        }



                        bool   found         = false;
                        object overrideValue = null;
                        if (overrides != null && overrides.Count > 0)
                        {
                            found = overrides.TryGetValue(GameObject.Override.TextureTransform, out overrideValue);
                        }

                        if (found)
                        {
                            Vector2 uvTransform = (Vector2)overrideValue;
                            GL.Uniform2(mUniform_TextureTransform, uvTransform.X, uvTransform.Y);
                        }
                        else
                        {
                            GL.Uniform2(mUniform_TextureTransform, mesh.Material.TextureDiffuse.UVTransform.X, mesh.Material.TextureDiffuse.UVTransform.Y);
                        }

                        // Diffuse texture:
                        overrideValue = null;
                        found         = false;
                        int texId = -1;
                        if (overrides != null && overrides.Count > 0)
                        {
                            found = overrides.TryGetValue(GameObject.Override.TextureDiffuse, out overrideValue);
                        }
                        if (found)
                        {
                            texId = ((GeoTexture)overrideValue).OpenGLID;
                        }
                        else
                        {
                            texId = mesh.Material.TextureDiffuse.OpenGLID;
                        }

                        if (texId > 0)
                        {
                            GL.ActiveTexture(TextureUnit.Texture0);
                            GL.BindTexture(TextureTarget.Texture2D, texId);
                            GL.Uniform1(mUniform_Texture, 0);
                            GL.Uniform1(mUniform_TextureUse, 1);
                            GL.Uniform3(mUniform_BaseColor, 1f, 1f, 1f);
                        }
                        else
                        {
                            GL.Uniform1(mUniform_TextureUse, 0);
                            GL.Uniform3(mUniform_BaseColor, mesh.Material.ColorDiffuse.X, mesh.Material.ColorDiffuse.Y, mesh.Material.ColorDiffuse.Z);
                        }

                        overrideValue = null;
                        found         = false;
                        texId         = -1;
                        if (overrides != null && overrides.Count > 0)
                        {
                            found = overrides.TryGetValue(GameObject.Override.TextureNormal, out overrideValue);
                        }
                        if (found)
                        {
                            texId = ((GeoTexture)overrideValue).OpenGLID;
                        }
                        else
                        {
                            texId = mesh.Material.TextureNormal.OpenGLID;
                        }
                        if (texId > 0)
                        {
                            GL.ActiveTexture(TextureUnit.Texture1);
                            GL.BindTexture(TextureTarget.Texture2D, texId);
                            GL.Uniform1(mUniform_TextureNormalMap, 1);
                            GL.Uniform1(mUniform_TextureUseNormalMap, 1);
                        }
                        else
                        {
                            GL.Uniform1(mUniform_TextureUseNormalMap, 0);
                        }

                        overrideValue = null;
                        found         = false;
                        texId         = -1;
                        if (overrides != null)
                        {
                            found = overrides.TryGetValue(GameObject.Override.TextureSpecular, out overrideValue);
                        }
                        if (found)
                        {
                            texId = ((GeoTexture)overrideValue).OpenGLID;
                        }
                        else
                        {
                            texId = mesh.Material.TextureSpecular.OpenGLID;
                        }
                        if (texId > 0)
                        {
                            GL.ActiveTexture(TextureUnit.Texture2);
                            GL.BindTexture(TextureTarget.Texture2D, texId);
                            GL.Uniform1(mUniform_TextureSpecularMap, 2);
                            GL.Uniform1(mUniform_TextureUseSpecularMap, 1);
                            if (!found && mesh.Material.TextureSpecularIsRoughness)
                            {
                                GL.Uniform1(mUniform_TextureSpecularIsRoughness, mesh.Material.TextureSpecularIsRoughness ? 1 : 0);
                            }
                            else
                            {
                                GL.Uniform1(mUniform_TextureSpecularIsRoughness, 0);
                            }
                        }
                        else
                        {
                            GL.Uniform1(mUniform_TextureUseSpecularMap, 0);
                            GL.Uniform1(mUniform_TextureSpecularIsRoughness, 0);
                        }


                        if (mesh.Material.TextureEmissive.OpenGLID > 0)
                        {
                            GL.ActiveTexture(TextureUnit.Texture4);
                            GL.BindTexture(TextureTarget.Texture2D, mesh.Material.TextureEmissive.OpenGLID);
                            GL.Uniform1(mUniform_TextureEmissiveMap, 4);
                            GL.Uniform1(mUniform_TextureUseEmissiveMap, 1);
                        }
                        else
                        {
                            GL.Uniform1(mUniform_TextureUseEmissiveMap, 0);
                        }

                        if (g.ColorEmissive.W > 0)
                        {
                            GL.Uniform4(mUniform_EmissiveColor, g.ColorEmissive);
                        }
                        else
                        {
                            GL.Uniform4(mUniform_EmissiveColor, mesh.Material.ColorEmissive);
                        }
                    }

                    GL.BindVertexArray(mesh.VAO);
                    GL.BindBuffer(BufferTarget.ElementArrayBuffer, mesh.VBOIndex);
                    GL.DrawElements(mesh.Primitive, mesh.IndexCount, DrawElementsType.UnsignedInt, 0);
                    //HelperGL.CheckGLErrors();
                    GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
                    GL.BindVertexArray(0);
                    GL.BindTexture(TextureTarget.Texture2D, 0);
                }
            }
            GL.BindTexture(TextureTarget.Texture2D, 0);
            GL.UseProgram(0);
        }