Пример #1
0
        /// <summary>Create an OpenGL/OpenTK VAO for a static background</summary>
        /// <param name="background">The background</param>
        /// <param name="vertexLayout">The vertex layout to use</param>
        /// <param name="renderer">A reference to the base renderer</param>
        public static void CreateVAO(this StaticBackground background, VertexLayout vertexLayout, BaseRenderer renderer)
        {
            try
            {
                float y0, y1;

                if (background.KeepAspectRatio)
                {
                    int    tw = background.Texture.Width;
                    int    th = background.Texture.Height;
                    double hh = Math.PI * background.BackgroundImageDistance * th / (tw * background.Repetition);
                    y0 = (float)(-0.5 * hh);
                    y1 = (float)(1.5 * hh);
                }
                else
                {
                    y0 = (float)(-0.125 * background.BackgroundImageDistance);
                    y1 = (float)(0.375 * background.BackgroundImageDistance);
                }

                const int    n              = 32;
                Vector3f[]   bottom         = new Vector3f[n];
                Vector3f[]   top            = new Vector3f[n];
                double       angleValue     = 2.61799387799149 - 3.14159265358979 / n;
                const double angleIncrement = 6.28318530717958 / n;

                /*
                 * To ensure that the whole background cylinder is rendered inside the viewing frustum,
                 * the background is rendered before the scene with z-buffer writes disabled. Then,
                 * the actual distance from the camera is irrelevant as long as it is inside the frustum.
                 * */
                for (int i = 0; i < n; i++)
                {
                    float x = (float)(background.BackgroundImageDistance * Math.Cos(angleValue));
                    float z = (float)(background.BackgroundImageDistance * Math.Sin(angleValue));
                    bottom[i]   = new Vector3f(x, y0, z);
                    top[i]      = new Vector3f(x, y1, z);
                    angleValue += angleIncrement;
                }

                float textureStart     = 0.5f * (float)background.Repetition / n;
                float textureIncrement = -(float)background.Repetition / n;
                float textureX         = textureStart;

                List <LibRenderVertex> vertexData = new List <LibRenderVertex>();
                List <ushort>          indexData  = new List <ushort>();

                for (int i = 0; i < n; i++)
                {
                    int j           = (i + 1) % n;
                    int indexOffset = vertexData.Count;

                    // side wall
                    vertexData.Add(new LibRenderVertex
                    {
                        Position = top[i],
                        UV       = new Vector2f(textureX, 0.005f),
                        Color    = Color128.White
                    });

                    vertexData.Add(new LibRenderVertex
                    {
                        Position = bottom[i],
                        UV       = new Vector2f(textureX, 0.995f),
                        Color    = Color128.White
                    });

                    vertexData.Add(new LibRenderVertex
                    {
                        Position = bottom[j],
                        UV       = new Vector2f(textureX + textureIncrement, 0.995f),
                        Color    = Color128.White
                    });

                    vertexData.Add(new LibRenderVertex
                    {
                        Position = top[j],
                        UV       = new Vector2f(textureX + textureIncrement, 0.005f),
                        Color    = Color128.White
                    });

                    indexData.AddRange(new[] { 0, 1, 2 }.Select(x => x + indexOffset).Select(x => (ushort)x));
                    indexData.AddRange(new[] { 0, 2, 3 }.Select(x => x + indexOffset).Select(x => (ushort)x));

                    // top cap
                    vertexData.Add(new LibRenderVertex
                    {
                        Position = new Vector3f(0.0f, top[i].Y, 0.0f),
                        UV       = new Vector2f(textureX + 0.5f * textureIncrement, 0.1f),
                        Color    = Color128.White
                    });

                    indexData.AddRange(new[] { 0, 3, 4 }.Select(x => x + indexOffset).Select(x => (ushort)x));

                    // bottom cap
                    vertexData.Add(new LibRenderVertex
                    {
                        Position = new Vector3f(0.0f, bottom[i].Y, 0.0f),
                        UV       = new Vector2f(textureX + 0.5f * textureIncrement, 0.9f),
                        Color    = Color128.White
                    });

                    indexData.AddRange(new[] { 5, 2, 1 }.Select(x => x + indexOffset).Select(x => (ushort)x));

                    // finish
                    textureX += textureIncrement;
                }

                VertexArrayObject VAO = (VertexArrayObject)background.VAO;
                VAO?.UnBind();
                VAO?.Dispose();

                VAO = new VertexArrayObject();
                VAO.Bind();
                VAO.SetVBO(new VertexBufferObject(vertexData.ToArray(), BufferUsageHint.StaticDraw));
                VAO.SetIBO(new IndexBufferObjectUS(indexData.ToArray(), BufferUsageHint.StaticDraw));
                VAO.SetAttributes(vertexLayout);
                VAO.UnBind();
                background.VAO = VAO;
            }
            catch (Exception e)
            {
                renderer.ForceLegacyOpenGL = true;
                renderer.currentHost.AddMessage(MessageType.Error, false, $"Creating VAO failed with the following error: {e}");
            }
        }
