[BACK]Return to shadows.c CVS log [TXT][DIR] Up to [Development] / performer / src / pguide / libpf / C

File: [Development] / performer / src / pguide / libpf / C / shadows.c (download)

Revision 1.1, Tue Nov 21 21:39:37 2000 UTC (16 years, 11 months ago) by flynnt
Branch: MAIN
CVS Tags: HEAD

Initial check-in based on OpenGL Performer 2.4 tree.
-flynnt

/*
 * Copyright 1995-1996-1997, Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED
 *
 * This source code ("Source Code") was originally derived from a
 * code base owned by Silicon Graphics, Inc. ("SGI")
 * 
 * LICENSE: SGI grants the user ("Licensee") permission to reproduce,
 * distribute, and create derivative works from this Source Code,
 * provided that: (1) the user reproduces this entire notice within
 * both source and binary format redistributions and any accompanying
 * materials such as documentation in printed or electronic format;
 * (2) the Source Code is not to be used, or ported or modified for
 * use, except in conjunction with OpenGL Performer; and (3) the
 * names of Silicon Graphics, Inc.  and SGI may not be used in any
 * advertising or publicity relating to the Source Code without the
 * prior written permission of SGI.  No further license or permission
 * may be inferred or deemed or construed to exist with regard to the
 * Source Code or the code base of which it forms a part. All rights
 * not expressly granted are reserved.
 * 
 * This Source Code is provided to Licensee AS IS, without any
 * warranty of any kind, either express, implied, or statutory,
 * including, but not limited to, any warranty that the Source Code
 * will conform to specifications, any implied warranties of
 * merchantability, fitness for a particular purpose, and freedom
 * from infringement, and any warranty that the documentation will
 * conform to the program, or any warranty that the Source Code will
 * be error free.
 * 
 * IN NO EVENT WILL SGI BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT
 * LIMITED TO DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES,
 * ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED WITH THE
 * SOURCE CODE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT OR
 * OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM,
 * OR AROSE OUT OF USE OR RESULTS FROM USE OF, OR LACK OF ABILITY TO
 * USE, THE SOURCE CODE.
 * 
 * Contact information:  Silicon Graphics, Inc., 
 * 1600 Amphitheatre Pkwy, Mountain View, CA  94043, 
 * or:  http://www.sgi.com
 *
 * shadows.c: Performer program to demonstrate use of pfLightSource shadowing
 *		and projected texturing.
 *        Based on simple.c
 *
 * $Revision: 1.1 $ $Date: 2000/11/21 21:39:37 $ 
 *
 */

static int Floor= 0;
static Ambient = 0;
static int SpotOne=0;
static int SpotTwo=0;
static int Whites=0;
static int Shadow=0;
static int SpotTexture = 1;
static int enable_shadows = 1; /* disable shadows in HW does not handle it */

#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <Performer/pf.h>
#include <Performer/pfdu.h>
#include <Performer/pfutil.h>

/*------------------------------------------------------------------*/


static int FBAttrs[] = {
    PFFB_RGBA,
    PFFB_DOUBLEBUFFER,
    PFFB_DEPTH_SIZE, 1,
    None,
};

char ProgName[PF_MAXSTRING];

void 
OpenPipeWin (pfPipeWindow *pw)
{
    pfLightModel	*lm;

    pfOpenPWin(pw);

    lm = pfNewLModel(NULL);
    pfLModelLocal(lm, 1);
    pfApplyLModel(lm);

    pfCullFace(PFCF_BACK);
}

/* ARGSUSED */
void 
DrawChannel (pfChannel *chan, void *data)
{
    pfVec4	clr;
    pfSetVec4(clr, .2f, .2f, .2f, 1.0f); 
    pfClear(PFCL_COLOR|PFCL_DEPTH, clr);
    pfDraw();
}

/*
 *	Usage() -- print usage advice and exit. This
 *      procedure is executed in the application process.
 */
