454 lines
12 KiB
C
454 lines
12 KiB
C
/* -*- Mode: C; tab-width: 4 -*- */
|
|
/* space --- A journey into deep space */
|
|
|
|
#if !defined( lint ) && !defined( SABER )
|
|
static const char sccsid[] = "@(#)space.c 5.00 2000/11/01 xlockmore";
|
|
|
|
#endif
|
|
|
|
/*-
|
|
* Copyright (c) 1998 by Vincent Caron [Vincent.Caron@ecl1999.ec-lyon.fr]
|
|
*
|
|
* 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.
|
|
*
|
|
* Revision History:
|
|
* 01-Nov-2000: Allocation checks
|
|
* 1998: Written.
|
|
*/
|
|
|
|
#ifdef STANDALONE
|
|
#define MODE_space
|
|
#define PROGCLASS "Space"
|
|
#define HACK_INIT init_space
|
|
#define HACK_DRAW draw_space
|
|
#define space_opts xlockmore_opts
|
|
#define DEFAULTS "*delay: 10000 \n" \
|
|
"*count: 100 \n" \
|
|
"*ncolors: 64 \n" \
|
|
"*use3d: False \n" \
|
|
"*delta3d: 1.5 \n" \
|
|
"*right3d: red \n" \
|
|
"*left3d: blue \n" \
|
|
"*both3d: magenta \n" \
|
|
"*none3d: black \n"
|
|
#define SMOOTH_COLORS
|
|
#include "xlockmore.h" /* in xscreensaver distribution */
|
|
#else /* STANDALONE */
|
|
#include "xlock.h" /* in xlockmore distribution */
|
|
#endif /* STANDALONE */
|
|
|
|
#ifdef MODE_space
|
|
|
|
ModeSpecOpt space_opts =
|
|
{0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
|
|
|
|
#ifdef USE_MODULES
|
|
ModStruct space_description =
|
|
{"space", "init_space", "draw_space", "release_space",
|
|
"refresh_space", "init_space", (char *) NULL, &space_opts,
|
|
10000, 100, 1, 1, 64, 1.0, "",
|
|
"a journey into deep space", 0, NULL};
|
|
|
|
#endif
|
|
|
|
#define X_LIMIT 400 /* space coords clipping */
|
|
#define Y_LIMIT 300
|
|
#define Z_MAX 450
|
|
#define Z_MIN (-330)
|
|
#define Z_L2 200 /* far-distance level */
|
|
#define Z_L3 (-150) /* close-distance level */
|
|
#define DIST 400 /* observer-starfield distance */
|
|
#define TRANS_X_MAX 3 /* cinetic parameters auto-change : */
|
|
#define TRANS_Y_MAX 3 /* defines limits for cinetic parameters randomizer */
|
|
#define TRANS_Z_MAX 7 /* idem */
|
|
#define ROT_X_MAX 60 /* idem */
|
|
#define ROT_Y_MAX 80 /* idem */
|
|
#define ROT_Z_MAX 50 /* idem */
|
|
#define TIME_MIN 500 /* idem */
|
|
#define TIME_AMP 800 /* idem */
|
|
#define DEGREE 0.01/100 /* custom angle unit :) */
|
|
|
|
typedef struct {
|
|
int nb; /* stars number */
|
|
int originX, originY; /* screen width/2,height/2 */
|
|
double zoom; /* adapt to screen dimensions */
|
|
double *starX; /* star buffer */
|
|
double *starY; /* holds X,Y,Z coords */
|
|
double *starZ;
|
|
int ddxn, ddyn, ddzn, daxn, dayn, dazn; /* counters */
|
|
double dx, ddx, dy, ddy, dz, ddz, ax, dax, ay, day, az, daz; /* cinetic params */
|
|
int pixel_nb; /* number of XPoints in last used pixel buffer */
|
|
XPoint *stars1; /* pixel buffer 1 */
|
|
XPoint *stars1copy; /* left eye or 2D mode */
|
|
XPoint *stars1a; /* two buffers in stars1a,stars1b */
|
|
XPoint *stars1b;
|
|
XPoint *stars2; /* pixel buffer 2 */
|
|
XPoint *stars2copy; /* right eye (3D mode only) */
|
|
XPoint *stars2a; /* two buffers in stars2a,stars2b */
|
|
XPoint *stars2b;
|
|
} SpaceStruct;
|
|
|
|
static SpaceStruct *spaces = (SpaceStruct *) NULL;
|
|
|
|
static void
|
|
free_space(SpaceStruct *sp)
|
|
{
|
|
if (sp->starX != NULL) {
|
|
free(sp->starX);
|
|
sp->starX = (double *) NULL;
|
|
}
|
|
if (sp->starY != NULL) {
|
|
free(sp->starY);
|
|
sp->starY = (double *) NULL;
|
|
}
|
|
if (sp->starZ != NULL) {
|
|
free(sp->starZ);
|
|
sp->starY = (double *) NULL;
|
|
}
|
|
if (sp->stars1a != NULL) {
|
|
free(sp->stars1a);
|
|
sp->stars1a = (XPoint *) NULL;
|
|
}
|
|
if (sp->stars1b != NULL) {
|
|
free(sp->stars1b);
|
|
sp->stars1b = (XPoint *) NULL;
|
|
}
|
|
if (sp->stars2a != NULL) {
|
|
free(sp->stars2a);
|
|
sp->stars2a = (XPoint *) NULL;
|
|
}
|
|
if (sp->stars2b != NULL) {
|
|
free(sp->stars2b);
|
|
sp->stars2b = (XPoint *) NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
init_space(ModeInfo * mi)
|
|
{
|
|
int i;
|
|
SpaceStruct *sp;
|
|
|
|
/* allocate a SpaceStruct for every screen */
|
|
if (spaces == NULL) {
|
|
if ((spaces = (SpaceStruct *) calloc(MI_NUM_SCREENS(mi),
|
|
sizeof (SpaceStruct))) == NULL)
|
|
return;
|
|
}
|
|
sp = &spaces[MI_SCREEN(mi)];
|
|
/* star density is linked to screen surface */
|
|
sp->nb = MI_COUNT(mi);
|
|
sp->originX = MI_WIDTH(mi) / 2;
|
|
sp->originY = MI_HEIGHT(mi) / 2;
|
|
sp->zoom = (double) MI_WIDTH(mi) * 0.54 + 40;
|
|
|
|
/* allocate stars buffers for current screen */
|
|
if (((sp->starX = (double *) calloc(sp->nb, sizeof (double))) == NULL) ||
|
|
((sp->starY = (double *) calloc(sp->nb, sizeof (double))) == NULL) ||
|
|
((sp->starZ = (double *) calloc(sp->nb, sizeof (double))) == NULL)) {
|
|
free_space(sp);
|
|
return;
|
|
}
|
|
|
|
/* allocate pixels buffers for current screen */
|
|
if (((sp->stars1a = (XPoint *) calloc(9 * sp->nb,
|
|
sizeof (XPoint))) == NULL) ||
|
|
((sp->stars1b = (XPoint *) calloc(9 * sp->nb,
|
|
sizeof (XPoint))) == NULL)) {
|
|
free_space(sp);
|
|
return;
|
|
}
|
|
if (MI_IS_USE3D(mi)) {
|
|
if (((sp->stars2a = (XPoint *) calloc(9 * sp->nb,
|
|
sizeof (XPoint))) == NULL) ||
|
|
((sp->stars2b = (XPoint *) calloc(9 * sp->nb,
|
|
sizeof (XPoint))) == NULL)) {
|
|
free_space(sp);
|
|
return;
|
|
}
|
|
}
|
|
sp->pixel_nb = 0;
|
|
sp->stars1 = sp->stars1a;
|
|
sp->stars1copy = sp->stars1b;
|
|
sp->stars2 = sp->stars2a;
|
|
sp->stars2copy = sp->stars2b;
|
|
|
|
/* place stars randomly */
|
|
for (i = 0; i < sp->nb; i++) {
|
|
sp->starX[i] = ((double) NRAND(10000) / 5000 - 1) * X_LIMIT;
|
|
sp->starY[i] = ((double) NRAND(10000) / 5000 - 1) * Y_LIMIT;
|
|
sp->starZ[i] = (double) NRAND(10000) / 10000 * (Z_MAX - Z_MIN) + Z_MIN;
|
|
}
|
|
sp->dx = 0;
|
|
sp->ddxn = 0;
|
|
sp->dy = 0;
|
|
sp->ddyn = 0;
|
|
sp->dz = 3;
|
|
sp->ddzn = 0;
|
|
sp->ax = 0;
|
|
sp->daxn = 0;
|
|
sp->ay = 0;
|
|
sp->dayn = 0;
|
|
sp->az = 0;
|
|
sp->dazn = 0;
|
|
|
|
/* clear screen */
|
|
MI_CLEARWINDOW(mi);
|
|
}
|
|
|
|
void
|
|
draw_space(ModeInfo * mi)
|
|
{
|
|
int i, n, originX, originY, x, x2 = 0, y, IS_SMALL;
|
|
double _x, _y, _z, cosX, sinX, cosY, sinY, cosZ, sinZ, k, z, zoom;
|
|
Display *display = MI_DISPLAY(mi);
|
|
Window window = MI_WINDOW(mi);
|
|
GC gc = MI_GC(mi);
|
|
int is3D = MI_IS_USE3D(mi);
|
|
double delta = MI_DELTA3D(mi) * 8;
|
|
SpaceStruct *sp;
|
|
|
|
if (spaces == NULL)
|
|
return;
|
|
sp = &spaces[MI_SCREEN(mi)];
|
|
if (sp->stars1a == NULL)
|
|
return;
|
|
originX = sp->originX;
|
|
originY = sp->originY;
|
|
zoom = sp->zoom;
|
|
IS_SMALL = ((originX * originY) < (160 * 100));
|
|
|
|
MI_IS_DRAWN(mi) = True;
|
|
|
|
/* get cos & sin of rotations */
|
|
cosX = COSF(sp->ax);
|
|
sinX = SINF(sp->ax);
|
|
cosY = COSF(sp->ay);
|
|
sinY = SINF(sp->ay);
|
|
cosZ = COSF(sp->az);
|
|
sinZ = SINF(sp->az);
|
|
|
|
/* move stars ! */
|
|
for (i = 0; i < sp->nb; i++) {
|
|
_x = sp->starX[i];
|
|
_y = sp->starY[i];
|
|
_z = sp->starZ[i];
|
|
|
|
/* X,Y & Z axis rotations */
|
|
k = _y * cosX + _z * sinX;
|
|
_z = _z * cosX - _y * sinX;
|
|
_y = k;
|
|
k = _x * cosY + _z * sinY;
|
|
_z = _z * cosY - _x * sinY;
|
|
_x = k;
|
|
k = _x * cosZ + _y * sinZ;
|
|
_y = _y * cosZ - _x * sinZ;
|
|
_x = k;
|
|
|
|
/* translations + space boundary overflow */
|
|
_x += sp->dx;
|
|
if (_x < (-X_LIMIT))
|
|
_x = X_LIMIT;
|
|
else if (_x > X_LIMIT)
|
|
_x = -X_LIMIT;
|
|
_y += sp->dy;
|
|
if (_y < (-Y_LIMIT))
|
|
_y = Y_LIMIT;
|
|
else if (_y > Y_LIMIT)
|
|
_y = -Y_LIMIT;
|
|
_z -= sp->dz;
|
|
if (_z < Z_MIN)
|
|
_z = Z_MAX;
|
|
else if (_z > Z_MAX)
|
|
_z = Z_MIN;
|
|
|
|
sp->starX[i] = _x;
|
|
sp->starY[i] = _y;
|
|
sp->starZ[i] = _z;
|
|
}
|
|
|
|
/* update translation parameters */
|
|
if (sp->ddxn == 0) {
|
|
k = (double) NRAND(TRANS_X_MAX * 2) - TRANS_X_MAX;
|
|
sp->ddxn = (int) NRAND(TIME_AMP) + TIME_MIN;
|
|
sp->ddx = (k - sp->dx) / sp->ddxn;
|
|
} else {
|
|
sp->dx += sp->ddx;
|
|
sp->ddxn--;
|
|
}
|
|
if (sp->ddyn == 0) {
|
|
k = (double) NRAND(TRANS_Y_MAX * 2) - TRANS_Y_MAX;
|
|
sp->ddyn = (int) NRAND(TIME_AMP) + TIME_MIN;
|
|
sp->ddy = (k - sp->dy) / sp->ddyn;
|
|
} else {
|
|
sp->dy += sp->ddy;
|
|
sp->ddyn--;
|
|
}
|
|
if (sp->ddzn == 0) {
|
|
k = (double) NRAND(TRANS_Z_MAX * 2) - TRANS_Z_MAX;
|
|
sp->ddzn = (int) NRAND(TIME_AMP) + TIME_MIN;
|
|
sp->ddz = (k - sp->dz) / sp->ddzn;
|
|
} else {
|
|
sp->dz += sp->ddz;
|
|
sp->ddzn--;
|
|
}
|
|
|
|
/* update rotation parameters */
|
|
if (sp->daxn == 0) {
|
|
k = (double) (NRAND(ROT_X_MAX * 2) - ROT_X_MAX) * DEGREE;
|
|
sp->daxn = (int) NRAND(TIME_AMP) + TIME_MIN;
|
|
sp->dax = (k - sp->ax) / sp->daxn;
|
|
} else {
|
|
sp->ax += sp->dax;
|
|
sp->daxn--;
|
|
}
|
|
if (sp->dayn == 0) {
|
|
k = (double) (NRAND(ROT_Y_MAX * 2) - ROT_Y_MAX) * DEGREE;
|
|
sp->dayn = (int) NRAND(TIME_AMP) + TIME_MIN;
|
|
sp->day = (k - sp->ay) / sp->dayn;
|
|
} else {
|
|
sp->ay += sp->day;
|
|
sp->dayn--;
|
|
}
|
|
if (sp->dazn == 0) {
|
|
k = (double) (NRAND(ROT_Z_MAX * 2) - ROT_Z_MAX) * DEGREE;
|
|
sp->dazn = (int) NRAND(TIME_AMP) + TIME_MIN;
|
|
sp->daz = (k - sp->az) / sp->dazn;
|
|
} else {
|
|
sp->az += sp->daz;
|
|
sp->dazn--;
|
|
}
|
|
|
|
/* project stars and create corresponding pixels */
|
|
n = 0;
|
|
for (i = 0; i < sp->nb; i++) {
|
|
z = sp->starZ[i];
|
|
k = zoom / (z + DIST);
|
|
y = sp->stars1[n].y = originY - (int) (k * sp->starY[i]);
|
|
if (is3D) {
|
|
x = sp->stars1[n].x = originX + (int) (k * (sp->starX[i] - delta));
|
|
x2 = sp->stars2[n].x = originX + (int) (k * (sp->starX[i] + delta));
|
|
sp->stars2[n].y = y;
|
|
} else
|
|
x = sp->stars1[n].x = originX + (int) (k * sp->starX[i]);
|
|
n++;
|
|
if (z < Z_L2) {
|
|
/* not to close but closer : use 4 more pixels for this star */
|
|
sp->stars1[n].x = x + 1;
|
|
sp->stars1[n].y = y;
|
|
sp->stars1[n + 1].x = x - 1;
|
|
sp->stars1[n + 1].y = y;
|
|
sp->stars1[n + 2].x = x;
|
|
sp->stars1[n + 2].y = y + 1;
|
|
sp->stars1[n + 3].x = x;
|
|
sp->stars1[n + 3].y = y - 1;
|
|
if (is3D) {
|
|
sp->stars2[n].x = x2 + 1;
|
|
sp->stars2[n].y = y;
|
|
sp->stars2[n + 1].x = x2 - 1;
|
|
sp->stars2[n + 1].y = y;
|
|
sp->stars2[n + 2].x = x2;
|
|
sp->stars2[n + 2].y = y + 1;
|
|
sp->stars2[n + 3].x = x2;
|
|
sp->stars2[n + 3].y = y - 1;
|
|
}
|
|
n += 4;
|
|
}
|
|
if ((z < Z_L3) && (!IS_SMALL)) {
|
|
/* very close : use again 4 more pixels (makes 9 for this star) */
|
|
sp->stars1[n].x = x - 1;
|
|
sp->stars1[n].y = y + 1;
|
|
sp->stars1[n + 1].x = x - 1;
|
|
sp->stars1[n + 1].y = y - 1;
|
|
sp->stars1[n + 2].x = x + 1;
|
|
sp->stars1[n + 2].y = y + 1;
|
|
sp->stars1[n + 3].x = x + 1;
|
|
sp->stars1[n + 3].y = y - 1;
|
|
if (is3D) {
|
|
sp->stars2[n].x = x2 - 1;
|
|
sp->stars2[n].y = y + 1;
|
|
sp->stars2[n + 1].x = x2 - 1;
|
|
sp->stars2[n + 1].y = y - 1;
|
|
sp->stars2[n + 2].x = x2 + 1;
|
|
sp->stars2[n + 2].y = y + 1;
|
|
sp->stars2[n + 3].x = x2 + 1;
|
|
sp->stars2[n + 3].y = y - 1;
|
|
}
|
|
n += 4;
|
|
}
|
|
}
|
|
|
|
/* erase pixels with previous pixel buffer */
|
|
if (MI_IS_INSTALL(mi) && is3D)
|
|
XSetForeground(display, gc, MI_NONE_COLOR(mi));
|
|
else
|
|
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
|
|
XDrawPoints(display, window, gc, sp->stars1copy, sp->pixel_nb, CoordModeOrigin);
|
|
if (is3D)
|
|
XDrawPoints(display, window, gc, sp->stars2copy, sp->pixel_nb, CoordModeOrigin);
|
|
|
|
/* draw pixels */
|
|
sp->pixel_nb = n;
|
|
if (is3D) {
|
|
if (MI_IS_INSTALL(mi))
|
|
XSetFunction(display, gc, GXor);
|
|
XSetForeground(display, gc, MI_LEFT_COLOR(mi));
|
|
XDrawPoints(display, window, gc, sp->stars1, sp->pixel_nb, CoordModeOrigin);
|
|
XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
|
|
XDrawPoints(display, window, gc, sp->stars2, sp->pixel_nb, CoordModeOrigin);
|
|
if (MI_IS_INSTALL(mi))
|
|
XSetFunction(display, gc, GXcopy);
|
|
} else {
|
|
XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
|
|
XDrawPoints(display, window, gc, sp->stars1, sp->pixel_nb, CoordModeOrigin);
|
|
}
|
|
XFlush(display);
|
|
|
|
/* swap pixel buffers a & b */
|
|
if (sp->stars1 == sp->stars1a) {
|
|
sp->stars1 = sp->stars1b;
|
|
sp->stars1copy = sp->stars1a;
|
|
} else {
|
|
sp->stars1 = sp->stars1a;
|
|
sp->stars1copy = sp->stars1b;
|
|
}
|
|
if (sp->stars2 == sp->stars2a) {
|
|
sp->stars2 = sp->stars2b;
|
|
sp->stars2copy = sp->stars2a;
|
|
} else {
|
|
sp->stars2 = sp->stars2a;
|
|
sp->stars2copy = sp->stars2b;
|
|
}
|
|
}
|
|
|
|
void
|
|
refresh_space(ModeInfo * mi)
|
|
{
|
|
/* Do nothing, it will refresh by itself */
|
|
}
|
|
|
|
void
|
|
release_space(ModeInfo * mi)
|
|
{
|
|
if (spaces != NULL) {
|
|
int screen;
|
|
|
|
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
|
|
free_space(&spaces[screen]);
|
|
free(spaces);
|
|
spaces = (SpaceStruct *) NULL;
|
|
}
|
|
}
|
|
|
|
#endif /* MODE_space */
|