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