static void
Usage (void)
{
    pfNotify(PFNFY_NOTICE, PFNFY_USAGE, "Usage: shadows [-1 -2 -a -s -f -w -t] file.ext ...\n");
    pfNotify(PFNFY_NOTICE, PFNFY_USAGE, "       -1 : enable projected texture 1 \n");
    pfNotify(PFNFY_NOTICE, PFNFY_USAGE, "       -2 : enable projected texture 2 \n");
    pfNotify(PFNFY_NOTICE, PFNFY_USAGE, "       -a : enable regular light\n");
    pfNotify(PFNFY_NOTICE, PFNFY_USAGE, "       -s : enable shadow \n");
    pfNotify(PFNFY_NOTICE, PFNFY_USAGE, "       -f : use the texture floor5.rgb on the floor\n");
    pfNotify(PFNFY_NOTICE, PFNFY_USAGE, "       -w : use white spots lights \n");
    pfNotify(PFNFY_NOTICE, PFNFY_USAGE, "       -t : use texture myspot.tex for the projected textures\n");
    pfNotify(PFNFY_NOTICE, PFNFY_USAGE, "Default: -1 -2 -a -s -f\n");

    exit(1);
}

static void
Default(void)
{
    SpotOne = SpotTwo = Shadow = Ambient = Floor = 1;
}

/*
*	docmdline() -- use getopt to get command-line arguments, 
*	executed at the start of the application process.
*/

static int
docmdline(int argc, char *argv[])
{
    int	    opt;

    strcpy(ProgName, argv[0]);
    
    /* process command-line arguments */
    while ((opt = getopt(argc, argv, "12asfwt")) != -1)
    {
	switch (opt)
	{
	case '1': 
	    SpotOne = 1;
	    break;
	case '2': 
	    SpotTwo = 1;
	    break;
	case 's':
	    Shadow = 1;
	    break;
	case 'f': 
	    Floor = 1;
	    break;
	case 't': 
	    SpotTexture = 1;
	    break;
	case 'a': 
	    Ambient = 1;
	    break;
	case 'w':
	    Whites = 1;
	    break;
	case 'h': 
	    Usage();
	}
    }
    return optind;
}


/* Compute matrix so +Y axis of DCS is pointing at 'at' */
static void
lookAt(pfDCS *dcs, pfVec3 at)
{
    pfVec3		v, pos;
    pfMatrix		mat;

    /* Light sources point down -z axis by default, so we want to rotate the
       dcs with the light source under it so that its -z axis points at the
       specified point
       */
    static pfVec3	zaxis = {0.0f, 0.0f, -1.0f};	
    
    /* v is origin of dcs */
    pfGetMatRowVec3(*pfGetDCSMatPtr(dcs), 3, pos); 
    
    /* v is direction vector from dcs to 'at' */
    pfSubVec3(v, at, pos);
    pfNormalizeVec3(v);

    /* Rotate y-axis onto v */
    pfMakeVecRotVecMat(mat, zaxis, v);
    pfSetMatRowVec3(mat, 3, pos); /* restore dcs origin */
    pfDCSMat(dcs, mat);
}

#define SPOT_SIZE	256
#define SPOT_RADIUS	64

/*
 * Create circular image for projected spotlight texture.
*/
static pfTexture*
initSpotTex(void)
{
    long            	i, j;
    pfTexture		*tex;
    unsigned short 	*image;

    image = pfMalloc(sizeof(unsigned short) * SPOT_SIZE * SPOT_SIZE,
		     pfGetSharedArena());

    for (i = 0; i < SPOT_SIZE / 2; i++)
    {
	for (j = 0; j < SPOT_SIZE / 2; j++)
	{
	    unsigned long   val;

	    val = 0;

	    /* See if corners of texel are within circle. This antialiases
	     * the edge of the circle somewhat. */
	    if ((i * i + j * j) <= SPOT_RADIUS * SPOT_RADIUS)
		val += 0xffff;
	    if (((i + 1) * (i + 1) + j * j) <= SPOT_RADIUS * SPOT_RADIUS)
		val += 0xffff;
	    if (((i + 1) * (i + 1) + (j + 1) * (j + 1)) <= SPOT_RADIUS * SPOT_RADIUS)
		val += 0xffff;
	    if ((i * i + (j + 1) * (j + 1)) <= SPOT_RADIUS * SPOT_RADIUS)
		val += 0xffff;

	    val >>= 2;
	    /* set alpha to 0 for openGL */
	    val &=  0xfff0;

	    /* Set all 4 quadrants to val */
	    image[(SPOT_SIZE / 2 - i) * SPOT_SIZE + (SPOT_SIZE / 2 - j)] =
		image[(SPOT_SIZE / 2 - i) * SPOT_SIZE + (SPOT_SIZE / 2 + j)] =
		image[(SPOT_SIZE / 2 + i) * SPOT_SIZE + (SPOT_SIZE / 2 + j)] =
		image[(SPOT_SIZE / 2 + i) * SPOT_SIZE + (SPOT_SIZE / 2 - j)] =
		(unsigned short) val;

	}
    }

    tex = pfNewTex(pfGetSharedArena());
    pfTexImage(tex, (uint*) image, 2, SPOT_SIZE, SPOT_SIZE, 0);
    pfTexFormat(tex, PFTEX_INTERNAL_FORMAT, PFTEX_IA_8);
    pfTexRepeat(tex, PFTEX_WRAP, PFTEX_CLAMP);

    /*
	pfSaveTexFile(tex, "myspot.tex");
    */
    return tex;
}

