/*
 * Copyright (C) 1996-1997 Id Software, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.
 *
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */
// r_aclip.c: clip routines for drawing Alias models directly to the screen

#include "quakedef.h"
#include "r_local.h"
#include "d_local.h"

static finalvert_t fv[2][8];
static auxvert_t av[8];

void          R_AliasProjectFinalVert (finalvert_t * fv, auxvert_t * av);
void          R_Alias_clip_top (finalvert_t * pfv0, finalvert_t * pfv1, finalvert_t * out);
void          R_Alias_clip_bottom (finalvert_t * pfv0, finalvert_t * pfv1, finalvert_t * out);
void          R_Alias_clip_left (finalvert_t * pfv0, finalvert_t * pfv1, finalvert_t * out);
void          R_Alias_clip_right (finalvert_t * pfv0, finalvert_t * pfv1, finalvert_t * out);

/*
 * R_Alias_clip_z
 *
 * pfv0 is the unclipped vertex, pfv1 is the z-clipped vertex
 */
void R_Alias_clip_z (finalvert_t * pfv0, finalvert_t * pfv1, finalvert_t * out) {
    float         scale;
    auxvert_t    *pav0, *pav1, avout;

    pav0 = &av[pfv0 - &fv[0][0]];
    pav1 = &av[pfv1 - &fv[0][0]];

    if (pfv0->v[1] >= pfv1->v[1]) {
        scale = (ALIAS_Z_CLIP_PLANE - pav0->fv[2]) / (pav1->fv[2] - pav0->fv[2]);

        avout.fv[0] = pav0->fv[0] + (pav1->fv[0] - pav0->fv[0]) * scale;
        avout.fv[1] = pav0->fv[1] + (pav1->fv[1] - pav0->fv[1]) * scale;
        avout.fv[2] = ALIAS_Z_CLIP_PLANE;

        out->v[2] = pfv0->v[2] + (pfv1->v[2] - pfv0->v[2]) * scale;
        out->v[3] = pfv0->v[3] + (pfv1->v[3] - pfv0->v[3]) * scale;
        out->v[4] = pfv0->v[4] + (pfv1->v[4] - pfv0->v[4]) * scale;
    } else {
        scale = (ALIAS_Z_CLIP_PLANE - pav1->fv[2]) / (pav0->fv[2] - pav1->fv[2]);

        avout.fv[0] = pav1->fv[0] + (pav0->fv[0] - pav1->fv[0]) * scale;
        avout.fv[1] = pav1->fv[1] + (pav0->fv[1] - pav1->fv[1]) * scale;
        avout.fv[2] = ALIAS_Z_CLIP_PLANE;

        out->v[2] = pfv1->v[2] + (pfv0->v[2] - pfv1->v[2]) * scale;
        out->v[3] = pfv1->v[3] + (pfv0->v[3] - pfv1->v[3]) * scale;
        out->v[4] = pfv1->v[4] + (pfv0->v[4] - pfv1->v[4]) * scale;
    }

    R_AliasProjectFinalVert (out, &avout);

    if (out->v[0] < r_refdef.aliasvrect.x)
        out->flags |= ALIAS_LEFT_CLIP;
    if (out->v[1] < r_refdef.aliasvrect.y)
        out->flags |= ALIAS_TOP_CLIP;
    if (out->v[0] > r_refdef.aliasvrectright)
        out->flags |= ALIAS_RIGHT_CLIP;
    if (out->v[1] > r_refdef.aliasvrectbottom)
        out->flags |= ALIAS_BOTTOM_CLIP;
}

#if	!id386

void R_Alias_clip_left (finalvert_t * pfv0, finalvert_t * pfv1, finalvert_t * out) {
    float         scale;
    int           i;

    if (pfv0->v[1] >= pfv1->v[1]) {
        scale = (float) (r_refdef.aliasvrect.x - pfv0->v[0]) / (pfv1->v[0] - pfv0->v[0]);
        for (i = 0; i < 6; i++)
            out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5;
    } else {
        scale = (float) (r_refdef.aliasvrect.x - pfv1->v[0]) / (pfv0->v[0] - pfv1->v[0]);
        for (i = 0; i < 6; i++)
            out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5;
    }
}

void R_Alias_clip_right (finalvert_t * pfv0, finalvert_t * pfv1, finalvert_t * out) {
    float         scale;
    int           i;

    if (pfv0->v[1] >= pfv1->v[1]) {
        scale = (float) (r_refdef.aliasvrectright - pfv0->v[0]) / (pfv1->v[0] - pfv0->v[0]);
        for (i = 0; i < 6; i++)
            out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5;
    } else {
        scale = (float) (r_refdef.aliasvrectright - pfv1->v[0]) / (pfv0->v[0] - pfv1->v[0]);
        for (i = 0; i < 6; i++)
            out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5;
    }
}

void R_Alias_clip_top (finalvert_t * pfv0, finalvert_t * pfv1, finalvert_t * out) {
    float         scale;
    int           i;

    if (pfv0->v[1] >= pfv1->v[1]) {
        scale = (float) (r_refdef.aliasvrect.y - pfv0->v[1]) / (pfv1->v[1] - pfv0->v[1]);
        for (i = 0; i < 6; i++)
            out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5;
    } else {
        scale = (float) (r_refdef.aliasvrect.y - pfv1->v[1]) / (pfv0->v[1] - pfv1->v[1]);
        for (i = 0; i < 6; i++)
            out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5;
    }
}

