matrix4x4 matrixQuickInverse(matrix4x4 m)
        {
            matrix4x4 matrix = new matrix4x4();

            matrix.m[0, 0] = m.m[0, 0];
            matrix.m[0, 1] = m.m[1, 0];
            matrix.m[0, 2] = m.m[2, 0];
            matrix.m[0, 3] = 0.0f;

            matrix.m[1, 0] = m.m[0, 1];
            matrix.m[1, 1] = m.m[1, 1];
            matrix.m[1, 2] = m.m[2, 1];
            matrix.m[1, 3] = 0.0f;

            matrix.m[2, 0] = m.m[0, 2];
            matrix.m[2, 1] = m.m[1, 2];
            matrix.m[2, 2] = m.m[2, 2];
            matrix.m[2, 3] = 0.0f;

            matrix.m[3, 0] = -(m.m[3, 0] * matrix.m[0, 0] + m.m[3, 1] * matrix.m[1, 0] + m.m[3, 2] * matrix.m[2, 0]);
            matrix.m[3, 1] = -(m.m[3, 0] * matrix.m[0, 1] + m.m[3, 1] * matrix.m[1, 1] + m.m[3, 2] * matrix.m[2, 1]);
            matrix.m[3, 2] = -(m.m[3, 0] * matrix.m[0, 2] + m.m[3, 1] * matrix.m[1, 2] + m.m[3, 2] * matrix.m[2, 2]);
            matrix.m[3, 3] = 1.0f;
            return(matrix);
        }
        matrix4x4 createUnitMatrix()
        {
            matrix4x4 m = new matrix4x4();

            m.m[0, 0] = 1.0;
            m.m[1, 1] = 1.0;
            m.m[2, 2] = 1.0;
            m.m[3, 3] = 1.0;
            return(m);
        }
        //FUNC
        matrix4x4 getMatrixProjection(double fFovRad, double fAspectRation, double fNear, double fFar)
        {
            matrix4x4 matProj = new matrix4x4();

            matProj.m[0, 0] = fAspectRation * fFovRad;
            matProj.m[1, 1] = fFovRad;
            matProj.m[2, 2] = fFar / (fFar - fNear);
            matProj.m[3, 2] = (-fFar * fNear) / (fFar - fNear);
            matProj.m[2, 3] = 1.0;
            matProj.m[3, 3] = 0.0;
            return(matProj);
        }
        matrix4x4 getMatrixRotationY(double angle)
        {
            matrix4x4 matrix = new matrix4x4();

            matrix.m[0, 0] = Math.Cos(angle);
            matrix.m[0, 2] = Math.Sin(angle);
            matrix.m[2, 0] = -Math.Sin(angle);
            matrix.m[1, 1] = 1.0f;
            matrix.m[2, 2] = Math.Cos(angle);
            matrix.m[3, 3] = 1.0f;
            return(matrix);
        }
        matrix4x4 getMatrixRotationX(double angle)
        {
            matrix4x4 matRotX = new matrix4x4();

            matRotX.m[0, 0] = 1;
            matRotX.m[1, 1] = Math.Cos(angle);
            matRotX.m[1, 2] = Math.Sin(angle);
            matRotX.m[2, 1] = -Math.Sin(angle);
            matRotX.m[2, 2] = Math.Cos(angle);;
            matRotX.m[3, 3] = 1;
            return(matRotX);
        }
        //FUNC
        matrix4x4 getMatrixRotationZ(double angle)
        {
            matrix4x4 matRotZ = new matrix4x4();

            matRotZ.m[0, 0] = Math.Cos(angle);
            matRotZ.m[0, 1] = Math.Sin(angle);
            matRotZ.m[1, 0] = -Math.Sin(angle);
            matRotZ.m[1, 1] = Math.Cos(angle);
            matRotZ.m[2, 2] = 1;
            matRotZ.m[3, 3] = 1;
            return(matRotZ);
        }
            public static matrix4x4 operator *(matrix4x4 m1, matrix4x4 m2)
            {
                matrix4x4 res = new matrix4x4();

                for (int c = 0; c < 4; c++)
                {
                    for (int r = 0; r < 4; r++)
                    {
                        res.m[r, c] = m1.m[r, 0] * m2.m[0, c] + m1.m[r, 1] * m2.m[1, c] + m1.m[r, 2] * m2.m[2, c] + m1.m[r, 3] * m2.m[3, c];
                    }
                }
                return(res);
            }
        //FUNC
        matrix4x4 getMatrixTranslation(double x, double y, double z)
        {
            matrix4x4 matrixTranslation = new matrix4x4();

            matrixTranslation.m[0, 0] = 1.0;
            matrixTranslation.m[1, 1] = 1.0;
            matrixTranslation.m[2, 2] = 1.0;
            matrixTranslation.m[3, 3] = 1.0;
            matrixTranslation.m[3, 0] = x;
            matrixTranslation.m[3, 1] = y;
            matrixTranslation.m[3, 2] = z;
            return(matrixTranslation);
        }
        //FUNC
        matrix4x4 matrixPointAt(vect3D pos, vect3D target, vect3D up)
        {
            vect3D newForward = target - pos;

            newForward.Normalize();
            // Calculate new Up direction
            vect3D a     = newForward * up.DotProduct(newForward);
            vect3D newUp = up - a;

            newUp.Normalize();
            // New Right direction is easy, its just cross product
            vect3D newRight = newUp.CrossProduct(newForward);
            // Construct Dimensioning and Translation Matrix
            matrix4x4 matrix = new matrix4x4();

            matrix.m[0, 0] = newRight.x; matrix.m[0, 1] = newRight.y; matrix.m[0, 2] = newRight.z; matrix.m[0, 3] = 0.0f;
            matrix.m[1, 0] = newUp.x; matrix.m[1, 1] = newUp.y; matrix.m[1, 2] = newUp.z; matrix.m[1, 3] = 0.0f;
            matrix.m[2, 0] = newForward.x; matrix.m[2, 1] = newForward.y; matrix.m[2, 2] = newForward.z; matrix.m[2, 3] = 0.0f;
            matrix.m[3, 0] = pos.x; matrix.m[3, 1] = pos.y; matrix.m[3, 2] = pos.z; matrix.m[3, 3] = 1.0f;
            return(matrix);
        }
        private void timerTick(object sender, EventArgs e)
        {
            canvas.Children.Clear();

            double deltaX = 0.2;
            double fDelta = 2 * Math.PI / (4 * r / deltaX);

            fTheta += fDelta;
            matrix4x4 matRotZ = getMatrixRotationZ(0);
            matrix4x4 matRotX = getMatrixRotationX(0);
            matrix4x4 matRotY = getMatrixRotationY(fTheta);

            if (Math.Abs(x) >= r)
            {
                isHalfRotated = !isHalfRotated;
            }
            x += isHalfRotated ? deltaX : -deltaX;


            double z = isHalfRotated ? -Math.Sqrt(r * r - x * x) : Math.Sqrt(r * r - x * x);

            z += 4 * r;


            matrix4x4 matTrans = getMatrixTranslation(x, 1, z);;


            // all the transformations that we need to do in this matrix
            matrix4x4 matWorld = createUnitMatrix();

            matWorld = matRotZ * matRotX * matRotY;
            matWorld = matWorld * matTrans;

            //camera
            vect3D    vUp          = new vect3D(0, 1, 0);
            vect3D    vTarget      = new vect3D(0, 0, 1);
            matrix4x4 matCameraRot = getMatrixRotationY(fYaw) * getMatrixRotationX(fXaw);

            vLookDir = matCameraRot * vTarget;
            vTarget  = vCamera + vLookDir;
            matrix4x4 matCamera = matrixPointAt(vCamera, vTarget, vUp);
            matrix4x4 matView   = matrixQuickInverse(matCamera);

            List <triangle> trianglesToRaster = new List <triangle>();

            foreach (triangle tri in meshCube.tris)
            {
                triangle triProjected   = new triangle();
                triangle triTransformed = new triangle();
                triangle triViewed      = new triangle();
                triTransformed.vect[0] = matWorld * tri.vect[0];
                triTransformed.vect[1] = matWorld * tri.vect[1];
                triTransformed.vect[2] = matWorld * tri.vect[2];

                vect3D normal = new vect3D();
                vect3D line1  = new vect3D();
                vect3D line2  = new vect3D();

                line1 = triTransformed.vect[1] - triTransformed.vect[0];
                line2 = triTransformed.vect[2] - triTransformed.vect[0];

                normal = line1.CrossProduct(line2);
                normal.Normalize();

                //projecting

                // объект виден если угол между нормалью к нему и нашей камерой меньше 90 градусов
                // будем проверять это с помощью скалярного произведения векторов
                // скалярное произведение векторов угол между которыми > 90 градусов будет < 0
                if (normal.DotProduct(triTransformed.vect[0] - vCamera) < 0.0)
                {
                    // illumination (all illumination cooming from direction, not from the point)
                    // свет тем ярче чем меньше угол между нормалью поверхности с направлением света
                    vect3D lightDirection = new vect3D(-5.0, -5.0, -5.0);
                    lightDirection.Normalize();

                    double dp = normal.DotProduct(lightDirection);
                    triProjected.luminosity   = dp;
                    triTransformed.luminosity = dp;

                    triViewed.vect[0] = matView * triTransformed.vect[0];
                    triViewed.vect[1] = matView * triTransformed.vect[1];
                    triViewed.vect[2] = matView * triTransformed.vect[2];

                    triProjected.vect[0] = matProj * triViewed.vect[0];
                    triProjected.vect[1] = matProj * triViewed.vect[1];
                    triProjected.vect[2] = matProj * triViewed.vect[2];

                    triProjected.vect[0] = triProjected.vect[0] / triProjected.vect[0].w;
                    triProjected.vect[1] = triProjected.vect[1] / triProjected.vect[1].w;
                    triProjected.vect[2] = triProjected.vect[2] / triProjected.vect[2].w;

                    //offset into visible mormalized space
                    vect3D vOffsetView = new vect3D(1, 1, 0);

                    triProjected.vect[0] = triProjected.vect[0] + vOffsetView;
                    triProjected.vect[1] = triProjected.vect[1] + vOffsetView;
                    triProjected.vect[2] = triProjected.vect[2] + vOffsetView;

                    triProjected.vect[0].x *= canvas.Width / 2.0;
                    triProjected.vect[0].y *= canvas.Height / 2.0;
                    triProjected.vect[1].x *= canvas.Width / 2.0;
                    triProjected.vect[1].y *= canvas.Height / 2.0;
                    triProjected.vect[2].x *= canvas.Width / 2.0;
                    triProjected.vect[2].y *= canvas.Height / 2.0;

                    trianglesToRaster.Add(triProjected);
                }
            }
            // Sort triangles from back to front
            try {
                trianglesToRaster.Sort();
            }
            catch (Exception ex)
            {
            }
            foreach (triangle t in trianglesToRaster)
            {
                DrawTriangle(t, true, true);
            }
        }