/***********************************************************************************
           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:     VertFog.c                                                                      */
/* Title:    Vertex Fog Example                                                             */
/* Author:   Kevin Kralian                                                                  */
/* Created:  October 19, 1998                                                               */
/*                                                                                          */
/* Description:                                                                             */
/*      The purpose of this program is to provide an extremely simple example of            */
/*      using vertex 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 & background-plane color */
#define FOG_COLOR_G		0.4
#define FOG_COLOR_B		0.7



#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 OpaqueColorContext =
{
    CTX_MASK_ALL, KM_POLYGON, KM_OPAQUE_POLYGON, KM_FLOATINGCOLOR, KM_32BITUV,
    KM_GREATER, KM_CULLCCW, KM_SCREEN, KM_NOTEXTUREGOURAUD, KM_NOMODIFIER,
    FALSE, KM_ONE, KM_ZERO, FALSE, FALSE, KM_NOFOG, FALSE, FALSE, FALSE,
    KM_NOCLAMP, KM_NOFLIP, KM_POINT_SAMPLE, 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,
};

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, TRUE, 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 */
};


int main (void)
{
    KMSURFACEDESC       PrimarySurfaceDesc;     /* Primary framebuffer. */
    KMSURFACEDESC       BackSurfaceDesc;        /* Back framebuffer surface. */
    KMSURFACEDESC       TexSurfaceDesc;         /* Texture surface. */
    KMVERTEX_01         BackgroundVert[3];    	/* Used to set up background plane */
    KMVERTEX_05         CurrentStrip_05[16];    /* Strip of textured triangles. */
    PKMVERTEX_01        BackPlane[3];           /* Background plane. */
    int                 i;
	float 				fogDensity = 0.0;		/* value between [0.0,1.0] */
    float				fogDelta = 1.0/360.0;   /* fog 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);

    /* Pre-process the vertex context for opaque non-textured color polygons. */
    kmProcessVertexRenderState (&OpaqueColorContext);

	/***************** SETUP BACKGROUND PLANE ****************/

    /* Set background plane render state to opaque color. */

	/* Note: we could have enabled fog for the background, but instead, */
	/* we just draw it in the fog color- it looks the same */
    kmSetBackGroundRenderState (&OpaqueColorContext);

    /* Set the vertices of the background plane */
    BackgroundVert[0].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
    BackgroundVert[0].fX                   = 0.0f;
    BackgroundVert[0].fY                   = 0.0f;
    BackgroundVert[0].u.fZ                 = 0.2f;
    BackgroundVert[0].fBaseAlpha           = 1.0f;
    BackgroundVert[0].fBaseRed             = FOG_COLOR_R;
    BackgroundVert[0].fBaseGreen           = FOG_COLOR_G;
    BackgroundVert[0].fBaseBlue            = FOG_COLOR_B;

    BackgroundVert[1].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
    BackgroundVert[1].fX                   = 0.0f;
    BackgroundVert[1].fY                   = 480.0f;
    BackgroundVert[1].u.fZ                 = 0.0002f;
    BackgroundVert[1].fBaseAlpha           = 1.0f;
    BackgroundVert[1].fBaseRed             = FOG_COLOR_R;
    BackgroundVert[1].fBaseGreen           = FOG_COLOR_G;
    BackgroundVert[1].fBaseBlue            = FOG_COLOR_B;

    BackgroundVert[2].ParamControlWord     = KM_VERTEXPARAM_ENDOFSTRIP;
    BackgroundVert[2].fX                   = 640.0f;
    BackgroundVert[2].fY                   = 480.0f;
    BackgroundVert[2].u.fZ                 = 0.2f;
    BackgroundVert[2].fBaseAlpha           = 1.0f;
    BackgroundVert[2].fBaseRed             = FOG_COLOR_R;
    BackgroundVert[2].fBaseGreen           = FOG_COLOR_G;
    BackgroundVert[2].fBaseBlue            = FOG_COLOR_B;

    /* Initialize the background plane to point to each vertex in a screen-sized strip. */
    for (i = 0; i < 3; i++)
        BackPlane[i] = &BackgroundVert[i];

    /* Set the background plane to the vertices filled in above. */
    kmSetBackGroundPlane ((void *) BackPlane, KM_VERTEXTYPE_01, sizeof (KMVERTEX_01));

	/******************** SETUP VERTEX-FOGGING *********************/

	/* Lets enable vertex-fogging in the 'OpaqueTextureContext' */
	/* We could have set these items in the statically declared */
	/* structure, but they are being done here for clarity.     */
	/* Smooth vertex fog also requires Gouraud shading enabled. */

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

    /* 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)) );
	kmSetFogVertexColor( packedARGB );

    /* The main render loop. */
    while (TRUE)
    {
		/* Gradually change fog density... */
		fogDensity += fogDelta;

		if (fogDensity <= 0.0  ||  fogDensity >= 1.0)
		{
			fogDensity = MIN( fogDensity, 1.0 );
			fogDensity = MAX( fogDensity, 0.0 );
			fogDelta = -fogDelta;
		}


		/* 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                   = 170;
        CurrentStrip_05[0].fY                   = 340;
        CurrentStrip_05[0].u.fZ                 = 1.0f;
        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[0].fOffsetAlpha         = fogDensity; /* Set vertex fog */
        CurrentStrip_05[0].fOffsetRed           = 0.0f;
        CurrentStrip_05[0].fOffsetGreen         = 0.0f;
        CurrentStrip_05[0].fOffsetBlue          = 0.0f;

        CurrentStrip_05[1].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[1].fX                   = 170;
        CurrentStrip_05[1].fY                   = 140;
        CurrentStrip_05[1].u.fZ                 = 1.0f;
        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[1].fOffsetAlpha         = fogDensity; /* Set vertex fog */
        CurrentStrip_05[1].fOffsetRed           = 0.0f;
        CurrentStrip_05[1].fOffsetGreen         = 0.0f;
        CurrentStrip_05[1].fOffsetBlue          = 0.0f;

        CurrentStrip_05[2].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[2].fX                   = 470;
        CurrentStrip_05[2].fY                   = 340;
        CurrentStrip_05[2].u.fZ                 = 1.0f;
        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[2].fOffsetAlpha         = fogDensity; /* Set vertex fog */
        CurrentStrip_05[2].fOffsetRed           = 0.0f;
        CurrentStrip_05[2].fOffsetGreen         = 0.0f;
        CurrentStrip_05[2].fOffsetBlue          = 0.0f;

        CurrentStrip_05[3].ParamControlWord     = KM_VERTEXPARAM_ENDOFSTRIP;
        CurrentStrip_05[3].fX                   = 470;
        CurrentStrip_05[3].fY                   = 140;
        CurrentStrip_05[3].u.fZ                 = 1.0f;
        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;
		CurrentStrip_05[3].fOffsetAlpha         = fogDensity; /* Set vertex fog */
        CurrentStrip_05[3].fOffsetRed           = 0.0f;
        CurrentStrip_05[3].fOffsetGreen         = 0.0f;
        CurrentStrip_05[3].fOffsetBlue          = 0.0f;

		/* And submit these vertices to Kamui */
        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;
}