void R_Alias_clip_bottom (finalvert_t * pfv0, finalvert_t * pfv1, finalvert_t * out) {
    float         scale;
    int           i;

    if (pfv0->v[1] >= pfv1->v[1]) {
        scale = (float) (r_refdef.aliasvrectbottom - pfv0->v[1]) / (pfv1->v[1] - pfv0->v[1]);

        for (i = 0; i < 6; i++)
            out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5;
    } else {
        scale = (float) (r_refdef.aliasvrectbottom - pfv1->v[1]) / (pfv0->v[1] - pfv1->v[1]);

        for (i = 0; i < 6; i++)
            out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5;
    }
}

#endif

int R_AliasClip (finalvert_t * in, finalvert_t * out, int flag, int count,
                 void (*clip) (finalvert_t * pfv0, finalvert_t * pfv1, finalvert_t * out)) {
    int           i, j, k;
    int           flags, oldflags;

    j = count - 1;
    k = 0;
    for (i = 0; i < count; j = i, i++) {
        oldflags = in[j].flags & flag;
        flags = in[i].flags & flag;

        if (flags && oldflags)
            continue;
        if (oldflags ^ flags) {
            clip (&in[j], &in[i], &out[k]);
            out[k].flags = 0;
            if (out[k].v[0] < r_refdef.aliasvrect.x)
                out[k].flags |= ALIAS_LEFT_CLIP;
            if (out[k].v[1] < r_refdef.aliasvrect.y)
                out[k].flags |= ALIAS_TOP_CLIP;
            if (out[k].v[0] > r_refdef.aliasvrectright)
                out[k].flags |= ALIAS_RIGHT_CLIP;
            if (out[k].v[1] > r_refdef.aliasvrectbottom)
                out[k].flags |= ALIAS_BOTTOM_CLIP;
            k++;
        }
        if (!flags) {
            out[k] = in[i];
            k++;
        }
    }

    return k;
}

/*
 * R_AliasClipTriangle
 */
void R_AliasClipTriangle (mtriangle_t * ptri) {
    int           i, k, pingpong;
    mtriangle_t   mtri;
    unsigned      clipflags;

    // copy vertexes and fix seam texture coordinates
    if (ptri->facesfront) {
        fv[0][0] = pfinalverts[ptri->vertindex[0]];
        fv[0][1] = pfinalverts[ptri->vertindex[1]];
        fv[0][2] = pfinalverts[ptri->vertindex[2]];
    } else {
        for (i = 0; i < 3; i++) {
            fv[0][i] = pfinalverts[ptri->vertindex[i]];

            if (!ptri->facesfront && (fv[0][i].flags & ALIAS_ONSEAM))
                fv[0][i].v[2] += r_affinetridesc.seamfixupX16;
        }
    }

    // clip
    clipflags = fv[0][0].flags | fv[0][1].flags | fv[0][2].flags;

    if (clipflags & ALIAS_Z_CLIP) {
        for (i = 0; i < 3; i++)
            av[i] = pauxverts[ptri->vertindex[i]];

        k = R_AliasClip (fv[0], fv[1], ALIAS_Z_CLIP, 3, R_Alias_clip_z);
        if (k == 0)
            return;

        pingpong = 1;
        clipflags = fv[1][0].flags | fv[1][1].flags | fv[1][2].flags;
    } else {
        pingpong = 0;
        k = 3;
    }

    if (clipflags & ALIAS_LEFT_CLIP) {
        k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], ALIAS_LEFT_CLIP, k, R_Alias_clip_left);
        if (k == 0)
            return;

        pingpong ^= 1;
    }

    if (clipflags & ALIAS_RIGHT_CLIP) {
        k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], ALIAS_RIGHT_CLIP, k, R_Alias_clip_right);
        if (k == 0)
            return;

        pingpong ^= 1;
    }

    if (clipflags & ALIAS_BOTTOM_CLIP) {
        k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], ALIAS_BOTTOM_CLIP, k, R_Alias_clip_bottom);
        if (k == 0)
            return;

        pingpong ^= 1;
    }

    if (clipflags & ALIAS_TOP_CLIP) {
        k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], ALIAS_TOP_CLIP, k, R_Alias_clip_top);
        if (k == 0)
            return;

        pingpong ^= 1;
    }

    for (i = 0; i < k; i++) {
        if (fv[pingpong][i].v[0] < r_refdef.aliasvrect.x)
            fv[pingpong][i].v[0] = r_refdef.aliasvrect.x;
        else if (fv[pingpong][i].v[0] > r_refdef.aliasvrectright)
            fv[pingpong][i].v[0] = r_refdef.aliasvrectright;

        if (fv[pingpong][i].v[1] < r_refdef.aliasvrect.y)
            fv[pingpong][i].v[1] = r_refdef.aliasvrect.y;
        else if (fv[pingpong][i].v[1] > r_refdef.aliasvrectbottom)
            fv[pingpong][i].v[1] = r_refdef.aliasvrectbottom;

        fv[pingpong][i].flags = 0;
    }

    // draw triangles
    mtri.facesfront = ptri->facesfront;
    r_affinetridesc.ptriangles = &mtri;
    r_affinetridesc.pfinalverts = fv[pingpong];

    // FIXME: do all at once as trifan?
    mtri.vertindex[0] = 0;
    for (i = 1; i < k - 1; i++) {
        mtri.vertindex[1] = i;
        mtri.vertindex[2] = i + 1;
        D_PolysetDraw ();
    }
}
