/****************************************************************************
 * RAGE128 Chapter 6 Sample Code                                            *
 *                                                                          *
 * zbuffer.c - Functions to manage z and stencil buffers and set their      *
 *             states.                                                      * 
 *                                                                          *
 * Copyright (c) 1999 ATI Technologies Inc.  All rights reserved.           *
 ****************************************************************************/

#include <stdio.h>
#include <string.h>
#include "..\..\util\regdef.h"
#include "..\..\util\defines.h"
#include "..\..\util\main.h"
#include "..\..\util\cce.h"
#include "cntx3d.h"
#include "zbuffer.h"
#include "memmgr.h"


// Z and stencil buffer information.

typedef struct tagZSTENSTATE {
	DWORD Offset;
	DWORD Pitch;
	EZSTENFORMAT Format;
	EZMODE ZMode;
	ECMP ZCmp;
	DWORD ZMask;
	DWORD StencilRef;
	ECMP StencilCmp;
	DWORD StencilMask;
	DWORD StencilWriteMask;
	DWORD StencilClearMask;
    ESTENCILOP StencilFailOp;
    ESTENCILOP StencilZPassOp;
    ESTENCILOP StencilZFailOp;
} ZSTENSTATE;

static ZSTENSTATE gZStenState = {0};

extern CONTEXT3D gContext3D;
extern DWORD gCommandBuf[];


/****************************************************************************
 * R128_CreateZStencilBuffer                                                *
 *  Function: Create a z, or combination z and stencil, buffer.             *
 *    Inputs: ZStenFormat - EZSTENFORMAT enum specifying the stecil format. *
 *   Outputs: TRUE - successful.                                            *
 *			  FALSE - unsuccessful.											*
 ****************************************************************************/

BOOL R128_CreateZStencilBuffer (EZSTENFORMAT ZStenFormat)
{
    DWORD i;
	DWORD z_sten_cntl_state;
	DWORD bpp;
	DWORD zoffset;

	memset (&gZStenState, 0, sizeof (gZStenState));

	// Validate the z/stencil format and set the bitdepth and 
	// z/stencil mask parameters.

	switch (ZStenFormat)
	{

	// Set 16 bit z depth.

    case ZSTENFORMAT_Z16:

		gZStenState.ZMask = 0x0000ffff;
		gZStenState.StencilClearMask = 0x00000000;
		z_sten_cntl_state = Z_STEN_CNTL_Z_PIX_WIDTH_16;
		bpp = 2;
		break;

	// Set 24 bit z depth.

    case ZSTENFORMAT_Z24:

		gZStenState.ZMask = 0x00ffffff;
		gZStenState.StencilClearMask = 0x00000000;
		z_sten_cntl_state = Z_STEN_CNTL_Z_PIX_WIDTH_24;
		bpp = 3;
		break;

	// Set 32 bit z depth.

	case ZSTENFORMAT_Z32:

		gZStenState.ZMask = 0xffffffff;
		gZStenState.StencilClearMask = 0x00000000;
		z_sten_cntl_state = Z_STEN_CNTL_Z_PIX_WIDTH_32;
		bpp = 4;
		break;

	// Set 8 bit stencil depth/24 bit z depth.

	case ZSTENFORMAT_Z24_STEN8:

		gZStenState.ZMask = 0x00ffffff;
		gZStenState.StencilClearMask = 0xff000000;
		z_sten_cntl_state = Z_STEN_CNTL_Z_PIX_WIDTH_24;
		bpp = 4;
		break;

	default:
		return FALSE;
	}

	// Allocate a buffer in video memory for the z/stencil buffer.

	zoffset = R128_AllocBuffer (R128_AdapterInfo.xres * bpp *
		R128_AdapterInfo.yres);
	if (zoffset == 0xffffffff)
		return FALSE;
                 
    gZStenState.Offset = zoffset; 
	gZStenState.Pitch = R128_AdapterInfo.pitch;
	gZStenState.Format = ZStenFormat;

	// Send following in packet 0.

	// Set Z_OFFSET_C and Z_PITCH_C.

	i = 0;
    gCommandBuf[i++] = CCE_PACKET0 | (Z_OFFSET_C >> 2);
	gCommandBuf[i++] = gContext3D.regZ_OFFSET_C = gZStenState.Offset;
	gCommandBuf[i++] = gContext3D.regZ_PITCH_C = gZStenState.Pitch;

	// Set Z_STEN_CNTL_C.

	gContext3D.regZ_STEN_CNTL_C &= ~(0x00000003 << 1);
	gContext3D.regZ_STEN_CNTL_C |= z_sten_cntl_state;
	gCommandBuf[i++] = gContext3D.regZ_STEN_CNTL_C; 

    gCommandBuf[0] |= ((i - 2) << 16);
    R128_CCESubmitPackets (gCommandBuf, i);

	return TRUE;
}


