//private void DrawTriangle ( VertexPT [] triVs ) {
        //    VertexPT [][] vss = ClipTriangleByZ (
        //        triVs,
        //        nearZ, 1 );

        //    foreach ( VertexPT [] clippedVs in vss ) {
        //        if ( culling != Culling.None ) {
        //            //double3 n = GetPerspectiveTriangleNormal ( new double3 [] { clippedVs [0].P, clippedVs [1].P, clippedVs [2].P },
        //            //    culling == Culling.Ccw ? CycleDir.Ccw : CycleDir.Cw );

        //            //if ( ( n & double3.UnitZ ) >= 0 )
        //            //    return;

        //            CycleDir triOrder = GetPerspectiveTriangleOrder ( new double3 [] { clippedVs [0].P, clippedVs [1].P, clippedVs [2].P } );

        //            if ( ( triOrder == CycleDir.Ccw && culling == Culling.Ccw ) ||
        //                 ( triOrder == CycleDir.Cw  && culling == Culling.Cw ) )
        //                return;
        //        }

        //        DrawClippedAndCulledTriangle ( clippedVs [0], clippedVs [1], clippedVs [2] );
        //    }
        //}

        void DrawClippedAndCulledTriangle(VertexPT v0, VertexPT v1, VertexPT v2)
        {
            double tpp = GetTexelsPerPixel(v0.P, v1.P, v2.P, v0.T, v1.T, v2.T,
                                           image.Size.width, image.Size.height,
                                           sampler.Map.Width, sampler.Map.Height);

            // Transform to screen space
            double2 halfWh = new double2(image.Size.width / 2, image.Size.height / 2);

            v0.P.xy /= v0.P.z;
            v1.P.xy /= v1.P.z;
            v2.P.xy /= v2.P.z;

            double3 screenP0 = new double3(halfWh + (v0.P.xy ^ halfWh), v0.P.z);
            double3 screenP1 = new double3(halfWh + (v1.P.xy ^ halfWh), v1.P.z);
            double3 screenP2 = new double3(halfWh + (v2.P.xy ^ halfWh), v2.P.z);

            // Invert vertically
            screenP0.y = image.Size.height - screenP0.y;
            screenP1.y = image.Size.height - screenP1.y;
            screenP2.y = image.Size.height - screenP2.y;

            image.DrawTexturedTriangle(new [] { screenP0, screenP1, screenP2 },
                                       new [] { v0.T, v1.T, v2.T }, sampler, tpp);
            //image.DrawTriangle ( new [] { screenP0, screenP1, screenP2 }, color );
        }
        VertexPT [] TransformVertices(VertexPT [] vs, double4x4 m)
        {
            VertexPT [] tVs = new VertexPT [vs.Length];

            for (int i = 0; i < vs.Length; i++)
            {
                tVs [i] = new VertexPT(m.Transform(vs [i].P), vs [i].T);
            }

            return(tVs);
        }
        VertexPT [][] ClipTriangleByZ(VertexPT [] vs, double z, int insideSign)
        {
            // Warning: this algorithm generally does not preserve cw/ccw order
            Debug.Assert(insideSign == 1 || insideSign == -1);
            List <VertexPT> vsInside  = new List <VertexPT> (vs);
            List <VertexPT> vsOutside = new List <VertexPT> ();

            for (int i = 0; i < vsInside.Count; i++)
            {
                if (vsInside [i].P.z.CompareTo(z) != insideSign)
                {
                    vsOutside.Add(vsInside [i]);
                    vsInside.RemoveAt(i--);
                }
            }

            if (vsOutside.Count == 0)
            {
                return new VertexPT [][] { ( VertexPT [] )vs.Clone() }
            }
            ;
            else if (vsOutside.Count == 3)
            {
                return new VertexPT [][] {}
            }
            ;
            else if (vsOutside.Count == 2)
            {
                VertexPT [] newVs = new VertexPT [3];

                newVs [0] = vsInside [0];

                double k1 = (z - vsOutside [0].P.z) / (vsInside [0].P.z - vsOutside [0].P.z);
                double k2 = (z - vsOutside [1].P.z) / (vsInside [0].P.z - vsOutside [1].P.z);

                newVs [1] = new VertexPT(
                    k1.Lerp(vsOutside [0].P, vsInside [0].P),
                    k1.Lerp(vsOutside [0].T, vsInside [0].T)
                    );

                newVs [2] = new VertexPT(
                    k2.Lerp(vsOutside [1].P, vsInside [0].P),
                    k2.Lerp(vsOutside [1].T, vsInside [0].T)
                    );

                return(new VertexPT [][] { newVs });
            }
            else if (vsOutside.Count == 1)
            {
                double k1 = (z - vsOutside [0].P.z) / (vsInside [0].P.z - vsOutside [0].P.z);
                double k2 = (z - vsOutside [0].P.z) / (vsInside [1].P.z - vsOutside [0].P.z);

                VertexPT v1 = new VertexPT(
                    k1.Lerp(vsOutside [0].P, vsInside [0].P),
                    k1.Lerp(vsOutside [0].T, vsInside [0].T)
                    );

                VertexPT v2 = new VertexPT(
                    k2.Lerp(vsOutside [0].P, vsInside [1].P),
                    k2.Lerp(vsOutside [0].T, vsInside [1].T)
                    );

                return(new VertexPT [][] {
                    new VertexPT [] { vsInside [0], v1, v2 },
                    new VertexPT [] { vsInside [0], vsInside [1], v2 }
                });
            }

            throw new ArgumentException("Invalid vertex data.");
        }