#help_index "Graphics/Mesh"
#define MESH_WORKSPACE_SIZE 4000

#define VF_SEL              1
#define VF_COPIED           2
#define VF_IGNORE           4

class CMeshEdVertex
{
    CMeshEdVertex   *next, *last, *copy;

    U0               start;
    CD3I32           p; //World coordinates of the point.
    U0               end;

    CD3I32           p0,
                     pt; //Transformed coordinates. (Screen)
    I32              num, flags;
};

#define TF_SEL      1
#define TF_COPIED   2

class CMeshEdTri
{
    CMeshEdTri      *next, *last;

    U0               start;
    CMeshTri         mt;
    U0               end;

    I32              cpu_num, flags; //Draw different tris with different cores.
    CMeshEdVertex   *t[3];
};

class CMeshFrame
{
    I64              mouse_z, thickness; //Mouse Z-coordinate
    I64              ed_mode, cx, cy;
    CColorROPU32     cur_color;
    Bool             grid_on, flip_y, sel_rect, vertex_on, closed, pad[3];
    I64              mp_not_done_flags; //Used for multiprocessing signaling.
    F64              view_scale;
    CDC             *dc;
    I32             *depth_buf;
    I64             *w2s, *s2w; //Screen-to-world and world-to-screen transform matrices.
    I64              vertex_count, tri_count; //Set by MeshSize
    CMeshEdVertex    vertex_head, *cur_vertex, *chain_pred;
    CMeshEdTri       tri_head, *cur_tri;
    I64              x1, y1, x2, y2, cur_snap;
};

CMeshEdVertex *MeshVertexNew(CMeshFrame *e, I64 x, I64 y, I64 z)
{
    CMeshEdVertex *tmpv = CAlloc(sizeof(CMeshEdVertex));

    tmpv->p.x = x;
    tmpv->p.y = y;
    tmpv->p.z = z;
    QueueInsert(tmpv, e->vertex_head.last);

    return tmpv;
}

CMeshEdTri *MeshTriNew(CMeshFrame *e, CColorROPU32 color, CMeshEdVertex *v1, CMeshEdVertex *v2, CMeshEdVertex *v3)
{
    static I64  cpu_num = 0;
    CMeshEdTri *tmpt = CAlloc(sizeof(CMeshEdTri));

    tmpt->cpu_num   = cpu_num++ % mp_count;
    tmpt->mt.color  = color;
    tmpt->t[0]      = v1;
    tmpt->t[1]      = v2;
    tmpt->t[2]      = v3;
    QueueInsert(tmpt, e->tri_head.last);

    return tmpt;
}

CMeshEdVertex *MeshVertexFindScrPt(CMeshFrame *e, I64 x, I64 y)
{//Screen coordinates
    CMeshEdVertex   *res = NULL, *tmpv = e->vertex_head.next;
    I64              dd, dz, best_dd = I64_MAX, best_dz = I64_MAX;

    while (tmpv != &e->vertex_head)
    {
        if (!(tmpv->flags & VF_IGNORE))
        {
            dd = SqrI64(x - tmpv->pt.x) + SqrI64(y - tmpv->pt.y);
            dz = AbsI64(e->mouse_z - tmpv->p.z);
            if (dd < best_dd || dd == best_dd && dz < best_dz)
            {
                res = tmpv;
                best_dd = dd;
                best_dz = dz;
            }
        }
        tmpv = tmpv->next;
    }

    return res;
}

CMeshEdVertex *MeshVertexFindNum(CMeshFrame *haystack_e, I64 needle_num)
{
    CMeshEdVertex *tmpv = haystack_e->vertex_head.next;

    while (tmpv != &haystack_e->vertex_head)
    {
        if (tmpv->num == needle_num)
            return tmpv;
        tmpv = tmpv->next;
    }

    return NULL;
}

U0 MeshTriDel(CMeshFrame *e, CMeshEdTri *tmpt)
{
    if (tmpt)
    {
        if (tmpt == e->cur_tri)
            e->cur_tri = NULL;
        QueueRemove(tmpt);
        Free(tmpt);
    }
}

U0 MeshVertexDel(CMeshFrame *e, CMeshEdVertex *tmpv)
{
    I64          i;
    CMeshEdTri  *tmpt, *tmpt1;

    if (tmpv)
    {
        tmpt = e->tri_head.next;
        while (tmpt != &e->tri_head)
        {
            tmpt1 = tmpt->next;
            for (i = 0; i < 3; i++)
                if (tmpt->t[i] == tmpv)
                    break;
            if (i < 3)
                MeshTriDel(e, tmpt);
            tmpt = tmpt1;
        }
        if (tmpv == e->cur_vertex)
            e->cur_vertex = NULL;
        if (tmpv == e->chain_pred)
            e->chain_pred = NULL;
        QueueRemove(tmpv);
        Free(tmpv);
    }
}

U0 MeshFence(CMeshFrame *e)
{
    CMeshEdVertex *tmpv, *tmpv1, *tmpv_last = NULL, *tmpv1_last = NULL,
                  *start = e->chain_pred->next, *end = e->vertex_head.last;

    tmpv = start;
    while (TRUE)
    {
        tmpv1 = MeshVertexNew(e, tmpv->p.x, tmpv->p.y, tmpv->p.z + e->thickness);
        if (tmpv_last)
        {
            MeshTriNew(e, e->cur_color, tmpv_last, tmpv, tmpv1);
            MeshTriNew(e, e->cur_color, tmpv1, tmpv1_last, tmpv_last);
        }
        tmpv_last = tmpv;
        tmpv1_last = tmpv1;
        if (tmpv == end)
            break;
        tmpv = tmpv->next;
    }
    if (e->closed && tmpv_last)
    {
        MeshTriNew(e, e->cur_color, tmpv_last, start, end->next);
        MeshTriNew(e, e->cur_color, end->next, tmpv1_last, tmpv_last);
    }
}

U0 MeshPolygon(CMeshFrame *e, CMeshEdVertex *start, CMeshEdVertex *end, Bool rev)
{
    CMeshEdVertex *tmpv, *tmpv1;

    if (start != end)
    {
        tmpv = start;
        tmpv1 = tmpv->next;
        while (tmpv1 != end)
        {
            if (rev)
                MeshTriNew(e, e->cur_color, tmpv1, tmpv, end);
            else
                MeshTriNew(e, e->cur_color, tmpv, tmpv1, end);
            tmpv = tmpv->next;
            tmpv1 = tmpv1->next;
        }
    }
}

U0 MeshPrism(CMeshFrame *e)
{
    CMeshEdVertex *start = e->chain_pred->next, *end = e->vertex_head.last;

    MeshFence(e);
    MeshPolygon(e, start, end, FALSE);
    MeshPolygon(e, end->next, e->vertex_head.last, TRUE);
}

U0 MeshVertexSelAll(CMeshFrame *e, Bool val)
{
    CMeshEdVertex *tmpv = e->vertex_head.next;

    while (tmpv != &e->vertex_head)
    {
        if (val)
            tmpv->flags |= VF_SEL;
        else
            tmpv->flags &= ~VF_SEL;
        tmpv = tmpv->next;
    }
}

U0 MeshTriSelAll(CMeshFrame *e, Bool val)
{
    CMeshEdTri *tmpt = e->tri_head.next;

    while (tmpt != &e->tri_head)
    {
        if (val)
            tmpt->flags |= TF_SEL;
        else
            tmpt->flags &= ~TF_SEL;
        tmpt = tmpt->next;
    }
}

U0 MeshVertexIgnoreSet(CMeshFrame *e, Bool val)
{
    CMeshEdVertex *tmpv = e->vertex_head.next;

    while (tmpv != &e->vertex_head)
    {
        tmpv->flags &= ~VF_IGNORE;
        if (tmpv->flags & VF_SEL && val)
            tmpv->flags |= VF_IGNORE;
        tmpv = tmpv->next;
    }
}

