khanat-opennel-code/code/nel/src/3d/ps_fan_light.cpp
2010-05-06 02:08:41 +02:00

597 lines
18 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// 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 <http://www.gnu.org/licenses/>.
#include "std3d.h"
#include "nel/3d/ps_fan_light.h"
#include "nel/3d/ps_macro.h"
#include "nel/3d/ps_attrib_maker.h"
#include "nel/3d/ps_iterator.h"
#include "nel/3d/particle_system.h"
#include "nel/3d/driver.h"
namespace NL3D
{
//////////////////////////////
// fan light implementation //
//////////////////////////////
uint8 CPSFanLight::_RandomPhaseTab[32][128];
bool CPSFanLight::_RandomPhaseTabInitialized = false;
CPSFanLight::TVBMap CPSFanLight::_VBMap; // fanlight, no texture
CPSFanLight::TVBMap CPSFanLight::_TexVBMap; // fanlight, textured
CPSFanLight::TVBMap CPSFanLight::_ColoredVBMap; // fanlight, no texture, varying color
CPSFanLight::TVBMap CPSFanLight::_ColoredTexVBMap; // fanlight, textured, varying color
CPSFanLight::TIBMap CPSFanLight::_IBMap;
static const uint FanLightBufSize = 128; // the size of a buffer of particle to deal with at a time
static const uint NumVertsInBuffer = 4 * FanLightBufSize;
///====================================================================================
/** Well, we could have put a method template in CPSFanLight, but some compilers
* want the definition of the methods in the header, and some compilers
* don't want friend with function template, so we use a static method template of a friend class instead,
* which gives us the same result :)
*/
class CPSFanLightHelper
{
public:
template <class T, class U>
static void drawFanLight(T posIt, U timeIt, CPSFanLight &f, uint size, uint32 srcStep)
{
NL_PS_FUNC(CPSFanLightHelper_drawFanLight)
PARTICLES_CHECK_MEM;
nlassert(f._RandomPhaseTabInitialized);
//
f.setupDriverModelMatrix();
const CVector I = f.computeI();
const CVector K = f.computeK();
//
CVertexBuffer *vb;
CIndexBuffer *ib;
// get (and build if necessary) the vb and the ib
f.getVBnIB(vb, ib);
// tmp
vb->setPreferredMemory(CVertexBuffer::AGPVolatile, true);
IDriver *driver = f.getDriver();
const uint maxNumFanLightToDealWith = std::min(FanLightBufSize, f.getNumFanlightsInVB());
uint8 *randomPhaseTab = &f._RandomPhaseTab[f._PhaseSmoothness][0];
f._Owner->incrementNbDrawnParticles(size); // for benchmark purpose
float pSizes[FanLightBufSize];
float pAngles[FanLightBufSize];
T endPosIt;
sint32 k; // helps to count the fans
// if so, we need to deal process separatly group of particles
const uint32 stride = vb->getVertexSize();
float currentAngle;
const float angleStep = 256.0f / f._NbFans;
float *currentSizePt; // it points either the particle constant size, or a size in a table
float *currentAnglePt; // it points either the particle constant angle, or an angle in a table
const uint32 currentSizePtIncrement = f._SizeScheme ? 1 : 0; // increment to get the next size for the size pointer. It is 0 if the size is constant
const uint32 currentAnglePtIncrement = f._Angle2DScheme ? 1 : 0; // increment to get the next angle for the angle pointer. It is 0 if the size is constant
uint leftToDo = size;
if (f._ColorScheme)
{
// we change the color at each fan light center
f._ColorScheme->setColorType(driver->getVertexColorFormat());
}
do
{
uint toProcess = std::min(leftToDo, maxNumFanLightToDealWith);
vb->setNumVertices(toProcess * f._NbFans * 3);
{
CVertexBufferReadWrite vba;
vb->lock (vba);
uint8 *ptVect = (uint8 *) vba.getVertexCoordPointer();
// compute individual colors if needed
if (f._ColorScheme)
{
// we change the color at each fan light center
f._ColorScheme->make(f._Owner, size - leftToDo, vba.getColorPointer(), vb->getVertexSize() * (f._NbFans + 2), toProcess, false, srcStep);
}
if (f._SizeScheme)
{
currentSizePt = (float *) (f._SizeScheme->make(f._Owner, size - leftToDo, pSizes, sizeof(float), toProcess, true, srcStep));
currentSizePt = pSizes;
}
else
{
currentSizePt = &f._ParticleSize;
}
if (f._Angle2DScheme)
{
currentAnglePt = (float *) (f._Angle2DScheme->make(f._Owner, size - leftToDo, pAngles, sizeof(float), toProcess, true, srcStep));
}
else
{
currentAnglePt = &f._Angle2D;
}
//
float fSize, firstSize, sizeStepBase=0.0, sizeStep;
if (f._PhaseSmoothness)
{
sizeStepBase = 1.f / f._PhaseSmoothness;
}
endPosIt = posIt + toProcess;
for (;posIt != endPosIt; ++posIt, ++timeIt)
{
CHECK_VERTEX_BUFFER(*vb, ptVect);
*(CVector *) ptVect = *posIt;
// the start angle
currentAngle = *currentAnglePt;
const uint8 phaseAdd = (uint8) (f._PhaseSpeed * (*timeIt));
ptVect += stride;
const float fanSize = *currentSizePt * 0.5f;
const float moveIntensity = f._MoveIntensity * fanSize;
// compute radius & vect for first fan
firstSize = fanSize + (moveIntensity * CPSUtil::getCos(randomPhaseTab[0] + phaseAdd));
*(CVector *) ptVect = (*posIt) + I * firstSize * (CPSUtil::getCos((sint32) currentAngle))
+ K * firstSize * (CPSUtil::getSin((sint32) currentAngle));
currentAngle += angleStep;
ptVect += stride;
fSize = firstSize;
// computes other fans
const sint32 upperBound = (sint32) (f._NbFans - f._PhaseSmoothness - 1);
for (k = 1; k <= upperBound; ++k)
{
fSize = fanSize + (moveIntensity * CPSUtil::getCos(randomPhaseTab[k] + phaseAdd));
*(CVector *) ptVect = (*posIt) + I * fSize * (CPSUtil::getCos((sint32) currentAngle))
+ K * fSize * (CPSUtil::getSin((sint32) currentAngle));
currentAngle += angleStep;
ptVect += stride;
}
// interpolate radius, so that the fanlight loops correctly
sizeStep = sizeStepBase * (firstSize - fSize);
for (; k <= (sint32) (f._NbFans - 1); ++k)
{
*(CVector *) ptVect = (*posIt) + I * fSize * (CPSUtil::getCos((sint32) currentAngle))
+ K * fSize * (CPSUtil::getSin((sint32) currentAngle));
currentAngle += angleStep;
ptVect += stride;
fSize += sizeStep;
}
// last fan
*(CVector *) ptVect = (*posIt) + I * firstSize * (CPSUtil::getCos((sint32) *currentAnglePt))
+ K * firstSize * (CPSUtil::getSin((sint32) *currentAnglePt));
ptVect += stride;
currentSizePt += currentSizePtIncrement;
currentAnglePt += currentAnglePtIncrement;
}
}
driver->activeIndexBuffer(*ib);
driver->activeVertexBuffer(*vb);
driver->renderTriangles(f._Mat, 0, toProcess * f._NbFans);
leftToDo -= toProcess;
}
while (leftToDo != 0);
PARTICLES_CHECK_MEM;
}
};
///====================================================================================
// this blur a tab of bytes once
static void BlurBytesTab(const uint8 *src, uint8 *dest, uint size)
{
NL_PS_FUNC(BlurBytesTab)
std::vector<uint8> b(src, src + size);
for (sint k = 1 ; k < (sint) (size - 1); ++k)
{
dest[k] = (uint8) (((uint16) b[k - 1] + (uint16) b[k + 1])>>1);
}
}
///====================================================================================
void CPSFanLight::initFanLightPrecalc(void)
{
NL_PS_FUNC(CPSFanLight_initFanLightPrecalc)
// build several random tab, and linearly interpolate between l values
float currPhase, nextPhase, phaseStep;
for (uint l = 0; l < 32 ; l++)
{
nextPhase = (float) (uint8) (rand()&0xFF);
uint32 k = 0;
while (k < 128)
{
currPhase = nextPhase;
nextPhase = (float) (uint8) (rand()&0xFF);
phaseStep = (nextPhase - currPhase) / (l + 1);
for (uint32 m = 0; m <= l; ++m)
{
_RandomPhaseTab[l][k] = (uint8) currPhase;
currPhase += phaseStep;
++k;
if (k >= 128) break;
}
}
for (uint m = 0; m < 2 * l; ++m)
BlurBytesTab(&_RandomPhaseTab[l][0], &_RandomPhaseTab[l][0], 128);
}
//#ifdef NL_DEBUG
_RandomPhaseTabInitialized = true;
//#endif
}
///====================================================================================
uint32 CPSFanLight::getNumWantedTris() const
{
NL_PS_FUNC(CPSFanLight_getNumWantedTris)
nlassert(_Owner);
//return _Owner->getMaxSize() * _NbFans;
return _Owner->getSize() * _NbFans;
}
///====================================================================================
bool CPSFanLight::hasTransparentFaces(void)
{
NL_PS_FUNC(CPSFanLight_hasTransparentFaces)
return getBlendingMode() != CPSMaterial::alphaTest ;
}
///====================================================================================
bool CPSFanLight::hasOpaqueFaces(void)
{
NL_PS_FUNC(CPSFanLight_hasOpaqueFaces)
return !hasTransparentFaces();
}
///====================================================================================
void CPSFanLight::newElement(const CPSEmitterInfo &info)
{
NL_PS_FUNC(CPSFanLight_newElement)
newColorElement(info);
newSizeElement(info);
newAngle2DElement(info);
}
///====================================================================================
void CPSFanLight::deleteElement(uint32 index)
{
NL_PS_FUNC(CPSFanLight_deleteElement)
deleteColorElement(index);
deleteSizeElement(index);
deleteAngle2DElement(index);
}
///====================================================================================
void CPSFanLight::setPhaseSpeed(float multiplier)
{
NL_PS_FUNC(CPSFanLight_setPhaseSpeed)
_PhaseSpeed = 256.0f * multiplier;
}
///====================================================================================
inline void CPSFanLight::setupMaterial()
{
NL_PS_FUNC(CPSFanLight_setupMaterial)
CParticleSystem &ps = *(_Owner->getOwner());
/// update material color
if (_Tex == NULL)
{
forceTexturedMaterialStages(1);
SetupModulatedStage(_Mat, 0, CMaterial::Diffuse, CMaterial::Constant);
}
else
{
_Mat.setTexture(0, _Tex);
forceTexturedMaterialStages(2);
SetupModulatedStage(_Mat, 0, CMaterial::Texture, CMaterial::Constant);
SetupModulatedStage(_Mat, 1, CMaterial::Diffuse, CMaterial::Previous);
}
// always setup global colors
if (_ColorScheme)
{
if (ps.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
{
_Mat.texConstantColor(0, ps.getGlobalColorLighted());
}
else
{
_Mat.texConstantColor(0, ps.getGlobalColor());
}
}
else
{
NLMISC::CRGBA col;
if (ps.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
{
col.modulateFromColor(ps.getGlobalColorLighted(), _Color);
}
else if (ps.getColorAttenuationScheme() != NULL || ps.isUserColorUsed())
{
col.modulateFromColor(ps.getGlobalColor(), _Color);
}
else
{
col = _Color;
}
_Mat.texConstantColor(0, col);
}
}
///====================================================================================
void CPSFanLight::draw(bool opaque)
{
// if (!FilterPS[3]) return;
NL_PS_FUNC(CPSFanLight_draw)
PARTICLES_CHECK_MEM;
if (!_Owner->getSize()) return;
uint32 step;
uint numToProcess;
computeSrcStep(step, numToProcess);
if (!numToProcess) return;
setupMaterial();
if (step == (1 << 16))
{
CPSFanLightHelper::drawFanLight(_Owner->getPos().begin(),
_Owner->getTime().begin(),
*this,
numToProcess,
step
);
}
else
{
CPSFanLightHelper::drawFanLight(TIteratorVectStep1616(_Owner->getPos().begin(), 0, step),
TIteratorTimeStep1616(_Owner->getTime().begin(), 0, step),
*this,
numToProcess,
step
);
}
PARTICLES_CHECK_MEM;
}
///====================================================================================
void CPSFanLight::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSFanLight_serial)
sint ver = f.serialVersion(2);
CPSParticle::serial(f);
CPSColoredParticle::serialColorScheme(f);
CPSSizedParticle::serialSizeScheme(f);
CPSRotated2DParticle::serialAngle2DScheme(f);
f.serial(_NbFans);
serialMaterial(f);
if (ver > 1)
{
f.serial(_PhaseSmoothness, _MoveIntensity);
ITexture *tex = _Tex;
f.serialPolyPtr(tex);
if (f.isReading()) _Tex = tex ;
}
if (f.isReading())
{
init();
}
}
///====================================================================================
bool CPSFanLight::completeBBox(NLMISC::CAABBox &box) const
{
NL_PS_FUNC(CPSFanLight_completeBBox)
// TODO
return false;
}
///====================================================================================
CPSFanLight::CPSFanLight(uint32 nbFans) : _NbFans(nbFans),
_PhaseSmoothness(0),
_MoveIntensity(1.5f),
_Tex(NULL),
_PhaseSpeed(256)
{
NL_PS_FUNC(CPSFanLight_CPSFanLight)
nlassert(nbFans >= 3);
init();
if (CParticleSystem::getSerializeIdentifierFlag()) _Name = std::string("FanLight");
}
///====================================================================================
CPSFanLight::~CPSFanLight()
{
NL_PS_FUNC(CPSFanLight_CPSFanLight)
}
///====================================================================================
void CPSFanLight::setNbFans(uint32 nbFans)
{
NL_PS_FUNC(CPSFanLight_setNbFans)
_NbFans = nbFans;
resize(_Owner->getMaxSize());
//notifyOwnerMaxNumFacesChanged();
}
///====================================================================================
void CPSFanLight::resize(uint32 size)
{
NL_PS_FUNC(CPSFanLight_resize)
nlassert(size < (1 << 16));
resizeColor(size);
resizeAngle2D(size);
resizeSize(size);
}
///====================================================================================
void CPSFanLight::init(void)
{
NL_PS_FUNC(CPSFanLight_init)
_Mat.setLighting(false);
_Mat.setZFunc(CMaterial::less);
_Mat.setDoubleSided(true);
_Mat.setColor(NLMISC::CRGBA::White);
updateMatAndVbForColor();
}
///====================================================================================
void CPSFanLight::updateMatAndVbForColor(void)
{
NL_PS_FUNC(CPSFanLight_updateMatAndVbForColor)
//touch();
}
///====================================================================================
void CPSFanLight::getVBnIB(CVertexBuffer *&retVb, CIndexBuffer *&retIb)
{
NL_PS_FUNC(CPSFanLight_getVBnIB)
TVBMap &vbMap = _ColorScheme ? (_Tex == NULL ? _ColoredVBMap : _ColoredTexVBMap)
: (_Tex == NULL ? _VBMap : _TexVBMap);
#ifdef NL_NAMED_INDEX_BUFFER
const char *ibName = _ColorScheme ? (_Tex == NULL ? "_ColoredVBMap" : "_ColoredTexVBMap")
: (_Tex == NULL ? "_VBMap" : "_TexVBMap");
#endif
TVBMap::iterator vbIt = vbMap.find(_NbFans);
if (vbIt != vbMap.end())
{
retVb = &(vbIt->second);
TIBMap::iterator pbIt = _IBMap.find(_NbFans);
nlassert(pbIt != _IBMap.end());
#ifdef NL_NAMED_INDEX_BUFFER
if (pbIt->second.getName().empty()) NL_SET_IB_NAME(pbIt->second, ibName);
#endif
retIb = &(pbIt->second);
}
else // we need to create the vb
{
// create an entry (we setup the primitive block at the same time, this could be avoided, but doesn't make much difference)
CVertexBuffer &vb = vbMap[_NbFans]; // create a vb
CIndexBuffer &ib = _IBMap[_NbFans]; // eventually create a pb
const uint32 size = getNumFanlightsInVB();
vb.setVertexFormat(CVertexBuffer::PositionFlag |
CVertexBuffer::PrimaryColorFlag |
(_Tex != NULL ? CVertexBuffer::TexCoord0Flag : 0)
);
vb.setNumVertices(size * (2 + _NbFans));
vb.setPreferredMemory(CVertexBuffer::AGPVolatile, true); // keep local memory because of interleaved format
vb.setName("CPSFanLight");
ib.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT);
ib.setNumIndexes(size * _NbFans * 3);
// pointer on the current index to fill
CIndexBufferReadWrite iba;
ib.lock (iba);
TIndexType *ptIndex = (TIndexType *) iba.getPtr();
CVertexBufferReadWrite vba;
vb.lock (vba);
// index of the first vertex of the current fanFilght
uint currVertFan = 0;
uint l; // the current fan in the current fanlight
uint k; // the current fan light
for (k = 0; k < size; ++k)
{
for (l = 0; l < _NbFans; ++l)
{
*ptIndex++ = (TIndexType) currVertFan;
*ptIndex++ = (TIndexType) (currVertFan + (l + 1));
*ptIndex++ = (TIndexType) (currVertFan + (l + 2));
}
currVertFan += 2 + _NbFans;
}
for (k = 0; k < size; ++k)
{
if (_Tex)
{
vba.setTexCoord(k * (_NbFans + 2), 0, NLMISC::CUV(0, 0));
}
if (!_ColorScheme)
{
vba.setColor(k * (_NbFans + 2), CRGBA::White);
}
if (!_Tex)
{
for(l = 1; l <= _NbFans + 1; ++l)
{
vba.setColor(l + k * (_NbFans + 2), CRGBA(0, 0, 0));
}
}
else
{
for(l = 1; l <= _NbFans + 1; ++l)
{
vba.setColor(l + k * (_NbFans + 2), CRGBA(0, 0, 0));
vba.setTexCoord(l + k * (_NbFans + 2), 0, NLMISC::CUV((l - 1) / (float) _NbFans, 1));
}
}
}
retVb = &vb;
retIb = &ib;
}
}
///====================================================================================
uint CPSFanLight::getNumFanlightsInVB() const
{
NL_PS_FUNC(CPSFanLight_getNumFanlightsInVB)
const uint numRib = NumVertsInBuffer / (2 + _NbFans);
return std::max(1u, numRib);
}
///====================================================================================
void CPSFanLight::enumTexs(std::vector<NLMISC::CSmartPtr<ITexture> > &dest, IDriver &drv)
{
NL_PS_FUNC(CPSFanLight_enumTexs)
if (_Tex)
{
dest.push_back(_Tex);
}
}
} // NL3D