#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 = ZCAlloc(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 = ZCAlloc(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;
}