/*****************************************************************************
 * Microchip Graphics Library Demo Application
 * This program shows how to use the Graphics Primitives Layer.
 *****************************************************************************
 * FileName:        MainDemo.c
 * Dependencies:    MainDemo.h
 * Processor:       PIC24, PIC32
 * Compiler:       	MPLAB C30 V3.00, MPLAB C32
 * Linker:          MPLAB LINK30, MPLAB LINK32
 * Company:         Microchip Technology Incorporated
 *
 * Software License Agreement
 *
 * Copyright ?2007 Microchip Technology Inc.  All rights reserved.
 * Microchip licenses to you the right to use, modify, copy and distribute
 * Software only when embedded on a Microchip microcontroller or digital
 * signal controller, which is integrated into your product or third party
 * product (pursuant to the sublicense terms in the accompanying license
 * agreement).  
 *
 * You should refer to the license agreement accompanying this Software
 * for additional information regarding your rights and obligations.
 *
 * SOFTWARE AND DOCUMENTATION ARE PROVIDED “AS IS?WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY
 * OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR
 * PURPOSE. IN NO EVENT SHALL MICROCHIP OR ITS LICENSORS BE LIABLE OR
 * OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION,
 * BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT
 * DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL,
 * INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA,
 * COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY
 * CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF),
 * OR OTHER SIMILAR COSTS.
 *
 * Author               Date        Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Anton Alkhimenok		30/05/07	...
 *****************************************************************************/

/*
******************************************************************************
* This is a quality assurance demo for the hardware platform
* PIC24-Eval Rev C + TY280T-240320-BO Rev 1B
* One should short JP6 on the LCD module for this demo.
*
* This program has been modified from the original Microchip Primitive Demo
* First the LCD will show simple RGB color, primitive graphic, and bmp file
* hard-code. Finally a screen showing the instruction to connect RS232 port
* to Windows HyperTerminal will pop up. Use the baud rate of 19200bps, 8-n-1
* for HyperTerminal. Follow the instruction to test the nandFlash.
* One major change to this project from previous versions is on the uart part.
* UART1 used in the past but this version swapped to UART2. For simplcity,
* the source code comm.c and monitor.c have been employed for stdio functions.
* A different version of printf as xprintf is now in effect.
*
* Programmer : John Leung (www.TechToys.com.hk)
* Date: 23 Jan 2009
******************************************************************************
*/

/*
******************************************************************************
* Program modified for the microntroller PIC24FJ256GA110 on PIC24-Eval-RevC
* The major change from PIC24FJ256GA110 t0 PIC24FJ128GA010 is that, we
* need to initial the associated peripheral features to RPx. For example,
* we need to initialize RP10 pin for U2RX, RP17 pin to U2TX for UART. 
* Otherwise, UART feature will NOT work! This is done by the uart_init()
* function inside comm.c
* 
* Date: 3rd Feb 2009
* Programmer: John Leung at TechToys Co. (www.TechToys.com.hk)
******************************************************************************
*/

/*
******************************************************************************
* Compiler version upgraded to version 3.11b Student version in order for
* successful compile for PIC24FJ256GA110, otherwise heap allocation problem.
* 
* Date: 13th Feb 2009
* Programmer: John Leung at TechToys Co. (www.TechToys.com.hk)
******************************************************************************
*/

/*
******************************************************************************
* Code modified by a customer, Emmanuel from France for 3D cube demo using
* 2.8" TFT LCD with Touch Panel (TY280T-240320-BO Rev 1B) on 
* PIC24-Eval-Board C with PIC24FJ256GA110 mcu option
* Date: 23th Feb 2009
******************************************************************************
*/

#include "MainDemo.h"
#include "3dmath.h"

// Configuration bits
// Configure fast RC oscillator with PLL and RC15 pin for Fosc/2 function	

_CONFIG2(FNOSC_FRCPLL & /*OSCIOFNC_OFF & */ OSCIOFNC_ON & POSCMOD_NONE)
_CONFIG1(JTAGEN_OFF & FWDTEN_OFF & ICS_PGx2)   // JTAG off, watchdog timer off

INT16U j;
INT8U dataInput;
INT16U DeviceID;
INT8U dataBuffer[NAND_PAGE_SIZE_BYTE];

///////////////////////////////// FONTS ///////////////////////////////////////
extern FONT_FLASH Font25;
extern FONT_FLASH Font35;

/////////////////////////////// PICTURES //////////////////////////////////////
extern BITMAP_FLASH flower16bit;
extern BITMAP_FLASH flower8bit;
extern BITMAP_FLASH flower4bit;
extern BITMAP_FLASH flower1bit;

//*******************
void CreateRotateMat(INT8U rx,INT8U ry,INT8U rz);
void TransformVec(Vector3 res, Vector3 v, Matrix4x4 m);

