//////////////////////////////////////////////////////////////////////////////
//
// AVP_Platform_Stencil_Setup.cpp
//
// Author : Ben Weston, based on Xbox 360 code by Andrew Southgate
// Date   : 21/5/2008 
//
// Copyright (c) 2008 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////


// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#include "Core/Asura_MaterialResponse_System.h"

#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_PS3_RenderingStates.h"
#include "Rendering/AVP_Platform_Stencil_Setup.h"

#include "FSFX/AVP_FSFX.h"

//
// Defines
//

// mask of all bits that could ever be used by a material context
#define AVP_MATERIAL_CONTEXT_STENCIL_MASK	(AVP_STENCIL_MASK_VISION)

//
// Variables
//

// allow stencil test to be performed on any bits that aren't used by this function
ASURA_PS3_STENCIL_FUNCTION	AVP_PlatformStencilSetup::s_uFunction = ASURA_PS3_STENCIL_ALWAYS;
u_int 						AVP_PlatformStencilSetup::s_uTestRef = 0;
u_int 						AVP_PlatformStencilSetup::s_uTestMask = 0;

//
// Functions
//


//
// AVP_PlatformStencilSetup::SetTestFunction
//
// Allow materials to both write to & test against the stencil buffer. e.g. for predator cloak transition whilst in heat vision mode.
// These parameters will be combined with the ones used by the material "project context" (which we use as stencil flags)
// uRef & uMask must use different bits to those defined in AVP_MATERIAL_CONTEXT_STENCIL_MASK
//
void AVP_PlatformStencilSetup::SetTestFunction( ASURA_PS3_STENCIL_FUNCTION uFunction, u_int uRef, u_int uMask )
{
	Asura_Assertf((uMask & AVP_MATERIAL_CONTEXT_STENCIL_MASK) == 0, ("AVP_PlatformStencilSetup::SetTestFunction - Error: mask overlaps bits used by material context\n Requested mask = %02x, material mask = %02x\n", uMask, AVP_MATERIAL_CONTEXT_STENCIL_MASK));
	Asura_Assertf((uRef & AVP_MATERIAL_CONTEXT_STENCIL_MASK) == 0, ("AVP_PlatformStencilSetup::SetTestFunction - Warning: ref value overlaps material context mask\n Requested ref = %02x, material mask = %02x\n", uRef, AVP_MATERIAL_CONTEXT_STENCIL_MASK));

	s_uTestMask = uMask & ~AVP_MATERIAL_CONTEXT_STENCIL_MASK;  //clear the bits just in case
	s_uTestRef = uRef & ~AVP_MATERIAL_CONTEXT_STENCIL_MASK;
	s_uFunction = uFunction;
}


//
// AVP_PlatformStencilSetup::ResetTestFunction
//
// Restore the default material context function (always write)
//
void AVP_PlatformStencilSetup::ResetTestFunction( void )
{
	s_uFunction = ASURA_PS3_STENCIL_ALWAYS;
	s_uTestRef = 0;
	s_uTestMask = 0;
}


//
// AVP_PlatformStencilSetup::MaterialContextCallback
//
// When the material has a "context" value, write it to the stencil buffer.
//
void AVP_PlatformStencilSetup::MaterialContextCallback(u_int uPrimaryContext, u_int uSecondaryContext)
{
	u_int uContext = ( uPrimaryContext == ASURA_MATERIAL_RESPONSE_PROJECT_CONTEXT_INVALID ) ? 0 : uPrimaryContext;
	uContext |= ( uSecondaryContext == ASURA_MATERIAL_RESPONSE_PROJECT_CONTEXT_INVALID ) ? 0 : uSecondaryContext;

	if ( Asura_PS3_Graphics::IsRenderingToSkybox() )
	{
		// renders to skybox use stencil buffer, so don't override those settings.
		// This means we can't have giant glowing aliens in the sky, I think we can live with that.
		return;
	}

//	if (uContext == 0 && s_uTestMask == 0)  //BW I want to do it like this, since I think a lot of stuff has 0, but that's kind of important so hot stuff doesn't show through cold on skins and such.
	if (uPrimaryContext == ASURA_MATERIAL_RESPONSE_PROJECT_CONTEXT_INVALID && s_uTestMask == 0)
	{
		//nobody needs to write or test anything, so disable stencil
		Asura_PS3_RenderingStates::SetStencilTestEnabled( false );
	} 
	else 
	{
		uContext <<= AVP_STENCIL_BIT_VISION_FIRST;
		uContext &= AVP_MATERIAL_CONTEXT_STENCIL_MASK;
		
 		Asura_PS3_RenderingStates::SetStencilTestEnabled( true );
		Asura_PS3_RenderingStates::SetStencilMask( AVP_MATERIAL_CONTEXT_STENCIL_MASK | s_uTestMask ); //read/write mask
		
		// setup the stencil test, combining the vals we want to write with what test we wish to do against the current stencil buffer contents.
		// this has the side effect of writing out the test reference value (s_uTestRef) if the test succeeds, so tests for NEQUAL should only be done against zero
		// N.B. actually this is no different to how the test usually works, but we also write the extra bits for the material context
		Asura_PS3_RenderingStates::SetStencilFunction( s_uFunction, uContext | s_uTestRef, s_uTestMask ); //write val, read mask
		
		// only write if the test passes (if no test is enabled, default test is "always")
		Asura_PS3_RenderingStates::SetStencilOperation( ASURA_PS3_STENCIL_OPERATION_KEEP, ASURA_PS3_STENCIL_OPERATION_KEEP, ASURA_PS3_STENCIL_OPERATION_REPLACE );
	}
}