1393 lines
38 KiB
C++
1393 lines
38 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_force.h"
|
|
#include "nel/3d/driver.h"
|
|
#include "nel/3d/index_buffer.h"
|
|
#include "nel/3d/material.h"
|
|
#include "nel/3d/vertex_buffer.h"
|
|
#include "nel/3d/computed_string.h"
|
|
#include "nel/3d/font_manager.h"
|
|
#include "nel/3d/particle_system.h"
|
|
#include "nel/misc/common.h"
|
|
#include "nel/3d/ps_util.h"
|
|
#include "nel/3d/ps_misc.h"
|
|
|
|
namespace NL3D {
|
|
|
|
|
|
/*
|
|
* Constructor
|
|
*/
|
|
CPSForce::CPSForce()
|
|
{
|
|
NL_PS_FUNC(CPSForce_CPSForce)
|
|
}
|
|
|
|
|
|
|
|
void CPSForce::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSForce_serial)
|
|
f.serialVersion(1);
|
|
CPSTargetLocatedBindable::serial(f);
|
|
CPSLocatedBindable::serial(f);
|
|
}
|
|
|
|
|
|
void CPSForce::registerToTargets(void)
|
|
{
|
|
NL_PS_FUNC(CPSForce_registerToTargets)
|
|
for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
|
|
{
|
|
if (this->isIntegrable())
|
|
{
|
|
(*it)->registerIntegrableForce(this);
|
|
}
|
|
else
|
|
{
|
|
(*it)->addNonIntegrableForceRef();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CPSForce::step(TPSProcessPass pass)
|
|
{
|
|
NL_PS_FUNC(CPSForce_step)
|
|
switch(pass)
|
|
{
|
|
case PSToolRender:
|
|
show();
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CPSForce::attachTarget(CPSLocated *ptr)
|
|
{
|
|
NL_PS_FUNC(CPSForce_attachTarget)
|
|
nlassert(_Owner);
|
|
CPSTargetLocatedBindable::attachTarget(ptr);
|
|
// check whether we are integrable, and if so, add us to the list
|
|
if (this->isIntegrable())
|
|
{
|
|
ptr->registerIntegrableForce(this);
|
|
}
|
|
else
|
|
{
|
|
ptr->addNonIntegrableForceRef();
|
|
}
|
|
}
|
|
|
|
void CPSForce::releaseTargetRsc(CPSLocated *target)
|
|
{
|
|
NL_PS_FUNC(CPSForce_releaseTargetRsc)
|
|
if (this->isIntegrable())
|
|
{
|
|
target->unregisterIntegrableForce(this);
|
|
}
|
|
else
|
|
{
|
|
target->releaseNonIntegrableForceRef();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CPSForce::basisChanged(TPSMatrixMode matrixMode)
|
|
{
|
|
NL_PS_FUNC(CPSForce_basisChanged)
|
|
if (!this->isIntegrable()) return;
|
|
for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
|
|
{
|
|
(*it)->integrableForceBasisChanged(matrixMode);
|
|
}
|
|
}
|
|
|
|
|
|
void CPSForce::cancelIntegrable(void)
|
|
{
|
|
NL_PS_FUNC(CPSForce_cancelIntegrable)
|
|
nlassert(_Owner);
|
|
for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
|
|
{
|
|
if ((*it)->getMatrixMode() == _Owner->getMatrixMode())
|
|
{
|
|
(*it)->unregisterIntegrableForce(this);
|
|
(*it)->addNonIntegrableForceRef();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CPSForce::renewIntegrable(void)
|
|
{
|
|
NL_PS_FUNC(CPSForce_renewIntegrable)
|
|
nlassert(_Owner);
|
|
for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
|
|
{
|
|
if ((*it)->getMatrixMode() == _Owner->getMatrixMode())
|
|
{
|
|
(*it)->registerIntegrableForce(this);
|
|
(*it)->releaseNonIntegrableForceRef();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////
|
|
// CPSForceIntensity implementation //
|
|
///////////////////////////////////////
|
|
|
|
void CPSForceIntensity::setIntensity(float value)
|
|
{
|
|
NL_PS_FUNC(CPSForceIntensity_setIntensity)
|
|
if (_IntensityScheme)
|
|
{
|
|
delete _IntensityScheme;
|
|
_IntensityScheme = NULL;
|
|
}
|
|
_K = value;
|
|
|
|
}
|
|
|
|
CPSForceIntensity::~CPSForceIntensity()
|
|
{
|
|
NL_PS_FUNC(CPSForceIntensity_CPSForceIntensityDtor)
|
|
delete _IntensityScheme;
|
|
}
|
|
|
|
void CPSForceIntensity::setIntensityScheme(CPSAttribMaker<float> *scheme)
|
|
{
|
|
NL_PS_FUNC(CPSForceIntensity_setIntensityScheme)
|
|
nlassert(scheme);
|
|
delete _IntensityScheme;
|
|
_IntensityScheme = scheme;
|
|
if (getForceIntensityOwner() && scheme->hasMemory()) scheme->resize(getForceIntensityOwner()->getMaxSize(), getForceIntensityOwner()->getSize());
|
|
}
|
|
|
|
void CPSForceIntensity::serialForceIntensity(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSForceIntensity_IStream )
|
|
f.serialVersion(1);
|
|
if (!f.isReading())
|
|
{
|
|
if (_IntensityScheme)
|
|
{
|
|
bool bFalse = false;
|
|
f.serial(bFalse);
|
|
f.serialPolyPtr(_IntensityScheme);
|
|
}
|
|
else
|
|
{
|
|
bool bTrue = true;
|
|
f.serial(bTrue);
|
|
f.serial(_K);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool constantIntensity;
|
|
f.serial(constantIntensity);
|
|
if (constantIntensity)
|
|
{
|
|
f.serial(_K);
|
|
}
|
|
else
|
|
{
|
|
f.serialPolyPtr(_IntensityScheme);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////
|
|
// CPSForceIntensityHelper //
|
|
////////////////////////////////////////
|
|
|
|
|
|
void CPSForceIntensityHelper::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSForceIntensityHelper_serial)
|
|
f.serialVersion(1);
|
|
CPSForce::serial(f);
|
|
serialForceIntensity(f);
|
|
if (f.isReading())
|
|
{
|
|
registerToTargets();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////
|
|
// CPSDirectionalForce implementation //
|
|
////////////////////////////////////////
|
|
|
|
void CPSDirectionnalForce::computeForces(CPSLocated &target)
|
|
{
|
|
NL_PS_FUNC(CPSDirectionnalForce_computeForces)
|
|
nlassert(CParticleSystem::InsideSimLoop);
|
|
// perform the operation on each target
|
|
CVector toAdd;
|
|
for (uint32 k = 0; k < _Owner->getSize(); ++k)
|
|
{
|
|
CVector toAddLocal;
|
|
CVector dir;
|
|
|
|
if (_GlobalValueHandle.isValid()) // is direction a global variable ?
|
|
{
|
|
dir = _GlobalValueHandle.get(); // takes direction from global variable instead
|
|
}
|
|
else
|
|
{
|
|
dir = _Dir;
|
|
}
|
|
toAddLocal = CParticleSystem::EllapsedTime * (_IntensityScheme ? _IntensityScheme->get(_Owner, k) : _K ) * dir;
|
|
toAdd = CPSLocated::getConversionMatrix(&target, this->_Owner).mulVector(toAddLocal); // express this in the target basis
|
|
TPSAttribVector::iterator it = target.getSpeed().begin(), itend = target.getSpeed().end();
|
|
// 1st case : non-constant mass
|
|
if (target.getMassScheme())
|
|
{
|
|
TPSAttribFloat::const_iterator invMassIt = target.getInvMass().begin();
|
|
for (;it != itend; ++it, ++invMassIt)
|
|
{
|
|
(*it) += *invMassIt * toAdd;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the mass is constant
|
|
toAdd /= target.getInitialMass();
|
|
for (; it != itend; ++it)
|
|
{
|
|
(*it) += toAdd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CPSDirectionnalForce::show()
|
|
{
|
|
NL_PS_FUNC(CPSDirectionnalForce_show)
|
|
CPSLocated *loc;
|
|
uint32 index;
|
|
CPSLocatedBindable *lb;
|
|
_Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
|
|
|
|
setupDriverModelMatrix();
|
|
|
|
CVector dir;
|
|
if (_GlobalValueHandle.isValid()) // is direction a global variable ?
|
|
{
|
|
dir = _GlobalValueHandle.get(); // takes direction from global variable instead
|
|
}
|
|
else
|
|
{
|
|
dir = _Dir;
|
|
}
|
|
|
|
// for each element, see if it is the selected element, and if yes, display in red
|
|
for (uint k = 0; k < _Owner->getSize(); ++k)
|
|
{
|
|
const CRGBA col = (((lb == NULL || this == lb) && loc == _Owner && index == k) ? CRGBA::Red : CRGBA(127, 127, 127));
|
|
CPSUtil::displayArrow(getDriver(), _Owner->getPos()[k], dir, 1.f, col, CRGBA(80, 80, 0));
|
|
}
|
|
}
|
|
|
|
void CPSDirectionnalForce::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSDirectionnalForce_serial)
|
|
// Version 2 : added link to a global vector value
|
|
//
|
|
sint ver = f.serialVersion(2);
|
|
CPSForceIntensityHelper::serial(f);
|
|
if (ver == 1)
|
|
{
|
|
f.serial(_Dir);
|
|
_GlobalValueHandle.reset();
|
|
}
|
|
else
|
|
{
|
|
bool useHandle = _GlobalValueHandle.isValid();
|
|
f.serial(useHandle);
|
|
if (useHandle)
|
|
{
|
|
// a global value is used
|
|
if (f.isReading())
|
|
{
|
|
std::string handleName;
|
|
f.serial(handleName);
|
|
// retrieve a handle to the global value from the particle system
|
|
_GlobalValueHandle = CParticleSystem::getGlobalVectorValueHandle(handleName);
|
|
}
|
|
else
|
|
{
|
|
std::string handleName = _GlobalValueHandle.getName();
|
|
f.serial(handleName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
f.serial(_Dir);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPSDirectionnalForce::enableGlobalVectorValue(const std::string &name)
|
|
{
|
|
NL_PS_FUNC(CPSDirectionnalForce_enableGlobalVectorValue)
|
|
if (name.empty())
|
|
{
|
|
_GlobalValueHandle.reset();
|
|
return;
|
|
}
|
|
_GlobalValueHandle = CParticleSystem::getGlobalVectorValueHandle(name);
|
|
}
|
|
|
|
std::string CPSDirectionnalForce::getGlobalVectorValueName() const
|
|
{
|
|
NL_PS_FUNC(CPSDirectionnalForce_getGlobalVectorValueName)
|
|
return _GlobalValueHandle.isValid() ? _GlobalValueHandle.getName() : "";
|
|
}
|
|
|
|
////////////////////////////
|
|
// gravity implementation //
|
|
////////////////////////////
|
|
void CPSGravity::computeForces(CPSLocated &target)
|
|
{
|
|
NL_PS_FUNC(CPSGravity_computeForces)
|
|
nlassert(CParticleSystem::InsideSimLoop);
|
|
// perform the operation on each target
|
|
CVector toAdd;
|
|
for (uint32 k = 0; k < _Owner->getSize(); ++k)
|
|
{
|
|
CVector toAddLocal = CParticleSystem::EllapsedTime * CVector(0, 0, _IntensityScheme ? - _IntensityScheme->get(_Owner, k) : - _K);
|
|
toAdd = CPSLocated::getConversionMatrix(&target, this->_Owner).mulVector(toAddLocal); // express this in the target basis
|
|
TPSAttribVector::iterator it = target.getSpeed().begin(), itend = target.getSpeed().end();
|
|
|
|
if (toAdd.x && toAdd.y)
|
|
{
|
|
for (; it != itend; ++it)
|
|
{
|
|
(*it) += toAdd;
|
|
|
|
}
|
|
}
|
|
else // only the z component is not null, which should be the majority of cases ...
|
|
{
|
|
for (; it != itend; ++it)
|
|
{
|
|
it->z += toAdd.z;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPSGravity::show()
|
|
{
|
|
NL_PS_FUNC(CPSGravity_show)
|
|
CVector I = computeI();
|
|
CVector K = CVector(0,0,1);
|
|
|
|
// this is not designed for efficiency (target : edition code)
|
|
CIndexBuffer pb;
|
|
CVertexBuffer vb;
|
|
CMaterial material;
|
|
IDriver *driver = getDriver();
|
|
const float toolSize = 0.2f;
|
|
|
|
vb.setVertexFormat(CVertexBuffer::PositionFlag);
|
|
vb.setNumVertices(6);
|
|
{
|
|
CVertexBufferReadWrite vba;
|
|
vb.lock (vba);
|
|
vba.setVertexCoord(0, -toolSize * I);
|
|
vba.setVertexCoord(1, toolSize * I);
|
|
vba.setVertexCoord(2, CVector(0, 0, 0));
|
|
vba.setVertexCoord(3, -6.0f * toolSize * K);
|
|
vba.setVertexCoord(4, -toolSize * I - 5.0f * toolSize * K);
|
|
vba.setVertexCoord(5, toolSize * I - 5.0f * toolSize * K);
|
|
}
|
|
pb.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT);
|
|
pb.setNumIndexes(2*4);
|
|
{
|
|
CIndexBufferReadWrite ibaWrite;
|
|
pb.lock (ibaWrite);
|
|
ibaWrite.setLine(0, 0, 1);
|
|
ibaWrite.setLine(2, 2, 3);
|
|
ibaWrite.setLine(4, 4, 3);
|
|
ibaWrite.setLine(6, 3, 5);
|
|
}
|
|
|
|
material.setColor(CRGBA(127, 127, 127));
|
|
material.setLighting(false);
|
|
material.setBlendFunc(CMaterial::one, CMaterial::one);
|
|
material.setZWrite(false);
|
|
material.setBlend(true);
|
|
|
|
|
|
CMatrix mat;
|
|
|
|
for (TPSAttribVector::const_iterator it = _Owner->getPos().begin(); it != _Owner->getPos().end(); ++it)
|
|
{
|
|
mat.identity();
|
|
mat.translate(*it);
|
|
mat = getLocalToWorldMatrix() * mat;
|
|
|
|
driver->setupModelMatrix(mat);
|
|
driver->activeVertexBuffer(vb);
|
|
driver->activeIndexBuffer(pb);
|
|
driver->renderLines(material, 0, pb.getNumIndexes()/2);
|
|
|
|
|
|
|
|
// affiche un g a cote de la force
|
|
|
|
CVector pos = *it + CVector(1.5f * toolSize, 0, -1.2f * toolSize);
|
|
|
|
pos = getLocalToWorldMatrix() * pos;
|
|
|
|
|
|
// must have set this
|
|
nlassert(getFontGenerator() && getFontGenerator());
|
|
|
|
CPSUtil::print(driver, std::string("G")
|
|
, *getFontGenerator()
|
|
, *getFontManager()
|
|
, pos
|
|
, 80.0f * toolSize );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void CPSGravity::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSGravity_IStream )
|
|
f.serialVersion(1);
|
|
CPSForceIntensityHelper::serial(f);
|
|
}
|
|
|
|
|
|
bool CPSGravity::isIntegrable(void) const
|
|
{
|
|
NL_PS_FUNC(CPSGravity_isIntegrable)
|
|
return _IntensityScheme == NULL;
|
|
}
|
|
|
|
void CPSGravity::integrate(float date, CPSLocated *src, uint32 startIndex, uint32 numObjects, NLMISC::CVector *destPos, NLMISC::CVector *destSpeed,
|
|
bool accumulate,
|
|
uint posStride, uint speedStride
|
|
) const
|
|
{
|
|
NL_PS_FUNC(CPSGravity_integrate)
|
|
#define NEXT_SPEED destSpeed = (NLMISC::CVector *) ((uint8 *) destSpeed + speedStride);
|
|
#define NEXT_POS destPos = (NLMISC::CVector *) ((uint8 *) destPos + posStride);
|
|
|
|
float deltaT;
|
|
|
|
if (!destPos && !destSpeed) return;
|
|
|
|
CPSLocated::TPSAttribParametricInfo::const_iterator it = src->_PInfo.begin() + startIndex,
|
|
endIt = src->_PInfo.begin() + startIndex + numObjects;
|
|
if (!accumulate) // compute coords from initial condition, and applying this force
|
|
{
|
|
if (destPos && !destSpeed) // fills dest pos only
|
|
{
|
|
while (it != endIt)
|
|
{
|
|
deltaT = date - it->Date;
|
|
destPos->x = it->Pos.x + deltaT * it->Speed.x;
|
|
destPos->y = it->Pos.y + deltaT * it->Speed.y;
|
|
destPos->z = it->Pos.z + deltaT * it->Speed.z - 0.5f * deltaT * deltaT * _K;
|
|
++it;
|
|
NEXT_POS;
|
|
}
|
|
}
|
|
else if (!destPos && destSpeed) // fills dest speed only
|
|
{
|
|
while (it != endIt)
|
|
{
|
|
deltaT = date - it->Date;
|
|
destSpeed->x = it->Speed.x;
|
|
destSpeed->y = it->Speed.y;
|
|
destSpeed->z = it->Speed.z - deltaT * _K;
|
|
++it;
|
|
NEXT_SPEED;
|
|
}
|
|
}
|
|
else // fills both speed and pos
|
|
{
|
|
while (it != endIt)
|
|
{
|
|
deltaT = date - it->Date;
|
|
destPos->x = it->Pos.x + deltaT * it->Speed.x;
|
|
destPos->y = it->Pos.y + deltaT * it->Speed.y;
|
|
destPos->z = it->Pos.z + deltaT * it->Speed.z - 0.5f * deltaT * deltaT * _K;
|
|
|
|
destSpeed->x = it->Speed.x;
|
|
destSpeed->y = it->Speed.y;
|
|
destSpeed->z = it->Speed.z - deltaT * _K;
|
|
|
|
++it;
|
|
NEXT_POS;
|
|
NEXT_SPEED;
|
|
}
|
|
}
|
|
}
|
|
else // accumulate datas
|
|
{
|
|
if (destPos && !destSpeed) // fills dest pos only
|
|
{
|
|
while (it != endIt)
|
|
{
|
|
deltaT = date - it->Date;
|
|
destPos->z -= 0.5f * deltaT * deltaT * _K;
|
|
++it;
|
|
NEXT_POS;
|
|
}
|
|
}
|
|
else if (!destPos && destSpeed) // fills dest speed only
|
|
{
|
|
while (it != endIt)
|
|
{
|
|
deltaT = date - it->Date;
|
|
destSpeed->z -= deltaT * _K;
|
|
++it;
|
|
NEXT_SPEED;
|
|
}
|
|
}
|
|
else // fills both speed and pos
|
|
{
|
|
while (it != endIt)
|
|
{
|
|
deltaT = date - it->Date;
|
|
destPos->z -= 0.5f * deltaT * deltaT * _K;
|
|
destSpeed->z -= deltaT * _K;
|
|
++it;
|
|
NEXT_POS;
|
|
NEXT_SPEED;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPSGravity::integrateSingle(float startDate, float deltaT, uint numStep,
|
|
const CPSLocated *src, uint32 indexInLocated,
|
|
NLMISC::CVector *destPos,
|
|
bool accumulate /*= false*/,
|
|
uint stride/* = sizeof(NLMISC::CVector)*/) const
|
|
{
|
|
NL_PS_FUNC(CPSGravity_CVector )
|
|
nlassert(src->isParametricMotionEnabled());
|
|
//nlassert(deltaT > 0);
|
|
nlassert(numStep > 0);
|
|
#ifdef NL_DEBUG
|
|
NLMISC::CVector *endPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride * numStep);
|
|
#endif
|
|
const CPSLocated::CParametricInfo &pi = src->_PInfo[indexInLocated];
|
|
const NLMISC::CVector &startPos = pi.Pos;
|
|
if (numStep != 0)
|
|
{
|
|
if (!accumulate)
|
|
{
|
|
destPos = FillBufUsingSubdiv(startPos, pi.Date, startDate, deltaT, numStep, destPos, stride);
|
|
if (numStep != 0)
|
|
{
|
|
float currDate = startDate - pi.Date;
|
|
nlassert(currDate >= 0);
|
|
const NLMISC::CVector &startSpeed = pi.Speed;
|
|
do
|
|
{
|
|
#ifdef NL_DEBUG
|
|
nlassert(destPos < endPos);
|
|
#endif
|
|
float halfTimeSquare = 0.5f * currDate * currDate;
|
|
destPos->x = startPos.x + currDate * startSpeed.x;
|
|
destPos->y = startPos.y + currDate * startSpeed.y;
|
|
destPos->z = startPos.z + currDate * startSpeed.z - _K * halfTimeSquare;
|
|
currDate += deltaT;
|
|
destPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride);
|
|
}
|
|
while (--numStep);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint numToSkip = ScaleFloatGE(startDate, deltaT, pi.Date, numStep);
|
|
if (numToSkip < numStep)
|
|
{
|
|
numStep -= numToSkip;
|
|
float currDate = startDate + deltaT * numToSkip - pi.Date;
|
|
do
|
|
{
|
|
#ifdef NL_DEBUG
|
|
nlassert(destPos < endPos);
|
|
#endif
|
|
float halfTimeSquare = 0.5f * currDate * currDate;
|
|
destPos->z -= _K * halfTimeSquare;
|
|
currDate += deltaT;
|
|
destPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride);
|
|
}
|
|
while (--numStep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CPSGravity::setIntensity(float value)
|
|
{
|
|
NL_PS_FUNC(CPSGravity_setIntensity)
|
|
if (_IntensityScheme)
|
|
{
|
|
CPSForceIntensityHelper::setIntensity(value);
|
|
renewIntegrable(); // integrable again
|
|
}
|
|
else
|
|
{
|
|
CPSForceIntensityHelper::setIntensity(value);
|
|
}
|
|
}
|
|
|
|
void CPSGravity::setIntensityScheme(CPSAttribMaker<float> *scheme)
|
|
{
|
|
NL_PS_FUNC(CPSGravity_setIntensityScheme)
|
|
if (!_IntensityScheme)
|
|
{
|
|
cancelIntegrable(); // not integrable anymore
|
|
}
|
|
CPSForceIntensityHelper::setIntensityScheme(scheme);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////
|
|
// CPSCentralGravity implementation //
|
|
/////////////////////////////////////////
|
|
|
|
void CPSCentralGravity::computeForces(CPSLocated &target)
|
|
{
|
|
NL_PS_FUNC(CPSCentralGravity_computeForces)
|
|
nlassert(CParticleSystem::InsideSimLoop);
|
|
// for each central gravity, and each target, we check if they are in the same basis
|
|
// if not, we need to transform the central gravity attachment pos into the target basis
|
|
uint32 size = _Owner->getSize();
|
|
// a vector that goes from the gravity to the object
|
|
CVector centerToObj;
|
|
float dist;
|
|
|
|
for (uint32 k = 0; k < size; ++k)
|
|
{
|
|
const float ellapsedTimexK = CParticleSystem::EllapsedTime * (_IntensityScheme ? _IntensityScheme->get(_Owner, k) : _K);
|
|
const CMatrix &m = CPSLocated::getConversionMatrix(&target, this->_Owner);
|
|
const CVector center = m * (_Owner->getPos()[k]);
|
|
TPSAttribVector::iterator it2 = target.getSpeed().begin(), it2End = target.getSpeed().end();
|
|
TPSAttribFloat::const_iterator invMassIt = target.getInvMass().begin();
|
|
TPSAttribVector::const_iterator posIt = target.getPos().begin();
|
|
for (; it2 != it2End; ++it2, ++invMassIt, ++posIt)
|
|
{
|
|
// our equation does 1 / r attenuation, which is not realistic, but fast ...
|
|
centerToObj = center - *posIt;
|
|
|
|
dist = centerToObj * centerToObj;
|
|
if (dist > 10E-6f)
|
|
{
|
|
(*it2) += (*invMassIt) * ellapsedTimexK * (1.f / dist) * centerToObj;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPSCentralGravity::show()
|
|
{
|
|
NL_PS_FUNC(CPSCentralGravity_show)
|
|
CVector I = CVector::I;
|
|
CVector J = CVector::J;
|
|
|
|
const CVector tab[] = { -I - J, I - J
|
|
,-I + J, I + J
|
|
, I - J, I + J
|
|
, -I - J, -I + J
|
|
, I + J, -I - J
|
|
, I - J, J - I
|
|
};
|
|
const uint tabSize = sizeof(tab) / (2 * sizeof(CVector));
|
|
|
|
const float sSize = 0.08f;
|
|
displayIcon2d(tab, tabSize, sSize);
|
|
}
|
|
|
|
void CPSCentralGravity::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSCentralGravity_IStream )
|
|
f.serialVersion(1);
|
|
CPSForceIntensityHelper::serial(f);
|
|
}
|
|
|
|
|
|
/////////////////////////////////
|
|
// CPSSpring implementation //
|
|
/////////////////////////////////
|
|
|
|
|
|
|
|
void CPSSpring::computeForces(CPSLocated &target)
|
|
{
|
|
NL_PS_FUNC(CPSSpring_computeForces)
|
|
nlassert(CParticleSystem::InsideSimLoop);
|
|
// for each spring, and each target, we check if they are in the same basis
|
|
// if not, we need to transform the spring attachment pos into the target basis
|
|
uint32 size = _Owner->getSize();
|
|
for (uint32 k = 0; k < size; ++k)
|
|
{
|
|
const float ellapsedTimexK = CParticleSystem::EllapsedTime * (_IntensityScheme ? _IntensityScheme->get(_Owner, k) : _K);
|
|
const CMatrix &m = CPSLocated::getConversionMatrix(&target, this->_Owner);
|
|
const CVector center = m * (_Owner->getPos()[k]);
|
|
TPSAttribVector::iterator it = target.getSpeed().begin(), itEnd = target.getSpeed().end();
|
|
TPSAttribFloat::const_iterator invMassIt = target.getInvMass().begin();
|
|
TPSAttribVector::const_iterator posIt = target.getPos().begin();
|
|
for (; it != itEnd; ++it, ++invMassIt, ++posIt)
|
|
{
|
|
// apply the spring equation
|
|
(*it) += (*invMassIt) * ellapsedTimexK * (center - *posIt);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CPSSpring::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSSpring_serial)
|
|
f.serialVersion(1);
|
|
CPSForceIntensityHelper::serial(f);
|
|
}
|
|
|
|
|
|
|
|
void CPSSpring::show()
|
|
{
|
|
NL_PS_FUNC(CPSSpring_show)
|
|
CVector I = CVector::I;
|
|
CVector J = CVector::J;
|
|
static const CVector tab[] =
|
|
{
|
|
-I + 2 * J,
|
|
I + 2 * J,
|
|
I + 2 * J, -I + J,
|
|
-I + J, I + J,
|
|
I + J, -I,
|
|
-I, I,
|
|
I, -I - J,
|
|
-I - J, I - J,
|
|
I - J,
|
|
- I - 2 * J,
|
|
- I - 2 * J,
|
|
I - 2 * J
|
|
};
|
|
const uint tabSize = sizeof(tab) / (2 * sizeof(CVector));
|
|
const float sSize = 0.08f;
|
|
displayIcon2d(tab, tabSize, sSize);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////
|
|
// CPSCylindricVortex implementation //
|
|
/////////////////////////////////////////
|
|
void CPSCylindricVortex::computeForces(CPSLocated &target)
|
|
{
|
|
NL_PS_FUNC(CPSCylindricVortex_computeForces)
|
|
nlassert(CParticleSystem::InsideSimLoop);
|
|
uint32 size = _Owner->getSize();
|
|
for (uint32 k = 0; k < size; ++k) // for each vortex
|
|
{
|
|
const float invR = 1.f / _Radius[k];
|
|
const float radius2 = _Radius[k] * _Radius[k];
|
|
// intensity for this vortex
|
|
nlassert(_Owner);
|
|
float intensity = (_IntensityScheme ? _IntensityScheme->get(_Owner, k) : _K);
|
|
// express the vortex position and plane normal in the located basis
|
|
const CMatrix &m = CPSLocated::getConversionMatrix(&target, this->_Owner);
|
|
const CVector center = m * (_Owner->getPos()[k]);
|
|
const CVector n = m.mulVector(_Normal[k]);
|
|
TPSAttribVector::iterator speedIt = target.getSpeed().begin(), speedItEnd = target.getSpeed().end();
|
|
TPSAttribFloat::const_iterator invMassIt = target.getInvMass().begin();
|
|
TPSAttribVector::const_iterator posIt = target.getPos().begin();
|
|
// projection of the current located pos on the vortex axis
|
|
CVector p;
|
|
// a vector that go from the vortex center to the point we're dealing with
|
|
CVector v2p;
|
|
// the square of the dist of the projected pos
|
|
float d2 , d;
|
|
CVector realTangentialSpeed;
|
|
CVector tangentialSpeed;
|
|
CVector radialSpeed;
|
|
for (; speedIt != speedItEnd; ++speedIt, ++invMassIt, ++posIt)
|
|
{
|
|
v2p = *posIt - center;
|
|
p = v2p - (v2p * n) * n;
|
|
d2 = p * p;
|
|
if (d2 < radius2) // not out of range ?
|
|
{
|
|
if (d2 > 10E-6)
|
|
{
|
|
d = sqrtf(d2);
|
|
p *= 1.f / d;
|
|
// compute the speed vect that we should have (normalized)
|
|
realTangentialSpeed = n ^ p;
|
|
tangentialSpeed = (*speedIt * realTangentialSpeed) * realTangentialSpeed;
|
|
radialSpeed = (p * *speedIt) * p;
|
|
// update radial speed;
|
|
*speedIt -= _RadialViscosity * CParticleSystem::EllapsedTime * radialSpeed;
|
|
// update tangential speed
|
|
*speedIt -= _TangentialViscosity * intensity * CParticleSystem::EllapsedTime * (tangentialSpeed - (1.f - d * invR) * realTangentialSpeed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CPSCylindricVortex::show()
|
|
{
|
|
NL_PS_FUNC(CPSCylindricVortex_show)
|
|
CPSLocated *loc;
|
|
uint32 index;
|
|
CPSLocatedBindable *lb;
|
|
_Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
|
|
|
|
|
|
// must have set this
|
|
nlassert(getFontGenerator() && getFontGenerator());
|
|
setupDriverModelMatrix();
|
|
|
|
for (uint k = 0; k < _Owner->getSize(); ++k)
|
|
{
|
|
const CRGBA col = ((lb == NULL || this == lb) && loc == _Owner && index == k ? CRGBA::Red : CRGBA(127, 127, 127));
|
|
CMatrix m;
|
|
CPSUtil::buildSchmidtBasis(_Normal[k], m);
|
|
CPSUtil::displayDisc(*getDriver(), _Radius[k], _Owner->getPos()[k], m, 32, col);
|
|
CPSUtil::displayArrow(getDriver(), _Owner->getPos()[k], _Normal[k], 1.f, col, CRGBA(200, 0, 200));
|
|
// display a V letter at the center
|
|
CPSUtil::print(getDriver(), std::string("v"), *getFontGenerator(), *getFontManager(), _Owner->getPos()[k], 80.f);
|
|
}
|
|
|
|
}
|
|
|
|
void CPSCylindricVortex::setMatrix(uint32 index, const CMatrix &m)
|
|
{
|
|
NL_PS_FUNC(CPSCylindricVortex_setMatrix)
|
|
nlassert(index < _Normal.getSize());
|
|
_Normal[index] = m.getK();
|
|
_Owner->getPos()[index] = m.getPos();
|
|
}
|
|
|
|
CMatrix CPSCylindricVortex::getMatrix(uint32 index) const
|
|
{
|
|
NL_PS_FUNC(CPSCylindricVortex_getMatrix)
|
|
CMatrix m;
|
|
CPSUtil::buildSchmidtBasis(_Normal[index], m);
|
|
m.setPos(_Owner->getPos()[index] );
|
|
return m;
|
|
}
|
|
|
|
|
|
void CPSCylindricVortex::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSCylindricVortex_IStream )
|
|
f.serialVersion(1);
|
|
CPSForceIntensityHelper::serial(f);
|
|
f.serial(_Normal);
|
|
f.serial(_Radius);
|
|
f.serial(_RadialViscosity);
|
|
f.serial(_TangentialViscosity);
|
|
}
|
|
|
|
void CPSCylindricVortex::newElement(const CPSEmitterInfo &info)
|
|
{
|
|
NL_PS_FUNC(CPSCylindricVortex_newElement)
|
|
CPSForceIntensityHelper::newElement(info);
|
|
_Normal.insert(CVector::K);
|
|
_Radius.insert(1.f);
|
|
}
|
|
void CPSCylindricVortex::deleteElement(uint32 index)
|
|
{
|
|
NL_PS_FUNC(CPSCylindricVortex_deleteElement)
|
|
CPSForceIntensityHelper::deleteElement(index);
|
|
_Normal.remove(index);
|
|
_Radius.remove(index);
|
|
}
|
|
void CPSCylindricVortex::resize(uint32 size)
|
|
{
|
|
NL_PS_FUNC(CPSCylindricVortex_resize)
|
|
nlassert(size < (1 << 16));
|
|
CPSForceIntensityHelper::resize(size);
|
|
_Normal.resize(size);
|
|
_Radius.resize(size);
|
|
}
|
|
|
|
|
|
/**
|
|
* a magnetic field that has the given direction
|
|
*/
|
|
|
|
void CPSMagneticForce::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSMagneticForce_serial)
|
|
f.serialVersion(1);
|
|
CPSDirectionnalForce::serial(f);
|
|
}
|
|
|
|
void CPSMagneticForce::computeForces(CPSLocated &target)
|
|
{
|
|
NL_PS_FUNC(CPSMagneticForce_computeForces)
|
|
nlassert(CParticleSystem::InsideSimLoop);
|
|
// perform the operation on each target
|
|
for (uint32 k = 0; k < _Owner->getSize(); ++k)
|
|
{
|
|
float intensity = CParticleSystem::EllapsedTime * (_IntensityScheme ? _IntensityScheme->get(_Owner, k) : _K);
|
|
NLMISC::CVector toAdd = CPSLocated::getConversionMatrix(&target, this->_Owner).mulVector(_Dir); // express this in the target basis
|
|
TPSAttribVector::iterator it = target.getSpeed().begin(), itend = target.getSpeed().end();
|
|
// 1st case : non-constant mass
|
|
if (target.getMassScheme())
|
|
{
|
|
TPSAttribFloat::const_iterator invMassIt = target.getInvMass().begin();
|
|
for (; it != itend; ++it, ++invMassIt)
|
|
{
|
|
(*it) += intensity * *invMassIt * (*it ^ toAdd);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float i = intensity / target.getInitialMass();
|
|
for (; it != itend; ++it)
|
|
{
|
|
(*it) += i * (*it ^ toAdd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Brownian force implementation
|
|
*/
|
|
|
|
const uint BFNumPredefinedPos = 8192; // should be a power of 2
|
|
const uint BFPredefinedNumInterp = 256; /** this should divide BFNumPredefinedPos. This define the number
|
|
* of values used to interpolate between 2 position of the npose
|
|
* (because we don't filter values when we access them)
|
|
*/
|
|
const uint BFNumPrecomputedImpulsions = 1024; /// used to avoid to have to call rand for each particle the force applies on...
|
|
|
|
NLMISC::CVector CPSBrownianForce::PrecomputedPos[BFNumPredefinedPos]; // after the sequence we must be back to the start position
|
|
NLMISC::CVector CPSBrownianForce::PrecomputedSpeed[BFNumPredefinedPos];
|
|
NLMISC::CVector CPSBrownianForce::PrecomputedImpulsions[BFNumPrecomputedImpulsions];
|
|
|
|
///==========================================================
|
|
CPSBrownianForce::CPSBrownianForce(float intensity /* = 1.f*/) : _ParametricFactor(1.f)
|
|
{
|
|
NL_PS_FUNC(CPSBrownianForce_CPSBrownianForce)
|
|
setIntensity(intensity);
|
|
if (CParticleSystem::getSerializeIdentifierFlag()) _Name = std::string("BrownianForce");
|
|
|
|
}
|
|
|
|
///==========================================================
|
|
bool CPSBrownianForce::isIntegrable(void) const
|
|
{
|
|
NL_PS_FUNC(CPSBrownianForce_isIntegrable)
|
|
return _IntensityScheme == NULL;
|
|
}
|
|
|
|
|
|
///==========================================================
|
|
void CPSBrownianForce::integrate(float date, CPSLocated *src,
|
|
uint32 startIndex,
|
|
uint32 numObjects,
|
|
NLMISC::CVector *destPos,
|
|
NLMISC::CVector *destSpeed,
|
|
bool accumulate,
|
|
uint posStride, uint speedStride
|
|
) const
|
|
{
|
|
NL_PS_FUNC(CPSBrownianForce_integrate)
|
|
/// MASS DIFFERENT FROM 1 IS NOT SUPPORTED
|
|
float deltaT;
|
|
if (!destPos && !destSpeed) return;
|
|
CPSLocated::TPSAttribParametricInfo::const_iterator it = src->_PInfo.begin() + startIndex,
|
|
endIt = src->_PInfo.begin() + startIndex + numObjects;
|
|
float lookUpFactor = _ParametricFactor * BFNumPredefinedPos;
|
|
float speedFactor = _ParametricFactor * _K;
|
|
if (!accumulate) // compute coords from initial condition, and applying this force
|
|
{
|
|
if (destPos && !destSpeed) // fills dest pos only
|
|
{
|
|
while (it != endIt)
|
|
{
|
|
float deltaT = date - it->Date;
|
|
uint index = (uint) (lookUpFactor * deltaT) & (BFNumPredefinedPos - 1);
|
|
destPos->set(it->Pos.x + deltaT * it->Speed.x + _K * PrecomputedPos[index].x,
|
|
it->Pos.y + deltaT * it->Speed.y + _K * PrecomputedPos[index].y,
|
|
it->Pos.z + deltaT * it->Speed.z + _K * PrecomputedPos[index].z );
|
|
++it;
|
|
NEXT_POS;
|
|
}
|
|
}
|
|
else if (!destPos && destSpeed) // fills dest speed only
|
|
{
|
|
while (it != endIt)
|
|
{
|
|
deltaT = date - it->Date;
|
|
uint index = (uint) (lookUpFactor * deltaT) & (BFNumPredefinedPos - 1);
|
|
destSpeed->x = it->Speed.x + speedFactor * PrecomputedSpeed[index].x;
|
|
destSpeed->y = it->Speed.y + speedFactor * PrecomputedSpeed[index].y;
|
|
destSpeed->z = it->Speed.z + speedFactor * PrecomputedSpeed[index].z;
|
|
++it;
|
|
NEXT_SPEED;
|
|
}
|
|
}
|
|
else // fills both speed and pos
|
|
{
|
|
while (it != endIt)
|
|
{
|
|
deltaT = date - it->Date;
|
|
uint index = (uint) (lookUpFactor * deltaT) & (BFNumPredefinedPos - 1);
|
|
destPos->x = it->Pos.x + deltaT * it->Speed.x + _K * PrecomputedPos[index].x;
|
|
destPos->y = it->Pos.y + deltaT * it->Speed.y + _K * PrecomputedPos[index].y;
|
|
destPos->z = it->Pos.z + deltaT * it->Speed.z + _K * PrecomputedPos[index].z;
|
|
|
|
destSpeed->x = it->Speed.x + speedFactor * PrecomputedSpeed[index].x;
|
|
destSpeed->y = it->Speed.y + speedFactor * PrecomputedSpeed[index].y;
|
|
destSpeed->z = it->Speed.z + speedFactor * PrecomputedSpeed[index].z;
|
|
|
|
++it;
|
|
NEXT_POS;
|
|
NEXT_SPEED;
|
|
}
|
|
}
|
|
}
|
|
else // accumulate datas
|
|
{
|
|
if (destPos && !destSpeed) // fills dest pos only
|
|
{
|
|
while (it != endIt)
|
|
{
|
|
deltaT = date - it->Date;
|
|
uint index = (uint) (lookUpFactor * deltaT) & (BFNumPredefinedPos - 1);
|
|
destPos->set(destPos->x + _K * PrecomputedPos[index].x,
|
|
destPos->y + _K * PrecomputedPos[index].y,
|
|
destPos->z + _K * PrecomputedPos[index].z);
|
|
++it;
|
|
NEXT_POS;
|
|
}
|
|
}
|
|
else if (!destPos && destSpeed) // fills dest speed only
|
|
{
|
|
while (it != endIt)
|
|
{
|
|
deltaT = date - it->Date;
|
|
uint index = (uint) (lookUpFactor * deltaT) & (BFNumPredefinedPos - 1);
|
|
destSpeed->set(destSpeed->x + speedFactor * PrecomputedSpeed[index].x,
|
|
destSpeed->y + speedFactor * PrecomputedSpeed[index].y,
|
|
destSpeed->z + speedFactor * PrecomputedSpeed[index].z);
|
|
++it;
|
|
NEXT_SPEED;
|
|
}
|
|
}
|
|
else // fills both speed and pos
|
|
{
|
|
while (it != endIt)
|
|
{
|
|
deltaT = date - it->Date;
|
|
uint index = (uint) (lookUpFactor * deltaT) & (BFNumPredefinedPos - 1);
|
|
destPos->set(destPos->x + _K * PrecomputedPos[index].x,
|
|
destPos->y + _K * PrecomputedPos[index].y,
|
|
destPos->z + _K * PrecomputedPos[index].z);
|
|
destSpeed->set(destSpeed->x + speedFactor * PrecomputedSpeed[index].x,
|
|
destSpeed->y + speedFactor * PrecomputedSpeed[index].y,
|
|
destSpeed->z + speedFactor * PrecomputedSpeed[index].z);
|
|
++it;
|
|
NEXT_POS;
|
|
NEXT_SPEED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///==========================================================
|
|
void CPSBrownianForce::integrateSingle(float startDate, float deltaT, uint numStep,
|
|
const CPSLocated *src, uint32 indexInLocated,
|
|
NLMISC::CVector *destPos,
|
|
bool accumulate,
|
|
uint stride) const
|
|
{
|
|
NL_PS_FUNC(CPSBrownianForce_integrateSingle)
|
|
nlassert(src->isParametricMotionEnabled());
|
|
//nlassert(deltaT > 0);
|
|
nlassert(numStep > 0);
|
|
#ifdef NL_DEBUG
|
|
NLMISC::CVector *endPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride * numStep);
|
|
#endif
|
|
const CPSLocated::CParametricInfo &pi = src->_PInfo[indexInLocated];
|
|
const NLMISC::CVector &startPos = pi.Pos;
|
|
if (numStep != 0)
|
|
{
|
|
float lookUpFactor = _ParametricFactor * BFPredefinedNumInterp;
|
|
if (!accumulate)
|
|
{
|
|
/// fill start of datas (particle didn't exist at that time, so we fill by the start position)
|
|
destPos = FillBufUsingSubdiv(startPos, pi.Date, startDate, deltaT, numStep, destPos, stride);
|
|
if (numStep != 0)
|
|
{
|
|
float currDate = startDate - pi.Date;
|
|
nlassert(currDate >= 0);
|
|
const NLMISC::CVector &startSpeed = pi.Speed;
|
|
do
|
|
{
|
|
#ifdef NL_DEBUG
|
|
nlassert(destPos < endPos);
|
|
#endif
|
|
uint index = (uint) (lookUpFactor * currDate) & (BFNumPredefinedPos - 1);
|
|
destPos->x = startPos.x + currDate * startSpeed.x + _K * PrecomputedPos[index].x;
|
|
destPos->y = startPos.y + currDate * startSpeed.y + _K * PrecomputedPos[index].y;
|
|
destPos->z = startPos.z + currDate * startSpeed.z + _K * PrecomputedPos[index].z;
|
|
currDate += deltaT;
|
|
destPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride);
|
|
}
|
|
while (--numStep);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint numToSkip = ScaleFloatGE(startDate, deltaT, pi.Date, numStep);
|
|
if (numToSkip < numStep)
|
|
{
|
|
numStep -= numToSkip;
|
|
float currDate = startDate + deltaT * numToSkip - pi.Date;
|
|
do
|
|
{
|
|
#ifdef NL_DEBUG
|
|
nlassert(destPos < endPos);
|
|
#endif
|
|
uint index = (uint) (lookUpFactor * currDate) & (BFNumPredefinedPos - 1);
|
|
destPos->x += _K * PrecomputedPos[index].x;
|
|
destPos->y += _K * PrecomputedPos[index].y;
|
|
destPos->z += _K * PrecomputedPos[index].z;
|
|
currDate += deltaT;
|
|
destPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride);
|
|
}
|
|
while (--numStep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///==========================================================
|
|
void CPSBrownianForce::initPrecalc()
|
|
{
|
|
NL_PS_FUNC(CPSBrownianForce_initPrecalc)
|
|
/// create the pos table
|
|
nlassert(BFNumPredefinedPos % BFPredefinedNumInterp == 0);
|
|
|
|
NLMISC::CVector p0(0, 0, 0), p1;
|
|
const uint numStep = BFNumPredefinedPos / BFPredefinedNumInterp;
|
|
NLMISC::CVector *dest = PrecomputedPos;
|
|
uint k, l;
|
|
for (k = 0; k < numStep; ++k)
|
|
{
|
|
if (k != numStep - 1)
|
|
{
|
|
p1.set(2.f * (NLMISC::frand(1.f) - 0.5f),
|
|
2.f * (NLMISC::frand(1.f) - 0.5f),
|
|
2.f * (NLMISC::frand(1.f) - 0.5f));
|
|
}
|
|
else
|
|
{
|
|
p1.set(0, 0, 0);
|
|
}
|
|
float lambda = 0.f;
|
|
float lambdaStep = 1.f / BFPredefinedNumInterp;
|
|
for (l = 0; l < BFPredefinedNumInterp; ++l)
|
|
{
|
|
*dest++ = lambda * p1 + (1.f - lambda) * p0;
|
|
lambda += lambdaStep;
|
|
}
|
|
p0 = p1;
|
|
}
|
|
|
|
// now, filter the table several time to get something more smooth
|
|
for (k = 0; k < (BFPredefinedNumInterp << 2) ; ++k)
|
|
{
|
|
for (l = 1; l < (BFNumPredefinedPos - 1); ++l)
|
|
{
|
|
PrecomputedPos[l] = 0.5f * (PrecomputedPos[l - 1] + PrecomputedPos[l + 1]);
|
|
}
|
|
}
|
|
|
|
|
|
// compute the table of speeds, by using on a step of 1.s
|
|
for (l = 1; l < (BFNumPredefinedPos - 1); ++l)
|
|
{
|
|
PrecomputedSpeed[l] = 0.5f * (PrecomputedPos[l + 1] - PrecomputedPos[l - 1]);
|
|
}
|
|
PrecomputedSpeed[BFNumPredefinedPos - 1] = NLMISC::CVector::Null;
|
|
|
|
// compute the table of impulsion
|
|
for (k = 0; k < BFNumPrecomputedImpulsions; ++k)
|
|
{
|
|
static double divRand = (2.f / RAND_MAX);
|
|
PrecomputedImpulsions[k].set( (float) (rand() * divRand - 1),
|
|
(float) (rand() * divRand - 1),
|
|
(float) (rand() * divRand - 1)
|
|
);
|
|
}
|
|
}
|
|
|
|
///==========================================================
|
|
void CPSBrownianForce::setIntensity(float value)
|
|
{
|
|
NL_PS_FUNC(CPSBrownianForce_setIntensity)
|
|
if (_IntensityScheme)
|
|
{
|
|
CPSForceIntensity::setIntensity(value);
|
|
renewIntegrable(); // integrable again
|
|
}
|
|
else
|
|
{
|
|
CPSForceIntensity::setIntensity(value);
|
|
}
|
|
}
|
|
|
|
|
|
///==========================================================
|
|
void CPSBrownianForce::setIntensityScheme(CPSAttribMaker<float> *scheme)
|
|
{
|
|
NL_PS_FUNC(CPSBrownianForce_setIntensityScheme)
|
|
if (!_IntensityScheme)
|
|
{
|
|
cancelIntegrable(); // not integrable anymore
|
|
}
|
|
CPSForceIntensity::setIntensityScheme(scheme);
|
|
}
|
|
|
|
///==========================================================
|
|
void CPSBrownianForce::computeForces(CPSLocated &target)
|
|
{
|
|
NL_PS_FUNC(CPSBrownianForce_computeForces)
|
|
nlassert(CParticleSystem::InsideSimLoop);
|
|
// perform the operation on each target
|
|
for (uint32 k = 0; k < _Owner->getSize(); ++k)
|
|
{
|
|
float intensity = _IntensityScheme ? _IntensityScheme->get(_Owner, k) : _K;
|
|
uint32 size = target.getSize();
|
|
if (!size) continue;
|
|
TPSAttribVector::iterator it2 = target.getSpeed().begin(), it2End;
|
|
/// start at a random position in the precomp impulsion tab
|
|
uint startPos = (uint) ::rand() % BFNumPrecomputedImpulsions;
|
|
NLMISC::CVector *imp = PrecomputedImpulsions + startPos;
|
|
|
|
if (target.getMassScheme())
|
|
{
|
|
float intensityXtime = intensity * CParticleSystem::EllapsedTime;
|
|
TPSAttribFloat::const_iterator invMassIt = target.getInvMass().begin();
|
|
do
|
|
{
|
|
uint toProcess = std::min((uint) (BFNumPrecomputedImpulsions - startPos), (uint) size);
|
|
it2End = it2 + toProcess;
|
|
do
|
|
{
|
|
float factor = intensityXtime * *invMassIt;
|
|
it2->set(it2->x + factor * imp->x,
|
|
it2->y + factor * imp->y,
|
|
it2->z + factor * imp->x);
|
|
++invMassIt;
|
|
++imp;
|
|
++it2;
|
|
}
|
|
while (it2 != it2End);
|
|
startPos = 0;
|
|
imp = PrecomputedImpulsions;
|
|
size -= toProcess;
|
|
}
|
|
while (size != 0);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
uint toProcess = std::min((uint) (BFNumPrecomputedImpulsions - startPos) , (uint) size);
|
|
it2End = it2 + toProcess;
|
|
float factor = intensity * CParticleSystem::EllapsedTime / target.getInitialMass();
|
|
do
|
|
{
|
|
it2->set(it2->x + factor * imp->x,
|
|
it2->y + factor * imp->y,
|
|
it2->z + factor * imp->x);
|
|
++imp;
|
|
++it2;
|
|
}
|
|
while (it2 != it2End);
|
|
startPos = 0;
|
|
imp = PrecomputedImpulsions;
|
|
size -= toProcess;
|
|
}
|
|
while (size != 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
///=======================================================================
|
|
void CPSBrownianForce::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSBrownianForce_serial)
|
|
sint ver = f.serialVersion(3);
|
|
if (ver <= 2)
|
|
{
|
|
uint8 dummy;
|
|
f.serial(dummy); // old data in version 2 not used anymore
|
|
CPSForce::serial(f);
|
|
f.serial(dummy); // old data in version 2 not used anymore
|
|
serialForceIntensity(f);
|
|
if (f.isReading())
|
|
{
|
|
registerToTargets();
|
|
}
|
|
}
|
|
|
|
if (ver >= 2)
|
|
{
|
|
CPSForceIntensityHelper::serial(f);
|
|
f.serial(_ParametricFactor);
|
|
}
|
|
}
|
|
|
|
|
|
} // NL3D
|