//------------------------------------------------------------------------------
// File : SkyRenderableInstanceCloud.cpp
//------------------------------------------------------------------------------
// SkyWorks : Copyright 2002 Mark J. Harris and
//						The University of North Carolina at Chapel Hill
//------------------------------------------------------------------------------
// Permission to use, copy, modify, distribute and sell this software and its 
// documentation for any purpose is hereby granted without fee, provided that 
// the above copyright notice appear in all copies and that both that copyright 
// notice and this permission notice appear in supporting documentation. 
// Binaries may be compiled with this software without any royalties or 
// restrictions. 
//
// The author(s) and The University of North Carolina at Chapel Hill make no 
// representations about the suitability of this software for any purpose. 
// It is provided "as is" without express or implied warranty.
/**
 * @file SkyRenderableInstanceCloud.cpp
 * 
 * Implementation of class SkyRenderableInstanceCloud.
 */
#include "SkyUtil.hpp"
#include "SkyCloud.hpp"
#include "SkyMaterial.hpp"
#include "SkyBoundingVolume.hpp"
#include "SkyRenderableInstanceCloud.hpp"
#include "SkyDynamicTextureManager.hpp"

//! Set this to 1 to see verbose messages about impostor updates.
#define SKYCLOUD_VERBOSE 0

//! Set this to control the number of frames a cloud has to be culled before its textures are released.
#define SKYCLOUD_CULL_RELEASE_COUNT 100

//------------------------------------------------------------------------------
// Static declarations.
//------------------------------------------------------------------------------
unsigned int        SkyRenderableInstanceCloud::s_iCount               = 0;
float               SkyRenderableInstanceCloud::s_rErrorToleranceAngle = SKYDEGREESTORADS * 0.125f;
SkyMaterial*        SkyRenderableInstanceCloud::s_pMaterial            = NULL;

//------------------------------------------------------------------------------
// Function     	  : SkyRenderableInstanceCloud::SkyRenderableInstanceCloud
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud, bool bUseOffScreenBuffer)
 * @brief Constructor.
 */ 
SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud, 
                                                       bool bUseOffScreenBuffer /* = true */)
: SkyRenderableInstance(),
  _iCloudID(-1),
  _pCloud(pCloud),
  _pWorldSpaceBV(NULL),
  _rRadius(0),
  _bScreenImpostor(false),
  _bImageExists(false),
  _bEnabled(true),
  _bUseOffScreenBuffer(bUseOffScreenBuffer),
  _bSplit(false),
  _vecSplit(0, 0, 0),
  _vecNearPoint(0, 0, 0),
  _vecFarPoint(0, 0, 0), 
  _iLogResolution(0),
  _pBackTexture(NULL),
  _pFrontTexture(NULL),
  _iCulledCount(0)
{
  _Initialize();
//   cout << "Cloud Instance created" << endl;
}

//------------------------------------------------------------------------------
// Function     	  : SkyRenderableInstanceCloud::SkyRenderableInstanceCloud
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud, const Vec3f &position, const Mat33f &rotation, const float scale, bool bUseOffScreenBuffer)
 * @brief Constructor.
 */ 
SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud     *pCloud, 
                                                       const Vec3f  &position, 
                                                       const Mat33f &rotation, 
                                                       const float  scale,
                                                       bool         bUseOffScreenBuffer /* = true */)
: SkyRenderableInstance(position, rotation, scale),
  _iCloudID(-1),
  _pCloud(pCloud),
  _pWorldSpaceBV(NULL),
  _rRadius(0),
  _bScreenImpostor(false),
  _bImageExists(false),
  _bEnabled(true),
  _bUseOffScreenBuffer(false),
  _bSplit(false),
  _vecSplit(0, 0, 0),
  _vecNearPoint(0, 0, 0),
  _vecFarPoint(0, 0, 0), 
  _iLogResolution(0),
  _pBackTexture(NULL),
  _pFrontTexture(NULL)
{
  _Initialize();
}

//------------------------------------------------------------------------------
// Function     	  : SkyRenderableInstanceCloud::~SkyRenderableInstanceCloud
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyRenderableInstanceCloud::~SkyRenderableInstanceCloud()
 * @brief Destructor
 */ 
