349 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			349 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
| 
 | |
|  Example sketch for TFT_eSPI library.
 | |
| 
 | |
|  No fonts are needed.
 | |
|  
 | |
|  Draws a 3d rotating cube on the TFT screen.
 | |
|  
 | |
|  Original code was found at http://forum.freetronics.com/viewtopic.php?f=37&t=5495
 | |
|  
 | |
|  */
 | |
| 
 | |
| #define BLACK 0x0000
 | |
| #define WHITE 0xFFFF
 | |
| 
 | |
| #include <SPI.h>
 | |
| 
 | |
| #include <TFT_eSPI.h> // Hardware-specific library
 | |
| 
 | |
| TFT_eSPI tft = TFT_eSPI();       // Invoke custom library
 | |
| 
 | |
| int16_t h;
 | |
| int16_t w;
 | |
| 
 | |
| int inc = -2;
 | |
| 
 | |
| float xx, xy, xz;
 | |
| float yx, yy, yz;
 | |
| float zx, zy, zz;
 | |
| 
 | |
| float fact;
 | |
| 
 | |
| int Xan, Yan;
 | |
| 
 | |
| int Xoff;
 | |
| int Yoff;
 | |
| int Zoff;
 | |
| 
 | |
| struct Point3d
 | |
| {
 | |
|   int x;
 | |
|   int y;
 | |
|   int z;
 | |
| };
 | |
| 
 | |
| struct Point2d
 | |
| {
 | |
|   int x;
 | |
|   int y;
 | |
| };
 | |
| 
 | |
| int LinestoRender; // lines to render.
 | |
| int OldLinestoRender; // lines to render just in case it changes. this makes sure the old lines all get erased.
 | |
| 
 | |
| struct Line3d
 | |
| {
 | |
|   Point3d p0;
 | |
|   Point3d p1;
 | |
| };
 | |
| 
 | |
| struct Line2d
 | |
| {
 | |
|   Point2d p0;
 | |
|   Point2d p1;
 | |
| };
 | |
| 
 | |
| Line3d Lines[20];
 | |
| Line2d Render[20];
 | |
| Line2d ORender[20];
 | |
| 
 | |
| /***********************************************************************************************************************************/
 | |
