protected void DrawScreenSpaceTriangleInterpolated(PreparedTriangle triangle)
        {
            WriteableBitmap rt = _renderWindow.Framebuffer;

            if (triangle.GetScreenSpaceDirection() != VisibleTriangleDirection)
            {
                return;
            }

            var triangleArea = CalculateTriangleArea(
                triangle.Vertices[0].X, triangle.Vertices[0].Y,
                triangle.Vertices[1].X, triangle.Vertices[1].Y,
                triangle.Vertices[2].X, triangle.Vertices[2].Y);

            var vertexX = new double[] { triangle.Vertices[0].X, triangle.Vertices[1].X, triangle.Vertices[2].X };
            var vertexY = new double[] { triangle.Vertices[0].Y, triangle.Vertices[1].Y, triangle.Vertices[2].Y };

            var scanEdges = new List <ScanEdge>();

            for (int current = 0; current < 3; ++current)
            {
                int next = (current + 1) % 3;

                double higherx, highery;
                double lowerx, lowery;

                if (vertexY[current] < vertexY[next])
                {
                    higherx = vertexX[current];
                    highery = vertexY[current];
                    lowerx  = vertexX[next];
                    lowery  = vertexY[next];
                }
                else
                {
                    higherx = vertexX[next];
                    highery = vertexY[next];
                    lowerx  = vertexX[current];
                    lowery  = vertexY[current];
                }

                int minY = (int)highery;
                int maxY = (int)lowery;
                //int startX = (int)higherx;
                //int endX = (int)lowerx;

                if (minY == maxY)
                {
                    continue;
                }

                var scanEdge = new ScanEdge();
                scanEdge.MinimalY = (int)Math.Ceiling(highery);
                scanEdge.MaximalY = (int)Math.Ceiling(lowery);
                scanEdge.X        = higherx;
                scanEdge.XSlope   = (lowerx - higherx) / ((double)(lowery - highery));

                scanEdges.Add(scanEdge);
            }

            if (scanEdges.Count == 0)
            {
                return;
            }

            scanEdges = scanEdges.OrderBy(t => t.MinimalY).ToList();
            int scanEdgesActivated = 0;

            var startY = Math.Max(scanEdges.Min(t => t.MinimalY), 0);
            var endY   = Math.Min(scanEdges.Max(t => t.MaximalY), rt.PixelHeight - 1);

            var activeEdges = new List <ScanEdge>();

            for (var y = startY; y <= endY; ++y)
            {
                activeEdges = activeEdges.Where(t => y < t.MaximalY).ToList();

                while (scanEdgesActivated < scanEdges.Count)
                {
                    if (y < scanEdges[scanEdgesActivated].MinimalY)
                    {
                        break;
                    }

                    activeEdges.Add(scanEdges[scanEdgesActivated]);
                    ++scanEdgesActivated;
                }

                activeEdges = activeEdges.OrderBy(t => t.X).ToList();

                for (int i = 0; i < activeEdges.Count - 1; i += 2)
                {
                    int startX = (int)Math.Ceiling(Math.Max(activeEdges[i].X, 0));
                    int endX   = (int)Math.Floor(Math.Min(activeEdges[i + 1].X, rt.PixelWidth - 1));

                    for (int x = startX; x <= endX; ++x)
                    {
                        var aArea = CalculateTriangleArea(
                            x, y,
                            triangle.Vertices[1].X, triangle.Vertices[1].Y,
                            triangle.Vertices[2].X, triangle.Vertices[2].Y);

                        var bArea = CalculateTriangleArea(
                            triangle.Vertices[0].X, triangle.Vertices[0].Y,
                            x, y,
                            triangle.Vertices[2].X, triangle.Vertices[2].Y);

                        var cArea = CalculateTriangleArea(
                            triangle.Vertices[0].X, triangle.Vertices[0].Y,
                            triangle.Vertices[1].X, triangle.Vertices[1].Y,
                            x, y);

                        var fa = Math.Min(aArea / triangleArea, 1.0);
                        var fb = Math.Min(bArea / triangleArea, 1.0);
                        var fc = Math.Min(cArea / triangleArea, 1.0);

                        if (fa + fb + fc > 1.0)
                        {
                            int    lowestAreaIndex = 0;
                            double lowestArea      = fa;

                            if (lowestArea > fb)
                            {
                                lowestAreaIndex = 1;
                                lowestArea      = fb;
                            }

                            if (lowestArea > fc)
                            {
                                lowestAreaIndex = 2;
                                lowestArea      = fc;
                            }

                            switch (lowestAreaIndex)
                            {
                            case 0:
                                fa = 1.0 - fb - fc;
                                break;

                            case 1:
                                fb = 1.0 - fa - fc;
                                break;

                            case 2:
                                fc = 1.0 - fa - fb;
                                break;
                            }
                        }

                        var z = fa * triangle.Vertices[0].Z
                                + fb * triangle.Vertices[1].Z
                                + fc * triangle.Vertices[2].Z;

                        if (z <= 1.0)
                        {
                            continue;
                        }

                        if (_zBuffer[x, y] > z)
                        {
                            continue;
                        }

                        _zBuffer[x, y] = z;

                        // Pixel Shader
                        Color outputColor = InterpolateColor(
                            fa, triangle.Vertices[0].VertexColor,
                            fb, triangle.Vertices[1].VertexColor,
                            fc, triangle.Vertices[2].VertexColor);

                        if (TexturingEnabled && Material.DiffuseTexture != null)
                        {
                            var u = fa * triangle.Vertices[0].U + fb * triangle.Vertices[1].U + fc * triangle.Vertices[2].U;
                            var v = fa * triangle.Vertices[0].V + fb * triangle.Vertices[1].V + fc * triangle.Vertices[2].V;

                            u = Math.Min(Math.Max(u, 0.0), 1.0);
                            v = Math.Min(Math.Max(v, 0.0), 1.0);

                            Color texColor = Material.DiffuseTexture.GetPixel((int)(u * Material.DiffuseTexture.PixelWidth),
                                                                              (int)(v * Material.DiffuseTexture.PixelHeight));
                            outputColor = MultiplyColors(outputColor, texColor);
                        }
                        // End of pixel shader

                        rt.SetPixel(x, y, outputColor);
                    }
                }

                activeEdges.ForEach(t => t.X += t.XSlope);
            }
        }
        public void RenderObjMesh(ObjData meshToRender, Matrix <double> transformation, Matrix <double> normalTransformation)
        {
            if (Material == null)
            {
                Material = new Material();
            }

            if (TexturingEnabled)
            {
                if (Material.DiffuseTexture != null)
                {
                    Material.DiffuseTexture.Lock();
                }
            }

            var hw = _renderWindow.Framebuffer.PixelWidth * 0.5;
            var hh = _renderWindow.Framebuffer.PixelHeight * 0.5;

            foreach (var triangle in meshToRender.Triangles)
            {
                var tri = new PreparedTriangle();

                for (int i = 0; i < 3; ++i)
                {
                    Vector <double> modelSpacePosition = meshToRender.Vertices[triangle.Vertices[i]];
                    Vector <double> modelSpaceNormal   = meshToRender.Normals[triangle.Normals[i]];

                    Vector <double> screenSpacePosition = (transformation * modelSpacePosition.ExtendVector())
                                                          .ToCartesian()
                                                          .Add(VectorHelpers.Create(1.0, 1.0, 0.0))
                                                          .PointwiseMultiply(VectorHelpers.Create(hw, hh, 1.0));

                    Vector <double> worldSpacePosition = (normalTransformation * modelSpacePosition.ExtendVector())
                                                         .ToCartesian();
                    Vector <double> worldSpaceNormal = (normalTransformation * modelSpaceNormal.ExtendVector(0.0))
                                                       .DiscardLastCoordinate().Normalize(2);

                    // Vertex shader
                    tri.Vertices[i].SetCoordinates(screenSpacePosition);
                    tri.Vertices[i].SetTextureCoordinates(meshToRender.TexCoords[triangle.TexCoords[i]]);

                    var vR = 0;
                    var vG = 0;
                    var vB = 0;

                    if (AmbientLightingEnabled)
                    {
                        vR += Material.AmbientColor.R;
                        vG += Material.AmbientColor.G;
                        vB += Material.AmbientColor.B;
                    }

                    if (DiffuseLightingEnabled || SpecularLightingEnabled)
                    {
                        foreach (var light in Lights)
                        {
                            var lightDirection = (light.Position - worldSpacePosition).Normalize(2);

                            if (DiffuseLightingEnabled)
                            {
                                var diffuseLightFactor         = Math.Max(lightDirection.DotProduct(worldSpaceNormal), 0);
                                var fullSaturationDiffuseColor = MultiplyColors(light.Color, Material.DiffuseColor);

                                vR += (byte)(fullSaturationDiffuseColor.R * diffuseLightFactor);
                                vG += (byte)(fullSaturationDiffuseColor.G * diffuseLightFactor);
                                vB += (byte)(fullSaturationDiffuseColor.B * diffuseLightFactor);
                            }

                            if (SpecularLightingEnabled)
                            {
                                var eyeDirection                = (WorldPositionEye - worldSpacePosition).Normalize(2);
                                var reflected                   = (2 * worldSpaceNormal.DotProduct(lightDirection) * worldSpaceNormal - lightDirection).Normalize(2);
                                var specularLightFactor         = Math.Pow(Math.Max(eyeDirection.DotProduct(reflected), 0), Material.ShineFactor);
                                var fullSaturationSpecularColor = MultiplyColors(light.Color, Material.SpecularColor);

                                vR += (byte)(fullSaturationSpecularColor.R * specularLightFactor);
                                vG += (byte)(fullSaturationSpecularColor.G * specularLightFactor);
                                vB += (byte)(fullSaturationSpecularColor.B * specularLightFactor);
                            }
                        }
                    }

                    tri.Vertices[i].VertexColor = Color.FromRgb(
                        (byte)Math.Min(vR, 255),
                        (byte)Math.Min(vG, 255),
                        (byte)Math.Min(vB, 255));
                    // End of vertex shader
                }

                DrawScreenSpaceTriangleInterpolated(tri);
            }

            if (TexturingEnabled && Material.DiffuseTexture != null)
            {
                Material.DiffuseTexture.Unlock();
            }
        }