//"ls" is light source.

U0 Lighting(CDC *dc, CD3I32 *ls, CD3I32 *poly, I64 color)
{//color is a color from 0-7
        CD3I32   v1, v2;
        I64             *r = dc->r, i, vn_x, vn_y, vn_z;
        F64              d;

        v1.x = poly[0].x - poly[1].x;
        v1.y = poly[0].y - poly[1].y;
        v1.z = poly[0].z - poly[1].z;

        v2.x = poly[2].x - poly[1].x;
        v2.y = poly[2].y - poly[1].y;
        v2.z = poly[2].z - poly[1].z;

        //V1 and V2 are vects along two sides
        //of the polygon joined at point[1].

        vn_x = v1.y * v2.z - v1.z * v2.y;
        vn_y = v1.z * v2.x - v1.x * v2.z;
        vn_z = v1.x * v2.y - v1.y * v2.x;

        if (d = Sqrt(SqrI64(vn_x) + SqrI64(vn_y) + SqrI64(vn_z)))
                d = 1 << 16 / d;
        vn_x *= d;
        vn_y *= d;
        vn_z *= d;
        //Vn is the cross product of V1 and V3
        //which means it is perpendicular.  It
        //is the normal vect to the surface.
        //It has been scaled to length 65536.
        Mat4x4MulXYZ(r, &vn_x, &vn_y, &vn_z);
        i = (vn_x * ls->x + vn_y * ls->y + vn_z * ls->z) >> 16;
        //The dot product of the light source
        //vect and the surface normal
        //gives an illumination number.

        //ZealOS will generate a random U16
        //and compare to dither_probability_u16 and
        //will pick from two colors.
        //Probability dithering does not work with thick>1 at this time.
        if (i < 0)
        {
                dc->color = ROPF_PROBABILITY_DITHER + BLACK << 16 + color;
                dc->dither_probability_u16 = -i;
        }
        else
        {
                dc->color = ROPF_PROBABILITY_DITHER + (color ^ 8) << 16 + color;
                dc->dither_probability_u16 = i;
        }
}

#define RINGS   8
#define FACES   32
#define SLOP    0.03 //Gaps appear without this.