SkyRenderableInstanceCloud::~SkyRenderableInstanceCloud()
{
  _pCloud = NULL;
  SAFE_DELETE(_pWorldSpaceBV);

  s_iCount--;
  // delete the offscreen buffer when no one else is using it.
  if (0 == s_iCount)
  {
//JW??    SAFE_DELETE(s_pRenderBuffer);
    SAFE_DELETE(s_pMaterial);
  }
}


//------------------------------------------------------------------------------
// Function     	  : SkyRenderableInstanceCloud::SetPosition
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyRenderableInstanceCloud::SetPosition(const Vec3f  &position)
 * @brief Set the world space position of the instance.
 * 
 * @todo <WRITE EXTENDED SkyRenderableInstanceCloud::SetPosition FUNCTION DOCUMENTATION>
 */ 
void SkyRenderableInstanceCloud::SetPosition(const Vec3f  &position)
{ 
  if (_pCloud)
  {
    _pCloud->Translate(position - _vecPosition);
  }
  _vecPosition = position; 
   
  _UpdateWorldSpaceBounds(); 
}


//------------------------------------------------------------------------------
// Function     	  : SkyRenderableInstanceCloud::SetRotation
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyRenderableInstanceCloud::SetRotation(const Mat33f &rotation)
 * @brief Set the world space rotation of the instance.
 * 
 * @todo <WRITE EXTENDED SkyRenderableInstanceCloud::SetRotation FUNCTION DOCUMENTATION>
 */ 
void SkyRenderableInstanceCloud::SetRotation(const Mat33f &rotation)
{ 
  if (_pCloud)
  {
    _pCloud->Translate(-_vecPosition);
    _pCloud->Rotate(_matInvRotation * rotation);
    _pCloud->Translate(_vecPosition);
  }
  _matRotation    = rotation;
  _matInvRotation = rotation; 
  _matInvRotation.Transpose();  
  _UpdateWorldSpaceBounds(); 
}


//------------------------------------------------------------------------------
// Function     	  : SkyRenderableInstanceCloud::SetScale
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyRenderableInstanceCloud::SetScale(const float  &scale)
 * @brief Set the world space scale of the instance. 
 */ 
void SkyRenderableInstanceCloud::SetScale(const float  &scale)
{ 
  if (_pCloud)
  {
    _pCloud->Translate(-_vecPosition);
    _pCloud->Scale(scale);
    _pCloud->Translate(_vecPosition);
  }
  _rScale = scale; 
  _UpdateWorldSpaceBounds(); 
}


//------------------------------------------------------------------------------
// Function     	  : DrawQuad
// Description	    : 
//------------------------------------------------------------------------------
/**
 * DrawQuad(Vec3f pos, Vec3f x, Vec3f y, Vec4f color)
 * @brief Simply draws an OpenGL quad at @a pos.
 *
 * The quad's size and orientation are determined by the (non-unit) vectors @a x
 * and @a y.  Its color is given by @a color.
 */ 
inline void DrawQuad(Vec3f pos, Vec3f x, Vec3f y, Vec4f color)
{
  glColor4fv(&(color.x));
  Vec3f left  = pos;  left   -= y; 
  Vec3f right = left; right  += x; 
  left  -= x;
  glTexCoord2f(0, 0); glVertex3fv(&(left.x));
  glTexCoord2f(1, 0); glVertex3fv(&(right.x));
  left  += y;  left  += y;
  right += y;  right += y;
  glTexCoord2f(1, 1); glVertex3fv(&(right.x));
  glTexCoord2f(0, 1); glVertex3fv(&(left.x));
}


//------------------------------------------------------------------------------
// Function     	  : SkyRenderableInstanceCloud::Display
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyRenderableInstanceCloud::Display(bool bDisplayFrontOfSplit)
 * @brief Display the instance of the cloud using the impostor image.
 */ 
