private void Render()
        {
            Array.Clear(camera.depthBuffer, 0, camera.depthBuffer.Length);

            //Console.WriteLine(transform.Rotation.RotateVector3(obj.transform.Location - camPos));
            fShader = (fShader ?? FlatShader);
            vShader = (vShader ?? new VertexShaderDelegate((x) => { }));
            for (int face = 0; face < mesh.faces.GetLength(0); face++)
            {
                float dot;
                dot = Vector3.Dot(mesh.faceNormals[face], -camera.transform.Forward);
                if (dot < 0) //Back face culling
                {
                    continue;
                }

                //Region vertex shader
                camera.vertex.location  = mesh.vertices[mesh.faces[face, 0]] + object3D.transform.Location;
                camera.vertex.objectPos = object3D.transform.Location;
                camera.vertex.camera    = camera;

                vShader(camera.vertex);
                camera.vert1 = camera.transform.Rotation.Conjugate.RotateVector3(camera.vertex.location - camera.transform.Location);

                camera.vertex.location  = mesh.vertices[mesh.faces[face, 1]] + object3D.transform.Location;
                camera.vertex.objectPos = object3D.transform.Location;
                camera.vertex.camera    = camera;

                vShader(camera.vertex);
                camera.vert2 = camera.transform.Rotation.Conjugate.RotateVector3(camera.vertex.location - camera.transform.Location);

                camera.vertex.location  = mesh.vertices[mesh.faces[face, 2]] + object3D.transform.Location;
                camera.vertex.objectPos = object3D.transform.Location;
                camera.vertex.camera    = camera;

                vShader(camera.vertex);
                camera.vert3 = camera.transform.Rotation.Conjugate.RotateVector3(camera.vertex.location - camera.transform.Location);
                //End region vertex shader


                camera.e1.x = (camera.projectionDistance * camera.vert1.x) / camera.vert1.y;           //Using similar triangles to project the verts onto the camera plane
                camera.e1.z = (camera.projectionDistance * camera.vert1.z) / camera.vert1.y;
                camera.e2.x = (camera.projectionDistance * camera.vert2.x) / camera.vert2.y;
                camera.e2.z = (camera.projectionDistance * camera.vert2.z) / camera.vert2.y;
                camera.e3.x = (camera.projectionDistance * camera.vert3.x) / camera.vert3.y;
                camera.e3.z = (camera.projectionDistance * camera.vert3.z) / camera.vert3.y;

                camera.e1.y = camera.vert1.y;
                camera.e2.y = camera.vert2.y;                 //Don't change e.y because we will use it for z-buffering later
                camera.e3.y = camera.vert3.y;

                camera.e1.x *= camera.screenNormCoeffX;
                camera.e1.z *= camera.screenNormCoeffZ;   //Bound x and z to the range [-1,1]
                camera.e2.x *= camera.screenNormCoeffX;
                camera.e2.z *= camera.screenNormCoeffZ;
                camera.e3.x *= camera.screenNormCoeffX;
                camera.e3.z *= camera.screenNormCoeffZ;


                //camera.projectedTriangle.v0 = new Vector2(camera.e1.x, camera.e1.z);
                //camera.projectedTriangle.v1 = new Vector2(camera.e2.x, camera.e2.z);
                //camera.projectedTriangle.v2 = new Vector2(camera.e3.x, camera.e3.z);
                camera.projectedTriangle.z0  = camera.e1.y;
                camera.projectedTriangle.z1  = camera.e2.y;
                camera.projectedTriangle.z2  = camera.e3.y;
                camera.projectedTriangle.uv0 = mesh.uvcoords[mesh.uvs[face, 0]];
                camera.projectedTriangle.uv1 = mesh.uvcoords[mesh.uvs[face, 1]];
                camera.projectedTriangle.uv2 = mesh.uvcoords[mesh.uvs[face, 2]];
                camera.projectedTriangle.vn0 = mesh.vertexNormalCoords[mesh.vertexNormals[face, 0]];
                camera.projectedTriangle.vn1 = mesh.vertexNormalCoords[mesh.vertexNormals[face, 1]];
                camera.projectedTriangle.vn2 = mesh.vertexNormalCoords[mesh.vertexNormals[face, 2]];


                //New stuff: still need a solution to near/far clip
                camera.projectedTriangle.v0 = new Vector2(camera.renderWidth / 2f + (camera.e1.x * camera.normToScreen.x), camera.renderHeight / 2f + (camera.e1.z * camera.normToScreen.z));
                camera.projectedTriangle.v1 = new Vector2(camera.renderWidth / 2f + (camera.e2.x * camera.normToScreen.x), camera.renderHeight / 2f + (camera.e2.z * camera.normToScreen.z));
                camera.projectedTriangle.v2 = new Vector2(camera.renderWidth / 2f + (camera.e3.x * camera.normToScreen.x), camera.renderHeight / 2f + (camera.e3.z * camera.normToScreen.z));

                DrawClipTriangle(camera.projectedTriangle, b, camera.depthBuffer, mesh.faceNormals[face], fShader);
            }
        }
        private void DrawClipTriangle(Triangle tri, DirectBitmap b, float[] depthBuffer, Vector3 normal, FragmentShaderDelegate frag)
        {
            Fragment fragment = new Fragment();
            float    zBuf;
            int      padding = 5;
            int      minX = int.MaxValue, maxX = int.MinValue, minY = int.MaxValue, maxY = int.MinValue;

            foreach (Vector2 v in tri.Points) //Get bounding box
            {
                if (v.x > maxX)
                {
                    maxX = (int)(v.x + 1);//Round up
                }
                if (v.x < minX)
                {
                    minX = (int)(v.x);
                }
                if (v.y > maxY)
                {
                    maxY = (int)(v.y + 1);
                }
                if (v.y < minY)
                {
                    minY = (int)(v.y);
                }
            }
            if (maxX > camera.renderWidth)
            {
                maxX = camera.renderWidth;
            }
            if (minX < 0)
            {
                minX = 0;
            }
            if (maxY > camera.renderHeight)
            {
                maxY = camera.renderHeight;
            }
            if (minY < 0)
            {
                minY = 0;
            }
            Vector2 pt = new Vector2();

            maxX = (maxX + padding > camera.renderWidth - 1 ? camera.renderWidth - 1 : maxX + padding);
            maxY = (maxY + padding > camera.renderHeight - 1 ? camera.renderHeight - 1 : maxY + padding);
            minX = (minX - padding < 0 ? 0 : minX - padding);
            minY = (minY - padding < 0 ? 0 : minY - padding);
            float z;

            for (int i = minX; i < maxX; i++)
            {
                for (int j = minY + 1; j <= maxY; j++)
                {
                    pt.x = i + 0.5f;
                    pt.y = j + 0.5f;
                    Vector3 baryCoords = tri.GetBarycentricCoordinates(pt, out bool inside);
                    if (inside)
                    {
                        //Do some interpolation with the z-buffer here
                        z    = tri.ZAt(baryCoords);
                        zBuf = depthBuffer[(i) + (camera.renderHeight * (camera.renderHeight - j))];

                        if (zBuf == 0 || z < zBuf)
                        {
                            fragment.uv         = tri.UVAt(baryCoords);
                            fragment.normal     = tri.NormalAt(baryCoords);
                            fragment.color      = Color.HotPink;
                            fragment.triangle   = tri;
                            fragment.faceNormal = normal;
                            fragment.camera     = camera;
                            fragment.thisMesh   = mesh;
                            fragment.x          = i;
                            fragment.y          = j;
                            fragment.z          = z;
                            frag(fragment);


                            b.SetPixel(fragment.x, camera.renderHeight - fragment.y, fragment.color);
                            depthBuffer[(fragment.x) + (camera.renderHeight * (camera.renderHeight - fragment.y))] = fragment.z;
                        }
                    }
                }
            }
        }