#include <SPI.h>
#include <TFT_ILI9163C.h>
//uncomment for wireframe
//#define _WIREFRAME
/*
ESP8266-----------------------------------
Use:
#define __CS 16 //(D0)
#define __DC 5 //(D1)
#define __RST 4 //(D2)
SCLK:D5
MOSI:D7
*/
#define __CS1 10
#define __DC 9
#if defined(TFT_ILI9163C_INSTANCES)
TFT_ILI9163C tft = TFT_ILI9163C(REDPCB_NEW, __CS1, __DC);
#else
TFT_ILI9163C tft = TFT_ILI9163C(__CS1, __DC);
#endif
struct pt3d
{
int16_t x, y, z;
};
struct surface
{
uint8_t p[4];
int16_t z;
};
struct pt2d
{
int16_t x, y;
unsigned is_visible;
};
// define a value that corresponds to "1"
#define U 100
// eye to screen distance (fixed)
#define ZS U
// cube edge length is 2*U
struct pt3d cube[8] =
{
{ -U, -U, U},
{ U, -U, U},
{ U, -U, -U},
{ -U, -U, -U},
{ -U, U, U},
{ U, U, U},
{ U, U, -U},
{ -U, U, -U},
};
// define the surfaces
struct surface cube_surface[6] =
{
{ {0, 1, 2, 3}, 0 }, // bottom
{ {4, 5, 6, 7}, 0 }, // top
{ {0, 1, 5, 4}, 0 }, // back
{ {3, 7, 6, 2}, 0 }, // front
{ {1, 2, 6, 5}, 0 }, // right
{ {0, 3, 7, 4}, 0 }, // left
};
// define some structures for the copy of the box, calculation will be done there
struct pt3d cube2[8];
struct pt2d cube_pt[8];
// will contain a rectangle border of the box projection into 2d plane
int16_t x_min, x_max;
int16_t y_min, y_max;
const int16_t sin_tbl[65] = {
0, 1606, 3196, 4756, 6270, 7723, 9102, 10394, 11585, 12665, 13623, 14449, 15137, 15679, 16069, 16305, 16384, 16305, 16069, 15679,
15137, 14449, 13623, 12665, 11585, 10394, 9102, 7723, 6270, 4756, 3196, 1606, 0, -1605, -3195, -4755, -6269, -7722, -9101, -10393,
-11584, -12664, -13622, -14448, -15136, -15678, -16068, -16304, -16383, -16304, -16068, -15678, -15136, -14448, -13622, -12664, -11584, -10393, -9101, -7722,
-6269, -4755, -3195, -1605, 0
};
const int16_t cos_tbl[65] = {
16384, 16305, 16069, 15679, 15137, 14449, 13623, 12665, 11585, 10394, 9102, 7723, 6270, 4756, 3196, 1606, 0, -1605, -3195, -4755,
-6269, -7722, -9101, -10393, -11584, -12664, -13622, -14448, -15136, -15678, -16068, -16304, -16383, -16304, -16068, -15678, -15136, -14448, -13622, -12664,
-11584, -10393, -9101, -7722, -6269, -4755, -3195, -1605, 0, 1606, 3196, 4756, 6270, 7723, 9102, 10394, 11585, 12665, 13623, 14449,
15137, 15679, 16069, 16305, 16384
};
void copy_cube(void)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
cube2[i] = cube[i];
}
}
void rotate_cube_y(uint16_t w)
{
uint8_t i;
int16_t x, z;
/*
x' = x * cos(w) + z * sin(w)
z' = - x * sin(w) + z * cos(w)
*/
for (i = 0; i < 8; i++)
{
x = ((int32_t)cube2[i].x * (int32_t)cos_tbl[w] + (int32_t)cube2[i].z * (int32_t)sin_tbl[w]) >> 14;
z = (-(int32_t)cube2[i].x * (int32_t)sin_tbl[w] + (int32_t)cube2[i].z * (int32_t)cos_tbl[w]) >> 14;
//printf("%d: %d %d --> %d %d\n", i, cube2[i].x, cube2[i].z, x, z);
cube2[i].x = x;
cube2[i].z = z;
}
}
void rotate_cube_x(uint16_t w)
{
uint8_t i;
int16_t y, z;
for (i = 0; i < 8; i++)
{
y = ((int32_t)cube2[i].y * (int32_t)cos_tbl[w] + (int32_t)cube2[i].z * (int32_t)sin_tbl[w]) >> 14;
z = (-(int32_t)cube2[i].y * (int32_t)sin_tbl[w] + (int32_t)cube2[i].z * (int32_t)cos_tbl[w]) >> 14;
cube2[i].y = y;
cube2[i].z = z;
}
}
void trans_cube(uint16_t z)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
cube2[i].z += z;
}
}
void reset_min_max(void)
{
x_min = 0x07fff;
y_min = 0x07fff;
x_max = -0x07fff;
y_max = -0x07fff;
}
// calculate xs and ys from a 3d value
void convert_3d_to_2d(struct pt3d *p3, struct pt2d *p2)
{
int32_t t;
p2->is_visible = 1;
if (p3->z >= ZS)
{
t = ZS;
t *= p3->x;
t <<= 1;
t /= p3->z;
if (t >= -(tft.width()/2) && t <= (tft.width()/2) - 1)
{
t += (tft.width()/2);
p2->x = t;
if (x_min > t) x_min = t;
if (x_max < t) x_max = t;
t = ZS;
t *= p3->y;
t <<= 1;
t /= p3->z;
if (t >= -(tft.height()/2) && t <= (tft.height()/2) - 1)
{
t += (tft.height()/2);
p2->y = t;
if (y_min > t) y_min = t;
if (y_max < t) y_max = t;
}
else
{
p2->is_visible = 0;
}
}
else
{
p2->is_visible = 0;
}
}
else
{
p2->is_visible = 0;
}
}
void convert_cube(void)
{
uint8_t i;
reset_min_max();
for (i = 0; i < 8; i++)
{
convert_3d_to_2d(cube2 + i, cube_pt + i);
}
}
void calculate_z(void)
{
uint8_t i, j;
uint16_t z;
for (i = 0; i < 6; i++)
{
z = 0;
for (j = 0; j < 4; j++)
{
z += cube2[cube_surface[i].p[j]].z;
}
z /= 4;
cube_surface[i].z = z;
//printf("%d: z=%d\n", i, z);
}
}
void draw_cube(void)
{
uint8_t i, ii;
uint8_t skip_cnt = 3; /* it is known, that the first 3 surfaces are invisible */
int16_t z, z_upper;
uint16_t color;
z_upper = 32767;
for (;;)
{
ii = 6;
z = -32767;
for (i = 0; i < 6; i++)
{
if (cube_surface[i].z <= z_upper)
{
if (z < cube_surface[i].z)
{
z = cube_surface[i].z;
ii = i;
}
}
}
if (ii >= 6) break;
z_upper = cube_surface[ii].z;
cube_surface[ii].z++;
if (skip_cnt > 0)
{
skip_cnt--;
}
else
{
color = tft.Color565((uint8_t)(((ii + 1) & 1) * 255), (uint8_t)((((ii + 1) >> 1) & 1) * 255), (uint8_t)((((ii + 1) >> 2) & 1) * 255));
#if defined(_WIREFRAME)
tft.drawQuad(
cube_pt[cube_surface[ii].p[0]].x, cube_pt[cube_surface[ii].p[0]].y,
cube_pt[cube_surface[ii].p[1]].x, cube_pt[cube_surface[ii].p[1]].y,
cube_pt[cube_surface[ii].p[2]].x, cube_pt[cube_surface[ii].p[2]].y,
cube_pt[cube_surface[ii].p[3]].x, cube_pt[cube_surface[ii].p[3]].y, color);
#else
tft.fillQuad(
cube_pt[cube_surface[ii].p[0]].x, cube_pt[cube_surface[ii].p[0]].y,
cube_pt[cube_surface[ii].p[1]].x, cube_pt[cube_surface[ii].p[1]].y,
cube_pt[cube_surface[ii].p[2]].x, cube_pt[cube_surface[ii].p[2]].y,
cube_pt[cube_surface[ii].p[3]].x, cube_pt[cube_surface[ii].p[3]].y, color);
#endif
}
}
}
void calc_and_draw(int16_t w, int16_t v)
{
copy_cube();
rotate_cube_y(w);
rotate_cube_x(v);
trans_cube(U * 8);
convert_cube();
calculate_z();
draw_cube();
}
void setup(void)
{
tft.begin();
}
int16_t w = 0;
int16_t v = 0;
void loop(void)
{
calc_and_draw(w, v >> 3);
v += 3;
v &= 511;
w++;
w &= 63;
delay(10);
tft.fillRect(x_min, y_min, x_max - x_min + 3, y_max - y_min + 3, 0x0000);
}