//////////////////////////////////////////////////////////////////////
//
// Project  : BRender "Teapot In Space" sample
//            Copyright (c) 1996 Argonaut Technologies Ltd.
//            All rights reserved.
//
// Author         : Dan Piponi
// Demo Framework : Steve Williams 
//
// File     : RDEMO.C
//
// Requires : BRender V1.3
//            Microsoft DirectX 3.0, Win95/NT or DOS
//
// Comments : Simulated reflections by rendering into environment maps
//
// Modifications : Owain Green, modified to under new demo framework
// 
//////////////////////////////////////////////////////////////////////

#define MOON "moon.pix"
#define AST "ast.pix"
#define PLANET "planet.pix"
#define EARTH "earth.pix"
#define RDEMOMAT "rdemo.mat"

#include "..\misc\br_demo.h"
#include "..\pickdemo\pickdemo.h"

#define PmFree(x) if(x){ BrPixelmapFree(x); x= NULL ; }

static br_pixelmap *screen_buffer = NULL;
static br_pixelmap *depth_buffer = NULL;
static br_pixelmap *back_buffer = NULL;
static br_pixelmap *palette = NULL;

/* This demo simulates a giant reflecting teapot hanging above a */
/* spinning earth */

static int RotateTeapot = 0;
static int moon_choice = 0;
static int earth_choice = 0;

static br_actor *world, *teapot, *earth, *moon, *camera, *mirror_view, *light1, *env;
static br_actor *pair, *system11, *light2;
static br_pixelmap *mirror_depth, *pShade, *earth_pm, *moon_pm;
static br_pixelmap *mirror_pm, *envmap1_pm;
static br_uint_32 envmap1_colour;
static br_colour *pal_entry;
static br_matrix34 rotation;
static br_material *moon_map, *earth_map, *mirror;
static br_angle theta = 0;
static br_material *mats[20], *mat;
static int i, n;

static long mouse_x, mouse_y;
static unsigned long mouse_buttons;