U0 MeshP0Capture(CMeshFrame *e)
{
    CMeshEdVertex *tmpv = e->vertex_head.next;

    while (tmpv != &e->vertex_head)
    {
        MemCopy(&tmpv->p0, &tmpv->p, sizeof(CD3I32));
        tmpv = tmpv->next;
    }
}

U0 MeshP0Offset(CMeshFrame *e, I64 dx, I64 dy, I64 dz)
{
    CMeshEdVertex *tmpv = e->vertex_head.next;

    while (tmpv != &e->vertex_head)
    {
        if (tmpv->flags & VF_SEL)
        {
            tmpv->p.x = tmpv->p0.x + dx;
            tmpv->p.y = tmpv->p0.y + dy;
            tmpv->p.z = tmpv->p0.z + dz;
        }
        tmpv = tmpv->next;
    }
}

#define SEL_MESH_EQU        0
#define SEL_MESH_OR         1
#define SEL_MESH_AND        2

U0 MeshVertexSelRect(CMeshFrame *e, I64 sel_mode, I64 x1, I64 x2, I64 y1, I64 y2)
{
    CMeshEdVertex *tmpv = e->vertex_head.next;

    if (x1 > x2)
        SwapI64(&x1, &x2);
    if (y1 > y2)
        SwapI64(&y1, &y2);
    while (tmpv != &e->vertex_head)
    {
        if (x1 <= tmpv->pt.x <= x2 && y1 <= tmpv->pt.y <= y2)
        {
            if (sel_mode == SEL_MESH_AND)
                tmpv->flags &= ~VF_SEL;
            else
                tmpv->flags |= VF_SEL;
        }
        else if (sel_mode == SEL_MESH_EQU)
            tmpv->flags &= ~VF_SEL;
        tmpv = tmpv->next;
    }
}

U0 MeshTriSelRect(CMeshFrame *e, I64 sel_mode, I64 x1, I64 x2, I64 y1, I64 y2)
{
    CMeshEdTri *tmpt = e->tri_head.next;

    if (x1 > x2)
        SwapI64(&x1, &x2);
    if (y1 > y2)
        SwapI64(&y1, &y2);
    while (tmpt != &e->tri_head)
    {
        if (x1 <= tmpt->t[0]->pt.x <= x2 &&
            y1 <= tmpt->t[0]->pt.y <= y2 &&
            x1 <= tmpt->t[1]->pt.x <= x2 &&
            y1 <= tmpt->t[1]->pt.y <= y2 &&
            x1 <= tmpt->t[2]->pt.x <= x2 &&
            y1 <= tmpt->t[2]->pt.y <= y2)
        {
            if (sel_mode == SEL_MESH_AND)
                tmpt->flags &= ~TF_SEL;
            else
                tmpt->flags |= TF_SEL;
        }
        else
        {
            if (sel_mode == SEL_MESH_EQU)
                tmpt->flags &= ~TF_SEL;
            else if (sel_mode == SEL_MESH_AND)
            {
                if (    x1 <= tmpt->t[0]->pt.x <= x2 &&
                        y1 <= tmpt->t[0]->pt.y <= y2 ||
                        x1 <= tmpt->t[1]->pt.x <= x2 &&
                        y1 <= tmpt->t[1]->pt.y <= y2 ||
                        x1 <= tmpt->t[2]->pt.x <= x2 &&
                        y1 <= tmpt->t[2]->pt.y <= y2)
                    tmpt->flags &= ~TF_SEL;
            }
        }
        tmpt = tmpt->next;
    }
}

I64 MeshSelCount(CMeshFrame *e)
{
    I64              res = 0;
    CMeshEdVertex   *tmpv = e->vertex_head.next;
    CMeshEdTri      *tmpt = e->tri_head.next;

    while (tmpv != &e->vertex_head)
    {
        if (tmpv->flags & VF_SEL)
            res++;
        tmpv = tmpv->next;
    }
    while (tmpt != &e->tri_head)
    {
        if (tmpt->flags & TF_SEL)
            res++;
        tmpt = tmpt->next;
    }
    return res;
}

U0 MeshSwapAxes(CMeshFrame *e, I64 o1, I64 o2)
{
    Bool             unsel;
    CMeshEdVertex   *tmpv = e->vertex_head.next;

    if (!MeshSelCount(e))
    {
        MeshVertexSelAll(e, TRUE);
        unsel = TRUE;
    }
    else
        unsel = FALSE;
    while (tmpv != &e->vertex_head)
    {
        if (tmpv->flags & VF_SEL)
            SwapU32((&tmpv->p)(U8 *) + o1, (&tmpv->p)(U8 *) + o2);
        tmpv = tmpv->next;
    }
    if (unsel)
        MeshVertexSelAll(e, FALSE);
}

U0 MeshInvertAxis(CMeshFrame *e, I64 o)
{
    Bool             unsel;
    CMeshEdVertex   *tmpv = e->vertex_head.next;

    if (!MeshSelCount(e))
    {
        MeshVertexSelAll(e, TRUE);
        unsel = TRUE;
    }
    else
        unsel = FALSE;
    while (tmpv != &e->vertex_head)
    {
        if (tmpv->flags & VF_SEL)
            *((&tmpv->p)(U8 *) + o)(I32 *) = -*((&tmpv->p)(U8 *) + o)(I32 *);
        tmpv = tmpv->next;
    }
    if (unsel)
        MeshVertexSelAll(e, FALSE);
}

U0 MeshTransformSel(CMeshFrame *e)
{
    Bool             unsel;
    I64              r[16], x, y, z;
    CMeshEdVertex   *tmpv = e->vertex_head.next;

    if (PopUpTransform(r))
    {
        if (!MeshSelCount(e))
        {
            MeshVertexSelAll(e, TRUE);
            unsel = TRUE;
        }
        else
            unsel = FALSE;
        while (tmpv != &e->vertex_head)
        {
            if (tmpv->flags & VF_SEL)
            {
                x = tmpv->p.x;
                y = tmpv->p.y;
                z = tmpv->p.z;
                Mat4x4MulXYZ(r, &x, &y, &z);
                tmpv->p.x = x;
                tmpv->p.y = y;
                tmpv->p.z = z;
            }
            tmpv = tmpv->next;
        }
        if (unsel)
            MeshVertexSelAll(e, FALSE);
    }
}

U0 MeshColorTris(CMeshFrame *e)
{
    Bool         unsel;
    CMeshEdTri  *tmpt = e->tri_head.next;

    if (!MeshSelCount(e))
    {
        MeshTriSelAll(e, TRUE);
        unsel = TRUE;
    }
    else
        unsel = FALSE;
    while (tmpt != &e->tri_head)
    {
        if (tmpt->flags & TF_SEL)
            tmpt->mt.color = e->cur_color;
        tmpt = tmpt->next;
    }
    if (unsel)
        MeshTriSelAll(e, FALSE);
}

U0 MeshRevTris(CMeshFrame *e)
{
    Bool         unsel;
    CMeshEdTri  *tmpt = e->tri_head.next;

    if (!MeshSelCount(e))
    {
        MeshTriSelAll(e, TRUE);
        unsel = TRUE;
    }
    else
        unsel = FALSE;
    while (tmpt != &e->tri_head)
    {
        if (tmpt->flags & TF_SEL)
            SwapI64(&tmpt->t[1], &tmpt->t[2]);
        tmpt = tmpt->next;
    }
    if (unsel)
        MeshTriSelAll(e, FALSE);
}

U0 MeshRecalcCxCy(CTask *task, CMeshFrame *e)
{
    e->cx = RoundI64(task->pix_width  / 2 - task->horz_scroll.pos, e->cur_snap);
    e->cy = RoundI64(task->pix_height / 2 - task->vert_scroll.pos, e->cur_snap);
}