SKYRESULT SkyRenderableInstanceCloud::Display(bool bDisplayFrontOfSplit /* = false */)
{
	
  if (!_bImageExists || !_bEnabled)
  {
    //FAIL_RETURN(DisplayWithoutImpostor(*(GLVU::GetCurrent()->GetCurrentCam())));
    FAIL_RETURN(DisplayWithoutImpostor(Camera()));
  }
  else
  {//cout << "Using impostor image\n";
      if (!_pBackTexture || (bDisplayFrontOfSplit && !_pFrontTexture)) {
          // cout << "texture id failure" << endl;
          FAIL_RETURN_MSG(SKYRESULT_FAIL, "SkyRenderableInstanceCloud::Display(): missing texture!");
      }

    s_pMaterial->SetTexture(0, GL_TEXTURE_2D, bDisplayFrontOfSplit ? *_pFrontTexture : *_pBackTexture);     
    if (_bScreenImpostor)
    {
      s_pMaterial->EnableDepthTest(false);
    }
    else if (_bSplit)
    {
      if (!bDisplayFrontOfSplit)
      {
        s_pMaterial->EnableDepthTest(true);
        s_pMaterial->SetDepthMask(false);
      }
      else
        s_pMaterial->EnableDepthTest(false);
    }
    else
    {
      s_pMaterial->EnableDepthTest(true);
      s_pMaterial->SetDepthMask(true); 
    }
     
    s_pMaterial->Activate();
    // s_pMaterial->Force();

    Vec3f x, y, z;
    
    if (!_bScreenImpostor)
    {//cout << "Outside the cloud\n";
      z  =    _vecPosition; 
      z -=    _impostorCam.Orig;
      z.Normalize();
      x  =    (z ^ _impostorCam.Y);
      x.Normalize();
      x *=    _rRadius;
      y  =    (x ^ z);
      y.Normalize();
      y *=    _rRadius;      

      glBegin(GL_QUADS);
      DrawQuad(_vecPosition, x, y, Vec4f(1, 1, 1, 1));
      glEnd();	
    }
    else
    { //cout <<  "Drawing a polygon - must be inside a cloud\n";
      x  =  _impostorCam.X;
      x *=  0.5f * (_impostorCam.wR - _impostorCam.wL);
      y  =  _impostorCam.Y;
      y *=  0.5f * (_impostorCam.wT - _impostorCam.wB);
      z  =  -_impostorCam.Z;
      z *=  _impostorCam.Near;

      // draw a polygon with this texture...
      glMatrixMode(GL_MODELVIEW);
      glPushMatrix();
      glLoadIdentity();
      glMatrixMode(GL_PROJECTION);
      glPushMatrix();
      glLoadIdentity();
      gluOrtho2D(-1, 1, -1, 1);
      
      glColor4f(1, 1, 1, 1);
      glBegin(GL_QUADS);
      glTexCoord2f(0, 0); glVertex2f(-1, -1);
      glTexCoord2f(1, 0); glVertex2f(1, -1);
      glTexCoord2f(1, 1); glVertex2f(1, 1);
      glTexCoord2f(0, 1); glVertex2f(-1, 1);
      glEnd();
      
      glPopMatrix();
      glMatrixMode(GL_MODELVIEW);
      glPopMatrix();
    }
  }
  return SKYRESULT_OK;
}



//------------------------------------------------------------------------------
// Function     	  : SkyRenderableInstanceCloud::DisplayWithoutImpostor
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyRenderableInstanceCloud::DisplayWithoutImpostor(const Camera &cam)
 * @brief Displays the cloud directly -- without an impotor.
 * 
 * This is used both when the impostor is disabled and to create the impostor image
 * when it needs to be updated.
 */ 
SKYRESULT SkyRenderableInstanceCloud::DisplayWithoutImpostor(const Camera &cam)
{
  // Get and set the world space transformation
  /*Mat44f mat;
  GetModelToWorldTransform(mat);

  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glMultMatrixf(mat.M);*/
 
  FAIL_RETURN_MSG(_pCloud->Display(cam, this), "SkyRenderableInstanceCloud:Display(): Cloud's display failed.");

  //glMatrixMode(GL_MODELVIEW);
  //glPopMatrix();

  return SKYRESULT_OK;
}


