/* From ucg, modified for use with all my libraries. IMPORTANT! You need my modified Adafruit_GFX library! https://github.com/sumotoy/Adafruit_GFX/ */ #include #include #include #ifndef _ADAFRUIT_GFX_VARIANT #error you need the modified Adafruit_GFX library! #endif #define __CS 10 #define __DC 6 TFT_ILI9163C tft = TFT_ILI9163C(__CS, __DC); struct pt3d { int x, y, z; }; struct surface { uint8_t p[4]; int16_t z; }; struct pt2d { int x, y; unsigned is_visible; }; // define the point at which the observer looks, 3d box will be centered there #define MX (tft.width()/2) #define MY (tft.height()/2) // 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 int x_min, x_max; int 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 >= -MX && t <= MX - 1) { t += MX; 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 >= -MY && t <= MY - 1) { t += MY; 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(((ii + 1) & 1) * 255, (((ii + 1) >> 1) & 1) * 255, (((ii + 1) >> 2) & 1) * 255); 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); } } } void calc_and_draw(uint16_t w, uint16_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(); } uint16_t w = 0; uint16_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); }