U0 DrawIt(CTask *task, CDC *dc)
{
        CCtrl           *c = CtrlFindUnique(task, CTRLT_VIEWING_ANGLES);
        CViewAngles     *s = c->state;
        F64                      tt = 0.5 * (Sin(pi * 2 * (tS % 10.0) / 10.0) + 2.0), 
                                 theta, theta2, phi, phi2, radius, d;
        I64                      i, j, cx = task->pix_width / 2, cy = task->pix_height / 2;
        CD3I32           poly[3], ls;

        dc->flags |= DCF_TRANSFORMATION;
        DCDepthBufAlloc(dc);

        Mat4x4IdentEqu(dc->r);
        Mat4x4RotZ(dc->r, s->az);
        Mat4x4RotY(dc->r, s->ay);
        Mat4x4RotX(dc->r, s->ax + pi);
        Mat4x4Scale(dc->r, tt);
        DCMat4x4Set(dc, dc->r);

        ls.x = -(mouse.pos.x - task->pix_left - task->scroll_x - cx);
        ls.y = -(mouse.pos.y - task->pix_top  - task->scroll_y - cy);
        ls.z = GR_WIDTH / 8;
        d = 1 << 16 / D3I32Norm(&ls);
        ls.x *= d;
        ls.y *= d;
        ls.z *= d;

        dc->x = cx;
        dc->y = cy;
        dc->z = MaxI64(cx, cy);
        radius = MinI64(cx, cy) / 2;

        for (i = 0; i < RINGS; i++)
        {
                phi  = i * pi / 2 / RINGS;
                phi2 = (i + 1) * pi / 2 / RINGS + SLOP;
                for (j = 0; j < FACES; j++)
                {
                        theta  = j * 2 * pi /FACES;
                        theta2 = (j + 1) * 2 * pi / FACES + SLOP;

                        //Upper half
                        poly[0].x = radius * Cos(phi) * Cos(theta);
                        poly[0].y = radius * Cos(phi) * Sin(theta);
                        poly[0].z = radius * Sin(phi);
                        poly[1].x = radius * Cos(phi) * Cos(theta2);
                        poly[1].y = radius * Cos(phi) * Sin(theta2);
                        poly[1].z = radius * Sin(phi);
                        poly[2].x = radius * Cos(phi2) * Cos(theta + 2 * pi / FACES / 2);
                        poly[2].y = radius * Cos(phi2) * Sin(theta + 2 * pi / FACES / 2);
                        poly[2].z = radius * Sin(phi2);
                        Lighting(dc, &ls, poly, BLUE);
                        GrFillPoly3(dc, 3, poly);

                        poly[2].x = radius * Cos(phi2) * Cos(theta + 2 * pi / FACES / 2);
                        poly[2].y = radius * Cos(phi2) * Sin(theta + 2 * pi / FACES / 2);
                        poly[2].z = radius * Sin(phi2);
                        poly[1].x = radius * Cos(phi2) * Cos(theta2 + 2 * pi / FACES / 2);
                        poly[1].y = radius * Cos(phi2) * Sin(theta2 + 2 * pi / FACES / 2);
                        poly[1].z = radius * Sin(phi2);
                        poly[0].x = radius * Cos(phi) * Cos(theta2);
                        poly[0].y = radius * Cos(phi) * Sin(theta2);
                        poly[0].z = radius * Sin(phi);
                        Lighting(dc, &ls, poly, BLUE);
                        GrFillPoly3(dc, 3, poly);

                        //Lower half
                        poly[2].x =  radius * Cos(phi) * Cos(theta);
                        poly[2].y =  radius * Cos(phi) * Sin(theta);
                        poly[2].z = -radius * Sin(phi);
                        poly[1].x =  radius * Cos(phi) * Cos(theta2);
                        poly[1].y =  radius * Cos(phi) * Sin(theta2);
                        poly[1].z = -radius * Sin(phi);
                        poly[0].x =  radius * Cos(phi2) * Cos(theta + 2 * pi / FACES / 2);
                        poly[0].y =  radius * Cos(phi2) * Sin(theta + 2 * pi / FACES / 2);
                        poly[0].z = -radius * Sin(phi2);
                        Lighting(dc, &ls, poly, RED);
                        GrFillPoly3(dc, 3, poly);

                        poly[0].x =  radius * Cos(phi2) * Cos(theta + 2 * pi / FACES / 2);
                        poly[0].y =  radius * Cos(phi2) * Sin(theta + 2 * pi / FACES / 2);
                        poly[0].z = -radius * Sin(phi2);
                        poly[1].x =  radius * Cos(phi2) * Cos(theta2 + 2 * pi / FACES / 2);
                        poly[1].y =  radius * Cos(phi2) * Sin(theta2 + 2 * pi / FACES / 2);
                        poly[1].z = -radius * Sin(phi2);
                        poly[2].x =  radius * Cos(phi) * Cos(theta2);
                        poly[2].y =  radius * Cos(phi) * Sin(theta2);
                        poly[2].z = -radius * Sin(phi);
                        Lighting(dc, &ls, poly, RED);
                        GrFillPoly3(dc, 3, poly);
                }
        }
}

//See ::/Demo/Graphics/SpritePlot3D.CC.
//for a CSprite example.

//See SpriteMeshEd() for a fancy example.

U0 Main()
{
        CCtrl           *c = ViewAnglesNew;
        CViewAngles *s = c->state;

        s->sx = 2 * VIEWANGLES_SNAP;
        s->sy = 7 * VIEWANGLES_SNAP;
        s->sz = 6 * VIEWANGLES_SNAP;

        SettingsPush; //See SettingsPush
        AutoComplete;
        WinBorder;
        WinMax;
        DocClear;
        Fs->draw_it = &DrawIt;
        try
        {
                "\n\nMove mouse to change light source.\n\n";
                PressAKey;
        }
        catch
                PutExcept;
        DocClear;
        SettingsPop;
        ViewAnglesDel;
}

Main;