//------------------------------------------------------------------------------
// Function     	  : SkyRenderableInstanceCloud::ViewFrustumCull
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyRenderableInstanceCloud::ViewFrustumCull(const Camera &cam)
 * @brief View frustum cull the object given its world position
 */ 
bool SkyRenderableInstanceCloud::ViewFrustumCull(const Camera &cam)
{
  Mat44f xform;
  //GetModelToWorldTransform(xform);
  xform.Identity();
  _bCulled = (_pWorldSpaceBV == NULL) ? false : _pWorldSpaceBV->ViewFrustumCull(cam, xform);
  return _bCulled;
}


//------------------------------------------------------------------------------
// Function     	  : SkyRenderableInstanceCloud::ReleaseImpostorTextures
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyRenderableInstanceCloud::ReleaseImpostorTextures()
 * @brief Causes the instance to release its impostor textures for use by other impostors.
 * 
 * This method is called when the cloud is view frustum culled.
 */ 
void SkyRenderableInstanceCloud::ReleaseImpostorTextures()
{
  _iCulledCount++;

  if (_iCulledCount > SKYCLOUD_CULL_RELEASE_COUNT)
  {
    _iCulledCount = 0;
  
    if (_pBackTexture)
    {
      DynamicTextureManager::InstancePtr()->CheckInTexture(_pBackTexture);
      _pBackTexture = NULL;
    }
   
    if (_pFrontTexture)
    {
      DynamicTextureManager::InstancePtr()->CheckInTexture(_pFrontTexture);
      _pFrontTexture = NULL;
    }
    _bImageExists = false;
  }
}


//------------------------------------------------------------------------------
// Function     	  : SkyRenderableInstanceCloud::Update
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyRenderableInstanceCloud::Update(const Camera &cam)
 * @brief Updates the impostor image to be valid for the current viewpoint.
 * 
 * If the image is already valid, exits early.
 *
 * @see SetErrorToleranceAngle, IsValid
 */ 
