//=================================================================
//
// Copyright (c) Microsoft Corporation 1999 - All Rights Reserved.
//
//=================================================================

//-----------------------------------------------------------------------------
//
// _GOURAUD_       Define this to interpolate the intensity accross the polygon.
// _AFFINE_        Define this to use affine texture mapping.
// _PERSPECTIVE_   Define this to use perspective texture mapping.
// _ZBUFFER_       Define this to support z-buffers.
// _FOG_           Define this to support fog.
//
//-----------------------------------------------------------------------------

void _INIT_FN_NAME_(void)
{
    // use pointers for convenience...
    GRFVERTEX * pgvV0 = &g_grdRenderData.gvVertices[0];
    GRFVERTEX * pgvV1 = &g_grdRenderData.gvVertices[1];
    GRFVERTEX * pgvV2 = &g_grdRenderData.gvVertices[2];

    // calc the gradients
	float Y0minusY2 = Fixed28_4ToFloat(pgvV0->y - pgvV2->y);
	float Y1minusY2 = Fixed28_4ToFloat(pgvV1->y - pgvV2->y);
	float X0minusX2 = Fixed28_4ToFloat(pgvV0->x - pgvV2->x);
	float X1minusX2 = Fixed28_4ToFloat(pgvV1->x - pgvV2->x);

    float oodx = 1.0F/(X1minusX2*Y0minusY2 - X0minusX2*Y1minusY2);
	float oody = -oodx;

#if defined(_ZBUFFER_)
	float Z1minusZ2 = pgvV1->z - pgvV2->z;
	float Z0minusZ2 = pgvV0->z - pgvV2->z;

	g_grdRenderData.dZdx = oodx * (Z1minusZ2*Y0minusY2 - Z0minusZ2*Y1minusY2);
	g_grdRenderData.dZdy = oody * (Z1minusZ2*X0minusX2 - Z0minusZ2*X1minusX2);
#endif

#if defined(_GOURAUD_)
    FIX5_5 dwBlueIncr, dwGreenIncr, dwRedIncr, dwAlphaIncr;

#if defined(_FOG_)
    // scale down shading to account for shader intensity lost to fog
    DWORD   dwF0 = FOGINV(pgvV0->specular);
    DWORD   dwF1 = FOGINV(pgvV1->specular);
    DWORD   dwF2 = FOGINV(pgvV2->specular);
#endif

    // scale down to 5.5 for 5-bit pixels

    unsigned long color0 = BLUE(pgvV0->color);
    unsigned long color1 = BLUE(pgvV1->color);
    unsigned long color2 = BLUE(pgvV2->color);

#if defined(_FOG_)
    // scale down shading to account for shader intensity lost to fog. Store changes back
    // in vertex so edges get created with scaled-down shading
    color0 = MUL_INT8_TIMES_INT8_WITH_FIXUP(color0, dwF0);
    pgvV0->color = (pgvV0->color & 0xFFFFFF00) | color0;
    color1 = MUL_INT8_TIMES_INT8_WITH_FIXUP(color1, dwF1);
    pgvV1->color = (pgvV1->color & 0xFFFFFF00) | color1;
    color2 = MUL_INT8_TIMES_INT8_WITH_FIXUP(color2, dwF2);
    pgvV2->color = (pgvV2->color & 0xFFFFFF00) | color2;
#endif

	float B1minusB2 = (float)(long)(color1 - color2);
	float B0minusB2 = (float)(long)(color0 - color2);

	g_grdRenderData.f16dBdx = FloatToFixed16_16(oodx * (B1minusB2*Y0minusY2 - B0minusB2*Y1minusY2));
	g_grdRenderData.f16dBdy = FloatToFixed16_16(oody * (B1minusB2*X0minusX2 - B0minusB2*X1minusX2));

    if (g_grdRenderData.f16dBdx >= 0)
        dwBlueIncr = g_grdRenderData.f16dBdx >> 14;
    else
        dwBlueIncr = (g_grdRenderData.f16dBdx + 0x3FFF) >> 14;

    g_grdChunkData.giIncrement_Chunk = BUILD_GI(dwBlueIncr);

    if (!(g_grdRenderData.dwState & STATE_SHADEI))
    {
        color0 = GREEN(pgvV0->color);
        color1 = GREEN(pgvV1->color);
        color2 = GREEN(pgvV2->color);

#if defined(_FOG_)
        // scale down shading to account for shader intensity lost to fog. Store changes back
        // in vertex so edges get created with scaled-down shading
        color0 = MUL_INT8_TIMES_INT8_WITH_FIXUP(color0, dwF0);
        pgvV0->color = (pgvV0->color & 0xFFFF00FF) | (color0 << 8);
        color1 = MUL_INT8_TIMES_INT8_WITH_FIXUP(color1, dwF1);
        pgvV1->color = (pgvV1->color & 0xFFFF00FF) | (color1 << 8);
        color2 = MUL_INT8_TIMES_INT8_WITH_FIXUP(color2, dwF2);
        pgvV2->color = (pgvV2->color & 0xFFFF00FF) | (color2 << 8);
#endif

	    float G1minusG2 = (float)(long)(color1 - color2);
	    float G0minusG2 = (float)(long)(color0 - color2);

	    g_grdRenderData.f16dGdx = FloatToFixed16_16(oodx * (G1minusG2*Y0minusY2 - G0minusG2*Y1minusY2));
	    g_grdRenderData.f16dGdy = FloatToFixed16_16(oody * (G1minusG2*X0minusX2 - G0minusG2*X1minusX2));

        if (g_grdRenderData.f16dGdx >= 0)
            dwGreenIncr = g_grdRenderData.f16dGdx >> 14;
        else
            dwGreenIncr = (g_grdRenderData.f16dGdx + 0x3FFF) >> 14;

        color0 = RED(pgvV0->color);
        color1 = RED(pgvV1->color);
        color2 = RED(pgvV2->color);

#if defined(_FOG_)
        // scale down shading to account for shader intensity lost to fog. Store changes back
        // in vertex so edges get created with scaled-down shading
        color0 = MUL_INT8_TIMES_INT8_WITH_FIXUP(color0, dwF0);
        pgvV0->color = (pgvV0->color & 0xFF00FFFF) | (color0 << 16);
        color1 = MUL_INT8_TIMES_INT8_WITH_FIXUP(color1, dwF1);
        pgvV1->color = (pgvV1->color & 0xFF00FFFF) | (color1 << 16);
        color2 = MUL_INT8_TIMES_INT8_WITH_FIXUP(color2, dwF2);
        pgvV2->color = (pgvV2->color & 0xFF00FFFF) | (color2 << 16);
#endif

	    float R1minusR2 = (float)(long)(color1 - color2);
	    float R0minusR2 = (float)(long)(color0 - color2);

	    g_grdRenderData.f16dRdx = FloatToFixed16_16(oodx * (R1minusR2*Y0minusY2 - R0minusR2*Y1minusY2));
	    g_grdRenderData.f16dRdy = FloatToFixed16_16(oody * (R1minusR2*X0minusX2 - R0minusR2*X1minusX2));

        if (g_grdRenderData.f16dRdx >= 0)
            dwRedIncr = g_grdRenderData.f16dRdx >> 14;
        else
            dwRedIncr = (g_grdRenderData.f16dRdx + 0x3FFF) >> 14;

        g_grdChunkData.grbIncrement_Chunk = BUILD_GRB(dwRedIncr, dwBlueIncr);
        g_grdChunkData.ggbIncrement_Chunk = BUILD_GGB(dwGreenIncr, dwBlueIncr);
        g_grdChunkData.grgbIncrement_Chunk = BUILD_GRGB(dwRedIncr, dwGreenIncr, dwBlueIncr);

        if (!(g_grdRenderData.dwState & STATE_SHADEALPHA))
        {
            g_grdChunkData.gagIncrement_Chunk = BUILD_GG(dwGreenIncr);
            g_grdChunkData.garIncrement_Chunk = BUILD_GR(dwRedIncr);
        }
    }

    if (g_grdRenderData.dwState & STATE_SHADEALPHA)
    {
        color0 = ALPHA(pgvV0->color);
        color1 = ALPHA(pgvV1->color);
        color2 = ALPHA(pgvV2->color);

	    float A1minusA2 = (float)(long)(color1 - color2);
	    float A0minusA2 = (float)(long)(color0 - color2);

	    g_grdRenderData.f16dAdx = FloatToFixed16_16(oodx * (A1minusA2*Y0minusY2 - A0minusA2*Y1minusY2));
	    g_grdRenderData.f16dAdy = FloatToFixed16_16(oody * (A1minusA2*X0minusX2 - A0minusA2*X1minusX2));

        if (g_grdRenderData.f16dAdx >= 0)
            dwAlphaIncr = g_grdRenderData.f16dAdx >> 14;
        else
            dwAlphaIncr = (g_grdRenderData.f16dAdx + 0x3FFF) >> 14;

        g_grdChunkData.gagIncrement_Chunk = BUILD_GAG(dwAlphaIncr, dwGreenIncr);
        g_grdChunkData.gaIncrement_Chunk = BUILD_GA(g_grdRenderData.f16dAdx);
        g_grdChunkData.garIncrement_Chunk = BUILD_GAR(dwAlphaIncr, dwRedIncr);
    }
#endif

#if defined(_FOG_)
    dwF0 = FOG(pgvV0->specular);

    if (g_grdRenderData.fFogIsFlat)
    {
        // the fog color and intensity are constant for the whole triangle, so do the
        // calculation of the fog component of each pixel's color now
        g_grdChunkData.m3FogColorFlattened = MUL_MUNGED3_TIMES_MUNGED1_WITH_FIXUP(g_grdChunkData.m3FogColor, dwF0 >> 3);
    }
    else
    {
        dwF1 = FOG(pgvV1->specular);
        dwF2 = FOG(pgvV2->specular);

	    float F1minusF2 = (float)(long)(dwF1 - dwF2);
	    float F0minusF2 = (float)(long)(dwF0 - dwF2);

	    g_grdRenderData.f16dFdx = FloatToFixed16_16(oodx * (F1minusF2*Y0minusY2 - F0minusF2*Y1minusY2));
	    g_grdRenderData.f16dFdy = FloatToFixed16_16(oody * (F1minusF2*X0minusX2 - F0minusF2*X1minusX2));

        FIX5_5 dwFogIncr;

        // scale down to 5.5 for 5-bit pixels

        if (g_grdRenderData.f16dFdx >= 0)
            dwFogIncr = g_grdRenderData.f16dFdx >> 14;
        else
            dwFogIncr = (g_grdRenderData.f16dFdx + 0x3FFF) >> 14;

        g_grdChunkData.giFogIncrement_Chunk = BUILD_GI(dwFogIncr);
    }
#endif

#if defined(_AFFINE_) || defined(_PERSPECTIVE_)
	float dWidthMask  = (float)g_grdChunkData.cxTextureMask;
	float dHeightMask = (float)g_grdChunkData.cyTextureMask;
#endif

#if defined(_AFFINE_)
    float U1minusU2 = pgvV1->tu - pgvV2->tu;
    float U0minusU2 = pgvV0->tu - pgvV2->tu;
    g_grdRenderData.dudx = oodx * (U1minusU2*Y0minusY2 - U0minusU2*Y1minusY2);
	g_grdRenderData.dudy = oody * (U1minusU2*X0minusX2 - U0minusU2*X1minusX2);

    float V1minusV2 = pgvV1->tv - pgvV2->tv;
    float V0minusV2 = pgvV0->tv - pgvV2->tv;
    g_grdRenderData.dvdx = oodx*(V1minusV2*Y0minusY2 - V0minusV2*Y1minusY2);
	g_grdRenderData.dvdy = oody*(V1minusV2*X0minusX2 - V0minusV2*X1minusX2);

    g_grdChunkData.fix16du_Chunk = FloatToFixed16_16(g_grdRenderData.dudx);
    g_grdRenderData.fix16du_Chunk_X16 = g_grdChunkData.fix16du_Chunk << 4;
    g_grdChunkData.fix16dv_Chunk = FloatToFixed16_16(g_grdRenderData.dvdx);
    g_grdRenderData.fix16dv_Chunk_X16 = g_grdChunkData.fix16dv_Chunk << 4;

	//
	// Set up rounding modifiers.  These rounding modifiers will determine whether we
	//   do a floor or a ceil in the grf_asm_affine_scan_line code.
	//
	// See Hecker's gradient.txt for the detailed description of why this is necessary
	//

	FIX16_16 const PosModifier = 0;
	FIX16_16 const NegModifier = -1;

	if (g_grdRenderData.dudx > 0)
	{
		// Use floor.
		g_grdRenderData.f16dudxModifier = PosModifier;
	} 
	else if (g_grdRenderData.dudx < 0)
	{
		// Use ceiling.
		g_grdRenderData.f16dudxModifier = NegModifier;
	}
	else if (g_grdRenderData.dudy >= 0)
	{
		// Use floor.
		g_grdRenderData.f16dudxModifier = PosModifier;
	}
	else
	{
		// Use ceiling.
		g_grdRenderData.f16dudxModifier = NegModifier;
	}

	if (g_grdRenderData.dvdx > 0)
	{
		// Use floor.	
		g_grdRenderData.f16dvdxModifier = PosModifier;
	} 
	else if (g_grdRenderData.dvdx < 0)
	{
		// Use ceiling.
		g_grdRenderData.f16dvdxModifier = NegModifier;
	}
	else if (g_grdRenderData.dvdy >= 0)
	{
		// Use floor.
		g_grdRenderData.f16dvdxModifier = PosModifier;
	}
	else
	{
		// Use ceiling. 
		g_grdRenderData.f16dvdxModifier = NegModifier;
	}
#elif defined(_PERSPECTIVE_)
    g_grdRenderData.rguow[0] = g_grdRenderData.gvVertices[0].tu *
            g_grdRenderData.gvVertices[0].rhw; // rhw = 0/z
    g_grdRenderData.rgvow[0] = g_grdRenderData.gvVertices[0].tv *
            g_grdRenderData.gvVertices[0].rhw;

    g_grdRenderData.rguow[1] = g_grdRenderData.gvVertices[1].tu *
            g_grdRenderData.gvVertices[1].rhw; // rhw = 1/z
    g_grdRenderData.rgvow[1] = g_grdRenderData.gvVertices[1].tv *
            g_grdRenderData.gvVertices[1].rhw;

    g_grdRenderData.rguow[2] = g_grdRenderData.gvVertices[2].tu *
            g_grdRenderData.gvVertices[2].rhw; // rhw = 2/z
    g_grdRenderData.rgvow[2] = g_grdRenderData.gvVertices[2].tv *
            g_grdRenderData.gvVertices[2].rhw;

	float RHW1minusRHW2 = pgvV1->rhw - pgvV2->rhw;
	float RHW0minusRHW2 = pgvV0->rhw - pgvV2->rhw;

	g_grdRenderData.doowdx = oodx * (RHW1minusRHW2*Y0minusY2 - RHW0minusRHW2*Y1minusY2);
	g_grdRenderData.doowdy = oody * (RHW1minusRHW2*X0minusX2 - RHW0minusRHW2*X1minusX2);

    float v1uowMinusV2uow = g_grdRenderData.rguow[1] - g_grdRenderData.rguow[2];
    float v0uowMinusV2uow = g_grdRenderData.rguow[0] - g_grdRenderData.rguow[2];
	g_grdRenderData.duowdx = oodx * 
		                  (v1uowMinusV2uow * Y0minusY2 - v0uowMinusV2uow * Y1minusY2);
	g_grdRenderData.duowdy = oody *
                          (v1uowMinusV2uow * X0minusX2 - v0uowMinusV2uow * X1minusX2);

    float v1vowMinusV2vow = g_grdRenderData.rgvow[1]-g_grdRenderData.rgvow[2];
    float v0vowMinusV2vow = g_grdRenderData.rgvow[0]-g_grdRenderData.rgvow[2];
	g_grdRenderData.dvowdx = oodx *
                          (v1vowMinusV2vow * Y0minusY2 - v0vowMinusV2vow * Y1minusY2);
	g_grdRenderData.dvowdy = oody *
                          (v1vowMinusV2vow * X0minusX2 - v0vowMinusV2vow * X1minusX2);

	g_grdRenderData.doowdx16 = g_grdRenderData.doowdx * MAX_SPAN_BLOCK_LEN;
	g_grdRenderData.duowdx16 = g_grdRenderData.duowdx * MAX_SPAN_BLOCK_LEN;
	g_grdRenderData.dvowdx16 = g_grdRenderData.dvowdx * MAX_SPAN_BLOCK_LEN;

	//
	// set up rounding modifiers
	//
	// see Hecker's gradient.txt for the detailed description of why this is necessary
	//

	FIX16_16 const PosModifier = 0;
	FIX16_16 const NegModifier = 0xffffffff;

	float dudxIndicator = g_grdRenderData.duowdx * pgvV0->rhw - g_grdRenderData.rguow[0] * g_grdRenderData.doowdx;
	if(dudxIndicator > 0)
	{
		g_grdRenderData.f16dudxModifier = PosModifier;
	}
	else if(dudxIndicator < 0)
	{
		g_grdRenderData.f16dudxModifier = NegModifier;
	}
	else // dudx == 0
	{
		float dudyIndicator = g_grdRenderData.duowdy * pgvV0->rhw - g_grdRenderData.rguow[0] * g_grdRenderData.doowdy;
        g_grdRenderData.f16dudxModifier = (dudyIndicator >= 0) ? PosModifier : NegModifier;
	}


	float dvdxIndicator = g_grdRenderData.dvowdx * pgvV0->rhw - g_grdRenderData.rgvow[0] * g_grdRenderData.doowdx;
	if(dvdxIndicator > 0)
	{
		g_grdRenderData.f16dvdxModifier = PosModifier;
	}
	else if(dvdxIndicator < 0)
	{
		g_grdRenderData.f16dvdxModifier = NegModifier;
	}
	else // dvdx == 0
	{
		float dvdyIndicator = g_grdRenderData.dvowdy * pgvV0->rhw - g_grdRenderData.rgvow[0] * g_grdRenderData.doowdy;
        g_grdRenderData.f16dvdxModifier = (dvdyIndicator >= 0) ? PosModifier : NegModifier;
	}
#endif
}

