/***********************************************************************************
           Copyright 1998, NEC Electronics Inc. All rights reserved.

Information and source code samples contained herein are provided "as-is",
without representations or warranties, and are subject to change without notice.
NEC Electronics Inc. cannot support modifications or derivative works created from
the sample source code provided. Developers may use, reproduce, and modify portions
or entire sections of the sample source code for the purposes of creating Dreamcast
applications.  Developers may only commercially distribute the executable or binary
portions of the sample source code.  Sample source code contained herein may not be
appropriate for use under all circumstances.  Please consult Developer documentation
provided along with your Dreamcast unit for additional information and suggestions.
************************************************************************************/


/********************************************************************************************/
/* Name:     TableFog.c                                                                     */
/* Title:    Table Fog Example                                                              */
/* Author:   Kevin Kralian                                                                  */
/* Created:  October 19, 1998                                                               */
/*                                                                                          */
/* Description:                                                                             */
/*      The purpose of this program is to provide an extremely simple example of            */
/*      using programmable table-based fog in Kamui.                                        */
/*                                                                                          */
/* History:                                                                                 */
/*      11/10/98 - Ported to Set5/R5 SDK                                                    */
/*      10/19/98 - Program created; derived from "QuikTest.c" (by Gary Lake).               */
/********************************************************************************************/

#include <shinobi.h>
#include <kamui.h>
#include <sg_sycbl.h>

#include "pvrlogo.h"				/* contains texture data */


#define FOG_COLOR_R		0.4			/* fog color */
#define FOG_COLOR_G		0.6
#define FOG_COLOR_B		0.6


#define CTX_MASK_ALL   (KM_PARAMTYPE | KM_LISTTYPE | KM_COLORTYPE | KM_UVFORMAT |       \
                        KM_DEPTHMODE | KM_CULLINGMODE | KM_SCREENCOORDINATION |         \
                        KM_SHADINGMODE | KM_MODIFIER | KM_ZWRITEDISABLE |               \
                        KM_SRCBLENDINGMODE | KM_DSTBLENDINGMODE | KM_SRCSELECT |        \
                        KM_DSTSELECT | KM_FOGMODE | KM_USESPECULAR | KM_USEALPHA |      \
                        KM_IGNORETEXTUREALPHA | KM_CLAMPUV | KM_FLIPUV |                \
                        KM_FILTERMODE | KM_SUPERSAMPLE | KM_MIPMAPDADJUST |             \
                        KM_TEXTURESHADINGMODE | KM_COLORCLAMP | KM_PALETTEBANK)

KMVERTEXCONTEXT OpaqueTextureContext =
{
    CTX_MASK_ALL, KM_POLYGON, KM_OPAQUE_POLYGON, KM_FLOATINGCOLOR, KM_32BITUV,
    KM_GREATER, KM_CULLCCW, KM_SCREEN, KM_TEXTUREGOURAUD, KM_NOMODIFIER,
    FALSE, KM_ONE, KM_ZERO, FALSE, FALSE, KM_NOFOG, FALSE, FALSE, FALSE,
    KM_NOCLAMP, KM_NOFLIP, KM_BILINEAR, FALSE, 0x4, KM_MODULATE, FALSE,
    0, 0, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
    0, 0, 0, 0, 0, 0.0f, 0.0f, 0.0f, 0.0f
};



/* Size of vertex buffer. */
#define VBUFFER_SIZE    8*3*2000

/* System Configuration */
PKMSURFACEDESC          psurface[2];            /* array of pointers to surfaces */
KMPACKEDARGB            packedARGB;
KMVERTEXBUFFDESC		VertexBufferDesc;		/* Vertex-buffer */

KMSYSTEMCONFIGSTRUCT kmsc =
{
	sizeof(KMSYSTEMCONFIGSTRUCT),
	KM_CONFIGFLAG_ENABLE_CLEAR_FRAMEBUFFER,	/* set any special flags = clear frame buffer */
    psurface,                               /* pointer to array of surface pointers */
    2,                                      /* number of buffers = 2 */
    640,480,                                /* width and height of surface */
    KM_DSPBPP_RGB888,                       /* bits per pixel */
    0x100000,                               /* allocate amount of VRAM for texture = 4MB */
    &VertexBufferDesc,                      /* pointer to vertex buffer descriptor */
    0,                                      /* pointer to vertex buffer - ** malloc now on application side */
	2*VBUFFER_SIZE,							/* size in DWORDS of vertex buffer - must be multiple of 32 */
    {20.f,20.f,20.f,20.f,20.f},             /* percentage of vbuf to use for each type of vertex */
    0,0,0,0,0,0,0,                          /* reserved */
    KM_VERTEXBUFMODEL_NORMAL                /* latency model = 3VB */
};

/* min and max values for variable W (which changes over time) */
#define MIN_W			1.0
#define MAX_W			1024.0


