Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / 3d / ps_ribbon_look_at.cpp
blobd7c646fc9fb7cb25d481afe7af88825d83d57b33
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_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"
25 #ifdef DEBUG_NEW
26 #define new DEBUG_NEW
27 #endif
29 namespace NL3D
32 ////////////////////////////////////
33 // CPSRibbonLookAt implementation //
34 ////////////////////////////////////
36 const float ZEpsilon = 10E-3f;
37 const float NormEpsilon = 10E-8f;
40 struct CVectInfo
42 NLMISC::CVector Interp;
43 NLMISC::CVector Proj;
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);
71 if (ver > 3)
73 CPSRibbonBase::serial(f);
75 else
77 CPSParticle::serial(f);
79 CPSColoredParticle::serialColorScheme(f);
80 CPSSizedParticle::serialSizeScheme(f);
81 serialMaterial(f);
82 uint32 dummy = 0; /* _NbDyingRibbons */
83 if (ver <= 3)
85 f.serial(_SegDuration, _NbSegs, dummy /*_NbDyingRibbons*/);
87 ITexture *tex = NULL;
89 if (ver > 2)
91 f.serial(_Parametric);
95 if (!f.isReading())
97 tex = _Tex;
98 f.serialPolyPtr(tex);
100 else
102 f.serialPolyPtr(tex);
103 setTexture(tex);
104 _Tex = tex;
105 if (_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)
119 _Tex = tex;
120 if (_Tex)
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)
135 if (!_Parametric)
137 updateGlobals();
140 else
141 if (
142 (pass == PSBlendRender && hasTransparentFaces())
143 || (pass == PSSolidRender && hasOpaqueFaces())
146 uint32 step;
147 uint numToProcess;
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());
157 else
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);
166 else
167 if (pass == PSToolRender) // edition mode only
169 //showTool();
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);
200 resizeColor(size);
201 resizeSize(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)
215 NL_PS_FUNC(MakeProj)
216 if (fabsf(src.y) > NormEpsilon * NormEpsilon)
218 dest.x = src.x / src.y;
219 dest.z = src.z / src.y;
220 dest.y = 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,
230 float ribSize)
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
239 float tgNorm;
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;
244 tangent.y = 0;
245 tgNorm = tangent.norm();
246 if (fabs(tgNorm) > 10E-8)
248 invTgNorm = 1.f / tgNorm;
250 else
252 invTgNorm = 1.f;
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);
269 else //
271 *(NLMISC::CVector *) currVert = pos->Interp;
272 *(NLMISC::CVector *) (currVert + vertexSize) = pos->Interp;
273 return;
276 tangent = tInter - prev->Proj;
277 tangent.y = 0;
279 tgNorm = tangent.norm();
280 if (fabs(tgNorm) > 10E-8)
282 invTgNorm = 1.f / tgNorm;
284 else
286 invTgNorm = 1.f;
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);
304 else //
306 *(NLMISC::CVector *) currVert = pos->Interp;
307 *(NLMISC::CVector *) (currVert + vertexSize) = pos->Interp;
308 return;
311 tangent = next->Proj - tInter;
312 tangent.y = 0;
313 tgNorm = tangent.norm();
314 if (fabs(tgNorm) > 10E-8)
316 invTgNorm = 1.f / tgNorm;
318 else
320 invTgNorm = 1.f;
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;
343 nlassert(_Owner);
344 CPSRibbonBase::updateLOD();
345 if (_UsedNbSegs < 2) return;
346 const float date = _Owner->getOwner()->getSystemDate();
347 uint8 *currVert;
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();
352 uint colorOffset=0;
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();
359 #ifdef NL_DEBUG
360 nlassert(drv);
361 #endif
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());
381 else
382 if (ps.getColorAttenuationScheme() != NULL || ps.isUserColorUsed())
384 CPSMaterial::forceModulateConstantColor(true, ps.getGlobalColor());
386 else
388 forceModulateConstantColor(false);
389 _Mat.setColor(ps.getGlobalColor());
392 if (_ColorScheme)
394 colorOffset = VB.getColorOff();
395 colors.resize(numRibbonBatch);
400 uint toProcess;
401 uint ribbonIndex = 0; // index of the first ribbon in the batch being processed
402 uint32 fpRibbonIndex = 0;
403 if (_ColorScheme)
405 _ColorScheme->setColorType(drv->getVertexColorFormat());
409 toProcess = std::min((uint) (nbRibbons - ribbonIndex) /* = left to do */, numRibbonBatch);
410 /// setup sizes
411 const float *ptCurrSize;
412 uint32 ptCurrSizeIncrement;
413 if (_SizeScheme)
415 ptCurrSize = (float *) _SizeScheme->make(this->_Owner, ribbonIndex, &sizes[0], sizeof(float), toProcess, true, srcStep);
416 ptCurrSizeIncrement = 1;
418 else
420 ptCurrSize = &_ParticleSize;
421 ptCurrSizeIncrement = 0;
425 /// setup colors
426 NLMISC::CRGBA *ptCurrColor=0;
427 if (_ColorScheme)
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;
435 VB.lock (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 ////////////////////////////////////
446 if (!_Parametric)
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);
458 ++rIt;
460 while (rIt != rItEnd);
462 else
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);
474 ++rIt;
476 while (rIt != rItEnd);
479 rIt = currRibbon.begin();
482 // setup colors
483 if (_ColorScheme)
485 uint8 *currColVertex = currVert + colorOffset;
486 uint colCount = (_UsedNbSegs + 1) << 1;
489 * (CRGBA *) currColVertex = *ptCurrColor;
490 currColVertex += vertexSize;
492 while (--colCount);
494 ++ptCurrColor;
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;
501 ++rIt;
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);
509 // next position
510 ++rIt;
511 if (rIt == rItEndMinusOne) break;
512 // next vertex
513 currVert += vertexSizeX2;
515 currVert += vertexSizeX2;
516 // last point.
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)
554 nlassert(_Owner);
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);
567 if (it != map.end())
569 return it->second;
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;
584 vb.lock (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;
591 pb.lock (iba);
592 /// Setup the pb and vb parts. Not very fast but executed only once
593 uint vbIndex = 0;
594 uint pbIndex = 0;
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);
606 pbIndex+=6;
608 vbIndex += 2;
611 return VBnPB;
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)
628 if (_Tex)
630 dest.push_back(_Tex);
631 _Tex->getShareName();
636 } // NL3D