// Copyright 2005, Timothy Miller, Tech Source Inc. // This code is licensed under GPL 2.0


#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include "render.hpp"


#define max(a, b) ((a) >? (b)) #define min(a, b) ((a) <? (b))


VerticalState_t VerticalState;


void VerticalRasterize() { while (VerticalState.H) { HorizontalRasterize(VerticalState.X1, VerticalState.X2, VerticalState.Y, VerticalState.W, VerticalState.Ap, VerticalState.Rp, VerticalState.Gp, VerticalState.Bp, VerticalState.As, VerticalState.Rs, VerticalState.Gs, VerticalState.Bs, VerticalState.S1, VerticalState.T1, VerticalState.S2, VerticalState.T2, VerticalState.LOD1, VerticalState.LOD2, VerticalState.F);

        VerticalState.X1 += VerticalState.dX1dY;
        VerticalState.X2 += VerticalState.dX2dY;
        VerticalState.W  += VerticalState.dWdY;
        VerticalState.Ap += VerticalState.dApdY;
        VerticalState.Rp += VerticalState.dRpdY;
        VerticalState.Gp += VerticalState.dGpdY;
        VerticalState.Bp += VerticalState.dBpdY;
        VerticalState.As += VerticalState.dAsdY;
        VerticalState.Rs += VerticalState.dRsdY;
        VerticalState.Gs += VerticalState.dGsdY;
        VerticalState.Bs += VerticalState.dBsdY;
        VerticalState.S1 += VerticalState.dS1dY;
        VerticalState.T1 += VerticalState.dT1dY;
        VerticalState.S2 += VerticalState.dS2dY;
        VerticalState.T2 += VerticalState.dT2dY;
        VerticalState.LOD1 += VerticalState.dLOD1dY;
        VerticalState.LOD2 += VerticalState.dLOD2dY;
        VerticalState.F  += VerticalState.dFdY;

        VerticalState.Y++;
        VerticalState.H--;
    }
}


HorizontalState_t HorizontalState;


void HorizontalRasterize( float25 X1f, float25 X2f, unsigned short Y, float25 W, float25 Ap, float25 Rp, float25 Gp, float25 Bp, float25 As, float25 Rs, float25 Gs, float25 Bs, float25 S1, float25 T1, float25 S2, float25 T2, float25 LOD1, float25 LOD2, float25 F) { unsigned short X1, X2; float25 adjust;

    //printf("Y=%d, X1=%f, X2=%f\n", Y, X1f, X2f);

    // Round
    X1 = (int)ceilf(X1f);
    X2 = (int)ceilf(X2f);
    if (HorizontalState.draw_right_edge) X2++;
    adjust = X1-X1f;

    // Adjust
    W  += HorizontalState.dWdX * adjust;
    Ap += HorizontalState.dApdX * adjust;
    Rp += HorizontalState.dRpdX * adjust;
    Gp += HorizontalState.dGpdX * adjust;
    Bp += HorizontalState.dBpdX * adjust;
    As += HorizontalState.dAsdX * adjust;
    Rs += HorizontalState.dRsdX * adjust;
    Gs += HorizontalState.dGsdX * adjust;
    Bs += HorizontalState.dBsdX * adjust;
    S1 += HorizontalState.dS1dX * adjust;
    T1 += HorizontalState.dT1dX * adjust;
    S2 += HorizontalState.dS2dX * adjust;
    T2 += HorizontalState.dT2dX * adjust;
    LOD1 += HorizontalState.dLOD1dX * adjust;
    LOD2 += HorizontalState.dLOD2dX * adjust;
    F  += HorizontalState.dFdX * adjust;


while (X1 != X2) { ScissorTest(HorizontalState.swap_xy ? Y : X1, HorizontalState.swap_xy ? X1 : Y, W, Ap, Rp, Gp, Bp, As, Rs, Gs, Bs, S1, T1, S2, T2, LOD1, LOD2, F);

        W  += HorizontalState.dWdX;
        Ap += HorizontalState.dApdX;
        Rp += HorizontalState.dRpdX;
        Gp += HorizontalState.dGpdX;
        Bp += HorizontalState.dBpdX;
        As += HorizontalState.dAsdX;
        Rs += HorizontalState.dRsdX;
        Gs += HorizontalState.dGsdX;
        Bs += HorizontalState.dBsdX;
        S1 += HorizontalState.dS1dX;
        T1 += HorizontalState.dT1dX;
        S2 += HorizontalState.dS2dX;
        T2 += HorizontalState.dT2dX;
        LOD1 += HorizontalState.dLOD1dX;
        LOD2 += HorizontalState.dLOD2dX;
        F  += HorizontalState.dFdX;

        X1++;
    }
}



ScissorState_t ScissorState;

void ScissorTest(
    unsigned short X, unsigned short Y, float25 W,
    float25 Ap, float25 Rp, float25 Gp, float25 Bp,
    float25 As, float25 Rs, float25 Gs, float25 Bs,
    float25 S1, float25 T1,
    float25 S2, float25 T2,
    float25 LOD1, float25 LOD2, float25 F)
{
    //printf("Scissor:  X=%d, Y=%d, color=%f %f %f\n", X, Y, Rp, Gp, Bp);
    if (ScissorState.enable) {
        if (X < ScissorState.Left) return;
        if (X >= ScissorState.Right) return;

        // OpenGL goes bottom to top, so some adjustment needs
        // to be made to these numbers.
        if (Y < ScissorState.Top) return;
        if (Y >= ScissorState.Bottom) return;
    }

    OwnershipTest(X, Y, W, Ap, Rp, Gp, Bp,
            As, Rs, Gs, Bs, S1, T1, S2, T2, LOD1, LOD2, F);
}



