SSFLWBOX.C

/**********************************Module**********************************\ 
*
* ssflwbox.c
*
* 3D FlowerBox screen saver
*
* Copyright 1995 - 1998 Microsoft Corporation
*
\**************************************************************************/

#include "precomp.h"
#pragma hdrstop

// Minimum and maximum image sizes
#define MINIMAGESIZE 10
#define MAXIMAGESIZE 100

// Color tables for checkboard, per-side and single color modes
GLfloat base_checker_cols[MAXSIDES][NCCOLS][4] =
{
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 1.0f, 1.0f,
1.0f, 0.5f, 0.5f, 1.0f,
1.0f, 0.5f, 0.5f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f
};
GLfloat checker_cols[MAXSIDES][NCCOLS][4];

GLfloat base_side_cols[MAXSIDES][4] =
{
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, 1.0f, 1.0f,
1.0f, 0.5f, 0.5f, 1.0f
};
GLfloat side_cols[MAXSIDES][4];

GLfloat base_solid_cols[4] =
{
1.0f, 1.0f, 1.0f, 1.0f
};
GLfloat solid_cols[4];

// Current geometry
GEOMETRY *cur_geom;

// Set when a rendering context is available
BOOL gbGlInit = FALSE;

// Common library context
SSContext gssc;

// Spin rotations
double xr = 0, yr = 0, zr = 0;
// Scale factor and increment
FLT sf;
FLT sfi;
// Color cycling hue phase
FLT phase = 0.0f;

// Default configuration
CONFIG config =
{
TRUE, FALSE, FALSE, TRUE, TRUE, MAXSUBDIV, ID_COL_PER_SIDE,
(MAXIMAGESIZE+MINIMAGESIZE)/2, GEOM_CUBE, GL_FRONT
};

// A slider range
typedef struct _RANGE
{
int min_val;
int max_val;
int step;
int page_step;
} RANGE;

RANGE complexity_range = {MINSUBDIV, MAXSUBDIV, 1, 2};
RANGE image_size_range = {MINIMAGESIZE, MAXIMAGESIZE, 1, 10};

// True if the current OpenGL version is 1.1
BOOL bOgl11;

// True if checkered mode is on
BOOL bCheckerOn;

/******************************Public*Routine******************************\
*
* dprintf
*
* Debug output printf
*
\**************************************************************************/

#if DBG
void dprintf_out(char *fmt, ...)
{
va_list args;
char dbg[256];

va_start(args, fmt);
vsprintf(dbg, fmt, args);
va_end(args);
OutputDebugStringA(dbg);
}
#endif

/******************************Public*Routine******************************\
*
* assert_failed
*
* Assertion failure handler
*
\**************************************************************************/

#if DBG
void assert_failed(char *file, int line, char *msg)
{
dprintf(("Assertion failed %s(%d): %s\n", file, line, msg));
DebugBreak();
}
#endif

/******************************Public*Routine******************************\
*
* V3Len
*
* Vector length
*
\**************************************************************************/

FLT V3Len(PT3 *v)
{
return (FLT)sqrt(v->x*v->x+v->y*v->y+v->z*v->z);
}

/******************************Public*Routine******************************\
*
* ComputeHsvColors
*
* Compute a smooth range of colors depending on the current color mode
*
\**************************************************************************/

void ComputeHsvColors(void)
{
GLfloat *cols;
int ncols;
FLT ang, da;
int hex;
FLT fhex, frac;
FLT p, q, t;
FLT sat, val;

switch(config.color_pick)
{
case ID_COL_CHECKER:
ncols = MAXSIDES*NCCOLS;
cols = &checker_cols[0][0][0];
break;
case ID_COL_PER_SIDE:
ncols = MAXSIDES;
cols = &side_cols[0][0];
break;
case ID_COL_SINGLE:
ncols = 1;
cols = &solid_cols[0];
break;
}

ang = phase;
da = (FLT)((2*PI)/ncols);
val = sat = 1.0f;

while (ncols > 0)
{
fhex = (FLT)(6*ang/(2*PI));
hex = (int)fhex;
frac = fhex-hex;
hex = hex % 6;

p = val*(1-sat);
q = val*(1-sat*frac);
t = val*(1-sat*(1-frac));

switch(hex)
{
case 0:
cols[0] = val;
cols[1] = t;
cols[2] = p;
break;
case 1:
cols[0] = q;
cols[1] = val;
cols[2] = p;
break;
case 2:
cols[0] = p;
cols[1] = val;
cols[2] = t;
break;
case 3:
cols[0] = p;
cols[1] = q;
cols[2] = val;
break;
case 4:
cols[0] = t;
cols[1] = p;
cols[2] = val;
break;
case 5:
cols[0] = val;
cols[1] = p;
cols[2] = q;
break;
}

ang += da;
cols += 4;
ncols--;
}
}