br_error BrDemo_InitApp(int argc, char **argv)
{
	br_error r ;
	
	// Allocate screen, back buffer, Z buffer and palette

	r = BrDemo_Allocate_Pixelmaps( "Reflection Demo",
					    &screen_buffer,&back_buffer,&depth_buffer,
									&palette, "winstd.pal" );

	if ( r != BRE_OK )
	   return r ;

	BrZbBegin(screen_buffer->type, BR_PMT_DEPTH_16);

	BrTableFindHook(BrTableFindFailedLoad);
	BrMapFindHook(BrMapFindFailedLoad);
	
	// Load a shade table 

	pShade = NULL ;

	switch ( screen_buffer->type )
	{
	case BR_PMT_INDEX_8 : pShade = BrPixelmapLoad("winsh8.tab");
				  break ;

	case BR_PMT_RGB_555 : pShade = BrPixelmapLoad("winsh15.tab");
				  break ;

	case BR_PMT_RGB_565 : pShade = BrPixelmapLoad("winsh16.tab");
				  break ;
	}

	if ( !pShade )
	{
		BR_ERROR0("Could not load shade table");
		return BRE_FAIL ;
	}

	BrTableAdd(pShade);

	// Load up materials
	
	n = BrFmtScriptMaterialLoadMany(RDEMOMAT, mats, BR_ASIZE(mats));
	BrMaterialAddMany(mats, n);
	
	/* Complete definition of mirror texture. Note that the colour_map */
	/* is a newly allocated pixelmap into which we'll render later */

	mirror = BrMaterialFind("mirror");

    
	mirror->colour_map = mirror_pm = BrPixelmapMatchTypedSized(back_buffer, BR_PMMATCH_HIDDEN_BUFFER, BR_PMT_INDEX_8, 256, 256);

	if ( mirror_pm )
	   BrMapAdd(mirror_pm);
	else {
	   mirror->colour_map = mirror_pm = BrPixelmapMatchSized(back_buffer, BR_PMMATCH_HIDDEN_BUFFER, 256, 256);
	   if (!mirror_pm) return BRE_FAIL ;
	}

	BrMaterialUpdate(mirror, BR_MATU_ALL);

	mirror_depth = BrPixelmapMatch(mirror_pm, BR_PMMATCH_DEPTH_16);

	/* Load the background for the environment map, or choose a colour if */
	/* the texture pixelmap is of an incompatible type */

	switch (mirror_pm->type) 
	{
		case BR_PMT_INDEX_8:
		envmap1_pm = BrPixelmapLoad("testenv.pix");
		if ( envmap1_pm == NULL )
			BR_ERROR0("Could't load testenv.pix");
		break;

	case BR_PMT_RGB_555:
		envmap1_pm = BrPixelmapLoad("tstenv15.pix");
		if ( envmap1_pm == NULL )
			BR_ERROR0("Could't load tstenv15.pix");
		break;

	case BR_PMT_RGB_565:
		envmap1_pm = BrPixelmapLoad("tstenv16.pix");
		if ( envmap1_pm == NULL )
			BR_ERROR0("Could't load tstenv16.pix");
		break;

	case BR_PMT_RGB_888:
	case BR_PMT_RGBX_888:
	case BR_PMT_RGBA_8888:
		envmap1_colour = (64 << 10) | (64 << 5) | 64;
		break;

	default:
		envmap1_colour = 0;
	}

	// In the beginning... 

	world = BrActorAllocate(BR_ACTOR_NONE, NULL);

	// BrDemo_SetRootActor lets the demo framework know which 
	// actor to enumerate when turning on/off actor based properties.
	
	BrDemo_SetRootActor(world);

	// The earth-teapot system11 forms a single `pair' unit 

	pair = BrActorAdd(world, BrActorAllocate(BR_ACTOR_NONE, NULL));
	pair->t.type = BR_TRANSFORM_MATRIX34;
	BrMatrix34Identity(&pair->t.t.mat);

	system11 = BrActorAdd(pair, BrActorAllocate(BR_ACTOR_NONE, NULL));
	system11->t.type = BR_TRANSFORM_MATRIX34;
	BrMatrix34PostTranslate(&system11->t.t.mat,
							BR_SCALAR(-0.7), BR_SCALAR(0), BR_SCALAR(0));

	// Load teapot and place into `pair'

	teapot = BrActorAdd(pair, BrActorAllocate(BR_ACTOR_MODEL, NULL));
	teapot->model = BrModelLoad("teapot.dat");
	
	if (teapot->model == NULL)
	{
		BR_ERROR0("Couldn't load teapot.dat");
		return BRE_FAIL ;
	}

	BrModelAdd(teapot->model);
	teapot->material = BrMaterialFind("mirror");
	teapot->t.type = BR_TRANSFORM_MATRIX34;
	BrMatrix34Scale(&teapot->t.t.mat,
					BR_SCALAR(0.7), BR_SCALAR(0.7), BR_SCALAR(0.7));
	BrMatrix34PostRotateX(&teapot->t.t.mat, BR_ANGLE_DEG(-70));
	BrMatrix34PostRotateY(&teapot->t.t.mat, BR_ANGLE_DEG(-45));
	BrMatrix34PostTranslate(&teapot->t.t.mat,
							BR_SCALAR(0.7), BR_SCALAR(0), BR_SCALAR(0));

	// Load and place spheres into earth-moon system11

	earth = BrActorAdd(system11, BrActorAllocate(BR_ACTOR_MODEL, NULL));
	earth->model = BrModelLoad("sph32.dat");

	if (earth->model == NULL)
	{
		BR_ERROR0("Couldn't load sph32.dat");
		return BRE_FAIL ;
	}

	BrModelAdd(earth->model);
	earth->material = BrMaterialFind("earth");
	earth->t.type = BR_TRANSFORM_MATRIX34;

	moon = BrActorAdd(system11, BrActorAllocate(BR_ACTOR_MODEL, NULL));
	moon->model = earth->model;
	moon->material = BrMaterialFind("moon");
	moon->t.type = BR_TRANSFORM_MATRIX34;

	// Now place the main camera from which we will view the system11 

	camera = BrActorAdd(world, BrActorAllocate(BR_ACTOR_CAMERA, NULL));
	((br_camera *) camera->type_data)->type = BR_CAMERA_PERSPECTIVE_OLD;
	((br_camera *) camera->type_data)->field_of_view = BR_ANGLE_DEG(45.0);
	((br_camera *) camera->type_data)->hither_z = BR_SCALAR(0.1);
	((br_camera *) camera->type_data)->yon_z = BR_SCALAR(20.0);
	((br_camera *) camera->type_data)->aspect = BR_SCALAR(1.46);
	camera->t.type = BR_TRANSFORM_MATRIX34;
	BrMatrix34Translate(&camera->t.t.mat,
						BR_SCALAR(0.0), BR_SCALAR(0.0), BR_SCALAR(2.0));

	/* This is the camera that will be used to provide the reflection */
	/* of the earth into the teapot */

	mirror_view = BrActorAdd(pair, BrActorAllocate(BR_ACTOR_CAMERA, NULL));
	((br_camera *) mirror_view->type_data)->type = BR_CAMERA_PERSPECTIVE_OLD;
	((br_camera *) mirror_view->type_data)->field_of_view = BR_ANGLE_DEG(90.0);
	((br_camera *) mirror_view->type_data)->hither_z = BR_SCALAR(0.1);
	((br_camera *) mirror_view->type_data)->yon_z = BR_SCALAR(20.0);
	((br_camera *) mirror_view->type_data)->aspect = BR_SCALAR(1.46);
	mirror_view->t.type = BR_TRANSFORM_MATRIX34;

	// Rotate the camera to face the earth... 

	BrMatrix34RotateY(&mirror_view->t.t.mat, BR_ANGLE_DEG(90));

	// ...and move it into the centre of the teapot 

	BrMatrix34PostTranslate(&mirror_view->t.t.mat,
							BR_SCALAR(0.5), BR_SCALAR(0), BR_SCALAR(0));

	// Let there be light...so we can see what we are doing 

	light1 = BrActorAdd(world, BrActorAllocate(BR_ACTOR_LIGHT, NULL));
	((br_light *) light1->type_data)->type = BR_LIGHT_DIRECT;
	((br_light *) light1->type_data)->attenuation_c = BR_SCALAR(1.0);
	light1->t.type = BR_TRANSFORM_MATRIX34;
	BrMatrix34RotateY(&light1->t.t.mat, BR_ANGLE_DEG(35));
	BrMatrix34RotateX(&light1->t.t.mat, BR_ANGLE_DEG(35));

	light2 = BrActorAdd(world, BrActorAllocate(BR_ACTOR_LIGHT, NULL));
	((br_light *) light2->type_data)->type = BR_LIGHT_DIRECT;
	((br_light *) light2->type_data)->attenuation_c = BR_SCALAR(1.0);
	light2->t.type = BR_TRANSFORM_MATRIX34;
	BrMatrix34RotateY(&light2->t.t.mat, BR_ANGLE_DEG(-35));
	BrMatrix34RotateX(&light2->t.t.mat, BR_ANGLE_DEG(-90));

	// Mustn't forget to turn the light on 

	BrLightEnable(light1);
	BrLightEnable(light2);

	/* The environment map is attached to the earth-teapot pair so that */
	/* when rotated the reflection rotates accordingly */

	env = BrActorAdd(pair, BrActorAllocate(BR_ACTOR_NONE, NULL));
	env->t.type = BR_TRANSFORM_MATRIX34;

	/* Rotate environment map so that the centre of it is in the same */
	/* direction as the earth */

	BrMatrix34RotateY(&env->t.t.mat, BR_ANGLE_DEG(-90));
	BrEnvironmentSet(env);

    return BRE_OK ;
} // End BrDemo_InitApp()