/****************************************************************************
 * R128_ClearZBuffer                                                        * 
 *  Function: Clear the z buffer by doing a colorfill through a Type-3      *
 *            CCE_PACKET3_CNTL_PAINT packet.                                * 
 *    Inputs: ZClearValue - value to clear the z buffer with.               *
 *   Outputs: TRUE - successful.                                            *
 *			  FALSE - unsuccessful.											*
 ****************************************************************************/

BOOL R128_ClearZBuffer (DWORD ZClearValue)
{
	DWORD dst_type_state, save_dp_write_mask;
    int i;
    DWORD temp_tex_cntl_c;

	// Validate z buffer format and set depth parameter.

	switch (gZStenState.Format)
	{
	case ZSTENFORMAT_Z16:

		dst_type_state = CCE_GC_DST_16BPP;
		break;

    case ZSTENFORMAT_Z24: 

        dst_type_state = CCE_GC_DST_24BPP;
		break;

	case ZSTENFORMAT_Z32:
    case ZSTENFORMAT_Z24_STEN8:

		dst_type_state = CCE_GC_DST_32BPP;
		break;

	default:
		return FALSE;
	}

	// Save the current DP_WRITE_MASK value.

    R128_WaitForFifo (1);
	save_dp_write_mask = regr (DP_WRITE_MASK);

	// Set the DP_WRITE_MASK value according to the z format mask. This
	// will ensure that only the z buffer bits are cleared. This is 
	// especially necessary to preserve the stencil bits in a combined
	// z/stencil buffer, where the z buffer is in the lower 24 bits,
	// and the stencil buffer in the upper eight bits.

	i = 0;
    gCommandBuf[i++] = CCE_PACKET0 | (DP_WRITE_MASK >> 2);
    gCommandBuf[i++] = gZStenState.ZMask;
    R128_CCESubmitPackets (gCommandBuf, i);

    // Temprarily turn off Z_EN, STEN_EN and TEX_EN.

    temp_tex_cntl_c = gContext3D.regTEX_CNTL_C &
        ~((0x00000001) | (0x00000001 << 3) | (0x00000001 << 4));
	i = 0;
    gCommandBuf[i++] = CCE_PACKET0 | (TEX_CNTL_C >> 2);
    gCommandBuf[i++] = temp_tex_cntl_c;
    R128_CCESubmitPackets (gCommandBuf, i);

	// Do a colorfill operation to clear the z bits. This is done
	// through a Type-3 CCE_PACKET3_CNTL_PAINT packet.

	i = 0;
    gCommandBuf[i++] = CCE_PACKET3_CNTL_PAINT;
    gCommandBuf[i++] =
        CCE_GC_SRC_PITCH_OFFSET_DEFAULT |
        CCE_GC_DST_PITCH_OFFSET_SUPPLIED |
        CCE_GC_DST_PITCH_OFFSET_DEFAULT |
		CCE_GC_SRC_CLIP_DEFAULT |
		CCE_GC_DST_CLIP_DEFAULT |
		CCE_GC_BRUSH_SOLIDCOLOR | 
        dst_type_state | 
		CCE_GC_SRC_DSTCOLOR | 
		ROP3_PATCOPY |
		CCE_GC_DST_CLR_CMP_FCN_CLEAR |
		CCE_GC_AUX_CLIP_CLEAR |
        CCE_GC_3D_FCN_EN_SET |
		CCE_GC_WRITE_MASK_LEAVE;

	// DST_PITCH_OFFSET

    gCommandBuf[i++] = (gZStenState.Offset >> 5) | (gZStenState.Pitch << 21);

    // Colour used to draw the rectangle due to CCE_GC_BRUSH_SOLIDCOLOR

    gCommandBuf[i++] = ZClearValue; // & mask?

    // Fill rectangle data into packet.

	// TOP | LEFT

    gCommandBuf[i++] = 0x00000000;

	// BOTTOM | RIGHT

    gCommandBuf[i++] = (R128_AdapterInfo.yres << 16) | R128_AdapterInfo.xres;

    gCommandBuf[0] |= ((i - 2) << 16);
    R128_CCESubmitPackets (gCommandBuf, i);

    // Restore DST_PITCH_OFFSET_C.

	i = 0;
    gCommandBuf[i++] = CCE_PACKET0 | (DST_PITCH_OFFSET_C >> 2);
//    gCommandBuf[i++] = R128_AdapterInfo.pitch << 21;
    gCommandBuf[i++] = gContext3D.regDST_PITCH_OFFSET_C;
    gCommandBuf[i++] = gContext3D.regDP_GUI_MASTER_CNTL; 
    gCommandBuf[0] |= ((i - 2) << 16);
    R128_CCESubmitPackets (gCommandBuf, i);

	// Restore TEX_CNTL_C.

	i = 0;
    gCommandBuf[i++] = CCE_PACKET0 | (TEX_CNTL_C >> 2);
    gCommandBuf[i++] = gContext3D.regTEX_CNTL_C;
    R128_CCESubmitPackets (gCommandBuf, i);

	// Restore the write mask.

	i = 0;
    gCommandBuf[i++] = CCE_PACKET0 | (DP_WRITE_MASK >> 2);
    gCommandBuf[i++] = save_dp_write_mask;
    R128_CCESubmitPackets (gCommandBuf, i);


	return TRUE;
}