U0 MeshCurSnap(CMeshFrame *e)
{
    I64 x1, y1, z1, x2, y2, z2;

    if (e->w2s)
    {
        x1 = e->cur_snap << 16;
        y1 = 0;
        z1 = 0;
        Mat4x4MulXYZ(e->w2s, &x1, &y1, &z1);
        x2 = 0;
        y2 = e->cur_snap << 16;
        z2 = 0;
        Mat4x4MulXYZ(e->w2s, &x2, &y2, &z2);
        mouse_grid.x = Max(1, MaxI64(x1, x2) >> 16);
        mouse_grid.y = Max(1, MaxI64(y1, y2) >> 16);
        mouse_grid.z = Min(mouse_grid.x, mouse_grid.y);
    }
}

U0 MeshScaleZoom(CMeshFrame *e, F64 scale)
{
    CTask   *task = Fs;
    I64      x = mouse.pos.x - task->pix_left - task->scroll_x - task->pix_width  / 2, 
             y = mouse.pos.y - task->pix_top  - task->scroll_y - task->pix_height / 2;

    task->horz_scroll.pos *= scale;
    task->vert_scroll.pos *= scale;
    task->horz_scroll.pos += scale * x - x;
    task->vert_scroll.pos += scale * y - y;
    e->view_scale *= scale;
    MeshRecalcCxCy(task, e);
    MeshCurSnap(e);
}

U0 MPDrawIt(CMeshFrame *e)
{//Multiprocessing draw it, called by each core.

    //Makes a copy of e->dc so we can change dc->color member and stuff.
    CDC         *dc = DCAlias(e->dc, e->dc->win_task);
    CMeshEdTri  *tmpt = e->tri_head.next;
    I64          i, *old_r = dc->r;

    //DCAlias() allocs a new identity rotation matrix.
    //We want e->dc's rotation matrix.
    dc->r           = e->dc->r;
    dc->depth_buf   = e->depth_buf;
    MemCopy(&dc->ls, &e->dc->ls, sizeof(CD3I32));

    //... and translation (shift) vals.
    dc->x = e->dc->x;
    dc->y = e->dc->y;
    dc->z = e->dc->z;
    dc->flags |= DCF_TRANSFORMATION;

    if (e->grid_on)
//Draw grid with different cores.
        for (i = -500 + 25 * Gs->num; i <= 500; i += 25 * mp_count)
        {
            if (i)
            {
                dc->color = DKGRAY;
                GrLine3(dc, i, -500, 0, i, 500, 0);
                dc->color = LTGRAY;
                GrLine3(dc, -500, i, 0, 500, i, 0);
            }
        }
    if (!Gs->num)
    {
        dc->color = RED;                    //Y-Axis red
        GrLine3(dc, 0, 0, 0, 0, 500, 0);
        dc->color = ROPF_DITHER + RED;      //Y-Axis red
        GrLine3(dc, 0, -500, 0, 0, 0, 0);

        dc->color = YELLOW;                 //X-Axis yellow
        GrLine3(dc, 0, 0, 0, 500, 0, 0);
        dc->color = ROPF_DITHER + YELLOW;   //X-Axis yellow
        GrLine3(dc, -500, 0, 0, 0, 0, 0);

        dc->color = GREEN;                  //Z-Axis green
        GrLine3(dc, 0, 0, 0, 0, 0, 500);
        dc->color = ROPF_DITHER + GREEN;        //Z-Axis green
        GrLine3(dc, 0, 0, -500, 0, 0, 0);
    }

    while (tmpt != &e->tri_head)
    {
        if (tmpt->cpu_num == Gs->num)
        {
            if (tmpt->flags & TF_SEL)
            {
                if (Blink)
                    dc->color = ROPF_DITHER + WHITE << 16 + RED;
                else
                    dc->color = ROPF_DITHER + RED << 16 + WHITE;
                GrFillTri0(dc, &tmpt->t[0]->pt, &tmpt->t[1]->pt, &tmpt->t[2]->pt);
            }
            else
            {
                (*dc->lighting)(dc, &tmpt->t[0]->pt, &tmpt->t[1]->pt, 
                                    &tmpt->t[2]->pt, tmpt->mt.color);
                GrFillTri0(dc, &tmpt->t[0]->pt, &tmpt->t[1]->pt, &tmpt->t[2]->pt);
            }
        }
        tmpt = tmpt->next;
    }
    dc->r = old_r;

    //e->dc's depth buf was copied but we don't want it freed during DCDel().
    dc->depth_buf = NULL;

    DCDel(dc);
    LBtr(&e->mp_not_done_flags, Gs->num);
}

I64 *MeshW2S(CMeshFrame *e, CTask *task)
{//World to screen coordinate transform matrix.
    CCtrl       *c = CtrlFindUnique(task, CTRLT_VIEWING_ANGLES);
    CViewAngles *s = c->state;
    I64         *r = Mat4x4IdentNew(task);

    Mat4x4Scale(r, e->view_scale);
    Mat4x4RotZ(r, s->az);
    Mat4x4RotY(r, s->ay);
    if (e->flip_y)
        Mat4x4RotX(r, s->ax);
    else
        Mat4x4RotX(r, s->ax + pi);

    return r;
}

I64 *MeshS2W(CMeshFrame *e, CTask *task)
{//Screen to world coordinate transform matrix.
    CCtrl       *c = CtrlFindUnique(task, CTRLT_VIEWING_ANGLES);
    CViewAngles *s = c->state;
    I64         *r = Mat4x4IdentNew(task);

    if (e->flip_y)
        Mat4x4RotX(r, -s->ax);
    else
        Mat4x4RotX(r, -(s->ax + pi));
    Mat4x4RotY(r, -s->ay);
    Mat4x4RotZ(r, -s->az);
    Mat4x4Scale(r, 1 / e->view_scale);

    return r;
}

I64 *MeshSetW2S(CMeshFrame *e, CTask *task)
{
    Free(e->w2s);
    e->w2s = MeshW2S(e, task);
    Free(e->s2w);
    e->s2w = MeshS2W(e, task);
//returned matrix is assigned to dc->r and will be freed by DCDel().

    return Mat4x4New(e->w2s, task);
}

U0 MeshCursorW(CMeshFrame *e, CTask *task, I64 *_x, I64 *_y, I64 *_z)
{
    I64 x_shadow, y_shadow, z_shadow, 
        xc = mouse.pos.x - task->pix_left - task->scroll_x - e->cx, 
        yc = mouse.pos.y - task->pix_top  - task->scroll_y - e->cy,
        zc = 0, x = 0, y = 0, z = e->mouse_z, i, x2, y2, z2;

    Mat4x4MulXYZ(e->w2s, &x, &y, &z); //screen of Z vect

    //Converges onto a solution for zc, an unknown.
    for (i = 0; i < 128; i++)
    {
        x_shadow = xc - x; //Shadow of mouse cursor on xy plane
        y_shadow = yc - y;
        z_shadow = zc - z;
        Mat4x4MulXYZ(e->s2w, &x_shadow, &y_shadow, &z_shadow);
        x2 = 0;
        y2 = 0;
        z2 = -z_shadow;
        Mat4x4MulXYZ(e->w2s, &x2, &y2, &z2);
        zc += Round(Sqrt(x2 * x2 + y2 * y2 + z2 * z2)) * SignI64(z2);
    }

    x = xc - x;
    y = yc - y;
    z = zc - z;
    Mat4x4MulXYZ(e->s2w, &x, &y, &z);
    x = RoundI64(x, e->cur_snap);
    y = RoundI64(y, e->cur_snap);
    z = RoundI64(e->mouse_z, e->cur_snap);
    *_x = x;
    *_y = y;
    *_z = z;
}

CMeshEdVertex   sys_clip_vertex_head;
CMeshEdTri      sys_clip_tri_head;

U0 MeshClipInit()
{
    QueueInit(&sys_clip_vertex_head);
    QueueInit(&sys_clip_tri_head);
}

