572 lines
16 KiB
C++
572 lines
16 KiB
C++
/* -*- Mode: C; tab-width: 4 -*- */
|
|
/* text3d2 --- Shows moving 3D texts */
|
|
|
|
#if !defined( lint ) && !defined( SABER )
|
|
static const char sccsid[] = "@(#)text3d.2cc 5.12 2004/03/09 xlockmore";
|
|
|
|
#endif
|
|
|
|
/* Copyright (c) E. Lassauge, 2004. */
|
|
|
|
/*
|
|
* 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 FTGL graphics library
|
|
* Copyright Henry Maddocks <henryj AT paradise.net.nz>
|
|
* http://homepages.paradise.net.nz/henryj/
|
|
*
|
|
* My e-mail address is <lassauge AT users.sourceforge.net> (NOSPAM please)
|
|
* Web site at http://lassauge.free.fr/
|
|
*
|
|
* Eric Lassauge (March-09-2004)
|
|
*
|
|
* Revision History:
|
|
*
|
|
* Eric Lassauge (March-09-2004) Created based on xscreensaver's gltext and
|
|
* the FTGL library demo:
|
|
* it uses freetype2 which is more common now.
|
|
*
|
|
*/
|
|
|
|
#define DEF_TEXT "(default)"
|
|
#define DEF_SPIN "XYZ"
|
|
#define DEF_WANDER "True"
|
|
#define DEF_NOSPLIT "False"
|
|
|
|
#ifdef STANDALONE
|
|
#define MODE_text3d2
|
|
#define PROGCLASS "Text3d2"
|
|
#define HACK_INIT init_text3d2
|
|
#define HACK_DRAW draw_text3d2
|
|
#define HACK_RESHAPE reshape_text3d2
|
|
#define text3d2_opts xlockmore_opts
|
|
|
|
|
|
#define DEFAULTS "*delay: 10000 \n" \
|
|
"*showFPS: False \n" \
|
|
"*wireframe: False \n" \
|
|
"*spin: " DEF_SPIN "\n" \
|
|
"*wander: " DEF_WANDER "\n" \
|
|
"*nosplit: " DEF_NOSPLIT "\n" \
|
|
"*message: " DEF_TEXT "\n"
|
|
|
|
extern "C"
|
|
{
|
|
#include "xlockmore.h" /* from the xscreensaver distribution */
|
|
}
|
|
#else /* !STANDALONE */
|
|
#include "xlock.h" /* from the xlockmore distribution */
|
|
#include "visgl.h"
|
|
#ifdef HAS_MMOV
|
|
#undef error
|
|
#endif
|
|
#endif /* !STANDALONE */
|
|
#include "iostuff.h"
|
|
|
|
#ifdef MODE_text3d2
|
|
|
|
#ifdef __CYGWIN__
|
|
#include "FTGL/FTGLExtrdFont.h"
|
|
#include "FTGL/FTGLOutlineFont.h"
|
|
#else
|
|
#include "FTGLExtrdFont.h"
|
|
#include "FTGLOutlineFont.h"
|
|
#endif
|
|
|
|
#include "rotator.h"
|
|
#include "text3d2.h"
|
|
#include <GL/glu.h>
|
|
|
|
#include <X11/Xcms.h>
|
|
|
|
/* Yes, it's an ugly mix of 'C' and 'C++' functions */
|
|
extern "C" { void init_text3d2(ModeInfo * mi); }
|
|
extern "C" { void draw_text3d2(ModeInfo * mi); }
|
|
extern "C" { void change_text3d2(ModeInfo * mi); }
|
|
extern "C" { void release_text3d2(ModeInfo * mi); }
|
|
extern "C" { void refresh_text3d2(ModeInfo * mi); }
|
|
|
|
/* arial.ttf is not supplied for legal reasons. */
|
|
/* NT and Windows 3.1 in c:\WINDOWS\SYSTEM\ARIAL.TTF */
|
|
/* Windows95 in c:\windows\fonts\arial.ttf */
|
|
#ifndef DEF_TTFONT
|
|
/* Directory of only *.ttf */
|
|
/* symbol.ttf and wingding.ttf should be excluded or it may core dump */
|
|
/* can be excluded if gltt is modified see README */
|
|
#ifdef __CYGWIN__
|
|
#define DEF_TTFONT "c:\\windows\\fonts\\"
|
|
#else
|
|
#define DEF_TTFONT "/usr/lib/X11/xlock/fonts/"
|
|
#endif
|
|
#endif
|
|
|
|
|
|
static char *mode_font;
|
|
static int nosplit;
|
|
static char *do_spin;
|
|
static Bool do_wander;
|
|
static char *fontfile = (char *) NULL;
|
|
|
|
/* Manage Option vars */
|
|
|
|
static XrmOptionDescRec opts[] =
|
|
{
|
|
{(char *) "-ttfont", (char *) ".text3d2.ttfont", XrmoptionSepArg, (caddr_t) NULL},
|
|
{(char *) "-no_split", (char *) ".text3d2.no_split", XrmoptionNoArg, (caddr_t) "on"},
|
|
{(char *) "+no_split", (char *) ".text3d2.no_split", XrmoptionNoArg, (caddr_t) "off"},
|
|
{(char *) "-wander", (char *) ".text3d2.wander", XrmoptionNoArg, (caddr_t) "on"},
|
|
{(char *) "+wander", (char *) ".text3d2.wander", XrmoptionNoArg, (caddr_t) "off"},
|
|
{(char *) "-spin", (char *) ".text3d2.spin", XrmoptionSepArg, (caddr_t) NULL},
|
|
{(char *) "+spin", (char *) ".text3d2.spin", XrmoptionNoArg, (caddr_t) ""},
|
|
};
|
|
|
|
static argtype vars[] =
|
|
{
|
|
{(void *) & mode_font, (char *) "ttfont", (char *) "TTFont", (char *) DEF_TTFONT, t_String},
|
|
{(void *) & nosplit, (char *) "no_split", (char *) "NoSplit", (char *) DEF_NOSPLIT, t_Bool},
|
|
{(void *) & do_wander, (char *) "wander", (char *) "Wander", (char *) DEF_WANDER, t_Bool},
|
|
{(void *) & do_spin, (char *) "spin", (char *) "Spin", (char *) DEF_SPIN, t_String},
|
|
};
|
|
|
|
static OptionStruct desc[] =
|
|
{
|
|
{(char *) "-ttfont filename", (char *) "Text3d2 TrueType font file name"},
|
|
{(char *) "-/+no_split", (char *) "Text3d2 words splitting off/on"},
|
|
{(char *) "-/+wander", (char *) "Text3d2 wander off/on"},
|
|
{(char *) "-spin name/+spin", (char *) "Text3d2 spin mode"},
|
|
};
|
|
|
|
ModeSpecOpt text3d2_opts =
|
|
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
|
|
|
|
#ifdef USE_MODULES
|
|
ModStruct text3d2_description =
|
|
{"text3d2", "init_text3d2", "draw_text3d2", "release_text3d2",
|
|
"refresh_text3d2", "change_text3d2", (char *) NULL, &text3d2_opts,
|
|
100000, 1, 10, 1, 64, 1.0, "",
|
|
"Shows 3D text", 0, NULL};
|
|
#endif
|
|
|
|
static text3d2struct *text3d2 = (text3d2struct *) NULL;
|
|
|
|
static GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
|
|
static GLfloat light1_ambient[4] = { 1.0, 1.0, 1.0, 1.0 };
|
|
static GLfloat light2_ambient[4] = { 0.2, 0.2, 0.2, 1.0 };
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* Utils.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static void
|
|
setup_lighting()
|
|
{
|
|
// Set up lighting.
|
|
float light1_diffuse[4] = { 1.0, 0.9, 0.9, 1.0 };
|
|
float light1_specular[4] = { 1.0, 0.7, 0.7, 1.0 };
|
|
float light1_position[4] = { -1.0, 1.0, 1.0, 0.0 };
|
|
glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient);
|
|
glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);
|
|
glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular);
|
|
glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
|
|
glEnable(GL_LIGHT1);
|
|
|
|
float light2_diffuse[4] = { 0.9, 0.9, 0.9, 1.0 };
|
|
float light2_specular[4] = { 0.7, 0.7, 0.7, 1.0 };
|
|
float light2_position[4] = { 1.0, -1.0, -1.0, 0.0 };
|
|
glLightfv(GL_LIGHT2, GL_AMBIENT, light2_ambient);
|
|
glLightfv(GL_LIGHT2, GL_DIFFUSE, light2_diffuse);
|
|
glLightfv(GL_LIGHT2, GL_SPECULAR, light2_specular);
|
|
glLightfv(GL_LIGHT2, GL_POSITION, light2_position);
|
|
glEnable(GL_LIGHT2);
|
|
|
|
float front_emission[4] = { 0.3, 0.2, 0.1, 0.0 };
|
|
float front_ambient[4] = { 0.2, 0.2, 0.2, 0.0 };
|
|
float front_diffuse[4] = { 0.95, 0.95, 0.8, 0.0 };
|
|
float front_specular[4] = { 0.6, 0.6, 0.6, 0.0 };
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, front_emission);
|
|
glMaterialfv(GL_FRONT, GL_AMBIENT, front_ambient);
|
|
glMaterialfv(GL_FRONT, GL_DIFFUSE, front_diffuse);
|
|
glMaterialfv(GL_FRONT, GL_SPECULAR, front_specular);
|
|
glMaterialf(GL_FRONT, GL_SHININESS, 16.0);
|
|
glColor4fv(front_diffuse);
|
|
|
|
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
|
|
glEnable(GL_CULL_FACE);
|
|
glColorMaterial(GL_FRONT, GL_DIFFUSE);
|
|
glEnable(GL_COLOR_MATERIAL);
|
|
|
|
glEnable(GL_LIGHTING);
|
|
glShadeModel(GL_SMOOTH);
|
|
}
|
|
|
|
static int
|
|
setup_font( text3d2struct * tp,
|
|
const char* fontfile)
|
|
{
|
|
if (!tp->wire)
|
|
tp->font = new FTGLExtrdFont(fontfile);
|
|
else
|
|
tp->font = new FTGLOutlineFont(fontfile);
|
|
|
|
if( tp->font->Error())
|
|
{
|
|
fprintf( stderr, "Failed to open font %s", fontfile);
|
|
return 0;
|
|
}
|
|
|
|
if( !tp->font->FaceSize( 192))
|
|
{
|
|
fprintf( stderr, "Failed to set size");
|
|
return(0);
|
|
}
|
|
|
|
tp->font->Depth(24);
|
|
|
|
tp->font->CharMap(ft_encoding_none);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*-----------------------------------------------------------------------------
|
|
* Mode funcs.
|
|
*-----------------------------------------------------------------------------
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
reshape_text3d2(ModeInfo * mi, int width, int height)
|
|
{
|
|
text3d2struct *tp = &text3d2[MI_SCREEN(mi)];
|
|
GLfloat h = (GLfloat) height / (GLfloat) width;
|
|
|
|
glViewport(0, 0, tp->WinW = (GLint) width, tp->WinH = (GLint) height);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
gluPerspective (30.0, 1/h, 1.0, 100.0);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
gluLookAt( 0.0, 0.0, 30.0,
|
|
0.0, 0.0, 0.0,
|
|
0.0, 1.0, 0.0);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
/*-------------------------------------------------------------*/
|
|
static void
|
|
gl_init (text3d2struct * tp)
|
|
{
|
|
glEnable( GL_CULL_FACE);
|
|
glFrontFace( GL_CCW);
|
|
glEnable( GL_DEPTH_TEST);
|
|
|
|
// Color is used for wire mode only
|
|
color[0] = ((float) (NRAND(70)) / 100.0) + 0.30;
|
|
color[1] = ((float) (NRAND(30)) / 100.0) + 0.70;
|
|
color[2] = color[1] * 0.56;
|
|
|
|
}
|
|
|
|
/*-------------------------------------------------------------*/
|
|
static void
|
|
draw_text(text3d2struct * tp,
|
|
Display * display,
|
|
Window window)
|
|
{
|
|
int text_length;
|
|
char *c_text;
|
|
|
|
if (!nosplit)
|
|
{
|
|
text_length = index_dir(tp->words, (char *) " ");
|
|
if (text_length == 0)
|
|
text_length = strlen(tp->words);
|
|
if ((c_text = (char *) malloc(text_length+2)) != NULL)
|
|
strncpy(c_text, tp->words, text_length);
|
|
// +2 is because of a bug in FTGL !!
|
|
c_text[text_length] = 0;
|
|
c_text[text_length+1] = 0;
|
|
}
|
|
else
|
|
{
|
|
text_length = strlen(tp->words_start);
|
|
if ((c_text = (char *) malloc(text_length+2)) != NULL)
|
|
strncpy(c_text, tp->words_start, text_length);
|
|
c_text[text_length] = 0;
|
|
c_text[text_length+1] = 0;
|
|
}
|
|
|
|
// Render text
|
|
tp->font->Render(c_text);
|
|
|
|
//if (!nosplit)
|
|
free(c_text);
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*-----------------------------------------------------------------------------
|
|
* Xlock hooks.
|
|
*-----------------------------------------------------------------------------
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* Initialize text3d2. Called each time the window changes.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
init_text3d2(ModeInfo * mi)
|
|
{
|
|
int i;
|
|
text3d2struct *tp;
|
|
|
|
if (text3d2 == NULL)
|
|
{
|
|
if ((text3d2 = (text3d2struct *) calloc(MI_NUM_SCREENS(mi),
|
|
sizeof(text3d2struct))) == NULL)
|
|
return;
|
|
}
|
|
tp = &text3d2[MI_SCREEN(mi)];
|
|
tp->wire = MI_IS_WIREFRAME(mi);
|
|
tp->counter = 0;
|
|
|
|
fontfile = getModeFont(mode_font);
|
|
if (!fontfile) {
|
|
MI_CLEARWINDOW(mi);
|
|
release_text3d2(mi);
|
|
return;
|
|
}
|
|
if (!setup_font(tp,fontfile)) {
|
|
(void) fprintf(stderr, "%s: unable to open True Type font %s!\n", MI_NAME(mi), fontfile);
|
|
MI_CLEARWINDOW(mi);
|
|
release_text3d2(mi);
|
|
return;
|
|
}
|
|
if (MI_IS_DEBUG(mi)) {
|
|
(void) fprintf(stderr,
|
|
"%s:\n\tfontfile=%s .\n", MI_NAME(mi), fontfile);
|
|
}
|
|
|
|
/* Do not free fontfile getModeFont handles potential leak */
|
|
if ((tp->glx_context = init_GL(mi)) != NULL)
|
|
{
|
|
if (MI_IS_USE3D(mi)) {
|
|
// Find out the RGB values out of the left/right colors !
|
|
XcmsColor search;
|
|
search.pixel = MI_RIGHT_COLOR(mi);
|
|
XcmsQueryColor(MI_DISPLAY(mi), MI_COLORMAP(mi), &search, XcmsRGBiFormat);
|
|
light1_ambient[0] = search.spec.RGBi.red;
|
|
light1_ambient[1] = search.spec.RGBi.green;
|
|
light1_ambient[2] = search.spec.RGBi.blue;
|
|
search.pixel = MI_LEFT_COLOR(mi);
|
|
XcmsQueryColor(MI_DISPLAY(mi), MI_COLORMAP(mi), &search, XcmsRGBiFormat);
|
|
light2_ambient[0] = search.spec.RGBi.red;
|
|
light2_ambient[1] = search.spec.RGBi.green;
|
|
light2_ambient[2] = search.spec.RGBi.blue;
|
|
}
|
|
gl_init(tp);
|
|
reshape_text3d2(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
|
|
}
|
|
else
|
|
{
|
|
MI_CLEARWINDOW(mi);
|
|
}
|
|
|
|
{ // Manage spin
|
|
Bool spinx=False, spiny=False, spinz=False;
|
|
double spin_speed = 1.0;
|
|
double wander_speed = 0.05;
|
|
double spin_accel = 1.0;
|
|
|
|
char *s = do_spin;
|
|
while (*s)
|
|
{
|
|
if (*s == 'x' || *s == 'X') spinx = True;
|
|
else if (*s == 'y' || *s == 'Y') spiny = True;
|
|
else if (*s == 'z' || *s == 'Z') spinz = True;
|
|
else
|
|
{
|
|
(void)fprintf (stderr,
|
|
"%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
|
|
MI_NAME(mi), do_spin);
|
|
MI_CLEARWINDOW(mi);
|
|
release_text3d2(mi);
|
|
return;
|
|
}
|
|
s++;
|
|
}
|
|
|
|
tp->rot = make_rotator (spinx ? spin_speed : 0,
|
|
spiny ? spin_speed : 0,
|
|
spinz ? spin_speed : 0,
|
|
spin_accel,
|
|
do_wander ? wander_speed : 0,
|
|
False);
|
|
}
|
|
|
|
/* Initialize displayed string */
|
|
tp->words_start = tp->words = getWords(MI_SCREEN(mi), MI_NUM_SCREENS(mi));
|
|
if (MI_IS_DEBUG(mi))
|
|
{
|
|
(void) fprintf(stderr,
|
|
"%s words:\n%s\n",
|
|
MI_NAME(mi), tp->words);
|
|
}
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* Called by the mainline code periodically to update the display.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
void
|
|
draw_text3d2(ModeInfo * mi)
|
|
{
|
|
Display *display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
text3d2struct *tp;
|
|
|
|
|
|
if (text3d2 == NULL) {
|
|
return;
|
|
}
|
|
tp = &text3d2[MI_SCREEN(mi)];
|
|
|
|
if (!tp->glx_context)
|
|
return;
|
|
|
|
MI_IS_DRAWN(mi) = True;
|
|
tp->counter = tp->counter + 1;
|
|
|
|
if (tp->counter > MI_CYCLES(mi) & !nosplit)
|
|
{
|
|
int text_length = index_dir(tp->words, (char *) " ");
|
|
|
|
/* Every now and then, get a new word */
|
|
if (text_length == 0)
|
|
text_length = strlen(tp->words);
|
|
tp->counter = 0;
|
|
tp->words += text_length;
|
|
text_length = strlen(tp->words);
|
|
if (text_length == 0)
|
|
{
|
|
tp->words_start = tp->words =
|
|
getWords(MI_SCREEN(mi), MI_NUM_SCREENS(mi));
|
|
}
|
|
}
|
|
glShadeModel(GL_SMOOTH);
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glEnable(GL_NORMALIZE);
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
glPushMatrix ();
|
|
|
|
glScalef(1.1, 1.1, 1.1);
|
|
|
|
{
|
|
double x, y, z;
|
|
get_position (tp->rot, &x, &y, &z, 1);
|
|
glTranslatef((x - 1.0) * 8,
|
|
(y - 0.5) * 8,
|
|
(z - 0.5) * 8);
|
|
|
|
get_rotation (tp->rot, &x, &y, &z, 1);
|
|
glRotatef (x * 360, 1.0, 0.0, 0.0);
|
|
glRotatef (y * 360, 0.0, 1.0, 0.0);
|
|
glRotatef (z * 360, 0.0, 0.0, 1.0);
|
|
}
|
|
|
|
|
|
glScalef(0.01, 0.01, 0.01);
|
|
|
|
if (!tp->wire)
|
|
{
|
|
glDisable( GL_BLEND);
|
|
setup_lighting();
|
|
}
|
|
else
|
|
glColor4fv (color);
|
|
|
|
draw_text(tp, display, window);
|
|
glPopMatrix ();
|
|
|
|
if (MI_IS_FPS(mi)) do_fps (mi);
|
|
|
|
glXSwapBuffers(display, window);
|
|
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* The display is being taken away from us. Free up malloc'ed
|
|
* memory and X resources that we've alloc'ed. Only called
|
|
* once, we must zap everything for every screen.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
release_text3d2(ModeInfo * mi)
|
|
{
|
|
if (text3d2 != NULL)
|
|
{
|
|
for (int screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
|
|
{
|
|
text3d2struct *tp = &text3d2[screen];
|
|
|
|
if (tp->font)
|
|
delete tp->font;
|
|
if (tp->rot)
|
|
free_rotator(tp->rot);
|
|
}
|
|
free(text3d2);
|
|
text3d2 = (text3d2struct *) NULL;
|
|
}
|
|
FreeAllGL(mi);
|
|
}
|
|
|
|
void
|
|
refresh_text3d2(ModeInfo * mi)
|
|
{
|
|
/* Do nothing, it will refresh by itself :) */
|
|
}
|
|
|
|
void
|
|
change_text3d2(ModeInfo * mi)
|
|
{
|
|
text3d2struct *tp;
|
|
|
|
if (text3d2 == NULL) {
|
|
return;
|
|
}
|
|
tp = &text3d2[MI_SCREEN(mi)];
|
|
|
|
if (!tp->glx_context)
|
|
return;
|
|
glDrawBuffer(GL_BACK);
|
|
glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tp->glx_context));
|
|
}
|
|
|
|
#endif /* MODE_text3d2 */
|