/*
   KallistiOS ##version##
   bubbles.c

   (c)2002 Dan Potter
*/

#include <kos.h>
#include <math.h>
#include <plx/matrix.h>
#include <plx/prim.h>

/*

This is a Parallax version of the "bubbles" KGL demo. The speed is about
the same, but it uses the Parallax functions instead of KGL.

*/

/* Draws a sphere out of GL_QUADS according to the syntax of glutSolidSphere

   This now uses triangle strips and PVR Direct Rendering for the best
   possible performance.

   Based very loosely on a routine from James Susinno.
*/
static float phase = 0.0f;
static pvr_poly_cxt_t cxt;
static pvr_poly_hdr_t hdr;
static void sphere( float radius, int slices, int stacks ) {
	int	i, j;
	float	pitch, pitch2;
	float	x, y, z, g, b;
	float	yaw, yaw2;
	pvr_dr_state_t	dr_state;

	/* Setup our Direct Render state: pick a store queue and setup QACR0/1 */
	pvr_dr_init(dr_state);

	/* Initialize xmtrx with the values from KGL */
	plx_mat_identity();
	plx_mat3d_apply(PLX_MAT_SCREENVIEW);
	plx_mat3d_apply(PLX_MAT_PROJECTION);
	plx_mat3d_apply(PLX_MAT_MODELVIEW);

	/* Put our own polygon header */
	pvr_prim(&hdr, sizeof(hdr));

	/* Iterate over stacks */
	for ( i=0; i<stacks; i++ ) {
		pitch = 2*M_PI * ( (float)i/(float)stacks );
		pitch2 = 2*M_PI * ( (float)(i+1)/(float)stacks );

		/* Iterate over slices: each entire stack will be one
		   long triangle strip. */
		for ( j=0; j<=slices/2; j++ ) {
			yaw = 2*M_PI * ( (float)j/(float)slices );
			yaw2 = 2*M_PI * ( (float)(j+1)/(float)slices );

			/* x, y+1 */
			x = radius * fcos( yaw ) * fcos( pitch2 );
			y = radius * fsin( pitch2 );
			z = radius * fsin( yaw ) * fcos( pitch2 );
			mat_trans_single(x, y, z);			/* Use ftrv to transform */
			g = fcos( yaw*2 ) / 2.0f + 0.5f;
			b = fsin( phase + pitch2 ) / 2.0f + 0.5f;
			plx_vert_fnd(&dr_state, PVR_CMD_VERTEX,
				x, y, z, 0.5f,
				0.0f, g, b);
                
			/* x, y */
			x = radius * fcos( yaw ) * fcos( pitch );
			y = radius * fsin( pitch );
			z = radius * fsin( yaw ) * fcos( pitch );
			mat_trans_single(x, y, z);
			g = fcos( yaw*2 ) / 2.0f + 0.5f;
			b = fsin( phase + pitch ) / 2.0f + 0.5f;

			if (j == (slices/2)) {
				plx_vert_fnd(&dr_state, PVR_CMD_VERTEX_EOL,
					x, y, z, 0.5f,
					0.0f, g, b);
			} else {
				plx_vert_fnd(&dr_state, PVR_CMD_VERTEX,
					x, y, z, 0.5f,
					0.0f, g, b);
			}
		}
	}
}

#define SPHERE_CNT 12
static float r = 0;
static void sphere_frame_opaque() {
	int i;

	vid_border_color(255, 0, 0);
	pvr_wait_ready();
	vid_border_color(0, 255, 0);

	pvr_scene_begin();
	pvr_list_begin(PVR_LIST_OP_POLY);

	plx_mat3d_identity();
	plx_mat3d_translate(0.0f, 0.0f, -12.0f);
	plx_mat3d_rotate(r * 2, 0.75f, 1.0f, 0.5f);
	plx_mat3d_push();

	for (i=0; i<SPHERE_CNT; i++) {
		plx_mat3d_translate(6.0f * fcos(i * 2*M_PI / SPHERE_CNT), 0.0f, 6.0f * fsin(i * 2*M_PI / SPHERE_CNT));
		plx_mat3d_rotate(r, 1.0f, 1.0f, 1.0f);
		sphere(1.2f, 20, 20);
		if (i < (SPHERE_CNT-1))
			plx_mat3d_peek();
	}
	plx_mat3d_pop();

	plx_mat3d_identity();
	plx_mat3d_translate(0.0f, 0.0f, -12.0f);
	plx_mat3d_rotate(-r * 2, 0.75f, 1.0f, 0.5f);
	plx_mat3d_push();

	for (i=0; i<SPHERE_CNT; i++) {
		plx_mat3d_translate(3.0f * fcos(i * 2*M_PI / SPHERE_CNT), 0.0f, 3.0f * fsin(i * 2*M_PI / SPHERE_CNT));
		plx_mat3d_rotate(r, 1.0f, 1.0f, 1.0f);
		sphere(0.8f, 20, 20);
		if (i < (SPHERE_CNT-1))
			plx_mat3d_peek();
	}
	plx_mat3d_pop();

	vid_border_color(0, 0, 255);
	pvr_scene_finish();

	r++;
	phase += 2*M_PI / 240.0f;
}

