// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "std3d.h"
#include "nel/3d/cloud_scape.h"
#include "nel/3d/driver.h"
#include "nel/3d/scissor.h"
#include "nel/3d/viewport.h"
// ------------------------------------------------------------------------------------------------
using namespace NLMISC;
namespace NL3D
{
// ------------------------------------------------------------------------------------------------
#define SQR(x) (x)*(x)
#define MAX_DIST 400.0f
#define MAX_CLOUDS 256
// QUEUE_SIZE must be at least 2*MAX_CLOUDS
#define QUEUE_SIZE 512
static const double MAX_CLOUDS_ANIM_DELTA_TIME = 0.075; // maximum delta time handled by cloud animation, delta t above that are clamped
static const double MIN_CLOUDS_ANIM_DELTA_TIME = 0.005; // minimum delta time handled by clouds animation
static const double MAX_TIME_FOR_CLOUD_ANIM = 0.02; // max number of second spent for cloud render before we check if too slow
static const double MAX_FRAME_PERCENT_FOR_CLOUD_RENDERING = 20 / 100; // at most 20 % of the frame for cloud render
// ------------------------------------------------------------------------------------------------
// SCloudTexture3D
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
SCloudTexture3D::SCloudTexture3D ()
{
Mem = NULL;
Mem2 = NULL;
MemBuffer = NULL;
ToLightRGB.initUnlit();
ToLightRGB.setShader (CMaterial::Normal);
ToLightRGB.setZFunc (CMaterial::always);
ToLightRGB.setZWrite (false);
ToLightRGB.texEnvOpRGB (0, CMaterial::Replace);
ToLightRGB.texEnvArg0RGB (0, CMaterial::Diffuse, CMaterial::SrcColor);
ToLightRGB.texEnvOpAlpha (0, CMaterial::Replace);
ToLightRGB.texEnvArg0Alpha (0, CMaterial::Texture, CMaterial::SrcAlpha);
ToLightRGB.setBlend (true);
ToLightRGB.setBlendFunc (CMaterial::invsrcalpha, CMaterial::srcalpha);
ToLightRGB.setColor (CRGBA(0,0,0,255));
ToLightAlpha.initUnlit();
ToLightAlpha.setShader (CMaterial::Normal);
ToLightAlpha.setZFunc (CMaterial::always);
ToLightAlpha.setZWrite (false);
ToLightAlpha.texEnvOpRGB (0, CMaterial::Replace);
ToLightAlpha.texEnvArg0RGB (0, CMaterial::Diffuse, CMaterial::SrcColor);
ToLightAlpha.texEnvOpAlpha (0, CMaterial::Replace);
ToLightAlpha.texEnvArg0Alpha (0, CMaterial::Texture, CMaterial::InvSrcAlpha);
ToLightAlpha.setColor (CRGBA(0,0,0,255));
ToBill.initUnlit();
ToBill.setZFunc (CMaterial::always);
ToBill.setZWrite (false);
ToBill.setDoubleSided(true);
ToBill.texEnvOpRGB (0, CMaterial::Add);
ToBill.texEnvArg0RGB (0, CMaterial::Texture, CMaterial::SrcColor);
ToBill.texEnvArg1RGB (0, CMaterial::Diffuse, CMaterial::SrcColor);
ToBill.setColor (CRGBA(80,80,80,255));
ToBill.texEnvOpAlpha (0, CMaterial::Replace);
ToBill.texEnvArg0Alpha (0, CMaterial::Texture, CMaterial::SrcAlpha);
ToBill.texEnvOpRGB (1, CMaterial::Modulate);
ToBill.texEnvArg0RGB (1, CMaterial::Previous, CMaterial::SrcColor);
ToBill.texEnvArg1RGB (1, CMaterial::Previous, CMaterial::SrcAlpha);
ToBill.texEnvOpAlpha (1, CMaterial::Replace);
ToBill.texEnvArg0Alpha (1, CMaterial::Previous, CMaterial::SrcAlpha);
ToBill.setBlendFunc (CMaterial::one, CMaterial::invsrcalpha);
ToBill.setBlend (true);
MatCopy.initUnlit();
MatCopy.setShader (CMaterial::Normal);
MatCopy.setZFunc (CMaterial::always);
MatCopy.setZWrite (false);
MatCopy.texEnvOpRGB (0, CMaterial::Replace);
MatCopy.texEnvArg0RGB (0, CMaterial::Texture, CMaterial::SrcColor);
MatCopy.texEnvOpAlpha (0, CMaterial::Replace);
MatCopy.texEnvArg0Alpha (0, CMaterial::Texture, CMaterial::SrcAlpha);
MatCopy.setBlend (false);
MatCopy.setColor (CRGBA(255,255,255,255));
}
// ------------------------------------------------------------------------------------------------
void SCloudTexture3D::init (uint32 nWidth, uint32 nHeight, uint32 nDepth)
{
if (Mem != NULL)
return;
Width = raiseToNextPowerOf2 (nWidth);
Height = raiseToNextPowerOf2 (nHeight);
Depth = raiseToNextPowerOf2 (nDepth);
uint32 vdpo2 = getPowerOf2(Depth);
NbW = 1 << (vdpo2 / 2);
if ((vdpo2 & 1) != 0)
NbH = 2 << (vdpo2 / 2);
else
NbH = 1 << (vdpo2 / 2);
Mem = new uint8[4*NbW*Width*NbH*Height];
Mem2 = new uint8[4*NbW*Width*NbH*Height];
MemBuffer = new uint8[4*Width*Height];
Tex = new CTextureMem (Mem, 4*NbW*Width*NbH*Height, true, false, NbW*Width, NbH*Height, CBitmap::RGBA);
Tex2 = new CTextureMem (Mem2, 4*NbW*Width*NbH*Height, true, false, NbW*Width, NbH*Height, CBitmap::RGBA);
TexBuffer = new CTextureMem (MemBuffer, 4*Width*Height, true, false, Width, Height, CBitmap::RGBA);
Tex->setWrapS (ITexture::Clamp);
Tex->setWrapT (ITexture::Clamp);
Tex->setFilterMode (ITexture::Linear, ITexture::LinearMipMapOff);
Tex->setReleasable (false);
Tex->setRenderTarget (true);
Tex->generate ();
Tex2->setWrapS (ITexture::Clamp);
Tex2->setWrapT (ITexture::Clamp);
Tex2->setFilterMode (ITexture::Linear, ITexture::LinearMipMapOff);
Tex2->setReleasable (false);
Tex2->setRenderTarget (true);
Tex2->generate ();
TexBuffer->setWrapS (ITexture::Clamp);
TexBuffer->setWrapT (ITexture::Clamp);
TexBuffer->setFilterMode (ITexture::Linear, ITexture::LinearMipMapOff);
TexBuffer->setReleasable (false);
TexBuffer->setRenderTarget (true);
TexBuffer->generate ();
ToLightRGB.setTexture (0, Tex);
ToLightAlpha.setTexture (0, Tex);
ToBill.setTexture(0, Tex2);
ToBill.setTexture(1, Tex2);
MatCopy.setTexture(0, TexBuffer);
}
// ------------------------------------------------------------------------------------------------
// SCloudTextureClamp
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
SCloudTextureClamp::SCloudTextureClamp ()
{
Mem = NULL;
ToClamp.initUnlit();
ToClamp.setShader (CMaterial::Normal);
ToClamp.texEnvOpAlpha (0, CMaterial::Add);
ToClamp.texEnvArg0Alpha (0, CMaterial::Texture, CMaterial::SrcAlpha);
ToClamp.texEnvArg1Alpha (0, CMaterial::Diffuse, CMaterial::SrcAlpha);
ToClamp.setColor (CRGBA(255,255,255,255));
ToClamp.setBlend (true);
ToClamp.setBlendFunc (CMaterial::one, CMaterial::one);
ToClamp.setZFunc (CMaterial::always);
ToClamp.setZWrite (false);
}
// ------------------------------------------------------------------------------------------------
void SCloudTextureClamp::init (uint32 nWidth, uint32 nHeight, uint32 nDepth, const std::string &filename)
{
if (Mem != NULL)
return;
Width = raiseToNextPowerOf2 (nWidth);
Height = raiseToNextPowerOf2 (nHeight);
Depth = raiseToNextPowerOf2 (nDepth);
uint32 vdpo2 = getPowerOf2(Depth);
NbW = 1 << (vdpo2 / 2);
if ((vdpo2 & 1) != 0)
NbH = 2 << (vdpo2 / 2);
else
NbH = 1 << (vdpo2 / 2);
Mem = new uint8[NbW*Width*NbH*Height];
uint32 i, j;
if (filename.empty())
{
// No filename so init with default
for (i = 0; i < NbW; ++i)
{
for (j = 0; j < NbH; ++j)
{
uint32 d = i+j*NbW;
uint32 k, l;
for (k = 0; k < Width; ++k)
for (l = 0; l < Height; ++l)
{
float x = k+0.5f;
float y = l+0.5f;
float z = d+0.5f;
float xc = Width/2.0f;
float yc = Height/2.0f;
float zc = Depth/2.0f;
float r = (x-xc)*(x-xc)/(Width*Width/4.0f) + (y-yc)*(y-yc)/(Height*Height/4.0f)
+ (z-zc)*(z-zc)/(Depth*Depth/4.0f);
uint8 col = 255;
if (r < 1.0f)
{
col = (uint8)((r)*223+32);
}
Mem[i*Width+k + (j*Height+l)*NbW*Width] = col;
}
}
}
}
else
{
// Load file TODO !
}
Tex = new CTextureMem (Mem, NbW*Width*NbH*Height, true, false, NbW*Width, NbH*Height, CBitmap::Alpha);
Tex->setWrapS (ITexture::Repeat);
Tex->setWrapT (ITexture::Repeat);
Tex->setFilterMode (ITexture::Linear, ITexture::LinearMipMapOff);
Tex->touch();
Tex->setReleasable (false);
Tex->generate();
ToClamp.setTexture(0, Tex);
}
// ------------------------------------------------------------------------------------------------
// CCloudScape
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
CCloudScape::CCloudScape (NL3D::IDriver *pDriver) : _Noise3D (pDriver)
{
_Driver = pDriver;
// Misc purpose VB
_VertexBuffer.setVertexFormat (CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag | CVertexBuffer::TexCoord1Flag);
_VertexBuffer.setNumVertices (4);
_VertexBuffer.setName("CloudScape");
// Material used for cleaning
_MatClear.initUnlit();
_MatClear.setDoubleSided (true);
_MatClear.setZFunc (CMaterial::always);
_MatClear.setZWrite (false);
_MatClear.setBlend (false);
_MatBill.initUnlit();
_MatBill.setShader (CMaterial::Normal); // not needed
_MatBill.texEnvOpRGB (0, CMaterial::Replace);
_MatBill.texEnvArg0RGB (0, CMaterial::Texture, CMaterial::SrcColor);
_MatBill.texEnvOpAlpha (0, CMaterial::Replace);
_MatBill.texEnvArg0Alpha (0, CMaterial::Texture, CMaterial::SrcAlpha);
_MatBill.texEnvOpRGB (1, CMaterial::InterpolateDiffuse);
_MatBill.texEnvArg0RGB (1, CMaterial::Texture, CMaterial::SrcColor);
_MatBill.texEnvArg1RGB (1, CMaterial::Previous, CMaterial::SrcColor);
_MatBill.texEnvOpAlpha (1, CMaterial::InterpolateDiffuse);
_MatBill.texEnvArg0Alpha (1, CMaterial::Texture, CMaterial::SrcAlpha);
_MatBill.texEnvArg1Alpha (1, CMaterial::Previous, CMaterial::SrcAlpha);
_MatBill.setBlend (true);
_MatBill.setBlendFunc(CMaterial::one, CMaterial::invsrcalpha);
_MatBill.setZFunc (CMaterial::always);
_MatBill.setZWrite (false);
_MatBill.setDoubleSided (true);
_MatBill.setAlphaTest(true);
_MatBill.setAlphaTestThreshold(2.0f/256.0f);
_LODQualityThreshold = 160.0f;
_IsIncomingCSS = false;
_DebugQuad = false;
_NbHalfCloudToUpdate = 1;
_CurrentCloudInProcess = NULL;
_LastAnimRenderTime = 0;
_MaxDeltaTime = 0.1; // 100 ms
}
// ------------------------------------------------------------------------------------------------
CCloudScape::~CCloudScape ()
{
}
// ------------------------------------------------------------------------------------------------
void CCloudScape::init (SCloudScapeSetup *pCSS, NL3D::CCamera *pCamera)
{
_ResetCounter = _Driver->getResetCounter();
_ViewerCam = pCamera;
_Noise3D.init();
_AllClouds.resize (MAX_CLOUDS, CCloud(this));
_CloudPower.resize (MAX_CLOUDS);
_ShouldProcessCloud.resize (MAX_CLOUDS);
// For the moment only one clamp texture (generated)
Tex3DTemp.init (64, 32, 32);
TexClamp.init (64, 32, 32,"");
if (pCSS != NULL)
{
_CurrentCSS = *pCSS;
_NewCSS = *pCSS;
_OldCSS = *pCSS;
}
_IsIncomingCSS = false;
_TimeNewCSS = 60.0*60.0;
uint32 i;
for (i = 0; i < MAX_CLOUDS; ++i)
{
float newX, newY, newZ, newSizeX, newSizeY, newSizeZ;
CCloud &c = _AllClouds[i];
c.setTex3DTemp (Tex3DTemp);
c.setTexClamp (TexClamp);
for(;;)
{
bool bRecalc = false;
newX = MAX_DIST*(1.0f-2.0f*(((float)rand())/RAND_MAX));
newY = MAX_DIST*(1.0f-2.0f*(((float)rand())/RAND_MAX));
newZ = 85.0f+40.0f*(1.0f-2.0f*(((float)rand())/RAND_MAX));
newSizeX = 60.0f+10.0f*(1.0f-2.0f*(((float)rand())/RAND_MAX));
newSizeY = 30.0f+10.0f*(1.0f-2.0f*(((float)rand())/RAND_MAX));
newSizeZ = 30.0f+10.0f*(1.0f-2.0f*(((float)rand())/RAND_MAX));
float f = 0.7f+0.3f*((float)rand())/RAND_MAX;
newSizeX *= 1.5f*f;
newSizeY *= 1.5f*f;
newSizeZ *= 1.5f*f;
float d = sqrtf(SQR(newX)+SQR(newY));
if (d > MAX_DIST) bRecalc = true;
for (uint32 k = 0;k < i; ++k)
{
CCloud &c2 = _AllClouds[k];
if ((fabs(newX-c2.getX()) < (newSizeX/2+c2.getSizeX()/2)) &&
(fabs(newY-c2.getY()) < (newSizeY/2+c2.getSizeY()/2)) &&
(fabs(newZ-c2.getZ()) < (newSizeZ/2+c2.getSizeZ()/2)))
bRecalc = true;
}
if (!bRecalc) break;
}
c.init (64, 32, 32, 0.122f, 4);
c.setX (newX-newSizeX/2);
c.setY (newY-newSizeY/2);
c.setZ (newZ-newSizeZ/2);
c.setSizeX (newSizeX);
c.setSizeY (newSizeY);
c.setSizeZ (newSizeZ);
c.Time = 0;
c.FuturTime = _CurrentCSS.NbCloud * 2 * (0.04/_NbHalfCloudToUpdate);
if (i < _CurrentCSS.NbCloud)
{
_CloudPower[i] = 255;
_ShouldProcessCloud[i] = true;
}
else
{
_CloudPower[i] = 0;
_ShouldProcessCloud[i] = false;
}
}
_SortedClouds.resize (MAX_CLOUDS);
_CloudSchedulerSize = _CurrentCSS.NbCloud;
_CloudSchedulerLastAdded.resize (MAX_CLOUDS);
_FrameCounter = 0;
_CloudScheduler.clear();
for (i = 0; i < MAX_CLOUDS; ++i)
_CloudSchedulerLastAdded[i].ValidPos = false;
if (_CurrentCSS.NbCloud == 0)
{
for (i = 0; i < QUEUE_SIZE; ++i)
{
SCloudSchedulerEntry cse;
cse.CloudIndex = -1;
cse.Frame = _FrameCounter;
cse.Ambient = _CurrentCSS.Ambient;
cse.Diffuse = _CurrentCSS.Diffuse;
_CloudScheduler.insert(_CloudScheduler.end(), cse);
++_FrameCounter;
}
}
else
{
for (i = 0; i < QUEUE_SIZE; ++i)
{
sint32 nCloudNb;
nCloudNb = i%_CurrentCSS.NbCloud;
SCloudSchedulerEntry cse;
cse.CloudIndex = nCloudNb;
if (_CloudSchedulerLastAdded[nCloudNb].ValidPos == true)
{
SCloudSchedulerEntry &lastCSE = *_CloudSchedulerLastAdded[nCloudNb].Pos;
sint32 delta = _FrameCounter - lastCSE.Frame;
lastCSE.DeltaNextCalc = delta;
}
cse.Frame = _FrameCounter;
cse.Ambient = _CurrentCSS.Ambient;
cse.Diffuse = _CurrentCSS.Diffuse;
cse.Power = _CloudPower[cse.CloudIndex];
_CloudSchedulerLastAdded[cse.CloudIndex].Pos = _CloudScheduler.insert(_CloudScheduler.end(), cse);
_CloudSchedulerLastAdded[cse.CloudIndex].ValidPos = true;
//_CloudSchedulerLastAdded[cse.CloudIndex].Pos = _CloudScheduler.end()-1;
++_FrameCounter;
}
}
_GlobalTime = 0.0f;
_DTRest = 0.0f;
_Generate = true;
_AverageFrameRate.init(16);
for (i = 0; i < 16; ++i)
_AverageFrameRate.addValue (40.0f/1000.0f);
_ExtrapolatedPriorities.resize (MAX_CLOUDS);
for (i = 0; i < QUEUE_SIZE; ++i)
anim (41.0/1000.0, _ViewerCam);
}
// ------------------------------------------------------------------------------------------------
void CCloudScape::set (SCloudScapeSetup &css)
{
_IncomingCSS = css;
_IsIncomingCSS = true;
}
// ------------------------------------------------------------------------------------------------
void CCloudScape::anim (double dt, NL3D::CCamera *pCamera)
{
double startDate = double(CTime::getLocalTime())/1000.0;
sint32 i;
// Disable fog
bool fog = _Driver->fogEnabled();
_Driver->enableFog (false);
_ViewerCam = pCamera;
// update the max delta time
// If rendering was too slow and took too much time of the previous frame, we decrease the max delta time to give clouds less processing time
// Otherwise a cycle occurs, and slow rendering propagate from frame to frame
if (dt != 0 && _LastAnimRenderTime > MAX_TIME_FOR_CLOUD_ANIM && (_LastAnimRenderTime / dt) > MAX_FRAME_PERCENT_FOR_CLOUD_RENDERING)
{
// if cloud rendering take too much time of previous frame, allocate less time for clouds
// NB : check is only done if clouds rendering is above a given thrheshold, because if only clouds are rendered, then they may take 100% of frame without
// having slow rendering
_MaxDeltaTime = std::max(MIN_CLOUDS_ANIM_DELTA_TIME, _MaxDeltaTime / 2);
}
else
{
// clouds didn't take too much time -> allocate more time for them
_MaxDeltaTime = std::min(MAX_CLOUDS_ANIM_DELTA_TIME , _MaxDeltaTime + 0.002);
}
// 10 fps -> 200 fps
if (dt > _MaxDeltaTime) dt = _MaxDeltaTime;
if (dt < MIN_CLOUDS_ANIM_DELTA_TIME) dt = MIN_CLOUDS_ANIM_DELTA_TIME;
_DeltaTime = dt;
_GlobalTime += _DeltaTime;
_AverageFrameRate.addValue ((float)_DeltaTime);
// Animate the CSS
if (_TimeNewCSS > _NewCSS.TimeToChange)
{
_CurrentCSS = _NewCSS;
_OldCSS = _NewCSS;
if (_IsIncomingCSS)
{
_IsIncomingCSS = false;
_NewCSS = _IncomingCSS;
_TimeNewCSS = 0;
if (_NewCSS.NbCloud > _OldCSS.NbCloud)
for (i = 0; i < (sint32)(_NewCSS.NbCloud-_OldCSS.NbCloud); ++i)
{
CCloud &c = _AllClouds[_OldCSS.NbCloud+i];
c.CloudPower = 0;
_CloudPower[_OldCSS.NbCloud+i] = 0;
}
}
}
else
{
float inter = (float)(_TimeNewCSS / _NewCSS.TimeToChange);
_CurrentCSS.WindSpeed = (_NewCSS.WindSpeed - _OldCSS.WindSpeed)*inter + _OldCSS.WindSpeed;
_CurrentCSS.CloudSpeed = (_NewCSS.CloudSpeed - _OldCSS.CloudSpeed)*inter + _OldCSS.CloudSpeed;
_CurrentCSS.Ambient.R = (uint8)((_NewCSS.Ambient.R - _OldCSS.Ambient.R)*inter + _OldCSS.Ambient.R);
_CurrentCSS.Ambient.G = (uint8)((_NewCSS.Ambient.G - _OldCSS.Ambient.G)*inter + _OldCSS.Ambient.G);
_CurrentCSS.Ambient.B = (uint8)((_NewCSS.Ambient.B - _OldCSS.Ambient.B)*inter + _OldCSS.Ambient.B);
_CurrentCSS.Ambient.A = (uint8)((_NewCSS.Ambient.A - _OldCSS.Ambient.A)*inter + _OldCSS.Ambient.A);
_CurrentCSS.Diffuse.R = (uint8)((_NewCSS.Diffuse.R - _OldCSS.Diffuse.R)*inter + _OldCSS.Diffuse.R);
_CurrentCSS.Diffuse.G = (uint8)((_NewCSS.Diffuse.G - _OldCSS.Diffuse.G)*inter + _OldCSS.Diffuse.G);
_CurrentCSS.Diffuse.B = (uint8)((_NewCSS.Diffuse.B - _OldCSS.Diffuse.B)*inter + _OldCSS.Diffuse.B);
_CurrentCSS.Diffuse.A = (uint8)((_NewCSS.Diffuse.A - _OldCSS.Diffuse.A)*inter + _OldCSS.Diffuse.A);
if (_NewCSS.NbCloud > _OldCSS.NbCloud)
{
// Add some clouds
float slice = (_NewCSS.TimeToChange/4) / (_NewCSS.NbCloud-_OldCSS.NbCloud);
sint32 diffCloud = _NewCSS.NbCloud-_OldCSS.NbCloud;
_CurrentCSS.NbCloud = _OldCSS.NbCloud + (1+(uint32)(_TimeNewCSS/slice));
if (_CurrentCSS.NbCloud > _NewCSS.NbCloud)
_CurrentCSS.NbCloud = _NewCSS.NbCloud;
for (i = 0; i < diffCloud; ++i)
{
_ShouldProcessCloud[_OldCSS.NbCloud+i] = true;
if (_TimeNewCSS < i*slice)
_CloudPower[_OldCSS.NbCloud+i] = 1;
else if (_TimeNewCSS > (i*slice+3*_NewCSS.TimeToChange/4))
_CloudPower[_OldCSS.NbCloud+i] = 255;
else
_CloudPower[_OldCSS.NbCloud+i] = (uint8)(255*(_TimeNewCSS-i*slice)/(3*_NewCSS.TimeToChange/4));
}
}
else
{
// Remove some clouds
sint32 diffCloud = _OldCSS.NbCloud-_NewCSS.NbCloud;
if (diffCloud)
{
float slice = (_NewCSS.TimeToChange/4) / (float)diffCloud;
_CurrentCSS.NbCloud = _OldCSS.NbCloud;
for (i = 0; i < diffCloud; ++i)
{
if (_TimeNewCSS < i*slice)
_CloudPower[_OldCSS.NbCloud-i-1] = 255;
else if (_TimeNewCSS > (i*slice+3*_NewCSS.TimeToChange/4))
_CloudPower[_OldCSS.NbCloud-i-1] = 0;
else
_CloudPower[_OldCSS.NbCloud-i-1] = (uint8)(255-255*(_TimeNewCSS-i*slice)/(3*_NewCSS.TimeToChange/4));
}
}
}
}
// Make the right number of half cloud
_DTRest += dt;
while (_DTRest > (0.04/_NbHalfCloudToUpdate))
{
makeHalfCloud ();
_DTRest -= 0.04/_NbHalfCloudToUpdate;
for (i = 0; i < MAX_CLOUDS; ++i)
{
CCloud &c = _AllClouds[i];
c.Time += 0.04/_NbHalfCloudToUpdate;
}
_TimeNewCSS += 0.04/_NbHalfCloudToUpdate;
}
// Backup fog
_Driver->enableFog (fog);
// Has the driver been reseted ?
if (_ResetCounter != _Driver->getResetCounter())
{
/* Yes. Force the rebuild of all the clouds not setuped in VRAM */
_ResetCounter = _Driver->getResetCounter();
i = 0;
while (i < MAX_CLOUDS)
{
while (_ShouldProcessCloud[i] &&
(!_AllClouds[i]._TexBill->setupedIntoDriver() || !_AllClouds[i]._TexOldBill->setupedIntoDriver()))
{
// Force a cloudscape rebuild
anim (41.0/1000.0, _ViewerCam);
}
i++;
}
}
double endDate = double(CTime::getLocalTime())/1000.0;
_LastAnimRenderTime = endDate - startDate;
}
// ------------------------------------------------------------------------------------------------
void CCloudScape::makeHalfCloud ()
{
CVector Viewer = CVector(0,0,0); //_ViewerCam->getMatrix().getPos();
if (_Generate)
{
// Find the next cloud in the list
SCloudSchedulerEntry FrontCSE;
FrontCSE = _CloudScheduler.front();
// Is the cloud do not have another reference in the list add it now because it should be processed
sint32 CloudIndexToAdd = -1;
if ((FrontCSE.CloudIndex != -1) &&
(_ShouldProcessCloud[FrontCSE.CloudIndex] == true) &&
( (_CloudSchedulerLastAdded[FrontCSE.CloudIndex].ValidPos == false) ||
((_CloudSchedulerLastAdded[FrontCSE.CloudIndex].ValidPos == true) &&
(_CloudSchedulerLastAdded[FrontCSE.CloudIndex].Pos == _CloudScheduler.begin()))
))
{
// It should be added now !
CloudIndexToAdd = FrontCSE.CloudIndex;
FrontCSE.DeltaNextCalc = QUEUE_SIZE;
}
else if (_CurrentCSS.NbCloud != 0)
{
// Choose a Cloud Index To Add at the end of the list
uint32 nPeriodeMax = _CurrentCSS.NbCloud+_CurrentCSS.NbCloud/10;
sint32 Priority = -10000;
uint32 i;
float sumPrior = 0.0f;
for (i = 0; i < MAX_CLOUDS; ++i)
if (_ShouldProcessCloud[i])
{
CCloud &rC = _AllClouds[i];
float ExtrapolatedTime = ((0.04f/_NbHalfCloudToUpdate) * QUEUE_SIZE * 2);
float x = rC.getLastX () + ExtrapolatedTime * _CurrentCSS.WindSpeed;
//float d = sqrtf(SQR(x+rC.getSizeX()/2-Viewer.x)+SQR(rC.getY()+rC.getSizeY()/2-Viewer.y)+
// SQR(rC.getZ()+rC.getSizeZ()/2-Viewer.z));
float d = SQR(x+rC.getSizeX()/2-Viewer.x)+SQR(rC.getY()+rC.getSizeY()/2-Viewer.y)+
SQR(rC.getZ()+rC.getSizeZ()/2-Viewer.z);
float d05 = sqrtf(d);
float d025 = sqrtf(d05);
float d075 = d05*d025;
_ExtrapolatedPriorities[i] = 1.0f / d075;
sumPrior += _ExtrapolatedPriorities[i];
}
sint32 sumJeton = 0;
for (i = 0; i < MAX_CLOUDS; ++i)
if (_ShouldProcessCloud[i])
{
// Normalize priorities
float factor = ((float)QUEUE_SIZE) / sumPrior;
sint32 nbJeton = (sint32)(0.5f+(factor * _ExtrapolatedPriorities[i]));
if (nbJeton < 1)
nbJeton = 1;
_ExtrapolatedPriorities[i] = (float)nbJeton;
sumJeton += nbJeton;
}
if (sumJeton > QUEUE_SIZE)
{
do
{
for (i = 0; i < MAX_CLOUDS; ++i)
if (_ShouldProcessCloud[i])
{
if (_ExtrapolatedPriorities[i] > 1)
{
_ExtrapolatedPriorities[i] -= 1;
--sumJeton;
if (sumJeton == QUEUE_SIZE) break;
}
}
}
while (sumJeton > QUEUE_SIZE);
}
for (i = 0; i < MAX_CLOUDS; ++i)
if (_ShouldProcessCloud[i])
{
// Cloud Period
sint32 newPriority = nPeriodeMax;
// Is there a last entry in array ?
if (_CloudSchedulerLastAdded[i].ValidPos == true)
{
SCloudSchedulerEntry &rLastCSE = *_CloudSchedulerLastAdded[i].Pos;
newPriority = (sint32)(QUEUE_SIZE/_ExtrapolatedPriorities[i]);
newPriority = (_FrameCounter - rLastCSE.Frame) - newPriority;
}
else
{
newPriority = 10000;
}
if (newPriority > Priority)
{
Priority = newPriority;
CloudIndexToAdd = i;
}
}
nlassert (CloudIndexToAdd != -1);
}
// Ok now we have a good cloud index to add so make the new cloud entry
SCloudSchedulerEntry newCSE;
newCSE.CloudIndex = CloudIndexToAdd;
newCSE.Frame = _FrameCounter;
newCSE.Ambient = _CurrentCSS.Ambient;
newCSE.Diffuse = _CurrentCSS.Diffuse;
if (CloudIndexToAdd != -1)
{
newCSE.Power = _CloudPower[CloudIndexToAdd];
// If the cloud where added previously to the list
if (_CloudSchedulerLastAdded[CloudIndexToAdd].ValidPos == true)
{
// This means that the cloud were added from a long time ago
SCloudSchedulerEntry &lastCSE = *_CloudSchedulerLastAdded[CloudIndexToAdd].Pos;
sint32 delta = _FrameCounter - lastCSE.Frame;
lastCSE.DeltaNextCalc = delta;
// But the cloud can be removed (if so we have to not process it anymore)
if (newCSE.Power == 0)
_ShouldProcessCloud[CloudIndexToAdd] = false;
}
else
{
// No the cloud do not appear previously in the list... So its a new one
_AllClouds[CloudIndexToAdd].reset (_ViewerCam);
}
// If the last cloud occurence of the cloud appear at beginning so no more occurence in list
if (_CloudSchedulerLastAdded[FrontCSE.CloudIndex].Pos == _CloudScheduler.begin())
_CloudSchedulerLastAdded[FrontCSE.CloudIndex].ValidPos = false;
_CloudSchedulerLastAdded[CloudIndexToAdd].Pos = _CloudScheduler.insert(_CloudScheduler.end(), newCSE);
_CloudSchedulerLastAdded[CloudIndexToAdd].ValidPos = true;
//_CloudSchedulerLastAdded[CloudIndexToAdd].Pos = _CloudScheduler.end()-1;
}
else
{
_CloudScheduler.insert(_CloudScheduler.end(), newCSE);
}
_CloudScheduler.pop_front ();
++_FrameCounter;
// End of scheduling
// Get the cloud to process (this must be the next occurence of front cloud)
std::list::iterator it = _CloudScheduler.begin();
while (it != _CloudScheduler.end())
{
SCloudSchedulerEntry &rCSE = *it;
if (rCSE.CloudIndex == FrontCSE.CloudIndex)
break;
++it;
}
SCloudSchedulerEntry CSEToCalc;
// The cloud is no more present in the list
if (it == _CloudScheduler.end())
{
FrontCSE.DeltaNextCalc = 1;
CSEToCalc = FrontCSE;
}
else
{
CSEToCalc = *it;
}
// Is the cloud to calc is a real cloud
if (CSEToCalc.CloudIndex == -1)
{
_CurrentCloudInProcess = NULL;
}
else
{
_CurrentCloudInProcess = &_AllClouds[CSEToCalc.CloudIndex];
CCloud &c = *_CurrentCloudInProcess;
// To go from Front cloud to CSEToCalc cloud we should take the front DeltaNextCalc
_CurrentCloudInProcessFuturTime = ((0.04/_NbHalfCloudToUpdate) * FrontCSE.DeltaNextCalc * 2);
c.setX ((float)(c.getLastX() + _CurrentCloudInProcessFuturTime * _CurrentCSS.WindSpeed));
float d2D = sqrtf(SQR(c.getX()+c.getSizeX()/2-Viewer.x)+SQR(c.getY()+c.getSizeY()/2-Viewer.y));
if (d2D > MAX_DIST)
c.CloudDistAtt = 255;
else if (d2D > (MAX_DIST-100.0f))
c.CloudDistAtt = (uint8)(255*((d2D-(MAX_DIST-100.0f))/100.0f));
else
c.CloudDistAtt = 0;
c.LastCloudPower = c.CloudPower;
c.CloudPower = CSEToCalc.Power;
c.CloudDiffuse = CSEToCalc.Diffuse;
c.CloudAmbient = CSEToCalc.Ambient;
c.anim (_CurrentCloudInProcessFuturTime*_CurrentCSS.CloudSpeed,
_CurrentCloudInProcessFuturTime*_CurrentCSS.WindSpeed);
c.generate (_Noise3D);
}
}
else
{
if (_CurrentCloudInProcess != NULL)
{
CCloud &c = *_CurrentCloudInProcess;
c.Time = 0;
c.FuturTime = _CurrentCloudInProcessFuturTime;
c.light();
if (c.getX() > MAX_DIST)
{
c.setX (c.getX() - (2 * MAX_DIST));
c.setLooping ();
}
float r = sqrtf(SQR(c.getSizeX()/2)+SQR(c.getSizeY()/2)+SQR(c.getSizeZ()/2));
float d = sqrtf(SQR(c.getX()+c.getSizeX()/2-Viewer.x)+SQR(c.getY()+c.getSizeY()/2-Viewer.y)+
SQR(c.getZ()+c.getSizeZ()/2-Viewer.z));
uint32 lookAtSize = (uint32)(_LODQualityThreshold*r/d);
lookAtSize = raiseToNextPowerOf2 (lookAtSize);
if (lookAtSize > 128) lookAtSize = 128;
c.genBill (_ViewerCam, lookAtSize);
}
}
_Generate = !_Generate;
}
// ------------------------------------------------------------------------------------------------
void CCloudScape::render ()
{
uint32 i, j;
CVector Viewer = CVector (0,0,0);
// Disable fog
bool fog = _Driver->fogEnabled();
_Driver->enableFog (false);
CMatrix viewMat;
viewMat = _ViewerCam->getMatrix ();
viewMat.setPos(CVector(0,0,0));
viewMat.invert ();
CScissor s;
s.initFullScreen ();
_Driver->setupScissor (s);
CViewport v;
_Driver->setupViewport (v);
CFrustum f = _ViewerCam->getFrustum();
_Driver->setFrustum (f.Left, f.Right, f.Bottom, f.Top, f.Near, f.Far, f.Perspective);
_Driver->setupViewMatrix (viewMat);
_Driver->setupModelMatrix (CMatrix::Identity);
uint32 nNbCloudToRender = 0;
for (i = 0; i < MAX_CLOUDS; ++i)
{
CCloud &c = _AllClouds[i];
SSortedCloudEntry &sce = _SortedClouds[nNbCloudToRender];
sce.Cloud = &c;
sce.Distance = sqrtf(SQR(c.getX()+c.getSizeX()/2-Viewer.x)+SQR(c.getY()+c.getSizeY()/2-Viewer.y)+
SQR(c.getZ()+c.getSizeZ()/2-Viewer.z));
nNbCloudToRender++;
}
for (i = 0; i < nNbCloudToRender-1; ++i)
for (j = i+1; j < nNbCloudToRender; ++j)
{
if (_SortedClouds[i].Distance < _SortedClouds[j].Distance)
{
SSortedCloudEntry sceTmp = _SortedClouds[i];
_SortedClouds[i] = _SortedClouds[j];
_SortedClouds[j] = sceTmp;
}
}
for (i = 0; i < nNbCloudToRender; ++i)
{
CCloud *pC = _SortedClouds[i].Cloud;
if ((pC->CloudPower > 0) || (pC->LastCloudPower > 0))
pC->dispBill (_ViewerCam);
}
// Backup fog
_Driver->enableFog (fog);
}
// ------------------------------------------------------------------------------------------------
uint32 CCloudScape::getMemSize()
{
uint32 nMemSize = 0;
for (uint32 i = 0; i < MAX_CLOUDS; ++i)
{
CCloud &c = _AllClouds[i];
nMemSize += c.getMemSize();
}
return nMemSize;
}
} // namespace NL3D