Пример #2
0
        /// <summary>Create an OpenGL/OpenTK VAO for a mesh</summary>
        /// <param name="mesh">The mesh</param>
        /// <param name="isDynamic">Whether the mesh is dynamic (e.g. part of an animated object / train)</param>
        /// <param name="vertexLayout">The vertex layout to use</param>
        /// <param name="renderer">A reference to the base renderer</param>
        public static void CreateVAO(ref Mesh mesh, bool isDynamic, VertexLayout vertexLayout, BaseRenderer renderer)
        {
            try
            {
                var hint = isDynamic ? BufferUsageHint.DynamicDraw : BufferUsageHint.StaticDraw;

                var vertexData = new List <LibRenderVertex>();
                var indexData  = new List <uint>();

                var normalsVertexData = new List <LibRenderVertex>();
                var normalsIndexData  = new List <uint>();

                for (int i = 0; i < mesh.Faces.Length; i++)
                {
                    mesh.Faces[i].IboStartIndex        = indexData.Count;
                    mesh.Faces[i].NormalsIboStartIndex = normalsIndexData.Count;

                    foreach (var vertex in mesh.Faces[i].Vertices)
                    {
                        var data = new LibRenderVertex
                        {
                            Position = mesh.Vertices[vertex.Index].Coordinates,
                            Normal   = vertex.Normal,
                            UV       = new Vector2f(mesh.Vertices[vertex.Index].TextureCoordinates),
                            Color    = (mesh.Vertices[vertex.Index] as ColoredVertex)?.Color ?? Color128.White
                        };

                        vertexData.Add(data);

                        var normalsData = new LibRenderVertex[2];
                        normalsData[0].Position = data.Position;
                        normalsData[1].Position = data.Position + data.Normal;

                        for (int j = 0; j < normalsData.Length; j++)
                        {
                            normalsData[j].Color = Color128.White;
                        }

                        normalsVertexData.AddRange(normalsData);
                    }

                    indexData.AddRange(Enumerable.Range(mesh.Faces[i].IboStartIndex, mesh.Faces[i].Vertices.Length).Select(x => (uint)x));
                    normalsIndexData.AddRange(Enumerable.Range(mesh.Faces[i].NormalsIboStartIndex, mesh.Faces[i].Vertices.Length * 2).Select(x => (uint)x));
                }

                VertexArrayObject VAO = (VertexArrayObject)mesh.VAO;
                VAO?.UnBind();
                VAO?.Dispose();

                VAO = new VertexArrayObject();
                VAO.Bind();
                VAO.SetVBO(new VertexBufferObject(vertexData.ToArray(), hint));
                if (indexData.Count > 65530)
                {
                    //Marginal headroom, although it probably doesn't matter
                    VAO.SetIBO(new IndexBufferObjectUI(indexData.ToArray(), hint));
                }
                else
                {
                    VAO.SetIBO(new IndexBufferObjectUS(indexData.Select(x => (ushort)x).ToArray(), hint));
                }

                VAO.SetAttributes(vertexLayout);
                VAO.UnBind();
                mesh.VAO = VAO;
                VertexArrayObject NormalsVAO = (VertexArrayObject)mesh.NormalsVAO;
                NormalsVAO?.UnBind();
                NormalsVAO?.Dispose();

                NormalsVAO = new VertexArrayObject();
                NormalsVAO.Bind();
                NormalsVAO.SetVBO(new VertexBufferObject(normalsVertexData.ToArray(), hint));
                if (normalsIndexData.Count > 65530)
                {
                    //Marginal headroom, although it probably doesn't matter
                    NormalsVAO.SetIBO(new IndexBufferObjectUI(normalsIndexData.ToArray(), hint));
                }
                else
                {
                    NormalsVAO.SetIBO(new IndexBufferObjectUS(normalsIndexData.Select(x => (ushort)x).ToArray(), hint));
                }

                NormalsVAO.SetAttributes(vertexLayout);
                NormalsVAO.UnBind();
                mesh.NormalsVAO = NormalsVAO;
            }
            catch (Exception e)
            {
                renderer.ForceLegacyOpenGL = true;
                renderer.currentHost.AddMessage(MessageType.Error, false, $"Creating VAO failed with the following error: {e}");
            }
        }