/****************************************************************************
 * R128_SetZBufferState                                                     *
 *  Function: Set an enumerated z buffer state.                             *
 *    Inputs: eZState - EZBUFFERSTATE enum of state to set.                 *
 *			  data - state-sepcific data to be set.							*
 *   Outputs: TRUE - successful.                                            *
 *			  FALSE - unsuccessful.											*
 ****************************************************************************/

BOOL R128_SetZBufferState (EZBUFFERSTATE eZState, DWORD data)
{
	int i=0;
	DWORD tex_cntl_state;

	switch (eZState)
	{

	// Set z buffer mode.

	case ZBUFFERSTATE_MODE:
        switch ((EZMODE)data)
        {

		// z buffer off.

        case ZMODE_OFF:
            tex_cntl_state = TEX_CNTL_Z_EN_OFF | TEX_CNTL_Z_MASK_DIS;
            break;

		// z buffer test and write.
			
        case ZMODE_TESTWRITE:
            tex_cntl_state = TEX_CNTL_Z_EN_ON | TEX_CNTL_Z_MASK_EN;
            break;

		// z buffer test, but no write.

        case ZMODE_TESTNOWRITE:
            tex_cntl_state = TEX_CNTL_Z_EN_ON | TEX_CNTL_Z_MASK_DIS;
            break;

        default:
            return FALSE;
        }

        // Set TEX_CNTL_C:Z_EN and Z_MASK fields.

        gContext3D.regTEX_CNTL_C &= ~((0x00000001) | (0x00000001 << 1));
        gContext3D.regTEX_CNTL_C |= tex_cntl_state;
        gCommandBuf[i++] = CCE_PACKET0 | (TEX_CNTL_C >> 2);
        gCommandBuf[i++] = gContext3D.regTEX_CNTL_C;
        R128_CCESubmitPackets (gCommandBuf, i);

        // Update the ZMode record.

        gZStenState.ZMode = (EZMODE)data;

		break;

	// Set the z compare function.

	case ZBUFFERSTATE_CMP:
        switch ((ECMP)data)
		{
		case CMP_NEVER:
		case CMP_LESS:
		case CMP_LEQUAL:
		case CMP_EQUAL:
		case CMP_GREATER:
		case CMP_GEQUAL:
		case CMP_NEQUAL:
		case CMP_ALWAYS:
			break;

		default:
			return FALSE;
		}

		// Set Z_STEN_CNTL_C:Z_TEST field.

		gContext3D.regZ_STEN_CNTL_C &= ~(0x00000007 << 4);
		gContext3D.regZ_STEN_CNTL_C |= (data << 4);
        gCommandBuf[i++] = CCE_PACKET0 | (Z_STEN_CNTL_C >> 2);
		gCommandBuf[i++] = gContext3D.regZ_STEN_CNTL_C; 
		R128_CCESubmitPackets (gCommandBuf, i);

        gZStenState.ZCmp = (ECMP)data;

		break;

	default:
		return FALSE;
	}

	return TRUE;
}


