//////////////////////////////////////////////////////////////////////
//
// Project  : BRender Robot Demo
//            Copyright (c) 1996 Argonaut Technologies Ltd.
//            All rights reserved.
//
// Author   : Sam Littlewood / Steve Williams 
//
// File     : ROBOT.C
//
// Requires : BRender V1.3
//            Microsoft DirectX 3.0, Win95/NT or DOS
//
// Comments : Robot specific source.
//
//////////////////////////////////////////////////////////////////////

#include <math.h>
#include <stdio.h>
#include <string.h>
#include "..\misc\br_demo.h"
#include "robot.h"


// Module wide variables
// ---------------------

static br_actor *camera, *light, *world, *actors;

static br_pixelmap *screen, *screen_buffer, *back_screen, *palette, *depth_buffer;

static int total_frames;

// All frames in animation and all transforms for a single frame. 

static struct ani_frame frames[MAXFRAMES];
static struct ani_transform transforms[MAXTRANSFORMS];


br_error LoadShadeTable( int type )
{
	br_pixelmap *pShade ;

	// Load a shade table 

	pShade = NULL ;

    switch ( 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);

    return BRE_OK ;
}

void
LoadTexture()
{
	br_pixelmap    *tile, *mask;
	tile = BrPixelmapLoad("tile01.pix");
	mask = BrPixelmapLoad("mask.pix");
	if (!((int) mask | (int) tile))
		BR_ERROR0("Failed to load textures.");

	BrMapAdd(tile);
	BrMapAdd(mask);
}

void
LoadMaterial()
{
	br_material    *material[10];
	int             n;

	n = BrMaterialLoadMany("robot.mat", material, 10);
	if (!n)
		BR_ERROR0("Failed to load materials.");

	BrMaterialAddMany(material, n);
}

void
LoadModel()
{
	br_model       *model[40];
	int             n;

	n = BrModelLoadMany("robot.dat", model, 40);
	if (!n)
		BR_ERROR0("Failed to load any models");

	BrModelAddMany(model, n);
}

br_actor       *
LoadActors()
{
	br_actor       *root;

	root = BrActorLoad("robot.act");
	if (root == NULL) {
		BR_ERROR0("Failed to load actor file.");
	}
	BrMatrix34RotateX(&root->t.t.mat, BR_ANGLE_DEG(-90.0));
	return root;
}


 
void
LoadVUE(char *file_name, br_actor * root)
{
	FILE           *file;
	int             f = -1, i;
	char            buff[1024];
	char            name[80];
	float           v[4][3];
	struct ani_transform *t = transforms;

	file = fopen(file_name, "r");

	while (!feof(file)) {
		fgets(buff, 1024, file);

		/* Is it a frame specification line? */

		if (sscanf(buff, "frame %d", &i) > 0) {
			if (f > 0)
				frames[f].ntransforms = t - frames[f].transforms;
			f = i;
			frames[f].transforms = t;
		}
		/* Or a transform specification? */

		if (sscanf(buff,
		 "transform \"%[^\"]\" %f %f %f %f %f %f %f %f %f %f %f %f",
				name,
				&v[0][0], &v[0][1], &v[0][2],
				&v[1][0], &v[1][1], &v[1][2],
				&v[2][0], &v[2][1], &v[2][2],
				&v[3][0], &v[3][1], &v[3][2]) > 0
			) {
			int             k, l;

			/* Found a transform so convert it to scalars */

			for (k = 0; k < 4; k++)
				for (l = 0; l < 3; l++)
					t->mat.m[k][l] = BrFloatToScalar(v[k][l]);

			t->actor = BrActorSearch(root, name);

			if (t->actor != NULL)
				t++;
		}
	}

	fclose(file);

	if (f > 0)
		frames[f].ntransforms = t - frames[f].transforms;

	total_frames = f + 1;
}



