1 //------------------------------------------------------------------------------
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
21 * Implementation of class SkyCloud.
24 // warning for truncation of template name for browse info
25 #pragma warning( disable : 4786)
28 #include "SkyCloud.hpp"
29 #include "SkyRenderableInstance.hpp"
30 #include "SkyContext.hpp"
31 #include "SkyMaterial.hpp"
32 #include "SkyLight.hpp"
33 #include "SkyTextureManager.hpp"
34 #include "SkySceneManager.hpp"
37 //! The version used for cloud archive files.
38 #define CLOUD_ARCHIVE_VERSION 0.1f
40 //------------------------------------------------------------------------------
41 // Static initialization
42 //------------------------------------------------------------------------------
43 SkyMaterial* SkyCloud::s_pMaterial = NULL;
44 SkyMaterial* SkyCloud::s_pShadeMaterial = NULL;
45 unsigned int SkyCloud::s_iShadeResolution = 32;
46 float SkyCloud::s_rAlbedo = 0.9f;
47 float SkyCloud::s_rExtinction = 80.0f;
48 float SkyCloud::s_rTransparency = exp(-s_rExtinction);
49 float SkyCloud::s_rScatterFactor = s_rAlbedo * s_rExtinction * SKY_INV_4PI;
50 float SkyCloud::s_rSortAngleErrorTolerance = 0.8f;
51 float SkyCloud::s_rSortSquareDistanceTolerance = 100;
53 //------------------------------------------------------------------------------
54 // Function : SkyCloud::SkyCloud
56 //------------------------------------------------------------------------------
58 * @fn SkyCloud::SkyCloud()
63 _bUsePhaseFunction(true),
64 _vecLastSortViewDir(Vec3f::ZERO),
65 _vecLastSortCamPos(Vec3f::ZERO)
67 if (!s_pShadeMaterial)
69 s_pShadeMaterial = new SkyMaterial;
70 s_pShadeMaterial->SetAmbient(Vec4f(0.1f, 0.1f, 0.1f, 1));
71 s_pShadeMaterial->EnableDepthTest(false);
72 s_pShadeMaterial->SetBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
73 s_pShadeMaterial->EnableBlending(true);
74 s_pShadeMaterial->SetAlphaFunc(GL_GREATER);
75 s_pShadeMaterial->SetAlphaRef(0);
76 s_pShadeMaterial->EnableAlphaTest(true);
77 s_pShadeMaterial->SetColorMaterialMode(GL_DIFFUSE);
78 s_pShadeMaterial->EnableColorMaterial(true);
79 s_pShadeMaterial->EnableLighting(false);
80 s_pShadeMaterial->SetTextureApplicationMode(GL_MODULATE);
84 s_pMaterial = new SkyMaterial;
85 s_pMaterial->SetAmbient(Vec4f(0.3f, 0.3f, 0.3f, 1));
86 s_pMaterial->SetDepthMask(false);
87 s_pMaterial->SetBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
88 s_pMaterial->EnableBlending(true);
89 s_pMaterial->SetAlphaFunc(GL_GREATER);
90 s_pMaterial->SetAlphaRef(0);
91 s_pMaterial->EnableAlphaTest(true);
92 s_pMaterial->SetColorMaterialMode(GL_DIFFUSE);
93 s_pMaterial->EnableColorMaterial(true);
94 s_pMaterial->EnableLighting(false);
95 s_pMaterial->SetTextureApplicationMode(GL_MODULATE);
96 _CreateSplatTexture(32); // will assign the texture to both static materials
101 //------------------------------------------------------------------------------
102 // Function : SkyCloud::~SkyCloud
104 //------------------------------------------------------------------------------
106 * @fn SkyCloud::~SkyCloud()
109 SkyCloud::~SkyCloud()
114 //------------------------------------------------------------------------------
115 // Function : SkyCloud::Update
117 //------------------------------------------------------------------------------
119 * @fn SkyCloud::Update(const Camera &cam, SkyRenderableInstance* pInstance)
120 * @brief Currently does nothing.
122 SKYRESULT SkyCloud::Update(const Camera &cam, SkyRenderableInstance* pInstance)
129 //------------------------------------------------------------------------------
130 // Function : DrawQuad
132 //------------------------------------------------------------------------------
134 * @fn DrawQuad(Vec3f pos, Vec3f x, Vec3f y, Vec4f color)
135 * @brief Draw a quad.
137 inline void DrawQuad(Vec3f pos, Vec3f x, Vec3f y, Vec4f color)
139 glColor4fv(&(color.x));
140 Vec3f left = pos; left -= y;
141 Vec3f right = left; right += x;
143 glTexCoord2f(0, 0); glVertex3fv(&(left.x));
144 glTexCoord2f(1, 0); glVertex3fv(&(right.x));
145 left += y; left += y;
146 right += y; right += y;
147 glTexCoord2f(1, 1); glVertex3fv(&(right.x));
148 glTexCoord2f(0, 1); glVertex3fv(&(left.x));
152 //------------------------------------------------------------------------------
153 // Function : SkyCloud::Display
155 //------------------------------------------------------------------------------
157 * @fn SkyCloud::Display(const Camera &camera, SkyRenderableInstance *pInstance)
158 * @brief Renders the cloud.
160 * The cloud is rendered by splatting the particles from back to front with respect
161 * to @a camera. Since instances of clouds each have their own particles, which
162 * are pre-transformed into world space, @a pInstance is not used.
164 * An alternative method is to store the particles untransformed, and transform the
165 * camera and light into cloud space for rendering. This is more complicated,
166 * and not as straightforward. Since I have to store the particles with each instance
167 * anyway, I decided to pre-transform them instead.
169 SKYRESULT SkyCloud::Display(const Camera &camera, SkyRenderableInstance *pInstance)
171 // copy the current camera
174 // This cosine computation, along with the if() below, are an optimization. The goal
175 // is to avoid sorting when it will make no visual difference. This will be true when the
176 // cloud particles are almost sorted for the current viewpoint. This is the case most of the
177 // time, since the viewpoint does not move very far in a single frame. Each time we sort,
178 // we cache the current view direction. Then, each time the cloud is displayed, if the
179 // current view direction is very close to the current view direction (dot product is nearly 1)
180 // then we do not resort the particles.
181 float rCosAngleSinceLastSort =
182 _vecLastSortViewDir * cam.ViewDir(); // dot product
184 float rSquareDistanceSinceLastSort =
185 (cam.Orig - _vecLastSortCamPos).LengthSqr();
187 if (rCosAngleSinceLastSort < s_rSortAngleErrorTolerance ||
188 rSquareDistanceSinceLastSort > s_rSortSquareDistanceTolerance)
190 // compute the sort position for particles.
191 // don't just use the camera position -- if it is too far away from the cloud, then
192 // precision limitations may cause the STL sort to hang. Instead, put the sort position
193 // just outside the bounding sphere of the cloud in the direction of the camera.
194 Vec3f vecSortPos = -cam.ViewDir();
195 vecSortPos *= (1.1 * _boundingBox.GetRadius());
197 // sort the particles from back to front wrt the camera position.
198 _SortParticles(cam.ViewDir(), vecSortPos, SKY_CLOUD_SORT_TOWARD);
200 //_vecLastSortViewDir = GLVU::GetCurrent()->GetCurrentCam()->ViewDir();
201 //_vecLastSortCamPos = GLVU::GetCurrent()->GetCurrentCam()->Orig;
202 _vecLastSortViewDir = cam.ViewDir();
203 _vecLastSortCamPos = cam.Orig;
206 // set the material state / properties that clouds use for rendering:
207 // Enables blending, with blend func (ONE, ONE_MINUS_SRC_ALPHA).
208 // Enables alpha test to discard completely transparent fragments.
209 // Disables depth test.
210 // Enables texturing, with modulation, and the texture set to the shared splat texture.
211 s_pMaterial->Activate();
216 // Draw the particles using immediate mode.
220 for (ParticleIterator iter = _particles.begin(); iter != _particles.end(); iter++)
223 SkyCloudParticle *p = *iter;
225 // Start with ambient light
226 color = p->GetBaseColor();
228 if (_bUsePhaseFunction) // use the phase function for anisotropic scattering.
231 eyeDir -= p->GetPosition();
235 // add the color contribution to this particle from each light source, modulated by
236 // the phase function. See _PhaseFunction() documentation for details.
237 for (int i = 0; i < p->GetNumLitColors(); i++)
239 pf = _PhaseFunction(_lightDirections[i], eyeDir);
240 // expand this to avoid temporary vector creation in the inner loop
241 color.x += p->GetLitColor(i).x * pf;
242 color.y += p->GetLitColor(i).y * pf;
243 color.z += p->GetLitColor(i).z * pf;
246 else // just use isotropic scattering instead.
248 for (int i = 0; i < (*iter)->GetNumLitColors(); ++i)
250 color += p->GetLitColor(i);
254 // Set the transparency independently of the colors
255 color.w = 1 - s_rTransparency;
257 // draw the particle as a textured billboard.
258 DrawQuad((*iter)->GetPosition(), cam.X * p->GetRadius(), cam.Y * p->GetRadius(), color);
266 //------------------------------------------------------------------------------
267 // Function : SkyCloud::DisplaySplit
269 //------------------------------------------------------------------------------
271 * @fn SkyCloud::DisplaySplit(const Camera &camera, const Vec3f &vecSplitPoint, bool bBackHalf, SkyRenderableInstance *pInstance)
272 * @brief The same as Display(), except it displays only the particles in front of or behind the split point.
274 * This is used to render clouds into two impostor images for displaying clouds that contain objects.
276 * @see SkyRenderableInstanceCloud
278 SKYRESULT SkyCloud::DisplaySplit(const Camera &camera,
279 const Vec3f &vecSplitPoint,
281 SkyRenderableInstance *pInstance /* = NULL */)
283 // copy the current camera
286 Vec3f vecCloudSpaceSplit = vecSplitPoint;
288 if (bBackHalf) // only sort when rendering the back half. Reuse sort for front half.
290 // compute the sort position for particles.
291 // don't just use the camera position -- if it is too far away from the cloud, then
292 // precision limitations may cause the STL sort to hang. Instead, put the sort position
293 // just outside the bounding sphere of the cloud in the direction of the camera.
294 _vecSortPos = -cam.ViewDir();
295 _vecSortPos *= (1.1 * _boundingBox.GetRadius());
297 // sort the particles from back to front wrt the camera position.
298 _SortParticles(cam.ViewDir(), _vecSortPos, SKY_CLOUD_SORT_TOWARD);
300 // we can't use the view direction optimization when the cloud is split, or we get a lot
301 // of popping of objects in and out of cloud cover. For consistency, though, we need to update
302 // the cached sort direction, since we just sorted the particles.
303 ///_vecLastSortViewDir = GLVU::GetCurrent()->GetCurrentCam()->ViewDir();
305 // compute the split distance.
306 vecCloudSpaceSplit -= _vecSortPos;
307 _rSplitDistance = vecCloudSpaceSplit * cam.ViewDir();
310 // set the material state / properties that clouds use for rendering:
311 // Enables blending, with blend func (ONE, ONE_MINUS_SRC_ALPHA).
312 // Enables alpha test to discard completely transparent fragments.
313 // Disables depth test.
314 // Enables texturing, with modulation, and the texture set to the shared splat texture.
315 s_pMaterial->Activate();
320 // Draw the particles using immediate mode.
323 // if bBackHalf is false, then we just continue where we left off. If it is true, we
324 // reset the iterator to the beginning of the sorted list.
325 static ParticleIterator iter;
327 iter = _particles.begin();
329 // iterate over the particles and render them.
330 for (; iter != _particles.end(); ++iter)
332 SkyCloudParticle *p = *iter;
334 if (bBackHalf && (p->GetSquareSortDistance() < _rSplitDistance))
337 // Start with ambient light
338 color = p->GetBaseColor();
340 if (_bUsePhaseFunction) // use the phase function for anisotropic scattering.
343 eyeDir -= p->GetPosition();
347 // add the color contribution to this particle from each light source, modulated by
348 // the phase function. See _PhaseFunction() documentation for details.
349 for (int i = 0; i < p->GetNumLitColors(); i++)
351 pf = _PhaseFunction(_lightDirections[i], eyeDir);
352 // expand this to avoid temporary vector creation in the inner loop
353 color.x += p->GetLitColor(i).x * pf;
354 color.y += p->GetLitColor(i).y * pf;
355 color.z += p->GetLitColor(i).z * pf;
358 else // just use isotropic scattering instead.
360 for (int i = 0; i < p->GetNumLitColors(); ++i)
362 color += p->GetLitColor(i);
366 // set the transparency independently of the colors.
367 color.w = 1 - s_rTransparency;
369 // draw the particle as a textured billboard.
370 DrawQuad((*iter)->GetPosition(), cam.X * p->GetRadius(), cam.Y * p->GetRadius(), color);
377 //------------------------------------------------------------------------------
378 // Function : SkyCloud::Illuminate
380 //------------------------------------------------------------------------------
382 * @fn SkyCloud::Illuminate(SkyLight *pLight, SkyRenderableInstance* pInstance, bool bReset)
383 * @brief Compute the illumination of the cloud by the lightsource @a pLight
385 * This method uses graphics hardware to compute multiple forward scattering at each cloud
386 * in the cloud of light from the directional light source @a pLight. The algorithm works
387 * by successively subtracting "light" from an initially white (fully lit) frame buffer by
388 * using hardware blending and read back. The method stores the illumination from each light
389 * source passed to it separately at each particle, unless @a bReset is true, in which case
390 * the lists of illumination in the particles are reset before the lighting is computed.
393 SKYRESULT SkyCloud::Illuminate(SkyLight *pLight, SkyRenderableInstance* pInstance, bool bReset)
397 glGetIntegerv(GL_VIEWPORT, iOldVP);
398 glViewport(0, 0, s_iShadeResolution, s_iShadeResolution);
400 Vec3f vecDir(pLight->GetDirection());
402 // if this is the first pass through the lights, reset will be true, and the cached light
403 // directions should be updated. Light directions are cached in cloud space to accelerate
404 // computation of the phase function, which depends on light direction and view direction.
406 _lightDirections.clear();
407 _lightDirections.push_back(vecDir); // cache the (unit-length) light direction
409 // compute the light/sort position for particles from the light direction.
410 // don't just use the camera position -- if it is too far away from the cloud, then
411 // precision limitations may cause the STL sort to hang. Instead, put the sort position
412 // just outside the bounding sphere of the cloud in the direction of the camera.
413 Vec3f vecLightPos(vecDir);
414 vecLightPos *= (1.1*_boundingBox.GetRadius());
415 vecLightPos += _boundingBox.GetCenter();
417 // Set up a camera to look at the cloud from the light position. Since the sun is an infinite
418 // light source, this camera will use an orthographic projection tightly fit to the bounding
419 // sphere of the cloud.
422 // Avoid degenerate camera bases.
423 Vec3f vecUp(0, 1, 0);
424 if (fabs(vecDir * vecUp) - 1 < 1e-6) // check that the view and up directions are not parallel.
427 cam.LookAt(vecLightPos, _boundingBox.GetCenter(), vecUp);
429 // sort the particles away from the light source.
430 _SortParticles(cam.ViewDir(), vecLightPos, SKY_CLOUD_SORT_AWAY);
432 // projected dist to cntr along viewdir
433 float DistToCntr = (_boundingBox.GetCenter() - vecLightPos) * cam.ViewDir();
435 // calc tight-fitting near and far distances for the orthographic frustum
436 float rNearDist = DistToCntr - _boundingBox.GetRadius();
437 float rFarDist = DistToCntr + _boundingBox.GetRadius();
439 // set the modelview matrix from this camera.
440 glMatrixMode(GL_MODELVIEW);
445 cam.GetModelviewMatrix(M);
448 // switch to parallel projection
449 glMatrixMode(GL_PROJECTION);
452 glOrtho(-_boundingBox.GetRadius(), _boundingBox.GetRadius(),
453 -_boundingBox.GetRadius(), _boundingBox.GetRadius(),
454 rNearDist, rFarDist);
456 // set the material state / properties that clouds use for shading:
457 // Enables blending, with blend func (ONE, ONE_MINUS_SRC_ALPHA).
458 // Enables alpha test to discard completely transparent fragments.
459 // Disables depth test.
460 // Enables texturing, with modulation, and the texture set to the shared splat texture.
461 s_pShadeMaterial->Activate();
463 // these are used for projecting the particle position to determine where to read pixels.
464 double MM[16], PM[16];
465 int VP[4] = { 0, 0, s_iShadeResolution, s_iShadeResolution };
466 glGetDoublev(GL_MODELVIEW_MATRIX, MM);
467 glGetDoublev(GL_PROJECTION_MATRIX, PM);
469 // initialize back buffer to all white -- modulation darkens areas where cloud particles
470 // absorb light, and lightens it where they scatter light in the forward direction.
471 glClearColor(1, 1, 1, 1);
472 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
474 float rPixelsPerLength = s_iShadeResolution / (2 * _boundingBox.GetRadius());
476 // the solid angle over which we will sample forward-scattered light.
477 float rSolidAngle = 0.09;
480 for (ParticleIterator iter = _particles.begin(); iter != _particles.end(); ++iter, ++i)
482 Vec3f vecParticlePos = (*iter)->GetPosition();
484 Vec3f vecOffset(vecLightPos);
485 vecOffset -= vecParticlePos;
487 // compute the pixel area to read back in order to integrate the illumination of the particle
488 // over a constant solid angle.
489 float rDistance = fabs(cam.ViewDir() * vecOffset) - rNearDist;
491 float rArea = rSolidAngle * rDistance * rDistance;
492 int iPixelDim = sqrt(rArea) * rPixelsPerLength;
493 int iNumPixels = iPixelDim * iPixelDim;
500 // the scale factor to convert the read back pixel colors to an average illumination of the area.
501 float rColorScaleFactor = rSolidAngle / (iNumPixels * 255.0f);
503 unsigned char *c = new unsigned char[4 * iNumPixels];
507 // find the position in the buffer to which the particle position projects.
508 if (!gluProject(vecParticlePos.x, vecParticlePos.y, vecParticlePos.z,
510 &(vecWinPos.x), &(vecWinPos.y), &(vecWinPos.z)))
512 FAIL_RETURN_MSG(SKYRESULT_FAIL,
513 "Error: SkyCloud::Illuminate(): failed to project particle position.");
516 // offset the projected window position by half the size of the readback region.
517 vecWinPos.x -= 0.5 * iPixelDim;
518 if (vecWinPos.x < 0) vecWinPos.x = 0;
519 vecWinPos.y -= 0.5 * iPixelDim;
520 if (vecWinPos.y < 0) vecWinPos.y = 0;
522 // read back illumination of this particle from the buffer.
523 glReadBuffer(GL_BACK);
524 glReadPixels(vecWinPos.x, vecWinPos.y, iPixelDim, iPixelDim, GL_RGBA, GL_UNSIGNED_BYTE, c);
526 // scattering coefficient vector.
527 Vec4f vecScatter(s_rScatterFactor, s_rScatterFactor, s_rScatterFactor, 1);
529 // add up the read back pixels (only need one component -- its grayscale)
531 for (int k = 0; k < 4 * iNumPixels; k+=4)
535 // compute the amount of light scattered to this particle by particles closer to the light.
536 // this is the illumination over the solid angle that we measured (using glReadPixels) times
537 // the scattering coefficient (vecScatter);
538 Vec4f vecScatteredAmount(iSum * rColorScaleFactor,
539 iSum * rColorScaleFactor,
540 iSum * rColorScaleFactor,
541 1 - s_rTransparency);
542 vecScatteredAmount &= vecScatter;
544 // the color of th particle (iter) contributed by this light source (pLight) is the
545 // scattered light from the part of the cloud closer to the light, times the diffuse color
546 // of the light source. The alpha is 1 - the uniform transparency of all particles (modulated
547 // by the splat texture).
548 Vec4f vecColor = vecScatteredAmount;
549 vecColor &= pLight->GetDiffuse();
550 vecColor.w = 1 - s_rTransparency;
552 // add this color to the list of lit colors for the particle. The contribution from each light
553 // is kept separate because the phase function we apply at runtime depends on the light vector
554 // for each light source separately. This view-dependent effect is impossible without knowing
555 // the amount of light contributed for each light. This, of course, assumes the clouds will
556 // be lit by a reasonably small number of lights (The sun plus some simulation of light reflected
557 // from the sky and / or ground.) This technique works very well for simulating anisotropic
558 // illumination by skylight.
561 (*iter)->SetBaseColor(s_pMaterial->GetAmbient());
562 (*iter)->ClearLitColors();
563 (*iter)->AddLitColor(vecColor);
567 (*iter)->AddLitColor(vecColor);
570 // the following computation (scaling of the scattered amount by the phase function) is done
571 // after the lit color is stored so we don't add the scattering to this particle twice.
572 vecScatteredAmount *= 1.5; // rayleigh scattering phase function for angle of zero or 180 = 1.5!
575 if (vecScatteredAmount.x > 1) vecScatteredAmount.x = 1;
576 if (vecScatteredAmount.y > 1) vecScatteredAmount.y = 1;
577 if (vecScatteredAmount.z > 1) vecScatteredAmount.z = 1;
578 vecScatteredAmount.w = 1 - s_rTransparency;
580 vecScatteredAmount.x = 0.50; vecScatteredAmount.y = 0.60; vecScatteredAmount.z = 0.70;
582 // Draw the particle as a texture billboard. Use the scattered light amount as the color to
583 // simulate forward scattering of light by this particle.
585 DrawQuad(vecParticlePos, cam.X * (*iter)->GetRadius(), cam.Y * (*iter)->GetRadius(), vecScatteredAmount);
588 //glutSwapBuffers(); // Uncomment this swap buffers to visualize cloud illumination computation.
591 // Note: here we could optionally store the current back buffer as a shadow image
592 // to be projected from the light position onto the scene. This way we can have clouds shadow
595 // restore matrix stack and viewport.
596 glMatrixMode(GL_PROJECTION);
598 glMatrixMode(GL_MODELVIEW);
600 glViewport(iOldVP[0], iOldVP[1], iOldVP[2], iOldVP[3]);
606 //------------------------------------------------------------------------------
607 // Function : SkyCloud::CopyBoundingVolume
609 //------------------------------------------------------------------------------
611 * @fn SkyCloud::CopyBoundingVolume() const
612 * @brief Returns a new copy of the SkyMinMaxBox for this cloud.
614 SkyMinMaxBox* SkyCloud::CopyBoundingVolume() const
616 SkyMinMaxBox *pBox = new SkyMinMaxBox();
617 pBox->SetMax(_boundingBox.GetMax());
618 pBox->SetMin(_boundingBox.GetMin());
623 //------------------------------------------------------------------------------
624 // Function : SkyCloud::Load
626 //------------------------------------------------------------------------------
628 * @fn SkyCloud::Load(const SkyArchive &archive, float rScale, bool bLocal)
629 * @brief Loads the cloud data from @a archive.
631 * If @a rScale does not equal 1.0, then the cloud is scaled by an amount rScale.
633 SKYRESULT SkyCloud::Load(const SkyArchive &archive,
634 float rScale /* = 1.0f */,
635 bool bLocal /* = false */)
637 unsigned int iNumParticles;
638 Vec3f vecCenter = Vec3f::ZERO;
641 //archive.FindVec3f("CldCenter", &vecCenter);
642 //archive.FindFloat32("CldRadius", &rRadius);
644 //_boundingBox.SetMin(vecCenter - Vec3f(rRadius, rRadius, rRadius));
645 //_boundingBox.SetMax(vecCenter + Vec3f(rRadius, rRadius, rRadius));
647 archive.FindUInt32("CldNumParticles", &iNumParticles);
649 archive.FindVec3f("CldCenter", &vecCenter);
651 Vec3f *pParticlePositions = new Vec3f[iNumParticles];
652 float *pParticleRadii = new float[iNumParticles];
653 Vec4f *pParticleColors = new Vec4f[iNumParticles];
655 unsigned int iNumBytes;
656 archive.FindData("CldParticlePositions", ANY_TYPE, (void**const)&pParticlePositions, &iNumBytes);
657 archive.FindData("CldParticleRadii", ANY_TYPE, (void**const)&pParticleRadii, &iNumBytes);
658 archive.FindData("CldParticleColors", ANY_TYPE, (void**const)&pParticleColors, &iNumBytes);
660 for (unsigned int i = 0; i < iNumParticles; ++i)
662 SkyCloudParticle *pParticle = new SkyCloudParticle((pParticlePositions[i] + vecCenter) * rScale,
663 pParticleRadii[i] * rScale,
665 _boundingBox.AddPoint(pParticle->GetPosition());
667 _particles.push_back(pParticle);
669 // this is just a bad hack to align cloud field from skyworks with local horizon at KSFO
670 // we need to develop out own scheme for loading and positioning clouds
674 //moveit.Set( -10000.0, 1500.0, 1500.0 );
675 moveit.Set( 0.0, 0.0, 1050.0 );
677 rot_mat.Set( 1, 0, 0,
680 // flip the y and z axis
683 // adjust for lon af KSFO -122.357
684 rot_mat.Set( -0.5352f, 0.8447f, 0.0f,
685 -0.8447f, -0.5352f, 0.0f,
690 // and about x for latitude 37.6135
691 rot_mat.Set( 1.0f, 0.0, 0.0f,
692 0.0f, 0.7921f, -0.6103f,
693 0.0f, 0.6103f, 0.7921f);
703 //------------------------------------------------------------------------------
704 // Function : SkyCloud::Save
706 //------------------------------------------------------------------------------
708 * @fn SkyCloud::Save(SkyArchive &archive) const
709 * @brief Saves the cloud data to @a archive.
711 * @todo <WRITE EXTENDED SkyCloud::Save FUNCTION DOCUMENTATION>
713 SKYRESULT SkyCloud::Save(SkyArchive &archive) const
715 SkyArchive myArchive("Cloud");
716 //myArchive.AddVec3f("CldCenter", _center);
717 //myArchive.AddFloat32("CldRadius", _boundingBox.GetRadius());
718 myArchive.AddUInt32("CldNumParticles", _particles.size());
721 Vec3f *pParticlePositions = new Vec3f[_particles.size()];
722 float *pParticleRadii = new float[_particles.size()];
723 Vec4f *pParticleColors = new Vec4f[_particles.size()];
727 for (ParticleConstIterator iter = _particles.begin(); iter != _particles.end(); ++iter, ++i)
729 pParticlePositions[i] = (*iter)->GetPosition(); // position around origin
730 pParticleRadii[i] = (*iter)->GetRadius();
731 pParticleColors[i] = (*iter)->GetBaseColor();
734 myArchive.AddData("CldParticlePositions",
740 myArchive.AddData("CldParticleRadii",
746 myArchive.AddData("CldParticleColors",
752 archive.AddArchive(myArchive);
758 //------------------------------------------------------------------------------
759 // Function : SkyCloud::Rotate
761 //------------------------------------------------------------------------------
763 * @fn SkyCloud::Rotate(const Mat33f& rot)
764 * @brief @todo <WRITE BRIEF SkyCloud::Rotate DOCUMENTATION>
766 * @todo <WRITE EXTENDED SkyCloud::Rotate FUNCTION DOCUMENTATION>
768 void SkyCloud::Rotate(const Mat33f& rot)
770 _boundingBox.Clear();
771 for (int i = 0; i < _particles.size(); ++i)
773 _particles[i]->SetPosition(rot * _particles[i]->GetPosition());
774 _boundingBox.AddPoint(_particles[i]->GetPosition());
779 //------------------------------------------------------------------------------
780 // Function : SkyCloud::Translate
782 //------------------------------------------------------------------------------
784 * @fn SkyCloud::Translate(const Vec3f& trans)
785 * @brief @todo <WRITE BRIEF SkyCloud::Translate DOCUMENTATION>
787 * @todo <WRITE EXTENDED SkyCloud::Translate FUNCTION DOCUMENTATION>
789 void SkyCloud::Translate(const Vec3f& trans)
791 for (int i = 0; i < _particles.size(); ++i)
793 _particles[i]->SetPosition(_particles[i]->GetPosition() + trans);
795 _boundingBox.SetMax(_boundingBox.GetMax() + trans);
796 _boundingBox.SetMin(_boundingBox.GetMin() + trans);
800 //------------------------------------------------------------------------------
801 // Function : SkyCloud::Scale
803 //------------------------------------------------------------------------------
805 * @fn SkyCloud::Scale(const float scale)
806 * @brief @todo <WRITE BRIEF SkyCloud::Scale DOCUMENTATION>
808 * @todo <WRITE EXTENDED SkyCloud::Scale FUNCTION DOCUMENTATION>
810 void SkyCloud::Scale(const float scale)
812 _boundingBox.Clear();
813 for (int i = 0; i < _particles.size(); ++i)
815 _particles[i]->SetPosition(_particles[i]->GetPosition() * scale);
816 _boundingBox.AddPoint(_particles[i]->GetPosition());
821 //------------------------------------------------------------------------------
822 // Function : SkyCloud::_SortParticles
824 //------------------------------------------------------------------------------
826 * @fn SkyCloud::_SortParticles(const Vec3f& vecViewDir, const Vec3f& sortPoint, SortDirection dir)
827 * @brief Sorts the cloud particles in the direction specified by @a dir.
829 * @vecSortPoint is assumed to already be transformed into the basis space of the cloud.
831 void SkyCloud::_SortParticles(const Vec3f& vecViewDir,
832 const Vec3f& vecSortPoint,
836 for (int i = 0; i < _particles.size(); ++i)
838 partPos = _particles[i]->GetPosition();
839 partPos -= vecSortPoint;
840 _particles[i]->SetSquareSortDistance(partPos * vecViewDir);//partPos.LengthSqr());
845 case SKY_CLOUD_SORT_TOWARD:
846 std::sort(_particles.begin(), _particles.end(), _towardComparator);
848 case SKY_CLOUD_SORT_AWAY:
849 std::sort(_particles.begin(), _particles.end(), _awayComparator);
858 //------------------------------------------------------------------------------
859 // Function : EvalHermite
861 //------------------------------------------------------------------------------
863 * EvalHermite(float pA, float pB, float vA, float vB, float u)
864 * @brief Evaluates Hermite basis functions for the specified coefficients.
866 inline float EvalHermite(float pA, float pB, float vA, float vB, float u)
868 float u2=(u*u), u3=u2*u;
869 float B0 = 2*u3 - 3*u2 + 1;
870 float B1 = -2*u3 + 3*u2;
871 float B2 = u3 - 2*u2 + u;
873 return( B0*pA + B1*pB + B2*vA + B3*vB );
876 // NORMALIZED GAUSSIAN INTENSITY MAP (N must be a power of 2)
878 //------------------------------------------------------------------------------
879 // Function : CreateGaussianMap
881 //------------------------------------------------------------------------------
883 * CreateGaussianMap(int N)
885 * Creates a 2D gaussian image using a hermite surface.
887 unsigned char* CreateGaussianMap(int N)
889 float *M = new float[2*N*N];
890 unsigned char *B = new unsigned char[4*N*N];
896 for (int y=0; y<N; y++, Y+=Incr)
900 for (int x=0; x<N; x++, X+=Incr, i+=2, j+=4)
902 Dist = (float)sqrt(X*X+Y2);
904 M[i+1] = M[i] = EvalHermite(0.4f,0,0,0,Dist);// * (1 - noise);
905 B[j+3] = B[j+2] = B[j+1] = B[j] = (unsigned char)(M[i] * 255);
908 SAFE_DELETE_ARRAY(M);
913 //------------------------------------------------------------------------------
914 // Function : SkyCloud::_CreateSplatTexture
916 //------------------------------------------------------------------------------
918 * @fn SkyCloud::_CreateSplatTexture(unsigned int iResolution)
919 * @brief Creates the texture map used for cloud particles.
921 void SkyCloud::_CreateSplatTexture(unsigned int iResolution)
923 unsigned char *splatTexture = CreateGaussianMap(iResolution);
925 TextureManager::InstancePtr()->Create2DTextureObject(texture, iResolution, iResolution,
926 GL_RGBA, splatTexture);
928 s_pMaterial->SetTexture(0, GL_TEXTURE_2D, texture);
929 s_pShadeMaterial->SetTexture(0, GL_TEXTURE_2D, texture);
930 s_pMaterial->SetTextureParameter(0, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
931 s_pShadeMaterial->SetTextureParameter(0, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
932 s_pMaterial->SetTextureParameter(0, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
933 s_pShadeMaterial->SetTextureParameter(0, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
934 s_pMaterial->SetTextureParameter(0, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
935 s_pShadeMaterial->SetTextureParameter(0, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
936 s_pMaterial->EnableTexture(0, true);
937 s_pShadeMaterial->EnableTexture(0, true);
939 SAFE_DELETE_ARRAY(splatTexture);
943 //------------------------------------------------------------------------------
944 // Function : SkyCloud::_PhaseFunction
946 //------------------------------------------------------------------------------
948 * @fn SkyCloud::_PhaseFunction(const Vec3f& vecLightDir, const Vec3f& vecViewDir)
949 * @brief Computes the phase (scattering) function of the given light and view directions.
951 * A phase function is a transfer function that determines, for any angle between incident
952 * and outgoing directions, how much of the incident light intensity will be
953 * scattered in the outgoing direction. For example, scattering by very small
954 * particles such as those found in clear air, can be approximated using <i>Rayleigh
955 * scattering</i>. The phase function for Rayleigh scattering is
956 * p(q) = 0.75*(1 + cos<sup>2</sup>(q)), where q is the angle between incident
957 * and scattered directions. Scattering by larger particles is more complicated.
958 * It is described by Mie scattering theory. Cloud particles are more in the regime
959 * of Mie scattering than Rayleigh scattering. However, we obtain good visual
960 * results by using the simpler Rayleigh scattering phase function as an approximation.
962 float SkyCloud::_PhaseFunction(const Vec3f& vecLightDir, const Vec3f& vecViewDir)
964 float rCosAlpha = vecLightDir * vecViewDir;
965 return .75f * (1 + rCosAlpha * rCosAlpha); // rayleigh scattering = (3/4) * (1+cos^2(alpha))