1 //-----------------------------------------------------------------------------
2 // Helper functions that ultimately draw stuff with gl.
3 //
4 // Copyright 2008-2013 Jonathan Westhues.
5 //-----------------------------------------------------------------------------
6 #include <zlib.h>
7 #include "solvespace.h"
8
9 namespace SolveSpace {
10
11 // A vector font.
12 #include "generated/vectorfont.table.h"
13
14 // A bitmap font.
15 #include "generated/bitmapfont.table.h"
16
17 static bool ColorLocked;
18 static bool DepthOffsetLocked;
19
GetVectorGlyph(char32_t chr)20 static const VectorGlyph &GetVectorGlyph(char32_t chr) {
21 int first = 0;
22 int last = sizeof(VectorFont) / sizeof(VectorGlyph);
23 while(first <= last) {
24 int mid = (first + last) / 2;
25 char32_t midChr = VectorFont[mid].character;
26 if(midChr > chr) {
27 last = mid - 1; // and first stays the same
28 continue;
29 }
30 if(midChr < chr) {
31 first = mid + 1; // and last stays the same
32 continue;
33 }
34 return VectorFont[mid];
35 }
36 return GetVectorGlyph(0xfffd); // replacement character
37 }
38
39 // Internally and in the UI, the vector font is sized using cap height.
40 #define FONT_SCALE(h) ((h)/(double)FONT_CAP_HEIGHT)
ssglStrCapHeight(double h)41 double ssglStrCapHeight(double h)
42 {
43 return FONT_CAP_HEIGHT * FONT_SCALE(h) / SS.GW.scale;
44 }
ssglStrFontSize(double h)45 double ssglStrFontSize(double h)
46 {
47 return FONT_SIZE * FONT_SCALE(h) / SS.GW.scale;
48 }
ssglStrWidth(const std::string & str,double h)49 double ssglStrWidth(const std::string &str, double h)
50 {
51 int width = 0;
52 for(char32_t chr : ReadUTF8(str)) {
53 const VectorGlyph &glyph = GetVectorGlyph(chr);
54 if(glyph.baseCharacter != 0) {
55 const VectorGlyph &baseGlyph = GetVectorGlyph(glyph.baseCharacter);
56 width += max(glyph.advanceWidth, baseGlyph.advanceWidth);
57 } else {
58 width += glyph.advanceWidth;
59 }
60 }
61 return width * FONT_SCALE(h) / SS.GW.scale;
62 }
ssglWriteTextRefCenter(const std::string & str,double h,Vector t,Vector u,Vector v,ssglLineFn * fn,void * fndata)63 void ssglWriteTextRefCenter(const std::string &str, double h, Vector t, Vector u, Vector v,
64 ssglLineFn *fn, void *fndata)
65 {
66 u = u.WithMagnitude(1);
67 v = v.WithMagnitude(1);
68
69 double scale = FONT_SCALE(h)/SS.GW.scale;
70 double fh = ssglStrCapHeight(h);
71 double fw = ssglStrWidth(str, h);
72
73 t = t.Plus(u.ScaledBy(-fw/2));
74 t = t.Plus(v.ScaledBy(-fh/2));
75
76 // Apply additional offset to get an exact center alignment.
77 t = t.Plus(v.ScaledBy(-4608*scale));
78
79 ssglWriteText(str, h, t, u, v, fn, fndata);
80 }
81
ssglLineWidth(GLfloat width)82 void ssglLineWidth(GLfloat width) {
83 // Intel GPUs with Mesa on *nix render thin lines poorly.
84 static bool workaroundChecked, workaroundEnabled;
85 if(!workaroundChecked) {
86 // ssglLineWidth can be called before GL is initialized
87 if(glGetString(GL_VENDOR)) {
88 workaroundChecked = true;
89 if(!strcmp((char*)glGetString(GL_VENDOR), "Intel Open Source Technology Center"))
90 workaroundEnabled = true;
91 }
92 }
93
94 if(workaroundEnabled && width < 1.6f)
95 width = 1.6f;
96
97 glLineWidth(width);
98 }
99
LineDrawCallback(void * fndata,Vector a,Vector b)100 static void LineDrawCallback(void *fndata, Vector a, Vector b)
101 {
102 ssglLineWidth(1);
103 glBegin(GL_LINES);
104 ssglVertex3v(a);
105 ssglVertex3v(b);
106 glEnd();
107 }
108
pixelAlign(Vector v)109 Vector pixelAlign(Vector v) {
110 v = SS.GW.ProjectPoint3(v);
111 v.x = floor(v.x) + 0.5;
112 v.y = floor(v.y) + 0.5;
113 v = SS.GW.UnProjectPoint3(v);
114 return v;
115 }
116
ssglDrawCharacter(const VectorGlyph & glyph,Vector t,Vector o,Vector u,Vector v,double scale,ssglLineFn * fn,void * fndata,bool gridFit)117 int ssglDrawCharacter(const VectorGlyph &glyph, Vector t, Vector o, Vector u, Vector v,
118 double scale, ssglLineFn *fn, void *fndata, bool gridFit) {
119 int advanceWidth = glyph.advanceWidth;
120
121 if(glyph.baseCharacter != 0) {
122 const VectorGlyph &baseGlyph = GetVectorGlyph(glyph.baseCharacter);
123 int baseWidth = ssglDrawCharacter(baseGlyph, t, o, u, v, scale, fn, fndata, gridFit);
124 advanceWidth = max(glyph.advanceWidth, baseWidth);
125 }
126
127 int actualWidth, offsetX;
128 if(gridFit) {
129 o.x += glyph.leftSideBearing;
130 offsetX = glyph.leftSideBearing;
131 actualWidth = glyph.boundingWidth;
132 if(actualWidth == 0) {
133 // Dot, "i", etc.
134 actualWidth = 1;
135 }
136 } else {
137 offsetX = 0;
138 actualWidth = advanceWidth;
139 }
140
141 Vector tt = t;
142 tt = tt.Plus(u.ScaledBy(o.x * scale));
143 tt = tt.Plus(v.ScaledBy(o.y * scale));
144
145 Vector tu = tt;
146 tu = tu.Plus(u.ScaledBy(actualWidth * scale));
147
148 Vector tv = tt;
149 tv = tv.Plus(v.ScaledBy(FONT_CAP_HEIGHT * scale));
150
151 if(gridFit) {
152 tt = pixelAlign(tt);
153 tu = pixelAlign(tu);
154 tv = pixelAlign(tv);
155 }
156
157 tu = tu.Minus(tt).ScaledBy(1.0 / actualWidth);
158 tv = tv.Minus(tt).ScaledBy(1.0 / FONT_CAP_HEIGHT);
159
160 const int16_t *data = glyph.data;
161 bool pen_up = true;
162 Vector prevp;
163 while(true) {
164 int16_t x = *data++;
165 int16_t y = *data++;
166
167 if(x == PEN_UP && y == PEN_UP) {
168 if(pen_up) break;
169 pen_up = true;
170 } else {
171 Vector p = tt;
172 p = p.Plus(tu.ScaledBy(x - offsetX));
173 p = p.Plus(tv.ScaledBy(y));
174 if(!pen_up) fn(fndata, prevp, p);
175 prevp = p;
176 pen_up = false;
177 }
178 }
179
180 return advanceWidth;
181 }
182
ssglWriteText(const std::string & str,double h,Vector t,Vector u,Vector v,ssglLineFn * fn,void * fndata)183 void ssglWriteText(const std::string &str, double h, Vector t, Vector u, Vector v,
184 ssglLineFn *fn, void *fndata)
185 {
186 if(!fn) fn = LineDrawCallback;
187 u = u.WithMagnitude(1);
188 v = v.WithMagnitude(1);
189
190 // Perform grid-fitting only when the text is parallel to the view plane.
191 bool gridFit = !SS.exportMode && u.Equals(SS.GW.projRight) && v.Equals(SS.GW.projUp);
192
193 double scale = FONT_SCALE(h) / SS.GW.scale;
194 Vector o = { 3840.0, 3840.0, 0.0 };
195 for(char32_t chr : ReadUTF8(str)) {
196 const VectorGlyph &glyph = GetVectorGlyph(chr);
197 o.x += ssglDrawCharacter(glyph, t, o, u, v, scale, fn, fndata, gridFit);
198 }
199 }
200
ssglVertex3v(Vector u)201 void ssglVertex3v(Vector u)
202 {
203 glVertex3f((GLfloat)u.x, (GLfloat)u.y, (GLfloat)u.z);
204 }
205
ssglAxisAlignedQuad(double l,double r,double t,double b,bool lone)206 void ssglAxisAlignedQuad(double l, double r, double t, double b, bool lone)
207 {
208 if(lone) glBegin(GL_QUADS);
209 glVertex2d(l, t);
210 glVertex2d(l, b);
211 glVertex2d(r, b);
212 glVertex2d(r, t);
213 if(lone) glEnd();
214 }
215
ssglAxisAlignedLineLoop(double l,double r,double t,double b)216 void ssglAxisAlignedLineLoop(double l, double r, double t, double b)
217 {
218 glBegin(GL_LINE_LOOP);
219 glVertex2d(l, t);
220 glVertex2d(l, b);
221 glVertex2d(r, b);
222 glVertex2d(r, t);
223 glEnd();
224 }
225
FatLineEndcap(Vector p,Vector u,Vector v)226 static void FatLineEndcap(Vector p, Vector u, Vector v)
227 {
228 // A table of cos and sin of (pi*i/10 + pi/2), as i goes from 0 to 10
229 static const double Circle[11][2] = {
230 { 0.0000, 1.0000 },
231 { -0.3090, 0.9511 },
232 { -0.5878, 0.8090 },
233 { -0.8090, 0.5878 },
234 { -0.9511, 0.3090 },
235 { -1.0000, 0.0000 },
236 { -0.9511, -0.3090 },
237 { -0.8090, -0.5878 },
238 { -0.5878, -0.8090 },
239 { -0.3090, -0.9511 },
240 { 0.0000, -1.0000 },
241 };
242 glBegin(GL_TRIANGLE_FAN);
243 for(int i = 0; i <= 10; i++) {
244 double c = Circle[i][0], s = Circle[i][1];
245 ssglVertex3v(p.Plus(u.ScaledBy(c)).Plus(v.ScaledBy(s)));
246 }
247 glEnd();
248 }
249
ssglLine(const Vector & a,const Vector & b,double pixelWidth,bool maybeFat)250 void ssglLine(const Vector &a, const Vector &b, double pixelWidth, bool maybeFat) {
251 if(!maybeFat || pixelWidth <= 3.0) {
252 glBegin(GL_LINES);
253 ssglVertex3v(a);
254 ssglVertex3v(b);
255 glEnd();
256 } else {
257 ssglFatLine(a, b, pixelWidth / SS.GW.scale);
258 }
259 }
260
ssglPoint(Vector p,double pixelSize)261 void ssglPoint(Vector p, double pixelSize)
262 {
263 if(/*!maybeFat || */pixelSize <= 3.0) {
264 glBegin(GL_LINES);
265 Vector u = SS.GW.projRight.WithMagnitude(pixelSize / SS.GW.scale / 2.0);
266 ssglVertex3v(p.Minus(u));
267 ssglVertex3v(p.Plus(u));
268 glEnd();
269 } else {
270 Vector u = SS.GW.projRight.WithMagnitude(pixelSize / SS.GW.scale / 2.0);
271 Vector v = SS.GW.projUp.WithMagnitude(pixelSize / SS.GW.scale / 2.0);
272
273 FatLineEndcap(p, u, v);
274 FatLineEndcap(p, u.ScaledBy(-1.0), v);
275 }
276 }
277
ssglStippledLine(Vector a,Vector b,double width,int stippleType,double stippleScale,bool maybeFat)278 void ssglStippledLine(Vector a, Vector b, double width,
279 int stippleType, double stippleScale, bool maybeFat)
280 {
281 const char *stipplePattern;
282 switch(stippleType) {
283 case Style::STIPPLE_CONTINUOUS: ssglLine(a, b, width, maybeFat); return;
284 case Style::STIPPLE_DASH: stipplePattern = "- "; break;
285 case Style::STIPPLE_LONG_DASH: stipplePattern = "_ "; break;
286 case Style::STIPPLE_DASH_DOT: stipplePattern = "-."; break;
287 case Style::STIPPLE_DASH_DOT_DOT: stipplePattern = "-.."; break;
288 case Style::STIPPLE_DOT: stipplePattern = "."; break;
289 case Style::STIPPLE_FREEHAND: stipplePattern = "~"; break;
290 case Style::STIPPLE_ZIGZAG: stipplePattern = "~__"; break;
291 default: oops();
292 }
293 ssglStippledLine(a, b, width, stipplePattern, stippleScale, maybeFat);
294 }
295
ssglStippledLine(Vector a,Vector b,double width,const char * stipplePattern,double stippleScale,bool maybeFat)296 void ssglStippledLine(Vector a, Vector b, double width,
297 const char *stipplePattern, double stippleScale, bool maybeFat)
298 {
299 if(stipplePattern == NULL || *stipplePattern == 0) oops();
300
301 Vector dir = b.Minus(a);
302 double len = dir.Magnitude();
303 dir = dir.WithMagnitude(1.0);
304
305 const char *si = stipplePattern;
306 double end = len;
307 double ss = stippleScale / 2.0;
308 do {
309 double start = end;
310 switch(*si) {
311 case ' ':
312 end -= 1.0 * ss;
313 break;
314
315 case '-':
316 start = max(start - 0.5 * ss, 0.0);
317 end = max(start - 2.0 * ss, 0.0);
318 if(start == end) break;
319 ssglLine(a.Plus(dir.ScaledBy(start)), a.Plus(dir.ScaledBy(end)), width, maybeFat);
320 end = max(end - 0.5 * ss, 0.0);
321 break;
322
323 case '_':
324 end = max(end - 4.0 * ss, 0.0);
325 ssglLine(a.Plus(dir.ScaledBy(start)), a.Plus(dir.ScaledBy(end)), width, maybeFat);
326 break;
327
328 case '.':
329 end = max(end - 0.5 * ss, 0.0);
330 if(end == 0.0) break;
331 ssglPoint(a.Plus(dir.ScaledBy(end)), width);
332 end = max(end - 0.5 * ss, 0.0);
333 break;
334
335 case '~': {
336 Vector ab = b.Minus(a);
337 Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
338 Vector abn = (ab.Cross(gn)).WithMagnitude(1);
339 abn = abn.Minus(gn.ScaledBy(gn.Dot(abn)));
340 double pws = 2.0 * width / SS.GW.scale;
341
342 end = max(end - 0.5 * ss, 0.0);
343 Vector aa = a.Plus(dir.ScaledBy(start));
344 Vector bb = a.Plus(dir.ScaledBy(end))
345 .Plus(abn.ScaledBy(pws * (start - end) / (0.5 * ss)));
346 ssglLine(aa, bb, width, maybeFat);
347 if(end == 0.0) break;
348
349 start = end;
350 end = max(end - 1.0 * ss, 0.0);
351 aa = a.Plus(dir.ScaledBy(end))
352 .Plus(abn.ScaledBy(pws))
353 .Minus(abn.ScaledBy(2.0 * pws * (start - end) / ss));
354 ssglLine(bb, aa, width, maybeFat);
355 if(end == 0.0) break;
356
357 start = end;
358 end = max(end - 0.5 * ss, 0.0);
359 bb = a.Plus(dir.ScaledBy(end))
360 .Minus(abn.ScaledBy(pws))
361 .Plus(abn.ScaledBy(pws * (start - end) / (0.5 * ss)));
362 ssglLine(aa, bb, width, maybeFat);
363 break;
364 }
365
366 default: oops();
367 }
368 if(*(++si) == 0) si = stipplePattern;
369 } while(end > 0.0);
370 }
371
ssglFatLine(Vector a,Vector b,double width)372 void ssglFatLine(Vector a, Vector b, double width)
373 {
374 if(a.EqualsExactly(b)) return;
375 // The half-width of the line we're drawing.
376 double hw = width / 2;
377 Vector ab = b.Minus(a);
378 Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
379 Vector abn = (ab.Cross(gn)).WithMagnitude(1);
380 abn = abn.Minus(gn.ScaledBy(gn.Dot(abn)));
381 // So now abn is normal to the projection of ab into the screen, so the
382 // line will always have constant thickness as the view is rotated.
383
384 abn = abn.WithMagnitude(hw);
385 ab = gn.Cross(abn);
386 ab = ab. WithMagnitude(hw);
387
388 // The body of a line is a quad
389 glBegin(GL_QUADS);
390 ssglVertex3v(a.Minus(abn));
391 ssglVertex3v(b.Minus(abn));
392 ssglVertex3v(b.Plus (abn));
393 ssglVertex3v(a.Plus (abn));
394 glEnd();
395 // And the line has two semi-circular end caps.
396 FatLineEndcap(a, ab, abn);
397 FatLineEndcap(b, ab.ScaledBy(-1), abn);
398 }
399
400
ssglLockColorTo(RgbaColor rgb)401 void ssglLockColorTo(RgbaColor rgb)
402 {
403 ColorLocked = false;
404 glColor3d(rgb.redF(), rgb.greenF(), rgb.blueF());
405 ColorLocked = true;
406 }
407
ssglUnlockColor(void)408 void ssglUnlockColor(void)
409 {
410 ColorLocked = false;
411 }
412
ssglColorRGB(RgbaColor rgb)413 void ssglColorRGB(RgbaColor rgb)
414 {
415 // Is there a bug in some graphics drivers where this is not equivalent
416 // to glColor3d? There seems to be...
417 ssglColorRGBa(rgb, 1.0);
418 }
419
ssglColorRGBa(RgbaColor rgb,double a)420 void ssglColorRGBa(RgbaColor rgb, double a)
421 {
422 if(!ColorLocked) glColor4d(rgb.redF(), rgb.greenF(), rgb.blueF(), a);
423 }
424
Stipple(bool forSel)425 static void Stipple(bool forSel)
426 {
427 static bool Init;
428 const int BYTES = (32*32)/8;
429 static GLubyte HoverMask[BYTES];
430 static GLubyte SelMask[BYTES];
431 if(!Init) {
432 int x, y;
433 for(x = 0; x < 32; x++) {
434 for(y = 0; y < 32; y++) {
435 int i = y*4 + x/8, b = x % 8;
436 int ym = y % 4, xm = x % 4;
437 for(int k = 0; k < 2; k++) {
438 if(xm >= 1 && xm <= 2 && ym >= 1 && ym <= 2) {
439 (k == 0 ? SelMask : HoverMask)[i] |= (0x80 >> b);
440 }
441 ym = (ym + 2) % 4; xm = (xm + 2) % 4;
442 }
443 }
444 }
445 Init = true;
446 }
447
448 glEnable(GL_POLYGON_STIPPLE);
449 if(forSel) {
450 glPolygonStipple(SelMask);
451 } else {
452 glPolygonStipple(HoverMask);
453 }
454 }
455
StippleTriangle(STriangle * tr,bool s,RgbaColor rgb)456 static void StippleTriangle(STriangle *tr, bool s, RgbaColor rgb)
457 {
458 glEnd();
459 glDisable(GL_LIGHTING);
460 ssglColorRGB(rgb);
461 Stipple(s);
462 glBegin(GL_TRIANGLES);
463 ssglVertex3v(tr->a);
464 ssglVertex3v(tr->b);
465 ssglVertex3v(tr->c);
466 glEnd();
467 glEnable(GL_LIGHTING);
468 glDisable(GL_POLYGON_STIPPLE);
469 glBegin(GL_TRIANGLES);
470 }
471
ssglFillMesh(bool useSpecColor,RgbaColor specColor,SMesh * m,uint32_t h,uint32_t s1,uint32_t s2)472 void ssglFillMesh(bool useSpecColor, RgbaColor specColor,
473 SMesh *m, uint32_t h, uint32_t s1, uint32_t s2)
474 {
475 RgbaColor rgbHovered = Style::Color(Style::HOVERED),
476 rgbSelected = Style::Color(Style::SELECTED);
477
478 glEnable(GL_NORMALIZE);
479 bool hasMaterial = false;
480 RgbaColor prevColor;
481 glBegin(GL_TRIANGLES);
482 for(int i = 0; i < m->l.n; i++) {
483 STriangle *tr = &(m->l.elem[i]);
484
485 RgbaColor color;
486 if(useSpecColor) {
487 color = specColor;
488 } else {
489 color = tr->meta.color;
490 }
491 if(!hasMaterial || !color.Equals(prevColor)) {
492 GLfloat mpf[] = { color.redF(), color.greenF(), color.blueF(), color.alphaF() };
493 glEnd();
494 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mpf);
495 prevColor = color;
496 hasMaterial = true;
497 glBegin(GL_TRIANGLES);
498 }
499
500 if(tr->an.EqualsExactly(Vector::From(0, 0, 0))) {
501 // Compute the normal from the vertices
502 Vector n = tr->Normal();
503 glNormal3d(n.x, n.y, n.z);
504 ssglVertex3v(tr->a);
505 ssglVertex3v(tr->b);
506 ssglVertex3v(tr->c);
507 } else {
508 // Use the exact normals that are specified
509 glNormal3d((tr->an).x, (tr->an).y, (tr->an).z);
510 ssglVertex3v(tr->a);
511
512 glNormal3d((tr->bn).x, (tr->bn).y, (tr->bn).z);
513 ssglVertex3v(tr->b);
514
515 glNormal3d((tr->cn).x, (tr->cn).y, (tr->cn).z);
516 ssglVertex3v(tr->c);
517 }
518
519 if((s1 != 0 && tr->meta.face == s1) ||
520 (s2 != 0 && tr->meta.face == s2))
521 {
522 StippleTriangle(tr, true, rgbSelected);
523 }
524 if(h != 0 && tr->meta.face == h) {
525 StippleTriangle(tr, false, rgbHovered);
526 }
527 }
528 glEnd();
529 }
530
Vertex(Vector * p)531 static void SSGL_CALLBACK Vertex(Vector *p)
532 {
533 ssglVertex3v(*p);
534 }
ssglFillPolygon(SPolygon * p)535 void ssglFillPolygon(SPolygon *p)
536 {
537 GLUtesselator *gt = gluNewTess();
538 gluTessCallback(gt, GLU_TESS_BEGIN, (ssglCallbackFptr *)glBegin);
539 gluTessCallback(gt, GLU_TESS_END, (ssglCallbackFptr *)glEnd);
540 gluTessCallback(gt, GLU_TESS_VERTEX, (ssglCallbackFptr *)Vertex);
541
542 ssglTesselatePolygon(gt, p);
543
544 gluDeleteTess(gt);
545 }
546
Combine(double coords[3],void * vertexData[4],float weight[4],void ** outData)547 static void SSGL_CALLBACK Combine(double coords[3], void *vertexData[4],
548 float weight[4], void **outData)
549 {
550 Vector *n = (Vector *)AllocTemporary(sizeof(Vector));
551 n->x = coords[0];
552 n->y = coords[1];
553 n->z = coords[2];
554
555 *outData = n;
556 }
ssglTesselatePolygon(GLUtesselator * gt,SPolygon * p)557 void ssglTesselatePolygon(GLUtesselator *gt, SPolygon *p)
558 {
559 int i, j;
560
561 gluTessCallback(gt, GLU_TESS_COMBINE, (ssglCallbackFptr *)Combine);
562 gluTessProperty(gt, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
563
564 Vector normal = p->normal;
565 glNormal3d(normal.x, normal.y, normal.z);
566 gluTessNormal(gt, normal.x, normal.y, normal.z);
567
568 gluTessBeginPolygon(gt, NULL);
569 for(i = 0; i < p->l.n; i++) {
570 SContour *sc = &(p->l.elem[i]);
571 gluTessBeginContour(gt);
572 for(j = 0; j < (sc->l.n-1); j++) {
573 SPoint *sp = &(sc->l.elem[j]);
574 double ap[3];
575 ap[0] = sp->p.x;
576 ap[1] = sp->p.y;
577 ap[2] = sp->p.z;
578 gluTessVertex(gt, ap, &(sp->p));
579 }
580 gluTessEndContour(gt);
581 }
582 gluTessEndPolygon(gt);
583 }
584
ssglDebugPolygon(SPolygon * p)585 void ssglDebugPolygon(SPolygon *p)
586 {
587 int i, j;
588 ssglLineWidth(2);
589 glPointSize(7);
590 glDisable(GL_DEPTH_TEST);
591 for(i = 0; i < p->l.n; i++) {
592 SContour *sc = &(p->l.elem[i]);
593 for(j = 0; j < (sc->l.n-1); j++) {
594 Vector a = (sc->l.elem[j]).p;
595 Vector b = (sc->l.elem[j+1]).p;
596
597 ssglLockColorTo(RGBi(0, 0, 255));
598 Vector d = (a.Minus(b)).WithMagnitude(-0);
599 glBegin(GL_LINES);
600 ssglVertex3v(a.Plus(d));
601 ssglVertex3v(b.Minus(d));
602 glEnd();
603 ssglLockColorTo(RGBi(255, 0, 0));
604 glBegin(GL_POINTS);
605 ssglVertex3v(a.Plus(d));
606 ssglVertex3v(b.Minus(d));
607 glEnd();
608 }
609 }
610 }
611
ssglDrawEdges(SEdgeList * el,bool endpointsToo,hStyle hs)612 void ssglDrawEdges(SEdgeList *el, bool endpointsToo, hStyle hs)
613 {
614 double lineWidth = Style::Width(hs);
615 int stippleType = Style::PatternType(hs);
616 double stippleScale = Style::StippleScaleMm(hs);
617 ssglLineWidth(float(lineWidth));
618 ssglColorRGB(Style::Color(hs));
619
620 SEdge *se;
621 for(se = el->l.First(); se; se = el->l.NextAfter(se)) {
622 ssglStippledLine(se->a, se->b, lineWidth, stippleType, stippleScale,
623 /*maybeFat=*/true);
624 }
625
626 if(endpointsToo) {
627 glPointSize(12);
628 glBegin(GL_POINTS);
629 for(se = el->l.First(); se; se = el->l.NextAfter(se)) {
630 ssglVertex3v(se->a);
631 ssglVertex3v(se->b);
632 }
633 glEnd();
634 }
635 }
636
ssglDrawOutlines(SOutlineList * sol,Vector projDir,hStyle hs)637 void ssglDrawOutlines(SOutlineList *sol, Vector projDir, hStyle hs)
638 {
639 double lineWidth = Style::Width(hs);
640 int stippleType = Style::PatternType(hs);
641 double stippleScale = Style::StippleScaleMm(hs);
642 ssglLineWidth((float)lineWidth);
643 ssglColorRGB(Style::Color(hs));
644
645 sol->FillOutlineTags(projDir);
646 for(SOutline *so = sol->l.First(); so; so = sol->l.NextAfter(so)) {
647 if(!so->tag) continue;
648 ssglStippledLine(so->a, so->b, lineWidth, stippleType, stippleScale,
649 /*maybeFat=*/true);
650 }
651 }
652
ssglDebugMesh(SMesh * m)653 void ssglDebugMesh(SMesh *m)
654 {
655 int i;
656 ssglLineWidth(1);
657 glPointSize(7);
658 ssglDepthRangeOffset(1);
659 ssglUnlockColor();
660 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
661 ssglColorRGBa(RGBi(0, 255, 0), 1.0);
662 glBegin(GL_TRIANGLES);
663 for(i = 0; i < m->l.n; i++) {
664 STriangle *t = &(m->l.elem[i]);
665 if(t->tag) continue;
666
667 ssglVertex3v(t->a);
668 ssglVertex3v(t->b);
669 ssglVertex3v(t->c);
670 }
671 glEnd();
672 ssglDepthRangeOffset(0);
673 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
674 }
675
ssglMarkPolygonNormal(SPolygon * p)676 void ssglMarkPolygonNormal(SPolygon *p)
677 {
678 Vector tail = Vector::From(0, 0, 0);
679 int i, j, cnt = 0;
680 // Choose some reasonable center point.
681 for(i = 0; i < p->l.n; i++) {
682 SContour *sc = &(p->l.elem[i]);
683 for(j = 0; j < (sc->l.n-1); j++) {
684 SPoint *sp = &(sc->l.elem[j]);
685 tail = tail.Plus(sp->p);
686 cnt++;
687 }
688 }
689 if(cnt == 0) return;
690 tail = tail.ScaledBy(1.0/cnt);
691
692 Vector gn = SS.GW.projRight.Cross(SS.GW.projUp);
693 Vector tip = tail.Plus((p->normal).WithMagnitude(40/SS.GW.scale));
694 Vector arrow = (p->normal).WithMagnitude(15/SS.GW.scale);
695
696 glColor3d(1, 1, 0);
697 glBegin(GL_LINES);
698 ssglVertex3v(tail);
699 ssglVertex3v(tip);
700 ssglVertex3v(tip);
701 ssglVertex3v(tip.Minus(arrow.RotatedAbout(gn, 0.6)));
702 ssglVertex3v(tip);
703 ssglVertex3v(tip.Minus(arrow.RotatedAbout(gn, -0.6)));
704 glEnd();
705 glEnable(GL_LIGHTING);
706 }
707
ssglDepthRangeOffset(int units)708 void ssglDepthRangeOffset(int units)
709 {
710 if(!DepthOffsetLocked) {
711 // The size of this step depends on the resolution of the Z buffer; for
712 // a 16-bit buffer, this should be fine.
713 double d = units/60000.0;
714 glDepthRange(0.1-d, 1-d);
715 }
716 }
717
ssglDepthRangeLockToFront(bool yes)718 void ssglDepthRangeLockToFront(bool yes)
719 {
720 if(yes) {
721 DepthOffsetLocked = true;
722 glDepthRange(0, 0);
723 } else {
724 DepthOffsetLocked = false;
725 ssglDepthRangeOffset(0);
726 }
727 }
728
729 const int BitmapFontChunkSize = 64 * 64;
730 static bool BitmapFontChunkInitialized[0x10000 / BitmapFontChunkSize];
731 static int BitmapFontCurrentChunk = -1;
732
CreateBitmapFontChunk(const uint8_t * source,size_t sourceLength,int textureIndex)733 static void CreateBitmapFontChunk(const uint8_t *source, size_t sourceLength,
734 int textureIndex)
735 {
736 // Place the font in our texture in a two-dimensional grid.
737 // The maximum texture size that is reasonably supported is 1024x1024.
738 const size_t fontTextureSize = BitmapFontChunkSize*16*16;
739 uint8_t *fontTexture = (uint8_t *)malloc(fontTextureSize),
740 *mappedTexture = (uint8_t *)malloc(fontTextureSize);
741
742 z_stream stream;
743 stream.zalloc = Z_NULL;
744 stream.zfree = Z_NULL;
745 stream.opaque = Z_NULL;
746 if(inflateInit(&stream) != Z_OK)
747 oops();
748
749 stream.next_in = (Bytef *)source;
750 stream.avail_in = sourceLength;
751 stream.next_out = fontTexture;
752 stream.avail_out = fontTextureSize;
753 if(inflate(&stream, Z_NO_FLUSH) != Z_STREAM_END)
754 oops();
755 if(stream.avail_out != 0)
756 oops();
757
758 inflateEnd(&stream);
759
760 for(int a = 0; a < BitmapFontChunkSize; a++) {
761 int row = a / 64, col = a % 64;
762
763 for(int i = 0; i < 16; i++) {
764 memcpy(mappedTexture + row*64*16*16 + col*16 + i*64*16,
765 fontTexture + a*16*16 + i*16,
766 16);
767 }
768 }
769
770 free(fontTexture);
771
772 glBindTexture(GL_TEXTURE_2D, textureIndex);
773 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
774 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
775 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
776 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
777 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
778 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA,
779 16*64, 64*16,
780 0,
781 GL_ALPHA, GL_UNSIGNED_BYTE,
782 mappedTexture);
783
784 free(mappedTexture);
785 }
786
SwitchToBitmapFontChunkFor(char32_t chr)787 static void SwitchToBitmapFontChunkFor(char32_t chr)
788 {
789 int plane = chr / BitmapFontChunkSize,
790 textureIndex = TEXTURE_BITMAP_FONT + plane;
791
792 if(BitmapFontCurrentChunk != textureIndex) {
793 glEnd();
794
795 if(!BitmapFontChunkInitialized[plane]) {
796 CreateBitmapFontChunk(CompressedFontTexture[plane].data,
797 CompressedFontTexture[plane].length,
798 textureIndex);
799 BitmapFontChunkInitialized[plane] = true;
800 } else {
801 glBindTexture(GL_TEXTURE_2D, textureIndex);
802 }
803
804 BitmapFontCurrentChunk = textureIndex;
805
806 glBegin(GL_QUADS);
807 }
808 }
809
ssglInitializeBitmapFont()810 void ssglInitializeBitmapFont()
811 {
812 memset(BitmapFontChunkInitialized, 0, sizeof(BitmapFontChunkInitialized));
813 BitmapFontCurrentChunk = -1;
814 }
815
ssglBitmapCharWidth(char32_t chr)816 int ssglBitmapCharWidth(char32_t chr) {
817 if(!CodepointProperties[chr].exists)
818 chr = 0xfffd; // replacement character
819
820 return CodepointProperties[chr].isWide ? 2 : 1;
821 }
822
ssglBitmapCharQuad(char32_t chr,double x,double y)823 void ssglBitmapCharQuad(char32_t chr, double x, double y)
824 {
825 int w, h;
826
827 if(!CodepointProperties[chr].exists)
828 chr = 0xfffd; // replacement character
829
830 h = 16;
831 if(chr >= 0xe000 && chr <= 0xefff) {
832 // Special character, like a checkbox or a radio button
833 w = 16;
834 x -= 3;
835 } else if(CodepointProperties[chr].isWide) {
836 // Wide (usually CJK or reserved) character
837 w = 16;
838 } else {
839 // Normal character
840 w = 8;
841 }
842
843 if(chr != ' ' && chr != 0) {
844 int n = chr % BitmapFontChunkSize;
845 int row = n / 64, col = n % 64;
846 double s0 = col/64.0,
847 s1 = (col+1)/64.0,
848 t0 = row/64.0,
849 t1 = t0 + (w/16.0)/64;
850
851 SwitchToBitmapFontChunkFor(chr);
852
853 glTexCoord2d(s1, t0);
854 glVertex2d(x, y);
855
856 glTexCoord2d(s1, t1);
857 glVertex2d(x + w, y);
858
859 glTexCoord2d(s0, t1);
860 glVertex2d(x + w, y - h);
861
862 glTexCoord2d(s0, t0);
863 glVertex2d(x, y - h);
864 }
865 }
866
ssglBitmapText(const std::string & str,Vector p)867 void ssglBitmapText(const std::string &str, Vector p)
868 {
869 glEnable(GL_TEXTURE_2D);
870 glBegin(GL_QUADS);
871 for(char32_t chr : ReadUTF8(str)) {
872 ssglBitmapCharQuad(chr, p.x, p.y);
873 p.x += 8 * ssglBitmapCharWidth(chr);
874 }
875 glEnd();
876 glDisable(GL_TEXTURE_2D);
877 }
878
ssglDrawPixelsWithTexture(uint8_t * data,int w,int h)879 void ssglDrawPixelsWithTexture(uint8_t *data, int w, int h)
880 {
881 #define MAX_DIM 32
882 static uint8_t Texture[MAX_DIM*MAX_DIM*3];
883 int i, j;
884 if(w > MAX_DIM || h > MAX_DIM) oops();
885
886 for(i = 0; i < w; i++) {
887 for(j = 0; j < h; j++) {
888 Texture[(j*MAX_DIM + i)*3 + 0] = data[(j*w + i)*3 + 0];
889 Texture[(j*MAX_DIM + i)*3 + 1] = data[(j*w + i)*3 + 1];
890 Texture[(j*MAX_DIM + i)*3 + 2] = data[(j*w + i)*3 + 2];
891 }
892 }
893
894 glBindTexture(GL_TEXTURE_2D, TEXTURE_DRAW_PIXELS);
895 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
896 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
897 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
898 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
899 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
900
901 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, MAX_DIM, MAX_DIM, 0,
902 GL_RGB, GL_UNSIGNED_BYTE, Texture);
903
904 glEnable(GL_TEXTURE_2D);
905 glBegin(GL_QUADS);
906 glTexCoord2d(0, 0);
907 glVertex2d(0, h);
908
909 glTexCoord2d(((double)w)/MAX_DIM, 0);
910 glVertex2d(w, h);
911
912 glTexCoord2d(((double)w)/MAX_DIM, ((double)h)/MAX_DIM);
913 glVertex2d(w, 0);
914
915 glTexCoord2d(0, ((double)h)/MAX_DIM);
916 glVertex2d(0, 0);
917 glEnd();
918 glDisable(GL_TEXTURE_2D);
919 }
920
921 };
922