c_render.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. // c_render.cpp
  2. // Drawing code for the graphics window
  3. // Copyright (C)/©/¸ Codemist Ltd, 1995-96
  4. /* Signature: 42e464bc 08-Jan-1996 */
  5. #ifdef GRAPHICS_WINDOW
  6. #include "cwin.hpp"
  7. // I have two serious concerns here. The first is the amount of space needed
  8. // to store descriptions of objects that I might draw. The second is the
  9. // potential performance-bottleneck of floating point arithmetic on many
  10. // PCs. To try to alleviate these difficulties I will do much of my work
  11. // using fixed point arithmetic and with data packed into 16-bit fields
  12. // in structures.
  13. // I will store 3D points (after rotation and so on) with X and Y
  14. // coordinates in the range 0 to 64K, and I will translate & clip this
  15. // so that objects at (0x8000, 0x8000) appear in the middle of my screen.
  16. // But I will leave myself a full range of integers for the Z coordinate
  17. // so that I do not have to worry so much about fine-tuning the
  18. // transformations I use in that respect.
  19. // The odd-looking range for X and Y is so I can use unsigned bitfield
  20. // components in the following structure.
  21. // If I want to do Phong shading at some stage (that puts nice highlights
  22. // onto rendered surfaces) I should extend the representation of an
  23. // XYZPoint to include a surface normal. It should be enough to use 10
  24. // bits for each of the three direction cosines, since I do not have
  25. // that many bits of discrimination in my control over display brightness.
  26. // I put the field in now so that using it later on will be easy, but
  27. // will probably not fill in the values to start with. Note that the
  28. // direction cosines need to be signed fields, so I should normalise them
  29. // to the range -511 to +511 (nb NOT 512). The typedef is in my main
  30. // header file but is replicated here to make it easier to read this code.
  31. //-- typedef struct tagXYZPoint
  32. //-- {
  33. //-- unsigned int x:16;
  34. //-- unsigned int y:16;
  35. //-- int z;
  36. //-- signed int nx:10, ny:10, nz:10;
  37. //-- unsigned int red:8, green:8, blue:8, spare:8;
  38. //-- } XYZPoint;
  39. // I will build up images out of triangles. When I need a rectangle or
  40. // bigger polygon I will just split it into triangles. I will assume that
  41. // all the vertices I will ever use have been stored in a linear array
  42. // of type XYZPoint[].
  43. // A triangle is then represented as a triple of XYZPoints each denoted
  44. // by their index values in the array, say v1, v2, v3.
  45. // The vertices should be listed in clockwise when viewed from the "outside"
  46. // of the object, and if pairs of triangles are used to form rectangles
  47. // then the diagonal of the rectangle should be given as the first pair of
  48. // vertices.
  49. // When a triangle is drawn some or all of its edges can be drawn in with
  50. // a pen that will often have a contrasting colour to that used for the
  51. // interior of the cell. The need to have only SOME edges drawn in this
  52. // way arises when larger polygons have been split into multiple triangles
  53. // for processing - the edges of the original polygon may need drawing but
  54. // synthetic interior joins do not. The three flag bits edge1, edge2 and
  55. // edge3 are used to control which (if any) edges get drawn. All edges
  56. // in an entire figure will end up drawn in the same colour: no provision is
  57. // made for giving all the edges their own separate colours.
  58. //***** What follows has not been implemented yet *****
  59. // So that axes and other line-art can be included there is special support
  60. // for straight line "triangles". These have the flag "lineflag" set and then
  61. // do not need to use vertex3, which should be set equal to vertex2.
  62. // A record with lineflag set represents a straight line segment from vertex1
  63. // to vertex2. If edge1 is set it is drawn in the outline colour (as used for
  64. // edges of proper triangles) while otherwise it uses its own R-G-B colour.
  65. // If vertex1 is equal to vertex2 when lineflag is set then the record would
  66. // appear to represent a point (and vertex3 will be equal to vertex1 and
  67. // vertex2). I am inclined to believe that I do not want to draw individual
  68. // points.
  69. typedef struct tagTriangle
  70. { // Observe that each line packs to just 16 bits.
  71. // At present I leave 15 bits spare! I used to keep a colour in
  72. // that slot but now I keep colous with vertices not faces, so do
  73. // not need that space.
  74. unsigned int lineflag:1, moved:1, spare:14;
  75. unsigned int edge1:1, vertex1:15;
  76. unsigned int edge2:1, vertex2:15;
  77. unsigned int edge3:1, vertex3:15;
  78. } Triangle;
  79. // The above layout has several uncomfortable limitations. Perhaps the
  80. // most obvious is that it can only handle 32K points. This represents
  81. // a grid 180 by 180. A brief experiment with "gnuplot" suggests that it
  82. // runs out of space well before that, and many plots I have seen demonstrated
  83. // use a grid that is more like 20 by 20, The examples in the Jenks and
  84. // Sutor book on Axiom have their most impressive pictures using under
  85. // 25K vertices (a bit tight....) and I think that only their fractal
  86. // images (eg Antoine's Necklace) would defeat the structure I have here.
  87. // I could keep my triangle records the same size but allow for up to
  88. // about 1000000 vertices by packing vertex numbers in a way that would
  89. // cross word boundaries. I will consider that possibility later on.
  90. // I will do a somewhat crude job clipping triangles that do not intersect
  91. // the part of the screen that is being re-drawn.
  92. // I will also assume that triangles never intersect, so a version
  93. // of the painter's algorithm will do for rendering.
  94. XYZPoint *points;
  95. Triangle *triangles;
  96. int triangleCount = 0;
  97. #define x_steps 27
  98. #define y_steps 29
  99. XYZPoint vertices[(x_steps+1)*(y_steps+1)];
  100. Triangle faces[x_steps*y_steps*2];
  101. // I will want to colour faces based on their Z coordinate or some other
  102. // parameter. The following function maps an integer into a colour in
  103. // such a way that increasing the integer leads to a sequence of colours
  104. // that cycles through the rainbow and then back from blue to red via
  105. // magenta.
  106. void selectColour(int z, int &red, int &green, int &blue)
  107. {
  108. while (z < 0) z += (6*255)*0x00100000;
  109. z = z % (6*255);
  110. switch (z / 255)
  111. {
  112. case 0: red = 255;
  113. green = z % 255;
  114. blue = 0;
  115. return;
  116. case 1: red = 255 - z % 255;
  117. green = 255;
  118. blue = 0;
  119. return;
  120. case 2: red = 0;
  121. green = 255;
  122. blue = z % 255;
  123. return;
  124. case 3: red = 0;
  125. green = 255 - z % 255;
  126. blue = 255;
  127. return;
  128. case 4: red = z % 255;
  129. green = 0;
  130. blue = 255;
  131. return;
  132. case 5: red = 255;
  133. green = 0;
  134. blue = 255 - z % 255;
  135. return;
  136. }
  137. }
  138. // The "raw data" section of this code is really for debugging purposes only
  139. // and will need to be replaced by something that can accept surface
  140. // specifications from some external source.
  141. typedef struct tagRealPoint
  142. {
  143. double x, y, z;
  144. } RealPoint;
  145. RealPoint rawData[x_steps+1][y_steps+1];
  146. CBrush thirtyTwoBrushes[32];
  147. void initRawData()
  148. {
  149. int x, y;
  150. for (x=0; x<=x_steps; x++)
  151. { double rx = (6.0*(double)x)/(double)x_steps - 3.0;
  152. for (y=0; y<=y_steps; y++)
  153. { int n = x+(x_steps+1)*y;
  154. double ry = (6.0*(double)y)/(double)y_steps - 3.0;
  155. double rz = 3.0*cos(3.0*sqrt(rx*rx+ry*ry));
  156. rawData[x][y].x = rx;
  157. rawData[x][y].y = ry;
  158. rawData[x][y].z = rz;
  159. }
  160. }
  161. for (int i=0; i<32; i++)
  162. { int red, green, blue;
  163. selectColour((255*6*i)/32, red, green, blue);
  164. thirtyTwoBrushes[i].CreateSolidBrush(RGB(red, green, blue));
  165. }
  166. }
  167. static doneInit = 0;
  168. // I truncate at 2^30 because that is slightly easier than using the
  169. // whole of the integer range, and the cases where it makes a difference
  170. // will not arise very often.
  171. #define IntegerRange 1073741824.0 // 2^30
  172. void CGraphicsWindow::initSurface()
  173. {
  174. if (doneInit == 0)
  175. { initRawData();
  176. doneInit = 1;
  177. }
  178. int x, y, sx, sy;
  179. points = vertices;
  180. for (x=0; x<=x_steps; x++)
  181. { for (y=0; y<=y_steps; y++)
  182. { int n = x+(x_steps+1)*y;
  183. double rx = rawData[x][y].x;
  184. double ry = rawData[x][y].y;
  185. double rz = rawData[x][y].z;
  186. double aw = xform[3][3];
  187. double ax = (xform[0][0]*rx+xform[1][0]*ry+xform[2][0]*rz+xform[3][0])/aw;
  188. double ay = (xform[0][1]*rx+xform[1][1]*ry+xform[2][1]*rz+xform[3][1])/aw;
  189. double az = (xform[0][2]*rx+xform[1][2]*ry+xform[2][2]*rz+xform[3][2])/aw;
  190. double rw = perspecDistance/(perspecDistance - az);
  191. ax *= rw;
  192. ay *= rw;
  193. if (ax < -IntegerRange) sx = 0;
  194. else if (ax > IntegerRange) sx = 0xffff;
  195. else sx = 0x8000+(int)ax;
  196. if (sx < 0) sx = 0; else if (sx > 0xffff) sx = 0xffff;
  197. points[n].x = sx;
  198. if (ay < -IntegerRange) sy = 0;
  199. else if (ay > IntegerRange) sy = 0xffff;
  200. else sy = 0x8000+(int)ay;
  201. if (sy < 0) sy = 0; else if (sy > 0xffff) sy = 0xffff;
  202. points[n].y = sy;
  203. if (az < -IntegerRange) az = -IntegerRange;
  204. else if (az > IntegerRange) az = IntegerRange;
  205. points[n].z = (int)az;
  206. int red, green, blue;
  207. selectColour((int)(400.0*rz), red, green, blue);
  208. points[n].red = red;
  209. points[n].green = green;
  210. points[n].blue = blue;
  211. points[n].spare = 0xff & (int)(100*rz);
  212. }
  213. }
  214. triangles = faces;
  215. triangleCount = 0;
  216. for (x=0; x<x_steps; x++)
  217. { for (y=0; y<y_steps; y++)
  218. { int n = x+(x_steps+1)*y;
  219. // I set things up here so that edges 1 and 2 of each triangle are the
  220. // ones that form the diagonal of the rectangle that is being divided. I
  221. // do this so that it is at least partially reasonable to select a colour
  222. // for the whole of a triangle by averaging these...
  223. triangles[triangleCount].lineflag = 0;
  224. triangles[triangleCount].edge1 = 0;
  225. triangles[triangleCount].edge2 = 1;
  226. triangles[triangleCount].edge3 = 1;
  227. triangles[triangleCount+1].lineflag = 0;
  228. triangles[triangleCount+1].edge1 = 0;
  229. triangles[triangleCount+1].edge2 = 1;
  230. triangles[triangleCount+1].edge3 = 1;
  231. // Note I make the triangles clockwise here
  232. int x1 = points[n].x, y1 = points[n].y, z1 = points[n].z;
  233. int x2 = points[n+1].x, y2 = points[n+1].y, z2 = points[n+1].z;
  234. int x3 = points[n+x_steps+1].x, y3 = points[n+x_steps+1].y, z3 = points[n+x_steps+1].z;
  235. int x4 = points[n+x_steps+2].x, y4 = points[n+x_steps+2].y, z4 = points[n+x_steps+2].z;
  236. int d1 = x4-x1, d2 = y4-y1, d3 = z4-z1;
  237. int d4 = x3-x2, d5 = y3-y2, d6 = z3-z2;
  238. if (d1*d1+d2*d2+d3*d3 >= d4*d4+d5*d5+d6*d6)
  239. { triangles[triangleCount].vertex1 = n + x_steps + 1;
  240. triangles[triangleCount].vertex2 = n + 1;
  241. triangles[triangleCount].vertex3 = n;
  242. triangles[triangleCount+1].vertex1 = n + 1;
  243. triangles[triangleCount+1].vertex2 = n + x_steps + 1;
  244. triangles[triangleCount+1].vertex3 = n + x_steps + 2;
  245. }
  246. else
  247. { triangles[triangleCount].vertex1 = n + x_steps + 2;
  248. triangles[triangleCount].vertex2 = n;
  249. triangles[triangleCount].vertex3 = n + x_steps + 1;
  250. triangles[triangleCount+1].vertex1 = n;
  251. triangles[triangleCount+1].vertex2 = n + x_steps + 2;
  252. triangles[triangleCount+1].vertex3 = n + 1;
  253. }
  254. triangleCount += 2;
  255. }
  256. }
  257. sortTriangles();
  258. }
  259. int triangleLeft(const Triangle *a)
  260. {
  261. int w, r;
  262. r = points[a->vertex1].x;
  263. if ((w = points[a->vertex2].x) < r) r = w;
  264. if ((w = points[a->vertex3].x) < r) r = w;
  265. return r;
  266. }
  267. int triangleRight(const Triangle *a)
  268. {
  269. int w, r;
  270. r = points[a->vertex1].x;
  271. if ((w = points[a->vertex2].x) > r) r = w;
  272. if ((w = points[a->vertex3].x) > r) r = w;
  273. return r;
  274. }
  275. int triangleBottom(const Triangle *a)
  276. {
  277. int w, r;
  278. r = points[a->vertex1].y;
  279. if ((w = points[a->vertex2].y) < r) r = w;
  280. if ((w = points[a->vertex3].y) < r) r = w;
  281. return r;
  282. }
  283. int triangleTop(const Triangle *a)
  284. {
  285. int w, r;
  286. r = points[a->vertex1].y;
  287. if ((w = points[a->vertex2].y) > r) r = w;
  288. if ((w = points[a->vertex3].y) > r) r = w;
  289. return r;
  290. }
  291. int triangleFar(const Triangle *a)
  292. {
  293. int w, r;
  294. r = points[a->vertex1].z;
  295. if ((w = points[a->vertex2].z) < r) r = w;
  296. if ((w = points[a->vertex3].z) < r) r = w;
  297. return r;
  298. }
  299. int triangleNear(const Triangle *a)
  300. {
  301. int w, r;
  302. r = points[a->vertex1].z;
  303. if ((w = points[a->vertex2].z) > r) r = w;
  304. if ((w = points[a->vertex3].z) > r) r = w;
  305. return r;
  306. }
  307. int orderTriangles(const void *va, const void *vb)
  308. {
  309. int za = triangleFar((const Triangle *)va);
  310. int zb = triangleFar((const Triangle *)vb);
  311. if (za < zb) return -1;
  312. else if (za > zb) return 1;
  313. else return 0;
  314. }
  315. // The next function returns 1 is the line (a1-a2) lies above (and thus
  316. // obscures) the line (b1-b2), or -1 if there is an obscuring that goes
  317. // the other way.
  318. int linesCross(XYZPoint *a1, XYZPoint *a2, XYZPoint *b1, XYZPoint *b2)
  319. {
  320. // Firstly I will discard all cases where the line segments are not incident.
  321. int l1 = a1->y-a2->y, m1 = a2->x-a1->x, n1 = a1->x*a2->y-a2->x*a1->y;
  322. int u1 = l1*b1->x+m1*b1->y+n1, u2 = l1*b2->x+m1*b2->y+n1;
  323. if (u1 >= 0 && u2 >= 0) return 0;
  324. if (u1 <= 0 && u2 <= 0) return 0;
  325. int l2 = b1->y-b2->y, m2 = b2->x-b1->x, n2 = b1->x*b2->y-b2->x*b1->y;
  326. int v1 = l2*a1->x+m2*a1->y+n2, v2 = l2*a2->x+m2*a2->y+n2;
  327. if (v1 >= 0 && v2 >= 0) return 0;
  328. if (v1 <= 0 && v2 <= 0) return 0;
  329. // Now I know that the line segments cross in their XY projections, so I
  330. // compute the Z coordinates of the intersection.
  331. double z1 = ((double)v1*(double)a2->z-(double)v2*(double)a1->z)/
  332. (double)(v1-v2);
  333. double z2 = ((double)u1*(double)b2->z-(double)u2*(double)b1->z)/
  334. (double)(u1-u2);
  335. if (z1 > z2) return 1;
  336. if (z1 < z2) return -1;
  337. // The position here is possibly nasty - the two triangles DO overlap, but
  338. // at the point where the edges cross they have the same distance. I should
  339. // do something more here, but that can come later on.
  340. return 0;
  341. }
  342. int insideTriangle(XYZPoint *a, XYZPoint *b1, XYZPoint *b2, XYZPoint *b3)
  343. {
  344. // place-holder
  345. return 0;
  346. }
  347. int triangleObscures(Triangle *a, Triangle *b)
  348. {
  349. XYZPoint *a1 = &points[a->vertex1],
  350. *a2 = &points[a->vertex2],
  351. *a3 = &points[a->vertex3];
  352. XYZPoint *b1 = &points[b->vertex1],
  353. *b2 = &points[b->vertex2],
  354. *b3 = &points[b->vertex3];
  355. int v;
  356. if ((v = linesCross(a1, a2, b1, b2)) != 0) return (v > 0);
  357. if ((v = linesCross(a1, a2, b2, b3)) != 0) return (v > 0);
  358. if ((v = linesCross(a1, a2, b3, b1)) != 0) return (v > 0);
  359. if ((v = linesCross(a2, a3, b1, b2)) != 0) return (v > 0);
  360. if ((v = linesCross(a2, a3, b2, b3)) != 0) return (v > 0);
  361. if ((v = linesCross(a2, a3, b3, b1)) != 0) return (v > 0);
  362. if ((v = linesCross(a3, a1, b1, b2)) != 0) return (v > 0);
  363. if ((v = linesCross(a3, a1, b2, b3)) != 0) return (v > 0);
  364. if ((v = linesCross(a3, a1, b3, b1)) != 0) return (v > 0);
  365. if ((v = insideTriangle(a1, b1, b2, b3)) != 0) return v;
  366. if ((v = insideTriangle(a2, b1, b2, b3)) != 0) return v;
  367. if ((v = insideTriangle(a3, b1, b2, b3)) != 0) return v;
  368. if ((v = insideTriangle(b1, a1, a2, a3)) != 0) return v;
  369. if ((v = insideTriangle(b2, a1, a2, a3)) != 0) return v;
  370. if ((v = insideTriangle(b3, a1, a2, a3)) != 0) return v;
  371. return 0;
  372. }
  373. // After setting up the list of triangles (which must be re-done each time
  374. // I rotate or re-scale) I must call the following to Z-sort the data
  375. void CGraphicsWindow::sortTriangles()
  376. {
  377. // First I do a simple and direct sort based on the lowest point in each
  378. // triangle.
  379. qsort(triangles, triangleCount, sizeof(Triangle), orderTriangles);
  380. // Now the ordering I am left with is NOT guaranteed adequate, since I might
  381. // have a tall triangle whose lowest point is very low but where some
  382. // other triangle that starts higher up is in fact underneath it where they
  383. // happen to cross. I will do something more like an insertion sort
  384. // now to try to fix up such cases.
  385. int i;
  386. for (i=0; i<triangleCount; i++) triangles[i].moved = 0;
  387. i = 0;
  388. while (i<triangleCount-1)
  389. { int l = triangleLeft(&triangles[i]),
  390. r = triangleRight(&triangles[i]),
  391. t = triangleTop(&triangles[i]),
  392. b = triangleBottom(&triangles[i]),
  393. n = triangleNear(&triangles[i]);
  394. for (int j=i+1;j<triangleCount;j++)
  395. { if (triangleFar(&triangles[j]) >= n) break;
  396. if (triangleLeft(&triangles[j]) >= r) continue;
  397. if (triangleRight(&triangles[j]) <= l) continue;
  398. if (triangleBottom(&triangles[j]) >= t) continue;
  399. if (triangleTop(&triangles[j]) <= b) continue;
  400. if (triangleObscures(&triangles[i], &triangles[j]))
  401. { if (triangles[j].moved) continue;
  402. triangles[i].moved = 1;
  403. Triangle w = triangles[j];
  404. for (int k=j-1; k>=i; k--)
  405. triangles[k+1] = triangles[k];
  406. triangles[i] = w;
  407. i--;
  408. break;
  409. }
  410. }
  411. i++;
  412. }
  413. }
  414. // Supposing that the triangles have been sorted so that the bottom-most
  415. // one is at the front of the vector, I can just paint all the data...
  416. void CGraphicsWindow::paintTriangles(CDC *dc, RECT *clip, int width, int height)
  417. {
  418. // TESTING CODE HERE
  419. initSurface();
  420. // END OF TESTING CODE
  421. Triangle *p = triangles;
  422. int i;
  423. for (i=0; i<triangleCount; i++, p++)
  424. { BOOL lineflag = p->lineflag;
  425. XYZPoint *v1 = &points[p->vertex1],
  426. *v2 = &points[p->vertex2],
  427. *v3 = &points[p->vertex3];
  428. int xscale = (65536*width)/10000,
  429. yscale = (65536*height)/10000;
  430. int xoff = width/2, yoff = height/2;
  431. int x1 = xoff + ((((int)v1->x-32768)*xscale)>>16),
  432. y1 = yoff - ((((int)v1->y-32768)*yscale)>>16);
  433. int x2 = xoff + ((((int)v2->x-32768)*xscale)>>16),
  434. y2 = yoff - ((((int)v2->y-32768)*yscale)>>16);
  435. int x3 = xoff + ((((int)v3->x-32768)*xscale)>>16),
  436. y3 = yoff - ((((int)v3->y-32768)*yscale)>>16);
  437. if (clip != NULL)
  438. { int left = clip->left,
  439. top = clip->top,
  440. right = clip->right,
  441. bottom = clip->bottom;
  442. if (x1 < left && x2 < left && x3 < left) continue;
  443. if (x1 > right && x2 > right && x3 > right) continue;
  444. if (y1 < top && y2 < top && y3 < top) continue;
  445. if (y1 > bottom && y2 > bottom && y3 > bottom) continue;
  446. }
  447. int red1 = v1->red, green1 = v1->green, blue1 = v1->blue;
  448. int red2 = v2->red, green2 = v2->green, blue2 = v2->blue;
  449. int red3 = v3->red, green3 = v3->green, blue3 = v3->blue;
  450. BOOL e1 = p->edge1, e2 = p->edge2, e3 = p->edge3;
  451. if (fullRender || !wirePreview || !viewpointShown)
  452. { if (!drawWire && drawSurface != IDM_NOSURFACE) e1 = e2 = e3 = 0;
  453. switch (drawSurface)
  454. {
  455. case IDM_NOSURFACE:
  456. break;
  457. case IDM_SURFACE:
  458. { CBrush *b = &thirtyTwoBrushes[v1->spare & 0x1f];
  459. POINT tri[3];
  460. tri[0].x = x1; tri[0].y = y1;
  461. tri[1].x = x2; tri[1].y = y2;
  462. tri[2].x = x3; tri[2].y = y3;
  463. dc->SelectObject(b);
  464. dc->SelectStockObject(NULL_PEN);
  465. dc->Polygon(tri, 3);
  466. dc->SelectStockObject(BLACK_BRUSH);
  467. break;
  468. }
  469. case IDM_SQUARES:
  470. { CBrush b(RGB((red1+red2)/2, (green1+green2)/2, (blue1+blue2)/2));
  471. POINT tri[3];
  472. tri[0].x = x1; tri[0].y = y1;
  473. tri[1].x = x2; tri[1].y = y2;
  474. tri[2].x = x3; tri[2].y = y3;
  475. dc->SelectObject(&b);
  476. dc->SelectStockObject(NULL_PEN);
  477. dc->Polygon(tri, 3);
  478. dc->SelectStockObject(BLACK_BRUSH);
  479. break;
  480. }
  481. case IDM_TRIANGLES:
  482. { CBrush b(RGB((red1+red2+red3)/3,
  483. (green1+green2+green3)/3,
  484. (blue1+blue2+blue3)/3));
  485. POINT tri[3];
  486. tri[0].x = x1; tri[0].y = y1;
  487. tri[1].x = x2; tri[1].y = y2;
  488. tri[2].x = x3; tri[2].y = y3;
  489. dc->SelectObject(&b);
  490. dc->SelectStockObject(NULL_PEN);
  491. dc->Polygon(tri, 3);
  492. dc->SelectStockObject(BLACK_BRUSH);
  493. break;
  494. }
  495. case IDM_SMOOTH:
  496. dc->SelectStockObject(NULL_PEN);
  497. SubTriangle(dc, x1, y1, red1, green1, blue1,
  498. x2, y2, red2, green2, blue2,
  499. x3, y3, red3, green3, blue3, 2);
  500. break;
  501. case IDM_HISMOOTH:
  502. dc->SelectStockObject(NULL_PEN);
  503. SubTriangle(dc, x1, y1, red1, green1, blue1,
  504. x2, y2, red2, green2, blue2,
  505. x3, y3, red3, green3, blue3, 4);
  506. break;
  507. }
  508. }
  509. if (e1 && e3)
  510. { dc->SelectStockObject(BLACK_PEN);
  511. dc->MoveTo(x3, y3);
  512. dc->LineTo(x1, y1);
  513. dc->LineTo(x2, y2);
  514. if (e2) dc->LineTo(x3, y3);
  515. }
  516. else if (e1)
  517. { dc->SelectStockObject(BLACK_PEN);
  518. dc->MoveTo(x1, y1);
  519. dc->LineTo(x2, y2);
  520. if (e2) dc->LineTo(x3, y3);
  521. }
  522. else if (e2)
  523. { dc->SelectStockObject(BLACK_PEN);
  524. dc->MoveTo(x2, y2);
  525. dc->LineTo(x3, y3);
  526. if (e3) dc->LineTo(x1, y1);
  527. }
  528. else if (e3)
  529. { dc->SelectStockObject(BLACK_PEN);
  530. dc->MoveTo(x3, y3);
  531. dc->LineTo(x1, y1);
  532. }
  533. }
  534. if (!GetUpdateRect(NULL, FALSE))
  535. { fullyRendered = fullRender;
  536. fullRender = 0;
  537. }
  538. }
  539. #endif
  540. // end of c_render.cpp