#define FOV		45.0f
#define NEAR		1.0f
#define SQRT2INV	.7071067811865475244

static pfDCS 		*shadDCS, *projDCS, *proj2DCS;
static pfLightSource 	*shad, *proj, *proj2;

static pfGroup*
initLSources(pfSphere *bsphere)
{
    pfFrustum 		*shadFrust;
    pfTexture 		*tex;
    pfGroup		*group;
    float		d;
    pfMatrix		mat;
    void		*arena = pfGetSharedArena();

    /* Create and configure shadow frustum. Fit frustum tightly to scene. */
    shadFrust = pfNewFrust(arena);
    
    pfMakeSimpleFrust(shadFrust, FOV);

    d = bsphere->radius / sinf(PF_DEG2RAD(FOV/2.0f));
    d = PF_MAX2(d, NEAR + bsphere->radius);

    pfFrustNearFar(shadFrust, NEAR, d + 1.1f * bsphere->radius);

    if (enable_shadows)
    {
    	/* Create and configure white shadow casting light source */
    	shad = pfNewLSource();
    	pfLSourceMode(shad, PFLS_SHADOW_ENABLE, 1);
    	pfLSourceAttr(shad, PFLS_PROJ_FRUST, shadFrust);
    	pfLSourceColor(shad, PFLT_DIFFUSE, 1.0f, 1.0f, 1.0f);
    	pfLSourceVal(shad, PFLS_INTENSITY, .2f);   /* shadow not complety dark */
    	pfLSourcePos(shad, 0.0f, 0.0f, 0.0f, 1.0f);	/* Make light local */
	pfLSourceVal(shad, PFLS_SHADOW_SIZE, SPOT_SIZE);
    }

    /* Create and configure blue spotlight */
    proj = pfNewLSource();
    if (SpotTexture)
        tex = initSpotTex();
    else
    {
       tex = pfNewTex(arena);
       pfLoadTexFile(tex, "myspot.tex");
       pfTexRepeat(tex, PFTEX_WRAP, PFTEX_CLAMP);
    }

    pfLSourceMode(proj, PFLS_PROJTEX_ENABLE, 1);
    pfLSourceAttr(proj, PFLS_PROJ_FRUST, shadFrust);
    pfLSourceAttr(proj, PFLS_PROJ_TEX, tex);
    if (Whites) {
      pfLSourceColor(proj, PFLT_DIFFUSE, 1.0f, 1.0f, 1.0f);
      pfLSourceColor(proj, PFLT_SPECULAR, 1.0f, 1.0f, 1.0f);
    }
    else {
      pfLSourceColor(proj, PFLT_DIFFUSE, 0.0f, 0.0f, 1.0f);
      pfLSourceColor(proj, PFLT_SPECULAR, 0.0f, 0.0f, 1.0f);
    }

    pfLSourceColor(proj, PFLT_AMBIENT, 0.0f, 0.0f, 0.0f);
    pfLSourceVal(proj, PFLS_INTENSITY, 0.5f);
    pfLSourcePos(proj, 0.0f, 0.0f, 0.0f, 1.0f);	/* Make light local */

    group = pfNewGroup();

    /* Make DCSes to move lights around */
    if (enable_shadows)
    {

    	shadDCS = pfNewDCS();

    	pfMakeTransMat(mat, d*SQRT2INV, 0.0f, d*SQRT2INV);
    	pfDCSMat(shadDCS, mat);
    	lookAt(shadDCS, bsphere->center);

    	pfAddChild(shadDCS, shad);
	if (Shadow)
    	    pfAddChild(group, shadDCS);
    }

    projDCS = pfNewDCS();

    pfMakeTransMat(mat, -d*SQRT2INV, 0.0f, d*SQRT2INV);
    pfDCSMat(projDCS, mat);
    lookAt(projDCS, bsphere->center);

    pfAddChild(projDCS, proj);
    if (SpotOne)
        pfAddChild(group, projDCS);
    if (SpotTwo)
    {
	/* Create and configure red spotlight */
	proj2 = pfNewLSource();
	pfLSourceMode(proj2, PFLS_PROJTEX_ENABLE, 1);
	pfLSourceAttr(proj2, PFLS_PROJ_FRUST, shadFrust);
	pfLSourceAttr(proj2, PFLS_PROJ_TEX, tex);
	if (Whites) {
	  pfLSourceColor(proj2, PFLT_DIFFUSE, 1.0f, 1.0f, 1.0f);
	  pfLSourceColor(proj2, PFLT_SPECULAR, 1.0f, 1.0f, 1.0f);
	}
	else {
	  pfLSourceColor(proj2, PFLT_DIFFUSE, 1.0f, 0.0f, 0.0f);
	  pfLSourceColor(proj2, PFLT_SPECULAR, 1.0f, 0.0f, 0.0f);
	}
	pfLSourceColor(proj2, PFLT_AMBIENT, 0.0f, 0.0f, 0.0f);
	pfLSourceVal(proj2, PFLS_INTENSITY, .5f);
	pfLSourcePos(proj2, 0.0f, 0.0f, 0.0f, 1.0f); /* Make light local */

	proj2DCS = pfNewDCS();
	pfMakeTransMat(mat, d*SQRT2INV, 0.0f , d*SQRT2INV);
	pfDCSMat(proj2DCS, mat);
	lookAt(proj2DCS, bsphere->center);
	pfAddChild(proj2DCS, proj2);
	pfAddChild(group,proj2DCS);
    }
    /* Add a regular white light */
    if (Ambient)
        pfAddChild(group, pfNewLSource());

    return group;
}