void SetFov(float newFov);
void RecalcDists(void);
void Project(Vector3 coords);

void RenderFrame(void);
void InitTrigoTable(void);

void CreateBox();
void Oscillate();

// clamps the given angle to the range between 0 and 180 degrees
#define CLAMP_ANGLE(x)							\
{												\
	if(x >=180) x = 0;			\
}

#define NUM_POINTS            14
#define HALF_DISPLAY_WIDTH  240.0 / 2 //64.0    // half width of the used display in pixels
#define HALF_DISPLAY_HEIGHT 320.0 / 2 //32.0    // half height of the used display in pixels
#define PI 3.1415927

SHORT width, height;

// defines whether FPS should be displayed or not
//#define SHOW_FPS

// string used for text output
char     str[20];

float		fov,							// FieldOfView angle
			distX,							// Focal distance to X projection plane
			distY;							// Focal distance to Y projection plane

// rotation angles of the 3D-object
INT8U		rotX = 0,
			rotY = 0,
			rotZ = 0;

// position of the 3D-object
float    posX = 0,
         posY = 0,
         posZ = 5.5;


Vector3  points[NUM_POINTS];
BYTE     indices[24];

float    _SIN[180];  // variables for the precalculated sin &
float    _COS[180];  // cos values


Matrix4x4	temp,MatWorld;			// Transformation matrices
float	      scrX,scrY;
Vector3	   coordsWorld;


INT16U counter=0;
float frametime_i;
float fps;

// timer1 overflow ISR used for fps-calculation
//#define int_timer1
//void t1_isr()
//{
//   counter++;
//}

// define the 3D-object
void CreateBox()
{
   // 8 corners of the box
   V3_Set(points[0],-1.0,-1.0,-1.0);
   V3_Set(points[1],1.0,-1.0,-1.0);
   V3_Set(points[2],-1.0, 1.0,-1.0);
   V3_Set(points[3],1.0, 1.0,-1.0);
   V3_Set(points[4],-1.0,-1.0, 1.0);
   V3_Set(points[5], 1.0,-1.0, 1.0);
   V3_Set(points[6],-1.0, 1.0, 1.0);
   V3_Set(points[7],1.0, 1.0, 1.0);

   // coordinates of the oscillating spheres in
   // the center of the box
   V3_Set(points[8],0.0,0.0, 0.35);
   V3_Set(points[9], 0.0,0.0, -0.35);
   V3_Set(points[10],0.0, 0.35, 0.0);
   V3_Set(points[11],0.0, -0.35, 0.0);
   V3_Set(points[12],-0.35, 0.0, 0.0);
   V3_Set(points[13],0.35, 0.0, 0.0);

   // define indices for correct connections between the box-vertices
   indices[0]=0;
   indices[1]=1;
   indices[2]=2;
   indices[3]=3;
   indices[4]=4;
   indices[5]=5;
   indices[6]=6;
   indices[7]=7;
   indices[8]=0;
   indices[9]=4;
   indices[10]=1;
   indices[11]=5;
   indices[12]=2;
   indices[13]=6;
   indices[14]=3;
   indices[15]=7;
   indices[16]=0;
   indices[17]=2;
   indices[18]=1;
   indices[19]=3;
   indices[20]=4;
   indices[21]=6;
   indices[22]=5;
   indices[23]=7;
}

// animate the spheres in the box
void Oscillate()
{
   static int osc=0;
   osc+=2;
   CLAMP_ANGLE(osc);

   V3_Set(points[8],0.0,0.0, 1.5*_SIN[osc]);
   V3_Set(points[9], 0.0,0.0, -1.5*_SIN[osc]);
   V3_Set(points[10],0.0, 1.5*_SIN[osc], 0.0);
   V3_Set(points[11],0.0, -1.5*_SIN[osc], 0.0);
   V3_Set(points[12],-1.5*_SIN[osc], 0.0, 0.0);
   V3_Set(points[13],1.5*_SIN[osc], 0.0, 0.0);
}

// calculate sin/cos-table
void InitTrigoTable(void)
{
   INT8U i;
   float a;
   for(i=0; i<180; i++)
   {
      a=(float)i*PI/90.0;
      _SIN[i]=sin(a);
      _COS[i]=cos(a);
   }
}

