Пример #1
0
ssgBranch * cGrStars::build( int num, sgdVec3 *star_data, double star_dist )
{
  sgVec4 color;

  // clean-up previous
  ssgDeRefDelete( stars_transform );

  // create new
  stars_transform = new ssgTransform;
  stars_transform->ref();

  if ( star_data == NULL )
  {
	  if (num > 0)
		  ulSetError(UL_WARNING, "null star data passed to cGrStars::build()");
	  else
		  return stars_transform;
  }

  // set up the orb state
  state = new ssgSimpleState();
  state->disable( GL_LIGHTING );
  state->disable( GL_CULL_FACE );
  state->disable( GL_TEXTURE_2D );
  state->enable( GL_COLOR_MATERIAL );
  state->setColourMaterial( GL_AMBIENT_AND_DIFFUSE );
  state->setMaterial( GL_EMISSION, 0, 0, 0, 1 );
  state->setMaterial( GL_SPECULAR, 0, 0, 0, 1 );
  state->enable( GL_BLEND );
  state->disable( GL_ALPHA_TEST );

  vl = new ssgVertexArray( num );
  cl = new ssgColourArray( num );
  // cl = new ssgColourArray( 1 );
  // sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
  // cl->add( color );

  // Build ssg structure
  sgVec3 p;
  for ( int i = 0; i < num; ++i ) 
  {
    // position seeded to arbitrary values
    sgSetVec3( p, 
      (float)( star_dist * cos( star_data[i][0] )
        * cos( star_data[i][1] )),
      (float)( star_dist * sin( star_data[i][0] )
        * cos( star_data[i][1] )),
      (float)( star_dist * sin( star_data[i][1] )));
    vl->add( p );

    // color (magnitude)
    sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
    cl->add( color );
  }

  ssgLeaf *stars_obj = 
    new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl );
  stars_obj->setState( state );
  stars_obj->setCallback( SSG_CALLBACK_PREDRAW, grStarPreDraw );
  stars_obj->setCallback( SSG_CALLBACK_POSTDRAW, grStarPostDraw );

  stars_transform->addKid( stars_obj );

  return stars_transform;
}
Пример #2
0
bool cGrCloudLayer::reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt )
{
  sgMat4 T1, LON, LAT;
  sgVec3 axis;

  // combine p and asl (meters) to get translation offset
  sgVec3 asl_offset;
  sgCopyVec3( asl_offset, up );
  sgNormalizeVec3( asl_offset );
  if ( alt <= layer_asl ) 
  {
    sgScaleVec3( asl_offset, layer_asl );
  }
  else 
  {
    sgScaleVec3( asl_offset, layer_asl + layer_thickness );
  }
  sgAddVec3( asl_offset, p );

  // Translate to zero elevation
  sgMakeTransMat4( T1, asl_offset );

  // Rotate to proper orientation
  sgSetVec3( axis, 0.0, 0.0, 1.0 );
  sgMakeRotMat4( LON, (float)(lon * SGD_RADIANS_TO_DEGREES), axis );

  sgSetVec3( axis, 0.0, 1.0, 0.0 );
  sgMakeRotMat4( LAT, (float)(90.0 - lat * SGD_RADIANS_TO_DEGREES), axis );

  sgMat4 TRANSFORM;

  sgCopyMat4( TRANSFORM, T1 );
  sgPreMultMat4( TRANSFORM, LON );
  sgPreMultMat4( TRANSFORM, LAT );

  sgCoord layerpos;
  sgSetCoord( &layerpos, TRANSFORM );

  layer_transform->setTransform( &layerpos );

  // now calculate update texture coordinates
  if ( last_lon < -900 ) 
  {
    last_lon = lon;
    last_lat = lat;
  }

  double sp_dist = speed*dt;

  if ( lon != last_lon || lat != last_lat || sp_dist != 0 ) 
  {
    double course = 0.0, dist = 0.0;
    if ( lon != last_lon || lat != last_lat ) 
    {
	sgVec2 start, dest;
	sgSetVec2(start, (float)last_lon, (float)last_lat);
	sgSetVec2(dest, (float)lon, (float)lat);
	calc_gc_course_dist( dest, start, &course, &dist );
    }

    // calculate cloud movement
    double ax = 0.0, ay = 0.0, bx = 0.0, by = 0.0;

    if (dist > 0.0) 
    {
      ax = cos(course) * dist;
      ay = sin(course) * dist;
    }

    if (sp_dist > 0) 
    {
      bx = cos(-direction * SGD_DEGREES_TO_RADIANS) * sp_dist;
      by = sin(-direction * SGD_DEGREES_TO_RADIANS) * sp_dist;
    }

    float xoff = (float)((ax + bx) / (2 * scale));
    float yoff = (float)((ay + by) / (2 * scale));

    const float layer_scale = layer_span / scale;

    float *base, *tc;

    base = tl[0]->get( 0 );
    base[0] += xoff;

    if ( base[0] > -10.0 && base[0] < 10.0 ) 
    {
      base[0] -= (int)base[0];
    }
    else 
    {
      base[0] = 0.0;
	  ulSetError(UL_WARNING, "Warning: base1\n");
    }

    base[1] += yoff;

    if ( base[1] > -10.0 && base[1] < 10.0 ) 
    {
      base[1] -= (int)base[1];
    }
    else 
    {
      base[1] = 0.0;
	  ulSetError(UL_WARNING, "Warning: base2\n");
    }

    for (int i = 0; i < 4; i++) 
    {
      tc = tl[i]->get( 0 );
      sgSetVec2( tc, base[0] + layer_scale * i/4, base[1] );

      for (int j = 0; j < 4; j++) 
      {
        tc = tl[i]->get( j*2+1 );
        sgSetVec2( tc, base[0] + layer_scale * (i+1)/4,
        base[1] + layer_scale * j/4 );

        tc = tl[i]->get( (j+1)*2 );
        sgSetVec2( tc, base[0] + layer_scale * i/4,
        base[1] + layer_scale * (j+1)/4 );
      }

      tc = tl[i]->get( 9 );
      sgSetVec2( tc, base[0] + layer_scale * (i+1)/4,
      base[1] + layer_scale );
    }

    last_lon = lon;
    last_lat = lat;
  }

  return true;
}
Пример #3
0
static void
initBackground(void)
{
    int			i;
    float		x, y, z1, z2;
    double		alpha;
    float		texLen;
    tTrackGraphicInfo	*graphic;
    ssgSimpleState	*envst;
    sgVec3		vtx;
    sgVec4		clr;
    sgVec3		nrm;
    sgVec2		tex;
    static char		buf[1024];
    ssgVtxTable 	*bg;
    ssgVertexArray	*bg_vtx;
    ssgTexCoordArray	*bg_tex;
    ssgColourArray	*bg_clr;
    ssgNormalArray	*bg_nrm;
    ssgSimpleState	*bg_st;
    
    sprintf(buf, "tracks/%s/%s;data/img;data/textures;.", grTrack->category, grTrack->internalname);
    grFilePath = buf;
    grGammaValue = 1.8;
    grMipMap = 0;

    graphic = &grTrack->graphic;
    glClearColor(graphic->bgColor[0], graphic->bgColor[1], graphic->bgColor[2], 1.0);
    BackgroundTex = BackgroundTex2 = 0;

    TheBackground = new ssgRoot();
    clr[0] = clr[1] = clr[2] = 1.0;
    clr[3] = 1.0;
    nrm[0] = nrm[2] = 0.0;
    nrm[1] = 1.0;

    z1 = -0.5;
    z2 = 1.0;
    BackgroundType = graphic->bgtype;
    switch (BackgroundType) {
    case 0:
	bg_vtx = new ssgVertexArray(NB_BG_FACES + 1);
	bg_tex = new ssgTexCoordArray(NB_BG_FACES + 1);
	bg_clr = new ssgColourArray(1);
	bg_nrm = new ssgNormalArray(1);

	bg_clr->add(clr);
	bg_nrm->add(nrm);
	
	for (i = 0; i < NB_BG_FACES + 1; i++) {
	    alpha = (float)i * 2 * PI / (float)NB_BG_FACES;
	    texLen = (float)i / (float)NB_BG_FACES;

	    x = BG_DIST * cos(alpha);
	    y = BG_DIST * sin(alpha);
	    
	    vtx[0] = x;
	    vtx[1] = y;
	    vtx[2] = z1;
	    bg_vtx->add(vtx);
	    tex[0] = texLen*4.0;
	    tex[1] = 0;
	    bg_tex->add(tex);

	    vtx[0] = x;
	    vtx[1] = y;
	    vtx[2] = z2;
	    bg_vtx->add(vtx);
	    tex[0] = texLen*4.0;
	    tex[1] = 1.0;
	    bg_tex->add(tex);
	}
	bg = new ssgVtxTable(GL_TRIANGLE_STRIP, bg_vtx, bg_nrm, bg_tex, bg_clr);
	bg_st = (ssgSimpleState*)grSsgLoadTexState(graphic->background);
	bg_st->disable(GL_LIGHTING);
	bg->setState(bg_st);
	bg->setCullFace(0);
	TheBackground->addKid(bg);
	break;

    case 2:
	bg_vtx = new ssgVertexArray(NB_BG_FACES + 1);
	bg_tex = new ssgTexCoordArray(NB_BG_FACES + 1);
	bg_clr = new ssgColourArray(1);
	bg_nrm = new ssgNormalArray(1);

	bg_clr->add(clr);
	bg_nrm->add(nrm);

	for (i = 0; i < NB_BG_FACES / 4 + 1; i++) {
	    alpha = (float)i * 2 * PI / (float)NB_BG_FACES;
	    texLen = (float)i / (float)NB_BG_FACES;
	    
	    x = BG_DIST * cos(alpha);
	    y = BG_DIST * sin(alpha);
	    
	    vtx[0] = x;
	    vtx[1] = y;
	    vtx[2] = z1;
	    bg_vtx->add(vtx);
	    tex[0] = texLen*4.0;
	    tex[1] = 0;
	    bg_tex->add(tex);

	    vtx[0] = x;
	    vtx[1] = y;
	    vtx[2] = z2;
	    bg_vtx->add(vtx);
	    tex[0] = texLen*4.0;
	    tex[1] = 0.5;
	    bg_tex->add(tex);
	}
	bg = new ssgVtxTable(GL_TRIANGLE_STRIP, bg_vtx, bg_nrm, bg_tex, bg_clr);
	bg_st = (ssgSimpleState*)grSsgLoadTexState(graphic->background);
	bg_st->disable(GL_LIGHTING);
	bg->setState(bg_st);
	bg->setCullFace(0);
	TheBackground->addKid(bg);

	bg_vtx = new ssgVertexArray(NB_BG_FACES + 1);
	bg_tex = new ssgTexCoordArray(NB_BG_FACES + 1);
	bg_clr = new ssgColourArray(1);
	bg_nrm = new ssgNormalArray(1);

	bg_clr->add(clr);
	bg_nrm->add(nrm);

	for (i = NB_BG_FACES/4; i < NB_BG_FACES / 2 + 1; i++) {
	    alpha = (float)i * 2 * PI / (float)NB_BG_FACES;
	    texLen = (float)i / (float)NB_BG_FACES;
	    
	    x = BG_DIST * cos(alpha);
	    y = BG_DIST * sin(alpha);
	    
	    vtx[0] = x;
	    vtx[1] = y;
	    vtx[2] = z1;
	    bg_vtx->add(vtx);
	    tex[0] = texLen*4.0;
	    tex[1] = 0.5;
	    bg_tex->add(tex);

	    vtx[0] = x;
	    vtx[1] = y;
	    vtx[2] = z2;
	    bg_vtx->add(vtx);
	    tex[0] = texLen*4.0;
	    tex[1] = 1.0;
	    bg_tex->add(tex);
	}
	bg = new ssgVtxTable(GL_TRIANGLE_STRIP, bg_vtx, bg_nrm, bg_tex, bg_clr);
	bg_st = (ssgSimpleState*)grSsgLoadTexState(graphic->background);
	bg_st->disable(GL_LIGHTING);
	bg->setState(bg_st);
	bg->setCullFace(0);
	TheBackground->addKid(bg);

	bg_vtx = new ssgVertexArray(NB_BG_FACES + 1);
	bg_tex = new ssgTexCoordArray(NB_BG_FACES + 1);
	bg_clr = new ssgColourArray(1);
	bg_nrm = new ssgNormalArray(1);

	bg_clr->add(clr);
	bg_nrm->add(nrm);

	for (i = NB_BG_FACES / 2; i < 3 * NB_BG_FACES / 4 + 1; i++) {
	    alpha = (float)i * 2 * PI / (float)NB_BG_FACES;
	    texLen = (float)i / (float)NB_BG_FACES;
	    
	    x = BG_DIST * cos(alpha);
	    y = BG_DIST * sin(alpha);
	    
	    vtx[0] = x;
	    vtx[1] = y;
	    vtx[2] = z1;
	    bg_vtx->add(vtx);
	    tex[0] = texLen*4.0;
	    tex[1] = 0.0;
	    bg_tex->add(tex);

	    vtx[0] = x;
	    vtx[1] = y;
	    vtx[2] = z2;
	    bg_vtx->add(vtx);
	    tex[0] = texLen*4.0;
	    tex[1] = 0.5;
	    bg_tex->add(tex);
	}
	bg = new ssgVtxTable(GL_TRIANGLE_STRIP, bg_vtx, bg_nrm, bg_tex, bg_clr);
	bg_st = (ssgSimpleState*)grSsgLoadTexState(graphic->background);
	bg_st->disable(GL_LIGHTING);
	bg->setState(bg_st);
	bg->setCullFace(0);
	TheBackground->addKid(bg);

	bg_vtx = new ssgVertexArray(NB_BG_FACES + 1);
	bg_tex = new ssgTexCoordArray(NB_BG_FACES + 1);
	bg_clr = new ssgColourArray(1);
	bg_nrm = new ssgNormalArray(1);

	bg_clr->add(clr);
	bg_nrm->add(nrm);

	for (i = 3 * NB_BG_FACES / 4; i < NB_BG_FACES + 1; i++) {
	    alpha = (float)i * 2 * PI / (float)NB_BG_FACES;
	    texLen = (float)i / (float)NB_BG_FACES;
	    
	    x = BG_DIST * cos(alpha);
	    y = BG_DIST * sin(alpha);
	    
	    vtx[0] = x;
	    vtx[1] = y;
	    vtx[2] = z1;
	    bg_vtx->add(vtx);
	    tex[0] = texLen*4.0;
	    tex[1] = 0.5;
	    bg_tex->add(tex);

	    vtx[0] = x;
	    vtx[1] = y;
	    vtx[2] = z2;
	    bg_vtx->add(vtx);
	    tex[0] = texLen*4.0;
	    tex[1] = 1.0;
	    bg_tex->add(tex);
	}
	bg = new ssgVtxTable(GL_TRIANGLE_STRIP, bg_vtx, bg_nrm, bg_tex, bg_clr);
	bg_st = (ssgSimpleState*)grSsgLoadTexState(graphic->background);
	bg_st->disable(GL_LIGHTING);
	bg->setState(bg_st);
	bg->setCullFace(0);
	TheBackground->addKid(bg);

	break;


    case 4:
	z1 = -1.0;
	z2 = 1.0;

	bg_vtx = new ssgVertexArray(NB_BG_FACES + 1);
	bg_tex = new ssgTexCoordArray(NB_BG_FACES + 1);
	bg_clr = new ssgColourArray(1);
	bg_nrm = new ssgNormalArray(1);

	bg_clr->add(clr);
	bg_nrm->add(nrm);

	for (i = 0; i < NB_BG_FACES + 1; i++) {
	    alpha = (double)i * 2 * PI / (double)NB_BG_FACES;
	    texLen = 1.0 - (float)i / (float)NB_BG_FACES;
	    
	    x = BG_DIST * cos(alpha);
	    y = BG_DIST * sin(alpha);
	    
	    vtx[0] = x;
	    vtx[1] = y;
	    vtx[2] = z1;
	    bg_vtx->add(vtx);
	    tex[0] = texLen;
	    tex[1] = 0;
	    bg_tex->add(tex);

	    vtx[0] = x;
	    vtx[1] = y;
	    vtx[2] = z2;
	    bg_vtx->add(vtx);
	    tex[0] = texLen;
	    tex[1] = 1.0;
	    bg_tex->add(tex);
	}
	bg = new ssgVtxTable(GL_TRIANGLE_STRIP, bg_vtx, bg_nrm, bg_tex, bg_clr);
	bg_st = (ssgSimpleState*)grSsgLoadTexState(graphic->background);
	bg_st->disable(GL_LIGHTING);
	bg->setState(bg_st);
	bg->setCullFace(0);
	TheBackground->addKid(bg);
	break;

    default:
	break;
    }

    /* Environment Mapping Settings */
    grEnvSelector = new ssgStateSelector(graphic->envnb);
    for (i = 0; i < graphic->envnb; i++) {
      GfOut("Loading Environment Mapping Image %s\n", graphic->env[i]);
      envst = (ssgSimpleState*)grSsgLoadTexState(graphic->env[i]);
      envst->enable(GL_BLEND);
      grEnvSelector->setStep(i, envst);
	  envst->deRef();
    }
    grEnvSelector->selectStep(0); /* mandatory !!! */
    grEnvState=(grMultiTexState*)grSsgEnvTexState(graphic->env[0]);
    grEnvShadowState=(grMultiTexState*)grSsgEnvTexState("envshadow.png");
    grEnvShadowStateOnCars=(grMultiTexState*)grSsgEnvTexState("shadow2.rgb");
    if (grEnvShadowState==NULL)
      {
	ulSetError ( UL_WARNING, "grscene:initBackground Failed to open envshadow.png for reading") ;
	ulSetError ( UL_WARNING, "        mandatory for top env mapping ") ;
	ulSetError ( UL_WARNING, "        should be in the .xml !! ") ;
	ulSetError ( UL_WARNING, "        copy the envshadow.png from g-track-2 to the track you selected ") ;
	ulSetError ( UL_WARNING, "        c'est pas classe comme sortie, mais ca evite un crash ") ;
	GfScrShutdown();
	exit(-1);
      }
    if (grEnvShadowStateOnCars==NULL)
      {
	ulSetError ( UL_WARNING, "grscene:initBackground Failed to open shadow2.rgb for reading") ;
	ulSetError ( UL_WARNING, "        no shadow mapping on cars for this track ") ;
      }
}
Пример #4
0
bool cGrCloudLayer::repositionFlat( sgVec3 p, double dt )
{
  sgMat4 T1;

  // combine p and asl (meters) to get translation offset
  sgVec3 asl_offset;
  if ( p[SG_Z] <= layer_asl ) 
  {
    sgSetVec3( asl_offset, p[SG_X], p[SG_Y], layer_asl );
  }
  else 
  {
    sgSetVec3( asl_offset, p[SG_X], p[SG_Y], layer_asl + layer_thickness );
  }

  // Translate to elevation
  sgMakeTransMat4( T1, asl_offset );

  sgMat4 TRANSFORM;
  sgCopyMat4( TRANSFORM, T1 );

  sgCoord layerpos;
  sgSetCoord( &layerpos, TRANSFORM );

  layer_transform->setTransform( &layerpos );

  // now calculate update texture coordinates
  double sp_dist = speed*dt;

  if ( p[SG_X] != last_x || p[SG_Y] != last_y || sp_dist != 0 ) 
  {
    // calculate cloud movement
    double ax = 0.0, ay = 0.0, bx = 0.0, by = 0.0;

    ax = p[SG_X] - last_x;
    ay = p[SG_Y] - last_y;

    if (sp_dist > 0) 
    {
      bx = cos(-direction * SGD_DEGREES_TO_RADIANS) * sp_dist;
      by = sin(-direction * SGD_DEGREES_TO_RADIANS) * sp_dist;
    }

    float xoff = (float)((ax + bx) / (2 * scale));
    float yoff = (float)((ay + by) / (2 * scale));

    const float layer_scale = layer_span / scale;

    float *base, *tc;

    base = tl[0]->get( 0 );
    base[0] += xoff;

    // the while loops can lead to *long* pauses if base[0] comes
    // with a bogus value.
    // while ( base[0] > 1.0 ) { base[0] -= 1.0; }
    // while ( base[0] < 0.0 ) { base[0] += 1.0; }
    if ( base[0] > -10.0 && base[0] < 10.0 ) 
    {
      base[0] -= (int)base[0];
    }
    else 
    {
      base[0] = 0.0;
	  ulSetError(UL_WARNING, "Warning: base1\n");
    }

    base[1] += yoff;
    // the while loops can lead to *long* pauses if base[0] comes
    // with a bogus value.
    // while ( base[1] > 1.0 ) { base[1] -= 1.0; }
    // while ( base[1] < 0.0 ) { base[1] += 1.0; }
    if ( base[1] > -10.0 && base[1] < 10.0 ) 
    {
      base[1] -= (int)base[1];
    }
    else 
    {
      base[1] = 0.0;
	  ulSetError(UL_WARNING, "Warning: base2\n");
    }

    for (int i = 0; i < 4; i++) 
    {
      tc = tl[i]->get( 0 );
      sgSetVec2( tc, base[0] + layer_scale * i/4, base[1] );

      for (int j = 0; j < 4; j++) 
      {
        tc = tl[i]->get( j*2+1 );
        sgSetVec2( tc, base[0] + layer_scale * (i+1)/4,
        base[1] + layer_scale * j/4 );

        tc = tl[i]->get( (j+1)*2 );
        sgSetVec2( tc, base[0] + layer_scale * i/4,
        base[1] + layer_scale * (j+1)/4 );
      }

      tc = tl[i]->get( 9 );
      sgSetVec2( tc, base[0] + layer_scale * (i+1)/4,
      base[1] + layer_scale );
    }

    last_x = p[SG_X];
    last_y = p[SG_Y];
  }

  return true;
}
Пример #5
0
ssgBranch *grMakeSphere(
  ssgSimpleState *state, ssgColourArray *cl,
  float radius, int slices, int stacks,
  ssgCallback predraw, ssgCallback postdraw )
{
  double rho, drho, theta, dtheta;
  float x, y, z;
  float s, t, ds, dt;
  int i, j, imin, imax;
  float nsign = 1.0;
  ssgBranch *sphere = new ssgBranch;
  sgVec2 vec2;
  sgVec3 vec3;

  drho = SGD_PI / (float)stacks;
  dtheta = (2.0 * SGD_PI) / (float)slices;

  /* texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y
     axis t goes from -1.0/+1.0 at z = -radius/+radius (linear along
     longitudes) cannot use triangle fan on texturing (s coord. at
     top/bottom tip varies) */

  ds = 1.0f / slices;
  dt = 1.0f / stacks;
  t = 1.0;  /* because loop now runs from 0 */
  imin = 0;
  imax = stacks;

  /* build slices as quad strips */
  for ( i = imin; i < imax; i++ ) 
  {
    ssgVertexArray   *vl = new ssgVertexArray();
    ssgNormalArray   *nl = new ssgNormalArray();
    ssgTexCoordArray *tl = new ssgTexCoordArray();

    rho = i * drho;
    s = 0.0;
    for ( j = 0; j <= slices; j++ ) 
    {
      theta = (j == slices) ? 0.0 : j * dtheta;
      x = (float)(-sin(theta) * sin(rho));
      y = (float)(cos(theta) * sin(rho));
      z = (float)(nsign * cos(rho));

      sgSetVec3( vec3, x*nsign, y*nsign, z*nsign );
      sgNormalizeVec3( vec3 );
      nl->add( vec3 );

      sgSetVec2( vec2, s, t );
      tl->add( vec2 );

      sgSetVec3( vec3, x*radius, y*radius, z*radius );
      vl->add( vec3 );

      x = (float)(-sin(theta) * sin(rho+drho));
      y = (float)(cos(theta) * sin(rho+drho));
      z = (float)(nsign * cos(rho+drho));

      sgSetVec3( vec3, x*nsign, y*nsign, z*nsign );
      sgNormalizeVec3( vec3 );
      nl->add( vec3 );

      sgSetVec2( vec2, s, t-dt );
      tl->add( vec2 );
      s += ds;

      sgSetVec3( vec3, x*radius, y*radius, z*radius );
      vl->add( vec3 );
    }

    ssgLeaf *slice = 
      new ssgVtxTable ( GL_TRIANGLE_STRIP, vl, nl, tl, cl );

    if ( vl->getNum() != nl->getNum() ) 
    {
      ulSetError(UL_FATAL, "bad sphere1\n");
      exit(-1);
    }
    if ( vl->getNum() != tl->getNum() ) 
    {
      ulSetError(UL_FATAL, "bad sphere2\n");
      exit(-1);
    }
    slice->setState( state );
    slice->setCallback( SSG_CALLBACK_PREDRAW, predraw );
    slice->setCallback( SSG_CALLBACK_POSTDRAW, postdraw );

    sphere->addKid( slice );

    t -= dt;
  }

  return sphere;
}