br_error BrDemo_Render_Loop()
{

	int i;
    static int current_frame = 0;
    static int frame_delta = 1;
	br_transform store_root_transform;

	if ( !back_screen )
		return BRE_OK ;

	// Step backwards and forward, run backwards and forward and stop.
	
	if (DOSKeyTest(SC_M, 0, REPT_FIRST_DOWN))
	  current_frame--;
	
	if (DOSKeyTest(SC_P, 0, REPT_FIRST_DOWN))
	  current_frame++;

	if (DOSKeyTest(SC_R, 0, 0))
	  frame_delta = -1;
	
	if (DOSKeyTest(SC_F, 0, 0))
	  frame_delta = 1;
	
	if (DOSKeyTest(SC_S, 0, 0))
	  frame_delta = 0;

	// Store current root actor transform.
	BrTransformToTransform(&store_root_transform, &(actors->t));

	// Play VUE file by loading transforms into actors.
		
	if (total_frames != 0)
	{
		// Clamp frame number to valid range.

		if (current_frame < 0)
			current_frame = total_frames - 1;
	  
		if (current_frame >= total_frames)
			current_frame = 0;


		// Apply animation to world.
		if (frame_delta != 0)  
		for (i = 0; i < frames[current_frame].ntransforms; i++)
			frames[current_frame].transforms[i].actor->t.t.mat =
				frames[current_frame].transforms[i].mat;

		// Move to next frame.

		current_frame += frame_delta;

	}
	
	// Restore current world transform.
	BrTransformToTransform(&(actors->t), &store_root_transform);

	// Render.

	BrPixelmapFill(back_screen, 0x100 );
	BrPixelmapFill(depth_buffer, 0xffffffff);
	BrZbSceneRender(world, camera, back_screen, depth_buffer);
	BrPixelmapDoubleBuffer(screen, back_screen);
      
    
	// Mouse controls.

    if ( Control(world, camera, actors) )
       return BRE_FAIL ;


   	return(BRE_OK);
}
   

br_error BrDemo_ShutDown()
{
   // Close down

   BrPixelmapFree(depth_buffer);
   BrPixelmapFree(back_screen);

   depth_buffer = NULL ;
   back_screen = NULL ;

   BrZbEnd();

   return(BRE_OK);
}

   
br_error BrDemo_InitApp(int argc, char **argv)
{
   br_error r ;

   // Create world from VUE file.

   world = BrActorAllocate(BR_ACTOR_NONE, NULL);

   r = BrDemo_Allocate_Pixelmaps( "Robot Demo",
	                              &screen, &back_screen, 
	                              &depth_buffer, &palette, 
								  "winstd.pal" );

   if ( r != BRE_OK )
      return r ;

   screen->origin_x = screen->width/2;
   screen->origin_y = screen->height/2;
   back_screen->origin_x = back_screen->width/2;
   back_screen->origin_y = back_screen->height/2;

   BrDemo_SetRootActor( world );

   BrZbBegin(back_screen->type, depth_buffer->type);

   LoadShadeTable(back_screen->type);
   LoadTexture();
   LoadMaterial();
   LoadModel();
   actors = LoadActors();
   BrActorAdd(world, actors);
   LoadVUE("robot.vue", actors);
 
   light = BrActorAllocate(BR_ACTOR_LIGHT, NULL);
   ((br_light *) light->type_data)->type = BR_LIGHT_POINT;
   ((br_light *) light->type_data)->colour = BR_COLOUR_RGB(255, 255, 255);
   BrActorAdd(world, light);
   BrLightEnable(light);
   camera = BrActorAllocate(BR_ACTOR_CAMERA, NULL);
   camera->identifier = "camera";
   ((br_camera *) camera->type_data)->type = BR_CAMERA_PERSPECTIVE;
   ((br_camera *) camera->type_data)->field_of_view = BR_ANGLE_DEG(55.0);
   ((br_camera *) camera->type_data)->hither_z = BR_SCALAR(50.0);
   ((br_camera *) camera->type_data)->yon_z = BR_SCALAR(10000.0);
   ((br_camera *) camera->type_data)->aspect = BR_SCALAR(1.6);
   camera->t.type = BR_TRANSFORM_MATRIX34;

   // Shift camera as required by size of model.
   BrMatrix34Translate(&camera->t.t.mat, BR_SCALAR(0.0), BR_SCALAR(0.0), BR_SCALAR(1700.0));
   BrActorAdd(world, camera);
   BrMatrix34Copy(&light->t.t.mat, &camera->t.t.mat);

   return BRE_OK ;

}