// calculate a rotation matrix
void CreateRotateMat(INT8U rx,INT8U ry,INT8U rz)
{
   Matrix4x4   mrot;

   // multiply the world-matrix with the rotation matrices
	M4X4_Copy(temp,MatWorld);

    M4X4_Identity(mrot);
	mrot[1][1] = _COS[rx];
	mrot[1][2] = _SIN[rx];
	mrot[2][1] = -mrot[1][2];
	mrot[2][2] = mrot[1][1];

	M4X4_Mul(MatWorld,mrot,temp);

	M4X4_Identity(mrot);
	mrot[0][0] = _COS[ry];
	mrot[0][2] = -_SIN[ry];
	mrot[2][0] = -mrot[0][2];
	mrot[2][2] = mrot[0][0];

	M4X4_Mul(temp,mrot,MatWorld);

	M4X4_Identity(mrot);
	mrot[0][0] = _COS[rz];
	mrot[0][1] = _SIN[rz];
	mrot[1][0] = -mrot[0][1];
	mrot[1][1] = mrot[0][0];

	M4X4_Mul(MatWorld,mrot,temp);

}

// Sets a field-of-view angle
void SetFov(float newFov)
{
	fov = newFov;

	// Recalc focal distances
	RecalcDists();
}

// (Re)calculates the distances to the X/Y projection planes
void RecalcDists(void)
{
	float cotFOV;
    cotFOV = cos(fov/2.0) / sin(fov/2.0);

	distX = cotFOV * HALF_DISPLAY_WIDTH;
	distY = cotFOV * HALF_DISPLAY_HEIGHT;
}

// Converts 3D coordinates to 2D screen coords
void Project(Vector3 coords)
{
    float invZ;

	// Skip if z = 0
	if(coords[2] == 0)
		return;

	invZ = 1.0f / coords[2];

	// Calculate the projected values using the formulas
	scrX = coords[0] * distX * invZ + HALF_DISPLAY_WIDTH;
	scrY = -coords[1] * distY * invZ + HALF_DISPLAY_HEIGHT;
}

// render the cube
void RenderFrame(void)
{
	INT8U i,t;
	INT8U	x1,y1,x2,y2;

// ****
	
	// Clears old cube
	
	SetColor(BLACK);
	
	// Loop through the lines
	for(i = 0;i <12;i++)
	{
		// Transform and project first point
        t=indices[i*2];
		MatrixVectorMul(coordsWorld,points[t],MatWorld);
		Project(coordsWorld);

		x1 = (INT8U)scrX;
		y1 = (INT8U)scrY;

		// Transform and project second point
        t=indices[i*2+1];
		MatrixVectorMul(coordsWorld,points[t],MatWorld);
		Project(coordsWorld);

		x2 = (INT8U)scrX;
		y2 = (INT8U)scrY;

		// Draw the line
		Line(x1,y1,x2,y2);
	}

   // draw the spheres
	for(i = 0;i <14;i++)
	{
 		MatrixVectorMul(coordsWorld,points[i],MatWorld);
		Project(coordsWorld);

		x2 = (INT8U)scrX;
		y2 = (INT8U)scrY;
		
      	FillCircle(x2,y2,(INT8U)(30.0 /*20.0*/ /coordsWorld[2]));
   }

	Oscillate();         // animate spheres
	
// ****

	// clamp rotation angles to range [0...180] degrees
	CLAMP_ANGLE(rotX);
	CLAMP_ANGLE(rotY);
	CLAMP_ANGLE(rotZ);

   // create translation matrix
	M4X4_Identity(MatWorld);
	M4X4_Identity(temp);
	temp[3][0] = posX;
	temp[3][1] = posY;
	temp[3][2] = posZ;
	M4X4_MulSelf(MatWorld,temp);

   // create rotation matrix
	CreateRotateMat(rotX,rotY,rotZ);

	// Loop through the lines
	for(i = 0;i <12;i++)
	{
		// Transform and project first point
        t=indices[i*2];
		MatrixVectorMul(coordsWorld,points[t],MatWorld);
		Project(coordsWorld);

		x1 = (INT8U)scrX;
		y1 = (INT8U)scrY;

		// Transform and project second point
        t=indices[i*2+1];
		MatrixVectorMul(coordsWorld,points[t],MatWorld);
		Project(coordsWorld);

		x2 = (INT8U)scrX;
		y2 = (INT8U)scrY;

		// Draw the line
		SetColor(BRIGHTRED);
		Line(x1,y1,x2,y2);
	}

   // draw the spheres
	for(i = 0;i <14;i++)
	{
 		MatrixVectorMul(coordsWorld,points[i],MatWorld);
		Project(coordsWorld);

		x2 = (INT8U)scrX;
		y2 = (INT8U)scrY;
		
		if(i < 8)
		{
			SetColor(BRIGHTBLUE);
		}	
		else
		{
			SetColor(BRIGHTGREEN);
		}
		
      	FillCircle(x2,y2,(INT8U)(30.0 /*20.0*/ /coordsWorld[2]));
   }
}

//*******************

