// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "std3d.h"
#include "nel/misc/common.h"
#include "nel/3d/ps_ribbon_base.h"
#include "nel/3d/particle_system.h"
namespace NL3D
{
////////////////////////////////////
// CPSRibbonBase implementation //
////////////////////////////////////
/// build some hermite spline value, with the given points and tangents
static inline void BuildHermiteVector(const NLMISC::CVector &P0,
const NLMISC::CVector &P1,
const NLMISC::CVector &T0,
const NLMISC::CVector &T1,
NLMISC::CVector &dest,
float lambda
)
{
NL_PS_FUNC(BuildHermiteVector)
const float lambda2 = lambda * lambda;
const float lambda3 = lambda2 * lambda;
const float h1 = 2 * lambda3 - 3 * lambda2 + 1;
const float h2 = - 2 * lambda3 + 3 * lambda2;
const float h3 = lambda3 - 2 * lambda2 + lambda;
const float h4 = lambda3 - lambda2;
/// just avoid some ctor calls here...
dest.set (h1 * P0.x + h2 * P1.x + h3 * T0.x + h4 * T1.x,
h1 * P0.y + h2 * P1.y + h3 * T0.y + h4 * T1.y,
h1 * P0.z + h2 * P1.z + h3 * T0.z + h4 * T1.z);
}
/// for test
static inline void BuildLinearVector(const NLMISC::CVector &P0,
const NLMISC::CVector &P1,
NLMISC::CVector &dest,
float lambda,
float oneMinusLambda
)
{
NL_PS_FUNC(BuildLinearVector)
dest.set (lambda * P1.x + oneMinusLambda * P0.x,
lambda * P1.y + oneMinusLambda * P0.y,
lambda * P1.z + oneMinusLambda * P0.z);
}
const uint EndRibbonStorage = 1;
//=======================================================
CPSRibbonBase::CPSRibbonBase() : _NbSegs(8),
_SegDuration(0.02f),
_Parametric(false),
_RibbonIndex(0),
_MatrixMode(FatherMatrix),
_LastUpdateDate(0),
_RibbonMode(VariableSize),
_InterpolationMode(Hermitte),
_RibbonLength(1),
_SegLength(_RibbonLength / _NbSegs),
_LODDegradation(1)
{
NL_PS_FUNC(CPSRibbonBase_CPSRibbonBase)
initDateVect();
}
//=======================================================
void CPSRibbonBase::setMatrixMode(TMatrixMode matrixMode)
{
NL_PS_FUNC(CPSRibbonBase_setMatrixMode)
if (matrixMode == _MatrixMode) return;
if (_Owner) resetFromOwner();
_MatrixMode = matrixMode;
}
//=======================================================
void CPSRibbonBase::setRibbonLength(float length)
{
NL_PS_FUNC(CPSRibbonBase_setRibbonLength)
nlassert(length > 0.f);
_RibbonLength = length;
_SegLength = length / _NbSegs;
}
//=======================================================
void CPSRibbonBase::setRibbonMode(TRibbonMode mode)
{
NL_PS_FUNC(CPSRibbonBase_setRibbonMode)
nlassert(mode < RibbonModeLast);
_RibbonMode = mode;
}
//=======================================================
void CPSRibbonBase::setInterpolationMode(TInterpolationMode mode)
{
NL_PS_FUNC(CPSRibbonBase_setInterpolationMode)
nlassert(mode < InterpModeLast);
_InterpolationMode = mode;
}
//=======================================================
void CPSRibbonBase::setTailNbSeg(uint32 nbSegs)
{
NL_PS_FUNC(CPSRibbonBase_setTailNbSeg)
nlassert(nbSegs >= 1);
_NbSegs = nbSegs;
_RibbonIndex = 0;
if (_Owner)
{
resize(_Owner->getMaxSize());
}
initDateVect();
}
//=======================================================
void CPSRibbonBase::setSegDuration(TAnimationTime ellapsedTime)
{
NL_PS_FUNC(CPSRibbonBase_setSegDuration)
_SegDuration = ellapsedTime;
}
//=======================================================
void CPSRibbonBase::updateGlobals()
{
NL_PS_FUNC(CPSRibbonBase_updateGlobals)
nlassert(!_Parametric);
nlassert(_Owner);
const uint size = _Owner->getSize();
if (!size) return;
const TAnimationTime currDate = _Owner->getOwner()->getSystemDate() + CParticleSystem::RealEllapsedTime;
if (currDate - _LastUpdateDate >= _SegDuration)
{
if (_RibbonIndex == 0) _RibbonIndex = _NbSegs + EndRibbonStorage;
else --_RibbonIndex;
/// decal date
::memmove(&_SamplingDate[1], &_SamplingDate[0], sizeof(float) * (_NbSegs + EndRibbonStorage));
_LastUpdateDate = currDate;
}
/// save current date
_SamplingDate[0] = currDate;
/// updating ribbons positions
TPSMatrixMode mm = convertMatrixMode();
if (mm == _Owner->getMatrixMode())
{
// trail reside in the same coord system -> no conversion needed
TPSAttribVector::iterator posIt = _Owner->getPos().begin();
NLMISC::CVector *currIt = &_Ribbons[_RibbonIndex];
uint k = size;
for (;;)
{
*currIt = *posIt;
--k;
if (!k) break;
++posIt;
currIt += (_NbSegs + 1 + EndRibbonStorage);
}
}
else
{
nlassert(_Owner->getOwner());
const CMatrix &mat = CPSLocated::getConversionMatrix(*_Owner->getOwner(), mm, _Owner->getMatrixMode());
TPSAttribVector::iterator posIt = _Owner->getPos().begin();
NLMISC::CVector *currIt = &_Ribbons[_RibbonIndex];
uint k = size;
for (;;)
{
*currIt = mat * *posIt;
--k;
if (!k) break;
++posIt;
currIt += (_NbSegs + 1 + EndRibbonStorage);
}
}
}
//=======================================================
void CPSRibbonBase::computeHermitteRibbon(uint index, NLMISC::CVector *dest, uint stride /* = sizeof(NLMISC::CVector)*/)
{
NL_PS_FUNC(CPSRibbonBase_CVector )
nlassert(!_Parametric);
NLMISC::CVector *startIt = &_Ribbons[(_NbSegs + 1 + EndRibbonStorage) * index];
NLMISC::CVector *endIt = startIt + (_NbSegs + 1 + EndRibbonStorage);
NLMISC::CVector *currIt = startIt + _RibbonIndex;
const NLMISC::CVector *firstIt = currIt;
NLMISC::CVector *nextIt = currIt + 1;
if (nextIt == endIt) nextIt = startIt;
NLMISC::CVector *nextNextIt = nextIt + 1;
if (nextNextIt == endIt) nextNextIt = startIt;
float *date = &_SamplingDate[0];
NLMISC::CVector t0 = (*nextIt - *currIt);
NLMISC::CVector t1 = 0.5f * (*nextNextIt - *currIt);
uint leftToDo = _UsedNbSegs + 1;
float lambda = 0.f;
float lambdaStep = 1.f;
for (;;)
{
float dt = date[0] - date[1];
if (dt < 10E-6f) // we reached the start of ribbon
{
do
{
*dest = *currIt;
#ifdef NL_DEBUG
nlassert(NLMISC::isValidDouble(dest->x));
nlassert(NLMISC::isValidDouble(dest->y));
nlassert(NLMISC::isValidDouble(dest->z));
#endif
dest = (NLMISC::CVector *) ((uint8 *) dest + stride);
}
while (--leftToDo);
return;
}
float newLambdaStep = _UsedSegDuration / dt;
// readapt lambda
lambda *= newLambdaStep / lambdaStep;
lambdaStep = newLambdaStep;
for(;;)
{
if (lambda >= 1.f) break;
/// compute a location
BuildHermiteVector(*currIt, *nextIt, t0, t1, *dest, lambda);
#ifdef NL_DEBUG
nlassert(NLMISC::isValidDouble(dest->x));
nlassert(NLMISC::isValidDouble(dest->y));
nlassert(NLMISC::isValidDouble(dest->z));
#endif
dest = (NLMISC::CVector *) ((uint8 *) dest + stride);
-- leftToDo;
if (!leftToDo) return;
lambda += lambdaStep;
}
++date;
lambda -= 1.f;
// Start new segment and compute new tangents
t0 = t1;
currIt = nextIt;
nextIt = nextNextIt;
++nextNextIt;
if (nextNextIt == endIt) nextNextIt = startIt;
if (nextNextIt == firstIt)
{
t1 = *nextIt - *currIt;
}
else
{
t1 = 0.5f * (*nextNextIt - *currIt);
}
}
}
//=======================================================
void CPSRibbonBase::computeLinearRibbon(uint index, NLMISC::CVector *dest, uint stride)
{
NL_PS_FUNC(CPSRibbonBase_computeLinearRibbon)
nlassert(!_Parametric);
NLMISC::CVector *startIt = &_Ribbons[(_NbSegs + 1 + EndRibbonStorage) * index];
NLMISC::CVector *endIt = startIt + (_NbSegs + 1 + EndRibbonStorage);
NLMISC::CVector *currIt = startIt + _RibbonIndex;
NLMISC::CVector *nextIt = currIt + 1;
if (nextIt == endIt) nextIt = startIt;
NLMISC::CVector *nextNextIt = nextIt + 1;
if (nextNextIt == endIt) nextNextIt = startIt;
float *date = &_SamplingDate[0];
uint leftToDo = _UsedNbSegs + 1;
float lambda = 0.f;
float lambdaStep = 1.f;
for (;;)
{
float dt = date[0] - date[1];
if (dt < 10E-6f) // we reached the start of ribbon
{
do
{
*dest = *currIt;
#ifdef NL_DEBUG
nlassert(NLMISC::isValidDouble(dest->x));
nlassert(NLMISC::isValidDouble(dest->y));
nlassert(NLMISC::isValidDouble(dest->z));
#endif
dest = (NLMISC::CVector *) ((uint8 *) dest + stride);
}
while (--leftToDo);
return;
}
float newLambdaStep = _UsedSegDuration / dt;
// readapt lambda
lambda *= newLambdaStep / lambdaStep;
lambdaStep = newLambdaStep;
float oneMinusLambda = 1.f - lambda;
for(;;)
{
if (lambda >= 1.f) break;
/// compute a location
BuildLinearVector(*currIt, *nextIt, *dest, lambda, oneMinusLambda);
#ifdef NL_DEBUG
nlassert(NLMISC::isValidDouble(dest->x));
nlassert(NLMISC::isValidDouble(dest->y));
nlassert(NLMISC::isValidDouble(dest->z));
#endif
dest = (NLMISC::CVector *) ((uint8 *) dest + stride);
-- leftToDo;
if (!leftToDo) return;
lambda += lambdaStep;
oneMinusLambda -= lambdaStep;
}
++date;
lambda -= 1.f;
currIt = nextIt;
nextIt = nextNextIt;
++nextNextIt;
if (nextNextIt == endIt) nextNextIt = startIt;
}
}
/*
void CPSRibbonBase::computeLinearRibbon(uint index, NLMISC::CVector *dest, uint stride)
{
nlassert(!_Parametric);
NLMISC::CVector *startIt = &_Ribbons[(_NbSegs + 1 + EndRibbonStorage) * index];
NLMISC::CVector *endIt = startIt + (_NbSegs + 1 + EndRibbonStorage);
NLMISC::CVector *currIt = startIt + _RibbonIndex;
NLMISC::CVector *nextIt = currIt + 1;
if (nextIt == endIt) nextIt = startIt;
NLMISC::CVector *nextNextIt = nextIt + 1;
if (nextNextIt == endIt) nextNextIt = startIt;
float *date = &_SamplingDate[0];
uint leftToDo = _UsedNbSegs + 1;
float lambda = 0.f;
float dt = date[0] - date[1];
if (dt < 10E-6f) // we reached the start of ribbon
{
do
{
*dest = *currIt;
dest = (NLMISC::CVector *) ((uint8 *) dest + stride);
}
while (--leftToDo);
return;
}
float lambdaStep = _UsedSegDuration / dt;
BuildLinearVector(*currIt, *nextIt, *dest, 0.f, 1.f);
dest = (NLMISC::CVector *) ((uint8 *) dest + stride);
-- leftToDo;
// snap lambda to nearest time step
lambda = lambdaStep * fmodf(date[0], _UsedSegDuration) / _UsedSegDuration;
for (;;)
{
float oneMinusLambda = 1.f - lambda;
for(;;)
{
if (lambda >= 1.f) break;
/// compute a location
BuildLinearVector(*currIt, *nextIt, *dest, lambda, oneMinusLambda);
dest = (NLMISC::CVector *) ((uint8 *) dest + stride);
-- leftToDo;
if (!leftToDo) return;
lambda += lambdaStep;
oneMinusLambda -= lambdaStep;
}
++date;
lambda -= 1.f;
currIt = nextIt;
nextIt = nextNextIt;
++nextNextIt;
if (nextNextIt == endIt) nextNextIt = startIt;
float dt = date[0] - date[1];
if (dt < 10E-6f) // we reached the start of ribbon
{
do
{
*dest = *currIt;
dest = (NLMISC::CVector *) ((uint8 *) dest + stride);
}
while (--leftToDo);
return;
}
float newLambdaStep = _UsedSegDuration / dt;
// readapt lambda
lambda *= newLambdaStep / lambdaStep;
lambdaStep = newLambdaStep;
}
}
*/
//=======================================================
void CPSRibbonBase::computeLinearCstSizeRibbon(uint index, NLMISC::CVector *dest, uint stride /* = sizeof(NLMISC::CVector)*/)
{
NL_PS_FUNC(CPSRibbonBase_CVector )
nlassert(!_Parametric);
CVector *startIt = &_Ribbons[(_NbSegs + 1 + EndRibbonStorage) * index];
NLMISC::CVector *endIt = startIt + (_NbSegs + 1 + EndRibbonStorage);
NLMISC::CVector *currIt = startIt + _RibbonIndex;
NLMISC::CVector *firstIt = currIt;
NLMISC::CVector *nextIt = currIt + 1;
if (nextIt == endIt) nextIt = startIt;
NLMISC::CVector *nextNextIt = nextIt + 1;
if (nextNextIt == endIt) nextNextIt = startIt;
uint leftToDo = _UsedNbSegs + 1;
float lambda = 0.f;
float lambdaStep = 1.f;
/// Our goal here is to match the length of the ribbon, But if it isn't moving fast enough, we must truncate it
for (;;)
{
/// compute length between the 2 sampling points
const float sampleLength = (*nextIt - *currIt).norm();
if (sampleLength > 10E-6f)
{
/// compute lambda so that it match the length needed for each segment
float newLambdaStep = _UsedSegLength / sampleLength;
// readapt lambda
lambda *= newLambdaStep / lambdaStep;
lambdaStep = newLambdaStep;
float oneMinusLambda = 1.f - lambda;
for(;;)
{
if (lambda >= 1.f) break;
/// compute a location
BuildLinearVector(*currIt, *nextIt, *dest, lambda, oneMinusLambda);
#ifdef NL_DEBUG
nlassert(NLMISC::isValidDouble(dest->x));
nlassert(NLMISC::isValidDouble(dest->y));
nlassert(NLMISC::isValidDouble(dest->z));
#endif
dest = (NLMISC::CVector *) ((uint8 *) dest + stride);
-- leftToDo;
if (!leftToDo) return;
lambda += lambdaStep;
oneMinusLambda -= lambdaStep;
}
lambda -= 1.f;
}
/// go to next sampling pos
currIt = nextIt;
nextIt = nextNextIt;
++nextNextIt;
if (nextNextIt == endIt) nextNextIt = startIt;
if (nextNextIt == firstIt)
{
// The length of the sampling curve is too short
// must truncate the ribbon.
NLMISC::CVector &toDup = *nextIt;
while (leftToDo --)
{
*dest = toDup;
#ifdef NL_DEBUG
nlassert(NLMISC::isValidDouble(dest->x));
nlassert(NLMISC::isValidDouble(dest->y));
nlassert(NLMISC::isValidDouble(dest->z));
#endif
dest = (NLMISC::CVector *) ((uint8 *) dest + stride);
}
return;
}
}
}
//=======================================================
void CPSRibbonBase::computeHermitteCstSizeRibbon(uint index, NLMISC::CVector *dest, uint stride /* = sizeof(NLMISC::CVector)*/)
{
NL_PS_FUNC(CPSRibbonBase_CVector )
nlassert(!_Parametric);
NLMISC::CVector *startIt = &_Ribbons[(_NbSegs + 1 + EndRibbonStorage) * index];
NLMISC::CVector *endIt = startIt + (_NbSegs + 1 + EndRibbonStorage);
NLMISC::CVector *currIt = startIt + _RibbonIndex;
NLMISC::CVector *firstIt = currIt;
NLMISC::CVector *nextIt = currIt + 1;
if (nextIt == endIt) nextIt = startIt;
NLMISC::CVector *nextNextIt = nextIt + 1;
if (nextNextIt == endIt) nextNextIt = startIt;
NLMISC::CVector t0 = (*nextIt - *currIt);
NLMISC::CVector t1 = 0.5f * (*nextNextIt - *currIt);
uint leftToDo = _UsedNbSegs + 1;
float lambda = 0.f;
float lambdaStep = 1.f;
/// Our goal here is to match the length of the ribbon, But if it isn't moving fast enough, we must truncate it
/// Having a constant speed over a hermite curve is expensive, so we make a (very) rough approximation...
for (;;)
{
/// compute length between the 2 sampling points
const float sampleLength = (*nextIt - *currIt).norm();
if (sampleLength > 10E-6f)
{
/// compute lambda so that it match the length needed for each segment
float newLambdaStep = _UsedSegLength / sampleLength;
// readapt lambda
lambda *= newLambdaStep / lambdaStep;
lambdaStep = newLambdaStep;
for(;;)
{
if (lambda >= 1.f) break;
/// compute a location
BuildHermiteVector(*currIt, *nextIt, t0, t1, *dest, lambda);
#ifdef NL_DEBUG
nlassert(NLMISC::isValidDouble(dest->x));
nlassert(NLMISC::isValidDouble(dest->y));
nlassert(NLMISC::isValidDouble(dest->z));
#endif
dest = (NLMISC::CVector *) ((uint8 *) dest + stride);
-- leftToDo;
if (!leftToDo) return;
lambda += lambdaStep;
}
lambda -= 1.f;
}
/// go to next sampling pos
currIt = nextIt;
nextIt = nextNextIt;
++nextNextIt;
if (nextNextIt == endIt) nextNextIt = startIt;
if (nextNextIt == firstIt)
{
// The length of the sampling curve is too short
// must truncate the ribbon.
NLMISC::CVector &toDup = *nextIt;
while (leftToDo --)
{
*dest = toDup;
#ifdef NL_DEBUG
nlassert(NLMISC::isValidDouble(dest->x));
nlassert(NLMISC::isValidDouble(dest->y));
nlassert(NLMISC::isValidDouble(dest->z));
#endif
dest = (NLMISC::CVector *) ((uint8 *) dest + stride);
}
return;
}
/// update tangents
t0 = t1;
t1 = 0.5f * (*nextNextIt - *currIt);
}
}
//=======================================================
void CPSRibbonBase::computeRibbon(uint index, NLMISC::CVector *dest, uint stride /* = sizeof(NLMISC::CVector)*/)
{
NL_PS_FUNC(CPSRibbonBase_CVector )
switch (_InterpolationMode)
{
case Linear:
if (_RibbonMode == VariableSize)
{
computeLinearRibbon(index, dest, stride);
}
else
{
computeLinearCstSizeRibbon(index, dest, stride);
}
break;
case Hermitte:
if (_RibbonMode == VariableSize)
{
computeHermitteRibbon(index, dest, stride);
}
else
{
computeHermitteCstSizeRibbon(index, dest, stride);
}
break;
default:
nlassert(0);
break;
}
}
//=======================================================
void CPSRibbonBase::dupRibbon(uint dest, uint src)
{
NL_PS_FUNC(CPSRibbonBase_dupRibbon)
nlassert(!_Parametric);
nlassert(_Owner);
const uint size = _Owner->getSize();
nlassert(dest < size && src < size);
::memcpy(&_Ribbons[dest * (_NbSegs + EndRibbonStorage + 1)], &_Ribbons[src * (_NbSegs + EndRibbonStorage + 1)], sizeof(NLMISC::CVector) * (_NbSegs + 1 + EndRibbonStorage));
}
//=======================================================
void CPSRibbonBase::newElement(const CPSEmitterInfo &info)
{
NL_PS_FUNC(CPSRibbonBase_newElement)
if (_Parametric) return;
/// dump the same pos for all pos of the ribbon
const uint index = _Owner->getNewElementIndex();
const NLMISC::CVector &pos = _Owner->getPos()[index]; // get the pos of the new element;
resetSingleRibbon(index, pos);
}
//=======================================================
void CPSRibbonBase::deleteElement(uint32 index)
{
NL_PS_FUNC(CPSRibbonBase_deleteElement)
if (_Parametric) return;
const uint32 size = _Owner->getSize();
if(index == (size - 1)) return; // was the last element, no permutation needed.
dupRibbon(index, size - 1);
}
//=======================================================
void CPSRibbonBase::resize(uint32 size)
{
NL_PS_FUNC(CPSRibbonBase_resize)
nlassert(size < (1 << 16));
if (_Parametric) return;
_Ribbons.resize(size * (_NbSegs + 1 + EndRibbonStorage));
resetFromOwner();
}
//=======================================================
void CPSRibbonBase::resetSingleRibbon(uint index, const NLMISC::CVector &pos)
{
NL_PS_FUNC(CPSRibbonBase_resetSingleRibbon)
nlassert(!_Parametric);
TPSMatrixMode mm = convertMatrixMode();
NLMISC::CVector *it = &_Ribbons[(index * (_NbSegs + 1 + EndRibbonStorage))];
if (mm == _Owner->getMatrixMode())
{
std::fill(it, it + (_NbSegs + 1 + EndRibbonStorage), pos);
}
else
{
nlassert(_Owner->getOwner());
const CMatrix &mat = CPSLocated::getConversionMatrix(*_Owner->getOwner(), mm, _Owner->getMatrixMode());
std::fill(it, it + (_NbSegs + 1 + EndRibbonStorage), mat * pos);
}
}
//=======================================================
void CPSRibbonBase::resetFromOwner()
{
NL_PS_FUNC(CPSRibbonBase_resetFromOwner)
nlassert(!_Parametric);
TPSAttribVector::iterator posIt = _Owner->getPos().begin();
TPSAttribVector::iterator endPosIt = _Owner->getPos().end();
for (uint k = 0; posIt != endPosIt; ++posIt, ++k)
{
resetSingleRibbon(k, *posIt);
}
}
//=======================================================
void CPSRibbonBase::motionTypeChanged(bool parametric)
{
NL_PS_FUNC(CPSRibbonBase_motionTypeChanged)
_Parametric = parametric;
if (parametric)
{
NLMISC::contReset(_Ribbons); // kill the vector
}
else
{
nlassert(_Owner);
resize(_Owner->getMaxSize());
initDateVect();
resetFromOwner();
}
}
//=======================================================
void CPSRibbonBase::initDateVect()
{
NL_PS_FUNC(CPSRibbonBase_initDateVect)
_SamplingDate.resize( _NbSegs + 1 + EndRibbonStorage);
std::fill(_SamplingDate.begin(), _SamplingDate.begin() + (_NbSegs + 1 + EndRibbonStorage), 0.f);
}
//=======================================================
void CPSRibbonBase::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSRibbonBase_serial)
CPSParticle::serial(f);
// version 2 : added matrix mode
sint ver = f.serialVersion(2);
f.serialEnum(_RibbonMode);
f.serialEnum(_InterpolationMode);
f.serial(_NbSegs, _SegDuration);
if (_RibbonMode == FixedSize)
{
f.serial(_RibbonLength);
if (f.isReading())
{
_SegLength = _RibbonLength / _NbSegs;
}
}
if (f.isReading())
{
if (_Owner)
{
resize(_Owner->getMaxSize());
initDateVect();
resetFromOwner();
}
}
if (ver >= 1)
{
f.serial(_LODDegradation);
}
if (ver >= 2)
{
f.serialEnum(_MatrixMode);
}
}
//=======================================================
void CPSRibbonBase::updateLOD()
{
NL_PS_FUNC(CPSRibbonBase_updateLOD)
nlassert(_Owner);
float ratio = _Owner->getOwner()->getOneMinusCurrentLODRatio();
float squaredRatio = ratio * ratio;
float lodRatio = _LODDegradation + (1.f - _LODDegradation ) * squaredRatio * squaredRatio * squaredRatio;
_UsedNbSegs = (uint) (_NbSegs * lodRatio);
NLMISC::clamp(_UsedNbSegs, 0u, _NbSegs);
const float epsilon = 10E-4f;
_UsedSegDuration = _SegDuration / std::max(epsilon, lodRatio);
_UsedSegLength = _SegLength / std::max(epsilon, lodRatio);
}
//=======================================================
void CPSRibbonBase::systemDateChanged()
{
NL_PS_FUNC(CPSRibbonBase_systemDateChanged)
nlassert(_Owner->getOwner());
_Owner->getOwner()->getSystemDate();
float date = _Owner->getOwner()->getSystemDate();
std::fill(_SamplingDate.begin(), _SamplingDate.end(), date);
_LastUpdateDate = date;
}
} // NL3D