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_ribbon_look_at.h"
20 #include "nel/3d/particle_system.h"
21 #include "nel/3d/ps_macro.h"
22 #include "nel/3d/driver.h"
23 #include "nel/3d/debug_vb.h"
32 ////////////////////////////////////
33 // CPSRibbonLookAt implementation //
34 ////////////////////////////////////
36 const float ZEpsilon
= 10E-3f
;
37 const float NormEpsilon
= 10E-8f
;
42 NLMISC::CVector Interp
;
45 typedef std::vector
<CVectInfo
> TRibbonVect
; // a vector used for intermediate computations
47 CPSRibbonLookAt::TVBMap
CPSRibbonLookAt::_VBMap
; // index buffers with no color
48 CPSRibbonLookAt::TVBMap
CPSRibbonLookAt::_ColoredVBMap
; // index buffer + colors
50 //=======================================================
51 CPSRibbonLookAt::CPSRibbonLookAt()
53 NL_PS_FUNC(CPSRibbonLookAt_CPSRibbonLookAt
)
56 //=======================================================
57 CPSRibbonLookAt::~CPSRibbonLookAt()
59 NL_PS_FUNC(CPSRibbonLookAt_CPSRibbonLookAtDtor
)
60 // delete _DyingRibbons;
63 //=======================================================
64 void CPSRibbonLookAt::serial(NLMISC::IStream
&f
)
66 NL_PS_FUNC(CPSRibbonLookAt_serial
)
67 /** Version 4 : added CPSRibbonBase has a base class instead of CPSParticle
70 sint ver
= f
.serialVersion(4);
73 CPSRibbonBase::serial(f
);
77 CPSParticle::serial(f
);
79 CPSColoredParticle::serialColorScheme(f
);
80 CPSSizedParticle::serialSizeScheme(f
);
82 uint32 dummy
= 0; /* _NbDyingRibbons */
85 f
.serial(_SegDuration
, _NbSegs
, dummy
/*_NbDyingRibbons*/);
91 f
.serial(_Parametric
);
102 f
.serialPolyPtr(tex
);
107 _Tex
->setWrapS(ITexture::Clamp
);
108 _Tex
->setWrapT(ITexture::Clamp
);
110 setTailNbSeg(_NbSegs
); // force to build the vb
115 //=======================================================
116 void CPSRibbonLookAt::setTexture(CSmartPtr
<ITexture
> tex
)
118 NL_PS_FUNC(CPSRibbonLookAt_setTexture
)
122 _Tex
->setWrapS(ITexture::Clamp
);
123 _Tex
->setWrapT(ITexture::Clamp
);
125 updateMatAndVbForColor();
129 //=======================================================
130 void CPSRibbonLookAt::step(TPSProcessPass pass
)
132 NL_PS_FUNC(CPSRibbonLookAt_step
)
133 if (pass
== PSMotion
)
142 (pass
== PSBlendRender
&& hasTransparentFaces())
143 || (pass
== PSSolidRender
&& hasOpaqueFaces())
148 computeSrcStep(step
, numToProcess
);
149 if (!numToProcess
) return;
151 /// update the material color
152 CParticleSystem
&ps
= *(_Owner
->getOwner());
153 if (ps
.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
155 _Mat
.setColor(ps
.getGlobalColorLighted());
159 _Mat
.setColor(ps
.getGlobalColor());
161 /** We support Auto-LOD for ribbons, although there is a built-in LOD (that change the geometry rather than the number of ribbons)
162 * that gives better result (both can be used simultaneously)
164 displayRibbons(numToProcess
, step
);
167 if (pass
== PSToolRender
) // edition mode only
174 //=======================================================
175 void CPSRibbonLookAt::newElement(const CPSEmitterInfo
&info
)
177 NL_PS_FUNC(CPSRibbonLookAt_newElement
)
178 CPSRibbonBase::newElement(info
);
179 newColorElement(info
);
180 newSizeElement(info
);
184 //=======================================================
185 void CPSRibbonLookAt::deleteElement(uint32 index
)
187 NL_PS_FUNC(CPSRibbonLookAt_deleteElement
)
188 CPSRibbonBase::deleteElement(index
);
189 deleteColorElement(index
);
190 deleteSizeElement(index
);
194 //=======================================================
195 void CPSRibbonLookAt::resize(uint32 size
)
197 NL_PS_FUNC(CPSRibbonLookAt_resize
)
198 nlassert(size
< (1 << 16));
199 CPSRibbonBase::resize(size
);
204 //=======================================================
205 void CPSRibbonLookAt::updateMatAndVbForColor(void)
207 NL_PS_FUNC(CPSRibbonLookAt_updateMatAndVbForColor
)
208 _Mat
.setTexture(0, _Tex
);
209 _Mat
.setDoubleSided(true);
212 //=======================================================
213 static inline void MakeProj(NLMISC::CVector
&dest
, const NLMISC::CVector
&src
)
216 if (fabsf(src
.y
) > NormEpsilon
* NormEpsilon
)
218 dest
.x
= src
.x
/ src
.y
;
219 dest
.z
= src
.z
/ src
.y
;
224 static inline void BuildSlice(const NLMISC::CMatrix
&mat
, CVertexBuffer
&vb
, uint8
*currVert
, uint32 vertexSize
,
225 const NLMISC::CVector
&I
,
226 const NLMISC::CVector
&K
,
227 TRibbonVect::iterator pos
,
228 TRibbonVect::iterator prev
,
229 TRibbonVect::iterator next
,
231 /// TODO: some optimisation to get a better speed
233 NL_PS_FUNC(BuildSlice
)
234 CHECK_VERTEX_BUFFER(vb
, currVert
);
235 CHECK_VERTEX_BUFFER(vb
, currVert
);
236 NLMISC::CVector tangent
;
238 float invTgNorm
; // inverse of the' norm of the projected segment
241 if (prev
->Proj
.y
> ZEpsilon
&& next
->Proj
.y
> ZEpsilon
) // the 2 points are in front of the camera
243 tangent
= next
->Proj
- prev
->Proj
;
245 tgNorm
= tangent
.norm();
246 if (fabs(tgNorm
) > 10E-8)
248 invTgNorm
= 1.f
/ tgNorm
;
254 // build orthogonals vectors to tangent
255 *(NLMISC::CVector
*) currVert
= pos
->Interp
+ ribSize
* invTgNorm
* (tangent
.x
* K
- tangent
.z
* I
);
256 *(NLMISC::CVector
*) (currVert
+ vertexSize
) = pos
->Interp
+ ribSize
* invTgNorm
* (- tangent
.x
* K
+ tangent
.z
* I
);
258 else if (prev
->Proj
.y
> ZEpsilon
) // second point cross the near plane
260 // compute intersection point
261 NLMISC::CVector inter
;
262 NLMISC::CVector tInter
= CVector::Null
;
263 if (fabsf(prev
->Proj
.y
- next
->Proj
.y
) > NormEpsilon
)
265 float lambda
= (next
->Proj
.y
- ZEpsilon
) / (next
->Proj
.y
- prev
->Proj
.y
);
266 inter
= lambda
* prev
->Interp
+ (1.f
- lambda
) * next
->Interp
;
267 MakeProj(tInter
, mat
* inter
);
271 *(NLMISC::CVector
*) currVert
= pos
->Interp
;
272 *(NLMISC::CVector
*) (currVert
+ vertexSize
) = pos
->Interp
;
276 tangent
= tInter
- prev
->Proj
;
279 tgNorm
= tangent
.norm();
280 if (fabs(tgNorm
) > 10E-8)
282 invTgNorm
= 1.f
/ tgNorm
;
288 // build orthogonals vectors to tangent
290 *(NLMISC::CVector
*) currVert
= inter
+ ribSize
* invTgNorm
* (tangent
.x
* K
- tangent
.z
* I
);
291 *(NLMISC::CVector
*) (currVert
+ vertexSize
) = inter
+ ribSize
* invTgNorm
* (- tangent
.x
* K
+ tangent
.z
* I
);
293 else if (next
->Proj
.y
> ZEpsilon
) // first point cross the near plane
295 // compute intersection point
296 NLMISC::CVector inter
;
297 NLMISC::CVector tInter
= NLMISC::CVector::Null
;
298 if (fabsf(prev
->Proj
.y
- next
->Proj
.y
) > NormEpsilon
)
300 float lambda
= (next
->Proj
.y
- ZEpsilon
) / (next
->Proj
.y
- prev
->Proj
.y
);
301 inter
= lambda
* prev
->Interp
+ (1.f
- lambda
) * next
->Interp
;
302 MakeProj(tInter
, mat
* inter
);
306 *(NLMISC::CVector
*) currVert
= pos
->Interp
;
307 *(NLMISC::CVector
*) (currVert
+ vertexSize
) = pos
->Interp
;
311 tangent
= next
->Proj
- tInter
;
313 tgNorm
= tangent
.norm();
314 if (fabs(tgNorm
) > 10E-8)
316 invTgNorm
= 1.f
/ tgNorm
;
322 // build orthogonals vectors to tangent
324 *(NLMISC::CVector
*) currVert
= inter
+ ribSize
* invTgNorm
* (tangent
.x
* K
- tangent
.z
* I
);
325 *(NLMISC::CVector
*) (currVert
+ vertexSize
) = inter
+ ribSize
* invTgNorm
* (- tangent
.x
* K
+ tangent
.z
* I
);
328 else // two points are not visible
330 *(NLMISC::CVector
*) currVert
= pos
->Interp
;
331 *(NLMISC::CVector
*) (currVert
+ vertexSize
) = pos
->Interp
;
337 //==========================================================================
338 void CPSRibbonLookAt::displayRibbons(uint32 nbRibbons
, uint32 srcStep
)
340 // if (!FilterPS[6]) return;
341 NL_PS_FUNC(CPSRibbonLookAt_displayRibbons
)
342 if (!nbRibbons
) return;
344 CPSRibbonBase::updateLOD();
345 if (_UsedNbSegs
< 2) return;
346 const float date
= _Owner
->getOwner()->getSystemDate();
348 CVBnPB
&VBnPB
= getVBnPB(); // get the appropriate vb (build it if needed)
349 CVertexBuffer
&VB
= VBnPB
.VB
;
350 CIndexBuffer
&PB
= VBnPB
.PB
;
351 const uint32 vertexSize
= VB
.getVertexSize();
353 const uint32 vertexSizeX2
= vertexSize
<< 1;
354 const NLMISC::CVector I
= _Owner
->computeI();
355 const NLMISC::CVector K
= _Owner
->computeK();
356 const NLMISC::CMatrix
&localToWorldMatrix
= getLocalToWorldTrailMatrix();
357 const NLMISC::CMatrix
&mat
= getViewMat() * localToWorldMatrix
;
358 IDriver
*drv
= this->getDriver();
362 drv
->setupModelMatrix(localToWorldMatrix
);
363 _Owner
->incrementNbDrawnParticles(nbRibbons
); // for benchmark purpose
364 const uint numRibbonBatch
= getNumRibbonsInVB(); // number of ribbons to process at once
365 static TRibbonVect currRibbon
;
366 static std::vector
<float> sizes
;
367 static std::vector
<NLMISC::CRGBA
> colors
;
369 if (_UsedNbSegs
== 0) return;
371 currRibbon
.resize(_UsedNbSegs
+ 1);
372 sizes
.resize(numRibbonBatch
);
375 /// update material color
376 CParticleSystem
&ps
= *(_Owner
->getOwner());
377 if (ps
.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
379 CPSMaterial::forceModulateConstantColor(true, ps
.getGlobalColorLighted());
382 if (ps
.getColorAttenuationScheme() != NULL
|| ps
.isUserColorUsed())
384 CPSMaterial::forceModulateConstantColor(true, ps
.getGlobalColor());
388 forceModulateConstantColor(false);
389 _Mat
.setColor(ps
.getGlobalColor());
394 colorOffset
= VB
.getColorOff();
395 colors
.resize(numRibbonBatch
);
401 uint ribbonIndex
= 0; // index of the first ribbon in the batch being processed
402 uint32 fpRibbonIndex
= 0;
405 _ColorScheme
->setColorType(drv
->getVertexColorFormat());
409 toProcess
= std::min((uint
) (nbRibbons
- ribbonIndex
) /* = left to do */, numRibbonBatch
);
411 const float *ptCurrSize
;
412 uint32 ptCurrSizeIncrement
;
415 ptCurrSize
= (float *) _SizeScheme
->make(this->_Owner
, ribbonIndex
, &sizes
[0], sizeof(float), toProcess
, true, srcStep
);
416 ptCurrSizeIncrement
= 1;
420 ptCurrSize
= &_ParticleSize
;
421 ptCurrSizeIncrement
= 0;
426 NLMISC::CRGBA
*ptCurrColor
=0;
429 colors
.resize(nbRibbons
);
430 ptCurrColor
= (NLMISC::CRGBA
*) _ColorScheme
->make(this->_Owner
, ribbonIndex
, &colors
[0], sizeof(NLMISC::CRGBA
), toProcess
, true, srcStep
);
432 VB
.setNumVertices(2 * (_UsedNbSegs
+ 1) * toProcess
);
434 CVertexBufferReadWrite vba
;
436 currVert
= (uint8
*) vba
.getVertexCoordPointer();
437 for (uint k
= ribbonIndex
; k
< ribbonIndex
+ toProcess
; ++k
)
440 TRibbonVect::iterator rIt
= currRibbon
.begin(), rItEnd
= currRibbon
.end(), rItEndMinusOne
= rItEnd
- 1;
442 ////////////////////////////////////
443 // interpolate and project points //
444 ////////////////////////////////////
449 //////////////////////
450 // INCREMENTAL CASE //
451 //////////////////////
453 // the parent class has a method to get the ribbons positions
454 computeRibbon((uint
) (fpRibbonIndex
>> 16), &rIt
->Interp
, sizeof(CVectInfo
));
457 MakeProj(rIt
->Proj
, mat
* rIt
->Interp
);
460 while (rIt
!= rItEnd
);
464 //////////////////////
465 // PARAMETRIC CASE //
466 //////////////////////
467 // we compute each pos thanks to the parametric curve
468 _Owner
->integrateSingle(date
- _UsedSegDuration
* (_UsedNbSegs
+ 1), _UsedSegDuration
, _UsedNbSegs
+ 1, (uint
) (fpRibbonIndex
>> 16),
469 &rIt
->Interp
, sizeof(CVectInfo
) );
470 // project each position now
473 MakeProj(rIt
->Proj
, mat
* rIt
->Interp
);
476 while (rIt
!= rItEnd
);
479 rIt
= currRibbon
.begin();
485 uint8
*currColVertex
= currVert
+ colorOffset
;
486 uint colCount
= (_UsedNbSegs
+ 1) << 1;
489 * (CRGBA
*) currColVertex
= *ptCurrColor
;
490 currColVertex
+= vertexSize
;
497 /// build the ribbon in vb
498 // deals with first point
499 BuildSlice(mat
, VB
, currVert
, vertexSize
, I
, K
, rIt
, rIt
, rIt
+ 1, *ptCurrSize
);
500 currVert
+= vertexSizeX2
;
504 // deals with other points
505 for (;;) // we assume at least 2 segments, so we must have a middle point
507 // build 2 vertices with the right tangent. /* to project 2 */ is old projected point
508 BuildSlice(mat
, VB
, currVert
, vertexSize
, I
, K
, rIt
, rIt
- 1, rIt
+ 1, *ptCurrSize
);
511 if (rIt
== rItEndMinusOne
) break;
513 currVert
+= vertexSizeX2
;
515 currVert
+= vertexSizeX2
;
517 BuildSlice(mat
, VB
, currVert
, vertexSize
, I
, K
, rIt
, rIt
- 1, rIt
, *ptCurrSize
);
518 ptCurrSize
+= ptCurrSizeIncrement
;
519 currVert
+= vertexSizeX2
;
521 fpRibbonIndex
+= srcStep
;
525 PB
.setNumIndexes((_UsedNbSegs
<< 1) * toProcess
* 3);
526 drv
->activeVertexBuffer(VB
);
527 // display the result
528 drv
->activeIndexBuffer (PB
);
529 drv
->renderTriangles (_Mat
, 0, PB
.getNumIndexes()/3);
530 ribbonIndex
+= toProcess
;
532 while (ribbonIndex
!= nbRibbons
);
535 //==========================================================================
536 bool CPSRibbonLookAt::hasTransparentFaces(void)
538 NL_PS_FUNC(CPSRibbonLookAt_hasTransparentFaces
)
539 return getBlendingMode() != CPSMaterial::alphaTest
;
543 //==========================================================================
544 bool CPSRibbonLookAt::hasOpaqueFaces(void)
546 NL_PS_FUNC(CPSRibbonLookAt_hasOpaqueFaces
)
547 return !hasTransparentFaces();
550 //==========================================================================
551 uint32
CPSRibbonLookAt::getNumWantedTris() const
553 NL_PS_FUNC(CPSRibbonLookAt_getNumWantedTris
)
555 //return _Owner->getMaxSize() * _NbSegs * 2;
556 return _Owner
->getSize() * _NbSegs
* 2;
561 //==========================================================================
562 CPSRibbonLookAt::CVBnPB
&CPSRibbonLookAt::getVBnPB()
564 NL_PS_FUNC(CPSRibbonLookAt_getVBnPB
)
565 TVBMap
&map
= _ColorScheme
? _VBMap
: _ColoredVBMap
;
566 TVBMap::iterator it
= map
.find(_UsedNbSegs
+ 1);
571 else // must create this vb
573 const uint numRibbonInVB
= getNumRibbonsInVB();
574 CVBnPB
&VBnPB
= map
[_UsedNbSegs
+ 1]; // make an entry
576 /// set the vb format & size
577 CVertexBuffer
&vb
= VBnPB
.VB
;
578 vb
.setVertexFormat(CVertexBuffer::PositionFlag
|
579 CVertexBuffer::TexCoord0Flag
|
580 (_ColorScheme
? CVertexBuffer::PrimaryColorFlag
: 0));
581 vb
.setNumVertices(2 * (_UsedNbSegs
+ 1) * numRibbonInVB
);
582 vb
.setPreferredMemory(CVertexBuffer::AGPVolatile
, true);
583 CVertexBufferReadWrite vba
;
586 // set the primitive block size
587 CIndexBuffer
&pb
= VBnPB
.PB
;
588 pb
.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT
);
589 pb
.setNumIndexes((_UsedNbSegs
<< 1) * numRibbonInVB
* 3);
590 CIndexBufferReadWrite iba
;
592 /// Setup the pb and vb parts. Not very fast but executed only once
595 for (uint i
= 0; i
< numRibbonInVB
; ++i
)
597 for (uint k
= 0; k
< (_UsedNbSegs
+ 1); ++k
)
599 vba
.setTexCoord(vbIndex
, 0, CUV((1.f
- k
/ (float) _UsedNbSegs
), 0)); /// top vertex
600 vba
.setTexCoord(vbIndex
+ 1, 0, CUV((1.f
- k
/ (float) _UsedNbSegs
), 1)); /// bottom vertex
601 if (k
!= _UsedNbSegs
)
603 /// add 2 tri in the primitive block
604 iba
.setTri(pbIndex
, vbIndex
+ 1, vbIndex
+ 2, vbIndex
);
605 iba
.setTri(pbIndex
+3, vbIndex
+ 1, vbIndex
+ 3, vbIndex
+ 2);
615 //==========================================================================
616 uint
CPSRibbonLookAt::getNumRibbonsInVB() const
618 NL_PS_FUNC(CPSRibbonLookAt_getNumRibbonsInVB
)
619 /// approximation of the max number of vertices we want in a vb
620 const uint vertexInVB
= 256;
621 return std::max(1u, (uint
) (vertexInVB
/ (_UsedNbSegs
+ 1)));
624 //==========================================================================
625 void CPSRibbonLookAt::enumTexs(std::vector
<NLMISC::CSmartPtr
<ITexture
> > &dest
, IDriver
&drv
)
627 NL_PS_FUNC(CPSRibbonLookAt_enumTexs
)
630 dest
.push_back(_Tex
);
631 _Tex
->getShareName();