Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / ps_ribbon.cpp
blob499991b5e7ee889a025a298eadaaaf01d3367121
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_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"
27 #ifdef DEBUG_NEW
28 #define new DEBUG_NEW
29 #endif
31 namespace NL3D
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,
44 sizeof(GradientB2W),
45 false, /* dont delete */
46 false, /* not a file */
47 2, 1)
49 //tex->setWrapS(ITexture::Clamp);
50 tex->setShareName("#GradBW");
51 return tex.release();
55 ///////////////////////////
56 // ribbon implementation //
57 ///////////////////////////
59 // predifined shapes
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);
145 if (ver == 1)
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);
155 f.serialPtr(_Owner);
156 if (ver3 > 1) f.serialEnum(_LOD);
157 if (ver3 > 2) f.serial(_Name);
158 if (ver3 > 3)
160 if (f.isReading())
162 uint32 id;
163 f.serial(id);
164 setExternID(id);
166 else
168 f.serial(_ExternID);
172 if (ver2 >= 2)
174 bool bDisableAutoLOD;
175 f.serial(bDisableAutoLOD);
176 disableAutoLOD(bDisableAutoLOD);
179 uint32 tailNbSegs;
180 bool colorFading;
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);
194 serialMaterial(f);
196 f.serial(drEnabled);
197 f.serial(tailNbSegs);
198 ITexture *tex = NULL;
199 f.serialPolyPtr(tex);
200 _Tex = tex;
201 if (_Tex != NULL)
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);
214 nlassert(_Owner);
215 resize(_Owner->getMaxSize());
216 initDateVect();
217 resetFromOwner();
221 if (ver >= 2)
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);
233 if (f.isReading())
235 setTailNbSeg(_NbSegs);
236 touch();
238 ITexture *tex = _Tex;
239 f.serialPolyPtr(tex);
240 _Tex = tex;
241 if (_Tex != NULL)
243 f.serial(_UFactor, _VFactor) ;
247 if (ver >= 3)
249 bool braceMode = _BraceMode;
250 f.serial(braceMode);
251 _BraceMode = braceMode;
252 f.serialEnum(_Orientation);
255 if (f.isReading())
257 touch();
262 //=======================================================
263 CPSRibbon::CPSRibbon() : _UFactor(1.f),
264 _VFactor(1.f),
265 _Orientation(FollowPath),
266 _BraceMode(true),
267 _ColorFading(true),
268 _GlobalColor(false),
269 _Lighted(false),
270 _ForceLighted(false),
271 _Touch(true)
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)
293 if (_BraceMode)
295 return (uint)_Shape.size();
297 else
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)
313 if (!_Parametric)
315 updateGlobals();
318 else
319 if (
320 (pass == PSBlendRender && hasTransparentFaces())
321 || (pass == PSSolidRender && hasOpaqueFaces())
324 uint32 step;
325 uint numToProcess;
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());
335 else
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);
347 else
348 if (pass == PSToolRender) // edition mode only
350 //showTool();
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);
381 resizeColor(size);
382 resizeSize(size);
385 //=======================================================
386 void CPSRibbon::updateMatAndVbForColor(void)
388 NL_PS_FUNC(CPSRibbon_updateMatAndVbForColor)
389 touch();
393 ///=========================================================================
394 // Create the start slice of a ribbon (all vertices at the same pos)
395 static inline uint8 *BuildRibbonFirstSlice(const NLMISC::CVector &pos,
396 uint numVerts,
397 uint8 *dest,
398 uint vertexSize
401 NL_PS_FUNC(BuildRibbonFirstSlice)
404 * (NLMISC::CVector *) dest = pos;
405 dest += vertexSize;
407 while (--numVerts);
408 return dest;
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,
417 uint numVerts,
418 uint8 *dest,
419 uint vertexSize,
420 float size,
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);
435 basis.setPos(next);
437 const NLMISC::CVector *shapeEnd = shape + numVerts;
440 *(NLMISC::CVector *) dest = basis * (size * (*shape));
441 ++shape;
442 dest += vertexSize;
444 while (shape != shapeEnd);
445 return dest;
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,
453 uint numVerts,
454 uint8 *dest,
455 uint vertexSize,
456 float size
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);
466 ++shape;
467 dest += vertexSize;
469 while (shape != shapeEnd);
470 return dest;
473 ///=========================================================================
474 static inline uint8 *ComputeRibbonSliceFollowPathXY(const NLMISC::CVector &prev,
475 const NLMISC::CVector &next,
476 const NLMISC::CVector *shape,
477 uint numVerts,
478 uint8 *dest,
479 uint vertexSize,
480 float size,
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);
498 basis.setPos(next);
499 const NLMISC::CVector *shapeEnd = shape + numVerts;
502 *(NLMISC::CVector *) dest = basis * (size * (*shape));
503 ++shape;
504 dest += vertexSize;
506 while (shape != shapeEnd);
507 return dest;
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,
517 uint vertexSize,
518 const NLMISC::CVector *curve,
519 const NLMISC::CVector *shape,
520 uint numSegs,
521 uint numVerticesInShape,
522 float sizeIncrement,
523 float size,
524 CPSRibbon::TOrientation orientation
527 NL_PS_FUNC(ComputeUntexturedRibbonMesh)
528 CMatrix basis;
529 basis.scale(0);
530 switch(orientation)
532 case CPSRibbon::FollowPath:
535 destVb = ComputeRibbonSliceFollowPath(curve[1],
536 curve[0],
537 shape,
538 numVerticesInShape,
539 destVb,
540 vertexSize,
541 size,
542 basis);
543 ++ curve;
544 size -= sizeIncrement;
546 while (--numSegs);
547 break;
548 case CPSRibbon::FollowPathXY:
551 destVb = ComputeRibbonSliceFollowPathXY(curve[1],
552 curve[0],
553 shape,
554 numVerticesInShape,
555 destVb,
556 vertexSize,
557 size,
558 basis);
559 ++ curve;
560 size -= sizeIncrement;
562 while (--numSegs);
563 break;
564 case CPSRibbon::Identity:
567 destVb = ComputeRibbonSliceIdentity(curve[1],
568 curve[0],
569 shape,
570 numVerticesInShape,
571 destVb,
572 vertexSize,
573 size
575 ++ curve;
576 size -= sizeIncrement;
578 while (--numSegs);
579 break;
580 default:
581 nlassert(0);
582 break;
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,
591 uint vertexSize,
592 const NLMISC::CVector *curve,
593 const NLMISC::CVector *shape,
594 uint numSegs,
595 uint numVerticesInShape,
596 float sizeIncrement,
597 float size,
598 CPSRibbon::TOrientation orientation
601 NL_PS_FUNC(ComputeTexturedRibbonMesh)
602 CMatrix basis;
603 basis.scale(0);
604 switch(orientation)
606 case CPSRibbon::FollowPath:
609 uint8 *nextDestVb = ComputeRibbonSliceFollowPath(curve[1],
610 curve[0],
611 shape,
612 numVerticesInShape,
613 destVb,
614 vertexSize,
615 size,
616 basis
618 // duplicate last vertex ( equal first)
619 * (NLMISC::CVector *) nextDestVb = * (NLMISC::CVector *) destVb;
620 destVb = nextDestVb + vertexSize;
622 ++ curve;
623 size -= sizeIncrement;
625 while (--numSegs);
626 break;
627 case CPSRibbon::FollowPathXY:
630 uint8 *nextDestVb = ComputeRibbonSliceFollowPathXY(curve[1],
631 curve[0],
632 shape,
633 numVerticesInShape,
634 destVb,
635 vertexSize,
636 size,
637 basis
639 // duplicate last vertex ( equal first)
640 * (NLMISC::CVector *) nextDestVb = * (NLMISC::CVector *) destVb;
641 destVb = nextDestVb + vertexSize;
643 ++ curve;
644 size -= sizeIncrement;
646 while (--numSegs);
647 break;
648 case CPSRibbon::Identity:
651 uint8 *nextDestVb = ComputeRibbonSliceIdentity(curve[1],
652 curve[0],
653 shape,
654 numVerticesInShape,
655 destVb,
656 vertexSize,
657 size
659 // duplicate last vertex ( equal first)
660 * (NLMISC::CVector *) nextDestVb = * (NLMISC::CVector *) destVb;
661 destVb = nextDestVb + vertexSize;
663 ++ curve;
664 size -= sizeIncrement;
666 while (--numSegs);
667 break;
668 default:
669 nlassert(0);
670 break;
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;
681 nlassert(_Owner);
682 CPSRibbonBase::updateLOD();
683 if (_UsedNbSegs < 2) return;
684 const float date = _Owner->getOwner()->getSystemDate();
685 uint8 *currVert;
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();
690 uint colorOffset=0;
692 IDriver *drv = this->getDriver();
693 #ifdef NL_DEBUG
694 nlassert(drv);
695 #endif
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;
700 ////////////////////
701 // Material setup //
702 ////////////////////
703 CParticleSystem &ps = *(_Owner->getOwner());
704 bool useGlobalColor = ps.getColorAttenuationScheme() != NULL || ps.isUserColorUsed();
705 if (useGlobalColor != _GlobalColor)
707 _GlobalColor = useGlobalColor;
708 touch();
710 if (usesGlobalColorLighting() != _Lighted)
712 _Lighted = usesGlobalColorLighting();
713 touch();
715 if (ps.getForceGlobalColorLightingFlag() != _ForceLighted)
717 _ForceLighted = ps.getForceGlobalColorLightingFlag();
718 touch();
720 updateMaterial();
721 setupGlobalColor();
723 if (_ColorScheme)
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);
739 uint toProcess;
740 uint ribbonIndex = 0; // index of the first ribbon in the batch being processed
741 uint32 fpRibbonIndex = 0; // fixed point index in source
742 if (_ColorScheme)
744 _ColorScheme->setColorType(drv->getVertexColorFormat());
748 toProcess = std::min((uint) (nbRibbons - ribbonIndex) , numRibbonBatch);
749 VB.setNumVertices((_UsedNbSegs + 1) * toProcess * numVerticesInSlice);
751 CVertexBufferReadWrite vba;
752 VB.lock(vba);
753 currVert = (uint8 *) vba.getVertexCoordPointer();
754 /// setup sizes
755 const float *ptCurrSize;
756 uint32 ptCurrSizeIncrement;
757 if (_SizeScheme)
759 ptCurrSize = (float *) _SizeScheme->make(this->_Owner, ribbonIndex, &sizes[0], sizeof(float), toProcess, true, srcStep);
760 ptCurrSizeIncrement = 1;
762 else
764 ptCurrSize = &_ParticleSize;
765 ptCurrSizeIncrement = 0;
768 /// compute colors
769 if (_ColorScheme)
771 _ColorScheme->makeN(this->_Owner, ribbonIndex, currVert + colorOffset, vertexSize, toProcess, numVerticesInSlice * (_UsedNbSegs + 1), srcStep);
773 uint k = toProcess;
774 //////////////////////////////////////////////////////////////////////////////////////
775 // interpolate and project points the result is directly setup in the vertex buffer //
776 //////////////////////////////////////////////////////////////////////////////////////
777 if (!_Parametric)
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,
791 vertexSize,
792 &ribbonPos[0],
793 &_Shape[0],
794 _UsedNbSegs,
795 numVerticesInShape,
796 ribbonSizeIncrement,
797 *ptCurrSize,
798 _Orientation
800 fpRibbonIndex += srcStep;
802 while (--k);
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,
813 vertexSize,
814 &ribbonPos[0],
815 &_Shape[0],
816 _UsedNbSegs,
817 numVerticesInShape,
818 ribbonSizeIncrement,
819 *ptCurrSize,
820 _Orientation
822 fpRibbonIndex += srcStep;
824 while (--k);
827 else
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),
839 _UsedSegDuration,
840 _UsedNbSegs + 1,
841 (uint) (fpRibbonIndex >> 16),
842 &ribbonPos[0]);
844 currVert = ComputeTexturedRibbonMesh(currVert,
845 vertexSize,
846 &ribbonPos[0],
847 &_Shape[0],
848 _UsedNbSegs,
849 numVerticesInShape,
850 ribbonSizeIncrement,
851 *ptCurrSize,
852 _Orientation
854 fpRibbonIndex += srcStep;
856 while (--k);
858 else // untextured case
862 const float ribbonSizeIncrement = *ptCurrSize / (float) _UsedNbSegs;
863 ptCurrSize += ptCurrSizeIncrement;
864 _Owner->integrateSingle(date - _UsedSegDuration * (_UsedNbSegs + 1),
865 _UsedSegDuration,
866 _UsedNbSegs + 1,
867 (uint) (fpRibbonIndex >> 16),
868 &ribbonPos[0]);
870 currVert = ComputeUntexturedRibbonMesh(currVert,
871 vertexSize,
872 &ribbonPos[0],
873 &_Shape[0],
874 _UsedNbSegs,
875 numVerticesInShape,
876 ribbonSizeIncrement,
877 *ptCurrSize,
878 _Orientation
880 fpRibbonIndex += srcStep;
882 while (--k);
886 // display the result
887 uint numTri = numVerticesInShape * _UsedNbSegs * toProcess;
888 if (!_BraceMode)
890 numTri <<= 1;
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)
921 nlassert(_Owner);
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);
957 if (it != map.end())
959 return it->second;
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
979 if (_BraceMode)
981 pb.setNumIndexes(6 * _UsedNbSegs * numRibbonInVB * (uint32)(_Shape.size() / 2));
983 else
985 pb.setNumIndexes(6 * _UsedNbSegs * numRibbonInVB * (uint32)_Shape.size());
988 CIndexBufferReadWrite ibaWrite;
989 pb.lock (ibaWrite);
990 CVertexBufferReadWrite vba;
991 vb.lock(vba);
992 /// Setup the pb and vb parts. Not very fast but executed only once
993 uint vbIndex = 0;
994 uint pbIndex = 0;
995 uint i, k, l;
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...
1004 if (_BraceMode)
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);
1010 pbIndex+=3;
1011 setTri(ibaWrite, vb, pbIndex, vIndex, vIndex + numVerticesInSlice + 1, vIndex + 1);
1012 pbIndex+=3;
1013 vIndex += 2;
1016 else
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);
1022 pbIndex+=3;
1023 setTri(ibaWrite, vb, pbIndex, vIndex, vIndex + numVerticesInSlice + 1, vIndex + 1);
1024 pbIndex+=3;
1025 ++ vIndex;
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);
1032 pbIndex+=3;
1033 setTri(ibaWrite, vb, pbIndex, vIndex, nextVertexIndex + numVerticesInSlice, nextVertexIndex);
1034 pbIndex+=3;
1038 /// setup vb
1039 if (_BraceMode)
1041 for (l = 0; l < numVerticesInSlice / 2; ++l) /// deals with each ribbon vertices
1043 nlassert(vbIndex < vb.getNumVertices());
1044 /// setup texture (if any)
1045 if (_Tex != NULL)
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
1050 0.f // v
1052 vba.setTexCoord(vbIndex + 1,
1053 _ColorScheme && _ColorFading ? 1 : 0,
1054 (float) k / _UsedNbSegs,
1058 /// setup gradient
1059 if (_ColorFading)
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);
1075 vbIndex += 2;
1078 else
1080 for (l = 0; l < numVerticesInSlice; ++l) /// deals with each ribbon vertices
1082 nlassert(vbIndex < vb.getNumVertices());
1083 /// setup texture (if any)
1084 if (_Tex != NULL)
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
1092 /// setup gradient
1093 if (_ColorFading)
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);
1107 ++ vbIndex;
1112 return VBnPB;
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());
1137 if (_ColorScheme)
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);
1160 else
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
1182 if (_ColorFading)
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
1192 _Touch = false;
1195 //==========================================================================
1196 inline void CPSRibbon::updateTexturedMaterial()
1198 NL_PS_FUNC(CPSRibbon_updateTexturedMaterial)
1199 /////////////////////
1200 // TEXTURED RIBBON //
1201 /////////////////////
1202 if (_Tex)
1204 //_Tex->setWrapS(ITexture::Clamp);
1205 //_Tex->setWrapT(ITexture::Clamp);
1207 static NLMISC::CRefPtr<ITexture> ptGradTexture;
1208 CParticleSystem &ps = *(_Owner->getOwner());
1209 if (_ColorScheme)
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);
1238 else
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);
1279 _Touch = false;
1282 //==========================================================================
1283 void CPSRibbon::updateMaterial()
1285 NL_PS_FUNC(CPSRibbon_updateMaterial)
1286 if (!_Touch) return;
1287 if (_Tex != NULL)
1289 updateTexturedMaterial();
1290 setupTextureMatrix();
1292 else
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());
1306 if (_ColorScheme)
1308 if (ps.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
1310 _Mat.texConstantColor(0, ps.getGlobalColorLighted());
1312 else
1314 _Mat.texConstantColor(0, ps.getGlobalColor());
1317 else // GLOBAL COLOR with / without fading
1319 NLMISC::CRGBA col;
1320 if (ps.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
1322 col.modulateFromColor(ps.getGlobalColorLighted(), _Color);
1324 else if (ps.getColorAttenuationScheme() || ps.isUserColorUsed())
1326 col.modulateFromColor(ps.getGlobalColor(), _Color);
1328 else
1330 col = _Color;
1332 if (_ColorFading)
1334 _Mat.texConstantColor(0, col);
1336 else // color attenuation, no fading :
1338 _Mat.setColor(col);
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());
1349 if (_ColorScheme)
1351 if (ps.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
1353 if (_ColorFading)
1355 _Mat.texConstantColor(2, ps.getGlobalColorLighted());
1357 else
1359 _Mat.texConstantColor(1, ps.getGlobalColorLighted());
1362 else
1364 if (_ColorFading)
1366 _Mat.texConstantColor(2, ps.getGlobalColor());
1368 else
1370 _Mat.texConstantColor(1, ps.getGlobalColor());
1374 else // GLOBAL COLOR with / without fading
1376 if (ps.getForceGlobalColorLightingFlag() || usesGlobalColorLighting())
1378 NLMISC::CRGBA col;
1379 col.modulateFromColor(ps.getGlobalColorLighted(), _Color);
1380 if (_ColorFading)
1382 _Mat.texConstantColor(1, col);
1384 else // color attenuation, no fading :
1386 _Mat.setColor(col);
1389 else
1390 if (ps.getColorAttenuationScheme() || ps.isUserColorUsed())
1392 NLMISC::CRGBA col;
1393 col.modulateFromColor(ps.getGlobalColor(), _Color);
1394 if (_ColorFading)
1396 _Mat.texConstantColor(1, col);
1398 else // color attenuation, no fading :
1400 _Mat.setColor(col);
1403 else
1405 if (_ColorFading)
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);
1434 CMatrix texMat;
1435 texMat.setRot(_UFactor * NLMISC::CVector::I,
1436 _VFactor * NLMISC::CVector::J,
1437 NLMISC::CVector::K
1439 _Mat.setUserTexMat(stage, texMat);
1441 else
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)
1453 if (!braceMode)
1455 nlassert(nbPointsInShape >= 3);
1457 else
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);
1472 #ifdef NL_COMP_VC14
1473 std::copy(_Shape.begin(), _Shape.end(), stdext::make_unchecked_array_iterator(shape));
1474 #else
1475 std::copy(_Shape.begin(), _Shape.end(), shape);
1476 #endif
1479 ///==================================================================================================================
1480 void CPSRibbon::enumTexs(std::vector<NLMISC::CSmartPtr<ITexture> > &dest, IDriver &drv)
1482 NL_PS_FUNC(CPSRibbon_enumTexs)
1483 if (_Tex)
1485 dest.push_back(_Tex);
1490 } // NL3D