U0 MeshClipReset()
{
    QueueDel(&sys_clip_vertex_head, TRUE);
    QueueDel(&sys_clip_tri_head, TRUE);
    MeshClipInit;
}

U0 MeshClipCopy(CMeshFrame *e)
{
    CMeshEdVertex   *tmpv = e->vertex_head.next, *tmpv2;
    CMeshEdTri      *tmpt = e->tri_head.next, *tmpt2;

    MeshClipReset;
    while (tmpv != &e->vertex_head)
    {
        if (tmpv->flags & VF_SEL)
        {
            tmpv->copy = tmpv2 = SysCAlloc(sizeof(CMeshEdVertex));
            MemCopy(&tmpv2->p, &tmpv->p, sizeof(CD3I32));
            QueueInsert(tmpv2, sys_clip_vertex_head.last);
            tmpv->flags |= VF_COPIED;
            tmpv->flags &= ~VF_SEL;
        }
        else
        {
            tmpv->copy = NULL;
            tmpv->flags &= ~(VF_COPIED | VF_SEL);
        }
        tmpv = tmpv->next;
    }
    while (tmpt != &e->tri_head)
    {
        if (tmpt->flags & TF_SEL && tmpt->t[0]->copy && tmpt->t[1]->copy && tmpt->t[2]->copy)
        {
            tmpt2 = SysCAlloc(sizeof(CMeshEdTri));
            tmpt2->t[0] = tmpt->t[0]->copy;
            tmpt2->t[1] = tmpt->t[1]->copy;
            tmpt2->t[2] = tmpt->t[2]->copy;
            tmpt2->mt.color = tmpt->mt.color;
            QueueInsert(tmpt2, sys_clip_tri_head.last);
            tmpt->flags |= TF_COPIED;
            tmpt->flags &= ~TF_SEL;
        }
        else
            tmpt->flags &= ~(TF_COPIED|TF_SEL);
        tmpt = tmpt->next;
    }
}

U0 MeshClipCut(CMeshFrame *e)
{
    CMeshEdVertex   *tmpv = e->vertex_head.next, *tmpv1;
    CMeshEdTri      *tmpt = e->tri_head.next, *tmpt1;

    MeshClipCopy(e);
    while (tmpt != &e->tri_head)
    {
        tmpt1 = tmpt->next;
        if (tmpt->flags & TF_COPIED)
            MeshTriDel(e, tmpt);
        tmpt = tmpt1;
    }
    while (tmpv != &e->vertex_head)
    {
        tmpv1 = tmpv->next;
        if (tmpv->flags & VF_COPIED)
            MeshVertexDel(e, tmpv);
        tmpv = tmpv1;
    }
}

U0 MeshClipDel(CMeshFrame *e)
{//Technically not clip
    CMeshEdVertex   *tmpv = e->vertex_head.next, *tmpv1;
    CMeshEdTri      *tmpt = e->tri_head.next, *tmpt1;

    while (tmpt != &e->tri_head)
    {
        tmpt1 = tmpt->next;
        if (tmpt->flags & TF_SEL)
            MeshTriDel(e, tmpt);
        tmpt = tmpt1;
    }
    while (tmpv != &e->vertex_head)
    {
        tmpv1 = tmpv->next;
        if (tmpv->flags & VF_SEL)
            MeshVertexDel(e, tmpv);
        tmpv = tmpv1;
    }
}

U0 MeshClipPaste(CMeshFrame *e)
{
    CMeshEdVertex   *tmpv2 = sys_clip_vertex_head.next, *tmpv;
    CMeshEdTri      *tmpt2 = sys_clip_tri_head.next, *tmpt;

    MeshVertexSelAll(e, FALSE);
    MeshTriSelAll(e, FALSE);
    while (tmpv2 != &sys_clip_vertex_head)
    {
        tmpv2->copy = tmpv = CAlloc(sizeof(CMeshEdVertex));
        MemCopy(&tmpv->p, &tmpv2->p, sizeof(CD3I32));
        QueueInsert(tmpv, e->vertex_head.last);
        tmpv->flags |= VF_SEL;
        tmpv2 = tmpv2->next;
    }

    while (tmpt2 != &sys_clip_tri_head)
    {
        tmpt = MeshTriNew(e, tmpt2->mt.color, tmpt2->t[0]->copy, tmpt2->t[1]->copy, tmpt2->t[2]->copy);
        tmpt->flags |= TF_SEL;
        tmpt2 = tmpt2->next;
    }
}

MeshClipInit;

U0 DrawIt(CTask *task, CDC *dc)
{
    CMeshFrame      *e = FramePtr("CMeshFrame", task);
    CCtrl           *c = CtrlFindUnique(task, CTRLT_VIEWING_ANGLES);
    F64              d;
    I64              i, x, y, z;
    CMeshEdVertex   *tmpv;

    task->horz_scroll.min = -(MESH_WORKSPACE_SIZE - task->pix_width)  / 2;
    task->horz_scroll.max =  (MESH_WORKSPACE_SIZE - task->pix_width)  / 2;
    task->vert_scroll.min = -(MESH_WORKSPACE_SIZE - task->pix_height) / 2;
    task->vert_scroll.max =  (MESH_WORKSPACE_SIZE - task->pix_height) / 2;
    TaskDerivedValsUpdate(task);
    MeshRecalcCxCy(task, e);

    dc->flags |= DCF_TRANSFORMATION;

    Free(dc->r); //Set rotmat doesn't free old dc->r matrix.
    DCMat4x4Set(dc, MeshSetW2S(e, task));

    dc->x = e->cx;
    dc->y = e->cy;
//z-vals less than zero are in front of screen and not drawn.
    //we want to shift all Z-vals into a drawable range.
    //GR_Z_ALL is set to half of the Z-range which is an I32.
    dc->z = GR_Z_ALL;

    //Light source set to mouse.
    MeshCursorW(e, task, &x, &y, &z);
    dc->ls.x = x;
    dc->ls.y = y;
    dc->ls.z = z;
    d = 1 << 16 / D3I32Norm(&dc->ls); //Light source normalized to 65536.
    dc->ls.x *= d;
    dc->ls.y *= d;
    dc->ls.z *= d;

    DCDepthBufAlloc(dc);

    tmpv = e->vertex_head.next;
    while (tmpv != &e->vertex_head)
    {
        x = tmpv->p.x;
        y = tmpv->p.y;
        z = tmpv->p.z;
        (*dc->transform)(dc, &x, &y, &z);
        tmpv->pt.x = x;
        tmpv->pt.y = y;
        tmpv->pt.z = z;
        tmpv = tmpv->next;
    }

    e->mp_not_done_flags = 1 << mp_count - 1; //Issue jobs to all cores.
    e->dc                = dc;
    e->depth_buf         = dc->depth_buf;
    for (i = 0; i < mp_count; i++)
        JobQueue(&MPDrawIt, e, i);

    tmpv = e->vertex_head.next;
    while (tmpv != &e->vertex_head)
    {
        x = tmpv->pt.x;
        y = tmpv->pt.y;
        z = tmpv->pt.z;
        if (e->vertex_on)
        {
            if (Blink(10)) //This blinks at 10 Hz.
                dc->color = ROPF_DITHER + BLACK << 16 + WHITE;
            else
                dc->color = ROPF_DITHER + WHITE << 16 + BLACK;
            GrLine(dc, x - 3, y - 3, x + 3, y + 3);
            GrLine(dc, x - 3, y + 3, x + 3, y - 3);
        }
        if (tmpv->flags & VF_SEL)
        {
            if (e->ed_mode == 't')
            {
                if (Blink(10)) //This blinks at 10 Hz.
                    dc->color = ROPF_DITHER + e->cur_color.c0.color << 16 + e->cur_color.c0.color ^ 8;
                else
                    dc->color = ROPF_DITHER + (e->cur_color.c0.color ^ 8) << 16 + e->cur_color.c0.color;
            }
            else
            {
                if (Blink)
                    dc->color = ROPF_DITHER + RED << 16 + WHITE;
                else
                    dc->color = ROPF_DITHER + WHITE << 16 + RED;
            }
            GrCircle(dc, x, y, 3);
        }
        tmpv = tmpv->next;
    }

    if (CtrlInside(c, mouse.presnap.x, mouse.presnap.y) || winmgr.show_menu)
    {
        GridInit;
        task->win_inhibit = WIF_SELF_DOC;
    }
    else
    {
        MeshCurSnap(e);
        task->win_inhibit = WIG_TASK_DEFAULT |
                WIF_SELF_DOC - WIF_SELF_FOCUS - WIF_SELF_BORDER - WIF_SELF_CTRLS - WIF_FOCUS_TASK_MENU - WIF_SELF_GRAB_SCROLL;
    }

    MeshCursorW(e, task, &x, &y, &z);
    if (z < 0)
        dc->color = ROP_XOR + RED ^ TRANSPARENT;
    else
        dc->color = ROP_XOR + TRANSPARENT;
    GrPrint(dc, 0, 0, "%6.3f%% (%d,%d,%d)", e->view_scale * 100, x, y, z);
    dc->thick = 1;
    dc->color &= 0xF;
    if (Blink(10))
        dc->color ^= 0xF;
    GrLine3(dc, x, y, z, x, y, 0);

    if (e->sel_rect)
    {
        dc->flags &= ~DCF_TRANSFORMATION;
        dc->thick = 1;
        if (Blink)
            dc->color = ROPF_DITHER + RED << 16 + WHITE;
        else
            dc->color = ROPF_DITHER + WHITE << 16 + RED;
        GrBorder(dc, e->x1, e->y1, e->x2, e->y2);
    }
//Wait for all cores to complete.
    while (e->mp_not_done_flags)
        Yield;
}