SKYRESULT SkyRenderableInstanceCloud::Update(const Camera &cam)
{
  if (!_bEnabled || IsImpostorValid(cam)) 
    return SKYRESULT_OK;

  // since we are going to update it anyway, let's make sure we don't try to use it if something
  // goes wrong.  This will be set to true on the successful completion of this Update() method.
  _bImageExists = false;
//cout << "updating impostor\n";
  Mat44f M;
  
  _impostorCam = cam;
  float rDistance  = (_vecPosition - cam.Orig).Length();
  
  float rRadius    = _pWorldSpaceBV->GetRadius();
  float rCamRadius = sqrt(cam.wR*cam.wR + cam.Near*cam.Near);
  
  float rWidth     = cam.wR - cam.wL;
  float rHeight    = cam.wT - cam.wB;
  float rMaxdim    = (rWidth > rHeight) ? rWidth : rHeight;
  
  if (rRadius * cam.Near / rDistance < 0.5 * rMaxdim && (rDistance - rRadius > rCamRadius)) 
  { // outside cloud
    _impostorCam.TightlyFitToSphere(cam.Orig, cam.Y, _vecPosition, rRadius);
    _rRadius = 0.5f * (_impostorCam.wR - _impostorCam.wL) * rDistance / _impostorCam.Near;
    _rRadius *= GetScale();
    _bScreenImpostor = false;	
    // store points used in later error estimation 
    _vecNearPoint   =   -_impostorCam.Z;
    _vecNearPoint   *=  _impostorCam.Near;
    _vecNearPoint   +=  _impostorCam.Orig;
    _vecFarPoint    =   -_impostorCam.Z;
    _vecFarPoint    *=  _impostorCam.Far;
    _vecFarPoint    +=  _impostorCam.Orig;
  }
  else // inside cloud
  {
    _impostorCam.Far  =  _impostorCam.Near + 3 * rRadius;
    _bScreenImpostor =  true;
  }
  
  // resolution based on screensize, distance, and object size.
  // Cam radius is used to heuristically reduce resolution for clouds very close to the camera.
  _iLogResolution = _GetRequiredLogResolution(rDistance, rRadius, rCamRadius);

  int iRes = 1 << _iLogResolution;

  int iOldVP[4];

  glGetIntegerv(GL_VIEWPORT, iOldVP);
  
  _impostorCam.GetProjectionMatrix(M);
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadMatrixf(M);
  
  _impostorCam.GetModelviewMatrix(M);
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadMatrixf(M);
  
  glViewport(0, 0, iRes, iRes);
  
  s_pMaterial->SetDepthMask(true);  // so that the depth buffer gets cleared!
  s_pMaterial->Activate();
  glClearColor(0, 0, 0, 0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  if (!_bSplit)
  {
    FAIL_RETURN(DisplayWithoutImpostor(_impostorCam));
    
    if (_pBackTexture && _pBackTexture->GetWidth() != iRes)
    {
      DynamicTextureManager::InstancePtr()->CheckInTexture(_pBackTexture);
      _pBackTexture = NULL;
    }
    
    if (!_pBackTexture)
    {
      _pBackTexture = DynamicTextureManager::InstancePtr()->CheckOutTexture(iRes, iRes);
    }
    
    s_pMaterial->SetTexture(0, GL_TEXTURE_2D, *_pBackTexture);     // shared material for clouds. 
    s_pMaterial->Activate();
    
    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, iRes, iRes);
  }
  else
  {  
    FAIL_RETURN_MSG(_pCloud->DisplaySplit(cam, _vecSplit, true, this), 
                    "SkyRenderableInstanceCloud:Display(): Cloud's display failed.");

    if (_pBackTexture && _pBackTexture->GetWidth() != iRes)
    {
      DynamicTextureManager::InstancePtr()->CheckInTexture(_pBackTexture);
      _pBackTexture = NULL;
    }
    if (_pFrontTexture && _pFrontTexture->GetWidth() != iRes)
    {
      DynamicTextureManager::InstancePtr()->CheckInTexture(_pFrontTexture);
      _pFrontTexture = NULL;
    }

    if (!_pBackTexture)
    {
      _pBackTexture = DynamicTextureManager::InstancePtr()->CheckOutTexture(iRes, iRes);
    }

    s_pMaterial->SetTexture(0, GL_TEXTURE_2D, *_pBackTexture);     // shared material for clouds. 
    FAIL_RETURN(s_pMaterial->Activate());
   
    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, iRes, iRes);

    // now clear and draw the front.
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    FAIL_RETURN_MSG(_pCloud->DisplaySplit(cam, _vecSplit, false, this), 
                    "SkyRenderableInstanceCloud:Display(): Cloud's display failed.");

    if (!_pFrontTexture)
    {
      _pFrontTexture = DynamicTextureManager::InstancePtr()->CheckOutTexture(iRes, iRes);
    }

    s_pMaterial->GetTextureState().SetTexture(0, GL_TEXTURE_2D, *_pFrontTexture);
    FAIL_RETURN(s_pMaterial->GetTextureState().Activate());

    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, iRes, iRes);
  }
  
  glMatrixMode(GL_MODELVIEW);
  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  
  //GLVU::CheckForGLError("Cloud Impostor Update");

    glViewport(iOldVP[0], iOldVP[1], iOldVP[2], iOldVP[3]);

  _bImageExists = true;

  // the textures should now exist.
  assert(_pBackTexture);
  assert(!_bSplit || (_bSplit && _pFrontTexture));

  return SKYRESULT_OK;
}


//------------------------------------------------------------------------------
// Function     	  : SkyRenderableInstanceCloud::IsImpostorValid
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyRenderableInstanceCloud::IsImpostorValid(const Camera& cam)
 * @brief Returns true if the impostor image is valid for the given camera.
 * 
 * Will return false if this is a screen impostor, or if there is error in either
 * the translation of the camera from the capture point or the impostor image resolution.
 *
 * @see SetErrorToleranceAngle
 */ 