Пример #3
0
        public void RenderFace(Shader Shader, ObjectState State, MeshFace Face, Vector3 EyePosition, bool IsDebugTouchMode = false)
        {
            if (State.Prototype.Mesh.Vertices.Length < 1)
            {
                return;
            }

            VertexTemplate[]  vertices   = State.Prototype.Mesh.Vertices;
            MeshMaterial      material   = State.Prototype.Mesh.Materials[Face.Material];
            VertexArrayObject VAO        = (VertexArrayObject)State.Prototype.Mesh.VAO;
            VertexArrayObject NormalsVAO = (VertexArrayObject)State.Prototype.Mesh.NormalsVAO;

            if (!OptionBackFaceCulling || (Face.Flags & MeshFace.Face2Mask) != 0)
            {
                GL.Disable(EnableCap.CullFace);
            }
            else if (OptionBackFaceCulling)
            {
                if ((Face.Flags & MeshFace.Face2Mask) == 0)
                {
                    GL.Enable(EnableCap.CullFace);
                }
            }

            // matrix
            Matrix4D modelMatrix     = State.Scale * State.Rotate * State.Translation * Matrix4D.CreateTranslation(-EyePosition);
            Matrix4D modelViewMatrix = modelMatrix * CurrentViewMatrix;

            Shader.SetCurrentProjectionMatrix(CurrentProjectionMatrix);
            Shader.SetCurrentModelViewMatrix(modelViewMatrix);
            Shader.SetCurrentNormalMatrix(Matrix4D.Transpose(Matrix4D.Invert(modelViewMatrix)));
            Shader.SetCurrentTextureMatrix(State.TextureTranslation);

            if (OptionWireFrame || IsDebugTouchMode)
            {
                VAO.Bind();
                VAO.Draw(Shader.VertexLayout, PrimitiveType.LineLoop, Face.IboStartIndex, Face.Vertices.Length);
                VAO.UnBind();
                return;
            }

            // lighting
            if (material.NighttimeTexture == null)
            {
                if (OptionLighting)
                {
                    Shader.SetIsLight(true);
                    Shader.SetLightPosition(new Vector3(Lighting.OptionLightPosition.X, Lighting.OptionLightPosition.Y, -Lighting.OptionLightPosition.Z));
                    Shader.SetLightAmbient(new Color4(Lighting.OptionAmbientColor.R, Lighting.OptionAmbientColor.G, Lighting.OptionAmbientColor.B, 255));
                    Shader.SetLightDiffuse(new Color4(Lighting.OptionDiffuseColor.R, Lighting.OptionDiffuseColor.G, Lighting.OptionDiffuseColor.B, 255));
                    Shader.SetLightSpecular(new Color4(Lighting.OptionSpecularColor.R, Lighting.OptionSpecularColor.G, Lighting.OptionSpecularColor.B, 255));
                    Shader.SetMaterialAmbient(new Color4(material.Color.R, material.Color.G, material.Color.B, material.Color.A));                      // TODO
                    Shader.SetMaterialDiffuse(new Color4(material.Color.R, material.Color.G, material.Color.B, material.Color.A));
                    Shader.SetMaterialSpecular(new Color4(material.Color.R, material.Color.G, material.Color.B, material.Color.A));                     // TODO

                    if ((material.Flags & MeshMaterial.EmissiveColorMask) != 0)
                    {
                        Shader.SetMaterialEmission(new Color4(material.EmissiveColor.R, material.EmissiveColor.G, material.EmissiveColor.B, 255));
                    }
                    else
                    {
                        Shader.SetMaterialEmission(new Color4(0.0f, 0.0f, 0.0f, 1.0f));
                    }

                    Shader.SetMaterialShininess(1.0f);

                    Lighting.OptionLightingResultingAmount = (Lighting.OptionAmbientColor.R + Lighting.OptionAmbientColor.G + Lighting.OptionAmbientColor.B) / 480.0f;

                    if (Lighting.OptionLightingResultingAmount > 1.0f)
                    {
                        Lighting.OptionLightingResultingAmount = 1.0f;
                    }
                }
                else
                {
                    Shader.SetMaterialAmbient(new Color4(material.Color.R, material.Color.G, material.Color.B, material.Color.A));                      // TODO
                }
            }
            else
            {
                Shader.SetMaterialAmbient(new Color4(material.Color.R, material.Color.G, material.Color.B, material.Color.A));                  // TODO
            }

            // fog
            if (OptionFog)
            {
                Shader.SetIsFog(true);
                Shader.SetFogStart(Fog.Start);
                Shader.SetFogEnd(Fog.End);
                Shader.SetFogColor(new Color4(Fog.Color.R, Fog.Color.G, Fog.Color.B, 255));
            }

            PrimitiveType DrawMode;

            switch (Face.Flags & MeshFace.FaceTypeMask)
            {
            case MeshFace.FaceTypeTriangles:
                DrawMode = PrimitiveType.Triangles;
                break;

            case MeshFace.FaceTypeTriangleStrip:
                DrawMode = PrimitiveType.TriangleStrip;
                break;

            case MeshFace.FaceTypeQuads:
                DrawMode = PrimitiveType.Quads;
                break;

            case MeshFace.FaceTypeQuadStrip:
                DrawMode = PrimitiveType.QuadStrip;
                break;

            default:
                DrawMode = PrimitiveType.Polygon;
                break;
            }

            // daytime polygon
            {
                // texture
                if (material.DaytimeTexture != null)
                {
                    if (currentHost.LoadTexture(material.DaytimeTexture, (OpenGlTextureWrapMode)material.WrapMode))
                    {
                        GL.Enable(EnableCap.Texture2D);
                        Shader.SetIsTexture(true);
                        Shader.SetTexture(0);
                        GL.BindTexture(TextureTarget.Texture2D, material.DaytimeTexture.OpenGlTextures[(int)material.WrapMode].Name);
                    }
                }

                // blend mode
                float factor;
                if (material.BlendMode == MeshMaterialBlendMode.Additive)
                {
                    factor = 1.0f;
                    GL.Enable(EnableCap.Blend);
                    GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One);
                    Shader.SetIsFog(false);
                }
                else if (material.NighttimeTexture == null)
                {
                    float blend = inv255 * material.DaytimeNighttimeBlend + 1.0f - Lighting.OptionLightingResultingAmount;

                    if (blend > 1.0f)
                    {
                        blend = 1.0f;
                    }

                    factor = 1.0f - 0.7f * blend;
                }
                else
                {
                    factor = 1.0f;
                }
                Shader.SetBrightness(factor);

                float alphaFactor;
                if (material.GlowAttenuationData != 0)
                {
                    alphaFactor = (float)Glow.GetDistanceFactor(modelMatrix, vertices, ref Face, material.GlowAttenuationData);
                }
                else
                {
                    alphaFactor = 1.0f;
                }
                Shader.SetOpacity(inv255 * material.Color.A * alphaFactor);

                // render polygon
                VAO.Bind();
                VAO.Draw(Shader.VertexLayout, DrawMode, Face.IboStartIndex, Face.Vertices.Length);
                VAO.UnBind();

                GL.BindTexture(TextureTarget.Texture2D, 0);
            }

            // nighttime polygon
            if (material.NighttimeTexture != null && currentHost.LoadTexture(material.NighttimeTexture, (OpenGlTextureWrapMode)material.WrapMode))
            {
                // texture
                GL.Enable(EnableCap.Texture2D);
                Shader.SetIsTexture(true);
                Shader.SetTexture(0);
                GL.BindTexture(TextureTarget.Texture2D, material.NighttimeTexture.OpenGlTextures[(int)material.WrapMode].Name);

                GL.Enable(EnableCap.Blend);

                // alpha test
                GL.Enable(EnableCap.AlphaTest);
                GL.AlphaFunc(AlphaFunction.Greater, 0.0f);

                // blend mode
                float alphaFactor;
                if (material.GlowAttenuationData != 0)
                {
                    alphaFactor = (float)Glow.GetDistanceFactor(modelMatrix, vertices, ref Face, material.GlowAttenuationData);
                    float blend = inv255 * material.DaytimeNighttimeBlend + 1.0f - Lighting.OptionLightingResultingAmount;
                    if (blend > 1.0f)
                    {
                        blend = 1.0f;
                    }

                    alphaFactor *= blend;
                }
                else
                {
                    alphaFactor = inv255 * material.DaytimeNighttimeBlend + 1.0f - Lighting.OptionLightingResultingAmount;
                    if (alphaFactor > 1.0f)
                    {
                        alphaFactor = 1.0f;
                    }
                }

                Shader.SetOpacity(alphaFactor);

                // render polygon
                VAO.Bind();
                VAO.Draw(Shader.VertexLayout, DrawMode, Face.IboStartIndex, Face.Vertices.Length);
                VAO.UnBind();

                GL.BindTexture(TextureTarget.Texture2D, 0);

                RestoreBlendFunc();
                RestoreAlphaFunc();
            }

            GL.Disable(EnableCap.Texture2D);

            // normals
            if (OptionNormals)
            {
                Shader.SetIsTexture(false);
                Shader.SetBrightness(1.0f);
                Shader.SetOpacity(1.0f);

                NormalsVAO.Bind();
                NormalsVAO.Draw(Shader.VertexLayout, PrimitiveType.Lines, Face.NormalsIboStartIndex, Face.Vertices.Length * 2);
                NormalsVAO.UnBind();
            }

            // finalize
            if (material.BlendMode == MeshMaterialBlendMode.Additive)
            {
                RestoreBlendFunc();
            }
        }
