1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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"
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
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
)
74 nlassert(f
._RandomPhaseTabInitialized
);
76 f
.setupDriverModelMatrix();
77 const CVector I
= f
.computeI();
78 const CVector K
= f
.computeK();
82 // get (and build if necessary) the vb and the ib
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
];
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();
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
;
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
;
126 uint8
*ptVect
= (uint8
*) vba
.getVertexCoordPointer();
127 // compute individual colors if needed
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
);
135 currentSizePt
= (float *) (f
._SizeScheme
->make(f
._Owner
, size
- leftToDo
, pSizes
, sizeof(float), toProcess
, true, srcStep
));
136 currentSizePt
= pSizes
;
140 currentSizePt
= &f
._ParticleSize
;
142 if (f
._Angle2DScheme
)
144 currentAnglePt
= (float *) (f
._Angle2DScheme
->make(f
._Owner
, size
- leftToDo
, pAngles
, sizeof(float), toProcess
, true, srcStep
));
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
;
163 currentAngle
= *currentAnglePt
;
164 const uint8 phaseAdd
= (uint8
) (f
._PhaseSpeed
* (*timeIt
));
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
;
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
;
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
;
197 *(CVector
*) ptVect
= (*posIt
) + I
* firstSize
* (CPSUtil::getCos((sint32
) *currentAnglePt
))
198 + K
* firstSize
* (CPSUtil::getSin((sint32
) *currentAnglePt
));
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);
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);
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
;
251 for (uint m
= 0; m
< 2 * l
; ++m
)
252 BlurBytesTab(&_RandomPhaseTab
[l
][0], &_RandomPhaseTab
[l
][0], 128);
255 _RandomPhaseTabInitialized
= true;
259 ///====================================================================================
260 uint32
CPSFanLight::getNumWantedTris() const
262 NL_PS_FUNC(CPSFanLight_getNumWantedTris
)
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
315 forceTexturedMaterialStages(1);
316 SetupModulatedStage(_Mat
, 0, CMaterial::Diffuse
, CMaterial::Constant
);
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
329 if (ps
.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
331 _Mat
.texConstantColor(0, ps
.getGlobalColorLighted());
335 _Mat
.texConstantColor(0, ps
.getGlobalColor());
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
);
353 _Mat
.texConstantColor(0, col
);
358 ///====================================================================================
359 void CPSFanLight::draw(bool opaque
)
361 // if (!FilterPS[3]) return;
362 NL_PS_FUNC(CPSFanLight_draw
)
364 if (!_Owner
->getSize()) return;
368 computeSrcStep(step
, numToProcess
);
369 if (!numToProcess
) return;
373 if (step
== (1 << 16))
375 CPSFanLightHelper::drawFanLight(_Owner
->getPos().begin(),
376 _Owner
->getTime().begin(),
384 CPSFanLightHelper::drawFanLight(TIteratorVectStep1616(_Owner
->getPos().begin(), 0, step
),
385 TIteratorTimeStep1616(_Owner
->getTime().begin(), 0, step
),
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
);
408 f
.serial(_PhaseSmoothness
, _MoveIntensity
);
409 ITexture
*tex
= _Tex
;
410 f
.serialPolyPtr(tex
);
411 if (f
.isReading()) _Tex
= tex
;
419 ///====================================================================================
420 bool CPSFanLight::completeBBox(NLMISC::CAABBox
&box
) const
422 NL_PS_FUNC(CPSFanLight_completeBBox
)
428 ///====================================================================================
429 CPSFanLight::CPSFanLight(uint32 nbFans
) : _NbFans(nbFans
),
431 _MoveIntensity(1.5f
),
435 NL_PS_FUNC(CPSFanLight_CPSFanLight
)
436 nlassert(nbFans
>= 3);
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
)
456 resize(_Owner
->getMaxSize());
457 //notifyOwnerMaxNumFacesChanged();
460 ///====================================================================================
461 void CPSFanLight::resize(uint32 size
)
463 NL_PS_FUNC(CPSFanLight_resize
)
464 nlassert(size
< (1 << 16));
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
)
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");
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
);
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
;
529 TIndexType
*ptIndex
= (TIndexType
*) iba
.getPtr();
531 CVertexBufferReadWrite 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
)
555 vba
.setTexCoord(k
* (_NbFans
+ 2), 0, NLMISC::CUV(0, 0));
559 vba
.setColor(k
* (_NbFans
+ 2), CRGBA::White
);
563 for(l
= 1; l
<= _NbFans
+ 1; ++l
)
565 vba
.setColor(l
+ k
* (_NbFans
+ 2), CRGBA(0, 0, 0));
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));
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
)
597 dest
.push_back(_Tex
);