//////////////////////////////////////////////////////////////////////////////
//
// Asura_PS2_GSMRenderer.h
//
// Copyright (c) 2003 Rebellion. All rights reserved.
//
// Description:
//
//     EE side code for rendering generic static meshes. Intended to provide
//     an interface for use by other more specific renderers, eg. hierarchy
//     or environment.
//
//     All control of the rendering is done through this class, except that
//     clients are responsible for dictating the texture buffers and layout
//     used.
//
//////////////////////////////////////////////////////////////////////////////

// B E G I N   F I L E   W R A P P E R ///////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef ___ASURA_PS2_GSMRENDERER_H___
#define ___ASURA_PS2_GSMRENDERER_H___ 1

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

// Stdlib platform-specific.
#include <libvu0.h>

// M A C R O S ///////////////////////////////////////////////////////////////

// This many QWs at the beginning of SPR are required for internal use by
// the GSM rendering code.
#define ASURA_PS2_GSM_SPR_QWS_RESERVED (0x95)

#define ASURA_PS2_GSM_NUM_CAMS_VIS_FOR_ADDR   (reinterpret_cast<u_int*>(0x70000018))
#define ASURA_PS2_GSM_NUM_PASSES_PER_TRI_ADDR (reinterpret_cast<u_int*>(0x70000218))

// (MP - 22.04.2005) Nothing's using this anymore but for now let's allow
// projects to force it on in their engine cfg, in case it's required in
// some project code.
#ifndef ASURA_PS2_GSM_SUPPORT_RENDERING_SINGLE_STRIPS
#define ASURA_PS2_GSM_SUPPORT_RENDERING_SINGLE_STRIPS (0)
#endif

// C O N S T A N T S /////////////////////////////////////////////////////////

static const u_int ASURA_PS2_GSM_DUAL_TEXTURE_PALETTE_BFR_IDX = 2;

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////

class Asura_Bounding_Box;
class Asura_Matrix_3x3;
class Asura_SphericalHarmonics_Order2_RGB;
class Asura_Vector_3;

union Asura_PS2_QW;

// C L A S S E S /////////////////////////////////////////////////////////////

class Asura_PS2_GSMRenderer
{

    public:

        // T Y P E S /////////////////////////////////////////////////////////

        struct Light
        {
            float afLightPos[3]; // Worldspace.
            float fLightRange;
            float afLightRGB[3]; // Usual [0, 1] independent scale, with > 0.5 being overbrightening.
        };

        typedef void (*RSCallback)( Asura_PS2_QW* pxRSBfr );

        // C L A S S   M E T H O D S /////////////////////////////////////////

        /////
        //
        // General setup stuff.
        //
        /////

        static void Initialise( void );
        static void Reset( void );
        static void Shutdown( void );

        /////
        //
        // Rendering.
        //
        // These functions are listed in recommended call order. Also, no
        // method depends on any method listed after it. There are, however,
        // dependencies in the other direction so that, for example, the
        // SetLightingParameters() method assumes the object->world transform
        // has already been setup (unless the default identity transform from
        // Begin() is correct.)
        //
        // A minimal call sequence is:
        //
        //     Begin();
        //     SetTexture( ... );     (*)
        //     SetRenderstate( ... ); (*)
        //     SetCamFlags( ... );    (*)
        //     [lighting]             (*)
        //     [render]
        //     End();
        //
        // The steps marked with (*) may be omitted if the higher level code
        // knows the parameters are unchanged from the previous mesh.
        //
        // A loop can be made beginning at any of the steps excluding Begin()
        // and End(), though the end of the loop must always be at [render].
        //
        // Note that the first ASURA_PS2_GSM_SPR_QWS_RESERVED QWs of SPR are
        // required for internal use for the duration of rendering, ie.
        // between the Begin() and End() calls.
        //
        /////

        //
        // Startup.
        //

        static void Begin( void );

        // Uploads the textures to the specified buffer and caches some state
        // ready for their use. Must be called after Begin() if dual
        // texturing is going to be used.
        static void SetDualTexturePalette( const int* piTextureIDs, u_int uNumTextureIDs, u_int uTexBfr, u_int uMMAG, u_int uMMIN );

        //
        // Renderstate.
        //

        // See comments by implementation before using this.
        static void SetTextureClamp( bool bClamp );

        static void SetTexture( int iTexID, int iMMAG, int iMMIN, u_int uBuffer, bool bIsBaseTexture = true );
        static void SetRenderstate( u_int uMaterialFlags );
        // Note that SetRenderstate() resets all functionality setup via the
        // remaining RS methods below to be disabled.

        // Pass 0 is the base pass, using the base texture. Pass 2 is an
        // additional pass with another texture, some alpha blend and
        // modified STs.
        static void SetupPass0STManipulations( bool bEnable, float fScaleS = 0.0f, float fScaleT = 0.0f, float fTransS = 0.0f, float fTransT = 0.0f );
        static void SetupPass2STManipulations( bool bEnable,
                                               float fScaleS = 0.0f, float fScaleT = 0.0f, float fTransS = 0.0f, float fTransT = 0.0f,
                                               u_int uAlphaA = 0, u_int uAlphaB = 0, u_int uAlphaC = 0, u_int uAlphaD = 0, u_int uAlphaFIX = 0 );

