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.h"
20 #include "nel/3d/ps_macro.h"
21 #include "nel/3d/particle_system.h"
22 #include "nel/3d/driver.h"
23 #include "nel/3d/ps_util.h"
24 #include "nel/3d/texture_mem.h"
25 #include "nel/misc/matrix.h"
34 static NLMISC::CRGBA GradientB2W
[] = {NLMISC::CRGBA(0, 0, 0, 0), NLMISC::CRGBA(255, 255, 255, 255) };
36 CPSRibbon::TVBMap
CPSRibbon::_VBMaps
[16];
39 /// private use : this create a gradient texture that goew from black to white
40 static ITexture
*CreateGradientTexture()
42 NL_PS_FUNC(CreateGradientTexture
)
43 CUniquePtr
<CTextureMem
> tex(new CTextureMem((uint8
*) &GradientB2W
,
45 false, /* dont delete */
46 false, /* not a file */
49 //tex->setWrapS(ITexture::Clamp);
50 tex
->setShareName("#GradBW");
55 ///////////////////////////
56 // ribbon implementation //
57 ///////////////////////////
60 const NLMISC::CVector
CPSRibbon::Triangle
[] =
62 NLMISC::CVector(0, 1, 0),
63 NLMISC::CVector(1, -1, 0),
64 NLMISC::CVector(-1, -1, 0),
67 const NLMISC::CVector
CPSRibbon::Losange
[] =
69 NLMISC::CVector(0, 1.f
, 0),
70 NLMISC::CVector(1.f
, 0, 0),
71 NLMISC::CVector(0, -1.f
, 0),
72 NLMISC::CVector(-1.f
, 0, 0)
75 const NLMISC::CVector
CPSRibbon::HeightSides
[] =
77 NLMISC::CVector(-0.5f
, 1, 0),
78 NLMISC::CVector(0.5f
, 1, 0),
79 NLMISC::CVector(1, 0.5f
, 0),
80 NLMISC::CVector(1, -0.5f
, 0),
81 NLMISC::CVector(0.5f
, -1, 0),
82 NLMISC::CVector(-0.5f
, -1, 0),
83 NLMISC::CVector(-1, -0.5f
, 0),
84 NLMISC::CVector(-1, 0.5f
, 0)
88 const NLMISC::CVector
CPSRibbon::Pentagram
[] =
90 NLMISC::CVector(0, 1, 0),
91 NLMISC::CVector(1, -1, 0),
92 NLMISC::CVector(-1, 0, 0),
93 NLMISC::CVector(1, 0, 0),
94 NLMISC::CVector(-1, -1, 0)
97 const NLMISC::CVector
CPSRibbon::SimpleSegmentX
[] =
99 NLMISC::CVector(1, 0, 0),
100 NLMISC::CVector(-1, 0, 0),
102 const uint
CPSRibbon::NbVerticesInSimpleSegmentX
= sizeof(CPSRibbon::SimpleSegmentX
) / sizeof(CVector
);
104 const NLMISC::CVector
CPSRibbon::SimpleSegmentY
[] =
106 NLMISC::CVector(0, 1, 0),
107 NLMISC::CVector(0, -1, 0),
109 const uint
CPSRibbon::NbVerticesInSimpleSegmentY
= sizeof(CPSRibbon::SimpleSegmentY
) / sizeof(CVector
);
111 const NLMISC::CVector
CPSRibbon::SimpleSegmentZ
[] =
113 NLMISC::CVector(0, 0, 1),
114 NLMISC::CVector(0, 0, -1),
116 const uint
CPSRibbon::NbVerticesInSimpleSegmentZ
= sizeof(CPSRibbon::SimpleSegmentZ
) / sizeof(CVector
);
118 const NLMISC::CVector
CPSRibbon::SimpleBrace
[] =
120 NLMISC::CVector(1, 0, 0),
121 NLMISC::CVector(-1, 0, 0),
122 NLMISC::CVector(0, 1, 0),
123 NLMISC::CVector(0, -1, 0)
125 const uint
CPSRibbon::NbVerticesInSimpleBrace
= sizeof(CPSRibbon::SimpleBrace
) / sizeof(CVector
);
127 const uint
CPSRibbon::NbVerticesInTriangle
= sizeof(CPSRibbon::Triangle
) / sizeof(CVector
);
128 const uint
CPSRibbon::NbVerticesInLosange
= sizeof(Losange
) / sizeof(CVector
);
129 const uint
CPSRibbon::NbVerticesInHeightSide
= sizeof(CPSRibbon::HeightSides
) / sizeof(CVector
);
130 const uint
CPSRibbon::NbVerticesInPentagram
= sizeof(CPSRibbon::Pentagram
) / sizeof(CVector
);
133 struct CDummy2DAngle
: CPSRotated2DParticle
135 CPSLocated
*getAngle2DOwner(void) { return NULL
; }
138 ///==================================================================================================================
139 void CPSRibbon::serial(NLMISC::IStream
&f
)
141 NL_PS_FUNC(CPSRibbon_serial
)
142 // Version 3 : - added brace mode
143 // - added orientation enum
144 sint ver
= f
.serialVersion(3);
147 nlassert(f
.isReading());
149 /// we had CPSParticle::serial(f), but this is not the base class anymore, so we emulate this...
150 /// version 2 : auto-lod saved
151 sint ver2
= f
.serialVersion(2);
153 // here is CPSLocatedBindable::serial(f)
154 sint ver3
= f
.serialVersion(4);
156 if (ver3
> 1) f
.serialEnum(_LOD
);
157 if (ver3
> 2) f
.serial(_Name
);
174 bool bDisableAutoLOD
;
175 f
.serial(bDisableAutoLOD
);
176 disableAutoLOD(bDisableAutoLOD
);
181 bool systemBasisEnabled
;
182 bool drEnabled
; // dying ribbons, not supported in this version
184 CPSColoredParticle::serialColorScheme(f
);
185 CPSSizedParticle::serialSizeScheme(f
);
187 // we dont use the 2d angle anymore...serial a dummy one
189 CDummy2DAngle _Dummy2DAngle
;
190 _Dummy2DAngle
.serialAngle2DScheme(f
);
193 f
.serial(colorFading
, systemBasisEnabled
);
197 f
.serial(tailNbSegs
);
198 ITexture
*tex
= NULL
;
199 f
.serialPolyPtr(tex
);
203 f
.serial(_UFactor
, _VFactor
) ;
206 // shape serialization
207 f
.serialCont(_Shape
);
210 _NbSegs
= tailNbSegs
>> 1;
211 if (_NbSegs
< 1) _NbSegs
= 2;
212 setInterpolationMode(Linear
);
215 resize(_Owner
->getMaxSize());
223 CPSRibbonBase::serial(f
);
224 CPSColoredParticle::serialColorScheme(f
);
225 CPSSizedParticle::serialSizeScheme(f
);
226 CPSMaterial::serialMaterial(f
);
227 f
.serialCont(_Shape
);
228 bool colorFading
= _ColorFading
;
229 f
.serial(colorFading
);
230 _ColorFading
= colorFading
;
231 uint32 tailNbSegs
= _NbSegs
;
232 f
.serial(tailNbSegs
);
235 setTailNbSeg(_NbSegs
);
238 ITexture
*tex
= _Tex
;
239 f
.serialPolyPtr(tex
);
243 f
.serial(_UFactor
, _VFactor
) ;
249 bool braceMode
= _BraceMode
;
251 _BraceMode
= braceMode
;
252 f
.serialEnum(_Orientation
);
262 //=======================================================
263 CPSRibbon::CPSRibbon() : _UFactor(1.f
),
265 _Orientation(FollowPath
),
270 _ForceLighted(false),
273 NL_PS_FUNC(CPSRibbon_CPSRibbon
)
274 setInterpolationMode(Linear
);
275 setSegDuration(0.06f
);
276 if (CParticleSystem::getSerializeIdentifierFlag()) _Name
= std::string("Ribbon");
277 setShape(Triangle
, NbVerticesInTriangle
);
278 _Mat
.setDoubleSided(true);
282 //=======================================================
283 CPSRibbon::~CPSRibbon()
285 NL_PS_FUNC(CPSRibbon_CPSRibbonDtor
)
289 //==========================================================================
290 inline uint
CPSRibbon::getNumVerticesInSlice() const
292 NL_PS_FUNC(CPSRibbon_getNumVerticesInSlice
)
295 return (uint
)_Shape
.size();
299 return (uint
)_Shape
.size() + (_Tex
== NULL
? 0 : 1);
307 //=======================================================
308 void CPSRibbon::step(TPSProcessPass pass
)
310 NL_PS_FUNC(CPSRibbon_step
)
311 if (pass
== PSMotion
)
320 (pass
== PSBlendRender
&& hasTransparentFaces())
321 || (pass
== PSSolidRender
&& hasOpaqueFaces())
326 computeSrcStep(step
, numToProcess
);
327 if (!numToProcess
) return;
329 /// update the material color
330 CParticleSystem
&ps
= *(_Owner
->getOwner());
331 if (ps
.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
333 _Mat
.setColor(ps
.getGlobalColorLighted());
337 _Mat
.setColor(ps
.getGlobalColor());
340 /** We support Auto-LOD for ribbons, although there is a built-in LOD (that change the geometry rather than the number of ribbons)
341 * that gives better result (both can be used simultaneously)
344 displayRibbons(numToProcess
, step
);
348 if (pass
== PSToolRender
) // edition mode only
355 //=======================================================
356 void CPSRibbon::newElement(const CPSEmitterInfo
&info
)
358 NL_PS_FUNC(CPSRibbon_newElement
)
359 CPSRibbonBase::newElement(info
);
360 newColorElement(info
);
361 newSizeElement(info
);
365 //=======================================================
366 void CPSRibbon::deleteElement(uint32 index
)
368 NL_PS_FUNC(CPSRibbon_deleteElement
)
369 CPSRibbonBase::deleteElement(index
);
370 deleteColorElement(index
);
371 deleteSizeElement(index
);
375 //=======================================================
376 void CPSRibbon::resize(uint32 size
)
378 NL_PS_FUNC(CPSRibbon_resize
)
379 nlassert(size
< (1 << 16));
380 CPSRibbonBase::resize(size
);
385 //=======================================================
386 void CPSRibbon::updateMatAndVbForColor(void)
388 NL_PS_FUNC(CPSRibbon_updateMatAndVbForColor
)
393 ///=========================================================================
394 // Create the start slice of a ribbon (all vertices at the same pos)
395 static inline uint8
*BuildRibbonFirstSlice(const NLMISC::CVector
&pos
,
401 NL_PS_FUNC(BuildRibbonFirstSlice
)
404 * (NLMISC::CVector
*) dest
= pos
;
412 ///=========================================================================
413 // This compute one slice of a ribbon, and return the next vertex to be filled
414 static inline uint8
*ComputeRibbonSliceFollowPath(const NLMISC::CVector
&prev
,
415 const NLMISC::CVector
&next
,
416 const NLMISC::CVector
*shape
,
421 NLMISC::CMatrix
&basis
424 NL_PS_FUNC(ComputeRibbonSliceFollowPath
)
425 // compute a basis from the next and previous position.
426 // (not optimized for now, but not widely used, either...)
427 const float epsilon
= 10E-5f
;
428 if (fabsf(next
.x
- prev
.x
) > epsilon
429 || fabsf(next
.y
- prev
.y
) > epsilon
430 || fabsf(next
.z
- prev
.z
) > epsilon
)
432 // build a new basis, or use the previous one otherwise
433 CPSUtil::buildSchmidtBasis(next
- prev
, basis
);
437 const NLMISC::CVector
*shapeEnd
= shape
+ numVerts
;
440 *(NLMISC::CVector
*) dest
= basis
* (size
* (*shape
));
444 while (shape
!= shapeEnd
);
448 ///=========================================================================
449 // This compute one slice of a ribbon, and return the next vertex to be filled
450 static inline uint8
*ComputeRibbonSliceIdentity(const NLMISC::CVector
&prev
,
451 const NLMISC::CVector
&next
,
452 const NLMISC::CVector
*shape
,
459 NL_PS_FUNC(ComputeRibbonSliceIdentity
)
460 const NLMISC::CVector
*shapeEnd
= shape
+ numVerts
;
463 ((NLMISC::CVector
*) dest
)->set(size
* shape
->x
+ next
.x
,
464 size
* shape
->y
+ next
.y
,
465 size
* shape
->z
+ next
.z
);
469 while (shape
!= shapeEnd
);
473 ///=========================================================================
474 static inline uint8
*ComputeRibbonSliceFollowPathXY(const NLMISC::CVector
&prev
,
475 const NLMISC::CVector
&next
,
476 const NLMISC::CVector
*shape
,
481 NLMISC::CMatrix
&basis
484 NL_PS_FUNC(ComputeRibbonSliceFollowPathXY
)
485 float deltaX
= next
.x
- prev
.x
;
486 float deltaY
= next
.y
- prev
.y
;
487 const float epsilon
= 10E-5f
;
488 if (fabsf(deltaX
) > epsilon
489 || fabsf(deltaY
) > epsilon
)
491 float norm
= sqrtf(NLMISC::sqr(deltaX
) + NLMISC::sqr(deltaY
));
492 float invNorm
= (norm
!= 0.f
) ? 1.f
/ norm
: 0.f
;
493 NLMISC::CVector I
, J
;
494 J
.set(deltaX
* invNorm
, deltaY
* invNorm
, 0.f
);
495 I
.set(-J
.y
, J
.x
, 0.f
);
496 basis
.setRot(I
, CVector::K
, J
, true);
499 const NLMISC::CVector
*shapeEnd
= shape
+ numVerts
;
502 *(NLMISC::CVector
*) dest
= basis
* (size
* (*shape
));
506 while (shape
!= shapeEnd
);
513 ///=========================================================================
514 // This is used to compute a ribbon mesh from its curve and its base shape.
515 // This is for untextured versions (no need to duplicate the last vertex of each slice)
516 static inline uint8
*ComputeUntexturedRibbonMesh(uint8
*destVb
,
518 const NLMISC::CVector
*curve
,
519 const NLMISC::CVector
*shape
,
521 uint numVerticesInShape
,
524 CPSRibbon::TOrientation orientation
527 NL_PS_FUNC(ComputeUntexturedRibbonMesh
)
532 case CPSRibbon::FollowPath
:
535 destVb
= ComputeRibbonSliceFollowPath(curve
[1],
544 size
-= sizeIncrement
;
548 case CPSRibbon::FollowPathXY
:
551 destVb
= ComputeRibbonSliceFollowPathXY(curve
[1],
560 size
-= sizeIncrement
;
564 case CPSRibbon::Identity
:
567 destVb
= ComputeRibbonSliceIdentity(curve
[1],
576 size
-= sizeIncrement
;
584 return BuildRibbonFirstSlice(curve
[0], numVerticesInShape
, destVb
, vertexSize
);
587 ///=========================================================================
588 // This is used to compute a ribbon mesh from its curve and its base shape.
589 // (Textured Version)
590 static inline uint8
*ComputeTexturedRibbonMesh(uint8
*destVb
,
592 const NLMISC::CVector
*curve
,
593 const NLMISC::CVector
*shape
,
595 uint numVerticesInShape
,
598 CPSRibbon::TOrientation orientation
601 NL_PS_FUNC(ComputeTexturedRibbonMesh
)
606 case CPSRibbon::FollowPath
:
609 uint8
*nextDestVb
= ComputeRibbonSliceFollowPath(curve
[1],
618 // duplicate last vertex ( equal first)
619 * (NLMISC::CVector
*) nextDestVb
= * (NLMISC::CVector
*) destVb
;
620 destVb
= nextDestVb
+ vertexSize
;
623 size
-= sizeIncrement
;
627 case CPSRibbon::FollowPathXY
:
630 uint8
*nextDestVb
= ComputeRibbonSliceFollowPathXY(curve
[1],
639 // duplicate last vertex ( equal first)
640 * (NLMISC::CVector
*) nextDestVb
= * (NLMISC::CVector
*) destVb
;
641 destVb
= nextDestVb
+ vertexSize
;
644 size
-= sizeIncrement
;
648 case CPSRibbon::Identity
:
651 uint8
*nextDestVb
= ComputeRibbonSliceIdentity(curve
[1],
659 // duplicate last vertex ( equal first)
660 * (NLMISC::CVector
*) nextDestVb
= * (NLMISC::CVector
*) destVb
;
661 destVb
= nextDestVb
+ vertexSize
;
664 size
-= sizeIncrement
;
672 return BuildRibbonFirstSlice(curve
[0], numVerticesInShape
+ 1, destVb
, vertexSize
);
675 //==========================================================================
676 void CPSRibbon::displayRibbons(uint32 nbRibbons
, uint32 srcStep
)
678 // if (!FilterPS[5]) return;
679 NL_PS_FUNC(CPSRibbon_displayRibbons
)
680 if (!nbRibbons
) return;
682 CPSRibbonBase::updateLOD();
683 if (_UsedNbSegs
< 2) return;
684 const float date
= _Owner
->getOwner()->getSystemDate();
686 CVBnPB
&VBnPB
= getVBnPB(); // get the appropriate vb (built it if needed)
687 CVertexBuffer
&VB
= VBnPB
.VB
;
688 CIndexBuffer
&PB
= VBnPB
.PB
;
689 const uint32 vertexSize
= VB
.getVertexSize();
692 IDriver
*drv
= this->getDriver();
696 drv
->setupModelMatrix(getLocalToWorldTrailMatrix());
697 _Owner
->incrementNbDrawnParticles(nbRibbons
); // for benchmark purpose
698 const uint numRibbonBatch
= getNumRibbonsInVB(); // number of ribons to process at once
699 if (_UsedNbSegs
== 0) return;
703 CParticleSystem
&ps
= *(_Owner
->getOwner());
704 bool useGlobalColor
= ps
.getColorAttenuationScheme() != NULL
|| ps
.isUserColorUsed();
705 if (useGlobalColor
!= _GlobalColor
)
707 _GlobalColor
= useGlobalColor
;
710 if (usesGlobalColorLighting() != _Lighted
)
712 _Lighted
= usesGlobalColorLighting();
715 if (ps
.getForceGlobalColorLightingFlag() != _ForceLighted
)
717 _ForceLighted
= ps
.getForceGlobalColorLightingFlag();
725 colorOffset
= VB
.getColorOff();
727 /////////////////////
728 // Compute ribbons //
729 /////////////////////
730 const uint numVerticesInSlice
= getNumVerticesInSlice();
731 const uint numVerticesInShape
= (uint
)_Shape
.size();
733 static std::vector
<float> sizes
;
734 static std::vector
<NLMISC::CVector
> ribbonPos
; // this is where the position of each ribbon slice center i stored
735 ribbonPos
.resize(_UsedNbSegs
+ 1); // make sure we have enough room
736 sizes
.resize(numRibbonBatch
);
740 uint ribbonIndex
= 0; // index of the first ribbon in the batch being processed
741 uint32 fpRibbonIndex
= 0; // fixed point index in source
744 _ColorScheme
->setColorType(drv
->getVertexColorFormat());
748 toProcess
= std::min((uint
) (nbRibbons
- ribbonIndex
) , numRibbonBatch
);
749 VB
.setNumVertices((_UsedNbSegs
+ 1) * toProcess
* numVerticesInSlice
);
751 CVertexBufferReadWrite vba
;
753 currVert
= (uint8
*) vba
.getVertexCoordPointer();
755 const float *ptCurrSize
;
756 uint32 ptCurrSizeIncrement
;
759 ptCurrSize
= (float *) _SizeScheme
->make(this->_Owner
, ribbonIndex
, &sizes
[0], sizeof(float), toProcess
, true, srcStep
);
760 ptCurrSizeIncrement
= 1;
764 ptCurrSize
= &_ParticleSize
;
765 ptCurrSizeIncrement
= 0;
771 _ColorScheme
->makeN(this->_Owner
, ribbonIndex
, currVert
+ colorOffset
, vertexSize
, toProcess
, numVerticesInSlice
* (_UsedNbSegs
+ 1), srcStep
);
774 //////////////////////////////////////////////////////////////////////////////////////
775 // interpolate and project points the result is directly setup in the vertex buffer //
776 //////////////////////////////////////////////////////////////////////////////////////
779 //////////////////////
780 // INCREMENTAL CASE //
781 //////////////////////
782 if (_Tex
!= NULL
&& !_BraceMode
) // textured case : must duplicate last vertex, unless in brace mod
786 const float ribbonSizeIncrement
= *ptCurrSize
/ (float) _UsedNbSegs
;
787 ptCurrSize
+= ptCurrSizeIncrement
;
788 // the parent class has a method to get the ribbons positions
789 computeRibbon((uint
) (fpRibbonIndex
>> 16), &ribbonPos
[0], sizeof(NLMISC::CVector
));
790 currVert
= ComputeTexturedRibbonMesh(currVert
,
800 fpRibbonIndex
+= srcStep
;
804 else // untextured case
808 const float ribbonSizeIncrement
= *ptCurrSize
/ (float) _UsedNbSegs
;
809 ptCurrSize
+= ptCurrSizeIncrement
;
810 // the parent class has a method to get the ribbons positions
811 computeRibbon((uint
) (fpRibbonIndex
>> 16), &ribbonPos
[0], sizeof(NLMISC::CVector
));
812 currVert
= ComputeUntexturedRibbonMesh(currVert
,
822 fpRibbonIndex
+= srcStep
;
829 //////////////////////
830 // PARAMETRIC CASE //
831 //////////////////////
832 if (_Tex
!= NULL
) // textured case
836 const float ribbonSizeIncrement
= *ptCurrSize
/ (float) _UsedNbSegs
;
837 ptCurrSize
+= ptCurrSizeIncrement
;
838 _Owner
->integrateSingle(date
- _UsedSegDuration
* (_UsedNbSegs
+ 1),
841 (uint
) (fpRibbonIndex
>> 16),
844 currVert
= ComputeTexturedRibbonMesh(currVert
,
854 fpRibbonIndex
+= srcStep
;
858 else // untextured case
862 const float ribbonSizeIncrement
= *ptCurrSize
/ (float) _UsedNbSegs
;
863 ptCurrSize
+= ptCurrSizeIncrement
;
864 _Owner
->integrateSingle(date
- _UsedSegDuration
* (_UsedNbSegs
+ 1),
867 (uint
) (fpRibbonIndex
>> 16),
870 currVert
= ComputeUntexturedRibbonMesh(currVert
,
880 fpRibbonIndex
+= srcStep
;
886 // display the result
887 uint numTri
= numVerticesInShape
* _UsedNbSegs
* toProcess
;
892 PB
.setNumIndexes(3 * numTri
);
893 drv
->activeIndexBuffer(PB
);
894 drv
->activeVertexBuffer(VB
);
895 drv
->renderTriangles(_Mat
, 0, numTri
);
896 ribbonIndex
+= toProcess
;
898 while (ribbonIndex
!= nbRibbons
);
902 //==========================================================================
903 bool CPSRibbon::hasTransparentFaces(void)
905 NL_PS_FUNC(CPSRibbon_hasTransparentFaces
)
906 return getBlendingMode() != CPSMaterial::alphaTest
;
910 //==========================================================================
911 bool CPSRibbon::hasOpaqueFaces(void)
913 NL_PS_FUNC(CPSRibbon_hasOpaqueFaces
)
914 return !hasTransparentFaces();
917 //==========================================================================
918 uint32
CPSRibbon::getNumWantedTris() const
920 NL_PS_FUNC(CPSRibbon_getNumWantedTris
)
922 //return _Owner->getMaxSize() * _NbSegs;
923 return _Owner
->getSize() * _NbSegs
;
926 //==========================================================================
927 // Set a tri in ribbon with check added
928 static inline void setTri(CIndexBufferReadWrite
&ibrw
, const CVertexBuffer
&vb
, uint32 triIndex
, uint32 i0
, uint32 i1
, uint32 i2
)
930 nlassert(i0
< vb
.getNumVertices());
931 nlassert(i1
< vb
.getNumVertices());
932 nlassert(i2
< vb
.getNumVertices());
933 ibrw
.setTri(triIndex
, i0
, i1
, i2
);
936 //==========================================================================
937 CPSRibbon::CVBnPB
&CPSRibbon::getVBnPB()
939 NL_PS_FUNC(CPSRibbon_getVBnPB
)
940 // TODO : vb pointer caching ?
941 // TODO : better vb reuse ?
942 /// choose the right vb by building an index for lookup into 'vbMaps' defined above
943 TVBMap
&map
= _VBMaps
[ (_BraceMode
? 8 : 0) | // set bit 3 if brace mode
944 (_Tex
!= NULL
? 4 : 0) | // set bit 2 if textured
945 (_ColorScheme
!= NULL
? 2 : 0) | // set bit 1 if per ribbon color
946 (_ColorFading
? 1 : 0) // set bit 0 if color fading
949 const uint numVerticesInSlice
= getNumVerticesInSlice(); /// 1 vertex added for textured ribbon (to avoid texture stretching)
950 const uint numVerticesInShape
= (uint
)_Shape
.size();
953 // The number of slice is encoded in the upper word of the vb index
954 // The number of vertices per slices is encoded in the lower word
955 uint VBnPDIndex
= ((_UsedNbSegs
+ 1) << 16) | numVerticesInSlice
;
956 TVBMap::iterator it
= map
.find(VBnPDIndex
);
961 else // must create this vb, with few different size, it is still interseting, though they are only destroyed at exit
963 const uint numRibbonInVB
= getNumRibbonsInVB();
964 CVBnPB
&VBnPB
= map
[VBnPDIndex
]; // make an entry
965 CIndexBuffer
&pb
= VBnPB
.PB
;
966 CVertexBuffer
&vb
= VBnPB
.VB
;
967 vb
.setPreferredMemory(CVertexBuffer::AGPVolatile
, true); // keep local memory because of interleaved format
968 /// set the vb format & size
969 /// In the case of a ribbon with color and fading, we encode the fading in a texture
970 /// If the ribbon has fading, but only a global color, we encode it in the primary color
971 vb
.setVertexFormat(CVertexBuffer::PositionFlag
| /* alway need position */
972 (_ColorScheme
|| _ColorFading
? CVertexBuffer::PrimaryColorFlag
: 0) | /* need a color ? */
973 ((_ColorScheme
&& _ColorFading
) || _Tex
!= NULL
? CVertexBuffer::TexCoord0Flag
: 0) | /* need texture coordinates ? */
974 (_Tex
!= NULL
&& _ColorScheme
&& _ColorFading
? CVertexBuffer::TexCoord1Flag
: 0) /* need 2nd texture coordinates ? */
976 vb
.setNumVertices((_UsedNbSegs
+ 1) * numRibbonInVB
* numVerticesInSlice
); // 1 seg = 1 line + terminal vertices
977 pb
.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT
);
978 // set the primitive block size
981 pb
.setNumIndexes(6 * _UsedNbSegs
* numRibbonInVB
* (uint32
)(_Shape
.size() / 2));
985 pb
.setNumIndexes(6 * _UsedNbSegs
* numRibbonInVB
* (uint32
)_Shape
.size());
988 CIndexBufferReadWrite ibaWrite
;
990 CVertexBufferReadWrite vba
;
992 /// Setup the pb and vb parts. Not very fast but executed only once
996 for (i
= 0; i
< numRibbonInVB
; ++i
)
998 for (k
= 0; k
< (_UsedNbSegs
+ 1); ++k
)
1001 /// setup primitive block
1002 if (k
!= _UsedNbSegs
) /// there are alway one more slice than segments in the ribbon...
1006 uint vIndex
= vbIndex
;
1007 for (l
= 0; l
< numVerticesInShape
/ 2; ++l
) /// deals with segment
1009 setTri(ibaWrite
, vb
, pbIndex
, vIndex
, vIndex
+ numVerticesInSlice
, vIndex
+ numVerticesInSlice
+ 1);
1011 setTri(ibaWrite
, vb
, pbIndex
, vIndex
, vIndex
+ numVerticesInSlice
+ 1, vIndex
+ 1);
1018 uint vIndex
= vbIndex
;
1019 for (l
= 0; l
< (numVerticesInShape
- 1); ++l
) /// deals with each ribbon vertices
1021 setTri(ibaWrite
, vb
, pbIndex
, vIndex
, vIndex
+ numVerticesInSlice
, vIndex
+ numVerticesInSlice
+ 1);
1023 setTri(ibaWrite
, vb
, pbIndex
, vIndex
, vIndex
+ numVerticesInSlice
+ 1, vIndex
+ 1);
1028 /// the last 2 index don't loop if there's a texture
1029 uint nextVertexIndex
= (numVerticesInShape
== numVerticesInSlice
) ? vIndex
+ 1 - numVerticesInShape
// no texture -> we loop
1030 : vIndex
+ 1; // a texture is used : use onemore vertex
1031 setTri(ibaWrite
, vb
, pbIndex
, vIndex
, vIndex
+ numVerticesInSlice
, nextVertexIndex
+ numVerticesInSlice
);
1033 setTri(ibaWrite
, vb
, pbIndex
, vIndex
, nextVertexIndex
+ numVerticesInSlice
, nextVertexIndex
);
1041 for (l
= 0; l
< numVerticesInSlice
/ 2; ++l
) /// deals with each ribbon vertices
1043 nlassert(vbIndex
< vb
.getNumVertices());
1044 /// setup texture (if any)
1047 vba
.setTexCoord(vbIndex
,
1048 _ColorScheme
&& _ColorFading
? 1 : 0, // must we use the second texture coord ? (when 1st one used by the gradient texture : we can't encode it in the diffuse as it encodes each ribbon color)
1049 (float) k
/ _UsedNbSegs
, // u
1052 vba
.setTexCoord(vbIndex
+ 1,
1053 _ColorScheme
&& _ColorFading
? 1 : 0,
1054 (float) k
/ _UsedNbSegs
,
1061 // If not per ribbon color, we can encode it in the diffuse
1062 if (_ColorScheme
== NULL
)
1064 uint8 intensity
= (uint8
) (255 * (1.f
- ((float) k
/ _UsedNbSegs
)));
1065 NLMISC::CRGBA
col(intensity
, intensity
, intensity
, intensity
);
1066 vba
.setColor(vbIndex
, col
);
1067 vba
.setColor(vbIndex
+ 1, col
);
1069 else // encode it in the first texture
1071 vba
.setTexCoord(vbIndex
, 0, 0.5f
- 0.5f
* ((float) k
/ _UsedNbSegs
), 0);
1072 vba
.setTexCoord(vbIndex
+ 1, 0, 0.5f
- 0.5f
* ((float) k
/ _UsedNbSegs
), 0);
1080 for (l
= 0; l
< numVerticesInSlice
; ++l
) /// deals with each ribbon vertices
1082 nlassert(vbIndex
< vb
.getNumVertices());
1083 /// setup texture (if any)
1086 vba
.setTexCoord(vbIndex
,
1087 _ColorScheme
&& _ColorFading
? 1 : 0, // must we use the second texture coord ? (when 1st one used by the gradient texture : we can't encode it in the diffuse as it encodes each ribbon color)
1088 (float) k
/ _UsedNbSegs
, // u
1089 1.f
- (l
/ (float) numVerticesInShape
) // v
1095 // If not per ribbon color, we can encode it in the diffuse
1096 if (_ColorScheme
== NULL
)
1098 uint8 intensity
= (uint8
) (255 * (1.f
- ((float) k
/ _UsedNbSegs
)));
1099 NLMISC::CRGBA
col(intensity
, intensity
, intensity
, intensity
);
1100 vba
.setColor(vbIndex
, col
);
1102 else // encode it in the first texture
1104 vba
.setTexCoord(vbIndex
, 0, 0.5f
- 0.5f
* ((float) k
/ _UsedNbSegs
), 0);
1116 //==========================================================================
1117 uint
CPSRibbon::getNumRibbonsInVB() const
1119 NL_PS_FUNC(CPSRibbon_getNumRibbonsInVB
)
1120 const uint numVerticesInSlice
= getNumVerticesInSlice(); /// 1 vertex added for textured ribbon (to avoid texture stretching)
1121 const uint vertexInVB
= 512;
1122 return std::max(1u, (uint
) (vertexInVB
/ (numVerticesInSlice
* (_UsedNbSegs
+ 1))));
1126 //==========================================================================
1127 inline void CPSRibbon::updateUntexturedMaterial()
1129 NL_PS_FUNC(CPSRibbon_updateUntexturedMaterial
)
1130 ///////////////////////
1131 // UNTEXTURED RIBBON //
1132 ///////////////////////
1134 static NLMISC::CRefPtr
<ITexture
> ptGradTexture
;
1136 CParticleSystem
&ps
= *(_Owner
->getOwner());
1138 { // PER RIBBON COLOR
1139 if (ps
.getForceGlobalColorLightingFlag() || usesGlobalColorLighting() || ps
.getColorAttenuationScheme() || ps
.isUserColorUsed())
1141 if (_ColorFading
) // global color + fading + per ribbon color
1143 // the first stage is used to get fading * global color
1144 // the second stage multiply the result by the diffuse colot
1145 if (ptGradTexture
== NULL
) // have we got a gradient texture ?
1147 ptGradTexture
= CreateGradientTexture();
1149 _Mat
.setTexture(0, ptGradTexture
);
1150 CPSMaterial::forceTexturedMaterialStages(2); // use constant color 0 * diffuse, 1 stage needed
1151 SetupModulatedStage(_Mat
, 0, CMaterial::Texture
, CMaterial::Constant
);
1152 SetupModulatedStage(_Mat
, 1, CMaterial::Previous
, CMaterial::Diffuse
);
1154 else // per ribbon color with global color
1156 CPSMaterial::forceTexturedMaterialStages(1); // use constant color 0 * diffuse, 1 stage needed
1157 SetupModulatedStage(_Mat
, 0, CMaterial::Diffuse
, CMaterial::Constant
);
1162 if (_ColorFading
) // per ribbon color, no fading
1164 if (ptGradTexture
== NULL
) // have we got a gradient texture ?
1166 ptGradTexture
= CreateGradientTexture();
1168 _Mat
.setTexture(0, ptGradTexture
);
1169 ptGradTexture
->setWrapS(ITexture::Clamp
);
1170 ptGradTexture
->setWrapT(ITexture::Clamp
);
1171 CPSMaterial::forceTexturedMaterialStages(1);
1172 SetupModulatedStage(_Mat
, 0, CMaterial::Texture
, CMaterial::Diffuse
);
1174 else // per color ribbon with no fading, and no global color
1176 CPSMaterial::forceTexturedMaterialStages(0); // no texture use constant diffuse only
1180 else // GLOBAL COLOR
1184 CPSMaterial::forceTexturedMaterialStages(1); // use constant color 0 * diffuse, 1 stage needed
1185 SetupModulatedStage(_Mat
, 0, CMaterial::Diffuse
, CMaterial::Constant
);
1187 else // color attenuation, no fading :
1189 CPSMaterial::forceTexturedMaterialStages(0); // no texture use constant diffuse only
1195 //==========================================================================
1196 inline void CPSRibbon::updateTexturedMaterial()
1198 NL_PS_FUNC(CPSRibbon_updateTexturedMaterial
)
1199 /////////////////////
1200 // TEXTURED RIBBON //
1201 /////////////////////
1204 //_Tex->setWrapS(ITexture::Clamp);
1205 //_Tex->setWrapT(ITexture::Clamp);
1207 static NLMISC::CRefPtr
<ITexture
> ptGradTexture
;
1208 CParticleSystem
&ps
= *(_Owner
->getOwner());
1210 { // PER RIBBON COLOR
1211 if (ps
.getForceGlobalColorLightingFlag() || usesGlobalColorLighting() || ps
.getColorAttenuationScheme() || ps
.isUserColorUsed())
1213 if (_ColorFading
) // global color + fading + per ribbon color
1215 if (ptGradTexture
== NULL
) // have we got a gradient texture ?
1217 ptGradTexture
= CreateGradientTexture(); // create it
1219 /// fading is stored in last stage (work only with 3 stages...)
1220 _Mat
.setTexture(0, ptGradTexture
);
1221 ptGradTexture
->setWrapS(ITexture::Clamp
);
1222 ptGradTexture
->setWrapT(ITexture::Clamp
);
1223 _Mat
.setTexture(1, _Tex
);
1224 CPSMaterial::forceTexturedMaterialStages(3); // use constant color 0 * diffuse, 1 stage needed
1225 SetupModulatedStage(_Mat
, 0, CMaterial::Texture
, CMaterial::Diffuse
);
1226 SetupModulatedStage(_Mat
, 1, CMaterial::Texture
, CMaterial::Previous
);
1227 SetupModulatedStage(_Mat
, 2, CMaterial::Previous
, CMaterial::Constant
);
1229 else // per ribbon color with global color
1231 _Mat
.setTexture(0, _Tex
);
1233 CPSMaterial::forceTexturedMaterialStages(2); // use constant color 0 * diffuse, 1 stage needed
1234 SetupModulatedStage(_Mat
, 0, CMaterial::Texture
, CMaterial::Diffuse
);
1235 SetupModulatedStage(_Mat
, 1, CMaterial::Previous
, CMaterial::Constant
);
1240 if (_ColorFading
) // per ribbon color, fading : 2 textures needed
1242 if (ptGradTexture
== NULL
) // have we got a gradient texture ?
1244 ptGradTexture
= CreateGradientTexture(); // create it
1246 _Mat
.setTexture(0, ptGradTexture
);
1247 ptGradTexture
->setWrapS(ITexture::Clamp
);
1248 ptGradTexture
->setWrapT(ITexture::Clamp
);
1249 _Mat
.setTexture(1, _Tex
);
1250 CPSMaterial::forceTexturedMaterialStages(2);
1251 SetupModulatedStage(_Mat
, 0, CMaterial::Texture
, CMaterial::Diffuse
); // texture * ribbon color
1252 SetupModulatedStage(_Mat
, 1, CMaterial::Texture
, CMaterial::Previous
); // * gradient
1254 else // per color ribbon with no fading, and no global color
1256 _Mat
.setTexture(0, _Tex
);
1257 CPSMaterial::forceTexturedMaterialStages(1); // no texture use constant diffuse only
1258 SetupModulatedStage(_Mat
, 0, CMaterial::Texture
, CMaterial::Diffuse
);
1262 else // GLOBAL COLOR
1265 if (_ColorFading
) // gradient is encoded in diffuse
1267 _Mat
.setTexture(0, _Tex
);
1268 CPSMaterial::forceTexturedMaterialStages(2); // use constant color 0 * diffuse, 1 stage needed
1269 SetupModulatedStage(_Mat
, 0, CMaterial::Texture
, CMaterial::Diffuse
);
1270 SetupModulatedStage(_Mat
, 1, CMaterial::Previous
, CMaterial::Constant
);
1272 else // constant color
1274 _Mat
.setTexture(0, _Tex
);
1275 CPSMaterial::forceTexturedMaterialStages(1); // no texture use constant diffuse only
1276 SetupModulatedStage(_Mat
, 0, CMaterial::Texture
, CMaterial::Diffuse
);
1282 //==========================================================================
1283 void CPSRibbon::updateMaterial()
1285 NL_PS_FUNC(CPSRibbon_updateMaterial
)
1286 if (!_Touch
) return;
1289 updateTexturedMaterial();
1290 setupTextureMatrix();
1294 updateUntexturedMaterial();
1300 //==========================================================================
1301 inline void CPSRibbon::setupUntexturedGlobalColor()
1303 NL_PS_FUNC(CPSRibbon_setupUntexturedGlobalColor
)
1304 /// setup the global color if it is used
1305 CParticleSystem
&ps
= *(_Owner
->getOwner());
1308 if (ps
.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
1310 _Mat
.texConstantColor(0, ps
.getGlobalColorLighted());
1314 _Mat
.texConstantColor(0, ps
.getGlobalColor());
1317 else // GLOBAL COLOR with / without fading
1320 if (ps
.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
1322 col
.modulateFromColor(ps
.getGlobalColorLighted(), _Color
);
1324 else if (ps
.getColorAttenuationScheme() || ps
.isUserColorUsed())
1326 col
.modulateFromColor(ps
.getGlobalColor(), _Color
);
1334 _Mat
.texConstantColor(0, col
);
1336 else // color attenuation, no fading :
1343 //==========================================================================
1344 inline void CPSRibbon::setupTexturedGlobalColor()
1346 NL_PS_FUNC(CPSRibbon_setupTexturedGlobalColor
)
1347 /// setup the global color if it is used
1348 CParticleSystem
&ps
= *(_Owner
->getOwner());
1351 if (ps
.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
1355 _Mat
.texConstantColor(2, ps
.getGlobalColorLighted());
1359 _Mat
.texConstantColor(1, ps
.getGlobalColorLighted());
1366 _Mat
.texConstantColor(2, ps
.getGlobalColor());
1370 _Mat
.texConstantColor(1, ps
.getGlobalColor());
1374 else // GLOBAL COLOR with / without fading
1376 if (ps
.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
1379 col
.modulateFromColor(ps
.getGlobalColorLighted(), _Color
);
1382 _Mat
.texConstantColor(1, col
);
1384 else // color attenuation, no fading :
1390 if (ps
.getColorAttenuationScheme() || ps
.isUserColorUsed())
1393 col
.modulateFromColor(ps
.getGlobalColor(), _Color
);
1396 _Mat
.texConstantColor(1, col
);
1398 else // color attenuation, no fading :
1407 _Mat
.texConstantColor(1, _Color
);
1409 else // constant color
1411 _Mat
.setColor(_Color
);
1418 //==========================================================================
1419 void CPSRibbon::setupGlobalColor()
1421 NL_PS_FUNC(CPSRibbon_setupGlobalColor
)
1422 if (_Tex
!= NULL
) setupTexturedGlobalColor();
1423 else setupUntexturedGlobalColor();
1426 //==========================================================================
1427 void CPSRibbon::setupTextureMatrix()
1429 NL_PS_FUNC(CPSRibbon_setupTextureMatrix
)
1430 uint stage
= (_ColorScheme
!= NULL
&& _ColorFading
== true) ? 1 : 0;
1431 if (_UFactor
!= 1.f
|| _VFactor
!= 1.f
)
1433 _Mat
.enableUserTexMat(stage
);
1435 texMat
.setRot(_UFactor
* NLMISC::CVector::I
,
1436 _VFactor
* NLMISC::CVector::J
,
1439 _Mat
.setUserTexMat(stage
, texMat
);
1443 _Mat
.enableUserTexMat(stage
, false);
1445 _Mat
.enableUserTexMat(1 - stage
, false);
1448 //==========================================================================
1449 ///==================================================================================================================
1450 void CPSRibbon::setShape(const CVector
*shape
, uint32 nbPointsInShape
, bool braceMode
)
1452 NL_PS_FUNC(CPSRibbon_setShape
)
1455 nlassert(nbPointsInShape
>= 3);
1459 nlassert(nbPointsInShape
>= 2);
1460 nlassert(!(nbPointsInShape
& 1)); // must be even
1462 _Shape
.resize(nbPointsInShape
);
1463 std::copy(shape
, shape
+ nbPointsInShape
, _Shape
.begin());
1464 _BraceMode
= braceMode
;
1467 ///==================================================================================================================
1468 void CPSRibbon::getShape(CVector
*shape
) const
1470 NL_PS_FUNC(CPSRibbon_getShape
);
1473 std::copy(_Shape
.begin(), _Shape
.end(), stdext::make_unchecked_array_iterator(shape
));
1475 std::copy(_Shape
.begin(), _Shape
.end(), shape
);
1479 ///==================================================================================================================
1480 void CPSRibbon::enumTexs(std::vector
<NLMISC::CSmartPtr
<ITexture
> > &dest
, IDriver
&drv
)
1482 NL_PS_FUNC(CPSRibbon_enumTexs
)
1485 dest
.push_back(_Tex
);