/******************************Public*Routine******************************\
*
* Draw
*
* Draw everything
*
\**************************************************************************/

void Draw(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glLoadIdentity();

glRotated(xr, 1, 0, 0);
glRotated(yr, 0, 1, 0);
glRotated(zr, 0, 0, 1);

DrawGeom(cur_geom);

glFlush();
}

/******************************Public*Routine******************************\
*
* Update
*
* Update all varying values, called by the common library
*
\**************************************************************************/

void Update(void *data)
{
if (config.spin)
{
xr += 3;
yr += 2;
}

if (config.bloom)
{
sf += sfi;
if (sf > cur_geom->max_sf ||
sf < cur_geom->min_sf)
{
sfi = -sfi;
}
UpdatePts(cur_geom, sf);
}

if (config.cycle_colors)
{
ComputeHsvColors();
phase += (FLT)(2.5*PI/180.);
}

Draw();
}

// String storage
TCHAR geom_names[IDS_GEOM_COUNT][20];

/******************************Public*Routine******************************\
* getIniSettings
*
* Get the screen saver configuration options from .INI file/registry.
*
\**************************************************************************/

static void
getIniSettings()
{
// Get registry settings

if( ! ss_RegistrySetup( GetModuleHandle(NULL), IDS_INI_SECTION,
IDS_INIFILE ) )
return;

config.smooth_colors =
ss_GetRegistryInt( IDS_CONFIG_SMOOTH_COLORS, config.smooth_colors );
config.triangle_colors =
ss_GetRegistryInt( IDS_CONFIG_TRIANGLE_COLORS, config.triangle_colors );
config.cycle_colors =
ss_GetRegistryInt( IDS_CONFIG_CYCLE_COLORS, config.cycle_colors );
config.spin =
ss_GetRegistryInt( IDS_CONFIG_SPIN, config.spin );
config.bloom =
ss_GetRegistryInt( IDS_CONFIG_BLOOM, config.bloom );
config.subdiv =
ss_GetRegistryInt( IDS_CONFIG_SUBDIV, config.subdiv );
config.color_pick =
ss_GetRegistryInt( IDS_CONFIG_COLOR_PICK, config.color_pick );
config.image_size =
ss_GetRegistryInt( IDS_CONFIG_IMAGE_SIZE, config.image_size );
config.geom =
ss_GetRegistryInt( IDS_CONFIG_GEOM, config.geom );
config.two_sided =
ss_GetRegistryInt( IDS_CONFIG_TWO_SIDED, config.two_sided );
}

/******************************Public*Routine******************************\
* saveIniSettings
*
* Save the screen saver configuration option to the .INI file/registry.
*
\**************************************************************************/

static void
saveIniSettings()
{
if( ! ss_RegistrySetup( GetModuleHandle(NULL), IDS_INI_SECTION,
IDS_INIFILE ) )
return;

ss_WriteRegistryInt( IDS_CONFIG_SMOOTH_COLORS, config.smooth_colors );

ss_WriteRegistryInt( IDS_CONFIG_TRIANGLE_COLORS, config.triangle_colors );
ss_WriteRegistryInt( IDS_CONFIG_CYCLE_COLORS, config.cycle_colors );
ss_WriteRegistryInt( IDS_CONFIG_SPIN, config.spin );
ss_WriteRegistryInt( IDS_CONFIG_BLOOM, config.bloom );
ss_WriteRegistryInt( IDS_CONFIG_SUBDIV, config.subdiv );
ss_WriteRegistryInt( IDS_CONFIG_COLOR_PICK, config.color_pick );
ss_WriteRegistryInt( IDS_CONFIG_IMAGE_SIZE, config.image_size );
ss_WriteRegistryInt( IDS_CONFIG_GEOM, config.geom );
ss_WriteRegistryInt( IDS_CONFIG_TWO_SIDED, config.two_sided );
}

/******************************Public*Routine******************************\
*
* NewConfig
*
* Set up a new configuration
*
\**************************************************************************/