U0 MeshInit(CMeshFrame *e, Bool flip_y)
{
    MemSet(e, 0, sizeof(CMeshFrame));
    QueueInit(&e->vertex_head);
    QueueInit(&e->tri_head);
    e->ed_mode      = 'v';
    e->grid_on      = TRUE;
    e->vertex_on    = TRUE;
    e->mouse_z      = 0;
    e->thickness    = 25;
    e->closed       = TRUE;
    e->view_scale   = 1.0;
    e->w2s          = NULL;
    e->s2w          = NULL;
    e->cur_color    = RED;
    e->cur_snap     = 5;
    e->flip_y       = flip_y;
    e->sel_rect     = FALSE;
    e->cur_tri      = NULL;
    e->cur_vertex   = NULL;
    e->chain_pred   = NULL;
}

U0 MeshLoad(CMeshFrame *e, U8 *src)
{
    I64              i, j, x, y, z;
    CColorROPU32     color;
    CMeshEdVertex   *tmpv, *va[3];

    QueueInit(&e->vertex_head);
    QueueInit(&e->tri_head);

    e->vertex_count = *src(I32 *)++;
    e->tri_count = *src(I32 *)++;
    for (i = 0; i < e->vertex_count; i++)
    {
        x = *src(I32 *)++;
        y = *src(I32 *)++;
        z = *src(I32 *)++;
        tmpv = MeshVertexNew(e, x, y, z);
        tmpv->num = i;
    }
    for (i = 0; i < e->tri_count; i++)
    {
        color = *src(I32 *)++;
        for (j = 0; j < 3; j++)
            va[j] = MeshVertexFindNum(e, *src(I32 *)++);
        MeshTriNew(e, color, va[0], va[1], va[2]);
    }
}

I64 MeshSize(CMeshFrame *e)
{
    I64              i;
    CMeshEdVertex   *tmpv = e->vertex_head.next;
    CMeshEdTri      *tmpt = e->tri_head.next;

    e->vertex_count = 0;
    while (tmpv != &e->vertex_head)
    {
        tmpv->num = e->vertex_count++;
        tmpv = tmpv->next;
    }

    e->tri_count = 0;
    while (tmpt != &e->tri_head)
    {
        e->tri_count++;
        for (i = 0; i < 3; i++)
            tmpt->mt.nums[i] = tmpt->t[i]->num;
        tmpt = tmpt->next;
    }
    return sizeof(I32) * 2 + (offset(CMeshEdVertex.end) - offset(CMeshEdVertex.start))  * e->vertex_count +
                             (offset(CMeshEdTri.end)    - offset(CMeshEdTri.start))     * e->tri_count;
}

I32 *MeshSave(CMeshFrame *e, I64 *_size=NULL)
{
    I64              size = MeshSize(e);
    U8              *res = MAlloc(size), *dst = res;
    CMeshEdVertex   *tmpv = e->vertex_head.next;
    CMeshEdTri      *tmpt = e->tri_head.next;

    *dst(I32 *)++ = e->vertex_count;
    *dst(I32 *)++ = e->tri_count;

    e->vertex_count = 0;
    while (tmpv != &e->vertex_head)
    {
        MemCopy(dst, &tmpv->start, offset(CMeshEdVertex.end)
        -offset(CMeshEdVertex.start));
        dst += offset(CMeshEdVertex.end) - offset(CMeshEdVertex.start);
        tmpv = tmpv->next;
    }

    e->tri_count = 0;
    while (tmpt != &e->tri_head)
    {
        MemCopy(dst, &tmpt->start, offset(CMeshEdTri.end) - offset(CMeshEdTri.start));
        dst += offset(CMeshEdTri.end) - offset(CMeshEdTri.start);
        tmpt = tmpt->next;
    }
    if (_size)
        *_size = size;

    return res;
}

U0 MeshCleanUp(CMeshFrame *e)
{
    QueueDel(&e->vertex_head, TRUE);
    QueueDel(&e->tri_head, TRUE);
    Free(e->w2s);
    Free(e->s2w);
}

U0 MeshUpdateMenu(CMeshFrame *e)
{
    CMenuEntry *tmpse;

    if (tmpse = MenuEntryFind(Fs->cur_menu, "View/Grid"))
        tmpse->checked = ToBool(e->grid_on);

    if (tmpse = MenuEntryFind(Fs->cur_menu, "View/Vertex"))
        tmpse->checked = ToBool(e->vertex_on);

    if (tmpse = MenuEntryFind(Fs->cur_menu, "Mode/PlaceVertex"))
        tmpse->checked = ToBool(e->ed_mode == 'v');

    if (tmpse = MenuEntryFind(Fs->cur_menu, "Mode/MoveVertex"))
        tmpse->checked = ToBool(e->ed_mode == 'm');

    if (tmpse = MenuEntryFind(Fs->cur_menu, "Mode/MoveVertexZ"))
        tmpse->checked = ToBool(e->ed_mode == 'M');

    if (tmpse = MenuEntryFind(Fs->cur_menu, "Mode/Triangle"))
        tmpse->checked = ToBool(e->ed_mode == 't');

    if (tmpse = MenuEntryFind(Fs->cur_menu, "Mode/Polygon"))
        tmpse->checked = ToBool(e->ed_mode == 'n');

    if (tmpse = MenuEntryFind(Fs->cur_menu, "Mode/Fence"))
        tmpse->checked = ToBool(e->ed_mode == 'f');

    if (tmpse = MenuEntryFind(Fs->cur_menu, "Mode/Prism"))
        tmpse->checked = ToBool(e->ed_mode == 'p');

    if (tmpse = MenuEntryFind(Fs->cur_menu, "View/FlipY"))
        tmpse->checked = ToBool(e->flip_y);
}

