Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / 3d / ps_fan_light.cpp
bloba2a0dcf0bf63c5c1398d841a7936ee8268ebcb74
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "std3d.h"
19 #include "nel/3d/ps_fan_light.h"
20 #include "nel/3d/ps_macro.h"
21 #include "nel/3d/ps_attrib_maker.h"
22 #include "nel/3d/ps_iterator.h"
23 #include "nel/3d/particle_system.h"
24 #include "nel/3d/driver.h"
25 #include "nel/3d/debug_vb.h"
28 #ifdef DEBUG_NEW
29 #define new DEBUG_NEW
30 #endif
32 namespace NL3D
37 //////////////////////////////
38 // fan light implementation //
39 //////////////////////////////
42 uint8 CPSFanLight::_RandomPhaseTab[32][128];
43 bool CPSFanLight::_RandomPhaseTabInitialized = false;
45 CPSFanLight::TVBMap CPSFanLight::_VBMap; // fanlight, no texture
46 CPSFanLight::TVBMap CPSFanLight::_TexVBMap; // fanlight, textured
47 CPSFanLight::TVBMap CPSFanLight::_ColoredVBMap; // fanlight, no texture, varying color
48 CPSFanLight::TVBMap CPSFanLight::_ColoredTexVBMap; // fanlight, textured, varying color
49 CPSFanLight::TIBMap CPSFanLight::_IBMap;
52 static const uint FanLightBufSize = 128; // the size of a buffer of particle to deal with at a time
53 static const uint NumVertsInBuffer = 4 * FanLightBufSize;
59 ///====================================================================================
61 /** Well, we could have put a method template in CPSFanLight, but some compilers
62 * want the definition of the methods in the header, and some compilers
63 * don't want friend with function template, so we use a static method template of a friend class instead,
64 * which gives us the same result :)
66 class CPSFanLightHelper
68 public:
69 template <class T, class U>
70 static void drawFanLight(T posIt, U timeIt, CPSFanLight &f, uint size, uint32 srcStep)
72 NL_PS_FUNC(CPSFanLightHelper_drawFanLight)
73 PARTICLES_CHECK_MEM;
74 nlassert(f._RandomPhaseTabInitialized);
76 f.setupDriverModelMatrix();
77 const CVector I = f.computeI();
78 const CVector K = f.computeK();
80 CVertexBuffer *vb;
81 CIndexBuffer *ib;
82 // get (and build if necessary) the vb and the ib
83 f.getVBnIB(vb, ib);
84 // tmp
85 vb->setPreferredMemory(CVertexBuffer::AGPVolatile, true);
86 IDriver *driver = f.getDriver();
87 const uint maxNumFanLightToDealWith = std::min(FanLightBufSize, f.getNumFanlightsInVB());
88 uint8 *randomPhaseTab = &f._RandomPhaseTab[f._PhaseSmoothness][0];
89 f._Owner->incrementNbDrawnParticles(size); // for benchmark purpose
90 float pSizes[FanLightBufSize];
91 float pAngles[FanLightBufSize];
92 T endPosIt;
94 sint32 k; // helps to count the fans
97 // if so, we need to deal process separatly group of particles
98 const uint32 stride = vb->getVertexSize();
100 float currentAngle;
101 const float angleStep = 256.0f / f._NbFans;
104 float *currentSizePt; // it points either the particle constant size, or a size in a table
105 float *currentAnglePt; // it points either the particle constant angle, or an angle in a table
108 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
109 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
112 uint leftToDo = size;
113 if (f._ColorScheme)
115 // we change the color at each fan light center
116 f._ColorScheme->setColorType(driver->getVertexColorFormat());
120 uint toProcess = std::min(leftToDo, maxNumFanLightToDealWith);
121 vb->setNumVertices(toProcess * f._NbFans * 3);
123 CVertexBufferReadWrite vba;
124 vb->lock (vba);
126 uint8 *ptVect = (uint8 *) vba.getVertexCoordPointer();
127 // compute individual colors if needed
128 if (f._ColorScheme)
130 // we change the color at each fan light center
131 f._ColorScheme->make(f._Owner, size - leftToDo, vba.getColorPointer(), vb->getVertexSize() * (f._NbFans + 2), toProcess, false, srcStep);
133 if (f._SizeScheme)
135 currentSizePt = (float *) (f._SizeScheme->make(f._Owner, size - leftToDo, pSizes, sizeof(float), toProcess, true, srcStep));
136 currentSizePt = pSizes;
138 else
140 currentSizePt = &f._ParticleSize;
142 if (f._Angle2DScheme)
144 currentAnglePt = (float *) (f._Angle2DScheme->make(f._Owner, size - leftToDo, pAngles, sizeof(float), toProcess, true, srcStep));
146 else
148 currentAnglePt = &f._Angle2D;
151 float fSize, firstSize, sizeStepBase=0.0, sizeStep;
152 if (f._PhaseSmoothness)
154 sizeStepBase = 1.f / f._PhaseSmoothness;
156 endPosIt = posIt + toProcess;
157 for (;posIt != endPosIt; ++posIt, ++timeIt)
160 CHECK_VERTEX_BUFFER(*vb, ptVect);
161 *(CVector *) ptVect = *posIt;
162 // the start angle
163 currentAngle = *currentAnglePt;
164 const uint8 phaseAdd = (uint8) (f._PhaseSpeed * (*timeIt));
165 ptVect += stride;
166 const float fanSize = *currentSizePt * 0.5f;
167 const float moveIntensity = f._MoveIntensity * fanSize;
168 // compute radius & vect for first fan
169 firstSize = fanSize + (moveIntensity * CPSUtil::getCos(randomPhaseTab[0] + phaseAdd));
170 *(CVector *) ptVect = (*posIt) + I * firstSize * (CPSUtil::getCos((sint32) currentAngle))
171 + K * firstSize * (CPSUtil::getSin((sint32) currentAngle));
172 currentAngle += angleStep;
173 ptVect += stride;
174 fSize = firstSize;
175 // computes other fans
176 const sint32 upperBound = (sint32) (f._NbFans - f._PhaseSmoothness - 1);
177 for (k = 1; k <= upperBound; ++k)
179 fSize = fanSize + (moveIntensity * CPSUtil::getCos(randomPhaseTab[k] + phaseAdd));
180 *(CVector *) ptVect = (*posIt) + I * fSize * (CPSUtil::getCos((sint32) currentAngle))
181 + K * fSize * (CPSUtil::getSin((sint32) currentAngle));
182 currentAngle += angleStep;
183 ptVect += stride;
186 // interpolate radius, so that the fanlight loops correctly
187 sizeStep = sizeStepBase * (firstSize - fSize);
188 for (; k <= (sint32) (f._NbFans - 1); ++k)
190 *(CVector *) ptVect = (*posIt) + I * fSize * (CPSUtil::getCos((sint32) currentAngle))
191 + K * fSize * (CPSUtil::getSin((sint32) currentAngle));
192 currentAngle += angleStep;
193 ptVect += stride;
194 fSize += sizeStep;
196 // last fan
197 *(CVector *) ptVect = (*posIt) + I * firstSize * (CPSUtil::getCos((sint32) *currentAnglePt))
198 + K * firstSize * (CPSUtil::getSin((sint32) *currentAnglePt));
199 ptVect += stride;
200 currentSizePt += currentSizePtIncrement;
201 currentAnglePt += currentAnglePtIncrement;
204 driver->activeIndexBuffer(*ib);
205 driver->activeVertexBuffer(*vb);
206 driver->renderTriangles(f._Mat, 0, toProcess * f._NbFans);
207 leftToDo -= toProcess;
209 while (leftToDo != 0);
210 PARTICLES_CHECK_MEM;
215 ///====================================================================================
216 // this blur a tab of bytes once
217 static void BlurBytesTab(const uint8 *src, uint8 *dest, uint size)
219 NL_PS_FUNC(BlurBytesTab)
220 std::vector<uint8> b(src, src + size);
221 for (sint k = 1 ; k < (sint) (size - 1); ++k)
223 dest[k] = (uint8) (((uint16) b[k - 1] + (uint16) b[k + 1])>>1);
227 ///====================================================================================
228 void CPSFanLight::initFanLightPrecalc(void)
230 NL_PS_FUNC(CPSFanLight_initFanLightPrecalc)
231 // build several random tab, and linearly interpolate between l values
232 float currPhase, nextPhase, phaseStep;
233 for (uint l = 0; l < 32 ; l++)
235 nextPhase = (float) (uint8) (rand()&0xFF);
236 uint32 k = 0;
237 while (k < 128)
239 currPhase = nextPhase;
240 nextPhase = (float) (uint8) (rand()&0xFF);
241 phaseStep = (nextPhase - currPhase) / (l + 1);
243 for (uint32 m = 0; m <= l; ++m)
245 _RandomPhaseTab[l][k] = (uint8) currPhase;
246 currPhase += phaseStep;
247 ++k;
248 if (k >= 128) break;
251 for (uint m = 0; m < 2 * l; ++m)
252 BlurBytesTab(&_RandomPhaseTab[l][0], &_RandomPhaseTab[l][0], 128);
254 //#ifdef NL_DEBUG
255 _RandomPhaseTabInitialized = true;
256 //#endif
259 ///====================================================================================
260 uint32 CPSFanLight::getNumWantedTris() const
262 NL_PS_FUNC(CPSFanLight_getNumWantedTris)
263 nlassert(_Owner);
264 //return _Owner->getMaxSize() * _NbFans;
265 return _Owner->getSize() * _NbFans;
268 ///====================================================================================
269 bool CPSFanLight::hasTransparentFaces(void)
271 NL_PS_FUNC(CPSFanLight_hasTransparentFaces)
272 return getBlendingMode() != CPSMaterial::alphaTest ;
275 ///====================================================================================
276 bool CPSFanLight::hasOpaqueFaces(void)
278 NL_PS_FUNC(CPSFanLight_hasOpaqueFaces)
279 return !hasTransparentFaces();
282 ///====================================================================================
283 void CPSFanLight::newElement(const CPSEmitterInfo &info)
285 NL_PS_FUNC(CPSFanLight_newElement)
286 newColorElement(info);
287 newSizeElement(info);
288 newAngle2DElement(info);
291 ///====================================================================================
292 void CPSFanLight::deleteElement(uint32 index)
294 NL_PS_FUNC(CPSFanLight_deleteElement)
295 deleteColorElement(index);
296 deleteSizeElement(index);
297 deleteAngle2DElement(index);
300 ///====================================================================================
301 void CPSFanLight::setPhaseSpeed(float multiplier)
303 NL_PS_FUNC(CPSFanLight_setPhaseSpeed)
304 _PhaseSpeed = 256.0f * multiplier;
307 ///====================================================================================
308 inline void CPSFanLight::setupMaterial()
310 NL_PS_FUNC(CPSFanLight_setupMaterial)
311 CParticleSystem &ps = *(_Owner->getOwner());
312 /// update material color
313 if (_Tex == NULL)
315 forceTexturedMaterialStages(1);
316 SetupModulatedStage(_Mat, 0, CMaterial::Diffuse, CMaterial::Constant);
318 else
320 _Mat.setTexture(0, _Tex);
321 forceTexturedMaterialStages(2);
322 SetupModulatedStage(_Mat, 0, CMaterial::Texture, CMaterial::Constant);
323 SetupModulatedStage(_Mat, 1, CMaterial::Diffuse, CMaterial::Previous);
326 // always setup global colors
327 if (_ColorScheme)
329 if (ps.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
331 _Mat.texConstantColor(0, ps.getGlobalColorLighted());
333 else
335 _Mat.texConstantColor(0, ps.getGlobalColor());
338 else
340 NLMISC::CRGBA col;
341 if (ps.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
343 col.modulateFromColor(ps.getGlobalColorLighted(), _Color);
345 else if (ps.getColorAttenuationScheme() != NULL || ps.isUserColorUsed())
347 col.modulateFromColor(ps.getGlobalColor(), _Color);
349 else
351 col = _Color;
353 _Mat.texConstantColor(0, col);
358 ///====================================================================================
359 void CPSFanLight::draw(bool opaque)
361 // if (!FilterPS[3]) return;
362 NL_PS_FUNC(CPSFanLight_draw)
363 PARTICLES_CHECK_MEM;
364 if (!_Owner->getSize()) return;
366 uint32 step;
367 uint numToProcess;
368 computeSrcStep(step, numToProcess);
369 if (!numToProcess) return;
371 setupMaterial();
373 if (step == (1 << 16))
375 CPSFanLightHelper::drawFanLight(_Owner->getPos().begin(),
376 _Owner->getTime().begin(),
377 *this,
378 numToProcess,
379 step
382 else
384 CPSFanLightHelper::drawFanLight(TIteratorVectStep1616(_Owner->getPos().begin(), 0, step),
385 TIteratorTimeStep1616(_Owner->getTime().begin(), 0, step),
386 *this,
387 numToProcess,
388 step
392 PARTICLES_CHECK_MEM;
395 ///====================================================================================
396 void CPSFanLight::serial(NLMISC::IStream &f)
398 NL_PS_FUNC(CPSFanLight_serial)
399 sint ver = f.serialVersion(2);
400 CPSParticle::serial(f);
401 CPSColoredParticle::serialColorScheme(f);
402 CPSSizedParticle::serialSizeScheme(f);
403 CPSRotated2DParticle::serialAngle2DScheme(f);
404 f.serial(_NbFans);
405 serialMaterial(f);
406 if (ver > 1)
408 f.serial(_PhaseSmoothness, _MoveIntensity);
409 ITexture *tex = _Tex;
410 f.serialPolyPtr(tex);
411 if (f.isReading()) _Tex = tex ;
413 if (f.isReading())
415 init();
419 ///====================================================================================
420 bool CPSFanLight::completeBBox(NLMISC::CAABBox &box) const
422 NL_PS_FUNC(CPSFanLight_completeBBox)
423 // TODO
425 return false;
428 ///====================================================================================
429 CPSFanLight::CPSFanLight(uint32 nbFans) : _NbFans(nbFans),
430 _PhaseSmoothness(0),
431 _MoveIntensity(1.5f),
432 _Tex(NULL),
433 _PhaseSpeed(256)
435 NL_PS_FUNC(CPSFanLight_CPSFanLight)
436 nlassert(nbFans >= 3);
439 init();
440 if (CParticleSystem::getSerializeIdentifierFlag()) _Name = std::string("FanLight");
444 ///====================================================================================
445 CPSFanLight::~CPSFanLight()
447 NL_PS_FUNC(CPSFanLight_CPSFanLight)
451 ///====================================================================================
452 void CPSFanLight::setNbFans(uint32 nbFans)
454 NL_PS_FUNC(CPSFanLight_setNbFans)
455 _NbFans = nbFans;
456 resize(_Owner->getMaxSize());
457 //notifyOwnerMaxNumFacesChanged();
460 ///====================================================================================
461 void CPSFanLight::resize(uint32 size)
463 NL_PS_FUNC(CPSFanLight_resize)
464 nlassert(size < (1 << 16));
465 resizeColor(size);
466 resizeAngle2D(size);
467 resizeSize(size);
471 ///====================================================================================
472 void CPSFanLight::init(void)
474 NL_PS_FUNC(CPSFanLight_init)
475 _Mat.setLighting(false);
476 _Mat.setZFunc(CMaterial::less);
477 _Mat.setDoubleSided(true);
478 _Mat.setColor(NLMISC::CRGBA::White);
480 updateMatAndVbForColor();
483 ///====================================================================================
484 void CPSFanLight::updateMatAndVbForColor(void)
486 NL_PS_FUNC(CPSFanLight_updateMatAndVbForColor)
487 //touch();
490 ///====================================================================================
491 void CPSFanLight::getVBnIB(CVertexBuffer *&retVb, CIndexBuffer *&retIb)
493 NL_PS_FUNC(CPSFanLight_getVBnIB)
494 TVBMap &vbMap = _ColorScheme ? (_Tex == NULL ? _ColoredVBMap : _ColoredTexVBMap)
495 : (_Tex == NULL ? _VBMap : _TexVBMap);
496 #ifdef NL_NAMED_INDEX_BUFFER
497 const char *ibName = _ColorScheme ? (_Tex == NULL ? "_ColoredVBMap" : "_ColoredTexVBMap")
498 : (_Tex == NULL ? "_VBMap" : "_TexVBMap");
499 #endif
500 TVBMap::iterator vbIt = vbMap.find(_NbFans);
501 if (vbIt != vbMap.end())
503 retVb = &(vbIt->second);
504 TIBMap::iterator pbIt = _IBMap.find(_NbFans);
505 nlassert(pbIt != _IBMap.end());
506 #ifdef NL_NAMED_INDEX_BUFFER
507 if (pbIt->second.getName().empty()) NL_SET_IB_NAME(pbIt->second, ibName);
508 #endif
509 retIb = &(pbIt->second);
511 else // we need to create the vb
513 // create an entry (we setup the primitive block at the same time, this could be avoided, but doesn't make much difference)
514 CVertexBuffer &vb = vbMap[_NbFans]; // create a vb
515 CIndexBuffer &ib = _IBMap[_NbFans]; // eventually create a pb
516 const uint32 size = getNumFanlightsInVB();
517 vb.setVertexFormat(CVertexBuffer::PositionFlag |
518 CVertexBuffer::PrimaryColorFlag |
519 (_Tex != NULL ? CVertexBuffer::TexCoord0Flag : 0)
521 vb.setNumVertices(size * (2 + _NbFans));
522 vb.setPreferredMemory(CVertexBuffer::AGPVolatile, true); // keep local memory because of interleaved format
523 vb.setName("CPSFanLight");
524 ib.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT);
525 ib.setNumIndexes(size * _NbFans * 3);
526 // pointer on the current index to fill
527 CIndexBufferReadWrite iba;
528 ib.lock (iba);
529 TIndexType *ptIndex = (TIndexType *) iba.getPtr();
531 CVertexBufferReadWrite vba;
532 vb.lock (vba);
534 // index of the first vertex of the current fanFilght
535 uint currVertFan = 0;
537 uint l; // the current fan in the current fanlight
538 uint k; // the current fan light
540 for (k = 0; k < size; ++k)
542 for (l = 0; l < _NbFans; ++l)
544 *ptIndex++ = (TIndexType) currVertFan;
545 *ptIndex++ = (TIndexType) (currVertFan + (l + 1));
546 *ptIndex++ = (TIndexType) (currVertFan + (l + 2));
548 currVertFan += 2 + _NbFans;
551 for (k = 0; k < size; ++k)
553 if (_Tex)
555 vba.setTexCoord(k * (_NbFans + 2), 0, NLMISC::CUV(0, 0));
557 if (!_ColorScheme)
559 vba.setColor(k * (_NbFans + 2), CRGBA::White);
561 if (!_Tex)
563 for(l = 1; l <= _NbFans + 1; ++l)
565 vba.setColor(l + k * (_NbFans + 2), CRGBA(0, 0, 0));
568 else
570 for(l = 1; l <= _NbFans + 1; ++l)
572 vba.setColor(l + k * (_NbFans + 2), CRGBA(0, 0, 0));
573 vba.setTexCoord(l + k * (_NbFans + 2), 0, NLMISC::CUV((l - 1) / (float) _NbFans, 1));
578 retVb = &vb;
579 retIb = &ib;
583 ///====================================================================================
584 uint CPSFanLight::getNumFanlightsInVB() const
586 NL_PS_FUNC(CPSFanLight_getNumFanlightsInVB)
587 const uint numRib = NumVertsInBuffer / (2 + _NbFans);
588 return std::max(1u, numRib);
591 ///====================================================================================
592 void CPSFanLight::enumTexs(std::vector<NLMISC::CSmartPtr<ITexture> > &dest, IDriver &drv)
594 NL_PS_FUNC(CPSFanLight_enumTexs)
595 if (_Tex)
597 dest.push_back(_Tex);
601 } // NL3D