void NewConfig(CONFIG *cnf)
{
// Set new config
config = *cnf;

// Save to ini file
saveIniSettings();

// Reset colors
memcpy(checker_cols, base_checker_cols, sizeof(checker_cols));
memcpy(side_cols, base_side_cols, sizeof(side_cols));
memcpy(solid_cols, base_solid_cols, sizeof(solid_cols));

// Reset geometry
cur_geom = geom_table[config.geom];
cur_geom->init(cur_geom);
if (bOgl11 && !bCheckerOn) DrawWithVArrays (cur_geom);

assert(cur_geom->total_pts <= MAXPTS);

InitVlen(cur_geom, cur_geom->total_pts, cur_geom->pts);
sf = 0.0f;
sfi = cur_geom->sf_inc;
UpdatePts(cur_geom, sf);

// Reset OpenGL parameters according to configuration
// Only done if GL has been initialized
if (gbGlInit)
{
GLfloat fv4[4];

if (config.two_sided == GL_FRONT_AND_BACK)
{
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glDisable(GL_CULL_FACE);
}
else
{
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
glEnable(GL_CULL_FACE);
}

fv4[0] = fv4[1] = fv4[2] = .8f;
fv4[3] = 1.0f;
glMaterialfv(config.two_sided, GL_SPECULAR, fv4);
glMaterialf(config.two_sided, GL_SHININESS, 30.0f);
}
}

/******************************Public*Routine******************************\
*
* RegisterDialogClasses
*
* Standard screensaver hook
*
\**************************************************************************/

BOOL WINAPI RegisterDialogClasses(HANDLE hinst)
{
return TRUE;
}

// Temporary configuration for when the configuration dialog is active
// If the dialog is ok'ed then this becomes the current configuration,
// otherwise it is discarded
CONFIG temp_config;

/******************************Public*Routine******************************\
*
* ScreenSaverConfigureDialog
*
* Standard screensaver hook
*
\**************************************************************************/

BOOL CALLBACK ScreenSaverConfigureDialog(HWND hdlg, UINT msg,
WPARAM wpm, LPARAM lpm)
{
HWND hCtrl;
int i;

switch(msg)
{
case WM_INITDIALOG:

InitCommonControls();

getIniSettings();

temp_config = config;

CheckRadioButton(hdlg, ID_COL_PICK_FIRST, ID_COL_PICK_LAST,
config.color_pick);
CheckDlgButton(hdlg, ID_COL_SMOOTH, config.smooth_colors);
CheckDlgButton(hdlg, ID_COL_TRIANGLE, config.triangle_colors);
CheckDlgButton(hdlg, ID_COL_CYCLE, config.cycle_colors);
CheckDlgButton(hdlg, ID_SPIN, config.spin);
CheckDlgButton(hdlg, ID_BLOOM, config.bloom);
CheckDlgButton(hdlg, ID_TWO_SIDED,
config.two_sided == GL_FRONT_AND_BACK);

ss_SetupTrackbar( hdlg, ID_COMPLEXITY, MINSUBDIV, MAXSUBDIV,
complexity_range.step,
complexity_range.page_step,
config.subdiv);

ss_SetupTrackbar( hdlg, ID_IMAGE_SIZE, MINIMAGESIZE, MAXIMAGESIZE,
image_size_range.step,
image_size_range.page_step,
config.image_size);

hCtrl = GetDlgItem(hdlg, ID_GEOM);
SendMessage(hCtrl, CB_RESETCONTENT, 0, 0);
for (i = 0; i < IDS_GEOM_COUNT; i++)
{
LoadString( hMainInstance, i+IDS_GEOM_FIRST, geom_names[i],
sizeof(geom_names)/IDS_GEOM_COUNT );
SendMessage(hCtrl, CB_ADDSTRING, 0, (LPARAM)geom_names[i]);
}
SendMessage(hCtrl, CB_SETCURSEL, config.geom, 0);

SetFocus(GetDlgItem(hdlg, ID_COMPLEXITY));
return FALSE;

case WM_COMMAND:
switch(LOWORD(wpm))
{
case ID_COL_CHECKER:
case ID_COL_PER_SIDE:
case ID_COL_SINGLE:
temp_config.color_pick = LOWORD(wpm);
break;

case ID_COL_SMOOTH:
temp_config.smooth_colors = !temp_config.smooth_colors;
break;
case ID_COL_TRIANGLE:
temp_config.triangle_colors = !temp_config.triangle_colors;
break;
case ID_COL_CYCLE:
temp_config.cycle_colors = !temp_config.cycle_colors;
break;

case ID_SPIN:
temp_config.spin = !temp_config.spin;
break;
case ID_BLOOM:
temp_config.bloom = !temp_config.bloom;
break;
case ID_TWO_SIDED:
temp_config.two_sided =
temp_config.two_sided == GL_FRONT_AND_BACK ? GL_FRONT :
GL_FRONT_AND_BACK;
break;

case IDOK:
temp_config.subdiv =
ss_GetTrackbarPos(hdlg, ID_COMPLEXITY);
temp_config.image_size =
ss_GetTrackbarPos(hdlg, ID_IMAGE_SIZE);
temp_config.geom =
SendMessage(GetDlgItem(hdlg, ID_GEOM), CB_GETCURSEL, 0, 0);
NewConfig(&temp_config);
// Fall through
case IDCANCEL:
EndDialog(hdlg, LOWORD(wpm));
break;
}
return TRUE;

}

return FALSE;
}