I32 *SpriteMeshEd(I32 *head=NULL, I64 *_size=NULL, Bool flip_y=FALSE)
{/*Format for mesh:
{
I32 vertex_count;
I32 tri_count;
CD3I32 vertices[];
CMeshTri tris[];
}

If head points to a mesh, it will load it.

Returns a newly malloced mesh or NULL.

See ::/Demo/Graphics/SpritePlot3D.CC.
*/
    CCtrl           *c = CtrlFindUnique(Fs, CTRLT_VIEWING_ANGLES);
    CViewAngles     *s, *old_s;
    I64              i, message_code, sel_mode, arg1, arg2, make_tri_vertex_num = 0, x, y, z;
    CD3I32           p0a, p0b;
    CMeshEdVertex   *va[3], *tmpv;
    Bool             adjusting_z = FALSE, moving, save_and_exit, adjusting_camera_xy = FALSE, adjusting_camera_z = FALSE;

    CMeshFrame e;

    if (c)
    {
        old_s = MAlloc(sizeof(CViewAngles));
        MemCopy(old_s, c->state, sizeof(CViewAngles));
    }
    else
    {
        c = ViewAnglesNew;
        old_s = NULL;
    }

    s = c->state;
    s->sx = 0;
    s->sy = 0;
    s->sz = 0;
    s->cx = YELLOW;
    s->cy = RED;
    s->cz = GREEN;

    MenuPush(
                "File {"
                "  Abort(,CH_SHIFT_ESC);"
                "  Exit(,CH_ESC);"
                "}"
                "Edit {"
                "  Delete(,,SC_DELETE);"
                "  DelLast(,CH_BACKSPACE);"
                "  Cut(,CH_CTRLX);"
                "  Copy(,CH_CTRLC);"
                "  Paste(,CH_CTRLV);"
                "  SelectAll(,'A');"
                "  UnSelectAll(,'U');"
                "  SelectRect(,'a');"
                "  UnSelectRect(,'u');"
                "  OrSelectRect(,'o');"
                "  JumpToZ(,'j');"
                "  ResetColor(,'C');"
                "  ReverseTri(,'r');"
                "}"
                "Mode {"
                "  PlaceVertex(,'v');"
                "  MoveVertex(,'m');"
                "  MoveVertexZ(,'M');"
                "  Triangle(,'t');"
                "  Polygon(,'n');"
                "  Fence(,'f');"
                "  Prism(,'p');"
                "}"
                "Settings {"
                "  Color(,'c');"
                "  Snap(,'s');"
                "}"
                "View {"
                "  ZoomIn(,'z');"
                "  ZoomOut(,'Z');"
                "  NullAngles(,'N');"
                "  FlipY(,'y');"
                "  Grid(,'g');"
                "  Vertex(,'V');"
                "  ToggleBorder(,CH_CTRLB);"
                "}"
                "Transforms {"
                "  Transform(,'T');"
                "  SwapXY(,'1');"
                "  SwapXZ(,'2');"
                "  SwapYZ(,'3');"
                "  InvertX(,'4');"
                "  InvertY(,'5');"
                "  InvertZ(,'6');"
                "  ReverseTri(,'R');"
                "}");

    SettingsPush; //See SettingsPush
    AutoComplete;
    RegOneTimePopUp(ARf_MESH_ED,
                "$GREEN$Right Mouse$FG$: Hold and move to shift cursor z\n"
                "$GREEN$Shift + Right Mouse$FG$: Hold and move to rotate camera XY\n"
                "$GREEN$Alt + Right Mouse$FG$: Hold and move to rotate camera Z\n"
                "$GREEN$'j'$FG$: Jump cursor Z to nearest vertex's Z\n"
                "$GREEN$'v'$FG$: Place Vertex Mode\n"
                "$GREEN$'m'$FG$: Move Vertex Mode\n"
                "$GREEN$'M'$FG$: Move Vertex Z\n"
                "$GREEN$'t'$FG$: Form Triangle Mode\n"
                "$GREEN$'n'$FG$: Polygon Mode\n"
                "$GREEN$'f'$FG$: Fence Mode\n"
                "$GREEN$'p'$FG$: Prism Mode\n"
                "$GREEN$'c'$FG$: Set color\n"
                "$GREEN$'s'$FG$: Set snap\n"
                "\nSee menu at top of screen for more.\n");

    Fs->win_inhibit = WIG_TASK_DEFAULT |
            WIF_SELF_DOC - WIF_SELF_FOCUS - WIF_SELF_BORDER - WIF_SELF_CTRLS - WIF_FOCUS_TASK_MENU - WIF_SELF_GRAB_SCROLL;
    Fs->horz_scroll.pos = 0;
    Fs->vert_scroll.pos = 0;
    MeshInit(&e, flip_y);
    if (head)
        MeshLoad(&e, head);
    FramePtrAdd("CMeshFrame", &e);
    Fs->draw_it = &DrawIt;
    MeshCurSnap(&e);
    MeshRecalcCxCy(Fs, &e);

    try
    {//In case of <CTRL-ALT-c>
        while (TRUE)
        {
            MeshUpdateMenu(&e);
            message_code = MessageGet(&arg1, &arg2, 1 << MESSAGE_MS_MOVE | 1 << MESSAGE_KEY_DOWN  | 1 << MESSAGE_MS_L_DOWN|
                                                    1 << MESSAGE_MS_L_UP | 1 << MESSAGE_MS_R_DOWN | 1 << MESSAGE_MS_R_UP);
me_restart:
            switch (message_code)
            {
                case MESSAGE_KEY_DOWN:
                    switch (arg1)
                    {
                        case 0:
                            switch (arg2.u8[0])
                            {
                                case SC_DELETE:
                                    if (arg2 & SCF_SHIFT)
                                        goto me_clip_cut;
                                    else
                                    {
                                        if (MeshSelCount(&e))
                                            MeshClipDel(&e);
                                        else if (e.ed_mode != 't')
                                            MeshVertexDel(&e, MeshVertexFindScrPt(&e,
                                                                mouse.presnap.x - Fs->pix_left - Fs->scroll_x, 
                                                                mouse.presnap.y - Fs->pix_top  - Fs->scroll_y));
                                        MeshVertexSelAll(&e, FALSE);
                                        MeshTriSelAll(&e, FALSE);
                                        make_tri_vertex_num = 0;
                                    }
                                    break;

                                case SC_INS:
                                    if (arg2 & SCF_CTRL)
                                        goto me_clip_copy;
                                    else if (arg2 & SCF_SHIFT)
                                        goto me_clip_paste;
                            }
                            break;

                        case CH_BACKSPACE:
                            switch (e.ed_mode)
                            {
                                case 'n':
                                case 'f':
                                case 'p':
                                case 'v':
                                    MeshVertexDel(&e, e.cur_vertex);
                                    break;

                                case 't':
                                    if (make_tri_vertex_num)
                                    {
                                        MeshVertexSelAll(&e, FALSE);
                                        MeshTriSelAll(&e, FALSE);
                                        make_tri_vertex_num = 0;
                                    }
                                    else
                                        MeshTriDel(&e, e.cur_tri);
                                    break;
                            }
                            break;

                        case 'f':
                        case 'p':
                            e.thickness = PopUpI64Get("Thickness (%d):", e.thickness);
                        case 'n':
                            if (arg1 == 'n' || arg1 == 'p')
                                e.closed = TRUE;
                            else
                                e.closed = PopUpNoYes("Closed?\n");
me_chain:
                            e.chain_pred = e.vertex_head.last;
                        case 't':
                            MeshVertexSelAll(&e, FALSE);
                            MeshTriSelAll(&e, FALSE);
                        case 'v':
                        case 'm':
                        case 'M':
                            adjusting_z = FALSE;
                            moving = FALSE;
                            e.ed_mode = arg1;
                            make_tri_vertex_num = 0;
                            Sound;
                            break;

                        case 'T':
                            MeshTransformSel(&e);
                            break;

                        case 'A':
                            MeshTriSelAll(&e, TRUE);
                            if (e.ed_mode != 't')
                                MeshVertexSelAll(&e, TRUE);
                            else
                                MeshVertexSelAll(&e, FALSE);
                            make_tri_vertex_num = 0;
                            break;

                        case 'U':
                            MeshTriSelAll(&e, FALSE);
                            MeshVertexSelAll(&e, FALSE);
                            make_tri_vertex_num = 0;
                            break;

                        case 'a':
                        case 'u':
                        case 'o':
                            if (arg1 == 'a')
                                sel_mode = SEL_MESH_EQU;
                            else if (arg1 == 'u')
                                sel_mode = SEL_MESH_AND;
                            else
                                sel_mode = SEL_MESH_OR;
                            if ((message_code = MessageGet(&arg1, &arg2, 1 << MESSAGE_KEY_DOWN  |
                                                                         1 << MESSAGE_MS_L_DOWN |
                                                                         1 << MESSAGE_MS_L_UP   |
                                                                         1 << MESSAGE_MS_R_DOWN |
                                                                         1 << MESSAGE_MS_R_UP)) != MESSAGE_MS_L_DOWN)
                            {
                                Beep;
                                Beep;
                                goto me_restart;
                            }
                            e.x1        = arg1;
                            e.y1        = arg2;
                            e.x2        = arg1;
                            e.y2        = arg2;
                            e.sel_rect  = TRUE;
                            while (TRUE)
                            {
                                message_code = MessageGet(&arg1, &arg2, 1 << MESSAGE_MS_MOVE    |
                                                                        1 << MESSAGE_KEY_DOWN   |
                                                                        1 << MESSAGE_MS_L_DOWN  |
                                                                        1 << MESSAGE_MS_L_UP    |
                                                                        1 << MESSAGE_MS_R_DOWN  |
                                                                        1 << MESSAGE_MS_R_UP);
                                if (message_code == MESSAGE_MS_MOVE)
                                {
                                    e.x2 = arg1;
                                    e.y2 = arg2;
                                }
                                else if (message_code == MESSAGE_MS_L_UP)
                                {
                                    e.x2 = arg1;
                                    e.y2 = arg2;
                                    break;
                                }
                                else
                                {
                                    e.sel_rect = FALSE;
                                    Beep;
                                    Beep;
                                    goto me_restart;
                                }
                            }
                            e.sel_rect = FALSE;
                            MeshTriSelRect(&e, sel_mode, e.x1, e.x2, e.y1, e.y2);
                            if (e.ed_mode != 't')
                                MeshVertexSelRect(&e, sel_mode, e.x1, e.x2, e.y1, e.y2);
                            else
                                MeshVertexSelAll(&e, FALSE);
                            make_tri_vertex_num = 0;
                            break;

                        case CH_CTRLB:
                            WinBorder(Bt(&Fs->display_flags, DISPLAYf_NO_BORDER));
                            break;

                        case CH_CTRLC:
me_clip_copy:
                            if (e.ed_mode == 't')
                            {
                                Beep;
                                Beep;
                            }
                            else
                                MeshClipCopy(&e);
                            break;

                        case CH_CTRLV:
me_clip_paste:
                            if (e.ed_mode == 't')
                            {
                                Beep;
                                Beep;
                            }
                            else
                            {
                                MeshClipPaste(&e);
                                e.ed_mode = 'm';
                            }
                            break;

                        case CH_CTRLX:
me_clip_cut:
                            if (e.ed_mode == 't')
                            {
                                Beep;
                                Beep;
                            }
                            else
                                MeshClipCut(&e);
                            break;

                        case CH_SHIFT_ESC:
                            save_and_exit = FALSE;
                            goto me_done;

                        case CH_ESC:
                            save_and_exit = TRUE;
                            goto me_done;

                        case 'z':
                            MeshScaleZoom(&e, 1.5);
                            break;

                        case 'Z':
                            MeshScaleZoom(&e, 1 / 1.5);
                            break;

                        case 'c':
                            e.cur_color = PopUpColorLighting;
                            break;

                        case 's':
                            i = PopUpRangeI64(1, 25, 1, "New Snap\n");
                            if (i >= 1)
                                e.cur_snap = i;
                            MeshCurSnap(&e);
                            MeshRecalcCxCy(Fs, &e);
                            break;

                        case 'g':
                            e.grid_on = !e.grid_on;
                            break;

                        case 'V':
                            e.vertex_on = !e.vertex_on;
                            break;

                        case 'N':
                            s->sx = s->sy = s->sz = 0;
                            break;

                        case 'y':
                            e.flip_y = !e.flip_y;
                            break;

                        case 'j':
                            if (moving)
                                MeshVertexIgnoreSet(&e, TRUE);
                            if (tmpv = MeshVertexFindScrPt(&e,
                                                            mouse.pos.x - Fs->pix_left - Fs->scroll_x, 
                                                            mouse.pos.y - Fs->pix_top  - Fs->scroll_y))
                            {
                                Noise(25, 86, 110);
                                e.mouse_z = RoundI64(tmpv->p.z, e.cur_snap);
                            }
                            else
                            {
                                Beep;
                                Beep;
                                e.mouse_z = 0;
                            }
                            MeshVertexIgnoreSet(&e, FALSE);
                            if (moving)
                            {
                                MeshCursorW(&e, Fs, &x, &y, &z);
                                if (adjusting_z)
                                    MeshP0Offset(&e, 0, 0, z - p0a.z);
                                else
                                    MeshP0Offset(&e, x - p0a.x, y - p0a.y, z - p0a.z);
                                p0a.x = x;
                                p0a.y = y;
                                p0a.z = z;
                                MeshP0Capture(&e);
                            }
                            break;

                        case '1':
                            MeshSwapAxes(&e, offset(CD3I32.x), offset(CD3I32.y));
                            break;

                        case '2':
                            MeshSwapAxes(&e, offset(CD3I32.x), offset(CD3I32.z));
                            break;

                        case '3':
                            MeshSwapAxes(&e, offset(CD3I32.y), offset(CD3I32.z));
                            break;

                        case '4':
                            MeshInvertAxis(&e, offset(CD3I32.x));
                            break;

                        case '5':
                            MeshInvertAxis(&e, offset(CD3I32.y));
                            break;

                        case '6':
                            MeshInvertAxis(&e, offset(CD3I32.z));
                            break;

                        case 'r':
                            if (e.cur_tri)
                                SwapI64(&e.cur_tri->t[1], &e.cur_tri->t[2]);
                            break;

                        case 'C':
                            MeshColorTris(&e);
                            break;

                        case 'R':
                            MeshRevTris(&e);
                            break;
                    }
                    break;

                case MESSAGE_MS_L_DOWN:
                    switch (e.ed_mode)
                    {
                        case 'm':
                            if (!moving)
                            {
                                if (!MeshSelCount(&e) && (tmpv = MeshVertexFindScrPt(&e, arg1, arg2)))
                                {
                                    tmpv->flags |= VF_SEL;
                                    e.mouse_z = RoundI64(tmpv->p.z, e.cur_snap);
                                }
                                if (MeshSelCount(&e))
                                {
                                    MeshCursorW(&e, Fs, &x, &y, &z);
                                    p0a.x = x;
                                    p0a.y = y;
                                    p0a.z = z;
                                    MeshP0Capture(&e);
                                    moving = TRUE;
                                }
                            }
                            break;

                        case 'M':
                            if (!adjusting_z && !moving)
                            {
                                if (!MeshSelCount(&e) && (tmpv = MeshVertexFindScrPt(&e, arg1, arg2)))
                                {
                                    tmpv->flags |= VF_SEL;
                                    e.mouse_z = RoundI64(tmpv->p.z, e.cur_snap);
                                }
                                if (MeshSelCount(&e))
                                {
                                    MeshCursorW(&e, Fs, &x, &y, &z);
                                    p0a.x = x;
                                    p0a.y = y;
                                    p0a.z = z;
                                    MeshP0Capture(&e);
                                    moving = TRUE;

                                    p0b.x = mouse.presnap.x;
                                    p0b.y = mouse.presnap.y;
                                    p0b.z = e.mouse_z;
                                    adjusting_z = TRUE;
                                    Sound(ClampI64(Freq2Ona(3 * e.mouse_z + 1500), 0, I8_MAX));
                                }
                            }
                            break;
                    }

                    break;
                case MESSAGE_MS_L_UP:
                    switch (e.ed_mode)
                    {
                        case 'n':
                        case 'f':
                        case 'p':
                        case 'v':
                            Noise(25, 86, 110);
                            MeshCursorW(&e, Fs, &x, &y, &z);
                            e.cur_vertex = MeshVertexNew(&e, x, y, z);
                            break;

                        case 'm':
                        case 'M':
                            if (moving)
                            {
                                if (adjusting_z)
                                {
                                    e.mouse_z = RoundI64(Sign(p0b.y - mouse.presnap.y) *
                                                         Sqrt(Sqr(p0b.x - mouse.presnap.x) +
                                                              Sqr(p0b.y - mouse.presnap.y)) + p0b.z, e.cur_snap);
                                    Sound;
                                    adjusting_z = FALSE;
                                    MeshCursorW(&e, Fs, &x, &y, &z);
                                    MeshP0Offset(&e, 0, 0, z - p0a.z);
                                }
                                else
                                {
                                    MeshCursorW(&e, Fs, &x, &y, &z);
                                    MeshP0Offset(&e, x - p0a.x, y - p0a.y, z - p0a.z);
                                }
                                MeshTriSelAll(&e, FALSE);
                                MeshVertexSelAll(&e, FALSE);
                                moving = FALSE;
                            }
                            break;

                        case 't':
                            if (tmpv = MeshVertexFindScrPt(&e, arg1, arg2))
                            {
                                for (i = 0; i < make_tri_vertex_num; i++)
                                    if (va[i] == tmpv)
                                    {
                                        Beep;
                                        Beep;
                                        break;
                                    }
                                if (i == make_tri_vertex_num)
                                {
                                    Noise(25, 86, 110);
                                    va[make_tri_vertex_num++] = tmpv;
                                    tmpv->flags |= VF_SEL;
                                    if (make_tri_vertex_num == 3)
                                    {
                                        e.cur_tri = MeshTriNew(&e, e.cur_color, va[0], va[1], va[2]);
                                        for (i = 0; i < make_tri_vertex_num; i++)
                                            va[i]->flags &= ~VF_SEL;
                                        make_tri_vertex_num = 0;
                                    }
                                }
                            }
                            break;
                    }
                    break;

                case MESSAGE_MS_R_DOWN:
                    if (Bt(kbd.down_bitmap, SC_SHIFT))  adjusting_camera_xy = TRUE;
                    if (Bt(kbd.down_bitmap, SC_ALT))    adjusting_camera_z  = TRUE;

                    if (!adjusting_z && e.ed_mode != 'M' &&
                        (e.chain_pred == e.vertex_head.last || e.ed_mode != 'n' && e.ed_mode != 'f' && e.ed_mode != 'p') &&
                        !adjusting_camera_xy && !adjusting_camera_z)
                    {
                        if (moving)
                        {
                            MeshCursorW(&e, Fs, &x, &y, &z);
                            MeshP0Offset(&e, x - p0a.x, y - p0a.y, z - p0a.z);
                            p0a.x = x;
                            p0a.y = y;
                            p0a.z = z;
                            MeshP0Capture(&e);
                        }
                        p0b.x = mouse.presnap.x;
                        p0b.y = mouse.presnap.y;
                        p0b.z = e.mouse_z;
                        adjusting_z = TRUE;
                        Sound(ClampI64(Freq2Ona(3 * e.mouse_z + 1500), 0, I8_MAX));
                    }
                    break;

                case MESSAGE_MS_R_UP:
                    adjusting_camera_xy = FALSE;
                    adjusting_camera_z  = FALSE;
                    if (adjusting_z)
                    {
                        e.mouse_z = RoundI64(Sign(p0b.y - mouse.presnap.y) *
                                             Sqrt(Sqr(p0b.x - mouse.presnap.x) +
                                                  Sqr(p0b.y - mouse.presnap.y)) + p0b.z, e.cur_snap);
                        Sound;
                        adjusting_z = FALSE;
                        if (moving)
                        {
                            MeshCursorW(&e, Fs, &x, &y, &z);
                            MeshP0Offset(&e, 0, 0, z - p0a.z);
                            p0a.x = x;
                            p0a.y = y;
                            p0a.z = z;
                            MeshP0Capture(&e);
                        }
                    }
                    else if (e.ed_mode == 'n')
                    {
                        if (e.chain_pred && e.chain_pred != e.vertex_head.last)
                            MeshPolygon(&e, e.chain_pred->next, e.vertex_head.last, FALSE);
                        arg1 = e.ed_mode;
                        goto me_chain;
                    }
                    else if (e.ed_mode == 'f')
                    {
                        if (e.chain_pred && e.chain_pred != e.vertex_head.last)
                            MeshFence(&e);
                        arg1 = e.ed_mode;
                        goto me_chain;
                    }
                    else if (e.ed_mode == 'p')
                    {
                        if (e.chain_pred && e.chain_pred != e.vertex_head.last)
                            MeshPrism(&e);
                        arg1 = e.ed_mode;
                        goto me_chain;
                    }
                    break;

                case MESSAGE_MS_MOVE:
                    if (adjusting_camera_xy)
                    {
                        // In the editor, X and Y of mouse rotate around Y and X 3D axes.

                        s->sx += mouse.presnap.y - mouse_last.presnap.y;
                        if (s->sx < 0)
                            s->sx = 360; // VIEWANGLES_RANGE
                        else if (s->sx > 360)
                            s->sx = 0;
                        s->sy -= mouse.presnap.x - mouse_last.presnap.x;
                        if (s->sy < 0)
                            s->sy = 360; // VIEWANGLES_RANGE
                        else if (s->sy > 360)
                            s->sy = 0;
                    }
                    if (adjusting_camera_z)
                    {
                        // In the editor, X and Y of mouse rotates around Z 3D axis.

                        s->sz += mouse.presnap.x - mouse_last.presnap.x;
                        s->sz += mouse.presnap.y - mouse_last.presnap.y;
                        if (s->sz < 0)
                            s->sz = 360; // VIEWANGLES_RANGE
                        else if (s->sz > 360)
                            s->sz = 0;
                    }

                    if (adjusting_camera_xy || adjusting_camera_z)
                        break;

                    if (adjusting_z)
                    {
                        e.mouse_z = RoundI64(Sign(p0b.y - mouse.presnap.y) *
                                             Sqrt(Sqr(p0b.x - mouse.presnap.x) +
                                                  Sqr(p0b.y - mouse.presnap.y)) + p0b.z, e.cur_snap);
                        Sound(ClampI64(Freq2Ona(3 * e.mouse_z + 1500), 0, I8_MAX));
                    }
                    if (moving)
                    {
                        MeshCursorW(&e, Fs, &x, &y, &z);
                        if (adjusting_z)
                            MeshP0Offset(&e, 0, 0, z - p0a.z);
                        else
                            MeshP0Offset(&e, x - p0a.x, y - p0a.y, z - p0a.z);
                        p0a.x = x;
                        p0a.y = y;
                        p0a.z = z;
                        MeshP0Capture(&e);
                    }
                    break;
            }
        }
me_done:
    }
    catch
        Fs->catch_except = TRUE;

    SettingsPop;
    MenuPop;
    if (save_and_exit)
        head = MeshSave(&e, _size);
    else
        head = NULL;
    MeshCleanUp(&e);
    FramePtrDel("CMeshFrame");
    if (old_s)
    {
        MemCopy(c->state, old_s, sizeof(CViewAngles));
        Free(old_s);
    }
    else
        ViewAnglesDel;

    return head;
}