]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/clouds3d/SkyRenderableInstanceCloud.cpp
Clouds3D crashes because there is no Light
[simgear.git] / simgear / scene / sky / clouds3d / SkyRenderableInstanceCloud.cpp
1 //------------------------------------------------------------------------------
2 // File : SkyRenderableInstanceCloud.cpp
3 //------------------------------------------------------------------------------
4 // SkyWorks : Copyright 2002 Mark J. Harris and
5 //                                              The University of North Carolina at Chapel Hill
6 //------------------------------------------------------------------------------
7 // Permission to use, copy, modify, distribute and sell this software and its 
8 // documentation for any purpose is hereby granted without fee, provided that 
9 // the above copyright notice appear in all copies and that both that copyright 
10 // notice and this permission notice appear in supporting documentation. 
11 // Binaries may be compiled with this software without any royalties or 
12 // restrictions. 
13 //
14 // The author(s) and The University of North Carolina at Chapel Hill make no 
15 // representations about the suitability of this software for any purpose. 
16 // It is provided "as is" without express or implied warranty.
17 /**
18  * @file SkyRenderableInstanceCloud.cpp
19  * 
20  * Implementation of class SkyRenderableInstanceCloud.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #  include <simgear_config.h>
25 #endif
26
27 #ifdef HAVE_WINDOWS_H
28 #  include <windows.h>
29 #endif
30
31 #include <GL/glu.h>
32
33 #include "SkyUtil.hpp"
34 #include "SkyCloud.hpp"
35 #include "SkyMaterial.hpp"
36 #include "SkyBoundingVolume.hpp"
37 #include "SkyRenderableInstanceCloud.hpp"
38 #include "SkyDynamicTextureManager.hpp"
39
40 //! Set this to 1 to see verbose messages about impostor updates.
41 #define SKYCLOUD_VERBOSE 0
42
43 //! Set this to control the number of frames a cloud has to be culled before its textures are released.
44 #define SKYCLOUD_CULL_RELEASE_COUNT 100
45
46 //------------------------------------------------------------------------------
47 // Static declarations.
48 //------------------------------------------------------------------------------
49 unsigned int        SkyRenderableInstanceCloud::s_iCount               = 0;
50 float               SkyRenderableInstanceCloud::s_rErrorToleranceAngle = SKYDEGREESTORADS * 0.125f;
51 SkyMaterial*        SkyRenderableInstanceCloud::s_pMaterial            = NULL;
52
53 //------------------------------------------------------------------------------
54 // Function               : SkyRenderableInstanceCloud::SkyRenderableInstanceCloud
55 // Description      : 
56 //------------------------------------------------------------------------------
57 /**
58  * @fn SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud, bool bUseOffScreenBuffer)
59  * @brief Constructor.
60  */ 
61 SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud, 
62                                                        bool bUseOffScreenBuffer /* = true */)
63 : SkyRenderableInstance(),
64   _iCloudID(-1),
65   _pCloud(pCloud),
66   _pWorldSpaceBV(NULL),
67   _rRadius(0),
68   _bScreenImpostor(false),
69   _bImageExists(false),
70   _bEnabled(true),
71   _bUseOffScreenBuffer(bUseOffScreenBuffer),
72   _bSplit(false),
73   _vecSplit(0, 0, 0),
74   _vecNearPoint(0, 0, 0),
75   _vecFarPoint(0, 0, 0), 
76   _iLogResolution(0),
77   _pBackTexture(NULL),
78   _pFrontTexture(NULL),
79   _iCulledCount(0)
80 {
81   _Initialize();
82 //   cout << "Cloud Instance created" << endl;
83 }
84
85 //------------------------------------------------------------------------------
86 // Function               : SkyRenderableInstanceCloud::SkyRenderableInstanceCloud
87 // Description      : 
88 //------------------------------------------------------------------------------
89 /**
90  * @fn SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud, const Vec3f &position, const Mat33f &rotation, const float scale, bool bUseOffScreenBuffer)
91  * @brief Constructor.
92  */ 
93 SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud     *pCloud, 
94                                                        const Vec3f  &position, 
95                                                        const Mat33f &rotation, 
96                                                        const float  scale,
97                                                        bool         bUseOffScreenBuffer /* = true */)
98 : SkyRenderableInstance(position, rotation, scale),
99   _iCloudID(-1),
100   _pCloud(pCloud),
101   _pWorldSpaceBV(NULL),
102   _rRadius(0),
103   _bScreenImpostor(false),
104   _bImageExists(false),
105   _bEnabled(true),
106   _bUseOffScreenBuffer(false),
107   _bSplit(false),
108   _vecSplit(0, 0, 0),
109   _vecNearPoint(0, 0, 0),
110   _vecFarPoint(0, 0, 0), 
111   _iLogResolution(0),
112   _pBackTexture(NULL),
113   _pFrontTexture(NULL)
114 {
115   _Initialize();
116 }
117
118 //------------------------------------------------------------------------------
119 // Function               : SkyRenderableInstanceCloud::~SkyRenderableInstanceCloud
120 // Description      : 
121 //------------------------------------------------------------------------------
122 /**
123  * @fn SkyRenderableInstanceCloud::~SkyRenderableInstanceCloud()
124  * @brief Destructor
125  */ 
126 SkyRenderableInstanceCloud::~SkyRenderableInstanceCloud()
127 {
128   _pCloud = NULL;
129   SAFE_DELETE(_pWorldSpaceBV);
130
131   s_iCount--;
132   // delete the offscreen buffer when no one else is using it.
133   if (0 == s_iCount)
134   {
135 //JW??    SAFE_DELETE(s_pRenderBuffer);
136     SAFE_DELETE(s_pMaterial);
137   }
138 }
139
140
141 //------------------------------------------------------------------------------
142 // Function               : SkyRenderableInstanceCloud::SetPosition
143 // Description      : 
144 //------------------------------------------------------------------------------
145 /**
146  * @fn SkyRenderableInstanceCloud::SetPosition(const Vec3f  &position)
147  * @brief Set the world space position of the instance.
148  * 
149  * @todo <WRITE EXTENDED SkyRenderableInstanceCloud::SetPosition FUNCTION DOCUMENTATION>
150  */ 
151 void SkyRenderableInstanceCloud::SetPosition(const Vec3f  &position)
152
153   if (_pCloud)
154   {
155     _pCloud->Translate(position - _vecPosition);
156   }
157   _vecPosition = position; 
158    
159   _UpdateWorldSpaceBounds(); 
160 }
161
162
163 //------------------------------------------------------------------------------
164 // Function               : SkyRenderableInstanceCloud::SetRotation
165 // Description      : 
166 //------------------------------------------------------------------------------
167 /**
168  * @fn SkyRenderableInstanceCloud::SetRotation(const Mat33f &rotation)
169  * @brief Set the world space rotation of the instance.
170  * 
171  * @todo <WRITE EXTENDED SkyRenderableInstanceCloud::SetRotation FUNCTION DOCUMENTATION>
172  */ 
173 void SkyRenderableInstanceCloud::SetRotation(const Mat33f &rotation)
174
175   if (_pCloud)
176   {
177     _pCloud->Translate(-_vecPosition);
178     _pCloud->Rotate(_matInvRotation * rotation);
179     _pCloud->Translate(_vecPosition);
180   }
181   _matRotation    = rotation;
182   _matInvRotation = rotation; 
183   _matInvRotation.Transpose();  
184   _UpdateWorldSpaceBounds(); 
185 }
186
187
188 //------------------------------------------------------------------------------
189 // Function               : SkyRenderableInstanceCloud::SetScale
190 // Description      : 
191 //------------------------------------------------------------------------------
192 /**
193  * @fn SkyRenderableInstanceCloud::SetScale(const float  &scale)
194  * @brief Set the world space scale of the instance. 
195  */ 
196 void SkyRenderableInstanceCloud::SetScale(const float  &scale)
197
198   if (_pCloud)
199   {
200     _pCloud->Translate(-_vecPosition);
201     _pCloud->Scale(scale);
202     _pCloud->Translate(_vecPosition);
203   }
204   _rScale = scale; 
205   _UpdateWorldSpaceBounds(); 
206 }
207
208
209 //------------------------------------------------------------------------------
210 // Function               : DrawQuad
211 // Description      : 
212 //------------------------------------------------------------------------------
213 /**
214  * DrawQuad(Vec3f pos, Vec3f x, Vec3f y, Vec4f color)
215  * @brief Simply draws an OpenGL quad at @a pos.
216  *
217  * The quad's size and orientation are determined by the (non-unit) vectors @a x
218  * and @a y.  Its color is given by @a color.
219  */ 
220 inline void DrawQuad(Vec3f pos, Vec3f x, Vec3f y, Vec4f color)
221 {
222   glColor4fv(&(color.x));
223   Vec3f left  = pos;  left   -= y; 
224   Vec3f right = left; right  += x; 
225   left  -= x;
226   glTexCoord2f(0, 0); glVertex3fv(&(left.x));
227   glTexCoord2f(1, 0); glVertex3fv(&(right.x));
228   left  += y;  left  += y;
229   right += y;  right += y;
230   glTexCoord2f(1, 1); glVertex3fv(&(right.x));
231   glTexCoord2f(0, 1); glVertex3fv(&(left.x));
232 }
233
234
235 //------------------------------------------------------------------------------
236 // Function               : SkyRenderableInstanceCloud::Display
237 // Description      : 
238 //------------------------------------------------------------------------------
239 /**
240  * @fn SkyRenderableInstanceCloud::Display(bool bDisplayFrontOfSplit)
241  * @brief Display the instance of the cloud using the impostor image.
242  */ 
243 SKYRESULT SkyRenderableInstanceCloud::Display(bool bDisplayFrontOfSplit /* = false */)
244 {
245         
246   if (!_bImageExists || !_bEnabled)
247   {
248     //FAIL_RETURN(DisplayWithoutImpostor(*(GLVU::GetCurrent()->GetCurrentCam())));
249     FAIL_RETURN(DisplayWithoutImpostor(Camera()));
250   }
251   else
252   {//cout << "Using impostor image\n";
253       if (!_pBackTexture || (bDisplayFrontOfSplit && !_pFrontTexture)) {
254           // cout << "texture id failure" << endl;
255           FAIL_RETURN_MSG(SKYRESULT_FAIL, "SkyRenderableInstanceCloud::Display(): missing texture!");
256       }
257
258     s_pMaterial->SetTexture(0, GL_TEXTURE_2D, bDisplayFrontOfSplit ? *_pFrontTexture : *_pBackTexture);     
259     if (_bScreenImpostor)
260     {
261       s_pMaterial->EnableDepthTest(false);
262     }
263     else if (_bSplit)
264     {
265       if (!bDisplayFrontOfSplit)
266       {
267         s_pMaterial->EnableDepthTest(true);
268         s_pMaterial->SetDepthMask(false);
269       }
270       else
271         s_pMaterial->EnableDepthTest(false);
272     }
273     else
274     {
275       s_pMaterial->EnableDepthTest(true);
276       s_pMaterial->SetDepthMask(true); 
277     }
278      
279     s_pMaterial->Activate();
280     // s_pMaterial->Force();
281
282     Vec3f x, y, z;
283     
284     if (!_bScreenImpostor)
285     {//cout << "Outside the cloud\n";
286       z  =    _vecPosition; 
287       z -=    _impostorCam.Orig;
288       z.Normalize();
289       x  =    (z ^ _impostorCam.Y);
290       x.Normalize();
291       x *=    _rRadius;
292       y  =    (x ^ z);
293       y.Normalize();
294       y *=    _rRadius;      
295
296       glBegin(GL_QUADS);
297       DrawQuad(_vecPosition, x, y, Vec4f(1, 1, 1, 1));
298       glEnd();  
299     }
300     else
301     { //cout <<  "Drawing a polygon - must be inside a cloud\n";
302       x  =  _impostorCam.X;
303       x *=  0.5f * (_impostorCam.wR - _impostorCam.wL);
304       y  =  _impostorCam.Y;
305       y *=  0.5f * (_impostorCam.wT - _impostorCam.wB);
306       z  =  -_impostorCam.Z;
307       z *=  _impostorCam.Near;
308
309       // draw a polygon with this texture...
310       glMatrixMode(GL_MODELVIEW);
311       glPushMatrix();
312       glLoadIdentity();
313       glMatrixMode(GL_PROJECTION);
314       glPushMatrix();
315       glLoadIdentity();
316       gluOrtho2D(-1, 1, -1, 1);
317       
318       glColor4f(1, 1, 1, 1);
319       glBegin(GL_QUADS);
320       glTexCoord2f(0, 0); glVertex2f(-1, -1);
321       glTexCoord2f(1, 0); glVertex2f(1, -1);
322       glTexCoord2f(1, 1); glVertex2f(1, 1);
323       glTexCoord2f(0, 1); glVertex2f(-1, 1);
324       glEnd();
325       
326       glPopMatrix();
327       glMatrixMode(GL_MODELVIEW);
328       glPopMatrix();
329     }
330   }
331   return SKYRESULT_OK;
332 }
333
334
335
336 //------------------------------------------------------------------------------
337 // Function               : SkyRenderableInstanceCloud::DisplayWithoutImpostor
338 // Description      : 
339 //------------------------------------------------------------------------------
340 /**
341  * @fn SkyRenderableInstanceCloud::DisplayWithoutImpostor(const Camera &cam)
342  * @brief Displays the cloud directly -- without an impotor.
343  * 
344  * This is used both when the impostor is disabled and to create the impostor image
345  * when it needs to be updated.
346  */ 
347 SKYRESULT SkyRenderableInstanceCloud::DisplayWithoutImpostor(const Camera &cam)
348 {
349   // Get and set the world space transformation
350   /*Mat44f mat;
351   GetModelToWorldTransform(mat);
352
353   glMatrixMode(GL_MODELVIEW);
354   glPushMatrix();
355   glMultMatrixf(mat.M);*/
356  
357   FAIL_RETURN_MSG(_pCloud->Display(cam, this), "SkyRenderableInstanceCloud:Display(): Cloud's display failed.");
358
359   //glMatrixMode(GL_MODELVIEW);
360   //glPopMatrix();
361
362   return SKYRESULT_OK;
363 }
364
365
366 //------------------------------------------------------------------------------
367 // Function               : SkyRenderableInstanceCloud::ViewFrustumCull
368 // Description      : 
369 //------------------------------------------------------------------------------
370 /**
371  * @fn SkyRenderableInstanceCloud::ViewFrustumCull(const Camera &cam)
372  * @brief View frustum cull the object given its world position
373  */ 
374 bool SkyRenderableInstanceCloud::ViewFrustumCull(const Camera &cam)
375 {
376   Mat44f xform;
377   //GetModelToWorldTransform(xform);
378   xform.Identity();
379   _bCulled = (_pWorldSpaceBV == NULL) ? false : _pWorldSpaceBV->ViewFrustumCull(cam, xform);
380   return _bCulled;
381 }
382
383
384 //------------------------------------------------------------------------------
385 // Function               : SkyRenderableInstanceCloud::ReleaseImpostorTextures
386 // Description      : 
387 //------------------------------------------------------------------------------
388 /**
389  * @fn SkyRenderableInstanceCloud::ReleaseImpostorTextures()
390  * @brief Causes the instance to release its impostor textures for use by other impostors.
391  * 
392  * This method is called when the cloud is view frustum culled.
393  */ 
394 void SkyRenderableInstanceCloud::ReleaseImpostorTextures()
395 {
396   _iCulledCount++;
397
398   if (_iCulledCount > SKYCLOUD_CULL_RELEASE_COUNT)
399   {
400     _iCulledCount = 0;
401   
402     if (_pBackTexture)
403     {
404       DynamicTextureManager::InstancePtr()->CheckInTexture(_pBackTexture);
405       _pBackTexture = NULL;
406     }
407    
408     if (_pFrontTexture)
409     {
410       DynamicTextureManager::InstancePtr()->CheckInTexture(_pFrontTexture);
411       _pFrontTexture = NULL;
412     }
413     _bImageExists = false;
414   }
415 }
416
417
418 //------------------------------------------------------------------------------
419 // Function               : SkyRenderableInstanceCloud::Update
420 // Description      : 
421 //------------------------------------------------------------------------------
422 /**
423  * @fn SkyRenderableInstanceCloud::Update(const Camera &cam)
424  * @brief Updates the impostor image to be valid for the current viewpoint.
425  * 
426  * If the image is already valid, exits early.
427  *
428  * @see SetErrorToleranceAngle, IsValid
429  */ 
430 SKYRESULT SkyRenderableInstanceCloud::Update(const Camera &cam)
431 {
432   if (!_bEnabled || IsImpostorValid(cam)) 
433     return SKYRESULT_OK;
434
435   // since we are going to update it anyway, let's make sure we don't try to use it if something
436   // goes wrong.  This will be set to true on the successful completion of this Update() method.
437   _bImageExists = false;
438 //cout << "updating impostor\n";
439   Mat44f M;
440   
441   _impostorCam = cam;
442   float rDistance  = (_vecPosition - cam.Orig).Length();
443   
444   float rRadius    = _pWorldSpaceBV->GetRadius();
445   float rCamRadius = sqrt(cam.wR*cam.wR + cam.Near*cam.Near);
446   
447   float rWidth     = cam.wR - cam.wL;
448   float rHeight    = cam.wT - cam.wB;
449   float rMaxdim    = (rWidth > rHeight) ? rWidth : rHeight;
450   
451   if (rRadius * cam.Near / rDistance < 0.5 * rMaxdim && (rDistance - rRadius > rCamRadius)) 
452   { // outside cloud
453     _impostorCam.TightlyFitToSphere(cam.Orig, cam.Y, _vecPosition, rRadius);
454     _rRadius = 0.5f * (_impostorCam.wR - _impostorCam.wL) * rDistance / _impostorCam.Near;
455     _rRadius *= GetScale();
456     _bScreenImpostor = false;   
457     // store points used in later error estimation 
458     _vecNearPoint   =   -_impostorCam.Z;
459     _vecNearPoint   *=  _impostorCam.Near;
460     _vecNearPoint   +=  _impostorCam.Orig;
461     _vecFarPoint    =   -_impostorCam.Z;
462     _vecFarPoint    *=  _impostorCam.Far;
463     _vecFarPoint    +=  _impostorCam.Orig;
464   }
465   else // inside cloud
466   {
467     _impostorCam.Far  =  _impostorCam.Near + 3 * rRadius;
468     _bScreenImpostor =  true;
469   }
470   
471   // resolution based on screensize, distance, and object size.
472   // Cam radius is used to heuristically reduce resolution for clouds very close to the camera.
473   _iLogResolution = _GetRequiredLogResolution(rDistance, rRadius, rCamRadius);
474
475   int iRes = 1 << _iLogResolution;
476
477   int iOldVP[4];
478
479   glGetIntegerv(GL_VIEWPORT, iOldVP);
480   
481   _impostorCam.GetProjectionMatrix(M);
482   glMatrixMode(GL_PROJECTION);
483   glPushMatrix();
484   glLoadMatrixf(M);
485   
486   _impostorCam.GetModelviewMatrix(M);
487   glMatrixMode(GL_MODELVIEW);
488   glPushMatrix();
489   glLoadMatrixf(M);
490   
491   glViewport(0, 0, iRes, iRes);
492   
493   s_pMaterial->SetDepthMask(true);  // so that the depth buffer gets cleared!
494   s_pMaterial->Activate();
495   glClearColor(0, 0, 0, 0);
496   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
497   
498   if (!_bSplit)
499   {
500     FAIL_RETURN(DisplayWithoutImpostor(_impostorCam));
501     
502     if (_pBackTexture && _pBackTexture->GetWidth() != iRes)
503     {
504       DynamicTextureManager::InstancePtr()->CheckInTexture(_pBackTexture);
505       _pBackTexture = NULL;
506     }
507     
508     if (!_pBackTexture)
509     {
510       _pBackTexture = DynamicTextureManager::InstancePtr()->CheckOutTexture(iRes, iRes);
511     }
512     
513     s_pMaterial->SetTexture(0, GL_TEXTURE_2D, *_pBackTexture);     // shared material for clouds. 
514     s_pMaterial->Activate();
515     
516     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, iRes, iRes);
517   }
518   else
519   {  
520     FAIL_RETURN_MSG(_pCloud->DisplaySplit(cam, _vecSplit, true, this), 
521                     "SkyRenderableInstanceCloud:Display(): Cloud's display failed.");
522
523     if (_pBackTexture && _pBackTexture->GetWidth() != iRes)
524     {
525       DynamicTextureManager::InstancePtr()->CheckInTexture(_pBackTexture);
526       _pBackTexture = NULL;
527     }
528     if (_pFrontTexture && _pFrontTexture->GetWidth() != iRes)
529     {
530       DynamicTextureManager::InstancePtr()->CheckInTexture(_pFrontTexture);
531       _pFrontTexture = NULL;
532     }
533
534     if (!_pBackTexture)
535     {
536       _pBackTexture = DynamicTextureManager::InstancePtr()->CheckOutTexture(iRes, iRes);
537     }
538
539     s_pMaterial->SetTexture(0, GL_TEXTURE_2D, *_pBackTexture);     // shared material for clouds. 
540     FAIL_RETURN(s_pMaterial->Activate());
541    
542     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, iRes, iRes);
543
544     // now clear and draw the front.
545     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
546
547     FAIL_RETURN_MSG(_pCloud->DisplaySplit(cam, _vecSplit, false, this), 
548                     "SkyRenderableInstanceCloud:Display(): Cloud's display failed.");
549
550     if (!_pFrontTexture)
551     {
552       _pFrontTexture = DynamicTextureManager::InstancePtr()->CheckOutTexture(iRes, iRes);
553     }
554
555     s_pMaterial->GetTextureState().SetTexture(0, GL_TEXTURE_2D, *_pFrontTexture);
556     FAIL_RETURN(s_pMaterial->GetTextureState().Activate());
557
558     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, iRes, iRes);
559   }
560   
561   glMatrixMode(GL_MODELVIEW);
562   glPopMatrix();
563   glMatrixMode(GL_PROJECTION);
564   glPopMatrix();
565   
566   //GLVU::CheckForGLError("Cloud Impostor Update");
567
568     glViewport(iOldVP[0], iOldVP[1], iOldVP[2], iOldVP[3]);
569
570   _bImageExists = true;
571
572   // the textures should now exist.
573   assert(_pBackTexture);
574   assert(!_bSplit || (_bSplit && _pFrontTexture));
575
576   return SKYRESULT_OK;
577 }
578
579
580 //------------------------------------------------------------------------------
581 // Function               : SkyRenderableInstanceCloud::IsImpostorValid
582 // Description      : 
583 //------------------------------------------------------------------------------
584 /**
585  * @fn SkyRenderableInstanceCloud::IsImpostorValid(const Camera& cam)
586  * @brief Returns true if the impostor image is valid for the given camera.
587  * 
588  * Will return false if this is a screen impostor, or if there is error in either
589  * the translation of the camera from the capture point or the impostor image resolution.
590  *
591  * @see SetErrorToleranceAngle
592  */ 
593 bool SkyRenderableInstanceCloud::IsImpostorValid(const Camera& cam)
594
595   // first make sure there is a current image.
596   if (!_bImageExists) 
597     return false;
598
599   // screen impostors should always be updated
600   if (_bScreenImpostor)
601   {
602     _vecFarPoint = Vec3f::ZERO;
603     _vecNearPoint = Vec3f::ZERO;
604 #if SKYCLOUD_VERBOSE
605     SkyTrace("Screen Impostor Update");
606 #endif
607     return false;
608   }
609   // impostors are valid from the viewpoint from which they were captured
610   if (cam.Orig == _impostorCam.Orig) 
611     return true;
612   
613   if (_bSplit) 
614   {
615   #if SKYCLOUD_VERBOSE
616     SkyTrace("Split Impostor Update");
617   #endif
618     return false;
619   }
620   
621   Vec3f vecX = _vecNearPoint - cam.Orig;
622   Vec3f vecY = _vecFarPoint - cam.Orig;
623   float rXLength = vecX.Length();
624   float rYLength = vecY.Length();
625   if (rXLength > rYLength) 
626   {
627 #if SKYCLOUD_VERBOSE
628     SkyTrace("Backwards Impostor Update");
629 #endif
630     return false;
631   }
632   
633   vecX /= rXLength; 
634   vecY /= rYLength; 
635   float rCosAlpha = vecX * vecY; // dot product of normalized vectors = cosine
636   
637   if (fabs(rCosAlpha) < 1.0) 
638   {
639     float rAlpha = acos(rCosAlpha);
640     if (rAlpha >= s_rErrorToleranceAngle) 
641     {
642 #if SKYCLOUD_VERBOSE
643       SkyTrace("Angle Error Update %f", SKYRADSTODEGREES * rAlpha);
644 #endif
645       return false;
646     }
647   }
648  
649   float rDistance = (_vecPosition - cam.Orig).Length();
650   float rCamRadius = sqrt(cam.wR*cam.wR + cam.Near*cam.Near);
651   
652   int iRes = _GetRequiredLogResolution(rDistance, _pWorldSpaceBV->GetRadius(), rCamRadius);
653
654   if (iRes > _iLogResolution)
655   {
656 #if SKYCLOUD_VERBOSE
657     SkyTrace("Resolution Error Update: Required: %d Actual: %d", iRes, _iLogResolution);
658 #endif
659     return false;
660   }
661  
662   return true;
663 }
664
665
666 //------------------------------------------------------------------------------
667   // Function             : SetErrorToleranceAngle
668   // Description            : 
669   //------------------------------------------------------------------------------
670   /**
671   * @fn SkyRenderableInstanceCloud::SetErrorToleranceAngle(float rDegrees)
672   * @brief Set the global error tolerance angle for all impostors.
673   */ 
674 void SkyRenderableInstanceCloud::SetErrorToleranceAngle(float rDegrees)
675
676   s_rErrorToleranceAngle = SKYDEGREESTORADS * rDegrees; 
677 }
678
679
680
681 //------------------------------------------------------------------------------
682 // Function               : SkyRenderableInstanceCloud::_Initialize
683 // Description      : 
684 //------------------------------------------------------------------------------
685 /**
686  * @fn SkyRenderableInstanceCloud::_Initialize()
687  * @brief Initializer used by the constructors.
688  */ 
689 void SkyRenderableInstanceCloud::_Initialize()
690 {
691   _UpdateWorldSpaceBounds();
692
693 //  if (!s_pRenderBuffer && _bUseOffScreenBuffer)
694 //  {
695 //JW??    s_pRenderBuffer = new SkyOffScreenBuffer(GLUT_SINGLE | GLUT_DEPTH | GLUT_STENCIL);
696 //JW??    s_pRenderBuffer->Initialize(true);
697     
698 //JW??    s_pRenderBuffer->MakeCurrent();
699     // set some GL state:
700 //    glClearColor(0, 0, 0, 0);
701 //JW??    GLVU::GetCurrent()->MakeCurrent();
702 //  }
703   if (!s_pMaterial)
704   {
705     s_pMaterial = new SkyMaterial;
706     s_pMaterial->EnableBlending(true);
707     s_pMaterial->SetBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
708     s_pMaterial->SetAlphaFunc(GL_GREATER);
709     s_pMaterial->EnableDepthTest(false);
710     s_pMaterial->SetDepthMask(true);
711     s_pMaterial->EnableAlphaTest(true);
712     s_pMaterial->EnableLighting(false);
713     s_pMaterial->SetTextureParameter(0, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
714     s_pMaterial->SetTextureParameter(0, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
715     s_pMaterial->SetTextureParameter(0, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
716     s_pMaterial->EnableTexture(0, true);    
717   }
718   s_iCount++;
719 }