/******************************Public*Routine******************************\
*
* Init
*
* Drawing initialization, called by common library
*
\**************************************************************************/

void Init(void *data)
{
GLfloat fv4[4];

gbGlInit = TRUE;

bOgl11 = ss_fOnGL11();

if (config.color_pick == ID_COL_CHECKER) bCheckerOn = TRUE;
else bCheckerOn = FALSE;

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, 1, 2, 5); // for object range -1.5 to 1.5
gluLookAt(0, 0, 3.5, 0, 0, 0, 0, 1, 0);
glMatrixMode(GL_MODELVIEW);

glEnable(GL_DEPTH_TEST);
glClearDepth(1);

glCullFace(GL_BACK);

fv4[0] = 2.0f;
fv4[1] = 2.0f;
fv4[2] = 10.0f;
fv4[3] = 1.0f;
glLightfv(GL_LIGHT0, GL_POSITION, fv4);

glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);

glEnable(GL_NORMALIZE);

// Make default configuration current
NewConfig(&config);
}

/******************************Public*Routine******************************\
* SetFloaterInfo
*
* Set the size and motion of the floating window
*
\**************************************************************************/
void
SetFloaterInfo( ISIZE *pParentSize, CHILD_INFO *pChild )
{
float sizeFact;
float sizeScale;
int size;
ISIZE *pChildSize = &pChild->size;
MOTION_INFO *pMotion = &pChild->motionInfo;

sizeScale = (float)config.image_size / 100.0f;
sizeFact = 0.25f + (0.5f * sizeScale); // range 25-75%
size = (int) (sizeFact * ( ((float)(pParentSize->width + pParentSize->height )) / 2.0f ));
SS_CLAMP_TO_RANGE2( size, 0, pParentSize->width );
SS_CLAMP_TO_RANGE2( size, 0, pParentSize->height );

pChildSize->width = pChildSize->height = size;

// Floater motion
pMotion->posInc.x = .005f * (float) size;
if( pMotion->posInc.x < 1.0f )
pMotion->posInc.x = 1.0f;
pMotion->posInc.y = pMotion->posInc.x;
pMotion->posIncVary.x = .4f * pMotion->posInc.x;
pMotion->posIncVary.y = pMotion->posIncVary.x;
}

/******************************Public*Routine******************************\
*
* FloaterFail
*
* Called when the floating window cannot be created
*
\**************************************************************************/
void FloaterFail(void *data)
{
HINSTANCE hinst;
TCHAR error_str[20];
TCHAR start_failed[80];

hinst = GetModuleHandle(NULL);
if (LoadString(hinst, IDS_ERROR,
error_str, sizeof(error_str)) &&
LoadString(hinst, IDS_START_FAILED,
start_failed, sizeof(start_failed)))
{
MessageBox(NULL, start_failed, error_str, MB_OK);
}
}

/******************************Public*Routine******************************\
*
* ss_Init
*
* Screensaver initialization routine, called at startup by common library
*
\**************************************************************************/

SSContext *ss_Init(void)
{
getIniSettings();

ss_InitFunc(Init);
ss_UpdateFunc(Update);

gssc.bFloater = TRUE;
gssc.floaterInfo.bMotion = TRUE;
gssc.floaterInfo.ChildSizeFunc = SetFloaterInfo;

gssc.bDoubleBuf = TRUE;
gssc.depthType = SS_DEPTH16;

return &gssc;
}


/**************************************************************************\
* ConfigInit
*
* Dialog box version of ss_Init. Used for setting up any gl drawing on the
* dialog.
*
\**************************************************************************/
BOOL
ss_ConfigInit( HWND hDlg )
{
return TRUE;
}