Пример #4
0
        /// <summary>Create an OpenGL/OpenTK VAO for a mesh</summary>
        /// <param name="isDynamic"></param>
        public static void CreateVAO(ref Mesh mesh, bool isDynamic)
        {
            var hint = isDynamic ? BufferUsageHint.DynamicDraw : BufferUsageHint.StaticDraw;

            var vertexData = new List <LibRenderVertex>();
            var indexData  = new List <ushort>();

            var normalsVertexData = new List <LibRenderVertex>();
            var normalsIndexData  = new List <ushort>();

            for (int i = 0; i < mesh.Faces.Length; i++)
            {
                mesh.Faces[i].IboStartIndex        = indexData.Count;
                mesh.Faces[i].NormalsIboStartIndex = normalsIndexData.Count;

                foreach (var vertex in mesh.Faces[i].Vertices)
                {
                    var data = new LibRenderVertex
                    {
                        Position = mesh.Vertices[vertex.Index].Coordinates,
                        Normal   = vertex.Normal,
                        UV       = mesh.Vertices[vertex.Index].TextureCoordinates
                    };

                    var coloredVertex = mesh.Vertices[vertex.Index] as ColoredVertex;

                    if (coloredVertex != null)
                    {
                        data.Color = coloredVertex.Color;
                    }
                    else
                    {
                        data.Color = Color128.White;
                    }

                    vertexData.Add(data);

                    var normalsData = new LibRenderVertex[2];
                    normalsData[0].Position = data.Position;
                    normalsData[1].Position = data.Position + data.Normal;

                    for (int j = 0; j < normalsData.Length; j++)
                    {
                        normalsData[j].Color = Color128.White;
                    }

                    normalsVertexData.AddRange(normalsData);
                }

                indexData.AddRange(Enumerable.Range(mesh.Faces[i].IboStartIndex, mesh.Faces[i].Vertices.Length).Select(x => (ushort)x));
                normalsIndexData.AddRange(Enumerable.Range(mesh.Faces[i].NormalsIboStartIndex, mesh.Faces[i].Vertices.Length * 2).Select(x => (ushort)x));
            }

            VertexArrayObject VAO = (VertexArrayObject)mesh.VAO;

            VAO?.UnBind();
            VAO?.Dispose();

            VAO = new VertexArrayObject();
            VAO.Bind();
            VAO.SetVBO(new VertexBufferObject(vertexData.ToArray(), hint));
            VAO.SetIBO(new IndexBufferObject(indexData.ToArray(), hint));
            VAO.UnBind();
            mesh.VAO = VAO;
            VertexArrayObject NormalsVAO = (VertexArrayObject)mesh.NormalsVAO;

            NormalsVAO?.UnBind();
            NormalsVAO?.Dispose();

            NormalsVAO = new VertexArrayObject();
            NormalsVAO.Bind();
            NormalsVAO.SetVBO(new VertexBufferObject(normalsVertexData.ToArray(), hint));
            NormalsVAO.SetIBO(new IndexBufferObject(normalsIndexData.ToArray(), hint));
            NormalsVAO.UnBind();
            mesh.NormalsVAO = NormalsVAO;
        }
