204 lines
6.7 KiB
C++
204 lines
6.7 KiB
C++
|
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||
|
// 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 <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
#include "std3d.h"
|
||
|
|
||
|
#include "nel/3d/ps_float.h"
|
||
|
#include "nel/3d/ps_register_float_attribs.h"
|
||
|
#include "nel/misc/fast_floor.h"
|
||
|
|
||
|
namespace NL3D {
|
||
|
|
||
|
|
||
|
/////////////////////////////////////
|
||
|
// CPSFloatGradient implementation //
|
||
|
/////////////////////////////////////
|
||
|
|
||
|
CPSFloatGradient::CPSFloatGradient(const float *floatTab, uint32 nbValues, uint32 nbStages, float nbCycles)
|
||
|
: CPSValueGradient<float>(nbCycles)
|
||
|
{
|
||
|
_F.setValues(floatTab, nbValues, nbStages) ;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////
|
||
|
// CPSFloatBezierCurve implementation //
|
||
|
////////////////////////////////////////////
|
||
|
|
||
|
CPSFloatCurveFunctor::CPSFloatCurveFunctor() : _NumSamples(0), _Smoothing(true)
|
||
|
{
|
||
|
/*
|
||
|
_CtrlPoints.push_back(CCtrlPoint(0, 0.5f));
|
||
|
_CtrlPoints.push_back(CCtrlPoint(1, 0.5f));
|
||
|
updateTab();
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
///=======================================================================================
|
||
|
void CPSFloatCurveFunctor::sortPoints(void)
|
||
|
{
|
||
|
std::sort(_CtrlPoints.begin(), _CtrlPoints.end());
|
||
|
}
|
||
|
|
||
|
///=======================================================================================
|
||
|
void CPSFloatCurveFunctor::addControlPoint(const CCtrlPoint &ctrlPoint)
|
||
|
{
|
||
|
_CtrlPoints.push_back(ctrlPoint);
|
||
|
sortPoints();
|
||
|
updateTab();
|
||
|
}
|
||
|
|
||
|
///=======================================================================================
|
||
|
const CPSFloatCurveFunctor::CCtrlPoint &CPSFloatCurveFunctor::getControlPoint(uint index) const
|
||
|
{
|
||
|
return _CtrlPoints[index];
|
||
|
}
|
||
|
|
||
|
///=======================================================================================
|
||
|
void CPSFloatCurveFunctor::setCtrlPoint(uint index, const CCtrlPoint &ctrlPoint)
|
||
|
{
|
||
|
nlassert(ctrlPoint.Date >= 0 && ctrlPoint.Date <= 1);
|
||
|
_CtrlPoints[index] = ctrlPoint;
|
||
|
sortPoints();
|
||
|
updateTab();
|
||
|
}
|
||
|
|
||
|
///=======================================================================================
|
||
|
void CPSFloatCurveFunctor::removeCtrlPoint(uint index)
|
||
|
{
|
||
|
nlassert(_CtrlPoints.size() > 1);
|
||
|
_CtrlPoints.erase(_CtrlPoints.begin() + index);
|
||
|
updateTab();
|
||
|
}
|
||
|
|
||
|
///=======================================================================================
|
||
|
void CPSFloatCurveFunctor::setNumSamples(uint32 numSamples)
|
||
|
{
|
||
|
nlassert(numSamples > 0);
|
||
|
_NumSamples = numSamples;
|
||
|
updateTab();
|
||
|
}
|
||
|
|
||
|
///=======================================================================================
|
||
|
float CPSFloatCurveFunctor::getValue(float date) const
|
||
|
{
|
||
|
if (_CtrlPoints.empty()) return 0.f;
|
||
|
NLMISC::clamp(date, 0, 1);
|
||
|
// find a key that has a higher value
|
||
|
CPSVector<CCtrlPoint>::V::const_iterator it = _CtrlPoints.begin();
|
||
|
while ( it != _CtrlPoints.end() && it->Date <= date ) ++it;
|
||
|
|
||
|
if (it == _CtrlPoints.begin()) return _CtrlPoints[0].Value;
|
||
|
if (it == _CtrlPoints.end()) return _CtrlPoints[_CtrlPoints.size() - 1].Value;
|
||
|
CPSVector<CCtrlPoint>::V::const_iterator precIt = it - 1;
|
||
|
if (precIt->Date == it->Date) return 0.5f * (precIt->Value + it->Value);
|
||
|
const float lambda = (date - precIt->Date) / (it->Date - precIt->Date);
|
||
|
if (!_Smoothing) // linear interpolation
|
||
|
{
|
||
|
return lambda * it->Value + (1.f - lambda) * precIt->Value;
|
||
|
}
|
||
|
else // hermite interpolation
|
||
|
{
|
||
|
float width = it->Date - precIt->Date;
|
||
|
uint index = (uint)(precIt - _CtrlPoints.begin());
|
||
|
float t1 = getSlope(index) * width, t2 = getSlope(index + 1) * width;
|
||
|
const float lambda2 = NLMISC::sqr(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;
|
||
|
|
||
|
return h1 * precIt->Value + h2 * it->Value + h3 * t1 + h4 * t2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///=======================================================================================
|
||
|
void CPSFloatCurveFunctor::updateTab(void)
|
||
|
{
|
||
|
float step = 1.f / _NumSamples;
|
||
|
float d = 0.f;
|
||
|
_Tab.resize(_NumSamples + 1);
|
||
|
uint k;
|
||
|
for (k = 0; k <= _NumSamples; ++k)
|
||
|
{
|
||
|
_Tab[k] = getValue(d);
|
||
|
d += step;
|
||
|
}
|
||
|
_MinValue = _MaxValue = _Tab[0];
|
||
|
for (k = 1; k <= _NumSamples; ++k)
|
||
|
{
|
||
|
_MinValue = std::min(_MinValue, _Tab[k]);
|
||
|
_MaxValue = std::max(_MaxValue, _Tab[k]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///=======================================================================================
|
||
|
void CPSFloatCurveFunctor::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
||
|
{
|
||
|
f.serialVersion(1);
|
||
|
f.serial(_NumSamples, _Smoothing);
|
||
|
f.serialCont(_CtrlPoints);
|
||
|
if (f.isReading())
|
||
|
{
|
||
|
updateTab();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///=======================================================================================
|
||
|
float CPSFloatCurveFunctor::getSlope(uint index) const
|
||
|
{
|
||
|
// tangent for first point
|
||
|
if (index == 0)
|
||
|
{
|
||
|
return _CtrlPoints[1].Date != _CtrlPoints[0].Date ? (_CtrlPoints[1].Value - _CtrlPoints[0].Value)
|
||
|
/ (_CtrlPoints[1].Date - _CtrlPoints[0].Date)
|
||
|
: 1e6f;
|
||
|
}
|
||
|
|
||
|
// tangent for last point
|
||
|
if (index == _CtrlPoints.size() - 1)
|
||
|
{
|
||
|
return _CtrlPoints[index].Date != _CtrlPoints[index - 1].Date ? (_CtrlPoints[index].Value - _CtrlPoints[index - 1].Value)
|
||
|
/ (_CtrlPoints[index].Date - _CtrlPoints[index - 1].Date)
|
||
|
: 1e6f;
|
||
|
}
|
||
|
|
||
|
// tangent for other points
|
||
|
return _CtrlPoints[index + 1].Date != _CtrlPoints[index - 1].Date ? (_CtrlPoints[index + 1].Value - _CtrlPoints[index - 1].Value)
|
||
|
/ (_CtrlPoints[index + 1].Date - _CtrlPoints[index - 1].Date)
|
||
|
: 1e6f;
|
||
|
}
|
||
|
|
||
|
///=======================================================================================
|
||
|
void CPSFloatCurveFunctor::enableSmoothing(bool enable /* = true*/)
|
||
|
{
|
||
|
_Smoothing = enable;
|
||
|
updateTab();
|
||
|
}
|
||
|
|
||
|
///=======================================================================================
|
||
|
void PSRegisterFloatAttribs()
|
||
|
{
|
||
|
NLMISC_REGISTER_CLASS(CPSFloatBlender);
|
||
|
NLMISC_REGISTER_CLASS(CPSFloatGradient);
|
||
|
NLMISC_REGISTER_CLASS(CPSFloatMemory);
|
||
|
NLMISC_REGISTER_CLASS(CPSFloatBinOp);
|
||
|
NLMISC_REGISTER_CLASS(CPSFloatCurve);
|
||
|
}
|
||
|
|
||
|
} // NL3D
|