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
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.
18 * @file SkyRenderableInstanceCloud.cpp
20 * Implementation of class SkyRenderableInstanceCloud.
24 # include <simgear_config.h>
33 #include "SkyUtil.hpp"
34 #include "SkyCloud.hpp"
35 #include "SkyMaterial.hpp"
36 #include "SkyBoundingVolume.hpp"
37 #include "SkyRenderableInstanceCloud.hpp"
38 #include "SkyDynamicTextureManager.hpp"
40 //! Set this to 1 to see verbose messages about impostor updates.
41 #define SKYCLOUD_VERBOSE 0
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
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;
53 //------------------------------------------------------------------------------
54 // Function : SkyRenderableInstanceCloud::SkyRenderableInstanceCloud
56 //------------------------------------------------------------------------------
58 * @fn SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud, bool bUseOffScreenBuffer)
61 SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud,
62 bool bUseOffScreenBuffer /* = true */)
63 : SkyRenderableInstance(),
68 _bScreenImpostor(false),
71 _bUseOffScreenBuffer(bUseOffScreenBuffer),
74 _vecNearPoint(0, 0, 0),
75 _vecFarPoint(0, 0, 0),
82 // cout << "Cloud Instance created" << endl;
85 //------------------------------------------------------------------------------
86 // Function : SkyRenderableInstanceCloud::SkyRenderableInstanceCloud
88 //------------------------------------------------------------------------------
90 * @fn SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud, const Vec3f &position, const Mat33f &rotation, const float scale, bool bUseOffScreenBuffer)
93 SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud,
94 const Vec3f &position,
95 const Mat33f &rotation,
97 bool bUseOffScreenBuffer /* = true */)
98 : SkyRenderableInstance(position, rotation, scale),
101 _pWorldSpaceBV(NULL),
103 _bScreenImpostor(false),
104 _bImageExists(false),
106 _bUseOffScreenBuffer(false),
109 _vecNearPoint(0, 0, 0),
110 _vecFarPoint(0, 0, 0),
118 //------------------------------------------------------------------------------
119 // Function : SkyRenderableInstanceCloud::~SkyRenderableInstanceCloud
121 //------------------------------------------------------------------------------
123 * @fn SkyRenderableInstanceCloud::~SkyRenderableInstanceCloud()
126 SkyRenderableInstanceCloud::~SkyRenderableInstanceCloud()
129 SAFE_DELETE(_pWorldSpaceBV);
132 // delete the offscreen buffer when no one else is using it.
135 //JW?? SAFE_DELETE(s_pRenderBuffer);
136 SAFE_DELETE(s_pMaterial);
141 //------------------------------------------------------------------------------
142 // Function : SkyRenderableInstanceCloud::SetPosition
144 //------------------------------------------------------------------------------
146 * @fn SkyRenderableInstanceCloud::SetPosition(const Vec3f &position)
147 * @brief Set the world space position of the instance.
149 * @todo <WRITE EXTENDED SkyRenderableInstanceCloud::SetPosition FUNCTION DOCUMENTATION>
151 void SkyRenderableInstanceCloud::SetPosition(const Vec3f &position)
155 _pCloud->Translate(position - _vecPosition);
157 _vecPosition = position;
159 _UpdateWorldSpaceBounds();
163 //------------------------------------------------------------------------------
164 // Function : SkyRenderableInstanceCloud::SetRotation
166 //------------------------------------------------------------------------------
168 * @fn SkyRenderableInstanceCloud::SetRotation(const Mat33f &rotation)
169 * @brief Set the world space rotation of the instance.
171 * @todo <WRITE EXTENDED SkyRenderableInstanceCloud::SetRotation FUNCTION DOCUMENTATION>
173 void SkyRenderableInstanceCloud::SetRotation(const Mat33f &rotation)
177 _pCloud->Translate(-_vecPosition);
178 _pCloud->Rotate(_matInvRotation * rotation);
179 _pCloud->Translate(_vecPosition);
181 _matRotation = rotation;
182 _matInvRotation = rotation;
183 _matInvRotation.Transpose();
184 _UpdateWorldSpaceBounds();
188 //------------------------------------------------------------------------------
189 // Function : SkyRenderableInstanceCloud::SetScale
191 //------------------------------------------------------------------------------
193 * @fn SkyRenderableInstanceCloud::SetScale(const float &scale)
194 * @brief Set the world space scale of the instance.
196 void SkyRenderableInstanceCloud::SetScale(const float &scale)
200 _pCloud->Translate(-_vecPosition);
201 _pCloud->Scale(scale);
202 _pCloud->Translate(_vecPosition);
205 _UpdateWorldSpaceBounds();
209 //------------------------------------------------------------------------------
210 // Function : DrawQuad
212 //------------------------------------------------------------------------------
214 * DrawQuad(Vec3f pos, Vec3f x, Vec3f y, Vec4f color)
215 * @brief Simply draws an OpenGL quad at @a pos.
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.
220 inline void DrawQuad(Vec3f pos, Vec3f x, Vec3f y, Vec4f color)
222 glColor4fv(&(color.x));
223 Vec3f left = pos; left -= y;
224 Vec3f right = left; right += 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));
235 //------------------------------------------------------------------------------
236 // Function : SkyRenderableInstanceCloud::Display
238 //------------------------------------------------------------------------------
240 * @fn SkyRenderableInstanceCloud::Display(bool bDisplayFrontOfSplit)
241 * @brief Display the instance of the cloud using the impostor image.
243 SKYRESULT SkyRenderableInstanceCloud::Display(bool bDisplayFrontOfSplit /* = false */)
246 if (!_bImageExists || !_bEnabled)
248 //FAIL_RETURN(DisplayWithoutImpostor(*(GLVU::GetCurrent()->GetCurrentCam())));
249 FAIL_RETURN(DisplayWithoutImpostor(Camera()));
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!");
258 s_pMaterial->SetTexture(0, GL_TEXTURE_2D, bDisplayFrontOfSplit ? *_pFrontTexture : *_pBackTexture);
259 if (_bScreenImpostor)
261 s_pMaterial->EnableDepthTest(false);
265 if (!bDisplayFrontOfSplit)
267 s_pMaterial->EnableDepthTest(true);
268 s_pMaterial->SetDepthMask(false);
271 s_pMaterial->EnableDepthTest(false);
275 s_pMaterial->EnableDepthTest(true);
276 s_pMaterial->SetDepthMask(true);
279 s_pMaterial->Activate();
280 // s_pMaterial->Force();
284 if (!_bScreenImpostor)
285 {//cout << "Outside the cloud\n";
287 z -= _impostorCam.Orig;
289 x = (z ^ _impostorCam.Y);
297 DrawQuad(_vecPosition, x, y, Vec4f(1, 1, 1, 1));
301 { //cout << "Drawing a polygon - must be inside a cloud\n";
303 x *= 0.5f * (_impostorCam.wR - _impostorCam.wL);
305 y *= 0.5f * (_impostorCam.wT - _impostorCam.wB);
307 z *= _impostorCam.Near;
309 // draw a polygon with this texture...
310 glMatrixMode(GL_MODELVIEW);
313 glMatrixMode(GL_PROJECTION);
316 gluOrtho2D(-1, 1, -1, 1);
318 glColor4f(1, 1, 1, 1);
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);
327 glMatrixMode(GL_MODELVIEW);
336 //------------------------------------------------------------------------------
337 // Function : SkyRenderableInstanceCloud::DisplayWithoutImpostor
339 //------------------------------------------------------------------------------
341 * @fn SkyRenderableInstanceCloud::DisplayWithoutImpostor(const Camera &cam)
342 * @brief Displays the cloud directly -- without an impotor.
344 * This is used both when the impostor is disabled and to create the impostor image
345 * when it needs to be updated.
347 SKYRESULT SkyRenderableInstanceCloud::DisplayWithoutImpostor(const Camera &cam)
349 // Get and set the world space transformation
351 GetModelToWorldTransform(mat);
353 glMatrixMode(GL_MODELVIEW);
355 glMultMatrixf(mat.M);*/
357 FAIL_RETURN_MSG(_pCloud->Display(cam, this), "SkyRenderableInstanceCloud:Display(): Cloud's display failed.");
359 //glMatrixMode(GL_MODELVIEW);
366 //------------------------------------------------------------------------------
367 // Function : SkyRenderableInstanceCloud::ViewFrustumCull
369 //------------------------------------------------------------------------------
371 * @fn SkyRenderableInstanceCloud::ViewFrustumCull(const Camera &cam)
372 * @brief View frustum cull the object given its world position
374 bool SkyRenderableInstanceCloud::ViewFrustumCull(const Camera &cam)
377 //GetModelToWorldTransform(xform);
379 _bCulled = (_pWorldSpaceBV == NULL) ? false : _pWorldSpaceBV->ViewFrustumCull(cam, xform);
384 //------------------------------------------------------------------------------
385 // Function : SkyRenderableInstanceCloud::ReleaseImpostorTextures
387 //------------------------------------------------------------------------------
389 * @fn SkyRenderableInstanceCloud::ReleaseImpostorTextures()
390 * @brief Causes the instance to release its impostor textures for use by other impostors.
392 * This method is called when the cloud is view frustum culled.
394 void SkyRenderableInstanceCloud::ReleaseImpostorTextures()
398 if (_iCulledCount > SKYCLOUD_CULL_RELEASE_COUNT)
404 DynamicTextureManager::InstancePtr()->CheckInTexture(_pBackTexture);
405 _pBackTexture = NULL;
410 DynamicTextureManager::InstancePtr()->CheckInTexture(_pFrontTexture);
411 _pFrontTexture = NULL;
413 _bImageExists = false;
418 //------------------------------------------------------------------------------
419 // Function : SkyRenderableInstanceCloud::Update
421 //------------------------------------------------------------------------------
423 * @fn SkyRenderableInstanceCloud::Update(const Camera &cam)
424 * @brief Updates the impostor image to be valid for the current viewpoint.
426 * If the image is already valid, exits early.
428 * @see SetErrorToleranceAngle, IsValid
430 SKYRESULT SkyRenderableInstanceCloud::Update(const Camera &cam)
432 if (!_bEnabled || IsImpostorValid(cam))
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";
442 float rDistance = (_vecPosition - cam.Orig).Length();
444 float rRadius = _pWorldSpaceBV->GetRadius();
445 float rCamRadius = sqrt(cam.wR*cam.wR + cam.Near*cam.Near);
447 float rWidth = cam.wR - cam.wL;
448 float rHeight = cam.wT - cam.wB;
449 float rMaxdim = (rWidth > rHeight) ? rWidth : rHeight;
451 if (rRadius * cam.Near / rDistance < 0.5 * rMaxdim && (rDistance - rRadius > rCamRadius))
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;
467 _impostorCam.Far = _impostorCam.Near + 3 * rRadius;
468 _bScreenImpostor = true;
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);
475 int iRes = 1 << _iLogResolution;
479 glGetIntegerv(GL_VIEWPORT, iOldVP);
481 _impostorCam.GetProjectionMatrix(M);
482 glMatrixMode(GL_PROJECTION);
486 _impostorCam.GetModelviewMatrix(M);
487 glMatrixMode(GL_MODELVIEW);
491 glViewport(0, 0, iRes, iRes);
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);
500 FAIL_RETURN(DisplayWithoutImpostor(_impostorCam));
502 if (_pBackTexture && _pBackTexture->GetWidth() != iRes)
504 DynamicTextureManager::InstancePtr()->CheckInTexture(_pBackTexture);
505 _pBackTexture = NULL;
510 _pBackTexture = DynamicTextureManager::InstancePtr()->CheckOutTexture(iRes, iRes);
513 s_pMaterial->SetTexture(0, GL_TEXTURE_2D, *_pBackTexture); // shared material for clouds.
514 s_pMaterial->Activate();
516 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, iRes, iRes);
520 FAIL_RETURN_MSG(_pCloud->DisplaySplit(cam, _vecSplit, true, this),
521 "SkyRenderableInstanceCloud:Display(): Cloud's display failed.");
523 if (_pBackTexture && _pBackTexture->GetWidth() != iRes)
525 DynamicTextureManager::InstancePtr()->CheckInTexture(_pBackTexture);
526 _pBackTexture = NULL;
528 if (_pFrontTexture && _pFrontTexture->GetWidth() != iRes)
530 DynamicTextureManager::InstancePtr()->CheckInTexture(_pFrontTexture);
531 _pFrontTexture = NULL;
536 _pBackTexture = DynamicTextureManager::InstancePtr()->CheckOutTexture(iRes, iRes);
539 s_pMaterial->SetTexture(0, GL_TEXTURE_2D, *_pBackTexture); // shared material for clouds.
540 FAIL_RETURN(s_pMaterial->Activate());
542 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, iRes, iRes);
544 // now clear and draw the front.
545 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
547 FAIL_RETURN_MSG(_pCloud->DisplaySplit(cam, _vecSplit, false, this),
548 "SkyRenderableInstanceCloud:Display(): Cloud's display failed.");
552 _pFrontTexture = DynamicTextureManager::InstancePtr()->CheckOutTexture(iRes, iRes);
555 s_pMaterial->GetTextureState().SetTexture(0, GL_TEXTURE_2D, *_pFrontTexture);
556 FAIL_RETURN(s_pMaterial->GetTextureState().Activate());
558 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, iRes, iRes);
561 glMatrixMode(GL_MODELVIEW);
563 glMatrixMode(GL_PROJECTION);
566 //GLVU::CheckForGLError("Cloud Impostor Update");
568 glViewport(iOldVP[0], iOldVP[1], iOldVP[2], iOldVP[3]);
570 _bImageExists = true;
572 // the textures should now exist.
573 assert(_pBackTexture);
574 assert(!_bSplit || (_bSplit && _pFrontTexture));
580 //------------------------------------------------------------------------------
581 // Function : SkyRenderableInstanceCloud::IsImpostorValid
583 //------------------------------------------------------------------------------
585 * @fn SkyRenderableInstanceCloud::IsImpostorValid(const Camera& cam)
586 * @brief Returns true if the impostor image is valid for the given camera.
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.
591 * @see SetErrorToleranceAngle
593 bool SkyRenderableInstanceCloud::IsImpostorValid(const Camera& cam)
595 // first make sure there is a current image.
599 // screen impostors should always be updated
600 if (_bScreenImpostor)
602 _vecFarPoint = Vec3f::ZERO;
603 _vecNearPoint = Vec3f::ZERO;
605 SkyTrace("Screen Impostor Update");
609 // impostors are valid from the viewpoint from which they were captured
610 if (cam.Orig == _impostorCam.Orig)
616 SkyTrace("Split Impostor Update");
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)
628 SkyTrace("Backwards Impostor Update");
635 float rCosAlpha = vecX * vecY; // dot product of normalized vectors = cosine
637 if (fabs(rCosAlpha) < 1.0)
639 float rAlpha = acos(rCosAlpha);
640 if (rAlpha >= s_rErrorToleranceAngle)
643 SkyTrace("Angle Error Update %f", SKYRADSTODEGREES * rAlpha);
649 float rDistance = (_vecPosition - cam.Orig).Length();
650 float rCamRadius = sqrt(cam.wR*cam.wR + cam.Near*cam.Near);
652 int iRes = _GetRequiredLogResolution(rDistance, _pWorldSpaceBV->GetRadius(), rCamRadius);
654 if (iRes > _iLogResolution)
657 SkyTrace("Resolution Error Update: Required: %d Actual: %d", iRes, _iLogResolution);
666 //------------------------------------------------------------------------------
667 // Function : SetErrorToleranceAngle
669 //------------------------------------------------------------------------------
671 * @fn SkyRenderableInstanceCloud::SetErrorToleranceAngle(float rDegrees)
672 * @brief Set the global error tolerance angle for all impostors.
674 void SkyRenderableInstanceCloud::SetErrorToleranceAngle(float rDegrees)
676 s_rErrorToleranceAngle = SKYDEGREESTORADS * rDegrees;
681 //------------------------------------------------------------------------------
682 // Function : SkyRenderableInstanceCloud::_Initialize
684 //------------------------------------------------------------------------------
686 * @fn SkyRenderableInstanceCloud::_Initialize()
687 * @brief Initializer used by the constructors.
689 void SkyRenderableInstanceCloud::_Initialize()
691 _UpdateWorldSpaceBounds();
693 // if (!s_pRenderBuffer && _bUseOffScreenBuffer)
695 //JW?? s_pRenderBuffer = new SkyOffScreenBuffer(GLUT_SINGLE | GLUT_DEPTH | GLUT_STENCIL);
696 //JW?? s_pRenderBuffer->Initialize(true);
698 //JW?? s_pRenderBuffer->MakeCurrent();
699 // set some GL state:
700 // glClearColor(0, 0, 0, 0);
701 //JW?? GLVU::GetCurrent()->MakeCurrent();
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);