//"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.ZC.
//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;