khanat-opennel-code/code/nel/src/3d/ps_float.cpp

203 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