Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / seg_remanence.cpp
blobbe0b0d0cf1dfea75249c7d8c578974ad63d3ca64
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/scene_group.h"
20 #include "nel/3d/seg_remanence.h"
21 #include "nel/3d/seg_remanence_shape.h"
22 #include "nel/3d/vertex_buffer.h"
23 #include "nel/3d/driver.h"
24 #include "nel/3d/scene.h"
25 #include "nel/3d/anim_detail_trav.h"
26 #include "nel/3d/skeleton_model.h"
27 #include "nel/3d/dru.h"
32 #ifdef DEBUG_NEW
33 #define new DEBUG_NEW
34 #endif
36 namespace NL3D
40 CVertexBuffer CSegRemanence::_VB;
41 CIndexBuffer CSegRemanence::_IB;
44 /// TODO : put this in a header (same code in ps_ribbon_base.cpp ..)
45 static inline void BuildHermiteVector(const NLMISC::CVector &P0,
46 const NLMISC::CVector &P1,
47 const NLMISC::CVector &T0,
48 const NLMISC::CVector &T1,
49 NLMISC::CVector &dest,
50 float lambda
53 const float lambda2 = lambda * lambda;
54 const float lambda3 = lambda2 * lambda;
55 const float h1 = 2 * lambda3 - 3 * lambda2 + 1;
56 const float h2 = - 2 * lambda3 + 3 * lambda2;
57 const float h3 = lambda3 - 2 * lambda2 + lambda;
58 const float h4 = lambda3 - lambda2;
59 /// just avoid some ctor calls here...
60 dest.set (h1 * P0.x + h2 * P1.x + h3 * T0.x + h4 * T1.x,
61 h1 * P0.y + h2 * P1.y + h3 * T0.y + h4 * T1.y,
62 h1 * P0.z + h2 * P1.z + h3 * T0.z + h4 * T1.z);
67 /// for test
68 static inline void BuildLinearVector(const NLMISC::CVector &P0,
69 const NLMISC::CVector &P1,
70 NLMISC::CVector &dest,
71 float lambda,
72 float oneMinusLambda
75 NL_PS_FUNC(BuildLinearVector)
76 dest.set (lambda * P1.x + oneMinusLambda * P0.x,
77 lambda * P1.y + oneMinusLambda * P0.y,
78 lambda * P1.z + oneMinusLambda * P0.z);
83 //===============================================================
84 CSegRemanence::CSegRemanence() : _NumSlices(0),
85 _Started(false),
86 _Stopping(false),
87 _Restarted(false),
88 _UnrollRatio(0),
89 _SliceTime(0.05f),
90 _AniMat(NULL),
91 _LastSampleFrame(0)
93 IAnimatable::resize(AnimValueLast);
95 // RenderFilter: We are a SegRemanece
96 _RenderFilterType= UScene::FilterSegRemanence;
99 //===============================================================
100 CSegRemanence::~CSegRemanence()
102 delete _AniMat;
103 // Auto detach me from skeleton. Must do it here, not in ~CTransform().
104 if(_FatherSkeletonModel)
106 // detach me from the skeleton.
107 // hrc and clip hierarchy is modified.
108 _FatherSkeletonModel->detachSkeletonSon(this);
109 nlassert(_FatherSkeletonModel==NULL);
113 //===============================================================
114 CSegRemanence::CSegRemanence(CSegRemanence &other) : CTransformShape(other), _AniMat(NULL)
116 copyFromOther(other);
119 //===============================================================
120 CSegRemanence &CSegRemanence::operator = (CSegRemanence &other)
122 if (this != &other)
124 (CTransformShape &) *this = (CTransformShape &) other; // copy base
125 copyFromOther(other);
127 return *this;
130 //===============================================================
131 void CSegRemanence::copyFromOther(CSegRemanence &other)
133 if (this == &other) return;
135 CAnimatedMaterial *otherMat = other._AniMat != NULL ? new CAnimatedMaterial(*other._AniMat)
136 : NULL;
137 delete _AniMat;
138 _AniMat = otherMat;
139 std::copy(other._Samples, other._Samples + 4, _Samples);
140 _HeadSample = other._HeadSample;
141 _HeadProgress = other._HeadProgress;
143 _Pos = other._Pos; // sampled positions at each extremities of segment
144 _NumSlices = other._NumSlices;
145 _NumCorners = other._NumCorners;
146 _Started = other._Started;
147 _Stopping = other._Stopping;
148 _Restarted = other._Restarted;
149 _StartDate = other._StartDate;
150 _CurrDate = other._CurrDate;
151 _UnrollRatio = other._UnrollRatio;
152 _SliceTime = other._SliceTime;
153 _LastSampleFrame = other._LastSampleFrame;
156 //===============================================================
157 void CSegRemanence::registerBasic()
159 CScene::registerModel(SegRemanenceShapeId, TransformShapeId, CSegRemanence::creator);
164 // helper functions to fill vb
165 static inline void vbPush(uint8 *&dest, const CVector &v)
167 *(CVector *) dest = v;
168 dest +=sizeof(CVector);
171 static inline void vbPush(uint8 *&dest, float f)
173 *(float *) dest = f;
174 dest +=sizeof(float);
177 //===============================================================
178 void CSegRemanence::render(IDriver *drv, CMaterial &mat)
180 nlassert(_NumSlices >= 2);
181 nlassert(_NumCorners >= 2);
182 CSegRemanenceShape *srs = NLMISC::safe_cast<CSegRemanenceShape *>((IShape *) Shape);
183 // resize before locking because of volatile vb
184 _VB.setPreferredMemory(CVertexBuffer::AGPVolatile, false);
185 _VB.setVertexFormat(CVertexBuffer::PositionFlag|CVertexBuffer::TexCoord0Flag);
186 _VB.setNumVertices(_NumCorners * (_NumSlices + 1));
187 const uint vertexSize = _VB.getVertexSize();
188 // Fill Vertex Buffer part
190 CVertexBufferReadWrite vba;
191 _VB.lock (vba);
192 uint8 *datas = (uint8 *) vba.getVertexCoordPointer();
193 const uint8 *endDatas = datas + vertexSize *_VB.getNumVertices();
195 const CVector *src = &_Pos[0];
197 float deltaV = 1.f / (_NumCorners - 1);
198 // first slice
199 for(uint k = 0; k < _NumCorners; ++k)
201 vbPush(datas, *src++);
202 vbPush(datas, 0.f); // U
203 vbPush(datas, k * deltaV); // V
204 nlassert(datas <= endDatas);
207 float deltaU = 1.f / _NumSlices;
208 float baseU = _HeadProgress * deltaU;
210 for (uint l = 1; l < _NumSlices; ++l)
212 float currU = baseU + (l - 1) * deltaU;
213 for(uint k = 0; k < _NumCorners; ++k)
215 vbPush(datas, *src++);
216 vbPush(datas, currU); // U
217 vbPush(datas, k * deltaV); // V
218 nlassert(datas <= endDatas);
221 // last slice
222 const CVector *prevRow = src - _NumCorners;
223 for(uint k = 0; k < _NumCorners; ++k)
225 vbPush(datas, (1.f - _HeadProgress) * *src + _HeadProgress * *prevRow);
226 ++ src;
227 ++ prevRow;
228 vbPush(datas, 1.f); // U
229 vbPush(datas, k * deltaV); // V
230 nlassert(datas <= endDatas);
234 uint numQuads = (_NumCorners - 1) * _NumSlices;
235 // Fill Index Buffer part
237 _IB.setPreferredMemory(CIndexBuffer::RAMVolatile, false);
238 _IB.setFormat(CIndexBuffer::Indices16);
239 _IB.setNumIndexes(numQuads * 6);
241 CIndexBufferReadWrite iba;
242 _IB.lock(iba);
243 uint16 *indexPtr = (uint16 *) iba.getPtr();
245 for (uint l = 0; l < _NumSlices; ++l)
247 for(uint k = 0; k < (_NumCorners - 1); ++k)
249 *indexPtr++ = l * _NumCorners + k;
250 *indexPtr++ = l * _NumCorners + k + 1;
251 *indexPtr++ = (l + 1) * _NumCorners + k;
253 *indexPtr++ = l * _NumCorners + k + 1;
254 *indexPtr++ = (l + 1) * _NumCorners + k + 1;
255 *indexPtr++ = (l + 1) * _NumCorners + k;
258 nlassert(indexPtr == (uint16 *) iba.getPtr() + _IB.getNumIndexes());
262 // roll / unroll using texture matrix
263 CMatrix texMat;
264 texMat.setPos(NLMISC::CVector(1.f - _UnrollRatio, 0, 0));
266 if (mat.getTexture(0) != NULL)
267 mat.setUserTexMat(0, texMat);
268 drv->setupModelMatrix(CMatrix::Identity);
270 drv->activeVertexBuffer(_VB);
271 drv->activeIndexBuffer(_IB);
272 drv->renderTriangles(mat, 0, numQuads * 2);
274 // draw wire frame version if needed
275 #ifdef DEBUG_SEG_REMANENCE_DISPLAY
276 static CMaterial unlitWF;
277 unlitWF.initUnlit();
278 unlitWF.setDoubleSided(true);
279 IDriver::TPolygonMode oldPM = drv->getPolygonMode();
280 drv->setPolygonMode(IDriver::Line);
281 drv->renderTriangles(unlitWF, 0, numQuads * 2);
282 drv->setPolygonMode(oldPM);
283 #endif
285 CScene *scene = getOwnerScene();
286 // change unroll ratio
287 if (!_Stopping)
289 if (_UnrollRatio != 1.f)
290 _UnrollRatio = std::min(1.f, _UnrollRatio + scene->getEllapsedTime() / (srs->getNumSlices() * _SliceTime));
292 else
294 _UnrollRatio = std::max(0.f, _UnrollRatio - srs->getRollupRatio() * scene->getEllapsedTime() / (srs->getNumSlices() * _SliceTime));
295 if (_UnrollRatio == 0.f)
297 _Stopping = false;
298 _Started = false;
303 //===============================================================
304 void CSegRemanence::setupFromShape()
306 CSegRemanenceShape *srs = NLMISC::safe_cast<CSegRemanenceShape *>((IShape *) Shape);
307 if (srs->getNumCorners() != _NumCorners || srs->getNumSlices() != _NumSlices)
309 _NumCorners = srs->getNumCorners();
310 _NumSlices = srs->getNumSlices();
311 _Pos.resize(_NumCorners * (_NumSlices + 1));
312 for(uint k = 0; k < 4; ++k)
314 _Samples[k].Pos.resize(_NumSlices + 1);
317 updateOpacityFromShape();
320 //===============================================================
321 void CSegRemanence::samplePos(double date)
323 uint newHeadSample = (uint) floor(date / _SliceTime);
324 double sliceElapsedTime = date - (newHeadSample * _SliceTime);
325 _HeadProgress = (float) (sliceElapsedTime / _SliceTime);
326 NLMISC::clamp(_HeadProgress, 0.f, 1.f);
327 uint offset = newHeadSample - _HeadSample; // number of samples to remove
328 if(!_Restarted)
330 if (offset > 0)
332 offset = std::min(offset, (uint) _NumSlices);
333 _Samples[0].swap(_Samples[1]);
334 _Samples[1].swap(_Samples[2]);
335 _Samples[2].swap(_Samples[3]);
336 if (offset < _NumSlices + 1)
338 // make room for new position
339 memmove(&_Pos[_NumCorners * offset], &_Pos[0], sizeof(_Pos[0]) * _NumCorners * (_NumSlices + 1 - offset));
341 // else, too much time ellapsed, are sampled pos are invalidated
342 _HeadSample = newHeadSample;
345 _Samples[3].Date = date;
347 CSegRemanenceShape *srs = NLMISC::safe_cast<CSegRemanenceShape *>((IShape *) Shape);
348 // update positions for sample head
349 for(uint k = 0; k <_NumCorners;++k)
351 _Samples[3].Pos[k] = getWorldMatrix() * srs->getCorner(k);
353 if (_Restarted)
355 _HeadSample = newHeadSample;
356 _Samples[0] = _Samples[1] = _Samples[2] = _Samples[3];
357 CVector *head = &_Pos[0];
358 for(uint l = 0; l < _NumSlices + 1; ++l)
360 for(uint k = 0; k < _NumCorners;++k)
362 *head++ = _Samples[0].Pos[k];
365 _Restarted = false;
366 return;
368 // update head pos
369 CVector *head = &_Pos[0];
370 CVector *endPtr = head + _NumCorners * (_NumSlices + 1);
371 for(uint k = 0; k < _NumCorners;++k)
373 *head++ = _Samples[3].Pos[k];
375 // update current positions from sample pos
376 double currDate = _Samples[3].Date - sliceElapsedTime;
377 // interpolate linearly for 2 firstsamples
378 while (currDate > _Samples[2].Date && head != endPtr)
380 double dt = _Samples[3].Date - _Samples[2].Date;
381 float lambda = (float) (dt != 0 ? (currDate - _Samples[2].Date) / dt : 0);
382 for(uint k = 0; k < _NumCorners;++k)
384 *head++ = lambda * (_Samples[3].Pos[k] - _Samples[2].Pos[k]) + _Samples[2].Pos[k];
386 currDate -= _SliceTime;
388 if (head != endPtr)
390 // interpolate smoothly for remaining samples
391 while (currDate >= _Samples[1].Date)
393 double dt = _Samples[2].Date - _Samples[1].Date;
394 if (dt == 0)
396 for(uint k = 0; k < _NumCorners;++k)
398 *head++ = _Samples[2].Pos[k];
401 else
403 double lambda = (currDate - _Samples[1].Date) / dt;
404 CVector T0, T1;
405 for(uint k = 0; k < _NumCorners;++k)
407 if (_Samples[2].Date != _Samples[0].Date)
409 T0 = (float) dt * (_Samples[2].Pos[k] - _Samples[0].Pos[k]) / (float) (_Samples[2].Date - _Samples[0].Date);
411 else
413 T0= NLMISC::CVector::Null;
415 if (_Samples[3].Date != _Samples[1].Date)
417 T1 = (float) dt * (_Samples[3].Pos[k] - _Samples[1].Pos[k]) / (float) (_Samples[3].Date - _Samples[1].Date);
419 else
421 T1= NLMISC::CVector::Null;
423 BuildHermiteVector(_Samples[1].Pos[k], _Samples[2].Pos[k], T0, T1, *head, (float) lambda);
424 ++ head;
427 if (head == endPtr) break;
428 currDate -= _SliceTime;
431 // Version with no time correction
432 while (currDate >= _Samples[1].Date)
434 float lambda = (float) ((currDate - _Samples[1].Date) / (_Samples[2].Date - _Samples[1].Date));
435 for(uint k = 0; k < _NumCorners;++k)
437 CVector T1 = 0.5f * (_Samples[3].Pos[k] - _Samples[1].Pos[k]);
438 CVector T0 = 0.5f * (_Samples[2].Pos[k] - _Samples[0].Pos[k]);
439 BuildHermiteVector(_Samples[1].Pos[k], _Samples[2].Pos[k], T0, T1, *head, lambda);
440 ++ head;
442 if (head == endPtr) break;
443 currDate -= _SliceTime;
450 //===============================================================
451 void CSegRemanence::start()
453 if (_SliceTime == 0.f) return;
454 if (_Started && !_Stopping) return;
455 restart();
458 //===============================================================
459 void CSegRemanence::restart()
461 CSegRemanenceShape *srs = NLMISC::safe_cast<CSegRemanenceShape *>((IShape *) Shape);
462 if (!srs->getTextureShifting())
464 _UnrollRatio = 1.f;
466 else
468 if (!_Stopping)
469 _UnrollRatio = 0.f;
471 _Started = _Restarted = true;
472 _Stopping = false;
475 //===============================================================
476 void CSegRemanence::stop()
478 _Stopping = true;
481 //===============================================================
482 void CSegRemanence::stopNoUnroll()
484 _Started = _Restarted = _Stopping = false;
487 //===============================================================
488 void CSegRemanence::updateOpacityFromShape()
490 CSegRemanenceShape *srs = NLMISC::safe_cast<CSegRemanenceShape *>((IShape *) Shape);
491 bool transparent = srs->getMaterial().getBlend();
492 setTransparency(transparent);
493 setOpacity(!transparent);
496 //===============================================================
497 void CSegRemanence::setAnimatedMaterial(CAnimatedMaterial *mat)
499 if (mat == _AniMat) return;
500 delete _AniMat;
501 _AniMat = mat;
502 _AniMat->setFather(this, OwnerBit);
505 //===============================================================
506 void CSegRemanence::registerToChannelMixer(CChannelMixer *chanMixer, const std::string &prefix)
508 CTransformShape::registerToChannelMixer(chanMixer, prefix);
509 if (_AniMat)
511 _AniMat->registerToChannelMixer(chanMixer, prefix + _AniMat->getMaterialName() + ".") ;
515 //===============================================================
516 ITrack *CSegRemanence::getDefaultTrack (uint valueId)
518 CSegRemanenceShape *srs = NLMISC::safe_cast<CSegRemanenceShape *>((IShape *) Shape);
519 switch (valueId)
521 case PosValue: return srs->getDefaultPos();
522 case RotQuatValue: return srs->getDefaultRotQuat();
523 case ScaleValue: return srs->getDefaultScale();
525 return CTransformShape::getDefaultTrack(valueId);
526 return NULL;
530 //===============================================================
531 void CSegRemanence::traverseAnimDetail()
533 CTransformShape::traverseAnimDetail();
534 #ifndef DEBUG_SEG_REMANENCE_DISPLAY
535 if (isStarted())
536 #endif
538 /////////////////////////////////////////////////////////////////////////////
539 /////////////////////////////////////////////////////////////////////////////
541 CScene *scene = getOwnerScene();
542 if (scene->getNumRender() != (_LastSampleFrame + 1))
544 if (!isStopping())
546 // if wasn't visible at previous frame, must invalidate position
547 restart();
549 else
551 // ribbon started unrolling when it disapperaed from screen so simply remove it
552 stopNoUnroll();
555 _LastSampleFrame = scene->getNumRender();
556 setupFromShape();
557 samplePos(scene->getCurrentTime());
559 /////////////////////////////////////////////////////////////////////////////
560 /////////////////////////////////////////////////////////////////////////////
563 // test if animated material must be updated.
564 if(IAnimatable::isTouched(CSegRemanence::OwnerBit))
566 if (getAnimatedMaterial())
567 getAnimatedMaterial()->update();
568 clearAnimatedMatFlag();
573 //===============================================================
574 void CSegRemanence::setSliceTime(float duration)
576 if ( duration != _SliceTime )
578 stopNoUnroll();
579 _SliceTime = duration;