// To save bandwidth, perhaps it would be better to use 8-bit values
// for ownership numbers.  Is it worth the effort?
// Also, this design cannot draw to 8-bit pixels, so if you want to use
// the renderer to fill the ownership buffer, it gets complicated.
// In that case, you fill the body of an ownership region with a solid color
// in four-pixel units, then draw the left and right edges using the same
// color, but using a planemask/bytemask so as not to disturb adjacent
// windows which also may not lie on 4-pixel boundaries.
OwnershipState_t OwnershipState;

void OwnershipTest(unsigned short X, unsigned short Y, float25 W,
    float25 Ap, float25 Rp, float25 Gp, float25 Bp,
    float25 As, float25 Rs, float25 Gs, float25 Bs,
    float25 S1, float25 T1,
    float25 S2, float25 T2,
    float25 LOD1, float25 LOD2, float25 F)
{
    // Either we compute Addr here and then again later, or we
    // carry it with us through the rest of the pipeline.
    unsigned int Addr = OwnershipState.Pitch*Y + X;

    if (OwnershipState.enable) {
        if (OwnershipState.pBuffer[Addr] != OwnershipState.Ref) return;
    }

    ApplyTexture(X, Y, W, Ap, Rp, Gp, Bp,
            As, Rs, Gs, Bs, S1, T1, S2, T2, LOD1, LOD2, F);
}



// Where should we put the 2D 32x32 stipple?  Here???
// (1) leave-background stipple:  throw away fragments which
//     correspond to zeros in stipple.
// (2) keep-background stipple:  swap primary and secondary colors

// We could also do a generalized 1D line stippler here for X11.


// We can do 1-bit fonts with the 32x32 stipple, but does the texture // unit have enough functionality to handle 8-bit antialiazed fonts? // In that case, I think what we want to do is modulate the alpha channel // of the primary color based on the texture contents.



TextureState_t TextureState1, TextureState2;


static float25 extractA(unsigned int pix) { return (pix>>24) / 255.0; }

static float25 extractR(unsigned int pix)
{
    return (0xff&(pix>>16)) / 255.0;
}

static float25 extractG(unsigned int pix)
{
    return (0xff&(pix>>8)) / 255.0;
}

static float25 extractB(unsigned int pix)
{
    return (0xff&pix) / 255.0;
}



// ALERT:  U and V are SIGNED!

unsigned int SampleTexture(
    unsigned int *pTex, unsigned short Pitch,
    short U, short V,
    unsigned short Umask, unsigned short Vmask,
    unsigned short Umirror, unsigned short Vmirror,
    unsigned short Umode, unsigned short Vmode,
    unsigned int border_color)
{
    switch (Umode) {
    case REPEAT:
        U &= Umask;
        break;
    case MIRRORED_REPEAT:
        U &= Umask;
        if (U & Umirror) U ^= Umask;
        break;
    case CLAMP_TO_EDGE:
        if (U < 0) U = 0;
        if (U > Umask) U = Umask;
        break;
    case CLAMP_TO_BORDER:
        if (U < 0 || U > Umask) return border_color;
        break;
    }

    switch (Vmode) {
    case REPEAT:
        V &= Vmask;
        break;
    case MIRRORED_REPEAT:
        V &= Vmask;
        if (V & Vmirror) V ^= Vmask;
        break;
    case CLAMP_TO_EDGE:
        if (V < 0) V = 0;
        if (V > Vmask) V = Vmask;
        break;
    case CLAMP_TO_BORDER:
        if (V < 0 || V > Vmask) return border_color;
        break;
    }

    return pTex[V*Pitch + U];
}



// NOTE:  don't do excess lookups
static void LinearLookup(float25 U, float25 V,
    unsigned short Umask, unsigned short Vmask,
    unsigned short Umirror, unsigned short Vmirror,
    unsigned short Umode, unsigned short Vmode,
    unsigned int *pTex, unsigned short TexPitch,
    unsigned int border_color,
    float25& At, float25& Rt, float25& Gt, float25& Bt)
{
    short Ui, Vi, Uj, Vj;
    float25 Uf, Vf;
    unsigned int tex_data0, tex_data1;
    float25 Au, Ru, Gu, Bu;

    Ui = (int)floorf(U);
    Vi = (int)floorf(V);
    Uj = (int)ceilf(U);
    Vj = (int)ceilf(V);
    Uf = U-Ui;
    Vf = V-Vi;

    tex_data0 = SampleTexture(pTex, TexPitch,
        Ui, Vi, Umask, Vmask, Umirror, Vmirror, Umode, Vmode,
        border_color);
    tex_data1 = SampleTexture(pTex, TexPitch,
        Uj, Vi, Umask, Vmask, Umirror, Vmirror, Umode, Vmode,
        border_color);

    // need to deal with different formats
    At = extractA(tex_data0) * (1.0-Uf) + extractA(tex_data1) * Uf;
    Rt = extractR(tex_data0) * (1.0-Uf) + extractR(tex_data1) * Uf;
    Gt = extractG(tex_data0) * (1.0-Uf) + extractG(tex_data1) * Uf;
    Bt = extractB(tex_data0) * (1.0-Uf) + extractB(tex_data1) * Uf;

    tex_data0 = SampleTexture(pTex, TexPitch,
        Ui, Vj, Umask, Vmask, Umirror, Vmirror, Umode, Vmode,
        border_color);
    tex_data1 = SampleTexture(pTex, TexPitch,
        Uj, Vj, Umask, Vmask, Umirror, Vmirror, Umode, Vmode,
        border_color);

    Au = extractA(tex_data0) * (1.0-Uf) + extractA(tex_data1) * Uf;
    Ru = extractR(tex_data0) * (1.0-Uf) + extractR(tex_data1) * Uf;
    Gu = extractG(tex_data0) * (1.0-Uf) + extractG(tex_data1) * Uf;
    Bu = extractB(tex_data0) * (1.0-Uf) + extractB(tex_data1) * Uf;

    At = (At*(1.0-Vf) + Au*Vf);
    Rt = (Rt*(1.0-Vf) + Ru*Vf);
    Gt = (Gt*(1.0-Vf) + Gu*Vf);
    Bt = (Bt*(1.0-Vf) + Bu*Vf);
}