        // Resulting y is
        //     y_out = c0 cos p0 + c1 sin p1 + c2 y_in + c3
        // where
        //     p0 = a0 s + b0 t , p1 = a1 s + b1 t
        static void SetupYManipulations( bool bEnable,
                                         float fA0, float fB0, float fA1, float fB1,
                                         float fC0, float fC1, float fC2, float fC3 );

        static void SelectFromDualTexturePalette( int iTextureID );

        // Note that you cannot modify the number of passes made using this.
        // To be sure state is reset afterwards, you must call both
        // SetTexture() and SetRenderstate().
        static void ManuallyModifyRenderstate( RSCallback pfnCB );

        //
        // Renderstate callbacks.
        //

        static void CB_DisableZReadsAndWrites( Asura_PS2_QW* pxRSBfr );
        static void CB_JustWriteZ( Asura_PS2_QW* pxRSBfr );
        static void CB_IgnoreBaseTextureAlpha( Asura_PS2_QW* pxRSBfr );

        //
        // Cam flags.
        //

        // For the vis flags, bit c should be high iff camera c can see the
        // mesh. For the clip flags, bit (6 - 2c) should be high iff camera c
        // can see the mesh but clipping needs to be considered. See notes in
        // Asura_PS2_GSMDefines.h for further info.
        static void SetCamFlags( u_int uVisFlags, u_int uClipFlags );

        // Versions of the above that setup clip flags based on supplied data.
        static void SetCamFlags( u_int uVisFlags, const Asura_Bounding_Box& xBB );
        static void SetCamFlags( u_int uVisFlags, const Asura_Vector_3& xCentre, const float fRadius );

        //
        // Shadows.
        //

		// fallback shadow quad render
		static void RenderShadowQuad( u_int uFlags  );

        // Shadow is off by default so just call this if required.
        static void EnableShadow( const Asura_Vector_3& xBSCentre, float fBSRadius/*, const Asura_Vector_3& xShadowPos, float fMeshHeightAboveShadow*/ );

		// Shadows are now projected onto the ground, and for efficiency sake we need to
		// render several shadows onto a single mesh - so we use the following function
		static void SetShadowStates( u_int uNumShadows, const u_long* puTEX0Settings, const int* piShadowBufferIDs, const u_short* pusVisClipData );
		static void CancelLightingUpload( void );
		static void UploadExtraShadowData( void );
		static void GetShadowBufferPosition( u_int u_ShadowIndex );

        //
        // Transforms.
        //

        static void SetObjectToWorldMat( const Asura_Matrix_3x3& xOri, const Asura_Vector_3& xPos );
        static void SetObjectToWorldMat( const float* pfO2W );
        static void SetObjectToWorldMatToIdentity( void );

        //
        // Lighting.
        //

        // Alpha in range [0,1].
        static void SetPointLightingParametersForPrelitMesh( const Light* pxLights, u_int uNumLights, float fAlpha = 1.0f );

        // Alpha in range [0,1]. Mesh centre in worldspace.
//      static void CalculateSHForMesh( const Asura_Vector_3& xMeshCentre, float fAlpha );
        static void UseExistingSHForMesh( const Asura_SphericalHarmonics_Order2_RGB& xSH, float fAlpha );
        static void RecalculateSHForChangedObjectToWorldMat( void );

        //
        // Data transfer and actual rendering.
        //

#if ASURA_PS2_GSM_SUPPORT_RENDERING_SINGLE_STRIPS
        static void RenderStrip( u_int uREFAddr, u_int uNumQWs, u_int uNumVertices );
#endif

        // After UNPACKing each strip's data, the chain should set ITOP to
        // the vertex count and then MSCNT.
        static void RenderStripChain( u_int uCALLAddr );

        static void RenderStripsWithManualSend( void );

        //
        // Tidy up.
        //

        static void End( void );

        //
        // Miscellaneous methods for determining how GSM renderer will treat
        // particular meshes, materials, etc. Occasionally necessary for
        // controlling it correctly externally.
        //
        
        // Only valid between Begin() and End().
        static u_int GetNumCamsVisFor( void ) { return *ASURA_PS2_GSM_NUM_CAMS_VIS_FOR_ADDR; }
        static u_int GetNumPassesPerTri( void ) { return *ASURA_PS2_GSM_NUM_PASSES_PER_TRI_ADDR; }

        static bool IsMaterialReflective( u_int uMaterialFlags );

		static void SetProjectionMatrix( u_int uID, const sceVu0FMATRIX *pxData );

};
// End class Asura_PS2_GSMRenderer.

// E N D   F I L E   W R A P P E R ///////////////////////////////////////////

#endif // #ifndef ___ASURA_PS2_GSMRENDERER_H___