int main(void)
{
	CLKDIV = 0x0000;	//FRC postscaler divided by 1 (8MHz from internal RC)

/*   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_psp(PSP_DISABLED);
   setup_wdt(WDT_OFF);
   setup_comparator(NC_NC_NC_NC);

   setup_timer_0(T1_DISABLED);
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_4);   // setup timer for FPS-calculation
   setup_timer_2(T2_DISABLED,0,1);
   setup_timer_3(T3_INTERNAL);

   enable_interrupts(INT_TIMER1);
   enable_interrupts(GLOBAL);
*/

   TRISA = 0xFFFF;
   PORTA = 0;
   TRISD = 0xFFF0;
   PORTD = 0;

   // init LCD
   InitGraph();

   CreateBox();            // create the 3d-object
   InitTrigoTable();       // precalc sin/cos-values

	SetFov(45.0);

	// Some text to display
        SetFont(&Font35);
        width = GetTextWidth("3D Cube rotation",&Font35);
        height = GetTextHeight(&Font35);
        
        SetColor(GRAY6);
        Bar((GetMaxX()-width)>>1, 0, width + ((GetMaxX()-width)>>1), height);
        SetColor((WORD)0b1111111111100000 /*YELLOW*/);
        OutTextXY( (GetMaxX()-width)>>1, 0, "3D Cube rotation");
                    
        height = GetTextHeight(&Font25);
        SetFont(&Font25);
        SetColor(WHITE);
		OutTextXY(2,40,"3D routine by Daniel Porzig");
		OutTextXY(2,40+height,"home.tu-clausthal.de/");
		OutTextXY(2,40+2*height,"~dpor/3dengine_eng.html");
		OutTextXY(2,40+3*height,"Adapted by Emmanuel for");
		OutTextXY(2,40+4*height,"www.TechToys.com.hk");
		
		SetColor(CYAN);
		OutTextXY(2,40+6*height,"Button0 : Y+ / Y-");
		OutTextXY(2,40+7*height,"Button1 : X+ / X-");		
		OutTextXY(2,40+8*height,"Button2 : Z+ / Z-");
		
		SetColor(LIGHTMAGENTA);
		OutTextXY(2,40+10*height,"Have fun !");                    

		BYTE tempo,TextPosX = GetMaxX();
		width = GetTextWidth(str, &Font25);
		height = GetTextHeight(&Font25);
		for(tempo = 0; tempo < 21; tempo++)
		{
        	sprintf(str,"  %d s",20-tempo);
        	width = GetTextWidth(str, &Font25);
        	SetColor(BLACK);
        	Bar(TextPosX-width, 40+10*height, TextPosX, 40+10*height+height);

			SetColor(BRIGHTRED);
        	OutTextXY(TextPosX-width, 40+10*height, str);
        	DelayMs(1600);
        }      

   SetColor(BLACK);
   ClearDevice();   // clear the background
   
   while(1)
   {
//	  SetColor(BLACK);
//      ClearDevice();   // clear the background
      
	  //Oscillate();         // animate spheres
      RenderFrame();       // render the image

/*
#ifdef SHOW_FPS
      // calculate Frames per Second
      frametime_i=counter*0xffff;
      counter = 0;
      frametime_i*=4;
      frametime_i += (get_timer1()*4);
      fps=10000000.0;
      fps /= (float) frametime_i;
      set_timer1(0);
      // display text on LCD
      sprintf(str,"FPS: %3.2f",fps);
      glcd_text57(1,56,str,1,1);
#endif
*/
/*      Ks0108_update();*/     /* send contents of Display memory to Display 
                              not needed if your putpixel function sends data
                              to display directly
                           */

      // rotate cube
      rotX++;
      rotY++;
      rotZ++;

      // get Key Commands
      if(PORTAbits.RA14 == 0)
      {
		 LATDbits.LATD0 = 0;
//         if(posY<5.0) posY+=.1;
		 
		 if(posY>5.0)
		 	posY = -5.0;

		 posY+=.1;
      }
	  else
	  	 LATDbits.LATD0 = 1;

      if(PORTAbits.RA15 == 0)
      {
		 LATDbits.LATD1 = 0;
//         if(posY>-5.0) posY-=.1;
		 if(posX>5.0)
		 	posX = -5.0;

		 posX+=.1;
      }
	  else
	  	 LATDbits.LATD1 = 1;

      if(PORTDbits.RD8 == 0)
      {
		 LATDbits.LATD2 = 0;
//         if(posX<5.0) posX+=.1;
		 if(posZ>15.0)
		 	posZ = -15.0;

		 posZ+=.1;
      }
	  else
	  	 LATDbits.LATD2 = 1;

/*      if(PORTDbits.RD9 == 0)
      {
		 LATDbits.LATD3 = 0;
         if(posX>-5.0) posX-=.1;
      }
	  else
	  	 LATDbits.LATD3 = 1;

      if(input(PIN_A5))
      {
         if(posZ<15.0) posZ+=.1;
      }

      if(input(PIN_A4))
      {
         if(posZ>-15.0) posZ-=.1;
      }
*/
   }
}