static unsigned int ComputeMipOffset(unsigned int TexSize, int MipLevel)
{
    unsigned int offset = 0;

    while (MipLevel) {
        MipLevel--;
        offset += TexSize;
        TexSize >>= 2;
    }

    return offset;
}



template<class T>
T clamp(T val, T vmin, T vmax)
{
    if (val < vmin) val = vmin;
    if (val > vmax) val = vmax;
    return val;
}

float25 clamp(float25 f)
{
    if (f > 1.0) f = 1.0;
    if (f < 0.0) f = 0.0;
    return f;
}



// NOTE:  Need to make sure that right-shifting mask too far doesn't
// cause a problem.


// NOTE: Be sure to handle floor and ceil correctly for negative numbers // so that when they're converted to 2's compliment, they mask properly!

// NOTE:  For now, I am leaving out separate magify filtering.
//        All filtering is controlled by the minification filter mode.
static void TextureFilter(float25 U, float25 V, float25 LOD, float25 M,
    const TextureState_t& State,
    float25& At, float25& Rt, float25& Gt, float25& Bt)
{
    float25 Au, Ru, Gu, Bu;
    float25 mip_level, lf, Uf, Vf;
    short Ui, Vi;
    short Uj, Vj;
    unsigned short l0, l1;
    unsigned int offset;
    unsigned int tex_data0;

    Ui = (short)roundf(U);
    Vi = (short)roundf(V);

    switch (State.FilterMode) {
    case NO_TEXTURE:
        At = 0;
        Rt = 0;
        Gt = 0;
        Bt = 0;
        break;

    case NEAREST:
        tex_data0 = SampleTexture(State.pBuffer, State.Pitch,
            Ui, Vi, State.Umask, State.Vmask,
            State.Umirror, State.Vmirror,
            State.Umode, State.Vmode,
            State.border_color);
        At = extractA(tex_data0);
        Rt = extractR(tex_data0);
        Gt = extractG(tex_data0);
        Bt = extractB(tex_data0);
        break;

    case LINEAR:
        LinearLookup(U, V, State.Umask, State.Vmask,
            State.Umirror, State.Vmirror,
            State.Umode, State.Vmode, State.pBuffer, State.Pitch,
            State.border_color,
            At, Rt, Gt, Bt);
        break;

    case NEAREST_MIPMAP_NEAREST:
        mip_level = log2(LOD*M*M);

        l0 = clamp((short)roundf(mip_level), State.MIPmin, State.MIPmax);
        Ui >>= l0;
        Vi >>= l0;
        offset = ComputeMipOffset(State.Size, l0);

        tex_data0 = SampleTexture(State.pBuffer+offset,
            State.Pitch>>l0,
            Ui, Vi, State.Umask>>l0, State.Vmask>>l0,
            State.Umirror>>l0, State.Vmirror>>l0,
            State.Umode, State.Vmode,
            State.border_color);
        At = extractA(tex_data0);
        Rt = extractR(tex_data0);
        Gt = extractG(tex_data0);
        Bt = extractB(tex_data0);
        break;

    case NEAREST_MIPMAP_LINEAR:
        mip_level = log2(LOD*M*M);

        l0 = clamp((short)floorf(mip_level), State.MIPmin, State.MIPmax);
        lf = l0 - mip_level;  // if clamp, problem here?
        Uj = Ui >> l0;
        Vj = Vi >> l0;
        offset = ComputeMipOffset(State.Size, l0);

        tex_data0 = SampleTexture(State.pBuffer+offset,
            State.Pitch>>l0,
            Uj, Vj, State.Umask>>l0, State.Vmask>>l0,
            State.Umirror>>l0, State.Vmirror>>l0,
            State.Umode, State.Vmode,
            State.border_color);
        At = extractA(tex_data0);
        Rt = extractR(tex_data0);
        Gt = extractG(tex_data0);
        Bt = extractB(tex_data0);

        l1 = clamp((short)ceilf(mip_level), State.MIPmin, State.MIPmax);
        if (l0 != l1) {
            Uj = Ui >> l1;
            Vj = Vi >> l1;
            offset = ComputeMipOffset(State.Size, l1);

            tex_data0 = SampleTexture(State.pBuffer+offset,
                State.Pitch>>l1,
                Uj, Vj, State.Umask>>l1, State.Vmask>>l1,
                State.Umirror>>l1, State.Vmirror>>l1,
                State.Umode, State.Vmode,
                State.border_color);
            Au = extractA(tex_data0);
            Ru = extractR(tex_data0);
            Gu = extractG(tex_data0);
            Bu = extractB(tex_data0);

            At = (1-lf)*At + lf*Au;
            Rt = (1-lf)*Rt + lf*Ru;
            Gt = (1-lf)*Gt + lf*Gu;
            Bt = (1-lf)*Bt + lf*Bu;
        }
        break;

case LINEAR_MIPMAP_NEAREST:
mip_level = log2(LOD*M*M);
l0 = clamp((short)roundf(mip_level), State.MIPmin, State.MIPmax);
Uf = U / (1<<l0);
Vf = V / (1<<l0);
offset = ComputeMipOffset(State.Size, l0);
LinearLookup(Uf, Vf, State.Umask>>l0, State.Vmask>>l0,
State.Umirror>>l0, State.Vmirror>>l0,
State.Umode, State.Vmode, State.pBuffer+offset, State.Pitch>>l0,
State.border_color,
At, Rt, Gt, Bt);
break;


    case LINEAR_MIPMAP_LINEAR:
        mip_level = log2(LOD*M*M);

        l0 = clamp((short)floorf(mip_level), State.MIPmin, State.MIPmax);
        lf = l0 - mip_level;
        Uf = U / (1<<l0);
        Vf = V / (1<<l0);
        offset = ComputeMipOffset(State.Size, l0);

LinearLookup(Uf, Vf, State.Umask>>l0, State.Vmask>>l0,
State.Umirror>>l0, State.Vmirror>>l0,
State.Umode, State.Vmode, State.pBuffer+offset, State.Pitch>>l0,
State.border_color,
At, Rt, Gt, Bt);


        l1 = clamp((short)ceilf(mip_level), State.MIPmin, State.MIPmax);
        if (l1 != l0) {
            Uf = U / (1<<l1);
            Vf = V / (1<<l1);
            offset = ComputeMipOffset(State.Size, l1);

LinearLookup(Uf, Vf, State.Umask>>l1, State.Vmask>>l1,
State.Umirror>>l1, State.Vmirror>>l1,
State.Umode, State.Vmode, State.pBuffer+offset, State.Pitch>>l1,
State.border_color,
Au, Ru, Gu, Bu);


            At = (1-lf)*At + lf*Au;
            Rt = (1-lf)*Rt + lf*Ru;
            Gt = (1-lf)*Gt + lf*Gu;
            Bt = (1-lf)*Bt + lf*Bu;
        }
        break;
    }
}


