cube.ino 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. #include <EEPROM.h>
  2. #include <SPI.h>
  3. #include <GD2.h>
  4. #undef REPORT
  5. #define REPORT(VAR) (Serial.print(#VAR "="), Serial.println(VAR, DEC))
  6. #define PICTURE_HANDLE 0
  7. #define P 125
  8. #define N -P
  9. static const PROGMEM int8_t CUBE_vertices[] = {
  10. P,P,P,
  11. N,P,P,
  12. P,N,P,
  13. N,N,P,
  14. P,P,N,
  15. N,P,N,
  16. P,N,N,
  17. N,N,N,
  18. };
  19. // each line is a face: count, normal, 4 vertices
  20. static const PROGMEM int8_t CUBE_faces[] = {
  21. 4, 0,0,127, 0, 1, 3, 2,
  22. 4, 0,0,-127, 6, 7, 5, 4,
  23. 4, 0,127,0, 4, 5, 1, 0,
  24. 4, 0,-127,0, 2, 3, 7, 6,
  25. 4, 127,0,0, 0, 2, 6, 4,
  26. 4, -127,0,0, 3, 1, 5, 7,
  27. -1
  28. };
  29. ////////////////////////////////////////////////////////////////////////////////
  30. // 3D Projection
  31. ////////////////////////////////////////////////////////////////////////////////
  32. static float model_mat[9] = {
  33. 1.0, 0.0, 0.0,
  34. 0.0, 1.0, 0.0,
  35. 0.0, 0.0, 1.0 };
  36. static float normal_mat[9] = {
  37. 1.0, 0.0, 0.0,
  38. 0.0, 1.0, 0.0,
  39. 0.0, 0.0, 1.0 };
  40. #define M(nm,i,j) ((nm)[3 * (i) + (j)])
  41. // 3x3 matrix multiplication: c = a * b
  42. void mult_matrices(float *a, float *b, float *c)
  43. {
  44. int i, j, k;
  45. float result[9];
  46. for(i = 0; i < 3; i++) {
  47. for(j = 0; j < 3; j++) {
  48. M(result,i,j) = 0.0f;
  49. for(k = 0; k < 3; k++) {
  50. M(result,i,j) += M(a,i,k) * M(b,k,j);
  51. }
  52. }
  53. }
  54. memcpy(c, result, sizeof(result));
  55. }
  56. // Based on glRotate()
  57. // Returns 3x3 rotation matrix in 'm'
  58. // and its invese in 'mi'
  59. static void rotate(float *m, float *mi, float angle, float *axis)
  60. {
  61. float x = axis[0];
  62. float y = axis[1];
  63. float z = axis[2];
  64. float s = sin(angle);
  65. float c = cos(angle);
  66. float xx = x*x*(1-c);
  67. float xy = x*y*(1-c);
  68. float xz = x*z*(1-c);
  69. float yy = y*y*(1-c);
  70. float yz = y*z*(1-c);
  71. float zz = z*z*(1-c);
  72. float xs = x * s;
  73. float ys = y * s;
  74. float zs = z * s;
  75. m[0] = xx + c;
  76. m[1] = xy - zs;
  77. m[2] = xz + ys;
  78. m[3] = xy + zs;
  79. m[4] = yy + c;
  80. m[5] = yz - xs;
  81. m[6] = xz - ys;
  82. m[7] = yz + xs;
  83. m[8] = zz + c;
  84. mi[0] = m[0];
  85. mi[1] = xy + zs;
  86. mi[2] = xz - ys;
  87. mi[3] = xy - zs;
  88. mi[4] = m[4];
  89. mi[5] = yz + xs;
  90. mi[6] = xz + ys;
  91. mi[7] = yz - xs;
  92. mi[8] = m[8];
  93. }
  94. static void rotation(float angle, float *axis)
  95. {
  96. float mat[9], mati[9];
  97. rotate(mat, mati, angle, axis);
  98. mult_matrices(model_mat, mat, model_mat);
  99. mult_matrices(mati, normal_mat, normal_mat);
  100. }
  101. #define N_VERTICES (sizeof(CUBE_vertices) / 3)
  102. typedef struct {
  103. int x, y;
  104. } point2;
  105. static point2 projected[N_VERTICES];
  106. void project()
  107. {
  108. byte vx;
  109. const PROGMEM int8_t *pm = CUBE_vertices;
  110. const PROGMEM int8_t *pm_e = pm + sizeof(CUBE_vertices);
  111. point2 *dst = projected;
  112. int16_t x, y, z;
  113. int scale = 64 * GD.h / 280;
  114. while (pm < pm_e) {
  115. x = (scale * (int8_t)pgm_read_byte_near(pm++)) >> 6;
  116. y = (scale * (int8_t)pgm_read_byte_near(pm++)) >> 6;
  117. z = (scale * (int8_t)pgm_read_byte_near(pm++)) >> 6;
  118. float xx = x * model_mat[0] + y * model_mat[3] + z * model_mat[6];
  119. float yy = x * model_mat[1] + y * model_mat[4] + z * model_mat[7];
  120. dst->x = 16 * (GD.w / 2 + xx);
  121. dst->y = 16 * (GD.h / 2 + yy);
  122. dst++;
  123. }
  124. }
  125. static void transform_normal(int8_t &nx, int8_t &ny, int8_t &nz)
  126. {
  127. int8_t xx = nx * normal_mat[0] + ny * normal_mat[1] + nz * normal_mat[2];
  128. int8_t yy = nx * normal_mat[3] + ny * normal_mat[4] + nz * normal_mat[5];
  129. int8_t zz = nx * normal_mat[6] + ny * normal_mat[7] + nz * normal_mat[8];
  130. nx = xx, ny = yy, nz = zz;
  131. }
  132. static void quad(int x1, int y1,
  133. int x2, int y2,
  134. int x3, int y3,
  135. int bx1, int by1,
  136. int bx3, int by3)
  137. {
  138. // Compute the fourth vertex of the parallelogram, (x4,y4)
  139. int x4 = x3 + (x1 - x2);
  140. int y4 = y3 + (y1 - y2);
  141. // Apply Scissor to the extents of the quad
  142. int minx = max(0, min(min(x1, x2), min(x3, x4)));
  143. int maxx = min(GD.w, max(max(x1, x2), max(x3, x4)));
  144. int miny = max(0, min(min(y1, y2), min(y3, y4)));
  145. int maxy = min(GD.h, max(max(y1, y2), max(y3, y4)));
  146. GD.ScissorXY(minx, miny);
  147. GD.ScissorSize(maxx - minx, maxy - miny);
  148. // GD.ClearColorRGB(0, 255, 0); GD.Clear();
  149. // Set the new bitmap transform
  150. GD.cmd32(0xffffff21UL); // bitmap transform
  151. GD.cmd32(x1 - minx); GD.cmd32(y1 - miny);
  152. GD.cmd32(x2 - minx); GD.cmd32(y2 - miny);
  153. GD.cmd32(x3 - minx); GD.cmd32(y3 - miny);
  154. GD.cmd32(bx1); GD.cmd32(by1);
  155. GD.cmd32(bx1); GD.cmd32(by3);
  156. GD.cmd32(bx3); GD.cmd32(by3);
  157. GD.cmd32(0);
  158. // Draw the quad
  159. GD.Vertex2f(PIXELS(minx), PIXELS(miny));
  160. }
  161. void draw_faces(int dir)
  162. {
  163. int R = 15;
  164. const PROGMEM int8_t *p = CUBE_faces;
  165. byte n;
  166. GD.BlendFunc(ONE, ONE_MINUS_SRC_ALPHA);
  167. GD.Begin(BITMAPS);
  168. while ((n = pgm_read_byte_near(p++)) != 0xff) {
  169. int8_t nx = pgm_read_byte_near(p++);
  170. int8_t ny = pgm_read_byte_near(p++);
  171. int8_t nz = pgm_read_byte_near(p++);
  172. byte v1 = pgm_read_byte_near(p);
  173. byte v2 = pgm_read_byte_near(p + 1);
  174. byte v3 = pgm_read_byte_near(p + 2);
  175. p += n;
  176. long x1 = projected[v1].x;
  177. long y1 = projected[v1].y;
  178. long x2 = projected[v2].x;
  179. long y2 = projected[v2].y;
  180. long x3 = projected[v3].x;
  181. long y3 = projected[v3].y;
  182. long area = (x1 - x3) * (y2 - y1) - (x1 - x2) * (y3 - y1);
  183. byte face = (area < 0);
  184. if (face == dir) {
  185. uint16_t r = 20, g = 20, b = 80; // Ambient
  186. if (face) {
  187. transform_normal(nx, ny, nz);
  188. int d = max(0, -nz); // diffuse light from +ve Z
  189. r += 2 * d;
  190. g += 2 * d;
  191. b += 2 * d;
  192. }
  193. GD.ColorRGB(min(255, r), min(255, g), min(255, b));
  194. x1 >>= 4;
  195. y1 >>= 4;
  196. x2 >>= 4;
  197. y2 >>= 4;
  198. x3 >>= 4;
  199. y3 >>= 4;
  200. quad(x1, y1, x2, y2, x3, y3,
  201. -R, -R,
  202. 128 + R, 128 + R);
  203. }
  204. }
  205. }
  206. /*****************************************************************/
  207. /* simple trackball-like motion control */
  208. /* Based on projtex.c - by David Yu and David Blythe, SGI */
  209. float angle, axis[3] = {0,1,0};
  210. float lastPos[3];
  211. void
  212. ptov(int x, int y, int width, int height, float v[3])
  213. {
  214. float d, a;
  215. /* project x,y onto a hemi-sphere centered within width, height */
  216. v[0] = (2.0 * x - width) / width;
  217. v[1] = (2.0 * y - height) / height;
  218. d = sqrt(v[0] * v[0] + v[1] * v[1]);
  219. v[2] = cos((M_PI / 2.0) * ((d < 1.0) ? d : 1.0));
  220. a = 1.0 / sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
  221. v[0] *= a;
  222. v[1] *= a;
  223. v[2] *= a;
  224. }
  225. void
  226. startMotion(int x, int y)
  227. {
  228. angle = 0.0;
  229. ptov(x, y, GD.w, GD.h, lastPos);
  230. }
  231. void
  232. trackMotion(int x, int y)
  233. {
  234. float curPos[3], dx, dy, dz;
  235. ptov(x, y, GD.w, GD.h, curPos);
  236. dx = curPos[0] - lastPos[0];
  237. dy = curPos[1] - lastPos[1];
  238. dz = curPos[2] - lastPos[2];
  239. angle = (M_PI / 2) * sqrt(dx * dx + dy * dy + dz * dz);
  240. axis[0] = lastPos[1] * curPos[2] - lastPos[2] * curPos[1];
  241. axis[1] = lastPos[2] * curPos[0] - lastPos[0] * curPos[2];
  242. axis[2] = lastPos[0] * curPos[1] - lastPos[1] * curPos[0];
  243. float mag = 1 / sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
  244. axis[0] *= mag;
  245. axis[1] *= mag;
  246. axis[2] *= mag;
  247. lastPos[0] = curPos[0];
  248. lastPos[1] = curPos[1];
  249. lastPos[2] = curPos[2];
  250. }
  251. /*****************************************************************/
  252. Bitmap background, foreground;
  253. void setup()
  254. {
  255. Serial.begin(1000000); // JCB
  256. GD.begin();
  257. background.fromfile("tree.jpg");
  258. foreground.fromfile("healsky3.jpg");
  259. foreground.bind(PICTURE_HANDLE);
  260. GD.BitmapHandle(PICTURE_HANDLE);
  261. GD.BitmapSize(NEAREST, BORDER, BORDER, GD.w, GD.h);
  262. startMotion(240, 136);
  263. trackMotion(247, 138);
  264. }
  265. byte prev_touching;
  266. void loop()
  267. {
  268. GD.get_inputs();
  269. GD.Clear();
  270. GD.SaveContext(); // {
  271. GD.ColorRGB(0x605040);
  272. background.wallpaper();
  273. GD.RestoreContext(); // }
  274. if (!prev_touching && GD.inputs.touching)
  275. startMotion(GD.inputs.x, GD.inputs.y);
  276. else if (GD.inputs.touching)
  277. trackMotion(GD.inputs.x, GD.inputs.y);
  278. prev_touching = GD.inputs.touching;
  279. if (angle != 0.0f)
  280. rotation(angle, axis);
  281. project();
  282. draw_faces(0);
  283. draw_faces(1);
  284. GD.RestoreContext();
  285. GD.swap();
  286. }