bool SkyRenderableInstanceCloud::IsImpostorValid(const Camera& cam)
{ 
  // first make sure there is a current image.
  if (!_bImageExists) 
    return false;

  // screen impostors should always be updated
  if (_bScreenImpostor)
  {
    _vecFarPoint = Vec3f::ZERO;
    _vecNearPoint = Vec3f::ZERO;
#if SKYCLOUD_VERBOSE
    SkyTrace("Screen Impostor Update");
#endif
    return false;
  }
  // impostors are valid from the viewpoint from which they were captured
  if (cam.Orig == _impostorCam.Orig) 
    return true;
  
  if (_bSplit) 
  {
  #if SKYCLOUD_VERBOSE
    SkyTrace("Split Impostor Update");
  #endif
    return false;
  }
  
  Vec3f vecX = _vecNearPoint - cam.Orig;
  Vec3f vecY = _vecFarPoint - cam.Orig;
  float rXLength = vecX.Length();
  float rYLength = vecY.Length();
  if (rXLength > rYLength) 
  {
#if SKYCLOUD_VERBOSE
    SkyTrace("Backwards Impostor Update");
#endif
    return false;
  }
  
  vecX /= rXLength; 
  vecY /= rYLength; 
  float rCosAlpha = vecX * vecY; // dot product of normalized vectors = cosine
  
  if (fabs(rCosAlpha) < 1.0) 
  {
    float rAlpha = acos(rCosAlpha);
    if (rAlpha >= s_rErrorToleranceAngle) 
    {
#if SKYCLOUD_VERBOSE
      SkyTrace("Angle Error Update %f", SKYRADSTODEGREES * rAlpha);
#endif
      return false;
    }
  }
 
  float rDistance = (_vecPosition - cam.Orig).Length();
  float rCamRadius = sqrt(cam.wR*cam.wR + cam.Near*cam.Near);
  
  int iRes = _GetRequiredLogResolution(rDistance, _pWorldSpaceBV->GetRadius(), rCamRadius);

  if (iRes > _iLogResolution)
  {
#if SKYCLOUD_VERBOSE
    SkyTrace("Resolution Error Update: Required: %d Actual: %d", iRes, _iLogResolution);
#endif
    return false;
  }
 
  return true;
}


//------------------------------------------------------------------------------
  // Function     	  : SetErrorToleranceAngle
  // Description	    : 
  //------------------------------------------------------------------------------
  /**
  * @fn SkyRenderableInstanceCloud::SetErrorToleranceAngle(float rDegrees)
  * @brief Set the global error tolerance angle for all impostors.
  */ 
void SkyRenderableInstanceCloud::SetErrorToleranceAngle(float rDegrees)
{ 
  s_rErrorToleranceAngle = SKYDEGREESTORADS * rDegrees; 
}



//------------------------------------------------------------------------------
// Function     	  : SkyRenderableInstanceCloud::_Initialize
// Description	    : 
//------------------------------------------------------------------------------
/**
 * @fn SkyRenderableInstanceCloud::_Initialize()
 * @brief Initializer used by the constructors.
 */ 
void SkyRenderableInstanceCloud::_Initialize()
{
  _UpdateWorldSpaceBounds();

//  if (!s_pRenderBuffer && _bUseOffScreenBuffer)
//  {
//JW??    s_pRenderBuffer = new SkyOffScreenBuffer(GLUT_SINGLE | GLUT_DEPTH | GLUT_STENCIL);
//JW??    s_pRenderBuffer->Initialize(true);
    
//JW??    s_pRenderBuffer->MakeCurrent();
    // set some GL state:
//    glClearColor(0, 0, 0, 0);
//JW??    GLVU::GetCurrent()->MakeCurrent();
//  }
  if (!s_pMaterial)
  {
    s_pMaterial = new SkyMaterial;
    s_pMaterial->EnableBlending(true);
    s_pMaterial->SetBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    s_pMaterial->SetAlphaFunc(GL_GREATER);
    s_pMaterial->EnableDepthTest(false);
    s_pMaterial->SetDepthMask(true);
    s_pMaterial->EnableAlphaTest(true);
    s_pMaterial->EnableLighting(false);
    s_pMaterial->SetTextureParameter(0, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    s_pMaterial->SetTextureParameter(0, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    s_pMaterial->SetTextureParameter(0, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    s_pMaterial->EnableTexture(0, true);    
  }
  s_iCount++;
}
