1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* text3d --- Shows moving 3D texts */
3 
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)text3d.cc	5.02 2001/11/09 xlockmore";
6 
7 #endif
8 
9 /* Copyright (c) E. Lassauge, 1998. */
10 
11 /*
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * This module is based on a demo of the gltt graphics library
25  * Copyright (C) 1998 Stephane Rehel.
26  *
27  * See the gltt Official Site at http://gltt.sourceforge.net/
28  * May have better luck at http://lassauge.free.fr/xlock/
29  *
30  * My e-mail address changed to <lassauge AT users.sourceforge.net>
31  * Web site at http://lassauge.free.fr/
32  *
33  * Eric Lassauge  (October-28-1999)
34  *
35  * Revision History:
36  * 09-Nov-2001: Removed BLANK and various cleanups
37  *              Added Wander animation
38  * 09-Mar-2001: Removed an erroneous PushMatrix !!!
39  * 01-Nov-2000: Allocation checks
40  * 28-Oct-1999: fixes from Jouk "I play with every mode" Jansen.
41  *               Option ttanimate added.
42  * 02-Jun-1999: patches for initialization errors of GLTT library.
43  *              Thanks to Jouk Jansen and Scott <mcmillan@cambridge.com>.
44  *              text3d updates for fortunes thanks to Jouk Jansen
45  *              <joukj AT hrem.nano.tudelft.nl>
46  *              Option no_split added.
47  * 23-Aug-1998: add better handling of "faulty" fontfile and randomize
48  *              fontfile if '-ttfont' value is a directory.
49  *              Minor changes for AIX from Jouk Jansen
50  *              (joukj AT hrem.nano.tudelft.nl).
51  *
52  * TODO :
53  *       Need more animation functions. Help welcome !!
54  *       Light problem with some letters (don't know why they "reflect" more):
55  *       is the problem in gltt or Mesa ???
56  *       SPEED !!!!
57  *       It may sigfault when compiled with -fschedule-insns2
58  *         (i686-pc-linux-gnu)
59  *
60  */
61 
62 #ifdef STANDALONE	/* xscreensaver mode: can't work ! */
63 #define MODE_text3d
64 #define DEFAULTS 	"*delay: 	100000 \n"
65 
66 /* 			"*ncolors: 	    64 \n" \
67  			"*font:	     arial.ttf \n" \
68  			"*text:X-Window System \n"
69 
70  			"*filename: 	       \n" \
71  			"*fortunefile: 	       \n" \
72  			"*program: 	       \n"*/
73 
74 #define free_text3d 0
75 #define text3d_handle_event 0
76 extern "C"
77 {
78   #include "xlockmore.h"	/* from the xscreensaver distribution */
79   /*#include "iostuff.h"*/
80 }
81 #else				/* !STANDALONE */
82   #include "xlock.h"		/* from the xlockmore distribution */
83   #include "visgl.h"
84   #ifdef HAS_MMOV
85     #undef error
86   #endif
87   #include "iostuff.h"
88 #endif				/* !STANDALONE */
89 
90 #ifdef MODE_text3d
91 
92 #include <gltt/FTEngine.h>
93 #include <gltt/FTFace.h>
94 #include <gltt/FTInstance.h>
95 #include <gltt/FTGlyph.h>
96 #include <gltt/FTFont.h>
97 #include <gltt/GLTTOutlineFont.h>
98 #include <gltt/GLTTFont.h>
99 #include <gltt/GLTTGlyphPolygonizer.h>
100 #include <gltt/GLTTGlyphTriangulator.h>
101 
102 #include "text3d.h"
103 #include <GL/glu.h>
104 
105 /* Yes, it's an ugly mix of 'C' and 'C++' functions */
106 #ifndef STANDALONE
107 extern "C" { void init_text3d(ModeInfo * mi); }
108 extern "C" { void draw_text3d(ModeInfo * mi); }
109 extern "C" { void change_text3d(ModeInfo * mi); }
110 extern "C" { void release_text3d(ModeInfo * mi); }
111 extern "C" { void refresh_text3d(ModeInfo * mi); }
112 #endif
113 
114 /* arial.ttf is not supplied for legal reasons. */
115 /* NT and Windows 3.1 in C:\WINDOWS\SYSTEM\ARIAL.TTF */
116 /* Windows95 in C:\Windows\Fonts\arial.ttf */
117 
118 #ifndef DEF_TTFONT
119 /* Directory of only *.ttf */
120 /* symbol.ttf and wingding.ttf should be excluded or it may core dump */
121 /* can be excluded if gltt is modified see README */
122 #ifdef __CYGWIN__
123 #define DEF_TTFONT "C:\\Windows\\Fonts\\"
124 #else
125 #define DEF_TTFONT "/usr/lib/X11/xlock/fonts/"
126 #endif
127 #endif
128 
129 #define DEF_EXTRUSION  "25.0"
130 #define DEF_ROTAMPL  "1.0"
131 #define DEF_ROTFREQ  "0.001"
132 #define DEF_FONTSIZE  220
133 #define DEF_NOSPLIT   "False"
134 #define DEF_ANIMATE   "Default"
135 static float extrusion;
136 static float rampl;
137 static float rfreq;
138 static char *mode_font;
139 static int nosplit;
140 static char *animate;
141 
142 /* Manage Option vars */
143 
144 static XrmOptionDescRec opts[] =
145 {
146     {(char *) "-ttfont", (char *) ".text3d.ttfont", XrmoptionSepArg, (caddr_t) NULL},
147     {(char *) "-no_split", (char *) ".text3d.no_split", XrmoptionNoArg, (caddr_t) "on"},
148     {(char *) "+no_split", (char *) ".text3d.no_split", XrmoptionNoArg, (caddr_t) "off"},
149     {(char *) "-extrusion", (char *) ".text3d.extrusion", XrmoptionSepArg, (caddr_t) NULL},
150     {(char *) "-rot_amplitude", (char *) ".text3d.rot_amplitude", XrmoptionSepArg, (caddr_t) NULL},
151     {(char *) "-rot_frequency", (char *) ".text3d.rot_frequency", XrmoptionSepArg, (caddr_t) NULL},
152     {(char *) "-ttanimate", (char *) ".text3d.ttanimate", XrmoptionSepArg, (caddr_t) NULL},
153 };
154 
155 static argtype vars[] =
156 {
157     {(void *) & mode_font, (char *) "ttfont", (char *) "TTFont", (char *) DEF_TTFONT, t_String},
158     {(void *) & nosplit, (char *) "no_split", (char *) "NoSplit", (char *) DEF_NOSPLIT, t_Bool},
159     {(void *) & extrusion, (char *) "extrusion", (char *) "Extrusion", (char *) DEF_EXTRUSION, t_Float},
160     {(void *) & rampl, (char *) "rot_amplitude", (char *) "RotationAmplitude", (char *) DEF_ROTAMPL, t_Float},
161     {(void *) & rfreq, (char *) "rot_frequency", (char *) "RotationFrequency", (char *) DEF_ROTFREQ, t_Float},
162     {(void *) & animate, (char *) "ttanimate", (char *) "TTAnimate", (char *) DEF_ANIMATE, t_String},
163 };
164 
165 static OptionStruct desc[] =
166 {
167     {(char *) "-ttfont filename", (char *) "Text3d TrueType font file name"},
168     {(char *) "-/+no_split", (char *) "Text3d words splitting off/on"},
169     {(char *) "-extrusion float", (char *) "Text3d extrusion length"},
170     {(char *) "-rot_amplitude float", (char *) "Text3d rotation amplitude"},
171     {(char *) "-rot_frequency float", (char *) "Text3d rotation frequency"},
172     {(char *) "-ttanimate anim_name", (char *) "Text3d animation function"},
173 };
174 
175 ENTRYPOINT ModeSpecOpt text3d_opts =
176 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
177 
178 #ifdef USE_MODULES
179 ModStruct text3d_description =
180 {"text3d", "init_text3d", "draw_text3d", "release_text3d",
181  "refresh_text3d", "change_text3d", (char *) NULL, &text3d_opts,
182  100000, 10, 1, 1, 64, 1.0, "",
183  "Shows 3D text", 0, NULL};
184 #endif
185 
186 static text3dstruct *text3d = (text3dstruct *) NULL;
187 
188 const double angle_speed = 2.5 / 180.0 * M_PI;
189 
190 extern "C" {
191 typedef void (*t3dAnimProc) (text3dstruct * tp);
192 }
193 
194 #ifdef __cplusplus
195 extern "C" {
196 #endif
197 
198 static void t3d_anim_fullrandom(text3dstruct * tp);
199 static void t3d_anim_default(text3dstruct * tp);
200 static void t3d_anim_default2(text3dstruct * tp);
201 static void t3d_anim_none(text3dstruct * tp);
202 static void t3d_anim_crazy(text3dstruct * tp);
203 static void t3d_anim_updown(text3dstruct * tp);
204 static void t3d_anim_extrusion(text3dstruct * tp);
205 static void t3d_anim_rotatexy(text3dstruct * tp);
206 static void t3d_anim_rotateyz(text3dstruct * tp);
207 static void t3d_anim_frequency(text3dstruct * tp);
208 static void t3d_anim_amplitude(text3dstruct * tp);
209 static void t3d_anim_wander(text3dstruct * tp);
210 static char *fontfile = (char *) NULL;
211 
212 #ifdef __cplusplus
213 }
214 #endif
215 
216 static t3dAnimProc anim_array[] =
217 {
218 	t3d_anim_fullrandom,
219 	t3d_anim_default,
220 	t3d_anim_default2,
221 	t3d_anim_none,
222 	t3d_anim_crazy,
223 	t3d_anim_updown,
224 	t3d_anim_extrusion,
225 	t3d_anim_rotatexy,
226 	t3d_anim_rotateyz,
227 	t3d_anim_frequency,
228 	t3d_anim_amplitude,
229 	t3d_anim_wander,
230 };
231 
232 static char * anim_names[] =
233 {
234 	(char *) "Random",
235 	(char *) "FullRandom",
236 	(char *) "Default",
237 	(char *) "Default2",
238 	(char *) "None",
239 	(char *) "Crazy",
240 	(char *) "UpDown",
241 	(char *) "Extrude",
242 	(char *) "RotateXY",
243 	(char *) "RotateYZ",
244 	(char *) "Frequency",	/* needs -rot_frequency /= 0.0 */
245 	(char *) "Amplitude",	/* and   -rot_amplitude /= 0.0 */
246 	(char *) "Wander",
247 	(char *) NULL
248 };
249 
250 static int anims=sizeof anim_array / sizeof anim_array[0] ;
251 
252 /*
253  *-----------------------------------------------------------------------------
254  *-----------------------------------------------------------------------------
255  *    Mode funcs.
256  *-----------------------------------------------------------------------------
257  *-----------------------------------------------------------------------------
258  */
259 
260 /*
261  *-----------------------------------------------------------------------------
262  *    Utils.
263  *-----------------------------------------------------------------------------
264  */
265 
266 /* Select light stuff with the following defines */
267 #define TWO_LIGHTS
268 #define ALL_STUFF
269 //#define DIFFUSE_COLOR
270 
271 #ifdef DIFFUSE_COLOR
272 static void
hsv_to_rgb(double h,double s,double v,double * r,double * g,double * b)273  hsv_to_rgb(double h, double s, double v,
274 	    double *r, double *g, double *b)
275 {
276     double xh = fmod(h * 360., 360) / 60.0,
277 	   i = floor(xh),
278 	   f = xh - i,
279 	   p1 = v * (1 - s),
280 	   p2 = v * (1 - (s * f)),
281 	   p3 = v * (1 - (s * (1 - f)));
282 
283     switch ((int) i)
284     {
285     case 0:
286 	*r = v;
287 	*g = p3;
288 	*b = p1;
289 	break;
290     case 1:
291 	*r = p2;
292 	*g = v;
293 	*b = p1;
294 	break;
295     case 2:
296 	*r = p1;
297 	*g = v;
298 	*b = p3;
299 	break;
300     case 3:
301 	*r = p1;
302 	*g = p2;
303 	*b = v;
304 	break;
305     case 4:
306 	*r = p3;
307 	*g = p1;
308 	*b = v;
309 	break;
310     case 5:
311 	*r = v;
312 	*g = p1;
313 	*b = p2;
314 	break;
315     }
316 }
317 #endif				/* DIFFUSE_COLOR */
318 
319 /*-------------------------------------------------------------*/
spheric_camera(text3dstruct * tp,float center_x,float center_y,float center_z,float phi,float theta,float radius)320 static void spheric_camera(text3dstruct * tp,
321 			   float center_x, float center_y, float center_z,
322 			   float phi, float theta, float radius)
323 {
324     float x = center_x + cos(phi) * cos(theta) * radius;
325     float y = center_y + sin(phi) * cos(theta) * radius;
326     float z = center_z + sin(theta) * radius;
327 
328     float vx = -cos(phi) * sin(theta) * radius;
329     float vy = -sin(phi) * sin(theta) * radius;
330     float vz = cos(theta) * radius;
331 
332     glViewport(0, 0, tp->WinW, tp->WinH);
333     glMatrixMode(GL_PROJECTION);
334     glLoadIdentity();
335     gluPerspective(60, GLfloat(tp->WinW) / GLfloat(tp->WinH), 10, 10000);
336 
337     glMatrixMode(GL_MODELVIEW);
338     glLoadIdentity();
339     gluLookAt(x, y, z, center_x, center_y, center_z, vx, vy, vz);
340 }
341 
342 /*-------------------------------------------------------------*/
343 class GLTTGlyphTriangles:public GLTTGlyphTriangulator
344 {
345 public:
346     struct Triangle
347     {
348 	FTGlyphVectorizer::POINT * p1;
349 	FTGlyphVectorizer::POINT * p2;
350 	FTGlyphVectorizer::POINT * p3;
351     };
352 
353     Triangle *triangles;
354     int nTriangles;
355 
356     GLTTboolean count_them;
357 
GLTTGlyphTriangles(FTGlyphVectorizer * vectorizer)358     GLTTGlyphTriangles(FTGlyphVectorizer * vectorizer):
359 	GLTTGlyphTriangulator(vectorizer)
360 	{
361 	    triangles  = 0;
362 	    nTriangles = 0;
363 	    count_them = GLTT_TRUE;
364 	}
~GLTTGlyphTriangles()365     virtual ~GLTTGlyphTriangles()
366     {
367 	delete[]triangles;
368 	triangles = 0;
369     }
alloc()370     void alloc()
371     {
372 	delete triangles;
373 	triangles = new Triangle[nTriangles + 1];
374     }
triangle(FTGlyphVectorizer::POINT * p1,FTGlyphVectorizer::POINT * p2,FTGlyphVectorizer::POINT * p3)375     virtual void triangle(FTGlyphVectorizer::POINT * p1,
376 			  FTGlyphVectorizer::POINT * p2,
377 			  FTGlyphVectorizer::POINT * p3)
378     {
379 	if (count_them)
380 	{
381 	    ++nTriangles;
382 	    return;
383 	}
384 	triangles[nTriangles].p1 = p1;
385 
386 	triangles[nTriangles].p2 = p2;
387 	triangles[nTriangles].p3 = p3;
388 	++nTriangles;
389     }
390 };
391 
392 /*
393  *-----------------------------------------------------------------------------
394  *    Animation functions.
395  *-----------------------------------------------------------------------------
396  */
397 
398 /* defines for min/max/evolution factor of animations */
399 #define FAC_CAMERA	1.15
400 #define MAX_CAMERA	5.00
401 
402 #define MIN_EXTRUSION	 25.0
403 #define MAX_EXTRUSION	305.0
404 #define FAC_EXTRUSION	  5.0
405 
406 #define FAC_FREQ	0.15
407 #define FAC_AMPL	1.5
408 
409 #define FAC_RAND	15
410 
411 #ifdef __cplusplus
412 extern "C" {
413 #endif
414 
415 /*-------------------------------------------------------------*/
416 static void
t3d_anim_default(text3dstruct * tp)417  t3d_anim_default(text3dstruct * tp)
418 {
419     tp->phi   += tp->direction * angle_speed;
420     tp->theta += tp->direction * angle_speed;
421 }
422 
423 /*-------------------------------------------------------------*/
424 static void
t3d_anim_default2(text3dstruct * tp)425  t3d_anim_default2(text3dstruct * tp)
426 {
427     tp->phi   += tp->direction * angle_speed;
428     tp->theta += tp->direction * angle_speed * 2.0 ;
429 }
430 
431 /*-------------------------------------------------------------*/
432 static void
t3d_anim_none(text3dstruct * tp)433  t3d_anim_none(text3dstruct * tp)
434 {
435 }
436 
437 /*-------------------------------------------------------------*/
438 static void
t3d_anim_crazy(text3dstruct * tp)439  t3d_anim_crazy(text3dstruct * tp)
440 {
441     int key = NRAND(32);
442 
443     switch (key)
444     {
445     case 0:
446     case 1:
447     case 2:
448     case 3:
449 	tp->theta += angle_speed;
450 	break;
451     case 4:
452     case 5:
453     case 6:
454     case 7:
455 	tp->theta -= angle_speed;
456 	break;
457     case 8:
458     case 9:
459     case 10:
460     case 11:
461 	tp->phi -= angle_speed;
462 	break;
463     case 12:
464     case 13:
465     case 14:
466     case 15:
467 	tp->phi += angle_speed;
468 	break;
469     case 16:
470     case 17:
471 	if (tp->camera_dist / FAC_CAMERA > tp->ref_camera_dist)
472 	    tp->camera_dist /= FAC_CAMERA;
473 	break;
474     case 18:
475     case 19:
476 	if (tp->camera_dist * FAC_CAMERA < (tp->ref_camera_dist * MAX_CAMERA))
477 	    tp->camera_dist *= FAC_CAMERA;
478 	break;
479     case 20:
480 	if ((tp->extrusion - FAC_EXTRUSION) > MIN_EXTRUSION)
481 	    tp->extrusion -= FAC_EXTRUSION;
482 	break;
483     case 21:
484 	if ((tp->extrusion + FAC_EXTRUSION) < MAX_EXTRUSION)
485 	    tp->extrusion += FAC_EXTRUSION;
486 	break;
487     case 22:
488     case 23:
489 	tp->rampl /= FAC_AMPL;
490 	break;
491     case 24:
492     case 25:
493 	tp->rampl *= FAC_AMPL;
494 	break;
495     case 26:
496     case 27:
497 	tp->rfreq *= FAC_FREQ;
498 	break;
499     case 28:
500     case 29:
501 	tp->rfreq /= FAC_FREQ;
502 	break;
503     }
504 }
505 
506 static void
t3d_anim_updown(text3dstruct * tp)507  t3d_anim_updown(text3dstruct * tp)
508 {
509     if (tp->direction > 0)
510     {
511         if (tp->camera_dist / FAC_CAMERA > tp->ref_camera_dist)
512             tp->camera_dist /= FAC_CAMERA;
513         else
514 	    tp->direction *=-1;
515     }
516     else
517     {
518         if (tp->camera_dist * FAC_CAMERA < (tp->ref_camera_dist * MAX_CAMERA))
519             tp->camera_dist *= FAC_CAMERA;
520         else
521 	    tp->direction *=-1;
522     }
523 }
524 
525 static void
t3d_anim_extrusion(text3dstruct * tp)526  t3d_anim_extrusion(text3dstruct * tp)
527 {
528     if (tp->direction > 0)
529     {
530         if ((tp->extrusion - FAC_EXTRUSION) > MIN_EXTRUSION)
531             tp->extrusion -= FAC_EXTRUSION;
532         else
533             tp->direction *=-1;
534     }
535     else
536     {
537         if ((tp->extrusion + FAC_EXTRUSION) < MAX_EXTRUSION)
538             tp->extrusion += FAC_EXTRUSION;
539         else
540             tp->direction *=-1;
541     }
542 }
543 
544 static void
t3d_anim_rotatexy(text3dstruct * tp)545  t3d_anim_rotatexy(text3dstruct * tp)
546 {
547     tp->phi += tp->direction * angle_speed;
548 }
549 
550 static void
t3d_anim_rotateyz(text3dstruct * tp)551  t3d_anim_rotateyz(text3dstruct * tp)
552 {
553     tp->theta += tp->direction * angle_speed;
554 }
555 
556 static void
t3d_anim_frequency(text3dstruct * tp)557  t3d_anim_frequency(text3dstruct * tp)
558 {
559     /* Better visual if freq is a small value < 0.05 */
560     if (tp->direction > 0)
561     {
562         tp->rfreq /= FAC_FREQ;
563     }
564     else
565     {
566         tp->rfreq *= FAC_FREQ;
567     }
568 
569     if (NRAND(100) < FAC_RAND )
570             tp->direction *=-1;
571 }
572 
573 static void
t3d_anim_amplitude(text3dstruct * tp)574  t3d_anim_amplitude(text3dstruct * tp)
575 {
576     if (tp->direction > 0)
577     {
578         tp->rampl /= FAC_AMPL;
579     }
580     else
581     {
582         tp->rampl *= FAC_AMPL;
583     }
584 
585     if (NRAND(100) < FAC_RAND )
586             tp->direction *=-1;
587 }
588 
589 static void
t3d_anim_fullrandom(text3dstruct * tp)590  t3d_anim_fullrandom(text3dstruct * tp)
591 {
592     static int animation=NRAND(anims);
593     if (NRAND(100) < FAC_RAND/5 || animation <= 1)
594     {
595     	/* change animation */
596       	animation = NRAND(anims);
597     }
598     anim_array[animation](tp);
599 }
600 
601 static void
t3d_anim_wander(text3dstruct * tp)602  t3d_anim_wander(text3dstruct * tp)
603 {
604         static int frame = 0;
605 	GLfloat x, y, z;
606 #define SINOID(SCALE,SIZE) \
607  ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
608 
609 	x = SINOID(0.061, angle_speed);
610 	y = SINOID(0.083, angle_speed * 2.0);
611 	z = SINOID(0.075, MAX_CAMERA * 10.0);
612 	frame++;
613     	tp->phi   += tp->direction * x;
614     	tp->theta += tp->direction * y;
615         tp->camera_dist += tp->direction * z;
616 }
617 
618 #ifdef __cplusplus
619 }
620 #endif
621 
622 
623 /*-------------------------------------------------------------*/
624 /*-------------------------------------------------------------*/
625 /*
626  *-----------------------------------------------------------------------------
627  *    "Main" local funcs.
628  *-----------------------------------------------------------------------------
629  */
630 
631 void
reshape_text3d(ModeInfo * mi,int width,int height)632  reshape_text3d(ModeInfo * mi, int width, int height)
633 {
634     text3dstruct *tp = &text3d[MI_SCREEN(mi)];
635 
636     glViewport(0, 0, tp->WinW = (GLint) width, tp->WinH = (GLint) height);
637 
638     glMatrixMode(GL_PROJECTION);
639     glLoadIdentity();
640     gluPerspective(60.0, (GLdouble) width / (GLdouble) height, 10, 10000);
641     glMatrixMode(GL_MODELVIEW);
642 }
643 
644 /*-------------------------------------------------------------*/
645 static void
Animate(text3dstruct * tp)646  Animate(text3dstruct * tp)
647 {
648     anim_array[tp->animation](tp);
649 }
650 
651 /*-------------------------------------------------------------*/
652 static void
Draw(text3dstruct * tp,Display * display,Window window)653  Draw(text3dstruct * tp,
654       Display * display,
655       Window window)
656 {
657     int text_length;
658     char *c_text;
659 
660 #ifndef STANDALONE
661     if (!nosplit)
662     {
663 	text_length = index_dir(tp->words, (char *) " ");
664 	if (text_length == 0)
665 	    text_length = strlen(tp->words);
666 	if ((c_text = (char *) malloc(text_length)) != NULL)
667 	strncpy(c_text, tp->words, text_length);
668     }
669     else
670 #endif
671     {
672 	c_text = tp->words_start;
673 	text_length = strlen(tp->words_start);
674     }
675     GLTTFont font(tp->face);
676 
677     if (!font.create(DEF_FONTSIZE))
678 	return;
679 
680     FTGlyphVectorizer *vec = new FTGlyphVectorizer[text_length];
681     GLTTGlyphTriangles **tri = new GLTTGlyphTriangles *[text_length];
682 
683     int i;
684 
685     for (i = 0; i < text_length; ++i)
686 	tri[i] = new GLTTGlyphTriangles(vec + i);
687 
688     if (tp->camera_dist == 0.0)
689 	/* PURIFY reports an Array Bounds Read on the next line */
690 	tp->ref_camera_dist = tp->camera_dist = font.getWidth(c_text) * 0.75;
691     double min_y = 1e20;
692     double max_y = -1e20;
693     double size_x = 0.0;
694 
695     for (i = 0; i < text_length; ++i)
696     {
697 	int ch = (unsigned char) c_text[i];
698 
699 #if ((XMESA_MAJOR_VERSION > 3 ) || (( XMESA_MAJOR_VERSION == 3 ) && (XMESA_MINOR_VERSION > 0 )))
700 	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
701 	glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
702 	glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
703 	glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
704 	glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
705 	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
706 #endif
707 
708 	FTGlyph *g = font.getFont()->getGlyph(ch);
709 
710 	if (g == 0)
711 	    continue;
712 	FTGlyphVectorizer & v = vec[i];
713 	v.setPrecision(10.0);
714 	/* PURIFY reports an Array Bounds Write on the next line */
715 	if (!v.init(g))
716 	    continue;
717 
718 	size_x += v.getAdvance();
719 
720 	if (!v.vectorize())
721 	    continue;
722 
723 	for (int c = 0; c < v.getNContours(); ++c)
724 	{
725 	    FTGlyphVectorizer::Contour * contour = v.getContour(c);
726 	    if (contour == 0)
727 		continue;
728 	    for (int j = 0; j < contour->nPoints; ++j)
729 	    {
730 		FTGlyphVectorizer::POINT * point = contour->points + j;
731 		if (point->y < min_y)
732 		    min_y = point->y;
733 		if (point->y > max_y)
734 		    max_y = point->y;
735 		point->data = (void *) new double[6];
736 	    }
737 	}
738 	GLTTGlyphTriangles *t = tri[i];
739 
740 	if (!t->init(g))
741 	    continue;
742 
743 	t->count_them = GLTT_TRUE;
744 	t->nTriangles = 0;
745 	t->triangulate();
746 
747 	t->count_them = GLTT_FALSE;
748 	t->alloc();
749 	t->nTriangles = 0;
750 	t->triangulate();
751     }
752 
753 #ifndef STANDALONE
754     if (!nosplit)
755 	free(c_text);
756 #endif
757     if (size_x == 0.0)
758     {
759 	(void) fprintf(stderr, "Please give something to draw !\n");
760         delete[]vec;
761 	return;
762     }
763 
764     double y_delta = (min_y + max_y) / 2. + min_y + 50;
765 
766     for (i = 0; i < text_length; ++i)
767     {
768 
769 #if ((XMESA_MAJOR_VERSION > 3 ) || (( XMESA_MAJOR_VERSION == 3 ) && (XMESA_MINOR_VERSION > 0 )))
770 	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
771 	glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
772 	glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
773 	glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
774 	glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
775 	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
776 #endif
777 
778 	FTGlyphVectorizer & v = vec[i];
779 
780 	for (int c = 0; c < v.getNContours(); ++c)
781 	{
782 	    FTGlyphVectorizer::Contour * contour = v.getContour(c);
783 	    if (contour == 0)
784 		continue;
785 	    for (int j = 0; j < contour->nPoints; ++j)
786 	    {
787 		FTGlyphVectorizer::POINT * point = contour->points + j;
788 		point->y -= y_delta;
789 	    }
790 	}
791     }
792 
793 
794 #ifdef ALL_STUFF
795     float front_emission[4] = {0.1, 0.1, 0.1, 0};
796     float front_ambient[4]  = {0.2, 0.2, 0.2, 0};
797     float front_diffuse[4]  = {0.95, 0.95, 0.8, 0};
798     float back_diffuse[4]   = {0.75, 0.75, 0.95, 0};
799     float front_specular[4] = {0.6, 0.6, 0.6, 0};
800 
801     glMaterialfv(GL_FRONT, GL_EMISSION, front_emission);
802     glMaterialfv(GL_FRONT, GL_AMBIENT, front_ambient);
803     glMaterialfv(GL_FRONT, GL_DIFFUSE, front_diffuse);
804     glMaterialfv(GL_FRONT, GL_SPECULAR, front_specular);
805     glMaterialf(GL_FRONT, GL_SHININESS, 32.0);
806 #ifdef TWO_LIGHTS
807     float light1_ambient[4]  = {0.3, 0.3, 0.3, 1};
808     float light1_diffuse[4]  = {0.9, 0.9, 0.9, 1};	/* A "white" light */
809     float light1_specular[4] = {0.7, 0.7, 0.7, 1};
810     float light1_position[4] = {-1, 1, 1, 0};
811 
812     glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient);
813     glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);
814     glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular);
815     glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
816     glEnable(GL_LIGHT1);
817 
818     float light2_ambient[4]  = {0.1, 0.1, 0.1, 1};
819     float light2_diffuse[4]  = {0.85, 0.3, 0.3, 1};	/* A "red" light */
820     float light2_specular[4] = {0.6, 0.6, 0.6, 1};
821     float light2_position[4] = {1, -1, -1, 0};
822 
823     glLightfv(GL_LIGHT2, GL_AMBIENT, light2_ambient);
824     glLightfv(GL_LIGHT2, GL_DIFFUSE, light2_diffuse);
825     glLightfv(GL_LIGHT2, GL_SPECULAR, light2_specular);
826     glLightfv(GL_LIGHT2, GL_POSITION, light2_position);
827     glEnable(GL_LIGHT2);
828 #else
829     GLfloat pos[4] = {-1.0, 1.0, 1.0, 0.0};
830 
831     glLightfv(GL_LIGHT0, GL_POSITION, pos);
832     glEnable(GL_LIGHT0);
833 #endif				/* TWO_LIGHTS */
834 
835     float back_color[4] = {0.2, 0.2, 0.6, 0};
836 
837     glMaterialfv(GL_BACK, GL_DIFFUSE, back_color);
838     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
839     glCullFace(GL_BACK);
840     glFrontFace(GL_CCW);
841     glEnable(GL_CULL_FACE);
842 
843     glColorMaterial(GL_FRONT, GL_DIFFUSE);
844     glEnable(GL_COLOR_MATERIAL);
845 #endif				/* ALL_STUFF */
846 
847     spheric_camera(tp, tp->center_x,
848 		   tp->center_y + size_x / 2.,
849 		   0,
850 		   tp->phi, tp->theta + M_PI / 2, tp->camera_dist);
851     glClearColor(0, 0, 0, 0);
852     glEnable(GL_DEPTH_TEST);
853     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
854 
855     glMatrixMode(GL_MODELVIEW);
856     glPushMatrix();
857 
858 #ifdef ALL_STUFF
859     glEnable(GL_LIGHTING);
860     glShadeModel(GL_SMOOTH);
861     glEnable(GL_NORMALIZE);
862 #endif
863 
864     double base_x = 0.0;
865 
866     for (i = 0; i < text_length; ++i)
867     {
868 
869 #if ((XMESA_MAJOR_VERSION > 3 ) || (( XMESA_MAJOR_VERSION == 3 ) && (XMESA_MINOR_VERSION > 0 )))
870 	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
871 	glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
872 	glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
873 	glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
874 	glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
875 	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
876 #endif
877 
878 	FTGlyphVectorizer & v = vec[i];
879 
880 	int c;
881 
882 	for (c = 0; c < v.getNContours(); ++c)
883 	{
884 	    FTGlyphVectorizer::Contour * contour = v.getContour(c);
885 	    if (contour == 0)
886 		continue;
887 
888 	    for (int j = 0; j < contour->nPoints; ++j)
889 	    {
890 		FTGlyphVectorizer::POINT * point = contour->points + j;
891 		double cx = -point->y;
892 		double cy = base_x + point->x;
893 		double phi = sin(cy * tp->rfreq) * tp->rampl * M_PI / 2.;
894 		double rcx = cx * cos(phi);
895 		double rcz = cx * sin(phi);
896 
897 		double *p = (double *) point->data;
898 		double *n = p + 3;
899 
900 		p[0] = rcx;
901 		p[1] = cy;
902 		p[2] = rcz;
903 
904 		n[0] = -sin(phi);
905 		n[1] = 0.;
906 		n[2] = cos(phi);
907 	    }
908 	}
909 
910 	GLTTGlyphTriangles::Triangle * triangles = tri[i]->triangles;
911 	int nTriangles = tri[i]->nTriangles;
912 
913 	glBegin(GL_TRIANGLES);
914 
915 	for (int j = 0; j < nTriangles; ++j)
916 	{
917 	    GLTTGlyphTriangles::Triangle & t = triangles[j];
918 
919 	    double *p1 = ((double *) t.p1->data);
920 	    double *p2 = ((double *) t.p2->data);
921 	    double *p3 = ((double *) t.p3->data);
922 	    double *n1 = p1 + 3;
923 	    double *n2 = p2 + 3;
924 	    double *n3 = p3 + 3;
925 
926 #ifdef ALL_STUFF
927 	    glColor4fv(front_diffuse);
928 #endif
929 
930 	    glNormal3dv(n1);
931 	    glVertex3dv(p1);
932 	    glNormal3dv(n2);
933 	    glVertex3dv(p2);
934 	    glNormal3dv(n3);
935 	    glVertex3dv(p3);
936 
937 #ifdef ALL_STUFF
938 	    glColor4fv(back_diffuse);
939 #endif
940 
941 	    glNormal3d(-n3[0], 0., -n3[2]);
942 	    glVertex3d(p3[0] - n3[0] * tp->extrusion,
943 		       p3[1],
944 		       p3[2] - n3[2] * tp->extrusion);
945 	    glNormal3d(-n2[0], 0., -n2[2]);
946 	    glVertex3d(p2[0] - n2[0] * tp->extrusion,
947 		       p2[1],
948 		       p2[2] - n2[2] * tp->extrusion);
949 	    glNormal3d(-n1[0], 0., -n1[2]);
950 	    glVertex3d(p1[0] - n1[0] * tp->extrusion,
951 		       p1[1],
952 		       p1[2] - n1[2] * tp->extrusion);
953 	}
954 	glEnd();
955 
956 	for (c = 0; c < v.getNContours(); ++c)
957 	{
958 	    FTGlyphVectorizer::Contour * contour = v.getContour(c);
959 	    if (contour == 0)
960 		continue;
961 	    glBegin(GL_QUAD_STRIP);
962 	    for (int j = 0; j <= contour->nPoints; ++j)
963 	    {
964 		int j1 = (j < contour->nPoints) ? j : 0;
965 		int j0 = (j1 == 0) ? (contour->nPoints - 1) : (j1 - 1);
966 
967 		FTGlyphVectorizer::POINT * point0 = contour->points + j0;
968 		FTGlyphVectorizer::POINT * point1 = contour->points + j1;
969 		double *p0 = (double *) point0->data;
970 		double *p1 = (double *) point1->data;
971 		double *e = p0 + 3;
972 		double vx = p1[0] - p0[0];
973 		double vy = p1[1] - p0[1];
974 		double vz = p1[2] - p0[2];
975 		double nx = -vy * e[2];
976 		double ny = e[2] * vx - vz * e[0];
977 		double nz = e[0] * vy;
978 #ifdef DIFFUSE_COLOR
979 		double u = double ((j * 2) % contour->nPoints) / double (contour->nPoints);
980 		double r, g, b;
981 
982 		hsv_to_rgb(u, 0.7, 0.7, &r, &g, &b);
983 		glColor4f(r, g, b, 1);	// diffuse color of material
984 #else
985 		GLfloat blue[4] = {0.35, 0.35, 1.0, 1.0};
986 		glColor4fv(blue);
987 #endif
988 
989 		glNormal3f(nx, ny, nz);
990 		glVertex3f(p0[0] - e[0] * tp->extrusion,
991 			   p0[1],
992 			   p0[2] - e[2] * tp->extrusion);
993 		glNormal3f(nx, ny, nz);
994 		glVertex3f(p0[0], p0[1], p0[2]);
995 	    }
996 	    glEnd();
997 	}
998 
999 	base_x += v.getAdvance();
1000     }
1001 
1002 #ifdef ALL_STUFF
1003     glDisable(GL_NORMALIZE);
1004     glDisable(GL_LIGHTING);
1005     glShadeModel(GL_FLAT);
1006 #endif
1007     glPopMatrix();
1008 
1009     glXSwapBuffers(display, window);
1010 
1011     for (i = 0; i < text_length; ++i)
1012     {
1013 
1014 #if ((XMESA_MAJOR_VERSION > 3 ) || (( XMESA_MAJOR_VERSION == 3 ) && (XMESA_MINOR_VERSION > 0 )))
1015 	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1016 	glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
1017 	glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
1018 	glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
1019 	glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
1020 	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1021 #endif
1022 
1023 	delete tri[i];
1024 
1025 	FTGlyphVectorizer & v = vec[i];
1026 	for (int c = 0; c < v.getNContours(); ++c)
1027 	{
1028 	    FTGlyphVectorizer::Contour * contour = v.getContour(c);
1029 	    if (contour == 0)
1030 		continue;
1031 	    for (int j = 0; j < contour->nPoints; ++j)
1032 	    {
1033 		FTGlyphVectorizer::POINT * point = contour->points + j;
1034 		delete[](double *) point->data;
1035 		point->data = 0;
1036 	    }
1037 	}
1038     }
1039 
1040     delete[]tri;
1041     delete[]vec;
1042 }
1043 
1044 /*
1045  *-----------------------------------------------------------------------------
1046  *-----------------------------------------------------------------------------
1047  *    Xlock hooks.
1048  *-----------------------------------------------------------------------------
1049  *-----------------------------------------------------------------------------
1050  */
1051 
1052 /*
1053  *-----------------------------------------------------------------------------
1054  *    The display is being taken away from us.  Free up malloc'ed
1055  *      memory and X resources that we've alloc'ed.  Only called
1056  *      once, we must zap everything for every screen.
1057  *-----------------------------------------------------------------------------
1058  */
1059 
1060 ENTRYPOINT void
release_text3d(ModeInfo * mi)1061  release_text3d(ModeInfo * mi)
1062 {
1063     if (text3d != NULL)
1064     {
1065 	for (int screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1066 	{
1067 	    text3dstruct *tp = &text3d[screen];
1068 
1069 	    if (tp->face)
1070 		delete tp->face;
1071 	}
1072 	free(text3d);
1073 	text3d = (text3dstruct *) NULL;
1074     }
1075     FreeAllGL(mi);
1076 }
1077 
1078 /*
1079  *-----------------------------------------------------------------------------
1080  *    Initialize text3d.  Called each time the window changes.
1081  *-----------------------------------------------------------------------------
1082  */
1083 
1084 ENTRYPOINT void
init_text3d(ModeInfo * mi)1085  init_text3d(ModeInfo * mi)
1086 {
1087     int i;
1088     text3dstruct *tp;
1089 
1090 
1091     if (text3d == NULL)
1092     {
1093 	/*MI_INIT(mi, text3d);*/
1094 	if ((text3d = (text3dstruct *) calloc(MI_NUM_SCREENS(mi),
1095 					      sizeof(text3dstruct))) == NULL)
1096 	    return;
1097     }
1098     tp = &text3d[MI_SCREEN(mi)];
1099     tp->wire = MI_IS_WIREFRAME(mi);
1100     tp->extrusion = extrusion;
1101     tp->rampl = rampl;
1102     tp->rfreq = rfreq;
1103     tp->camera_dist = 0.0;
1104 
1105     /* Get animation function */
1106     tp->animation = 0;	/* Not found equals "Random" */
1107     tp->direction = (LRAND() & 1) ? 1 : -1; /* random direction */
1108     tp->rampl *= tp->direction;
1109     tp->rfreq *= tp->direction;
1110     for(i=0;anim_names[i] != NULL;i++)
1111     {
1112         if ( !strcmp( anim_names[i], animate ) )
1113         {
1114     		tp->animation = i;
1115 		break;
1116         }
1117     }
1118     if (!tp->animation)
1119     {
1120 	/* Random !!! */
1121     	tp->animation = NRAND(anims);
1122     }
1123     else
1124     {
1125         tp->animation --;
1126     }
1127 
1128     if (MI_IS_DEBUG(mi))
1129     {
1130 	(void) fprintf(stderr,
1131 		   "%s:\n\ttp->animation[%d]=%s\n",
1132 		   MI_NAME(mi), tp->animation, anim_names[tp->animation+1]);
1133     }
1134 
1135     tp->counter = 0;
1136 #ifdef STANDALONE
1137 	if ((fontfile = (char *) malloc(strlen(mode_font) + 10)) != NULL)
1138 		sprintf(fontfile, "%s%s", mode_font, "arial.ttf");
1139 #else
1140 	fontfile = getModeFont(mode_font);
1141 #endif
1142 	if (!fontfile) {
1143 		MI_CLEARWINDOW(mi);
1144 		release_text3d(mi);
1145 		return;
1146     }
1147     tp->face = new FTFace;
1148     if (!tp->face || !tp->face->open(fontfile)) {
1149 		(void) fprintf(stderr, "%s: unable to open True Type font %s!\n", MI_NAME(mi), fontfile);
1150 		MI_CLEARWINDOW(mi);
1151 		release_text3d(mi);
1152 		return;
1153     }
1154     if (MI_IS_DEBUG(mi)) {
1155 		(void) fprintf(stderr,
1156 			"%s:\n\tfontfile=%s .\n", MI_NAME(mi), fontfile);
1157     }
1158 
1159     /* Do not free fontfile getModeFont handles potential leak */
1160     if ((tp->glx_context = init_GL(mi)) != NULL)
1161     {
1162 
1163 	reshape_text3d(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1164 	/*glDrawBuffer(GL_BACK); */
1165 	if (MI_IS_DEBUG(mi))
1166 	{
1167 	    (void) fprintf(stderr,
1168 			   "%s:\n\tcamera_dist=%.1f\n\ttheta=%.1f\n\tphi=%.1f\n\textrusion=%.1f\n\trampl=%.1f.\n\trfreq=%.1f\n\tdirection=%d\n",
1169 			   MI_NAME(mi), tp->camera_dist, tp->theta, tp->phi,
1170 			   tp->extrusion, tp->rampl,tp->rfreq,tp->direction);
1171 	}
1172 /*
1173 	glXSwapBuffers(display, window);
1174 */
1175 
1176     }
1177     else
1178     {
1179 	MI_CLEARWINDOW(mi);
1180     }
1181 
1182     /* Initialize displayed string */
1183     tp->words_start = tp->words =
1184 	getWords(MI_SCREEN(mi), MI_NUM_SCREENS(mi));
1185     if (MI_IS_DEBUG(mi))
1186     {
1187 	(void) fprintf(stderr,
1188 		   "%s words:\n%s\n",
1189 		   MI_NAME(mi), tp->words);
1190     }
1191 
1192 }
1193 
1194 /*
1195  *-----------------------------------------------------------------------------
1196  *    Called by the mainline code periodically to update the display.
1197  *-----------------------------------------------------------------------------
1198  */
1199 ENTRYPOINT void
draw_text3d(ModeInfo * mi)1200  draw_text3d(ModeInfo * mi)
1201 {
1202     Display *display = MI_DISPLAY(mi);
1203     Window window = MI_WINDOW(mi);
1204     text3dstruct *tp;
1205 
1206     if (text3d == NULL) {
1207 	  return;
1208 	}
1209     tp = &text3d[MI_SCREEN(mi)];
1210 
1211     MI_IS_DRAWN(mi) = True;
1212     if (!tp->glx_context)
1213 	return;
1214     tp->counter = tp->counter + 1;
1215 #ifndef STANDALONE
1216     if (tp->counter > (MI_CYCLES(mi) & !nosplit))
1217     {
1218 	int text_length = index_dir(tp->words, (char *) " ");
1219 
1220 	/* Every now and then, get a new word */
1221 	if (text_length == 0)
1222 	    text_length = strlen(tp->words);
1223 	tp->counter = 0;
1224 	tp->words += text_length;
1225 	text_length = strlen(tp->words);
1226 	if (text_length == 0)
1227 	{
1228 	    tp->words_start = tp->words =
1229 		getWords(MI_SCREEN(mi), MI_NUM_SCREENS(mi));
1230 	}
1231     }
1232 #endif
1233     glDrawBuffer(GL_BACK);
1234 #ifdef WIN32
1235     wglMakeCurrent(hdc, tp->glx_context);
1236 #else
1237     glXMakeCurrent(display, window, *(tp->glx_context));
1238 #endif
1239     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1240 
1241     Draw(tp, display, window);
1242     Animate(tp);
1243     if (MI_IS_DEBUG(mi))
1244     {
1245 	(void) fprintf(stderr,
1246 		       "%s:\n\tcamera_dist=%.1f\n\ttheta=%.1f\n\tphi=%.1f\n\textrusion=%.1f\n\trampl=%.1f\n\trfreq=%.1f\n\tdirection=%d\n",
1247 		       MI_NAME(mi), tp->camera_dist, tp->theta, tp->phi,
1248 		       tp->extrusion, tp->rampl,tp->rfreq,tp->direction);
1249     }
1250 }
1251 
1252 #ifndef STANDALONE
1253 ENTRYPOINT void
refresh_text3d(ModeInfo * mi)1254  refresh_text3d(ModeInfo * mi)
1255 {
1256     /* Do nothing, it will refresh by itself :) */
1257 }
1258 
1259 ENTRYPOINT void
change_text3d(ModeInfo * mi)1260  change_text3d(ModeInfo * mi)
1261 {
1262     text3dstruct *tp;
1263 
1264     if (text3d == NULL) {
1265 	  return;
1266 	}
1267     tp = &text3d[MI_SCREEN(mi)];
1268 
1269     if (!tp->glx_context)
1270 	return;
1271     glDrawBuffer(GL_BACK);
1272 #ifdef WIN32
1273     wglMakeCurrent(hdc, tp->glx_context);
1274 #else
1275     glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tp->glx_context));
1276 #endif
1277 }
1278 #endif
1279 
1280 XSCREENSAVER_MODULE ("Text3d", text3d)
1281 
1282 #endif				/* MODE_text3d */
1283