/* -*- Mode: C; tab-width: 4 -*- */
/* text3d --- Shows moving 3D texts */
#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)text3d.cc 2.2 99/10/28 xlockmore";
#endif
/* Copyright (c) E. Lassauge, 1998. */
/*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* This file is provided AS IS with no warranties of any kind. The author
* shall have no liability with respect to the infringement of copyrights,
* trade secrets or any patents by this file or any part thereof. In no
* event will the author be liable for any lost revenue or profits or
* other special, indirect and consequential damages.
*
* This module is based on a demo of the gltt graphics library
* Copyright (C) 1998 Stephane Rehel.
*
* See the gltt Official Site at http://home.worldnet.fr/~rehel/gltt/gltt.html
*
* My e-mail address changed to lassauge@mail.dotcom.fr
* Web site at http://perso.libertysurf.fr/lassauge/
*
* Eric Lassauge (October-28-1999)
*
* REVISION HISTORY:
* 99/10/28: fixes from Jouk "I play with every mode" Jansen.
* Option ttanimate added.
* 99/06/02: patches for initialization errors of GLTT library.
* Thanks to Jouk Jansen and Scott <mcmillan@cambridge.com>.
* text3d updates for fortunes thanks to Jouk Jansen
* <joukj@hrem.stm.tudelft.nl>
* Option no_split added.
*
* 98/08/23: add better handling of "faulty" fontfile and randomize
* fontfile if '-ttfont' value is a directory.
* Minor changes for AIX from Jouk Jansen (joukj@hrem.stm.tudelft.nl).
*
* TODO :
* Need more animation functions. Help welcome !!
* Light problem with some letters (don't know why they "reflect" more):
* is the problem in gltt or Mesa ???
* SPEED !!!!
*
*/
#ifdef STANDALONE
#define PROGCLASS "Text3d"
#define HACK_INIT init_text3d
#define HACK_DRAW draw_text3d
#define text3d_opts xlockmore_opts
#define DEFAULTS "*delay: 100000 \n" \
"*ncolors: 64 \n" \
"*font: \n" \
"*text: \n" \
"*filename: \n" \
"*fortunefile: \n" \
"*program: \n"
extern "C"
{
#include "xlockmore.h" /* from the xscreensaver distribution */
}
#else /* !STANDALONE */
#include "xlock.h" /* from the xlockmore distribution */
#include "vis.h"
#endif /* !STANDALONE */
#ifdef MODE_text3d
#include <gltt/FTEngine.h>
#include <gltt/FTFace.h>
#include <gltt/FTInstance.h>
#include <gltt/FTGlyph.h>
#include <gltt/FTFont.h>
#include <gltt/GLTTOutlineFont.h>
#include <gltt/GLTTFont.h>
#include <gltt/GLTTGlyphPolygonizer.h>
#include <gltt/GLTTGlyphTriangulator.h>
#include "text3d.h"
#include <GL/glu.h>
/* #define USE_BLANK *//* This is really bad when debugging. */
#ifdef USE_BLANK /* if the module cannot create the font struct
* then use blank mode instead
*/
extern "C" { void init_blank(ModeInfo * mi); }
extern "C" { void draw_blank(ModeInfo * mi); }
extern "C" { void release_blank(ModeInfo * mi); }
extern "C" { void refresh_blank(ModeInfo * mi); }
#endif
/* Yes, it's an ugly mix of 'C' and 'C++' functions */
extern "C" { void init_text3d(ModeInfo * mi); }
extern "C" { void draw_text3d(ModeInfo * mi); }
extern "C" { void change_text3d(ModeInfo * mi); }
extern "C" { void release_text3d(ModeInfo * mi); }
extern "C" { void refresh_text3d(ModeInfo * mi); }
/* Manage Option vars */
/* I found this font on NT in c:\WINDOWS\SYSTEM\ARIAL.TTF can also be found
on Windows95 in c:\windows\fonts\arial.ttf */
#define DEF_FONT "arial.ttf"
#define DEF_EXTRUSION "25.0"
#define DEF_ROTAMPL "1.0"
#define DEF_ROTFREQ "0.001"
#define DEF_FONTSIZE 220
#define DEF_NOSPLIT 0
#define DEF_ANIMATE "Default"
static float extrusion;
static float rampl;
static float rfreq;
static char *mode_font;
static int nosplit;
static char *animate;
static XrmOptionDescRec opts[] =
{
{"-ttfont", ".text3d.ttfont", XrmoptionSepArg, (caddr_t) NULL},
{"-extrusion", ".text3d.extrusion", XrmoptionSepArg, (caddr_t) NULL},
{"-rot_amplitude", ".text3d.rot_amplitude", XrmoptionSepArg, (caddr_t) NULL},
{"-rot_frequency", ".text3d.rot_frequency", XrmoptionSepArg, (caddr_t) NULL},
{"-no_split", ".text3d.no_split", XrmoptionNoArg, (caddr_t) "on"},
{"+no_split", ".text3d.no_split", XrmoptionNoArg, (caddr_t) "off"},
{"-ttanimate", ".text3d.ttanimate", XrmoptionSepArg, (caddr_t) NULL},
};
static argtype vars[] =
{
{(caddr_t *) & mode_font, "ttfont", "TTFont", DEF_FONT, t_String},
{(caddr_t *) & extrusion, "extrusion", "Extrusion", DEF_EXTRUSION, t_Float},
{(caddr_t *) & rampl, "rot_amplitude", "RotationAmplitude", DEF_ROTAMPL, t_Float},
{(caddr_t *) & rfreq, "rot_frequency", "RotationFrequency", DEF_ROTFREQ, t_Float},
{(caddr_t *) & nosplit, "no_split", "NoSplit", DEF_NOSPLIT, t_Bool},
{(caddr_t *) & animate, "ttanimate", "TTAnimate", DEF_ANIMATE, t_String},
};
static OptionStruct desc[] =
{
{"-ttfont filename", "Text3d TrueType font file name"},
{"-extrusion float", "Text3d extrusion length"},
{"-rot_amplitude float", "Text3d rotation amplitude"},
{"-rot_frequency float", "Text3d rotation frequency"},
{"-/+no_split", "Text3d words splitting off/on"},
{"-ttanimate anim_name", "Text3d animation function"},
};
ModeSpecOpt text3d_opts =
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
#ifdef USE_MODULES
ModStruct text3d_description =
{"text3d", "init_text3d", "draw_text3d", "release_text3d",
"refresh_text3d", "change_text3d", NULL, &text3d_opts,
100000, 1, 1, 1, 64, 1.0, "",
"Shows 3D text", 0, NULL};
#endif
static text3dstruct *text3d = (text3dstruct *) NULL;
/* Hacks to use $TOP/xlock/util.c and iostuff.c functions */
extern "C" { int index_dir(char *str1, char *substr); }
extern "C" { char *getWords(int screen, int screens); }
extern "C" { FILE *my_fopen(char *filename, char *type); }
#if HAVE_DIRENT_H
#define sel_font sel_image /* "rename" the sel_image function */
extern "C" { void get_dir(char *fullpath, char *dir, char *filename); }
extern "C" { int sel_font(struct dirent *name); }
extern "C" { int scan_dir(const char *directoryname,
struct dirent ***namelist,
int (*select) (struct dirent *),
int (*compare) (const void *, const void *));
}
#endif /* HAVE_DIRENT_H */
const double angle_speed = 2.5 / 180.0 * M_PI;
extern "C" {
typedef void (*t3dAnimProc) (text3dstruct * tp);
}
static void t3d_anim_fullrandom(text3dstruct * tp);
static void t3d_anim_default(text3dstruct * tp);
static void t3d_anim_default2(text3dstruct * tp);
static void t3d_anim_none(text3dstruct * tp);
static void t3d_anim_crazy(text3dstruct * tp);
static void t3d_anim_updown(text3dstruct * tp);
static void t3d_anim_extrusion(text3dstruct * tp);
static void t3d_anim_rotatexy(text3dstruct * tp);
static void t3d_anim_rotateyz(text3dstruct * tp);
static void t3d_anim_frequency(text3dstruct * tp);
static void t3d_anim_amplitude(text3dstruct * tp);
static t3dAnimProc anim_array[] =
{
t3d_anim_fullrandom,
t3d_anim_default,
t3d_anim_default2,
t3d_anim_none,
t3d_anim_crazy,
t3d_anim_updown,
t3d_anim_extrusion,
t3d_anim_rotatexy,
t3d_anim_rotateyz,
t3d_anim_frequency,
t3d_anim_amplitude,
};
static char * anim_names[] =
{
"Random",
"FullRandom",
"Default",
"Default2",
"None",
"Crazy",
"UpDown",
"Extrude",
"RotateXY",
"RotateYZ",
"Frequency", /* needs -rot_frequency /= 0.0 */
"Amplitude", /* and -rot_amplitude /= 0.0 */
NULL
};
static int anims=sizeof anim_array / sizeof anim_array[0] ;
/*
*-----------------------------------------------------------------------------
*-----------------------------------------------------------------------------
* Mode funcs.
*-----------------------------------------------------------------------------
*-----------------------------------------------------------------------------
*/
/*
*-----------------------------------------------------------------------------
* Utils.
*-----------------------------------------------------------------------------
*/
static char *
text3d_newstr(char *s1)
{
char *s;
s = (char *) malloc(strlen(s1));
strcpy(s, s1);
return s;
}
static int
readable(char *filename)
{
FILE *fp;
if ((fp = my_fopen(filename, "r")) == NULL)
return False;
(void) fclose(fp);
return True;
}
/*-------------------------------------------------------------*/
#if HAVE_DIRENT_H
static void
randomFileFromList(char *directory,
struct dirent **filelist, int numfiles, char *file_local)
{
int num;
num = NRAND(numfiles);
if (strlen(directory) + strlen(filelist[num]->d_name) + 1 < 256)
{
(void) sprintf(file_local, "%s%s",
directory, filelist[num]->d_name);
}
}
/*-------------------------------------------------------------*/
static void
getRandomFontFile(char *randomfile, char *randomfile_local)
{
/* mimic getRandomImageFile from $TOP/xlock/iostuff.c */
char directory_r[DIRBUF];
struct dirent ***fonts_list;
struct dirent **font_list = (dirent **) NULL;
int num_list;
/* extern char 'stolen' from $TOP/xlock/resource.c */
extern char filename_r[MAXNAMLEN];
get_dir(randomfile, directory_r, filename_r);
fonts_list = (struct dirent ***) malloc(sizeof(struct dirent **));
num_list = scan_dir(directory_r, fonts_list, sel_font, (int (*)(const
void *, const void *)) NULL);
font_list = *fonts_list;
(void) free((void *) fonts_list);
if (num_list > 0)
{
randomFileFromList(directory_r, font_list, num_list, randomfile_local);
}
}
#endif /* HAVE_DIRENT_H */
/*-------------------------------------------------------------*/
#ifdef DIFFUSE_COLOR
static void
hsv_to_rgb(double h, double s, double v,
double *r, double *g, double *b)
{
double xh = fmod(h * 360., 360) / 60.0,
i = floor(xh),
f = xh - i,
p1 = v * (1 - s),
p2 = v * (1 - (s * f)),
p3 = v * (1 - (s * (1 - f)));
switch ((int) i)
{
case 0:
*r = v;
*g = p3;
*b = p1;
break;
case 1:
*r = p2;
*g = v;
*b = p1;
break;
case 2:
*r = p1;
*g = v;
*b = p3;
break;
case 3:
*r = p1;
*g = p2;
*b = v;
break;
case 4:
*r = p3;
*g = p1;
*b = v;
break;
case 5:
*r = v;
*g = p1;
*b = p2;
break;
}
}
#endif /* DIFFUSE_COLOR */
/*-------------------------------------------------------------*/
static void spheric_camera(text3dstruct * tp,
float center_x, float center_y, float center_z,
float phi, float theta, float radius)
{
float x = center_x + cos(phi) * cos(theta) * radius;
float y = center_y + sin(phi) * cos(theta) * radius;
float z = center_z + sin(theta) * radius;
float vx = -cos(phi) * sin(theta) * radius;
float vy = -sin(phi) * sin(theta) * radius;
float vz = cos(theta) * radius;
glViewport(0, 0, tp->WinW, tp->WinH);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, GLfloat(tp->WinW) / GLfloat(tp->WinH), 10, 10000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(x, y, z, center_x, center_y, center_z, vx, vy, vz);
}
/*-------------------------------------------------------------*/
class GLTTGlyphTriangles:public GLTTGlyphTriangulator
{
public:
struct Triangle
{
FTGlyphVectorizer::POINT * p1;
FTGlyphVectorizer::POINT * p2;
FTGlyphVectorizer::POINT * p3;
};
Triangle *triangles;
int nTriangles;
GLTTboolean count_them;
GLTTGlyphTriangles(FTGlyphVectorizer * vectorizer):
GLTTGlyphTriangulator(vectorizer)
{
triangles = 0;
nTriangles = 0;
count_them = GLTT_TRUE;
}
virtual ~GLTTGlyphTriangles()
{
delete[]triangles;
triangles = 0;
}
void alloc()
{
delete triangles;
triangles = new Triangle[nTriangles + 1];
}
virtual void triangle(FTGlyphVectorizer::POINT * p1,
FTGlyphVectorizer::POINT * p2,
FTGlyphVectorizer::POINT * p3)
{
if (count_them)
{
++nTriangles;
return;
}
triangles[nTriangles].p1 = p1;
triangles[nTriangles].p2 = p2;
triangles[nTriangles].p3 = p3;
++nTriangles;
}
};
/*
*-----------------------------------------------------------------------------
* Animation functions.
*-----------------------------------------------------------------------------
*/
#define FAC_CAMERA 1.15
#define MAX_CAMERA 5.00
#define MIN_EXTRUSION 25.0
#define MAX_EXTRUSION 305.0
#define FAC_EXTRUSION 5.0
#define FAC_FREQ 0.15
#define FAC_AMPL 1.5
#define FAC_RAND 25
/*-------------------------------------------------------------*/
static void
t3d_anim_default(text3dstruct * tp)
{
tp->phi += tp->direction * angle_speed;
tp->theta += tp->direction * angle_speed;
}
/*-------------------------------------------------------------*/
static void
t3d_anim_default2(text3dstruct * tp)
{
tp->phi += tp->direction * angle_speed;
tp->theta += tp->direction * angle_speed * 2.0 ;
}
/*-------------------------------------------------------------*/
static void
t3d_anim_none(text3dstruct * tp)
{
}
/*-------------------------------------------------------------*/
static void
t3d_anim_crazy(text3dstruct * tp)
{
int key = NRAND(32);
switch (key)
{
case 0:
case 1:
case 2:
case 3:
tp->theta += angle_speed;
break;
case 4:
case 5:
case 6:
case 7:
tp->theta -= angle_speed;
break;
case 8:
case 9:
case 10:
case 11:
tp->phi -= angle_speed;
break;
case 12:
case 13:
case 14:
case 15:
tp->phi += angle_speed;
case 16:
case 17:
if (tp->camera_dist / FAC_CAMERA > tp->ref_camera_dist)
tp->camera_dist /= FAC_CAMERA;
break;
case 18:
case 19:
if (tp->camera_dist * FAC_CAMERA < (tp->ref_camera_dist * MAX_CAMERA))
tp->camera_dist *= FAC_CAMERA;
break;
case 20:
if ((tp->extrusion - FAC_EXTRUSION) > MIN_EXTRUSION)
tp->extrusion -= FAC_EXTRUSION;
break;
case 21:
if ((tp->extrusion + FAC_EXTRUSION) < MAX_EXTRUSION)
tp->extrusion += FAC_EXTRUSION;
break;
case 22:
case 23:
tp->rampl /= FAC_AMPL;
break;
case 24:
case 25:
tp->rampl *= FAC_AMPL;
break;
case 26:
case 27:
tp->rfreq *= FAC_FREQ;
break;
case 28:
case 29:
tp->rfreq /= FAC_FREQ;
break;
}
}
static void
t3d_anim_updown(text3dstruct * tp)
{
if (tp->direction > 0)
{
if (tp->camera_dist / FAC_CAMERA > tp->ref_camera_dist)
tp->camera_dist /= FAC_CAMERA;
else
tp->direction *=-1;
}
else
{
if (tp->camera_dist * FAC_CAMERA < (tp->ref_camera_dist * MAX_CAMERA))
tp->camera_dist *= FAC_CAMERA;
else
tp->direction *=-1;
}
}
static void
t3d_anim_extrusion(text3dstruct * tp)
{
if (tp->direction > 0)
{
if ((tp->extrusion - FAC_EXTRUSION) > MIN_EXTRUSION)
tp->extrusion -= FAC_EXTRUSION;
else
tp->direction *=-1;
}
else
{
if ((tp->extrusion + FAC_EXTRUSION) < MAX_EXTRUSION)
tp->extrusion += FAC_EXTRUSION;
else
tp->direction *=-1;
}
}
static void
t3d_anim_rotatexy(text3dstruct * tp)
{
tp->phi += tp->direction * angle_speed;
}
static void
t3d_anim_rotateyz(text3dstruct * tp)
{
tp->theta += tp->direction * angle_speed;
}
static void
t3d_anim_frequency(text3dstruct * tp)
{
/* Better visual if freq is a small value < 0.05 */
if (tp->direction > 0)
{
tp->rfreq /= FAC_FREQ;
}
else
{
tp->rfreq *= FAC_FREQ;
}
if (NRAND(100) < FAC_RAND )
tp->direction *=-1;
}
static void
t3d_anim_amplitude(text3dstruct * tp)
{
if (tp->direction > 0)
{
tp->rampl /= FAC_AMPL;
}
else
{
tp->rampl *= FAC_AMPL;
}
if (NRAND(100) < FAC_RAND )
tp->direction *=-1;
}
static void
t3d_anim_fullrandom(text3dstruct * tp)
{
if (NRAND(100) < FAC_RAND || tp->animation == 1)
{
tp->animation = NRAND(anims);
}
anim_array[tp->animation](tp);
}
/*-------------------------------------------------------------*/
/*-------------------------------------------------------------*/
/*
*-----------------------------------------------------------------------------
* "Main" local funcs.
*-----------------------------------------------------------------------------
*/
static void
Reshape(ModeInfo * mi, int width, int height)
{
text3dstruct *tp = &text3d[MI_SCREEN(mi)];
glViewport(0, 0, tp->WinW = (GLint) width, tp->WinH = (GLint) height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, (GLdouble) width / (GLdouble) height, 10, 10000);
glMatrixMode(GL_MODELVIEW);
}
/*-------------------------------------------------------------*/
static void
Animate(text3dstruct * tp)
{
anim_array[tp->animation](tp);
}
/*-------------------------------------------------------------*/
static void
Draw(text3dstruct * tp,
Display * display,
Window window)
{
int text_length;
char *c_text;
if (!nosplit)
{
text_length = index_dir(tp->words, " ");
if (text_length == 0)
text_length = strlen(tp->words);
c_text = (char *) malloc(text_length);
strncpy(c_text, tp->words, text_length);
}
else
{
c_text = tp->words_start;
text_length = strlen(tp->words_start);
}
GLTTFont font(tp->face);
if (!font.create(DEF_FONTSIZE))
return;
FTGlyphVectorizer *vec = new FTGlyphVectorizer[text_length];
GLTTGlyphTriangles **tri = new GLTTGlyphTriangles *[text_length];
int i;
for (i = 0; i < text_length; ++i)
tri[i] = new GLTTGlyphTriangles(vec + i);
if (tp->camera_dist == 0.0)
/* PURIFY reports an Array Bounds Read on the next line */
tp->ref_camera_dist = tp->camera_dist = font.getWidth(c_text) * 0.75;
double min_y = 1e20;
double max_y = -1e20;
double size_x = 0.0;
for (i = 0; i < text_length; ++i)
{
int ch = (unsigned char) c_text[i];
#if ((MESA_MAJOR_VERSION > 3 ) || (( MESA_MAJOR_VERSION == 3 ) && (MESA_MINOR_VERSION > 0 )))
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
FTGlyph *g = font.getFont()->getGlyph(ch);
if (g == 0)
continue;
FTGlyphVectorizer & v = vec[i];
v.setPrecision(10.0);
if (!v.init(g))
continue;
size_x += v.getAdvance();
if (!v.vectorize())
continue;
for (int c = 0; c < v.getNContours(); ++c)
{
FTGlyphVectorizer::Contour * contour = v.getContour(c);
if (contour == 0)
continue;
for (int j = 0; j < contour->nPoints; ++j)
{
FTGlyphVectorizer::POINT * point = contour->points + j;
if (point->y < min_y)
min_y = point->y;
if (point->y > max_y)
max_y = point->y;
point->data = (void *) new double[6];
}
}
GLTTGlyphTriangles *t = tri[i];
if (!t->init(g))
continue;
t->count_them = GLTT_TRUE;
t->nTriangles = 0;
t->triangulate();
t->count_them = GLTT_FALSE;
t->alloc();
t->nTriangles = 0;
t->triangulate();
}
if (!nosplit)
free(c_text);
if (size_x == 0.0)
{
fprintf(stderr, "Please give something to draw !\n");
return;
}
double y_delta = (min_y + max_y) / 2. + min_y + 50;
for (i = 0; i < text_length; ++i)
{
#if ((MESA_MAJOR_VERSION > 3 ) || (( MESA_MAJOR_VERSION == 3 ) && (MESA_MINOR_VERSION > 0 )))
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
FTGlyphVectorizer & v = vec[i];
for (int c = 0; c < v.getNContours(); ++c)
{
FTGlyphVectorizer::Contour * contour = v.getContour(c);
if (contour == 0)
continue;
for (int j = 0; j < contour->nPoints; ++j)
{
FTGlyphVectorizer::POINT * point = contour->points + j;
point->y -= y_delta;
}
}
}
#define TWO_LIGHTS
#define ALL_STUFF
#ifdef ALL_STUFF
float