static void ColorAlphaCombiner( // primary float25 Af, float25 Rf, float25 Gf, float25 Bf, // previous float25 Ap, float25 Rp, float25 Gp, float25 Bp, // const float25 Ac, float25 Rc, float25 Gc, float25 Bc, // texture stage 0 float25 As0, float25 Rs0, float25 Gs0, float25 Bs0, // texture stage 1 float25 As1, float25 Rs1, float25 Gs1, float25 Bs1, // argument selectors unsigned short ColorSel0, unsigned short ColorSel1, unsigned short ColorSel2, unsigned short ColorSel3, unsigned short AlphaSel0, unsigned short AlphaSel1, unsigned short AlphaSel2, unsigned short AlphaSel3, // Combine functions unsigned short ColorFunc, unsigned short AlphaFunc, // Results float25& Av, float25& Rv, float25& Gv, float25& Bv) { // Collect available arguments float25 AT[16], RT[16], GT[16], BT[16];

    RT[0] = Rf;         GT[0] = Gf;         BT[0] = Bf;
    RT[1] = Rp;         GT[1] = Gp;         BT[1] = Bp;
    RT[2] = Rc;         GT[2] = Gc;         BT[2] = Bc;
    RT[3] = Rs0;        GT[3] = Gs0;        BT[3] = Bs0;
    RT[4] = Rs1;        GT[4] = Gs1;        BT[4] = Bs1;

    RT[8] = Af;         GT[8] = Af;         BT[8] = Af;
    RT[9] = Ap;         GT[9] = Ap;         BT[9] = Ap;
    RT[10] = Ac;        GT[10] = Ac;        BT[10] = Ac;
    RT[11] = As0;       GT[11] = As0;       BT[11] = As0;
    RT[12] = As1;       GT[12] = As1;       BT[12] = As1;

    AT[0] = Af;         AT[1] = Ap;         AT[2] = Ac;
    AT[3] = As0;        AT[4] = As1;


// Select arguments float25 A[4], R[4], G[4], B[4];

    R[0] = RT[ColorSel0&15];    if (ColorSel0&16) R[0] = 1-R[0];
    G[0] = GT[ColorSel0&15];    if (ColorSel0&16) G[0] = 1-G[0];
    B[0] = BT[ColorSel0&15];    if (ColorSel0&16) B[0] = 1-B[0];

    R[1] = RT[ColorSel1&15];    if (ColorSel1&16) R[1] = 1-R[1];
    G[1] = GT[ColorSel1&15];    if (ColorSel1&16) G[1] = 1-G[1];
    B[1] = BT[ColorSel1&15];    if (ColorSel1&16) B[1] = 1-B[1];

    R[2] = RT[ColorSel2&15];    if (ColorSel2&16) R[2] = 1-R[2];
    G[2] = GT[ColorSel2&15];    if (ColorSel2&16) G[2] = 1-G[2];
    B[2] = BT[ColorSel2&15];    if (ColorSel2&16) B[2] = 1-B[2];

    R[3] = RT[ColorSel3&15];    if (ColorSel3&16) R[3] = 1-R[3];
    G[3] = GT[ColorSel3&15];    if (ColorSel3&16) G[3] = 1-G[3];
    B[3] = BT[ColorSel3&15];    if (ColorSel3&16) B[3] = 1-B[3];

    A[0] = AT[AlphaSel0&15];    if (AlphaSel0&16) A[0] = 1-A[0];
    A[1] = AT[AlphaSel1&15];    if (AlphaSel1&16) A[1] = 1-A[1];
    A[2] = AT[AlphaSel2&15];    if (AlphaSel2&16) A[2] = 1-A[2];
    A[3] = AT[AlphaSel3&15];    if (AlphaSel3&16) A[3] = 1-A[3];


// Compute dot product float25 dot; dot = (R[0]-0.5) * (R[1]-0.5); dot += (G[0]-0.5) * (G[1]-0.5); dot += (B[0]-0.5) * (B[1]-0.5); dot *= 4.0;

    // Combine arguments
    switch (ColorFunc) {
    case 0:     // Replace
        Rv = R[0];      Gv = G[0];      Bv = B[0];
        break;
    case 1:     // Modulate
        Rv = R[0]*R[1]; Gv = G[0]*G[1]; Bv = B[0]*B[1];
        break;
    case 2:     // Add
        Rv = R[0]+R[1]; Gv = G[0]+G[1]; Bv = B[0]+B[1];
        break;
    case 3:     // Subtract
        Rv = R[0]-R[1]; Gv = G[0]-G[1]; Bv = B[0]-B[1];
        break;
    case 4:     // Add signed
        Rv = R[0]+R[1]-0.5;
        Gv = G[0]+G[1]-0.5;
        Bv = B[0]+B[1]-0.5;
        break;
    case 5:     // Blend/Interpolate/Decal
        Rv = R[0]*R[1] + R[2]*R[3];
        Gv = G[0]*G[1] + G[2]*G[3];
        Bv = B[0]*B[1] + B[2]*B[3];
        break;
    case 6:     // Dot product
        Rv = Gv = Bv = dot;
        break;
    }


switch (AlphaFunc) { case 0: // Replace Av = A[0]; break; case 1: // Modulate Av = A[0]*A[1]; break; case 2: // Add Av = A[0]+A[1]; break; case 3: // Subtract Av = A[0]-A[1]; break; case 4: // Add signed Av = A[0]+A[1]-0.5; break; case 5: // Blend/Interpolate/Decal Av = A[0]*A[1] + A[2]*A[3]; break; case 6: // Dot product Av = dot; break; } }


void ApplyTexture(unsigned short X, unsigned short Y, float25 W, float25 Ap, float25 Rp, float25 Gp, float25 Bp, float25 As, float25 Rs, float25 Gs, float25 Bs, float25 S1, float25 T1, float25 S2, float25 T2, float25 LOD1, float25 LOD2, float25 F) { // Perspective correct everything float25 M = 1/W; S1 *= M; T1 *= M; S2 *= M; T2 *= M; Ap *= M; Rp *= M; Gp *= M; Bp *= M; As *= M; Rs *= M; Gs *= M; Bs *= M; F *= M;


// Texture stage 1 float25 At1, Rt1, Gt1, Bt1; TextureFilter(S1, T1, LOD1, M, TextureState1, At1, Rt1, Gt1, Bt1);

    // Texture stage 2
    float25 At2, Rt2, Gt2, Bt2;
    TextureFilter(S2, T2, LOD2, M, TextureState2,
        At2, Rt2, Gt2, Bt2);


// Combine stage 1
float25 Av1, Rv1, Gv1, Bv1;
ColorAlphaCombiner(
Ap, Rp, Gp, Bp,
Ap, Rp, Gp, Bp,
TextureState1.A, TextureState1.R, TextureState1.G, TextureState1.B,
At1, Rt1, Gt1, Bt1,
At2, Rt2, Gt2, Bt2,
TextureState1.ColorSel0, TextureState1.ColorSel1,
TextureState1.ColorSel2, TextureState1.ColorSel3,
TextureState1.AlphaSel0, TextureState1.AlphaSel1,
TextureState1.AlphaSel2, TextureState1.AlphaSel3,
TextureState1.ColorFunc, TextureState1.AlphaFunc,
Av1, Rv1, Gv1, Bv1);


// Combine stage 2
float25 Av2, Rv2, Gv2, Bv2;
ColorAlphaCombiner(
Ap, Rp, Gp, Bp,
Av1, Rv1, Gv1, Bv1,
TextureState2.A, TextureState2.R, TextureState2.G, TextureState2.B,
At1, Rt1, Gt1, Bt1,
At2, Rt2, Gt2, Bt2,
TextureState2.ColorSel0, TextureState2.ColorSel1,
TextureState2.ColorSel2, TextureState2.ColorSel3,
TextureState2.AlphaSel0, TextureState2.AlphaSel1,
TextureState2.AlphaSel2, TextureState2.AlphaSel3,
TextureState2.ColorFunc, TextureState2.AlphaFunc,
Av2, Rv2, Gv2, Bv2);


    ColorSum(X, Y, W, Av2, Rv2, Gv2, Bv2,
        As, Rs, Gs, Bs, F);
}




ColorsumState_t ColorsumState;

void ColorSum(unsigned short X, unsigned short Y, float25 W,
    float25 Ap, float25 Rp, float25 Gp, float25 Bp,
    float25 As, float25 Rs, float25 Gs, float25 Bs,
    float25 F)
{
    if (ColorsumState.enable) {
        Ap += As;
        Rp += Rs;
        Gp += Gs;
        Bp += Bs;
    }

    Fog(X, Y, W, Ap, Rp, Gp, Bp, F);
}



FogState_t FogState;
// We only support linear fog.  Perspecitive correction may or may not
// be applied.

void Fog(unsigned short X, unsigned short Y, float25 W,
    float25 Ap, float25 Rp, float25 Gp, float25 Bp,
    float25 F)
{
    F = clamp(F);

    if (FogState.enable) {
        Ap = (1-F)*Ap + F*FogState.A;
        Rp = (1-F)*Rp + F*FogState.R;
        Gp = (1-F)*Gp + F*FogState.G;
        Bp = (1-F)*Bp + F*FogState.B;
    }

    AlphaTest(X, Y, W, Ap, Rp, Gp, Bp);
}



AlphaState_t AlphaState;


void AlphaTest(unsigned short X, unsigned short Y, float25 W, float25 Ap, float25 Rp, float25 Gp, float25 Bp) { // Convert Ap to framebuffer format unsigned char A; A = (int)roundf(clamp(Ap)*255);

    // Compute Alpha comparisons
    int AlphaResult = 1;
    if (AlphaState.enable) {
        // This ordering will change, depending on actual OpenGL constants
        switch (AlphaState.Func&6) {
        case 0:
            AlphaResult = 1;
            break;
        case 2:
            AlphaResult = A == AlphaState.Ref;
            break;
        case 4:
            AlphaResult = A > AlphaState.Ref;
            break;
        case 6:
            AlphaResult = A < AlphaState.Ref;
            break;
        }
        AlphaResult ^= (AlphaState.Func & 1);
    }

    if (!AlphaResult) return;
    StencilTest(X, Y, W, Ap, Rp, Gp, Bp);
}





// Note: Stencil/Depth gets its own pitch so that the depth buffer
// can be matched to a window, but for that, we need window-relative
// coordinates. Although they don't have to be, we adopt the convention
// of using window-relative coordinates when the depth/stencil buffer
// does not match the dimensions of the screen. Appropriate offsets
// need to be added to base addresses.
// Front and back facing options have to be set per-triangle by the driver.
// The whole stencil state pertaining to front/back can fit into less than
// 32 bits. If it becomes an issue, we can store both states in the hardware
// and let it be selected by a single bit. Otherwise, we can just reload
// that part of the state whenever the facing direction of the triangle
// changes.
StencilState_t StencilState;



static unsigned char ComputeStencilChange(unsigned char val, unsigned short op)
{
switch (op) {
case ZERO:
val = 0;
break;
case REPLACE:
val = StencilState.StencilRef;
break;
case INCR:
if (val < 255) val++;
break;
case DECR:
if (val > 0) val--;
break;
case INVERT:
val ^= 0xff;
break;
case INCR_WRAP:
val++;
break;
case DECR_WRAP:
val--;
break;
}
return val;
}



// Stencil test, Depth test void StencilTest(unsigned short X, unsigned short Y, float25 W, float25 Ap, float25 Rp, float25 Gp, float25 Bp) { unsigned int stencil_data = StencilState.StencilConst; unsigned int old_stencil_data = stencil_data; unsigned int stencil_addr; unsigned char stencil_buf_val = 0; float25 Wbuffer = 0; bool fail = false, StencilPass, DepthPass; unsigned short StencilOp;

    // Fetch from stencil buffer
    stencil_addr = Y*StencilState.Pitch + X;
    if (StencilState.enable_stencil_read) {
        stencil_data = StencilState.pBuffer[stencil_addr];
        old_stencil_data = stencil_data;
    }

    stencil_buf_val = stencil_data & 0xff;
    stencil_data &= ~0xff;
    Wbuffer = *(float25 *)&stencil_data;
    // In hardware, convert from float24 to float25 may
    // involve replicating lowest bit??


// By default, we take DepthOpPass, so if both stencil and // depth testing are disabled, this is the operator we use. StencilOp = StencilState.DepthOpPass; StencilPass = true;

    // Compute stencil comparison
    if (StencilState.enable_stencil_test) {
        unsigned char stencil_arg0, stencil_arg1;
        stencil_arg0 = stencil_buf_val & StencilState.StencilMask;
        stencil_arg1 = StencilState.StencilRef & StencilState.StencilMask;
        switch (StencilState.StencilFunc&6) {
        case 0:
            StencilPass = 1;
            break;
        case 2:
            StencilPass = stencil_arg1 == stencil_arg0;
            break;
        case 4:
            StencilPass = stencil_arg1 > stencil_arg0;
            break;
        case 6:
            StencilPass = stencil_arg1 < stencil_arg0;
            break;
        }
        StencilPass ^= (StencilState.StencilFunc & 1);

        if (!StencilPass) fail = 1;

        StencilOp = StencilPass ? StencilState.DepthOpPass :
            StencilState.StencilOpFail;
    }


if (StencilState.enable_depth_test && StencilPass) { switch (StencilState.DepthFunc&6) { case 0: DepthPass = 1; break; case 2: DepthPass = W == Wbuffer; break; case 4: DepthPass = W > Wbuffer; break; case 6: DepthPass = W < Wbuffer; break; } DepthPass ^= (StencilState.DepthFunc & 1);

        if (!DepthPass) fail = 1;
        if (DepthPass && StencilState.enable_depth_write) {
            Wbuffer = W;
        }

        // This just clobbers the stencil test op
        StencilOp = DepthPass ? StencilState.DepthOpPass :
            StencilState.DepthOpFail;
    }

    stencil_buf_val = ComputeStencilChange(stencil_buf_val, StencilOp);

    // Compute new stencil buffer data
    // In hardware, will want to round Wbuffer to float24 bits
    stencil_data = *(int *)&Wbuffer;
    stencil_data &= ~0xff;
    stencil_data |= stencil_buf_val & 0xff;

    // Write back to stencil buffer
    if (StencilState.enable_stencil_write &&
            (!StencilState.enable_stencil_read ||
                stencil_data != old_stencil_data)) {
        StencilState.pBuffer[stencil_addr] = stencil_data;
    }

    if (fail) return;

    DestMerge(X, Y, Ap, Rp, Gp, Bp);
}




BlendState_t BlendState; // By convention, coordinates are window-relative. Therefore, the // base address of the dest framebuffer should be offset by // (windowX + ScreenPitch*windowY).


static void ComputeColorBlendFactors( unsigned short func, float25 As, float25 Rs, float25 Gs, float25 Bs, float25 Ad, float25 Rd, float25 Gd, float25 Bd, float25 Ac, float25 Rc, float25 Gc, float25 Bc, float25& R, float25& G, float25& B) { switch (func) { case 0: R = G = B = 0; break; case 1: R = Rs; G = Gs; B = Bs; break; case 2: R = Rd; G = Gd; B = Bd; break; case 3: R = G = B = As; break; case 4: R = G = B = Ad; break; case 5: R = Rc; G = Gc, B = Bc; break; case 6: R = G = B = Ac; break; case 7: R = G = B = min(As, 1.0-Ad); break; }

    if (func & 8) {
        R = 1.0 - R;
        G = 1.0 - G;
        B = 1.0 - B;
    }
}


static float25 ComputeAlphaBlendFactor( unsigned short func, float25 As, float25 Ad, float25 Ac) { float25 A;

    switch (func) {
    case 0:
        A = 0;
        break;
    case 1:
        A = As;
        break;
    case 2:
        A = Ad;
        break;
    case 3:
        A = Ac;
        break;
    }

    if (func & 4) A = 1.0 - A;

    return A;
}



static void Blend(
    float25 As, float25 Rs, float25 Gs, float25 Bs,
    float25 Ad, float25 Rd, float25 Gd, float25 Bd,
    float25& A, float25& R, float25& G, float25& B)
{
    float25 Sr, Sg, Sb, Dr, Dg, Db, Sa, Da;

    // Compute source weights
    ComputeColorBlendFactors(BlendState.SourceColorFunc,
        As, Rs, Gs, Bs, Ad, Rd, Gd, Bd,
        BlendState.Ac, BlendState.Rc, BlendState.Gc, BlendState.Bc,
        Sr, Sg, Sb);
    Sa = ComputeAlphaBlendFactor(BlendState.SourceAlphaFunc,
        As, Ad, BlendState.Ac);

    // Compute dest weights
    ComputeColorBlendFactors(BlendState.DestColorFunc,
        As, Rs, Gs, Bs, Ad, Rd, Gd, Bd,
        BlendState.Ac, BlendState.Rc, BlendState.Gc, BlendState.Bc,
        Dr, Dg, Db);
    Da = ComputeAlphaBlendFactor(BlendState.DestAlphaFunc,
        As, Ad, BlendState.Ac);

    // Combine
    switch (BlendState.EquationColorMode) {
    case NONE:
        R = Rs;
        G = Gs;
        B = Bs;
        break;
    case FUNC_ADD:
        R = Rs*Sr + Rd*Dr;
        G = Gs*Sg + Gd*Dg;
        B = Bs*Sb + Bd*Db;
        break;
    case FUNC_SUBTRACT:
        R = Rs*Sr - Rd*Dr;
        G = Gs*Sg - Gd*Dg;
        B = Bs*Sb - Bd*Db;
        break;
    case FUNC_REVERSE_SUBTRACT:
        R = -Rs*Sr + Rd*Dr;
        G = -Gs*Sg + Gd*Dg;
        B = -Bs*Sb + Bd*Db;
        break;
    case MIN:
        R = min(Rs, Rd);
        G = min(Gs, Gd);
        B = min(Bs, Bd);
        break;
    case MAX:
        R = max(Rs, Rd);
        G = max(Gs, Gd);
        B = max(Bs, Bd);
        break;
    }

    switch (BlendState.EquationAlphaMode) {
    case NONE:
        A = As;
        break;
    case FUNC_ADD:
        A = As*Sa + Ad*Da;
        break;
    case FUNC_SUBTRACT:
        A = As*Sa - Ad*Da;
        break;
    case FUNC_REVERSE_SUBTRACT:
        A = -As*Sa + Ad*Da;
        break;
    case MIN:
        A = min(As, Ad);
        break;
    case MAX:
        A = max(As, Ad);
        break;
    }
}


static unsigned int LogicRopMerge(unsigned int SrcVal, unsigned int DestVal, unsigned short rop, unsigned int pmsk) { unsigned int src_bit, dst_bit, rop_selector, rop_result; unsigned int mask_bit, i; unsigned int result = 0;

    for (i=0; i<32; i++) {
        src_bit = (SrcVal >> i) & 1;
        dst_bit = (DestVal >> i) & 1;
        rop_selector = dst_bit | (src_bit << 1);
        rop_result = (rop >> rop_selector) & 1;
        mask_bit = (pmsk >> i) & 1;
        rop_result = mask_bit ? rop_result : dst_bit;

        result |= rop_result << i;
    }

    return result;
}


void DestMerge(unsigned short X, unsigned short Y, float25 Ap, float25 Rp, float25 Gp, float25 Bp) { unsigned int Addr = X + Y*BlendState.Pitch;


unsigned int DestVal = BlendState.DestDefault; if (BlendState.enable_read) { DestVal = BlendState.pBuffer[Addr]; } unsigned int OldDest = DestVal;


// Blend source with dest Blend(Ap, Rp, Gp, Bp, extractA(DestVal), extractR(DestVal), extractG(DestVal), extractB(DestVal), Ap, Rp, Gp, Bp);


// Convert blended value to fixed point unsigned int SrcVal; SrcVal = (int)roundf(clamp(Bp)*255); SrcVal |= (int)roundf(clamp(Gp)*255) << 8; SrcVal |= (int)roundf(clamp(Rp)*255) << 16; SrcVal |= (int)roundf(clamp(Ap)*255) << 24;

//printf("Blend: X=%d, Y=%d, color=%f %f %f = 0x%08x\n", X, Y, Rp, Gp, Bp,
// SrcVal);


    // Apply logic op and planemask
    DestVal = LogicRopMerge(SrcVal, DestVal, BlendState.LogicROP,
        BlendState.Planemask);


// In hardware, we can do byte-oriented planemasks without // requiring a read. Although the model doesn't use it, the hardware // will do something like this computation: unsigned char byteMask; byteMask = !!(BlendState.Planemask & 0xff); byteMask = (!!(BlendState.Planemask & 0xff00)) << 1; byteMask = (!!(BlendState.Planemask & 0xff0000)) << 2; byteMask = (!!(BlendState.Planemask & 0xff000000)) << 3;

    if (BlendState.enable_write &&
            (!BlendState.enable_read || OldDest!=DestVal)) {
        //printf("Writing 0x%08x to 0x%04x\n", DestVal, Addr);
        BlendState.pBuffer[Addr] = DestVal;
    }
}



void InitializeRenderer()
{
    HorizontalState.draw_right_edge = false;
    HorizontalState.swap_xy = false;

    ScissorState.enable = false;
    OwnershipState.enable = false;

    TextureState1.FilterMode = NO_TEXTURE;
    TextureState1.ColorSel0  = 1;
    TextureState1.AlphaSel0  = 1;

    TextureState2.FilterMode = NO_TEXTURE;
    TextureState2.ColorSel0  = 1;
    TextureState2.AlphaSel0  = 1;

    ColorsumState.enable = false;
    FogState.enable = false;
    AlphaState.enable = false;
    StencilState.enable_stencil_read = false;
    StencilState.enable_stencil_write = false;
    StencilState.enable_stencil_test = false;
    StencilState.enable_depth_test = false;
    StencilState.enable_depth_write = false;

    BlendState.LogicROP = 0xc;
    BlendState.Planemask = 0xffffffff;
    BlendState.enable_read = false;
    BlendState.enable_write = true;
    BlendState.EquationColorMode = NONE;
    BlendState.EquationAlphaMode = NONE;
}


void write_ppm(char *fname, int W, int H, unsigned int *ptr) { FILE *out;

    out = fopen(fname, "w");
    fprintf(out, "P3\n# CREATOR: Open Graphics Project\n");
    fprintf(out, "%d %d\n255\n", W, H);

    int x, y;
    unsigned int rgb;

for (y=0; y<H; y++) {
for (x=0; x<W; x++) {
rgb = *ptr++;
fprintf(out, "%d %d %d ", (rgb>>16)&0xff, (rgb>>8)&0xff, rgb&0xff);
}
fprintf(out, "\n");
}


    fclose(out);
}



int main(int argc, char *argv[])
{
    BlendState.pBuffer = new unsigned int[256*256];
    memset(BlendState.pBuffer, 0, 256*256*4);

    InitializeRenderer();
    VerticalState.Y = 28;
    VerticalState.H = 200;
    VerticalState.X1 = 128;
    VerticalState.X2 = 128;
    VerticalState.dX1dY = -0.5;
    VerticalState.dX2dY = 0.5;
    VerticalState.W = 1;
    VerticalState.dWdY = 0;
    VerticalState.Ap = 0;
    VerticalState.Rp = 1.0;
    VerticalState.Gp = 0;
    VerticalState.Bp = 0;
    VerticalState.dApdY = 0;
    VerticalState.dRpdY = -1.0/200.0;
    VerticalState.dGpdY = +1.0/200.0;
    VerticalState.dBpdY = 0;

    HorizontalState.dWdX = 0;
    HorizontalState.dApdX = 0;
    HorizontalState.dRpdX = 0;
    HorizontalState.dGpdX = -1.0/200.0;
    HorizontalState.dBpdX = +1.0/200.0;

    BlendState.Pitch = 256;

    VerticalRasterize();

    write_ppm("image.ppm", 256, 256, BlendState.pBuffer);
}
_______________________________________________
Open-graphics mailing list
[email protected]
http://lists.duskglow.com/mailman/listinfo/open-graphics
List service provided by Duskglow Consulting, LLC (www.duskglow.com)

Reply via email to