static void sphere_frame_trans() {
	int i;

	vid_border_color(255, 0, 0);
	pvr_wait_ready();
	vid_border_color(0, 255, 0);

	pvr_scene_begin();
	pvr_list_begin(PVR_LIST_TR_POLY);

	plx_mat3d_identity();
	plx_mat3d_translate(0.0f, 0.0f, -12.0f);
	plx_mat3d_rotate(r * 2, 0.75f, 1.0f, 0.5f);
	plx_mat3d_push();

	for (i=0; i<SPHERE_CNT; i++) {
		plx_mat3d_translate(4.0f * fcos(i * 2*M_PI / SPHERE_CNT), 0.0f, 4.0f * fsin(i * 2*M_PI / SPHERE_CNT));
		plx_mat3d_rotate(r, 1.0f, 1.0f, 1.0f);
		sphere(1.0f, 20, 20);
		if (i < (SPHERE_CNT-1))
			plx_mat3d_peek();
	}

	plx_mat3d_pop();

	vid_border_color(0, 0, 255);
	pvr_scene_finish();

	r++;
	phase += 2*M_PI / 240.0f;
}

void do_sphere_test() {
	uint8		addr;
	cont_cond_t	cond;
	int		mode = 0;
	int		a_was_down = 0;

	pvr_set_bg_color(0.2f, 0.0f, 0.4f);

	pvr_poly_cxt_col(&cxt, PVR_LIST_OP_POLY);
	cxt.gen.culling = PVR_CULLING_NONE;
	pvr_poly_compile(&hdr, &cxt);

	for (;;) {
		if (!mode)
			sphere_frame_opaque();
		else
			sphere_frame_trans();
	
		addr = maple_first_controller();
		if (addr == 0)
			continue;
		if (cont_get_cond(addr, &cond) < 0)
			continue;
		if (!(cond.buttons & CONT_START)) {
			return;
		}
		if (!(cond.buttons & CONT_A)) {
			if (a_was_down) continue;
			a_was_down = 1;
			mode ^= 1;
			switch(mode) {
			case 0:	/* Opaque */
				pvr_poly_cxt_col(&cxt, PVR_LIST_OP_POLY);
				cxt.gen.culling = PVR_CULLING_NONE;
				pvr_poly_compile(&hdr, &cxt);
				break;
			case 1:	/* Translucent */
				pvr_poly_cxt_col(&cxt, PVR_LIST_TR_POLY);
				cxt.gen.culling = PVR_CULLING_NONE;
				pvr_poly_compile(&hdr, &cxt);
				break;
			}
		} else {
			a_was_down = 0;
		}
	}
}

pvr_init_params_t params = {
	/* Enable opaque and translucent polygons with size 16 */
	{ PVR_BINSIZE_16, PVR_BINSIZE_0, PVR_BINSIZE_16, PVR_BINSIZE_0, PVR_BINSIZE_0 },
	
	/* Vertex buffer size 512K */
	512*1024
};
	
int main(int argc, char **argv) {
	/* Init PVR API */
	if (pvr_init(&params) < 0)
		return -1;

	/* Init matrices */
	plx_mat3d_init();
	plx_mat3d_mode(PLX_MAT_PROJECTION);
	plx_mat3d_identity();
	plx_mat3d_perspective(45.0f, 640.0f / 480.0f, 0.1f, 100.0f);
	plx_mat3d_mode(PLX_MAT_MODELVIEW);

	/* Do the test */
	printf("Bubbles KGL sample: press START to exit, A to toggle sphere type\n");
	do_sphere_test();

	return 0;
}