int main (void)
{
    KMSURFACEDESC       PrimarySurfaceDesc;     /* Primary framebuffer. */
    KMSURFACEDESC       BackSurfaceDesc;        /* Back framebuffer surface. */
    KMSURFACEDESC       TexSurfaceDesc;         /* Texture surface. */
    KMVERTEX_05         CurrentStrip_05[16];    /* Strip of textured triangles. */
    int                 i;
	float				variableW = MIN_W;		/* in the range [1.0, 1024.0] */
	float				WDelta = (MAX_W-MIN_W)/360.0;   /* W rate of change (full change in 360 frames) */

#ifdef __GNUC__
	shinobi_workaround();
#endif

    /* Check the cable for NTSC/PAL or VGA */
    switch (syCblCheckCable())
	{
        /* Initialize the display device and set the frame buffer based on the video mode. */
        case SYE_CBL_CABLE_NTSC:    /* Note: SYE_CBL_CABLE_NTSC/PAL defined as same value. */
            switch (syCblCheckBroadcast())
            {
                case SYE_CBL_BROADCAST_NTSC:    /* U.S./North America NTSC (60Hz). */
                case SYE_CBL_BROADCAST_PALM:    /* Brazil PAL-M (60Hz). */
                    sbInitSystem (KM_DSPMODE_NTSCNI640x480, KM_DSPBPP_RGB565, 1);
                    break;
                case SYE_CBL_BROADCAST_PAL:     /* Europe PAL (50Hz). */
                case SYE_CBL_BROADCAST_PALN:    /* Argentina PAL-N (50Hz). */
                    sbInitSystem (KM_DSPMODE_PALNI640x480, KM_DSPBPP_RGB565, 1);
                    break;
            }
            break;
        case SYE_CBL_CABLE_VGA:
            sbInitSystem (KM_DSPMODE_VGA, KM_DSPBPP_RGB565, 1);
            break;
	}

	/* setup border color */
	packedARGB.dwPacked	=	0x00000000;
	kmSetBorderColor(packedARGB);

	/* setup front & back framebuffer structs */
    psurface[0]         =   &PrimarySurfaceDesc;
    psurface[1]         =   &BackSurfaceDesc;

    /* generate vertex buffer */
	kmsc.pVertexBuffer	=	(void *)((((int)syMalloc(2*sizeof(KMDWORD)*VBUFFER_SIZE)&0x3FFFFFE0)|0xA0000000)+32);

	/* Submit system configuration */
    kmSetSystemConfiguration(&kmsc);

    /* Create a texture surface. */
    kmCreateTextureSurface (&TexSurfaceDesc, 128, 128,
        (KM_TEXTURE_TWIDDLED | KM_TEXTURE_RGB565));

    /* Load texture data into VRAM. (.PVR data file embedded in .H file) */
    kmLoadTexture (&TexSurfaceDesc, (void*)(pvrlogo + 16), FALSE, FALSE);


    /* Pre-process the vertex context for opaque textures, using the loaded texture. */
    OpaqueTextureContext.pTextureSurfaceDesc = &TexSurfaceDesc;
    kmProcessVertexRenderState (&OpaqueTextureContext);


	/******************** SETUP TABLE-FOGGING *********************/

	/* Lets enable table-fogging in the 'OpaqueTextureContext' */
	/* We could have set these items in the statically declared */
	/* structure, but they are being done here for clarity */

	/* fog is the only renderstate we're changing */
	OpaqueTextureContext.RenderState = KM_FOGMODE;
	OpaqueTextureContext.FogMode     = KM_FOGTABLE;

	/* Re-process the context to reflect this change */
    kmProcessVertexRenderState (&OpaqueTextureContext);


	/* and set the fog color */
	packedARGB.dwPacked =( ((unsigned char)(FOG_COLOR_R * 255) << 16 ) +
						   ((unsigned char)(FOG_COLOR_G * 255) << 8  ) +
						   ((unsigned char)(FOG_COLOR_B * 255)) );


	kmSetFogTableColor( packedARGB );


	/* Choose a fog density */

	/* This basically places the 128-entry table in a specified W range */
	/* We submit a desired W-depth for the furthest control point,      */
	/* and the closest point will be at 1/256'th that distance.         */

	/* So, we'll choose a value of 992, which means our fog table has   */
	/* control in the W range of [3.875, 992]; the closest and furthest */
	/* settings will simply extend through the rest of W space.         */

	/* The fog density value needs to be submitted in a unique format:  */
	/* a word with a high byte containing a mantissa, and a low byte    */
	/* containing a signed exponent. The high mantissa bit corresponds  */
	/* to the binary ones column. (mantissa of 0xff = binary 1.1111111) */

	/* so our value of 992.0 would be represented as: 0xF809.  Why?     */
	/* The mantissa, 0xF8, represents (bin) 1.1111000, or (dec) 1.9375  */
	/* The exponent, 0x09, acts as a shift left by 9 (multiply by 2^9 ) */

	kmSetFogDensity( 0xf809 ); /* fog-control W-depth range[3.875, 992] */

	/* Set up fog table */
	{
		float fogTable[129];
		float w;
		float density;

		/* since control points are not linearly spaced, it is easiest  */
		/* to walk through table indices, compute W at each index, and  */
		/* compute fog from W depth */

		for (i=0; i <= 128; i++)
		{
			/* compute W based on index & fogDensity value (see docs) */
			w = 992.0 / (pow(2.0, i>>4)*(float)((i & 0x0f)+16)/16.0f);

			/* Lets have a linear fog ramp in the w-range [100, 800]; */
			if (w < 100)
				density = 0.0;
			else if (w > 800)
				density = 1.0;
			else
			    density = (w-100)/(800-100);

			fogTable[i] = density;
		}

		/* table is set up, download it to hardware */
        kmSetFogTable( (void *) &fogTable );
	}

	/* The fog-table has been set up; geometry drawn with fogging enabled  */
	/* will be fogged based on each pixel's depth - nothing else is needed */


    /* The main render loop. */
    while (TRUE)
    {

		/* Gradually change W value (which affects our fog) */
		variableW += WDelta;

		if (variableW <= MIN_W  ||  variableW >= MAX_W)
		{
			variableW = MIN( variableW, MAX_W );
			variableW = MAX( variableW, MIN_W );
			WDelta = -WDelta;
		}


		/* Set Kamui's global render state for opaque textures. */
        kmSetVertexRenderState (&OpaqueTextureContext);

        /* Start rendering opaque textured strips. */
        kmStartVertexStrip (&VertexBufferDesc);

        /* Set the vertices in the current strip to draw a textured square. */
        CurrentStrip_05[0].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[0].fX                   = 0;
        CurrentStrip_05[0].fY                   = 480;
        CurrentStrip_05[0].u.fInvW              = 1.0/variableW; /* OOW here determines fog from table */
        CurrentStrip_05[0].fU                   = 0.0f;
        CurrentStrip_05[0].fV                   = 0.836f;
        CurrentStrip_05[0].fBaseAlpha           = 1.0f;
        CurrentStrip_05[0].fBaseRed             = 1.0f;
        CurrentStrip_05[0].fBaseGreen           = 0.0f;
        CurrentStrip_05[0].fBaseBlue            = 0.0f;

        CurrentStrip_05[1].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[1].fX                   = 0;
        CurrentStrip_05[1].fY                   = 0;
        CurrentStrip_05[1].u.fInvW              = 1.0/variableW; /* OOW here determines fog from table */
        CurrentStrip_05[1].fU                   = 0.0f;
        CurrentStrip_05[1].fV                   = 0.0f;
        CurrentStrip_05[1].fBaseAlpha           = 1.0f;
        CurrentStrip_05[1].fBaseRed             = 0.0f;
        CurrentStrip_05[1].fBaseGreen           = 1.0f;
        CurrentStrip_05[1].fBaseBlue            = 0.0f;

        CurrentStrip_05[2].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[2].fX                   = 640;
        CurrentStrip_05[2].fY                   = 480;
        CurrentStrip_05[2].u.fInvW              = 1.0/variableW; /* OOW here determines fog from table */
        CurrentStrip_05[2].fU                   = 0.625f;
        CurrentStrip_05[2].fV                   = 0.836f;
        CurrentStrip_05[2].fBaseAlpha           = 1.0f;
        CurrentStrip_05[2].fBaseRed             = 0.0f;
        CurrentStrip_05[2].fBaseGreen           = 0.0f;
        CurrentStrip_05[2].fBaseBlue            = 1.0f;

        CurrentStrip_05[3].ParamControlWord     = KM_VERTEXPARAM_ENDOFSTRIP;
        CurrentStrip_05[3].fX                   = 640;
        CurrentStrip_05[3].fY                   = 0;
        CurrentStrip_05[3].u.fInvW              = 1.0/variableW; /* OOW here determines fog from table */
        CurrentStrip_05[3].fU                   = 0.625f;
        CurrentStrip_05[3].fV                   = 0.0f;
        CurrentStrip_05[3].fBaseAlpha           = 1.0f;
        CurrentStrip_05[3].fBaseRed             = 0.0f;
        CurrentStrip_05[3].fBaseGreen           = 0.0f;
        CurrentStrip_05[3].fBaseBlue            = 0.0f;

        kmSetVertex (&VertexBufferDesc, CurrentStrip_05,
            KM_VERTEXTYPE_05, sizeof (KMVERTEX_05));
        kmSetVertex (&VertexBufferDesc, CurrentStrip_05+1,
            KM_VERTEXTYPE_05, sizeof (KMVERTEX_05));
        kmSetVertex (&VertexBufferDesc, CurrentStrip_05+2,
            KM_VERTEXTYPE_05, sizeof (KMVERTEX_05));
        kmSetVertex (&VertexBufferDesc, CurrentStrip_05+3,
            KM_VERTEXTYPE_05, sizeof (KMVERTEX_05));

        /* Render the polygons and flip the display. */
        kmRender();
        kmFlipFrameBuffer();
    }

    /* Free vertex buffers. */
    kmDiscardVertexBuffer (&VertexBufferDesc);

    /* Free the texture surface. */
    kmFreeTexture (&TexSurfaceDesc);

    /* Free the frame buffers. */
    kmFreeTexture (&PrimarySurfaceDesc);
    kmFreeTexture (&BackSurfaceDesc);

    return 0;
}