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/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"
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
,
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
);
68 static inline void BuildLinearVector(const NLMISC::CVector
&P0
,
69 const NLMISC::CVector
&P1
,
70 NLMISC::CVector
&dest
,
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),
93 IAnimatable::resize(AnimValueLast
);
95 // RenderFilter: We are a SegRemanece
96 _RenderFilterType
= UScene::FilterSegRemanence
;
99 //===============================================================
100 CSegRemanence::~CSegRemanence()
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
)
124 (CTransformShape
&) *this = (CTransformShape
&) other
; // copy base
125 copyFromOther(other
);
130 //===============================================================
131 void CSegRemanence::copyFromOther(CSegRemanence
&other
)
133 if (this == &other
) return;
135 CAnimatedMaterial
*otherMat
= other
._AniMat
!= NULL
? new CAnimatedMaterial(*other
._AniMat
)
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
)
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
;
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);
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
);
222 const CVector
*prevRow
= src
- _NumCorners
;
223 for(uint k
= 0; k
< _NumCorners
; ++k
)
225 vbPush(datas
, (1.f
- _HeadProgress
) * *src
+ _HeadProgress
* *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
;
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
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
;
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
);
285 CScene
*scene
= getOwnerScene();
286 // change unroll ratio
289 if (_UnrollRatio
!= 1.f
)
290 _UnrollRatio
= std::min(1.f
, _UnrollRatio
+ scene
->getEllapsedTime() / (srs
->getNumSlices() * _SliceTime
));
294 _UnrollRatio
= std::max(0.f
, _UnrollRatio
- srs
->getRollupRatio() * scene
->getEllapsedTime() / (srs
->getNumSlices() * _SliceTime
));
295 if (_UnrollRatio
== 0.f
)
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
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
);
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
];
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
;
390 // interpolate smoothly for remaining samples
391 while (currDate
>= _Samples
[1].Date
)
393 double dt
= _Samples
[2].Date
- _Samples
[1].Date
;
396 for(uint k
= 0; k
< _NumCorners
;++k
)
398 *head
++ = _Samples
[2].Pos
[k
];
403 double lambda
= (currDate
- _Samples
[1].Date
) / dt
;
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
);
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
);
421 T1
= NLMISC::CVector::Null
;
423 BuildHermiteVector(_Samples
[1].Pos
[k
], _Samples
[2].Pos
[k
], T0
, T1
, *head
, (float) lambda
);
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);
442 if (head == endPtr) break;
443 currDate -= _SliceTime;
450 //===============================================================
451 void CSegRemanence::start()
453 if (_SliceTime
== 0.f
) return;
454 if (_Started
&& !_Stopping
) return;
458 //===============================================================
459 void CSegRemanence::restart()
461 CSegRemanenceShape
*srs
= NLMISC::safe_cast
<CSegRemanenceShape
*>((IShape
*) Shape
);
462 if (!srs
->getTextureShifting())
471 _Started
= _Restarted
= true;
475 //===============================================================
476 void CSegRemanence::stop()
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;
502 _AniMat
->setFather(this, OwnerBit
);
505 //===============================================================
506 void CSegRemanence::registerToChannelMixer(CChannelMixer
*chanMixer
, const std::string
&prefix
)
508 CTransformShape::registerToChannelMixer(chanMixer
, prefix
);
511 _AniMat
->registerToChannelMixer(chanMixer
, prefix
+ _AniMat
->getMaterialName() + ".") ;
515 //===============================================================
516 ITrack
*CSegRemanence::getDefaultTrack (uint valueId
)
518 CSegRemanenceShape
*srs
= NLMISC::safe_cast
<CSegRemanenceShape
*>((IShape
*) Shape
);
521 case PosValue
: return srs
->getDefaultPos();
522 case RotQuatValue
: return srs
->getDefaultRotQuat();
523 case ScaleValue
: return srs
->getDefaultScale();
525 return CTransformShape::getDefaultTrack(valueId
);
530 //===============================================================
531 void CSegRemanence::traverseAnimDetail()
533 CTransformShape::traverseAnimDetail();
534 #ifndef DEBUG_SEG_REMANENCE_DISPLAY
538 /////////////////////////////////////////////////////////////////////////////
539 /////////////////////////////////////////////////////////////////////////////
541 CScene
*scene
= getOwnerScene();
542 if (scene
->getNumRender() != (_LastSampleFrame
+ 1))
546 // if wasn't visible at previous frame, must invalidate position
551 // ribbon started unrolling when it disapperaed from screen so simply remove it
555 _LastSampleFrame
= scene
->getNumRender();
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
)
579 _SliceTime
= duration
;