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.
22 #include "SkyUtil.hpp"
23 #include "SkyCloud.hpp"
24 #include "SkyMaterial.hpp"
25 #include "SkyBoundingVolume.hpp"
26 #include "SkyRenderableInstanceCloud.hpp"
27 #include "SkyDynamicTextureManager.hpp"
29 //! Set this to 1 to see verbose messages about impostor updates.
30 #define SKYCLOUD_VERBOSE 0
32 //! Set this to control the number of frames a cloud has to be culled before its textures are released.
33 #define SKYCLOUD_CULL_RELEASE_COUNT 100
35 //------------------------------------------------------------------------------
36 // Static declarations.
37 //------------------------------------------------------------------------------
38 unsigned int SkyRenderableInstanceCloud::s_iCount = 0;
39 float SkyRenderableInstanceCloud::s_rErrorToleranceAngle = SKYDEGREESTORADS * 0.125f;
40 SkyMaterial* SkyRenderableInstanceCloud::s_pMaterial = NULL;
42 //------------------------------------------------------------------------------
43 // Function : SkyRenderableInstanceCloud::SkyRenderableInstanceCloud
45 //------------------------------------------------------------------------------
47 * @fn SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud, bool bUseOffScreenBuffer)
50 SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud,
51 bool bUseOffScreenBuffer /* = true */)
52 : SkyRenderableInstance(),
57 _bScreenImpostor(false),
60 _bUseOffScreenBuffer(bUseOffScreenBuffer),
63 _vecNearPoint(0, 0, 0),
64 _vecFarPoint(0, 0, 0),
71 // cout << "Cloud Instance created" << endl;
74 //------------------------------------------------------------------------------
75 // Function : SkyRenderableInstanceCloud::SkyRenderableInstanceCloud
77 //------------------------------------------------------------------------------
79 * @fn SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud, const Vec3f &position, const Mat33f &rotation, const float scale, bool bUseOffScreenBuffer)
82 SkyRenderableInstanceCloud::SkyRenderableInstanceCloud(SkyCloud *pCloud,
83 const Vec3f &position,
84 const Mat33f &rotation,
86 bool bUseOffScreenBuffer /* = true */)
87 : SkyRenderableInstance(position, rotation, scale),
92 _bScreenImpostor(false),
95 _bUseOffScreenBuffer(false),
98 _vecNearPoint(0, 0, 0),
99 _vecFarPoint(0, 0, 0),
107 //------------------------------------------------------------------------------
108 // Function : SkyRenderableInstanceCloud::~SkyRenderableInstanceCloud
110 //------------------------------------------------------------------------------
112 * @fn SkyRenderableInstanceCloud::~SkyRenderableInstanceCloud()
115 SkyRenderableInstanceCloud::~SkyRenderableInstanceCloud()
118 SAFE_DELETE(_pWorldSpaceBV);
121 // delete the offscreen buffer when no one else is using it.
124 //JW?? SAFE_DELETE(s_pRenderBuffer);
125 SAFE_DELETE(s_pMaterial);
130 //------------------------------------------------------------------------------
131 // Function : SkyRenderableInstanceCloud::SetPosition
133 //------------------------------------------------------------------------------
135 * @fn SkyRenderableInstanceCloud::SetPosition(const Vec3f &position)
136 * @brief Set the world space position of the instance.
138 * @todo <WRITE EXTENDED SkyRenderableInstanceCloud::SetPosition FUNCTION DOCUMENTATION>
140 void SkyRenderableInstanceCloud::SetPosition(const Vec3f &position)
144 _pCloud->Translate(position - _vecPosition);
146 _vecPosition = position;
148 _UpdateWorldSpaceBounds();
152 //------------------------------------------------------------------------------
153 // Function : SkyRenderableInstanceCloud::SetRotation
155 //------------------------------------------------------------------------------
157 * @fn SkyRenderableInstanceCloud::SetRotation(const Mat33f &rotation)
158 * @brief Set the world space rotation of the instance.
160 * @todo <WRITE EXTENDED SkyRenderableInstanceCloud::SetRotation FUNCTION DOCUMENTATION>
162 void SkyRenderableInstanceCloud::SetRotation(const Mat33f &rotation)
166 _pCloud->Translate(-_vecPosition);
167 _pCloud->Rotate(_matInvRotation * rotation);
168 _pCloud->Translate(_vecPosition);
170 _matRotation = rotation;
171 _matInvRotation = rotation;
172 _matInvRotation.Transpose();
173 _UpdateWorldSpaceBounds();
177 //------------------------------------------------------------------------------
178 // Function : SkyRenderableInstanceCloud::SetScale
180 //------------------------------------------------------------------------------
182 * @fn SkyRenderableInstanceCloud::SetScale(const float &scale)
183 * @brief Set the world space scale of the instance.
185 void SkyRenderableInstanceCloud::SetScale(const float &scale)
189 _pCloud->Translate(-_vecPosition);
190 _pCloud->Scale(scale);
191 _pCloud->Translate(_vecPosition);
194 _UpdateWorldSpaceBounds();
198 //------------------------------------------------------------------------------
199 // Function : DrawQuad
201 //------------------------------------------------------------------------------
203 * DrawQuad(Vec3f pos, Vec3f x, Vec3f y, Vec4f color)
204 * @brief Simply draws an OpenGL quad at @a pos.
206 * The quad's size and orientation are determined by the (non-unit) vectors @a x
207 * and @a y. Its color is given by @a color.
209 inline void DrawQuad(Vec3f pos, Vec3f x, Vec3f y, Vec4f color)
211 glColor4fv(&(color.x));
212 Vec3f left = pos; left -= y;
213 Vec3f right = left; right += x;
215 glTexCoord2f(0, 0); glVertex3fv(&(left.x));
216 glTexCoord2f(1, 0); glVertex3fv(&(right.x));
217 left += y; left += y;
218 right += y; right += y;
219 glTexCoord2f(1, 1); glVertex3fv(&(right.x));
220 glTexCoord2f(0, 1); glVertex3fv(&(left.x));
224 //------------------------------------------------------------------------------
225 // Function : SkyRenderableInstanceCloud::Display
227 //------------------------------------------------------------------------------
229 * @fn SkyRenderableInstanceCloud::Display(bool bDisplayFrontOfSplit)
230 * @brief Display the instance of the cloud using the impostor image.
232 SKYRESULT SkyRenderableInstanceCloud::Display(bool bDisplayFrontOfSplit /* = false */)
235 if (!_bImageExists || !_bEnabled)
237 //FAIL_RETURN(DisplayWithoutImpostor(*(GLVU::GetCurrent()->GetCurrentCam())));
238 FAIL_RETURN(DisplayWithoutImpostor(Camera()));
241 {//cout << "Using impostor image\n";
242 if (!_pBackTexture || (bDisplayFrontOfSplit && !_pFrontTexture)) {
243 // cout << "texture id failure" << endl;
244 FAIL_RETURN_MSG(SKYRESULT_FAIL, "SkyRenderableInstanceCloud::Display(): missing texture!");
247 s_pMaterial->SetTexture(0, GL_TEXTURE_2D, bDisplayFrontOfSplit ? *_pFrontTexture : *_pBackTexture);
248 if (_bScreenImpostor)
250 s_pMaterial->EnableDepthTest(false);
254 if (!bDisplayFrontOfSplit)
256 s_pMaterial->EnableDepthTest(true);
257 s_pMaterial->SetDepthMask(false);
260 s_pMaterial->EnableDepthTest(false);
264 s_pMaterial->EnableDepthTest(true);
265 s_pMaterial->SetDepthMask(true);
268 s_pMaterial->Activate();
269 // s_pMaterial->Force();
273 if (!_bScreenImpostor)
274 {//cout << "Outside the cloud\n";
276 z -= _impostorCam.Orig;
278 x = (z ^ _impostorCam.Y);
286 DrawQuad(_vecPosition, x, y, Vec4f(1, 1, 1, 1));
290 { //cout << "Drawing a polygon - must be inside a cloud\n";
292 x *= 0.5f * (_impostorCam.wR - _impostorCam.wL);
294 y *= 0.5f * (_impostorCam.wT - _impostorCam.wB);
296 z *= _impostorCam.Near;
298 // draw a polygon with this texture...
299 glMatrixMode(GL_MODELVIEW);
302 glMatrixMode(GL_PROJECTION);
305 gluOrtho2D(-1, 1, -1, 1);
307 glColor4f(1, 1, 1, 1);
309 glTexCoord2f(0, 0); glVertex2f(-1, -1);
310 glTexCoord2f(1, 0); glVertex2f(1, -1);
311 glTexCoord2f(1, 1); glVertex2f(1, 1);
312 glTexCoord2f(0, 1); glVertex2f(-1, 1);
316 glMatrixMode(GL_MODELVIEW);
325 //------------------------------------------------------------------------------
326 // Function : SkyRenderableInstanceCloud::DisplayWithoutImpostor
328 //------------------------------------------------------------------------------
330 * @fn SkyRenderableInstanceCloud::DisplayWithoutImpostor(const Camera &cam)
331 * @brief Displays the cloud directly -- without an impotor.
333 * This is used both when the impostor is disabled and to create the impostor image
334 * when it needs to be updated.
336 SKYRESULT SkyRenderableInstanceCloud::DisplayWithoutImpostor(const Camera &cam)
338 // Get and set the world space transformation
340 GetModelToWorldTransform(mat);
342 glMatrixMode(GL_MODELVIEW);
344 glMultMatrixf(mat.M);*/
346 FAIL_RETURN_MSG(_pCloud->Display(cam, this), "SkyRenderableInstanceCloud:Display(): Cloud's display failed.");
348 //glMatrixMode(GL_MODELVIEW);
355 //------------------------------------------------------------------------------
356 // Function : SkyRenderableInstanceCloud::ViewFrustumCull
358 //------------------------------------------------------------------------------
360 * @fn SkyRenderableInstanceCloud::ViewFrustumCull(const Camera &cam)
361 * @brief View frustum cull the object given its world position
363 bool SkyRenderableInstanceCloud::ViewFrustumCull(const Camera &cam)
366 //GetModelToWorldTransform(xform);
368 _bCulled = (_pWorldSpaceBV == NULL) ? false : _pWorldSpaceBV->ViewFrustumCull(cam, xform);
373 //------------------------------------------------------------------------------
374 // Function : SkyRenderableInstanceCloud::ReleaseImpostorTextures
376 //------------------------------------------------------------------------------
378 * @fn SkyRenderableInstanceCloud::ReleaseImpostorTextures()
379 * @brief Causes the instance to release its impostor textures for use by other impostors.
381 * This method is called when the cloud is view frustum culled.
383 void SkyRenderableInstanceCloud::ReleaseImpostorTextures()
387 if (_iCulledCount > SKYCLOUD_CULL_RELEASE_COUNT)
393 DynamicTextureManager::InstancePtr()->CheckInTexture(_pBackTexture);
394 _pBackTexture = NULL;
399 DynamicTextureManager::InstancePtr()->CheckInTexture(_pFrontTexture);
400 _pFrontTexture = NULL;
402 _bImageExists = false;
407 //------------------------------------------------------------------------------
408 // Function : SkyRenderableInstanceCloud::Update
410 //------------------------------------------------------------------------------
412 * @fn SkyRenderableInstanceCloud::Update(const Camera &cam)
413 * @brief Updates the impostor image to be valid for the current viewpoint.
415 * If the image is already valid, exits early.
417 * @see SetErrorToleranceAngle, IsValid
419 SKYRESULT SkyRenderableInstanceCloud::Update(const Camera &cam)
421 if (!_bEnabled || IsImpostorValid(cam))
424 // since we are going to update it anyway, let's make sure we don't try to use it if something
425 // goes wrong. This will be set to true on the successful completion of this Update() method.
426 _bImageExists = false;
427 //cout << "updating impostor\n";
431 float rDistance = (_vecPosition - cam.Orig).Length();
433 float rRadius = _pWorldSpaceBV->GetRadius();
434 float rCamRadius = sqrt(cam.wR*cam.wR + cam.Near*cam.Near);
436 float rWidth = cam.wR - cam.wL;
437 float rHeight = cam.wT - cam.wB;
438 float rMaxdim = (rWidth > rHeight) ? rWidth : rHeight;
440 if (rRadius * cam.Near / rDistance < 0.5 * rMaxdim && (rDistance - rRadius > rCamRadius))
442 _impostorCam.TightlyFitToSphere(cam.Orig, cam.Y, _vecPosition, rRadius);
443 _rRadius = 0.5f * (_impostorCam.wR - _impostorCam.wL) * rDistance / _impostorCam.Near;
444 _rRadius *= GetScale();
445 _bScreenImpostor = false;
446 // store points used in later error estimation
447 _vecNearPoint = -_impostorCam.Z;
448 _vecNearPoint *= _impostorCam.Near;
449 _vecNearPoint += _impostorCam.Orig;
450 _vecFarPoint = -_impostorCam.Z;
451 _vecFarPoint *= _impostorCam.Far;
452 _vecFarPoint += _impostorCam.Orig;
456 _impostorCam.Far = _impostorCam.Near + 3 * rRadius;
457 _bScreenImpostor = true;
460 // resolution based on screensize, distance, and object size.
461 // Cam radius is used to heuristically reduce resolution for clouds very close to the camera.
462 _iLogResolution = _GetRequiredLogResolution(rDistance, rRadius, rCamRadius);
464 int iRes = 1 << _iLogResolution;
468 glGetIntegerv(GL_VIEWPORT, iOldVP);
470 _impostorCam.GetProjectionMatrix(M);
471 glMatrixMode(GL_PROJECTION);
475 _impostorCam.GetModelviewMatrix(M);
476 glMatrixMode(GL_MODELVIEW);
480 glViewport(0, 0, iRes, iRes);
482 s_pMaterial->SetDepthMask(true); // so that the depth buffer gets cleared!
483 s_pMaterial->Activate();
484 glClearColor(0, 0, 0, 0);
485 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
489 FAIL_RETURN(DisplayWithoutImpostor(_impostorCam));
491 if (_pBackTexture && _pBackTexture->GetWidth() != iRes)
493 DynamicTextureManager::InstancePtr()->CheckInTexture(_pBackTexture);
494 _pBackTexture = NULL;
499 _pBackTexture = DynamicTextureManager::InstancePtr()->CheckOutTexture(iRes, iRes);
502 s_pMaterial->SetTexture(0, GL_TEXTURE_2D, *_pBackTexture); // shared material for clouds.
503 s_pMaterial->Activate();
505 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, iRes, iRes);
509 FAIL_RETURN_MSG(_pCloud->DisplaySplit(cam, _vecSplit, true, this),
510 "SkyRenderableInstanceCloud:Display(): Cloud's display failed.");
512 if (_pBackTexture && _pBackTexture->GetWidth() != iRes)
514 DynamicTextureManager::InstancePtr()->CheckInTexture(_pBackTexture);
515 _pBackTexture = NULL;
517 if (_pFrontTexture && _pFrontTexture->GetWidth() != iRes)
519 DynamicTextureManager::InstancePtr()->CheckInTexture(_pFrontTexture);
520 _pFrontTexture = NULL;
525 _pBackTexture = DynamicTextureManager::InstancePtr()->CheckOutTexture(iRes, iRes);
528 s_pMaterial->SetTexture(0, GL_TEXTURE_2D, *_pBackTexture); // shared material for clouds.
529 FAIL_RETURN(s_pMaterial->Activate());
531 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, iRes, iRes);
533 // now clear and draw the front.
534 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
536 FAIL_RETURN_MSG(_pCloud->DisplaySplit(cam, _vecSplit, false, this),
537 "SkyRenderableInstanceCloud:Display(): Cloud's display failed.");
541 _pFrontTexture = DynamicTextureManager::InstancePtr()->CheckOutTexture(iRes, iRes);
544 s_pMaterial->GetTextureState().SetTexture(0, GL_TEXTURE_2D, *_pFrontTexture);
545 FAIL_RETURN(s_pMaterial->GetTextureState().Activate());
547 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, iRes, iRes);
550 glMatrixMode(GL_MODELVIEW);
552 glMatrixMode(GL_PROJECTION);
555 //GLVU::CheckForGLError("Cloud Impostor Update");
557 glViewport(iOldVP[0], iOldVP[1], iOldVP[2], iOldVP[3]);
559 _bImageExists = true;
561 // the textures should now exist.
562 assert(_pBackTexture);
563 assert(!_bSplit || (_bSplit && _pFrontTexture));
569 //------------------------------------------------------------------------------
570 // Function : SkyRenderableInstanceCloud::IsImpostorValid
572 //------------------------------------------------------------------------------
574 * @fn SkyRenderableInstanceCloud::IsImpostorValid(const Camera& cam)
575 * @brief Returns true if the impostor image is valid for the given camera.
577 * Will return false if this is a screen impostor, or if there is error in either
578 * the translation of the camera from the capture point or the impostor image resolution.
580 * @see SetErrorToleranceAngle
582 bool SkyRenderableInstanceCloud::IsImpostorValid(const Camera& cam)
584 // first make sure there is a current image.
588 // screen impostors should always be updated
589 if (_bScreenImpostor)
591 _vecFarPoint = Vec3f::ZERO;
592 _vecNearPoint = Vec3f::ZERO;
594 SkyTrace("Screen Impostor Update");
598 // impostors are valid from the viewpoint from which they were captured
599 if (cam.Orig == _impostorCam.Orig)
605 SkyTrace("Split Impostor Update");
610 Vec3f vecX = _vecNearPoint - cam.Orig;
611 Vec3f vecY = _vecFarPoint - cam.Orig;
612 float rXLength = vecX.Length();
613 float rYLength = vecY.Length();
614 if (rXLength > rYLength)
617 SkyTrace("Backwards Impostor Update");
624 float rCosAlpha = vecX * vecY; // dot product of normalized vectors = cosine
626 if (fabs(rCosAlpha) < 1.0)
628 float rAlpha = acos(rCosAlpha);
629 if (rAlpha >= s_rErrorToleranceAngle)
632 SkyTrace("Angle Error Update %f", SKYRADSTODEGREES * rAlpha);
638 float rDistance = (_vecPosition - cam.Orig).Length();
639 float rCamRadius = sqrt(cam.wR*cam.wR + cam.Near*cam.Near);
641 int iRes = _GetRequiredLogResolution(rDistance, _pWorldSpaceBV->GetRadius(), rCamRadius);
643 if (iRes > _iLogResolution)
646 SkyTrace("Resolution Error Update: Required: %d Actual: %d", iRes, _iLogResolution);
655 //------------------------------------------------------------------------------
656 // Function : SetErrorToleranceAngle
658 //------------------------------------------------------------------------------
660 * @fn SkyRenderableInstanceCloud::SetErrorToleranceAngle(float rDegrees)
661 * @brief Set the global error tolerance angle for all impostors.
663 void SkyRenderableInstanceCloud::SetErrorToleranceAngle(float rDegrees)
665 s_rErrorToleranceAngle = SKYDEGREESTORADS * rDegrees;
670 //------------------------------------------------------------------------------
671 // Function : SkyRenderableInstanceCloud::_Initialize
673 //------------------------------------------------------------------------------
675 * @fn SkyRenderableInstanceCloud::_Initialize()
676 * @brief Initializer used by the constructors.
678 void SkyRenderableInstanceCloud::_Initialize()
680 _UpdateWorldSpaceBounds();
682 // if (!s_pRenderBuffer && _bUseOffScreenBuffer)
684 //JW?? s_pRenderBuffer = new SkyOffScreenBuffer(GLUT_SINGLE | GLUT_DEPTH | GLUT_STENCIL);
685 //JW?? s_pRenderBuffer->Initialize(true);
687 //JW?? s_pRenderBuffer->MakeCurrent();
688 // set some GL state:
689 // glClearColor(0, 0, 0, 0);
690 //JW?? GLVU::GetCurrent()->MakeCurrent();
694 s_pMaterial = new SkyMaterial;
695 s_pMaterial->EnableBlending(true);
696 s_pMaterial->SetBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
697 s_pMaterial->SetAlphaFunc(GL_GREATER);
698 s_pMaterial->EnableDepthTest(false);
699 s_pMaterial->SetDepthMask(true);
700 s_pMaterial->EnableAlphaTest(true);
701 s_pMaterial->EnableLighting(false);
702 s_pMaterial->SetTextureParameter(0, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
703 s_pMaterial->SetTextureParameter(0, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
704 s_pMaterial->SetTextureParameter(0, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
705 s_pMaterial->EnableTexture(0, true);