/****************************************************************************
 * R128_SetStencilBufferState                                               *
 *  Function: Set an enumerated z buffer state.                             *
 *    Inputs: eStenState - ESTENCILSTATE enum of state to set.              *
 *			  data - state-sepcific data to be set.							*
 *   Outputs: TRUE - successful.                                            *
 *			  FALSE - unsuccessful.											*
 ****************************************************************************/

BOOL R128_SetStencilBufferState (ESTENCILSTATE eStenState, DWORD data)
{
	int i = 0;
	int shift;
    ESTENCILOP* peStenOp;

	switch (eStenState)
	{

	// Enable stencil buffering.

    case STENCILSTATE_ENABLE:

        gContext3D.regTEX_CNTL_C &= ~(0x00000001 << 3);
        if (data != FALSE)
            gContext3D.regTEX_CNTL_C |= (0x00000001 << 3);

		// Set TEX_CNTL_C:STEN_EN field.

        gCommandBuf[i++] = CCE_PACKET0 | (TEX_CNTL_C >> 2);
        gCommandBuf[i++] = gContext3D.regTEX_CNTL_C;
		R128_CCESubmitPackets (gCommandBuf, i);

        return TRUE;

	// Set Stencil reference.

	case STENCILSTATE_REF:

		gContext3D.regSTEN_REF_MASK_C &= ~(0x000000ff);
        gContext3D.regSTEN_REF_MASK_C |= (data & 0x000000ff);

		// Set STEN_REF_MASK_C:STEN_REF field.

	    gCommandBuf[i++] = CCE_PACKET0 | (STEN_REF_MASK_C >> 2);
	    gCommandBuf[i++] = gContext3D.regSTEN_REF_MASK_C;
		R128_CCESubmitPackets (gCommandBuf, i);

        gZStenState.StencilRef = (data & 0x000000ff);

		return TRUE;

	// Set stencil test function.

	case STENCILSTATE_CMP:

		switch ((ECMP)data)
		{
		case CMP_NEVER:
		case CMP_LESS:
		case CMP_LEQUAL:
		case CMP_EQUAL:
		case CMP_GREATER:
		case CMP_GEQUAL:
		case CMP_NEQUAL:
		case CMP_ALWAYS:
			gContext3D.regZ_STEN_CNTL_C &= ~(0x00000007 << 12);
			gContext3D.regZ_STEN_CNTL_C |= (data << 12);

			// Set Z_STEN-CNTL_C:STENCIL_TEST field.

			gCommandBuf[i++] = CCE_PACKET0 | (Z_STEN_CNTL_C >> 2);
			gCommandBuf[i++] = gContext3D.regZ_STEN_CNTL_C;
			R128_CCESubmitPackets (gCommandBuf, i);

            gZStenState.StencilCmp = (ECMP)data;

			return TRUE;

		default:
			return FALSE;
		}
		return TRUE;

	// Set stencil mask.

	case STENCILSTATE_MASK:

		gContext3D.regSTEN_REF_MASK_C &= ~(0x000000ff << 16);
		gContext3D.regSTEN_REF_MASK_C |= ((data & 0x000000ff) << 16);

		// Set STEN_REF_MASK_C:STEN_MSK field. 

	    gCommandBuf[i++] = CCE_PACKET0 | (STEN_REF_MASK_C >> 2);
        gCommandBuf[i++] = gContext3D.regSTEN_REF_MASK_C;
		R128_CCESubmitPackets (gCommandBuf, i);

        gZStenState.StencilMask = ((data & 0x000000ff) << 16);

		return TRUE;

	// Set stencil write mask.

	case STENCILSTATE_WRITEMASK:

		gContext3D.regSTEN_REF_MASK_C &= ~(0x000000ff << 24);
		gContext3D.regSTEN_REF_MASK_C |= ((data & 0x000000ff) << 24);

		// Set STEN_REF_MASK_C:STEN_WRITE_MSK field. 

	    gCommandBuf[i++] = CCE_PACKET0 | (STEN_REF_MASK_C >> 2);
        gCommandBuf[i++] = gContext3D.regSTEN_REF_MASK_C;
		R128_CCESubmitPackets (gCommandBuf, i);

        gZStenState.StencilWriteMask = ((data & 0x000000ff) << 24);

		return TRUE;

	// Set stencil fail op.

	case STENCILSTATE_SFAILOP:

		shift = 16;
        peStenOp = &(gZStenState.StencilFailOp);
		break;

	// Set stencil z pass op.

	case STENCILSTATE_ZPASSOP:
		shift = 20;
        peStenOp = &(gZStenState.StencilZPassOp);
		break;

	// Set stencil z fail op.

	case STENCILSTATE_ZFAILOP:
		shift = 24;
        peStenOp = &(gZStenState.StencilZFailOp);
		break;

	default:
		return FALSE;
	}

	// Validate stencil op state.

	switch ((ESTENCILOP)data)
	{
	case STENCILOP_CURRENT:
	case STENCILOP_ZERO:
	case STENCILOP_REF:
	case STENCILOP_INC:
	case STENCILOP_DEC:
	case STENCILOP_NOTCURRENT:
		break;

	default:
		return FALSE;
	}

	// Set Z_STEN_CNTL_C:[STEN_SFAIL_OP|STEN_ZPASS_OP|STEN_ZFAIL_OP] fields.

	gContext3D.regZ_STEN_CNTL_C &= ~(0x00000007 << shift);
	gContext3D.regZ_STEN_CNTL_C |= (data << shift);
	gCommandBuf[i++] = CCE_PACKET0 | (Z_STEN_CNTL_C >> 2);
	gCommandBuf[i++] = gContext3D.regZ_STEN_CNTL_C;
	R128_CCESubmitPackets (gCommandBuf, i);

    *peStenOp = (ESTENCILOP)data;

	return TRUE;
}