Пример #5
0
        public void RenderFace(Shader Shader, ObjectState State, MeshFace Face, bool IsDebugTouchMode = false)
        {
            if (State.Prototype.Mesh.Vertices.Length < 1)
            {
                return;
            }

            MeshMaterial      material = State.Prototype.Mesh.Materials[Face.Material];
            VertexArrayObject VAO      = (VertexArrayObject)State.Prototype.Mesh.VAO;

            if (lastVAO != VAO.handle)
            {
                VAO.BindForDrawing(Shader.VertexLayout);
                lastVAO = VAO.handle;
            }

            if (!OptionBackFaceCulling || (Face.Flags & MeshFace.Face2Mask) != 0)
            {
                GL.Disable(EnableCap.CullFace);
            }
            else if (OptionBackFaceCulling)
            {
                if ((Face.Flags & MeshFace.Face2Mask) == 0)
                {
                    GL.Enable(EnableCap.CullFace);
                }
            }

            // matrix
            Matrix4D modelMatrix     = State.ModelMatrix * Camera.TranslationMatrix;
            Matrix4D modelViewMatrix = modelMatrix * CurrentViewMatrix;

            Shader.SetCurrentModelViewMatrix(modelViewMatrix);
            Shader.SetCurrentTextureMatrix(State.TextureTranslation);

            if (OptionWireFrame || IsDebugTouchMode)
            {
                VAO.Draw(PrimitiveType.LineLoop, Face.IboStartIndex, Face.Vertices.Length);
                return;
            }

            // lighting
            if (material.NighttimeTexture == null || material.NighttimeTexture == material.DaytimeTexture)
            {
                if (OptionLighting)
                {
                    if (material.Color != lastColor)
                    {
                        Shader.SetMaterialAmbient(material.Color);                          // TODO
                        Shader.SetMaterialDiffuse(material.Color);
                        Shader.SetMaterialSpecular(material.Color);                         // TODO
                    }

                    if ((material.Flags & MeshMaterial.EmissiveColorMask) != 0)
                    {
                        Shader.SetMaterialEmission(material.EmissiveColor);
                        Shader.SetMaterialEmissive(true);
                    }
                    else
                    {
                        Shader.SetMaterialEmissive(false);
                    }

                    Shader.SetMaterialShininess(1.0f);
                }
                else
                {
                    if (material.Color != lastColor)
                    {
                        Shader.SetMaterialAmbient(material.Color);                          // TODO
                    }
                }
            }
            else
            {
                if (material.Color != lastColor)
                {
                    Shader.SetMaterialAmbient(material.Color);                      // TODO
                }
            }

            lastColor = material.Color;
            PrimitiveType DrawMode;

            switch (Face.Flags & MeshFace.FaceTypeMask)
            {
            case MeshFace.FaceTypeTriangles:
                DrawMode = PrimitiveType.Triangles;
                break;

            case MeshFace.FaceTypeTriangleStrip:
                DrawMode = PrimitiveType.TriangleStrip;
                break;

            case MeshFace.FaceTypeQuads:
                DrawMode = PrimitiveType.Quads;
                break;

            case MeshFace.FaceTypeQuadStrip:
                DrawMode = PrimitiveType.QuadStrip;
                break;

            default:
                DrawMode = PrimitiveType.Polygon;
                break;
            }

            // daytime polygon
            {
                // texture
                if (material.DaytimeTexture != null && currentHost.LoadTexture(material.DaytimeTexture, (OpenGlTextureWrapMode)material.WrapMode))
                {
                    Shader.SetIsTexture(true);
                    if (LastBoundTexture != material.DaytimeTexture.OpenGlTextures[(int)material.WrapMode])
                    {
                        GL.BindTexture(TextureTarget.Texture2D,
                                       material.DaytimeTexture.OpenGlTextures[(int)material.WrapMode].Name);
                        LastBoundTexture = material.DaytimeTexture.OpenGlTextures[(int)material.WrapMode];
                    }
                }
                else
                {
                    Shader.SetIsTexture(false);
                }

                // blend mode
                float factor;
                if (material.BlendMode == MeshMaterialBlendMode.Additive)
                {
                    factor = 1.0f;
                    GL.Enable(EnableCap.Blend);
                    GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One);
                    Shader.SetIsFog(false);
                }
                else if (material.NighttimeTexture == null)
                {
                    float blend = inv255 * material.DaytimeNighttimeBlend + 1.0f - Lighting.OptionLightingResultingAmount;

                    if (blend > 1.0f)
                    {
                        blend = 1.0f;
                    }

                    factor = 1.0f - 0.7f * blend;
                }
                else
                {
                    factor = 1.0f;
                }
                Shader.SetBrightness(factor);

                float alphaFactor;
                GlowAttenuationMode mode = GlowAttenuationMode.None;
                if (material.GlowAttenuationData != 0)
                {
                    alphaFactor = (float)Glow.GetDistanceFactor(modelMatrix, State.Prototype.Mesh.Vertices, ref Face, material.GlowAttenuationData, out mode);
                }
                else
                {
                    alphaFactor = 1.0f;
                }

                if (material.BlendMode == MeshMaterialBlendMode.Additive)
                {
                    Shader.SetMaterialAdditive(1 + (int)mode);
                }
                else
                {
                    Shader.SetMaterialAdditive(0);
                }

                Shader.SetOpacity(inv255 * material.Color.A * alphaFactor);

                // render polygon
                VAO.Draw(DrawMode, Face.IboStartIndex, Face.Vertices.Length);
            }

            // nighttime polygon
            if (material.NighttimeTexture != null && material.NighttimeTexture != material.DaytimeTexture && currentHost.LoadTexture(material.NighttimeTexture, (OpenGlTextureWrapMode)material.WrapMode))
            {
                // texture
                Shader.SetIsTexture(true);
                if (LastBoundTexture != material.NighttimeTexture.OpenGlTextures[(int)material.WrapMode])
                {
                    GL.BindTexture(TextureTarget.Texture2D, material.NighttimeTexture.OpenGlTextures[(int)material.WrapMode].Name);
                    LastBoundTexture = material.NighttimeTexture.OpenGlTextures[(int)material.WrapMode];
                }


                GL.Enable(EnableCap.Blend);

                // alpha test
                GL.Enable(EnableCap.AlphaTest);
                GL.AlphaFunc(AlphaFunction.Greater, 0.0f);

                // blend mode
                float alphaFactor;
                if (material.GlowAttenuationData != 0)
                {
                    alphaFactor = (float)Glow.GetDistanceFactor(modelMatrix, State.Prototype.Mesh.Vertices, ref Face, material.GlowAttenuationData);
                    float blend = inv255 * material.DaytimeNighttimeBlend + 1.0f - Lighting.OptionLightingResultingAmount;
                    if (blend > 1.0f)
                    {
                        blend = 1.0f;
                    }

                    alphaFactor *= blend;
                }
                else
                {
                    alphaFactor = inv255 * material.DaytimeNighttimeBlend + 1.0f - Lighting.OptionLightingResultingAmount;
                    if (alphaFactor > 1.0f)
                    {
                        alphaFactor = 1.0f;
                    }
                }

                Shader.SetOpacity(alphaFactor);

                // render polygon
                VAO.Draw(DrawMode, Face.IboStartIndex, Face.Vertices.Length);
                RestoreBlendFunc();
                RestoreAlphaFunc();
            }


            // normals
            if (OptionNormals)
            {
                Shader.SetIsTexture(false);
                Shader.SetBrightness(1.0f);
                Shader.SetOpacity(1.0f);
                VertexArrayObject NormalsVAO = (VertexArrayObject)State.Prototype.Mesh.NormalsVAO;
                NormalsVAO.BindForDrawing(Shader.VertexLayout);
                lastVAO = NormalsVAO.handle;
                NormalsVAO.Draw(PrimitiveType.Lines, Face.NormalsIboStartIndex, Face.Vertices.Length * 2);
            }

            // finalize
            if (material.BlendMode == MeshMaterialBlendMode.Additive)
            {
                RestoreBlendFunc();
                Shader.SetIsFog(OptionFog);
            }
        }