static pfGeode*
initFloor(pfSphere *bsphere)
{
    pfGeoSet	*gset;
    pfGeoState	*gstate;
    pfGeode	*geode;
    pfVec4 *c;
    pfVec3	*v;
    pfMaterial	*mtl;
    float	d;
    void	*arena = pfGetSharedArena();
    pfVec2 *t;
    pfTexture *tex;
    pfTexEnv  *tev;

    gset = pfNewGSet(arena);
    gstate = pfNewGState(arena);
    geode = pfNewGeode();
    
    v = (pfVec3*)pfMalloc(sizeof(pfVec3) * 4, arena);
    d = 1.7f * bsphere->radius; 
    

    pfSetVec3(v[0], -d, -d, -d/3.0f);
    pfSetVec3(v[1],  d, -d, -d/3.0f);
    pfSetVec3(v[2],  d,  d, -d/3.0f);
    pfSetVec3(v[3], -d,  d, -d/3.0f);
    pfGSetAttr(gset, PFGS_COORD3, PFGS_PER_VERTEX, v, NULL);

    v = (pfVec3*)pfMalloc(sizeof(pfVec3), arena);
    pfSetVec3(v[0], 0.0f, 0.0f, 1.0f);
    pfGSetAttr(gset, PFGS_NORMAL3, PFGS_OVERALL, v, NULL);

    if (Floor)
    {
      	c = (pfVec4*) pfMalloc(sizeof(pfVec4) , pfGetSharedArena());
 
	c[0][0] = 0.5;
	c[0][1] = 0.5;
	c[0][2] = 0.5;
	c[0][3] = 1.;
 
	pfGSetAttr(gset, PFGS_COLOR4, PFGS_OVERALL, c, NULL);

	t = (pfVec2*) pfMalloc(sizeof(pfVec2) *4, pfGetSharedArena());
	t[0][0] = 0.; t[0][1] = 0.;
	t[1][0] = 1.; t[1][1] = 0.;
	t[2][0] = 1.; t[2][1] = 1.;
	t[3][0] = 0.; t[3][1] = 1.;
	
	pfGSetAttr(gset, PFGS_TEXCOORD2, PFGS_PER_VERTEX, t, NULL);
    }
     else
    {

	c = (pfVec4*) pfMalloc(sizeof(pfVec4) , pfGetSharedArena());
 
	c[0][0] = 0.5;
	c[0][1] = 0.5;
	c[0][2] = 0.5;
	c[0][3] = 1.;
 
	pfGSetAttr(gset, PFGS_COLOR4, PFGS_OVERALL, c, NULL);
    }


    pfGSetPrimType(gset, PFGS_QUADS);
    pfGSetNumPrims(gset, 1);

    if (Floor)
    {
	tex = pfNewTex(pfGetSharedArena());
	pfLoadTexFile(tex,"floor5.rgb");

	pfTexFormat(tex,PFTEX_INTERNAL_FORMAT, PFTEX_RGB_8);
	pfTexRepeat(tex,PFTEX_WRAP, PFTEX_REPEAT);
       
	pfGStateAttr(gstate, PFSTATE_TEXTURE, tex);
	pfGStateMode(gstate, PFSTATE_ENTEXTURE, 1);

	tev = pfNewTEnv(pfGetSharedArena());

	pfTEnvMode(tev, PFTE_DECAL);
	pfGStateAttr(gstate,PFSTATE_TEXENV, tev);

	mtl = pfNewMtl(arena);    
	pfMtlColorMode(mtl, PFMTL_BOTH, PFMTL_CMODE_OFF);
	pfMtlColor(mtl,PFMTL_AMBIENT, 0.0, 0.0, 0.0);
	pfGStateAttr(gstate, PFSTATE_FRONTMTL, mtl);
	pfGStateMode(gstate, PFSTATE_ENLIGHTING, 1);
    }

    pfGSetGState(gset, gstate);
    pfAddGSet(geode, gset);

    return geode;
}