/****************************************************************************
 * R128_ClearStencilBuffer                                                  *
 *  Function: Clear the stencil buffer by doing a colorfill through a       *
 *            Type-3 CCE_PACKET3_CNTL_PAINT packet.                         *
 *    Inputs: StencilClearValue - value to clear the z buffer with.         *
 *   Outputs: TRUE - successful.                                            *
 *			  FALSE - unsuccessful.											*
 ****************************************************************************/

BOOL R128_ClearStencilBuffer (DWORD StencilClearValue)
{
	DWORD dst_type_state, save_dp_write_mask;
    int i;
    DWORD temp;


	switch (gZStenState.Format)
	{
    case ZSTENFORMAT_Z24_STEN8:
		dst_type_state = CCE_GC_DST_32BPP;
		break;

	default:
		return FALSE;
	}


    R128_WaitForFifo (1);
	save_dp_write_mask = regr (DP_WRITE_MASK);

	// Set the DP_WRITE_MASK value according to the z/stencil format. This
	// will ensure that only the stencil buffer bits are cleared. This is 
	// especially necessary to preserve the z bits in the z/stencil buffer,
	// where the z buffer is in the lower 24 bits, and the stencil buffer
	// in the upper eight bits.

	i = 0;
    gCommandBuf[i++] = CCE_PACKET0 | (DP_WRITE_MASK >> 2);
    gCommandBuf[i++] = gZStenState.StencilClearMask;
    R128_CCESubmitPackets (gCommandBuf, i);

    // Temprarily turn off Z_EN, STEN_EN, and TEX_EN if on.

    temp = gContext3D.regTEX_CNTL_C &
        ~((0x00000001) | (0x00000001 << 3) | (0x00000001 << 4));
	i = 0;
    gCommandBuf[i++] = CCE_PACKET0 | (TEX_CNTL_C >> 2);
    gCommandBuf[i++] = temp;
    R128_CCESubmitPackets (gCommandBuf, i);

	// Do a colorfill operation to clear the stencil bits. This is done
	// through a Type-3 CCE_PACKET3_CNTL_PAINT packet.

	i = 0;
    gCommandBuf[i++] = CCE_PACKET3_CNTL_PAINT;
    gCommandBuf[i++] =
        CCE_GC_SRC_PITCH_OFFSET_DEFAULT |
        CCE_GC_DST_PITCH_OFFSET_SUPPLIED |
        CCE_GC_DST_PITCH_OFFSET_DEFAULT |
		CCE_GC_SRC_CLIP_DEFAULT |
		CCE_GC_DST_CLIP_DEFAULT |
		CCE_GC_BRUSH_SOLIDCOLOR | 
        dst_type_state | 
		CCE_GC_SRC_DSTCOLOR | 
		ROP3_PATCOPY |
		CCE_GC_DST_CLR_CMP_FCN_CLEAR |
		CCE_GC_AUX_CLIP_CLEAR |
        CCE_GC_3D_FCN_EN_SET |
		CCE_GC_WRITE_MASK_LEAVE;

	// DST_PITCH_OFFSET

    gCommandBuf[i++] = (gZStenState.Offset >> 5) | (gZStenState.Pitch << 21);

    // Colour used to draw the rectangle due to CCE_GC_BRUSH_SOLIDCOLOR

    gCommandBuf[i++] = StencilClearValue << 24;

    // Fill rectangle data into packet.

	// TOP | LEFT

    gCommandBuf[i++] = 0x00000000;

	// BOTTOM | RIGHT

    gCommandBuf[i++] = (R128_AdapterInfo.yres << 16) | R128_AdapterInfo.xres;

    gCommandBuf[0] |= ((i - 2) << 16);
    R128_CCESubmitPackets (gCommandBuf, i);

    // Restore DST_PITCH_OFFSET_C.

	i = 0;
    gCommandBuf[i++] = CCE_PACKET0 | (DST_PITCH_OFFSET_C >> 2);
//    gCommandBuf[i++] = R128_AdapterInfo.pitch << 21;
    gCommandBuf[i++] = gContext3D.regDST_PITCH_OFFSET_C;
    gCommandBuf[i++] = gContext3D.regDP_GUI_MASTER_CNTL; 
    gCommandBuf[0] |= ((i - 2) << 16);
    R128_CCESubmitPackets (gCommandBuf, i);

	// Restore TEX_CNTL_C.

	i = 0;
    gCommandBuf[i++] = CCE_PACKET0 | (TEX_CNTL_C >> 2);
    gCommandBuf[i++] = gContext3D.regTEX_CNTL_C;
    R128_CCESubmitPackets (gCommandBuf, i);

	// Restore the write mask.

	i = 0;
    gCommandBuf[i++] = CCE_PACKET0 | (DP_WRITE_MASK >> 2);
    gCommandBuf[i++] = save_dp_write_mask;
    R128_CCESubmitPackets (gCommandBuf, i);


	return TRUE;
}


/****************************************************************************
 * R128_FreeZStencilBuffer													*
 *  Function: Free the resources associated with this buffer.				*
 *    Inputs: none                                                          *
 *   Outputs: none                                                          *
 ****************************************************************************/

void R128_FreeZStencilBuffer (void)
{
	if (gZStenState.Offset != 0)
	{
		R128_FreeBuffer (gZStenState.Offset);
	}

	memset (&gZStenState, 0, sizeof (ZSTENSTATE));
}
