//
// 3D Cube by Jason Wright (C)opyright Pyrofer 2006
//
// This code is free, there is no charge for it nor should anybody else charge
// for the code, its distribution or derivitives of this code.
//
// You may use this code, modify and change and improve upon it on the following conditions,
//
// 1> You send me the modified source code and where possible make it public
// 2> I am credited for the original work in all derivitives of this code
// 3> You do not charge for this code or code derived from this code
// 4> You comment the code you change! 
//
// Basically, use this to learn! I couldnt find anything this simple when I started so I hope
// that this helps others to learn. You should be able to change the LCD routines to drive almost 
// any graphics device. The base resolution is 128x128 for this display. Simply set the X and Y offset
// to half the screen resolution for each axis.
// Then adjust the Z offset to make the object fit nicely in the screen.




#device PIC18F2520
#include "C:\Dev\PICC\PCW Projects\Dontronics LCD\gfxlcd.h"
#include "math.h"               	// include complex math routines
#ORG 0x1E00,0x1FFF{}            	// reserve rom spce to protect bootloader program
#fuses HS,NOWDT,NOPROTECT       	// set pic fuses
#use delay(clock=20000000)      	// set Clock speed for time calculations 20Mghz
#use rs232(baud=115200, xmit=PIN_C1, rcv=PIN_C0) // set up IO for LCD
#define OFFSETX 64              	// offset for screen wont change unless
#define OFFSETY 64              	// i use different screen! so its kinda fixed
#define OFFSETZ 30

void cube (void);               	// define the subroutines this one is the actual cube routine
void clearscreen (void);		// clear the LCD screen
void lcdline ();			// draw a line on the LCD
void initlcd (void);			// initialise the LCD
void proginit (void);			// setup other program stuff
void shutdown (void);			// shutdown the LCD

                                	// use const as they are in rom, saving ram

const signed int aa[8]={10,-10,-10,10,   10,-10,-10,10};	// x data for shape vertex
const signed int bb[8]={10,10,-10,-10,   10,10,-10,-10};	// y data for shape vertex
const signed int cc[8]={-10,-10,-10,-10, 10,10,10,10};		// z data for shape vertex

const int ff[12]={1,2,3,4,  5,6,7,8, 1,2,3,4};    		// start vertex for lines
const int gg[12]={2,3,4,1,  6,7,8,5, 5,6,7,8};			// end vertex for lines

int sx,sy,ex,ey;                	// define global vars for calling graphics subroutines

void main()                     	// begin main program
 {
 initlcd();                     	// set autobaud and clearscreen
 proginit();				// call program inits
 cube();                        	// call main cube drawing routine
 delay_ms(9999);                	// wait for ages while we look at the pretty picture
 clearscreen();				// clear the screen
 shutdown();                    	// turn off LCD ready for power Down
 while(1);                      	// wait forever as program is done.
 }

void cube()                     	// routine to draw and calc 3d cube
 {
 int newx[8];                   	// translated screen x co-ordinates for vertex
 int newy[8];                   	// translated screen y co-ordinates for vertex
 int i,loop;                    	// temp variable for loops
 float xt,yt,zt,x,y,z,sinax,cosax,sinay,cosay,sinaz,cosaz,vertex;  // lots of work variables
 float xpos=0;				// position for object in 3d space, in x
 float ypos=0;				// y
 float zpos=0;				// and z values
 float rotx=0;                  	// starting amount of x rotation
 float roty=0;                 		// starting amount of y rotation
 float rotz=0;                		// starting amount of z rotation

 for (loop=0; loop<=100; loop++)	// rotate the cube 100 times
  {
  xpos=xpos+0.0;			// move the object
  ypos=ypos+0.0;			// it would wander off screen
  zpos=zpos+0.0;			// really quick, so leave it centered

  rotx=rotx+0.5;                	// rotate the cube on X axis
  roty=roty+0.5;                	// and on its y axis
  rotz=rotz+0.0;                	// dont bother with z or it gets confusing

  sinax=sin(rotx);			// precalculate the sin and cos values
  cosax=cos(rotx);			// for the rotation as this saves a 
  
  sinay=sin(roty);			// little time when running as we
  cosay=cos(roty);			// call sin and cos less often
  
  sinaz=sin(rotz);			// they are slow routines
  cosaz=cos(rotz);			// and we dont want slow!

  for (i=0; i<8; i++)           	// translate 3d vertex position to 2d screen position
        {
        x=aa[i];                	// get x for vertex i
        y=bb[i];                	// get y for vertex i
        z=cc[i];                	// get z for vertex i

        yt = y * cosax - z * sinax;	// rotate around the x axis
        zt = y * sinax + z * cosax;	// using the Y and Z for the rotation
        y = yt;
        z = zt;

        xt = x * cosay - z * sinay;	// rotate around the Y axis
        zt = x * sinay + z * cosay;	// using X and Z
        x = xt;
        z = zt;

        xt = X * cosaz - y * sinaz;	// finaly rotate around the Z axis
        yt = X * sinaz + y * cosaz;	// using X and Y
        x = xt;
        y = yt;

        x=x+xpos;			// add the object position offset
        y=y+ypos;			// for both x and y
        z=z+OFFSETZ-zpos;		// as well as Z

        newx[i]=(x*64/z)+OFFSETX;	// translate 3d to 2d coordinates for screen
        newy[i]=(y*64/z)+OFFSETY;	// drawing so we can see the cube
        }

  delay_ms(20);			// delay for a while to allow looking at the cube
  clearscreen();		// clear the screen to remove old cube

  for (i=0; i<12; i++)		// draw the lines that make up the object
        {
        vertex=ff[i]-1;         // temp = start vertex for this line
        sx=newx[vertex];        // set line start x to vertex[i] x position
        sy=newy[vertex];        // set line start y to vertex[i] y position
        vertex=gg[i]-1;         // temp = end vertex for this line
        ex=newx[vertex];	// set line end x to vertex[i+1] x position
        ey=newy[vertex];	// set line end y to vertex[i+1] y position
        lcdline();		// draw the line between these 2 vertex
        }
  }
 }

void lcdline()                  // subroutine for drawing line
 {
 delay_ms(18);                  // wait for LCD to be ready just in case
 putc(76);                      // send command for line
 putc(sx);                      // send topleft X coord
 putc(sy);                      // send topleft Y coord
 putc(ex);                      // send bottomright x coord
 putc(ey);                      // send bottomright y coord
 putc(0);                       // send high byte of colour
 putc(255);                     // send low byte of colour
 }

void clearscreen()              // subroutine to clear screen
 {
 delay_ms(20);                  // wait for LCD to be ready just in case
 putc(69);                      // send clearscreen command
 }

void initlcd()                  // routine to initialise LCD
 {
 delay_ms(5000);                // wait for LCD to power up if it was off
 putc(85);                      // send U for auto-baud
 delay_ms(20);                  // wait for LCD to be ready
 putc(89);                      // send command byte
 putc(3);                       // with Startup command High byte
 putc(1);                       // and low byte.
 delay_ms(5000);                // wait for LCD to power up if it was shutdown
 clearscreen();                 // clearscreen
 }

void shutdown()                 // routine to shutdown the LCD for poweroff
 {
 delay_ms(20);                  // wait for LCD to be ready
 putc(89);                      // send command byte
 putc(3);                       // with Shutdown command High byte
 putc(0);                       // and low byte.
 }

void proginit(void)
 {
 setup_wdt(WDT_OFF);		// turn off watchdog timer, low volt reset, internal clock and spi
 setup_low_volt_detect(FALSE);
 setup_oscillator(FALSE);
 setup_spi(FALSE);
 }