| void setup() {
 | |
| 
 | |
|   tft.init();
 | |
| 
 | |
|   h = tft.height();
 | |
|   w = tft.width();
 | |
| 
 | |
|   tft.setRotation(1);
 | |
| 
 | |
|   tft.fillScreen(TFT_BLACK);
 | |
| 
 | |
|   cube();
 | |
| 
 | |
|   fact = 180 / 3.14159259; // conversion from degrees to radians.
 | |
| 
 | |
|   Xoff = 240; // Position the center of the 3d conversion space into the center of the TFT screen.
 | |
|   Yoff = 160;
 | |
|   Zoff = 550; // Z offset in 3D space (smaller = closer and bigger rendering)
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************/
 | |
| void loop() {
 | |
| 
 | |
|   // Rotate around x and y axes in 1 degree increments
 | |
|   Xan++;
 | |
|   Yan++;
 | |
| 
 | |
|   Yan = Yan % 360;
 | |
|   Xan = Xan % 360; // prevents overflow.
 | |
| 
 | |
|   SetVars(); //sets up the global vars to do the 3D conversion.
 | |
| 
 | |
|   // Zoom in and out on Z axis within limits
 | |
|   // the cube intersects with the screen for values < 160
 | |
|   Zoff += inc; 
 | |
|   if (Zoff > 500) inc = -1;     // Switch to zoom in
 | |
|   else if (Zoff < 160) inc = 1; // Switch to zoom out
 | |
| 
 | |
|   for (int i = 0; i < LinestoRender ; i++)
 | |
|   {
 | |
|     ORender[i] = Render[i]; // stores the old line segment so we can delete it later.
 | |
|     ProcessLine(&Render[i], Lines[i]); // converts the 3d line segments to 2d.
 | |
|   }
 | |
|   RenderImage(); // go draw it!
 | |
| 
 | |
|   delay(14); // Delay to reduce loop rate (reduces flicker caused by aliasing with TFT screen refresh rate)
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************/
 | |
| void RenderImage( void)
 | |
| {
 | |
|   // renders all the lines after erasing the old ones.
 | |
|   // in here is the only code actually interfacing with the OLED. so if you use a different lib, this is where to change it.
 | |
| 
 | |
|   for (int i = 0; i < OldLinestoRender; i++ )
 | |
|   {
 | |
|     tft.drawLine(ORender[i].p0.x, ORender[i].p0.y, ORender[i].p1.x, ORender[i].p1.y, BLACK); // erase the old lines.
 | |
|   }
 | |
| 
 | |
| 
 | |
|   for (int i = 0; i < LinestoRender; i++ )
 | |
|   {
 | |
|     uint16_t color = TFT_BLUE;
 | |
|     if (i < 4) color = TFT_RED;
 | |
|     if (i > 7) color = TFT_GREEN;
 | |
|     tft.drawLine(Render[i].p0.x, Render[i].p0.y, Render[i].p1.x, Render[i].p1.y, color);
 | |
|   }
 | |
|   OldLinestoRender = LinestoRender;
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************/
 | |
| // Sets the global vars for the 3d transform. Any points sent through "process" will be transformed using these figures.
 | |
| // only needs to be called if Xan or Yan are changed.
 | |
| void SetVars(void)
 | |
| {
 | |
|   float Xan2, Yan2, Zan2;
 | |
|   float s1, s2, s3, c1, c2, c3;
 | |
| 
 | |
|   Xan2 = Xan / fact; // convert degrees to radians.
 | |
|   Yan2 = Yan / fact;
 | |
| 
 | |
|   // Zan is assumed to be zero
 | |
| 
 | |
|   s1 = sin(Yan2);
 | |
|   s2 = sin(Xan2);
 | |
| 
 | |
|   c1 = cos(Yan2);
 | |
|   c2 = cos(Xan2);
 | |
| 
 | |
|   xx = c1;
 | |
|   xy = 0;
 | |
|   xz = -s1;
 | |
| 
 | |
|   yx = (s1 * s2);
 | |
|   yy = c2;
 | |
|   yz = (c1 * s2);
 | |
| 
 | |
|   zx = (s1 * c2);
 | |
|   zy = -s2;
 | |
|   zz = (c1 * c2);
 | |
| }
 | |
| 
 | |
| 
 | |
| /***********************************************************************************************************************************/
 | |
| // processes x1,y1,z1 and returns rx1,ry1 transformed by the variables set in SetVars()
 | |
| // fairly heavy on floating point here.
 | |
| // uses a bunch of global vars. Could be rewritten with a struct but not worth the effort.
 | |
| void ProcessLine(struct Line2d *ret, struct Line3d vec)
 | |
| {
 | |
|   float zvt1;
 | |
|   int xv1, yv1, zv1;
 | |
| 
 | |
|   float zvt2;
 | |
|   int xv2, yv2, zv2;
 | |
| 
 | |
|   int rx1, ry1;
 | |
|   int rx2, ry2;
 | |
| 
 | |
|   int x1;
 | |
|   int y1;
 | |
|   int z1;
 | |
| 
 | |
|   int x2;
 | |
|   int y2;
 | |
|   int z2;
 | |
| 
 | |
|   int Ok;
 | |
| 
 | |
|   x1 = vec.p0.x;
 | |
|   y1 = vec.p0.y;
 | |
|   z1 = vec.p0.z;
 | |
| 
 | |
|   x2 = vec.p1.x;
 | |
|   y2 = vec.p1.y;
 | |
|   z2 = vec.p1.z;
 | |
| 
 | |
|   Ok = 0; // defaults to not OK
 | |
| 
 | |
|   xv1 = (x1 * xx) + (y1 * xy) + (z1 * xz);
 | |
|   yv1 = (x1 * yx) + (y1 * yy) + (z1 * yz);
 | |
|   zv1 = (x1 * zx) + (y1 * zy) + (z1 * zz);
 | |
| 
 | |
|   zvt1 = zv1 - Zoff;
 | |
| 
 | |
|   if ( zvt1 < -5) {
 | |
|     rx1 = 256 * (xv1 / zvt1) + Xoff;
 | |
|     ry1 = 256 * (yv1 / zvt1) + Yoff;
 | |
|     Ok = 1; // ok we are alright for point 1.
 | |
|   }
 | |
| 
 | |
|   xv2 = (x2 * xx) + (y2 * xy) + (z2 * xz);
 | |
|   yv2 = (x2 * yx) + (y2 * yy) + (z2 * yz);
 | |
|   zv2 = (x2 * zx) + (y2 * zy) + (z2 * zz);
 | |
| 
 | |
|   zvt2 = zv2 - Zoff;
 | |
| 
 | |
|   if ( zvt2 < -5) {
 | |
|     rx2 = 256 * (xv2 / zvt2) + Xoff;
 | |
|     ry2 = 256 * (yv2 / zvt2) + Yoff;
 | |
|   } else
 | |
|   {
 | |
|     Ok = 0;
 | |
|   }
 | |
| 
 | |
|   if (Ok == 1) {
 | |
| 
 | |
|     ret->p0.x = rx1;
 | |
|     ret->p0.y = ry1;
 | |
| 
 | |
|     ret->p1.x = rx2;
 | |
|     ret->p1.y = ry2;
 | |
|   }
 | |
|   // The ifs here are checks for out of bounds. needs a bit more code here to "safe" lines that will be way out of whack, so they dont get drawn and cause screen garbage.
 | |
| 
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************/
 | |
| // line segments to draw a cube. basically p0 to p1. p1 to p2. p2 to p3 so on.
 | |
| void cube(void)
 | |
| {
 | |
|   // Front Face.
 | |
| 
 | |
|   Lines[0].p0.x = -50;
 | |
|   Lines[0].p0.y = -50;
 | |
|   Lines[0].p0.z = 50;
 | |
|   Lines[0].p1.x = 50;
 | |
|   Lines[0].p1.y = -50;
 | |
|   Lines[0].p1.z = 50;
 | |
| 
 | |
|   Lines[1].p0.x = 50;
 | |
|   Lines[1].p0.y = -50;
 | |
|   Lines[1].p0.z = 50;
 | |
|   Lines[1].p1.x = 50;
 | |
|   Lines[1].p1.y = 50;
 | |
|   Lines[1].p1.z = 50;
 | |
| 
 | |
|   Lines[2].p0.x = 50;
 | |
|   Lines[2].p0.y = 50;
 | |
|   Lines[2].p0.z = 50;
 | |
|   Lines[2].p1.x = -50;
 | |
|   Lines[2].p1.y = 50;
 | |
|   Lines[2].p1.z = 50;
 | |
| 
 | |
|   Lines[3].p0.x = -50;
 | |
|   Lines[3].p0.y = 50;
 | |
|   Lines[3].p0.z = 50;
 | |
|   Lines[3].p1.x = -50;
 | |
|   Lines[3].p1.y = -50;
 | |
|   Lines[3].p1.z = 50;
 | |
| 
 | |
| 
 | |
|   //back face.
 | |
| 
 | |
|   Lines[4].p0.x = -50;
 | |
|   Lines[4].p0.y = -50;
 | |
|   Lines[4].p0.z = -50;
 | |
|   Lines[4].p1.x = 50;
 | |
|   Lines[4].p1.y = -50;
 | |
|   Lines[4].p1.z = -50;
 | |
| 
 | |
|   Lines[5].p0.x = 50;
 | |
|   Lines[5].p0.y = -50;
 | |
|   Lines[5].p0.z = -50;
 | |
|   Lines[5].p1.x = 50;
 | |
|   Lines[5].p1.y = 50;
 | |
|   Lines[5].p1.z = -50;
 | |
| 
 | |
|   Lines[6].p0.x = 50;
 | |
|   Lines[6].p0.y = 50;
 | |
|   Lines[6].p0.z = -50;
 | |
|   Lines[6].p1.x = -50;
 | |
|   Lines[6].p1.y = 50;
 | |
|   Lines[6].p1.z = -50;
 | |
| 
 | |
|   Lines[7].p0.x = -50;
 | |
|   Lines[7].p0.y = 50;
 | |
|   Lines[7].p0.z = -50;
 | |
|   Lines[7].p1.x = -50;
 | |
|   Lines[7].p1.y = -50;
 | |
|   Lines[7].p1.z = -50;
 | |
| 
 | |
| 
 | |
|   // now the 4 edge lines.
 | |
| 
 | |
|   Lines[8].p0.x = -50;
 | |
|   Lines[8].p0.y = -50;
 | |
|   Lines[8].p0.z = 50;
 | |
|   Lines[8].p1.x = -50;
 | |
|   Lines[8].p1.y = -50;
 | |
|   Lines[8].p1.z = -50;
 | |
| 
 | |
|   Lines[9].p0.x = 50;
 | |
|   Lines[9].p0.y = -50;
 | |
|   Lines[9].p0.z = 50;
 | |
|   Lines[9].p1.x = 50;
 | |
|   Lines[9].p1.y = -50;
 | |
|   Lines[9].p1.z = -50;
 | |
| 
 | |
|   Lines[10].p0.x = -50;
 | |
|   Lines[10].p0.y = 50;
 | |
|   Lines[10].p0.z = 50;
 | |
|   Lines[10].p1.x = -50;
 | |
|   Lines[10].p1.y = 50;
 | |
|   Lines[10].p1.z = -50;
 | |
| 
 | |
|   Lines[11].p0.x = 50;
 | |
|   Lines[11].p0.y = 50;
 | |
|   Lines[11].p0.z = 50;
 | |
|   Lines[11].p1.x = 50;
 | |
|   Lines[11].p1.y = 50;
 | |
|   Lines[11].p1.z = -50;
 | |
| 
 | |
|   LinestoRender = 12;
 | |
|   OldLinestoRender = LinestoRender;
 | |
| 
 | |
| }
 | |
| 
 |