]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/clouds3d/SkySceneManager.cpp
Clouds3D crashes because there is no Light
[simgear.git] / simgear / scene / sky / clouds3d / SkySceneManager.cpp
1 //------------------------------------------------------------------------------
2 // File : SkySceneManager.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 
17 // implied warranty.
18
19 /**
20  * @file SkySceneManager.cpp
21  * 
22  * Implementation of the singleton class SkySceneManager, which manages objects,
23  * instances, scene update, visibility, culling, and rendering.
24  */
25
26 // warning for truncation of template name for browse info
27 #pragma warning( disable : 4786)
28
29 #include "SkySceneManager.hpp"
30 #include "SkyMaterial.hpp"
31 #include "SkyLight.hpp"
32 #include "SkyCloud.hpp"
33 #include "SkyRenderable.hpp"
34 #include "SkyRenderableInstance.hpp"
35 #include "SkyRenderableInstanceCloud.hpp"
36
37 #include "camutils.hpp"
38 #include <algorithm>
39
40 //------------------------------------------------------------------------------
41 // Function               : SkySceneManager::SkySceneManager
42 // Description      : 
43 //------------------------------------------------------------------------------
44 /**
45  * @fn SkySceneManager::SkySceneManager()
46  * @brief Constructor
47  */ 
48 SkySceneManager::SkySceneManager()
49 : /*_pSkyBox(NULL),
50   _pTerrain(NULL),*/
51   _bDrawLights(false),
52   _bDrawTree(false),
53   _bReshadeClouds(true)
54 {
55   _wireframeMaterial.SetColorMaterialMode(GL_DIFFUSE);
56   _wireframeMaterial.EnableColorMaterial(true);
57   _wireframeMaterial.EnableLighting(false);
58
59   // add the default material with ID -1 
60   // this should avoid errors caused by models without materials exported from MAX
61   // (because flexporter gives them the ID -1).
62   SkyMaterial *pDefaultMaterial = new SkyMaterial;
63   pDefaultMaterial->SetMaterialID(-1);
64   AddMaterial(pDefaultMaterial);
65 }
66
67 //------------------------------------------------------------------------------
68 // Function               : SkySceneManager::~SkySceneManager
69 // Description      : 
70 //------------------------------------------------------------------------------
71 /**
72  * @fn SkySceneManager::~SkySceneManager()
73  * @brief Destructor.
74  *
75  * This destructor deletes all renderables, instances (renderable instances, cloud instances, 
76  * and otherwise), materials, and lights that were added to the scene using the Add*() functions.
77  * In other words, the scene manager owns all entities added to the scene.  This eases cleanup 
78  * and shutdown.
79  */ 
80 SkySceneManager::~SkySceneManager()
81 {
82   ObjectIterator oi;
83   for (oi = _objects.begin(); oi != _objects.end(); ++oi)
84     SAFE_DELETE(*oi);
85   _objects.clear();
86
87   CloudIterator ci;
88   for (ci = _clouds.begin(); ci != _clouds.end(); ++ci)
89     SAFE_DELETE(*ci);
90   _clouds.clear();
91
92   InstanceIterator ii;
93   for (ii = _instances.begin(); ii != _instances.end(); ++ii)
94     SAFE_DELETE(*ii);
95   _instances.clear();
96
97   CloudInstanceIterator cii;
98   for (cii = _cloudInstances.begin(); cii != _cloudInstances.end(); ++cii)
99     SAFE_DELETE(*cii);
100   _cloudInstances.clear();
101
102   ContainerSetIterator cni;
103   for (cni = _containerClouds.begin(); cni != _containerClouds.end(); ++cni)
104     SAFE_DELETE(cni->second);
105   _containerClouds.clear();
106
107   MaterialIterator mi;
108   for (mi = _materials.begin(); mi != _materials.end(); ++mi)
109     SAFE_DELETE(mi->second);
110   _materials.clear();
111
112   LightIterator li;
113   for (li = _lights.begin(); li!= _lights.end(); ++li)
114     SAFE_DELETE(li->second);
115   _lights.clear();
116
117   //SAFE_DELETE(_pSkyBox);
118   //SAFE_DELETE(_pTerrain);
119 }
120
121 //------------------------------------------------------------------------------
122 // Function               : SkySceneManager::AddObject
123 // Description      : 
124 //------------------------------------------------------------------------------
125 /**
126  * @fn SkySceneManager::AddObject(SkyRenderable *pObject)
127  * @brief Add a new SkyRenderable object to the manager.
128  */ 
129 SKYRESULT SkySceneManager::AddObject(SkyRenderable *pObject)
130 {
131   // Check for null object
132   if (NULL == pObject)
133   {
134     FAIL_RETURN_MSG(SKYRESULT_FAIL, 
135                     "SkySceneManager::AddObject(): Attempting to add NULL Renderable Object.");
136   }
137
138   _objects.push_back(pObject);
139
140   return SKYRESULT_OK;
141 }
142
143
144 //------------------------------------------------------------------------------
145 // Function               : SkySceneManager::AddInstance
146 // Description      : 
147 //------------------------------------------------------------------------------
148 /**
149 * @fn SkySceneManager::AddInstance(SkyRenderableInstance *pInstance, bool bTransparent)
150 * @brief Add a new SkyRenderableInstance to the manager.
151 */ 
152 SKYRESULT SkySceneManager::AddInstance(SkyRenderableInstance *pInstance, bool bTransparent /* = false */)
153 {
154   // Check for null instance
155   if (NULL == pInstance)
156   {
157     FAIL_RETURN_MSG(SKYRESULT_FAIL, 
158       "SkySceneManager::AddObject(): Attempting to add NULL Renderable Instance.");
159   }
160   
161   if (!bTransparent)
162     _instances.push_back(pInstance);
163   else
164     _transparentInstances.push_back(pInstance);
165   
166   return SKYRESULT_OK;
167 }
168
169
170 //------------------------------------------------------------------------------
171 // Function               : SkySceneManager::AddCloud
172 // Description      : 
173 //------------------------------------------------------------------------------
174 /**
175  * @fn SkySceneManager::AddCloud(SkyCloud *pCloud)
176  * @brief Add a new cloud object to the manager.
177  * 
178  * @todo <WRITE EXTENDED SkySceneManager::AddCloud FUNCTION DOCUMENTATION>
179  */ 
180 SKYRESULT SkySceneManager::AddCloud(SkyCloud *pCloud)
181 {
182   if (NULL == pCloud)
183   {
184     FAIL_RETURN_MSG(SKYRESULT_FAIL, 
185       "SkySceneManager::AddObject(): Attempting to add NULL SkyCloud Object.");
186   }
187   
188   _clouds.push_back(pCloud);
189   
190   return SKYRESULT_OK;
191 }
192
193
194
195 //------------------------------------------------------------------------------
196 // Function               : SkySceneManager::AddCloudInstance
197 // Description      : 
198 //------------------------------------------------------------------------------
199 /**
200  * @fn SkySceneManager::AddCloudInstance(SkyRenderableInstanceCloud *pInstance)
201  * @brief Add a new instance of a cloud to the manager.
202  * 
203  * @todo Note that since cloud instances share shading information, if two instances
204  * of a cloud have different orientations, one of the instances will have incorrect
205  * lighting for the scene.  For this reason, I recommend that the number of clouds and 
206  * cloud instances is equal.
207  */ 
208 SKYRESULT SkySceneManager::AddCloudInstance(SkyRenderableInstanceCloud *pInstance)
209 {
210   // Check for null instance
211   if (NULL == pInstance)
212   {
213     FAIL_RETURN_MSG(SKYRESULT_FAIL, 
214       "SkySceneManager::AddObject(): Attempting to add NULL SkyCloud Instance.");
215   }
216   
217   pInstance->SetID(_cloudInstances.size());
218
219   _cloudInstances.push_back(pInstance);
220
221   SkyContainerCloud *pContainer = new SkyContainerCloud(pInstance);
222   _containerClouds.insert(std::make_pair(pInstance->GetID(), pContainer));
223
224   RebuildCloudBVTree();
225   
226   return SKYRESULT_OK;
227 }
228
229
230 //------------------------------------------------------------------------------
231 // Function               : SkySceneManager::AddMaterial
232 // Description      : 
233 //------------------------------------------------------------------------------
234 /**
235 * @fn SkySceneManager::AddMaterial(SkyMaterial *pMaterial)
236 * @brief Adds a material to the scene.
237
238 * Materials are kept in a map with their ID as key.  A material can be retrieved
239 * from the scene manager by passing its ID to GetMaterial.
240 *
241 * @see GetMaterial, SkyMaterial
242 */ 
243 SKYRESULT SkySceneManager::AddMaterial(SkyMaterial *pMaterial)
244 {
245   // Check for null instance
246   if (NULL == pMaterial)
247   {
248     FAIL_RETURN_MSG(SKYRESULT_FAIL, 
249       "SkySceneMananger::AddMaterial(): Attempting to add NULL Material to Scene Manager");
250   }
251   
252   _materials.insert(std::make_pair(pMaterial->GetMaterialID(), pMaterial));
253   return SKYRESULT_OK;
254 }
255
256
257
258 //------------------------------------------------------------------------------
259 // Function               : SkySceneManager::GetMaterial
260 // Description      : 
261 //------------------------------------------------------------------------------
262 /**
263  * @fn SkySceneManager::GetMaterial(int iMaterialID)
264  * @brief Returns the material with ID @a iMaterialID.
265  * 
266  * If the material is not found, returns NULL.
267  *
268  * @see AddMaterial, SkyMaterial
269  */ 
270 SkyMaterial* SkySceneManager::GetMaterial(int iMaterialID)
271 {
272   MaterialIterator mi = _materials.find(iMaterialID);
273   if (_materials.end() == mi)
274   {
275     SkyTrace("SkySceneManager::GetMaterial: Error: invalid material ID");
276     return NULL;
277   }
278   else
279     return mi->second;
280 }
281
282
283 //------------------------------------------------------------------------------
284 // Function               : SkySceneManager::ActivateMaterial
285 // Description      : 
286 //------------------------------------------------------------------------------
287 /**
288  * @fn SkySceneManager::ActivateMaterial(int iMaterialID)
289  * @brief Makes the specified material active, setting the appropriate rendering state.
290  * 
291  * @todo <WRITE EXTENDED SkySceneManager::ActivateMaterial FUNCTION DOCUMENTATION>
292  */ 
293 SKYRESULT SkySceneManager::ActivateMaterial(int iMaterialID)
294 {
295 //   cout  << "Activating material\n"; char mm; cin >> mm;
296   MaterialIterator mi = _materials.find(iMaterialID);
297   if (_materials.end() == mi)
298   {
299     FAIL_RETURN_MSG(SKYRESULT_FAIL, 
300                     "SkySceneManager::ActivateMaterial: Error: invalid material ID.");
301   }
302   else
303   {
304     FAIL_RETURN_MSG(mi->second->Activate(), 
305                     "SkySceneManager::ActivateMaterial: Error: failed to activate.");
306   }
307
308   return SKYRESULT_OK;
309 }
310
311
312 //------------------------------------------------------------------------------
313 // Function               : SkySceneManager::AddLight
314 // Description      : 
315 //------------------------------------------------------------------------------
316 /**
317  * @fn SkySceneManager::AddLight(SkyLight *pLight)
318  * @brief @todo <WRITE BRIEF SkySceneManager::AddLight DOCUMENTATION>
319  * 
320  * @todo <WRITE EXTENDED SkySceneManager::AddLight FUNCTION DOCUMENTATION>
321  */ 
322 SKYRESULT SkySceneManager::AddLight(SkyLight *pLight)
323 {
324   // Check for null instance
325   if (NULL == pLight)
326   {
327     FAIL_RETURN_MSG(SKYRESULT_FAIL, 
328                     "SkySceneMananger::AddLight(): Attempting to add NULL Light to Scene Manager");
329   }
330   
331   _lights.insert(std::make_pair(_lights.size(), pLight));
332   return SKYRESULT_OK;
333 }
334
335
336 //------------------------------------------------------------------------------
337 // Function               : SkySceneManager::GetLight
338 // Description      : 
339 //------------------------------------------------------------------------------
340 /**
341  * @fn SkySceneManager::GetLight(int iLightID)
342  * @brief @todo <WRITE BRIEF SkySceneManager::GetLight DOCUMENTATION>
343  * 
344  * @todo <WRITE EXTENDED SkySceneManager::GetLight FUNCTION DOCUMENTATION>
345  */ 
346 SkyLight* SkySceneManager::GetLight(int iLightID)
347 {
348   LightIterator li = _lights.find(iLightID);
349   if (_lights.end() == li)
350   {
351     SkyTrace("SkySceneManager::GetLight: Error: Invalid light ID");
352     return NULL;
353   }
354   else
355     return li->second;
356 }
357
358
359 //------------------------------------------------------------------------------
360 // Function               : Alive
361 // Description      : 
362 //------------------------------------------------------------------------------
363 /**
364  * @fn Alive(SkyRenderableInstance* pInstance)
365  * @brief A predicate to determine if an object is dead or not.
366  */ 
367 bool Alive(SkyRenderableInstance* pInstance)
368 {
369   return (pInstance->IsAlive());
370 }
371
372
373 //------------------------------------------------------------------------------
374 // Function               : SkySceneManager::Update
375 // Description      : 
376 //------------------------------------------------------------------------------
377 /**
378 * @fn SkySceneManager::Update(const Camera &cam)
379 * @brief Iterate through all SkyRenderableInstances and update them.
380 */ 
381 SKYRESULT SkySceneManager::Update(const Camera &cam)
382 {
383   _ResolveVisibility(cam);
384   
385   return SKYRESULT_OK;
386 }
387
388
389 //------------------------------------------------------------------------------
390 // Function               : SkySceneManager::Display
391 // Description      : 
392 //------------------------------------------------------------------------------
393 /**
394 * @fn SkySceneManager::Display(const Camera &cam)
395 * @brief Iterate through all SkyRenderableInstances and display them.
396 */ 
397 SKYRESULT SkySceneManager::Display( const Camera &cam )
398
399 {
400   // _clearMaterial.Force();
401   _clearMaterial.Activate();
402   //glClear(GL_DEPTH_BUFFER_BIT);
403
404   // set lights (only lights that have changed will be passed to GL).
405   for (LightIterator li = _lights.begin(); li != _lights.end(); ++li)
406   {
407     li->second->Activate(li->first);
408     //if (_bDrawLights)
409     //li->second->Display();
410   }
411  
412   //if (_bDrawTree)// force the issue and draw
413     //_VisualizeCloudBVTree(cam, _cloudBVTree.GetRoot());
414 /*    
415   glLineWidth(2.0);
416   glBegin(GL_LINES);
417   //  red is Cartesian y-axis
418   glColor3ub( 255, 0, 0 );
419   glVertex3f( 0.0,0.0,0.0 );
420   glVertex3f( 0.0, -104000.0, 0.0);
421   // yellow is Cartesian z-axis
422   glColor3ub( 255, 255, 0 );
423   glVertex3f( 0.0, 0.0, 0.0);
424   glVertex3f( 0.0, 0.0, 104000.0);
425   // blue is Cartesian x-axis
426   glColor3ub( 0, 0, 255 );
427   glVertex3f( 0.0, 0.0, 0.0);
428   glVertex3f( -104000.0, 0.0, 0.0);
429   glEnd();
430 */
431   // draw all container clouds and "free" objects not in clouds.
432   //int i = 0;
433   for (InstanceIterator iter = _visibleInstances.begin(); iter != _visibleInstances.end(); ++iter)
434   {
435     FAIL_RETURN_MSG((*iter)->Display(),
436                   "SkySceneManager::Display(): instance display failed.");
437     //i++;
438   }
439   //cout << "There are " << i << " visible clouds\n";
440  
441   return SKYRESULT_OK;
442 }
443
444
445 //------------------------------------------------------------------------------
446 // Function               : SkySceneManager::RebuildCloudBVTree
447 // Description      : 
448 //------------------------------------------------------------------------------
449 /**
450  * @fn SkySceneManager::RebuildCloudBVTree()
451  * @brief Builds an AABB tree of the cloud bounding volumes.  
452  */ 
453 SKYRESULT SkySceneManager::RebuildCloudBVTree()
454 {
455   CloudInstanceIterator cii;
456   SkyMinMaxBox bbox;
457
458   _cloudBVTree.BeginTree();
459   for (cii = _cloudInstances.begin(); cii != _cloudInstances.end(); ++cii)
460   {
461     bbox = (*cii)->GetWorldSpaceBounds();
462     _cloudBVTree.AddObject(*cii, bbox);
463   }
464   _cloudBVTree.EndTree();
465
466   return SKYRESULT_OK;
467 }
468
469
470 //------------------------------------------------------------------------------
471 // Function               : SkySceneManager::ShadeClouds
472 // Description      : 
473 //------------------------------------------------------------------------------
474 /**
475  * @fn SkySceneManager::ShadeClouds()
476  * @brief @todo <WRITE BRIEF SkySceneManager::ShadeClouds DOCUMENTATION>
477  * 
478  * @todo <WRITE EXTENDED SkySceneManager::ShadeClouds FUNCTION DOCUMENTATION>
479  */ 
480 SKYRESULT SkySceneManager::ShadeClouds()
481 {
482 //   cout  <<  "SkySceneManager::ShadeClouds()\n";
483   int i=0;
484   
485   for (CloudInstanceIterator cii = _cloudInstances.begin(); cii != _cloudInstances.end(); ++cii)
486   {
487     for (LightIterator li = _lights.begin(); li != _lights.end(); ++li)
488     {
489       
490       if (SkyLight::SKY_LIGHT_DIRECTIONAL == li->second->GetType())
491       {
492         (*cii)->GetCloud()->Illuminate(li->second, *cii, li == _lights.begin());
493 //         printf("Shading Cloud %d of %d with light %d \n", i++, _cloudInstances.size(), *li );
494       }
495     }
496   }
497   _bReshadeClouds = false;
498   return SKYRESULT_OK;
499 }
500
501
502 //------------------------------------------------------------------------------
503 // Function               : SkySceneManager::LoadClouds
504 // Description      :
505 //------------------------------------------------------------------------------
506 /**
507  * @fn SkySceneManager::LoadClouds(SkyArchive& cloudArchive, float rScale)
508  * @brief @todo <WRITE BRIEF SkySceneManager::LoadClouds DOCUMENTATION>
509  *
510  * @todo <WRITE EXTENDED SkySceneManager::LoadClouds FUNCTION DOCUMENTATION>
511  */
512 SKYRESULT SkySceneManager::LoadClouds(unsigned char *data, unsigned int size, float rScale, double latitude, double longitude)
513 {
514   SkyCloud *pCloud = new SkyCloud();
515   pCloud->Load(data, size, rScale, latitude, longitude);
516   SkyRenderableInstanceCloud *pInstance = new SkyRenderableInstanceCloud(pCloud, false);
517   AddCloud(pCloud);
518   AddCloudInstance(pInstance);
519
520   RebuildCloudBVTree();
521   return SKYRESULT_OK;
522 }
523
524
525 //------------------------------------------------------------------------------
526 // Function               : SkySceneManager::LoadClouds
527 // Description      : 
528 //------------------------------------------------------------------------------
529 /**
530  * @fn SkySceneManager::LoadClouds(SkyArchive& cloudArchive, float rScale)
531  * @brief @todo <WRITE BRIEF SkySceneManager::LoadClouds DOCUMENTATION>
532  * 
533  * @todo <WRITE EXTENDED SkySceneManager::LoadClouds FUNCTION DOCUMENTATION>
534  */ 
535 SKYRESULT SkySceneManager::LoadClouds(SkyArchive& cloudArchive, float rScale, double latitude, double longitude)
536 {
537   unsigned int iNumClouds = 0;
538   cloudArchive.FindUInt32("CldNumClouds", &iNumClouds);
539   iNumClouds = ulEndianLittle32(iNumClouds);
540  
541   SkyArchive subArchive;
542         //iNumClouds = 5;  //set this value to reduce cloud field for debugging
543   for (int i = 0; i < iNumClouds; ++i)
544   {printf("Loading # %d of %d clouds\n", i+1, iNumClouds);
545     cloudArchive.FindArchive("Cloud", &subArchive, i);
546     SkyCloud *pCloud = new SkyCloud();
547     pCloud->Load(subArchive, rScale, latitude, longitude);
548     SkyRenderableInstanceCloud *pInstance = new SkyRenderableInstanceCloud(pCloud, false);
549     AddCloud(pCloud);
550     AddCloudInstance(pInstance);
551   }
552   RebuildCloudBVTree();
553   return SKYRESULT_OK;
554 }
555
556 //------------------------------------------------------------------------------
557 // Function               : SkySceneManager::_SortClouds
558 // Description      : 
559 //------------------------------------------------------------------------------
560 /**
561  * @fn SkySceneManager::_SortClouds(CloudInstanceArray& clouds, const Vec3f& vecSortPoint)
562  * @brief @todo <WRITE BRIEF SkySceneManager::_SortClouds DOCUMENTATION>
563  * 
564  * @todo <WRITE EXTENDED SkySceneManager::_SortClouds FUNCTION DOCUMENTATION>
565  */ 
566 void SkySceneManager::_SortClouds(CloudInstanceArray& clouds, const Vec3f& vecSortPoint)
567 {
568   static InstanceComparator comparator;
569
570   for (CloudInstanceIterator cii = clouds.begin(); cii != clouds.end(); ++cii)
571   {
572     Vec3f vecPos = (*cii)->GetPosition();
573     vecPos -= vecSortPoint;
574     (*cii)->SetSquareSortDistance(vecPos.LengthSqr());
575   }
576
577   std::sort(clouds.begin(), clouds.end(), comparator);
578 }
579
580
581 //------------------------------------------------------------------------------
582 // Function               : SkySceneManager::_SortInstances
583 // Description      : 
584 //------------------------------------------------------------------------------
585 /**
586  * @fn SkySceneManager::SortInstances(InstanceArray& instances, const Vec3f& vecSortPoint)
587  * @brief @todo <WRITE BRIEF SkySceneManager::_SortInstances DOCUMENTATION>
588  * 
589  * @todo <WRITE EXTENDED SkySceneManager::_SortInstances FUNCTION DOCUMENTATION>
590  */ 
591 void SkySceneManager::SortInstances(InstanceArray& instances, const Vec3f& vecSortPoint)
592 {
593   static InstanceComparator comparator;
594
595   for (InstanceIterator ii = instances.begin(); ii != instances.end(); ++ii)
596   {
597     Vec3f vecPos = (*ii)->GetPosition();
598     vecPos -= vecSortPoint;
599     (*ii)->SetSquareSortDistance(vecPos.LengthSqr());
600   }
601   
602   std::sort(instances.begin(), instances.end(), comparator);
603 }
604
605
606 //------------------------------------------------------------------------------
607 // Function               : SkySceneManager::_ViewFrustumCullClouds
608 // Description      : 
609 //------------------------------------------------------------------------------
610 /**
611  * @fn SkySceneManager::_ViewFrustumCullClouds(const Camera& cam, const CloudBVTree::Node *pNode)
612  * @brief @todo <WRITE BRIEF SkySceneManager::_ViewFrustumCullClouds DOCUMENTATION>
613  * 
614  * @todo <WRITE EXTENDED SkySceneManager::_ViewFrustumCullClouds FUNCTION DOCUMENTATION>
615  */ 
616 void SkySceneManager::_ViewFrustumCullClouds(const Camera& cam, const CloudBVTree::Node *pNode)
617
618   if (!pNode)
619     return;
620
621   int i;
622   int iResult = CamMinMaxBoxOverlap(&cam, pNode->GetNodeBV().GetMin(), pNode->GetNodeBV().GetMax());
623   
624  //iResult = COMPLETEIN; // just a hack to force the issue
625   if (COMPLETEIN == iResult)
626   {
627     // trivially add all instances 
628     for (i = 0; i < pNode->GetNumObjs(); ++i)
629     {
630       SkyRenderableInstanceCloud* pInstance = 
631         const_cast<SkyRenderableInstanceCloud*>(pNode->GetObj(i));
632       _visibleCloudInstances.push_back(pInstance);
633     }
634   }
635   else if ((PARTIAL == iResult) && pNode->IsLeaf())
636   {
637     SkyMinMaxBox bbox;
638     
639     // check each instance in this node against camera
640     for (i = 0; i < pNode->GetNumObjs(); ++i)
641     {
642       SkyRenderableInstanceCloud* pInstance = 
643         const_cast<SkyRenderableInstanceCloud*>(pNode->GetObj(i));
644       bbox = pInstance->GetWorldSpaceBounds();
645       iResult = CamMinMaxBoxOverlap(&cam, bbox.GetMin(), bbox.GetMax());
646       if (COMPLETEOUT != iResult)
647         _visibleCloudInstances.push_back(pInstance);
648       else
649         pInstance->ReleaseImpostorTextures();
650     }
651   }
652   else if (PARTIAL == iResult)
653   {
654     _ViewFrustumCullClouds(cam, pNode->GetLeftChild());
655     _ViewFrustumCullClouds(cam, pNode->GetRightChild());
656   }
657   else // the node is completely out.  All of its child clouds should release their textures.
658   {
659     for (i = 0; i < pNode->GetNumObjs(); ++i)
660     {
661       SkyRenderableInstanceCloud* pInstance = 
662         const_cast<SkyRenderableInstanceCloud*>(pNode->GetObj(i));
663       pInstance->ReleaseImpostorTextures();
664     }
665   }
666 }
667
668
669 //------------------------------------------------------------------------------
670 // Function               : SkySceneManager::_VisualizeCloudBVTree
671 // Description      : 
672 //------------------------------------------------------------------------------
673 /**
674  * @fn SkySceneManager::_VisualizeCloudBVTree(const Camera& cam, const CloudBVTree::Node *pNode)
675  * @brief @todo <WRITE BRIEF SkySceneManager::_VisualizeCloudBVTree DOCUMENTATION>
676  * 
677  * @todo <WRITE EXTENDED SkySceneManager::_VisualizeCloudBVTree FUNCTION DOCUMENTATION>
678  */ 
679 void SkySceneManager::_VisualizeCloudBVTree(const Camera& cam, const CloudBVTree::Node *pNode)
680 {
681   // set display state.
682   _wireframeMaterial.Activate();
683
684   int iResult = CamMinMaxBoxOverlap(&cam, pNode->GetNodeBV().GetMin(), pNode->GetNodeBV().GetMax());
685
686   if (COMPLETEIN == iResult)
687   {
688     // draw this node's bounding box in green.
689     glColor3f(0, 1, 0);
690     pNode->GetNodeBV().Display();
691   }
692   else if (PARTIAL == iResult)
693   {
694     SkyMinMaxBox bbox;
695
696     if (pNode->IsLeaf())
697     {
698       // draw this node's bounding box and the boxes of all of its objects that are visible.
699       // draw this node's bbox in orange.
700       glColor3f(1, 0.5, 0);
701       pNode->GetNodeBV().Display();
702
703       int i;
704       for (i = 0; i < pNode->GetNumObjs(); ++i)
705       {
706         SkyRenderableInstanceCloud* pInstance = 
707           const_cast<SkyRenderableInstanceCloud*>(pNode->GetObj(i));
708         bbox = pInstance->GetWorldSpaceBounds();
709         iResult = CamMinMaxBoxOverlap(&cam, bbox.GetMin(), bbox.GetMax());
710
711         if (COMPLETEIN == iResult)
712         {
713           // draw the box in green
714           glColor3f(0, 1, 0);
715           bbox.Display();
716         }
717         else if (PARTIAL == iResult)
718         {
719           // draw the box in yellow
720           glColor3f(1, 1, 0);
721           bbox.Display();          
722         }
723       }
724     }
725     else
726     {
727       _VisualizeCloudBVTree(cam, pNode->GetLeftChild());
728       _VisualizeCloudBVTree(cam, pNode->GetRightChild());
729     }
730   }
731   else
732   {
733     // draw the node's bbox in red.  
734     // This should NEVER be visible from the camera from which it was culled!
735     glColor3f(1, 0, 0);
736     pNode->GetNodeBV().Display();
737   }
738 }
739
740
741 //------------------------------------------------------------------------------
742 // Function               : SkySceneManager::_ResolveVisibility
743 // Description      : 
744 //------------------------------------------------------------------------------
745 /**
746  * @fn SkySceneManager::_ResolveVisibility(const Camera &cam)
747  * @brief @todo <WRITE BRIEF SkySceneManager::_ResolveRenderingOrder DOCUMENTATION>
748  * 
749  * @todo <WRITE EXTENDED SkySceneManager::_ResolveRenderingOrder FUNCTION DOCUMENTATION>
750  */ 
751 SKYRESULT SkySceneManager::_ResolveVisibility(const Camera &cam)
752 {
753   InstanceIterator ii;
754
755   // clear the free instance array
756   _visibleInstances.clear();
757
758   // clear the contained instance arrays
759   ContainerSetIterator csi;
760   for (csi = _containerClouds.begin(); csi != _containerClouds.end(); ++csi)
761   {
762     csi->second->containedOpaqueInstances.clear();
763     csi->second->containedTransparentInstances.clear();
764   }
765
766   // clear the visible cloud array.
767   _visibleCloudInstances.clear();
768   
769   // Test each instance for containment inside a cloud's bounding volume.
770   // If the instance is inside a cloud, it is considered a "contained" instance, and will be 
771   // rendered with the cloud in which it is contained for correct visibility.  If the instance is
772   // not inside any cloud, then it is a "free" instance, and will be rendered after all contained
773   // instances.  Transparent instances of each type are rendered after opaque instances of each 
774   // type.
775
776   // opaque instances
777   for (ii = _instances.begin(); ii != _instances.end(); ++ii)
778   {
779 //     cout <<  "Opague instance\n"; char zz; cin >> zz;
780     (*ii)->ViewFrustumCull(cam);  // First VFC then check if culled, some instances may
781     // manually set the culled flag, instead of using VFC  
782     if (!(*ii)->IsCulled())
783     {
784       // first update this instance.
785       FAIL_RETURN_MSG((*ii)->Update(cam), "SkySceneManager::_ResolveVisibility(): instance update failed.");
786       
787       if (!_TestInsertInstanceIntoClouds(cam, _cloudBVTree.GetRoot(), *ii, false))
788         _visibleInstances.push_back(*ii);
789     }
790   }
791
792   // transparent instances
793   for (ii = _transparentInstances.begin(); ii != _transparentInstances.end(); ++ii)
794   {
795 //     cout << "Transparent instance\n"; char tt; cin >> tt;
796     (*ii)->ViewFrustumCull(cam);  // First VFC then check if culled, some instances may
797     // manually set the culled flag, instead of using VFC  
798     if (!(*ii)->IsCulled())
799     {
800       // first update this instance.
801       FAIL_RETURN_MSG((*ii)->Update(cam), "SkySceneManager::Update(): instance update failed.");
802       
803       if (!_TestInsertInstanceIntoClouds(cam, _cloudBVTree.GetRoot(), *ii, true))
804         _visibleInstances.push_back(*ii);
805     }
806   }
807
808   // view frustum cull the clouds
809   _ViewFrustumCullClouds(cam, _cloudBVTree.GetRoot());  
810
811   // Clouds must be rendered in sorted order.
812   //_SortClouds(_visibleCloudInstances, cam.Orig);
813
814   // reshade the clouds if necessary.
815   if (_bReshadeClouds)
816   {
817   printf("ReShading clouds\n");
818     FAIL_RETURN(ShadeClouds());
819   }
820
821   // Now process the visible clouds.  First, go through the container clouds corresponding to the
822   // clouds, calculate their split points, and update their impostors.
823   for (CloudInstanceIterator cii = _visibleCloudInstances.begin(); 
824        cii != _visibleCloudInstances.end(); 
825        ++cii)
826   {
827     // get the container corresponding to this cloud
828     ContainerSetIterator csi = _containerClouds.find((*cii)->GetID());
829    
830     if (csi == _containerClouds.end())
831     {
832       SkyTrace("Error: SkySceneManager::_ResolveVisibility(): Invalid cloud instance %d.", 
833                (*cii)->GetID());
834       return SKYRESULT_FAIL;
835     }
836     
837     if (csi->second->containedOpaqueInstances.size() > 0 ||
838         csi->second->containedTransparentInstances.size() > 0)
839     {
840       SortInstances(csi->second->containedOpaqueInstances, cam.Orig);
841       SortInstances(csi->second->containedTransparentInstances, cam.Orig);
842     
843     
844       SkyRenderableInstance *pOpaque = (csi->second->containedOpaqueInstances.size() > 0) ? 
845                                         csi->second->containedOpaqueInstances.back() : NULL;
846       SkyRenderableInstance *pTransparent = (csi->second->containedTransparentInstances.size() > 0) ? 
847                                              csi->second->containedTransparentInstances.back() : NULL;
848     
849       // find the closest contained instance to the camera
850       if (pOpaque && pTransparent)
851       {
852         if (*pOpaque < *pTransparent)
853           (*cii)->SetSplitPoint(pOpaque->GetPosition());
854         else
855           (*cii)->SetSplitPoint(pTransparent->GetPosition());
856       }
857       else if (pOpaque)
858         (*cii)->SetSplitPoint(pOpaque->GetPosition());
859       else if (pTransparent)
860         (*cii)->SetSplitPoint(pTransparent->GetPosition());
861       else
862         (*cii)->SetSplit(false);
863     }
864     else
865       (*cii)->SetSplit(false);
866     
867     // add the container to the list of visiblie clouds to be rendered this frame.
868     _visibleInstances.push_back(csi->second);
869
870     // now update the impostors
871     FAIL_RETURN_MSG((*cii)->Update(cam),
872       "SkySceneManager::_ResolveVisibility(): cloud instance update failed.");
873   } 
874
875   SortInstances(_visibleInstances, cam.Orig);
876
877   return SKYRESULT_OK;
878 }
879
880
881 //------------------------------------------------------------------------------
882 // Function               : SkySceneManager::_TestInsertInstanceIntoClouds
883 // Description      : 
884 //------------------------------------------------------------------------------
885 /**
886  * @fn SkySceneManager::_TestInsertInstanceIntoClouds(const Camera &cam, const CloudBVTree::Node *pNode, SkyRenderableInstance *pInstanceToInsert, bool bTransparent)
887  * @brief @todo <WRITE BRIEF SkySceneManager::_TestInsertInstanceIntoClouds DOCUMENTATION>
888  * 
889  * @todo <WRITE EXTENDED SkySceneManager::_TestInsertInstanceIntoClouds FUNCTION DOCUMENTATION>
890  */ 
891 bool SkySceneManager::_TestInsertInstanceIntoClouds(const Camera            &cam,
892                                                     const CloudBVTree::Node *pNode, 
893                                                     SkyRenderableInstance   *pInstanceToInsert,
894                                                     bool                    bTransparent)
895 {
896   if (_clouds.size() <= 0)
897     return false;
898
899   if (pNode->GetNodeBV().PointInBBox(pInstanceToInsert->GetPosition()))
900   {
901     if (pNode->IsLeaf())
902     {
903       SkyMinMaxBox bbox;
904       int i;
905
906       // check the instance against each cloud in this leaf node.
907       for (i = 0; i < pNode->GetNumObjs(); ++i)
908       {
909         SkyRenderableInstanceCloud* pCloud = 
910           const_cast<SkyRenderableInstanceCloud*>(pNode->GetObj(i));
911         bbox = pCloud->GetWorldSpaceBounds();
912         if (bbox.PointInBBox(pInstanceToInsert->GetPosition()))
913         {
914           // get the container cloud struct for this cloud instance, and add this instance.
915           ContainerSetIterator csi = _containerClouds.find(pCloud->GetID());
916           if (csi == _containerClouds.end())
917           {
918             SkyTrace(
919               "Error: SkySceneManager::_TestInsertInstanceIntoClouds(): Invalid cloud instance %d.", 
920               pCloud->GetID());
921             return false;
922           }
923           else // this instance is inside a cloud.  Set up for split cloud rendering.
924           {
925             if (!bTransparent)
926               csi->second->containedOpaqueInstances.push_back(pInstanceToInsert);
927             else
928               csi->second->containedTransparentInstances.push_back(pInstanceToInsert);
929             csi->second->pCloud->SetSplit(true);
930             return true;
931           }
932         }
933       }
934       return false;
935     }
936     else
937     {
938       if (!_TestInsertInstanceIntoClouds(cam, pNode->GetLeftChild(), pInstanceToInsert, bTransparent))
939         return _TestInsertInstanceIntoClouds(cam, pNode->GetRightChild(), pInstanceToInsert, bTransparent);
940       else
941         return true;
942     }
943   }
944   else
945     return false;
946 }