int
main (int argc, char *argv[])
{
    int loop;
    
    pfScene     *scene;
    pfNode	*root;
    pfDCS	*dcs;
    pfPipe      *p;
    pfPipeWindow *pw;
    pfChannel   *chan;
    pfSphere 	bsphere;
    pfCoord	view;
    pfMatrix	mat, orbit1, orbit2, orbit3;
    pfList	*texList;
    int		ret;
    int 	arg = 1;
    int		found;
    pfGeoState  *gstate;
    pfLightModel *lm;

    if (argc < 2)
	Usage();
    if (argc == 2)
	Default();
    else
	arg = docmdline(argc,argv);


    /* Initialize Performer */
    pfInit();	


    pfQueryFeature(PFQFTR_TEXTURE_PROJECTIVE, &ret);
    if (!(ret == PFQFTR_FAST))
    {
	printf("Sorry, Projected Light Sources are not supported \n");
	return 0;
    }

    if (Shadow)
    {
	pfQueryFeature(PFQFTR_TEXTURE_SHADOW, &ret);
	if (!(ret == PFQFTR_FAST))
	{
	    printf("Sorry, Shadow are not supported !\n");
	    enable_shadows = 0;
	}
    } else
    enable_shadows = 0;

    /* Use default multiprocessing mode based on number of
     * processors.
     */
    pfMultiprocess(PFMP_DEFAULT); 

    /* Load all loader DSO's before pfConfig() forks */
    for (found = arg; found < argc; found++)
        pfdInitConverter(argv[found]);

    /* Configure multiprocessing mode and start parallel
     * processes.
     */
    pfConfig();			

    /* Append to PFPATH additional standard directories where 
     * geometry and textures exist 
     */
    pfFilePath(".:/usr/share/Performer/data");

    /* Do not use FLAT_ primitives because they look bad 
     * with local lighting.
    */
    pfdBldrMode(PFDBLDR_MESH_LOCAL_LIGHTING, 1);

    /* load files named by command line arguments */
    scene = pfNewScene();
    dcs = pfNewDCS();
    pfMakeRotMat(mat, 30.0f, 1.0f, 0.0f, 0.0f);
    pfDCSMat(dcs, mat);
    pfAddChild(scene, dcs);

    for (found = 0; arg < argc; arg++)
    {
        if ((root = pfdLoadFile(argv[arg])) != NULL)
        {
            pfAddChild(dcs, root);
            found++;
        }
    }

    if (!found)
    {
	pfNotify(PFNFY_FATAL,PFNFY_USAGE,"Error loading database files");
	exit(-1);
    }


    /* determine extent of scene's geometry */
    pfGetNodeBSphere (scene, &bsphere);

    /* Create pfLightSources and add to scene */
    pfAddChild(scene, initLSources(&bsphere));

    /* Create "floor" for shadows */
    pfAddChild(dcs, initFloor(&bsphere));

    /* Configure and open GL window */
    p = pfGetPipe(0);
    pw = pfNewPWin(p);
    pfPWinName(pw, argv[0]);
    pfPWinConfigFunc(pw, OpenPipeWin);	
    pfPWinOriginSize(pw, 0, 0, 500, 500);
    pfPWinType(pw, PFPWIN_TYPE_X);
    pfPWinFBConfigAttrs(pw, FBAttrs);
    pfConfigPWin(pw);

    /* Create and configure a pfChannel. */
    chan = pfNewChan(p);	
    pfChanScene(chan, scene);
    pfChanNearFar(chan, 1.0f, 5.0f * bsphere.radius);
    pfChanFOV(chan, 45.0f, 0.0f);
    pfCopyVec3(view.xyz, bsphere.center);
    view.xyz[PF_Y] -= 3.0f * bsphere.radius;
    pfSetVec3(view.hpr, 0.0f, 0.0f, 0.0f); 
    pfChanView(chan, view.xyz, view.hpr);
    pfChanTravFunc(chan, PFTRAV_DRAW, DrawChannel);
    
    gstate = pfNewGState(pfGetSharedArena());
    lm = pfNewLModel(pfGetSharedArena());
    
    pfLModelAmbient(lm, 0, 0, 0);
    pfGStateMode(gstate, PFSTATE_ENLIGHTING, 1);
    pfGStateAttr(gstate, PFSTATE_LIGHTMODEL, lm);
    pfChanGState(chan, gstate);

    /* If scene is non-textured then we can save an entire rendering pass */
    texList = pfuMakeSceneTexList(scene);
    if (pfGetNum(texList) == 0)
    {
	pfChanTravMode(chan, PFTRAV_MULTIPASS, 
		       PFMPASS_NONTEX_SCENE | 
		       PFMPASS_EMISSIVE_PASS |
		       PFMPASS_BLACK_PASS);
	pfNotify(PFNFY_NOTICE, PFNFY_PRINT, 
		 "Scene is non-textured! Using PFMPASS_NONTEX_SCENE "
		 "optimization");
    }
    else {
      pfChanTravMode(chan, PFTRAV_MULTIPASS, 
		     PFMPASS_EMISSIVE_PASS | PFMPASS_BLACK_PASS);
    }

    pfMakeRotMat(orbit1, 1.3f, 0.0f, 0.0f, 1.0f);
    pfMakeRotMat(orbit2, 0.7f, 0.0f, 0.0f, 1.0f);
    pfMakeRotMat(orbit3, -1.2f, 0.f, 0.0f, 1.0f);

    /* Simulate for ever ! */
    loop = 0;
    while (1)
    {
	loop ++;
	/* Spin light sources around object */
	if (SpotOne)
	{
	    pfMultMat(mat, *pfGetDCSMatPtr(projDCS), orbit1);
	    pfDCSMat(projDCS, mat);
	    lookAt(projDCS, bsphere.center);
	}
	if (SpotTwo)
	{
	    pfMultMat(mat, *pfGetDCSMatPtr(proj2DCS), orbit2);
	    pfDCSMat(proj2DCS, mat);
	    lookAt(proj2DCS, bsphere.center);
	}
	if (enable_shadows)
	{
		pfMultMat(mat, *pfGetDCSMatPtr(shadDCS), orbit3);
		pfDCSMat(shadDCS, mat);
		lookAt(shadDCS, bsphere.center);
	}

	/* Initiate cull/draw for this frame. */
	pfFrame();		
   {
   int err;
   if ((err = glGetError()) != GL_NO_ERROR)
        pfNotify(PFNFY_NOTICE,PFNFY_USAGE,"OpenGL Error 0x%x - %s",err, gluErrorString(err));
   }

    }

    /*  press Ctrl-C to exit ! */
}