br_error BrDemo_Render_Loop() 
{
    // If no buffer into which to render, then quit
	
    if (!back_buffer) 
       return BRE_OK ;

    // Clear buffer and its Z buffer 
    
	BrPixelmapFill(back_buffer, 0);
	BrPixelmapFill(depth_buffer, 0xffffffff);

	/* Copy plain environment map into mirror pixelmap. We will render */
	/* the earth over this. Also clear the Z buffer. */


        BrPixelmapCopy(mirror_pm, envmap1_pm);

	BrPixelmapFill(mirror_depth, 0xffffffff);

	/* When rendering the earth into the reflection we don't want to */
	/* see a view of the inside of the teapot. Turn it off for now */

	teapot->render_style = BR_RSTYLE_NONE;

	/* Render reflection and turn back on teapot */

	BrZbSceneRender(world, mirror_view, mirror_pm, mirror_depth);
	teapot->render_style = BR_RSTYLE_DEFAULT;

	/* Update the texture after it has been rendered to */

	BrMapUpdate(mirror_pm, BR_MAPU_ALL);

	/* Place rotated earth and moon */

	++theta;

	BrMatrix34Scale(&earth->t.t.mat,
					BR_SCALAR(0.35), BR_SCALAR(0.35), BR_SCALAR(0.35));
	BrMatrix34PostRotateX(&earth->t.t.mat, BR_ANGLE_DEG(90));
	BrMatrix34PreRotateZ(&earth->t.t.mat, BR_ANGLE_DEG(-3 * theta));

	BrMatrix34Scale(&moon->t.t.mat,
					BR_SCALAR(0.1), BR_SCALAR(0.1), BR_SCALAR(0.1));
	BrMatrix34PostTranslate(&moon->t.t.mat,
								BR_SCALAR(-0.7), BR_SCALAR(0), BR_SCALAR(0));
	BrMatrix34PostRotateY(&moon->t.t.mat, BR_ANGLE_DEG(-2 * theta));
	
	/*
	 * first time around while loop camera depth of field is set to zero
	 * so nothing is rendered and only backdrop is visible
	 */

	/* And now render the earth along with the shiny teapot */

    BrZbSceneRender(world, camera, back_buffer, depth_buffer);
    BrPixelmapDoubleBuffer(screen_buffer, back_buffer);


	/* Allow user to move the system11 around */

	mouse_x = mouse_y = 0;
	DOSMouseRead(&mouse_x, &mouse_y, &mouse_buttons);

	if (RotateTeapot) {
	  if (mouse_x || mouse_y) {
	    if ((mouse_buttons & BR_MSM_BUTTONR) &&
		(mouse_buttons & BR_MSM_BUTTONL))  {
		BrMatrix34PostTranslate(&teapot->t.t.mat,
		   0, 0, BR_MUL(BR_SCALAR(-mouse_y), BR_SCALAR(0.01)));
	    }
	    else if (mouse_buttons & BR_MSM_BUTTONL) {
		    BrMatrix34PostTranslate(&teapot->t.t.mat,
			  BR_MUL(BR_SCALAR(mouse_x), BR_SCALAR(0.01)),
			  BR_MUL(BR_SCALAR(-mouse_y), BR_SCALAR(0.01)), 0);
		 } 
	    else if (mouse_buttons & BR_MSM_BUTTONR) {
		    BrMatrix34RollingBall(&rotation, mouse_x, -mouse_y, 800);
		    BrMatrix34Pre(&teapot->t.t.mat, &rotation);
	    } 
	  }
	}
	else 
	{

		if (mouse_x || mouse_y)
		{
			if ((mouse_buttons & BR_MSM_BUTTONR) && (mouse_buttons & BR_MSM_BUTTONL)) 
			{
				BrMatrix34PostTranslate(&pair->t.t.mat,
						0, 0, BR_MUL(BR_SCALAR(-mouse_y), BR_SCALAR(0.01)));

			}
			else if (mouse_buttons & BR_MSM_BUTTONL) 
			{
				BrMatrix34PostTranslate(&pair->t.t.mat,
							BR_MUL(BR_SCALAR(mouse_x), BR_SCALAR(0.01)),
						   BR_MUL(BR_SCALAR(-mouse_y), BR_SCALAR(0.01)), 0);
			} 
			else if (mouse_buttons & BR_MSM_BUTTONR) 
			{
				BrMatrix34RollingBall(&rotation, mouse_x, -mouse_y, 800);
				BrMatrix34Pre(&pair->t.t.mat, &rotation);
				
			} 
		}
	}

	if ( DOSKeyTest(SC_Q,0,REPT_FIRST_DOWN) )
	       return BRE_FAIL ;

	if(DOSKeyTest(SC_P,0,REPT_FIRST_DOWN))
	BrPixelmapSave("scrn_dmp.pix", back_buffer);

	if(DOSKeyTest(SC_T,0,REPT_FIRST_DOWN))
		RotateTeapot = !RotateTeapot;

	if(DOSKeyTest(SC_R,0,REPT_FIRST_DOWN)) 
	{
		BrMatrix34Identity(&pair->t.t.mat);
		BrMatrix34Identity(&teapot->t.t.mat);
		BrMatrix34Scale(&teapot->t.t.mat,
						BR_SCALAR(0.7), BR_SCALAR(0.7), BR_SCALAR(0.7));
		BrMatrix34PostRotateX(&teapot->t.t.mat, BR_ANGLE_DEG(-70));
		BrMatrix34PostRotateY(&teapot->t.t.mat, BR_ANGLE_DEG(-45));
		BrMatrix34PostTranslate(&teapot->t.t.mat,
							BR_SCALAR(0.7), BR_SCALAR(0), BR_SCALAR(0));
	}

	if(DOSKeyTest(SC_M,0,REPT_FIRST_DOWN)) 
	{
		moon_choice = !moon_choice;

		mat = BrMaterialFind("moon");
	 
		if (mat == NULL)
		return BRE_FAIL;  

	if (mat->colour_map)
		{
			br_pixelmap *pPix ;
			pPix = mat->colour_map ;
			mat->colour_map = NULL ;
			BrMaterialUpdate( mat, BR_MATU_ALL );
			BrMapRemove(pPix);
			BrPixelmapFree(pPix);
		}

		mat->colour_map = BrPixelmapLoad(moon_choice ? MOON : AST);
		
		if (mat->colour_map)
			BrMapAdd(mat->colour_map);

		BrMaterialUpdate(mat, BR_MATU_ALL);
	}

	if(DOSKeyTest(SC_E,0,REPT_FIRST_DOWN)) 
	{
		earth_choice = !earth_choice;

		mat = BrMaterialFind("earth");

		if (mat == NULL)

	   return BRE_FAIL;

		if (mat->colour_map)
		{
			BrMapRemove(mat->colour_map);
			BrPixelmapFree(mat->colour_map);
			mat->colour_map = NULL;
		}
		
		mat->colour_map = BrPixelmapLoad(earth_choice ? PLANET : EARTH);
		
		if (mat->colour_map)
			BrMapAdd(mat->colour_map);

		BrMaterialUpdate(mat, BR_MATU_ALL);
	}

   return BRE_OK;
}

br_error BrDemo_ShutDown()
{
    PmFree(back_buffer);
    PmFree(depth_buffer);
    back_buffer = NULL;
    depth_buffer = NULL;
    BrZbEnd();
    BrEnd();

    return BRE_OK ;
}


br_error BrDemo_OnResize()
{
	BrDemo_Refresh_Pointers( &screen_buffer, &back_buffer, &depth_buffer );

	return BRE_OK ;
}    