// User controls. 

int Control(br_actor *world, br_actor *observer, br_actor *target)
{
   ControlObject(target);
   
   if(DOSKeyTest(SC_Q,0,REPT_FIRST_DOWN))
      return 1;

   return 0;
}



// Read the mouse to control an actor's translation and rotation.

void ControlObject(br_actor *target)
{
   static br_int_32 mouse_x,mouse_y;
   br_uint_32 mouse_buttons;
   br_matrix34 mat_target, mat_roll;
   br_scalar tx,ty,tz;

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

   // Mouse controls
   
   if ( ( mouse_buttons & BR_MSM_BUTTONL ) &&
       ( mouse_buttons & BR_MSM_BUTTONR ) )
   {
      // Drag object around in x/y plane

      tx = BR_MUL(BR_SCALAR(mouse_x),MSCALE * 500.0);
      tz = BR_MUL(BR_SCALAR(mouse_y),MSCALE * 500.0);

      BrMatrix34PostTranslate(&target->t.t.mat,tx,BR_SCALAR(0.0),tz);
      
   } 
   else if( mouse_buttons & BR_MSM_BUTTONL ) 
   {
      // Move object along z axis

      tx =  BR_MUL(BR_SCALAR(mouse_x),MSCALE * 500.0);
      ty = -BR_MUL(BR_SCALAR(mouse_y),MSCALE * 500.0);

      BrMatrix34PostTranslate(&target->t.t.mat,tx,ty,BR_SCALAR(0.0));
   } 
   else if ( mouse_buttons & BR_MSM_BUTTONR ) 
   {
      // Rotate object via rolling ball interface

      BrMatrix34RollingBall(&mat_roll,mouse_x,-mouse_y, ROLLING_BALL_SENSITIVITY );

      // Only modify the top 3x3

      BrMatrix34Copy(&mat_target, &target->t.t.mat);
      Matrix33Mul(&target->t.t.mat, &mat_target, &mat_roll);
   }

   mouse_x = 0;
   mouse_y = 0;
}



// Screen resize function.

br_error BrDemo_OnResize()
{
	BrDemo_Refresh_Pointers( &screen_buffer, &back_screen, &depth_buffer );
   
	return BRE_OK ;
}



// Matrix multiply for rotation only.

#define A(x,y) A->m[x][y]
#define B(x,y) B->m[x][y]
#define C(x,y) C->m[x][y]

void Matrix33Mul(br_matrix34 *A, br_matrix34 *B, br_matrix34 *C)
{
   A(0,0) = BR_MAC3(B(0,0),C(0,0), B(0,1),C(1,0), B(0,2),C(2,0));
   A(0,1) = BR_MAC3(B(0,0),C(0,1), B(0,1),C(1,1), B(0,2),C(2,1));
   A(0,2) = BR_MAC3(B(0,0),C(0,2), B(0,1),C(1,2), B(0,2),C(2,2));

   A(1,0) = BR_MAC3(B(1,0),C(0,0), B(1,1),C(1,0), B(1,2),C(2,0));
   A(1,1) = BR_MAC3(B(1,0),C(0,1), B(1,1),C(1,1), B(1,2),C(2,1));
   A(1,2) = BR_MAC3(B(1,0),C(0,2), B(1,1),C(1,2), B(1,2),C(2,2));

   A(2,0) = BR_MAC3(B(2,0),C(0,0), B(2,1),C(1,0), B(2,2),C(2,0));
   A(2,1) = BR_MAC3(B(2,0),C(0,1), B(2,1),C(1,1), B(2,2),C(2,1));
   A(2,2) = BR_MAC3(B(2,0),C(0,2), B(2,1),C(1,2), B(2,2),C(2,2));
}



