khanat-opennel-code/code/nel/src/3d/ps_shockwave.cpp

574 lines
17 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_macro.h"
#include "nel/3d/ps_shockwave.h"
#include "nel/3d/driver.h"
#include "nel/3d/texture_grouped.h"
#include "nel/3d/ps_iterator.h"
#include "nel/3d/particle_system.h"
namespace NL3D
{
///////////////////////////
// constant definition //
///////////////////////////
// max number of shockwave to be processed at once
static const uint ShockWaveBufSize = 128;
// the number of vertices we want in a vertex buffer
static const uint NumVertsInBuffer = 8 * ShockWaveBufSize;
CPSShockWave::TPBMap CPSShockWave::_PBMap; // the primitive blocks
CPSShockWave::TVBMap CPSShockWave::_VBMap; // vb ith unanimated texture
CPSShockWave::TVBMap CPSShockWave::_AnimTexVBMap; // vb ith unanimated texture
CPSShockWave::TVBMap CPSShockWave::_ColoredVBMap; // vb ith unanimated texture
CPSShockWave::TVBMap CPSShockWave::_ColoredAnimTexVBMap; // vb ith unanimated texture
/////////////////////////////////
// CPSShockWave implementation //
/////////////////////////////////
/** Well, we could have put a method template in CPSShockWave, 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 CPSShockWaveHelper
{
public:
template <class T>
static void drawShockWave(T posIt, CPSShockWave &s, uint size, uint32 srcStep)
{
NL_PS_FUNC(drawShockWave_drawShockWave)
PARTICLES_CHECK_MEM;
nlassert(s._Owner);
// get / build the vertex buffer and the primitive block
CVertexBuffer *vb;
CIndexBuffer *pb;
s.getVBnPB(vb, pb);
const uint32 vSize = vb->getVertexSize();
IDriver *driver = s.getDriver();
if (s._ColorScheme)
{
s._ColorScheme->setColorType(driver->getVertexColorFormat());
}
s._Owner->incrementNbDrawnParticles(size); // for benchmark purpose
s.setupDriverModelMatrix();
const uint numShockWaveToDealWith = std::min(ShockWaveBufSize, s.getNumShockWavesInVB());
static CPlaneBasis planeBasis[ShockWaveBufSize];
float sizes[ShockWaveBufSize];
float angles[ShockWaveBufSize];
uint leftToDo = size, toProcess;
T endIt;
uint8 *currVertex;
uint k ;
const float angleStep = 256.f / s._NbSeg;
float currAngle;
CPlaneBasis *ptCurrBasis;
uint32 ptCurrBasisIncrement = s._PlaneBasisScheme ? 1 : 0;
float *ptCurrSize;
uint32 ptCurrSizeIncrement = s._SizeScheme ? 1 : 0;
float *ptCurrAngle;
uint32 ptCurrAngleIncrement = s._Angle2DScheme ? 1 : 0;
CVector radVect, innerVect;
float radiusRatio;
do
{
toProcess = leftToDo > numShockWaveToDealWith ? numShockWaveToDealWith : leftToDo;
vb->setNumVertices((toProcess * (s._NbSeg + 1)) << 1);
{
CVertexBufferReadWrite vba;
vb->lock (vba);
currVertex = (uint8 *) vba.getVertexCoordPointer();
endIt = posIt + toProcess;
if (s._SizeScheme)
{
ptCurrSize = (float *) (s._SizeScheme->make(s._Owner, size - leftToDo, (void *) sizes, sizeof(float), toProcess, true, srcStep));
}
else
{
ptCurrSize = &s._ParticleSize;
}
if (s._PlaneBasisScheme)
{
ptCurrBasis = (CPlaneBasis *) (s._PlaneBasisScheme->make(s._Owner, size - leftToDo, (void *) planeBasis, sizeof(CPlaneBasis), toProcess, true, srcStep));
}
else
{
ptCurrBasis = &s._PlaneBasis;
}
if (s._Angle2DScheme)
{
ptCurrAngle = (float *) (s._Angle2DScheme->make(s._Owner, size - leftToDo, (void *) angles, sizeof(float), toProcess, true, srcStep));
}
else
{
ptCurrAngle = &s._Angle2D;
}
s.updateVbColNUVForRender(size - leftToDo, toProcess, srcStep, *vb, *driver);
do
{
currAngle = *ptCurrAngle;
if (fabsf(*ptCurrSize) > 10E-6)
{
radiusRatio = (*ptCurrSize - s._RadiusCut) / *ptCurrSize;
}
else
{
radiusRatio = 0.f;
}
for (k = 0; k <= s._NbSeg; ++k)
{
radVect = *ptCurrSize * (CPSUtil::getCos((sint32) currAngle) * ptCurrBasis->X + CPSUtil::getSin((sint32) currAngle) * ptCurrBasis->Y);
innerVect = radiusRatio * radVect;
CHECK_VERTEX_BUFFER(*vb, currVertex);
* (CVector *) currVertex = *posIt + radVect;
currVertex += vSize;
CHECK_VERTEX_BUFFER(*vb, currVertex);
* (CVector *) currVertex = *posIt + innerVect;
currVertex += vSize;
currAngle += angleStep;
}
++posIt;
ptCurrBasis += ptCurrBasisIncrement;
ptCurrSize += ptCurrSizeIncrement;
ptCurrAngle += ptCurrAngleIncrement;
}
while (posIt != endIt);
}
const uint numTri = 2 * toProcess * s._NbSeg;
pb->setNumIndexes(3 * numTri);
driver->activeIndexBuffer(*pb);
driver->activeVertexBuffer(*vb);
driver->renderTriangles(s._Mat, 0, numTri);
leftToDo -= toProcess;
}
while (leftToDo);
PARTICLES_CHECK_MEM;
}
};
///=================================================================================
CPSShockWave::CPSShockWave(uint nbSeg, float radiusCut, CSmartPtr<ITexture> tex)
: _NbSeg(nbSeg)
, _RadiusCut(radiusCut)
, _UFactor(1.f)
{
NL_PS_FUNC(CPSShockWave_CPSShockWave)
nlassert(nbSeg > 2 && nbSeg <= 64);
setTexture(tex);
init();
if (CParticleSystem::getSerializeIdentifierFlag()) _Name = std::string("ShockWave");
}
///=================================================================================
uint32 CPSShockWave::getNumWantedTris() const
{
NL_PS_FUNC(CPSShockWave_getNumWantedTris)
nlassert(_Owner);
//return (_Owner->getMaxSize() * _NbSeg) << 1 ;
return (_Owner->getSize() * _NbSeg) << 1 ;
}
///=================================================================================
bool CPSShockWave::hasTransparentFaces(void)
{
NL_PS_FUNC(CPSShockWave_hasTransparentFaces)
return getBlendingMode() != CPSMaterial::alphaTest ;
}
///=================================================================================
bool CPSShockWave::hasOpaqueFaces(void)
{
NL_PS_FUNC(CPSShockWave_hasOpaqueFaces)
return !hasTransparentFaces();
}
///=================================================================================
void CPSShockWave::setNbSegs(uint nbSeg)
{
NL_PS_FUNC(CPSShockWave_setNbSegs)
nlassert(nbSeg > 2 && nbSeg <= 64);
_NbSeg = nbSeg;
if (_Owner)
{
resize(_Owner->getMaxSize());
//notifyOwnerMaxNumFacesChanged();
}
}
///=================================================================================
void CPSShockWave::setRadiusCut(float radiusCut)
{
NL_PS_FUNC(CPSShockWave_setRadiusCut)
_RadiusCut = radiusCut;
if (_Owner)
{
resize(_Owner->getMaxSize());
}
}
///=================================================================================
void CPSShockWave::setUFactor(float value)
{
NL_PS_FUNC(CPSShockWave_setUFactor)
nlassert(_Owner); // must be attached to an owner before to call this method
_UFactor = value;
resize(_Owner->getSize()); // resize also recomputes the UVs..
}
///=================================================================================
void CPSShockWave::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSShockWave_serial)
sint ver = f.serialVersion(2);
CPSParticle::serial(f);
CPSColoredParticle::serialColorScheme(f);
CPSSizedParticle::serialSizeScheme(f);
CPSTexturedParticle::serialTextureScheme(f);
CPSRotated3DPlaneParticle::serialPlaneBasisScheme(f);
CPSRotated2DParticle::serialAngle2DScheme(f);
serialMaterial(f);
f.serial(_NbSeg, _RadiusCut);
if (ver > 1)
{
f.serial(_UFactor);
}
init();
}
///=================================================================================
inline void CPSShockWave::setupUFactor()
{
NL_PS_FUNC(CPSShockWave_setupUFactor)
if (_UFactor != 1.f)
{
_Mat.enableUserTexMat(0);
CMatrix texMat;
texMat.setRot(_UFactor * NLMISC::CVector::I,
NLMISC::CVector::J,
NLMISC::CVector::K
);
_Mat.setUserTexMat(0, texMat);
}
else
{
_Mat.enableUserTexMat(0, false);
}
}
///=================================================================================
void CPSShockWave::draw(bool opaque)
{
// if (!FilterPS[7]) return;
NL_PS_FUNC(CPSShockWave_draw)
PARTICLES_CHECK_MEM;
if (!_Owner->getSize()) return;
uint32 step;
uint numToProcess;
computeSrcStep(step, numToProcess);
if (!numToProcess) return;
/// update the material if the global color of the system is variable
CParticleSystem &ps = *(_Owner->getOwner());
/// update the material if the global color of the system is variable
if (_ColorScheme != NULL &&
(ps.getColorAttenuationScheme() != NULL ||
ps.isUserColorUsed() ||
ps.getForceGlobalColorLightingFlag() ||
usesGlobalColorLighting()
)
)
{
if (ps.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
{
CPSMaterial::forceModulateConstantColor(true, ps.getGlobalColorLighted());
}
else
{
CPSMaterial::forceModulateConstantColor(true, ps.getGlobalColor());
}
}
else
{
forceModulateConstantColor(false);
if (ps.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
{
NLMISC::CRGBA col;
col.modulateFromColor(ps.getGlobalColorLighted(), _Color);
_Mat.setColor(col);
}
else
if (!ps.getColorAttenuationScheme() || ps.isUserColorUsed())
{
_Mat.setColor(_Color);
}
else
{
NLMISC::CRGBA col;
col.modulateFromColor(ps.getGlobalColor(), _Color);
_Mat.setColor(col);
}
}
//////
setupUFactor();
if (step == (1 << 16))
{
CPSShockWaveHelper::drawShockWave(_Owner->getPos().begin(),
*this,
numToProcess,
step
);
}
else
{
CPSShockWaveHelper::drawShockWave(TIteratorVectStep1616(_Owner->getPos().begin(), 0, step),
*this,
numToProcess,
step
);
}
PARTICLES_CHECK_MEM;
}
///=================================================================================
bool CPSShockWave::completeBBox(NLMISC::CAABBox &box) const
{
NL_PS_FUNC(CPSShockWave_completeBBox)
// TODO : implement this
return false;
}
///=================================================================================
void CPSShockWave::init(void)
{
NL_PS_FUNC(CPSShockWave_init)
_Mat.setLighting(false);
_Mat.setZFunc(CMaterial::less);
_Mat.setDoubleSided(true);
updateMatAndVbForColor();
updateMatAndVbForTexture();
}
///=================================================================================
void CPSShockWave::updateVbColNUVForRender(uint32 startIndex, uint32 size, uint32 srcStep, CVertexBuffer &vb, IDriver &drv)
{
NL_PS_FUNC(CPSShockWave_updateVbColNUVForRender)
nlassert(_Owner);
CVertexBufferReadWrite vba;
vb.lock (vba);
if (!size) return;
if (_ColorScheme)
{
// compute the colors, each color is replicated n times...
_ColorScheme->makeN(_Owner, startIndex, vba.getColorPointer(), vb.getVertexSize(), size, (_NbSeg + 1) << 1, srcStep);
}
if (_TexGroup) // if it has a constant texture we are sure it has been setupped before...
{
sint32 textureIndex[ShockWaveBufSize];
const uint32 stride = vb.getVertexSize(), stride2 = stride << 1;
uint8 *currUV = (uint8 *) vba.getTexCoordPointer();
uint k;
uint32 currIndexIncr;
const sint32 *currIndex;
if (_TextureIndexScheme)
{
currIndex = (sint32 *) (_TextureIndexScheme->make(_Owner, startIndex, textureIndex, sizeof(sint32), size, true, srcStep));
currIndexIncr = 1;
}
else
{
currIndex = &_TextureIndex;
currIndexIncr = 0;
}
while (size--)
{
// for now, we don't make texture index wrapping
const CTextureGrouped::TFourUV &uvGroup = _TexGroup->getUVQuad((uint32) *currIndex);
for (k = 0; k <= _NbSeg; ++k)
{
*(CUV *) currUV = uvGroup.uv0 + CUV(k * _UFactor, 0);
*(CUV *) (currUV + stride) = uvGroup.uv3 + CUV(k * _UFactor, 0);
// point the next quad
currUV += stride2;
}
currIndex += currIndexIncr;
}
}
}
///=================================================================================
void CPSShockWave::updateMatAndVbForColor(void)
{
NL_PS_FUNC(CPSShockWave_updateMatAndVbForColor)
if (_Owner)
{
resize(_Owner->getMaxSize());
}
}
///=================================================================================
void CPSShockWave::updateMatAndVbForTexture(void)
{
NL_PS_FUNC(CPSShockWave_updateMatAndVbForTexture)
_Mat.setTexture(0, _TexGroup ? (ITexture *) _TexGroup : (ITexture *) _Tex);
}
///=================================================================================
void CPSShockWave::newElement(const CPSEmitterInfo &info)
{
NL_PS_FUNC(CPSShockWave_newElement)
newColorElement(info);
newTextureIndexElement(info);
newSizeElement(info);
newAngle2DElement(info);
}
///=================================================================================
void CPSShockWave::deleteElement(uint32 index)
{
NL_PS_FUNC(CPSShockWave_deleteElement)
deleteColorElement(index);
deleteTextureIndexElement(index);
deleteSizeElement(index);
deleteAngle2DElement(index);
}
///=================================================================================
void CPSShockWave::resize(uint32 aSize)
{
NL_PS_FUNC(CPSShockWave_resize)
nlassert(aSize < (1 << 16));
resizeColor(aSize);
resizeTextureIndex(aSize);
resizeSize(aSize);
resizeAngle2D(aSize);
}
///=================================================================================
void CPSShockWave::getVBnPB(CVertexBuffer *&retVb, CIndexBuffer *&retPb)
{
NL_PS_FUNC(CPSShockWave_getVBnPB)
TVBMap &vbMap = _ColorScheme == NULL ? (_TexGroup == NULL ? _VBMap : _AnimTexVBMap)
: (_TexGroup == NULL ? _ColoredVBMap : _ColoredAnimTexVBMap);
TVBMap::iterator vbIt = vbMap.find(_NbSeg);
if (vbIt != vbMap.end())
{
retVb = &(vbIt->second);
TPBMap::iterator pbIt = _PBMap.find(_NbSeg);
nlassert(pbIt != _PBMap.end());
retPb = &(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[_NbSeg]; // create a vb
CIndexBuffer &pb = _PBMap[_NbSeg]; // eventually create a pb
const uint32 size = getNumShockWavesInVB();
vb.setVertexFormat(CVertexBuffer::PositionFlag |
CVertexBuffer::TexCoord0Flag |
(_ColorScheme != NULL ? CVertexBuffer::PrimaryColorFlag : 0)
);
vb.setNumVertices((size * (_NbSeg + 1)) << 1 );
vb.setPreferredMemory(CVertexBuffer::AGPVolatile, true);
CVertexBufferReadWrite vba;
vb.lock (vba);
pb.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT);
pb.setNumIndexes(2 * 3 * size * _NbSeg);
CIndexBufferReadWrite ibaWrite;
pb.lock (ibaWrite);
uint finalIndex = 0;
for (uint32 k = 0; k < size; ++k)
{
for (uint32 l = 0; l < _NbSeg; ++l)
{
const uint32 index = ((k * (_NbSeg + 1)) + l) << 1;
ibaWrite.setTri(finalIndex, index + 1 , index + 3, index + 2);
finalIndex+=3;
ibaWrite.setTri(finalIndex, index + 1, index + 2, index + 0);
finalIndex+=3;
vba.setTexCoord(index, 0, CUV((float) l, 0));
vba.setTexCoord(index + 1, 0, CUV((float) l, 1));
}
const uint32 index = ((k * (_NbSeg + 1)) + _NbSeg) << 1;
vba.setTexCoord(index, 0, CUV((float) _NbSeg, 0));
vba.setTexCoord(index + 1, 0, CUV((float) _NbSeg, 1));
}
retVb = &vb;
retPb = &pb;
vb.setName("CPSShockWave");
NL_SET_IB_NAME(pb, "CPSShockWave");
}
}
///=================================================================================
uint CPSShockWave::getNumShockWavesInVB() const
{
NL_PS_FUNC(CPSShockWave_getNumShockWavesInVB)
const uint numRib = NumVertsInBuffer / ((_NbSeg + 1) << 1);
return std::max(1u, numRib);
}
///=================================================================================
void CPSShockWave::enumTexs(std::vector<NLMISC::CSmartPtr<ITexture> > &dest, IDriver &drv)
{
NL_PS_FUNC(CPSShockWave_enumTexs)
CPSTexturedParticle::enumTexs(dest);
}
} // NL3D