Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / 3d / ps_force.cpp
blobfa42766b10f12b5dc123e87dde69e35b05a3ef5f
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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.
8 //
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/>.
17 #include "std3d.h"
19 #include "nel/3d/ps_force.h"
20 #include "nel/3d/driver.h"
21 #include "nel/3d/index_buffer.h"
22 #include "nel/3d/material.h"
23 #include "nel/3d/vertex_buffer.h"
24 #include "nel/3d/computed_string.h"
25 #include "nel/3d/font_manager.h"
26 #include "nel/3d/particle_system.h"
27 #include "nel/misc/common.h"
28 #include "nel/3d/ps_util.h"
29 #include "nel/3d/ps_misc.h"
31 #ifdef DEBUG_NEW
32 #define new DEBUG_NEW
33 #endif
35 namespace NL3D {
39 * Constructor
41 CPSForce::CPSForce()
43 NL_PS_FUNC(CPSForce_CPSForce)
48 void CPSForce::serial(NLMISC::IStream &f)
50 NL_PS_FUNC(CPSForce_serial)
51 f.serialVersion(1);
52 CPSTargetLocatedBindable::serial(f);
53 CPSLocatedBindable::serial(f);
57 void CPSForce::registerToTargets(void)
59 NL_PS_FUNC(CPSForce_registerToTargets)
60 for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
62 if (this->isIntegrable())
64 (*it)->registerIntegrableForce(this);
66 else
68 (*it)->addNonIntegrableForceRef();
74 void CPSForce::step(TPSProcessPass pass)
76 NL_PS_FUNC(CPSForce_step)
77 switch(pass)
79 case PSToolRender:
80 show();
81 break;
82 default: break;
88 void CPSForce::attachTarget(CPSLocated *ptr)
90 NL_PS_FUNC(CPSForce_attachTarget)
91 nlassert(_Owner);
92 CPSTargetLocatedBindable::attachTarget(ptr);
93 // check whether we are integrable, and if so, add us to the list
94 if (this->isIntegrable())
96 ptr->registerIntegrableForce(this);
98 else
100 ptr->addNonIntegrableForceRef();
104 void CPSForce::releaseTargetRsc(CPSLocated *target)
106 NL_PS_FUNC(CPSForce_releaseTargetRsc)
107 if (this->isIntegrable())
109 target->unregisterIntegrableForce(this);
111 else
113 target->releaseNonIntegrableForceRef();
119 void CPSForce::basisChanged(TPSMatrixMode matrixMode)
121 NL_PS_FUNC(CPSForce_basisChanged)
122 if (!this->isIntegrable()) return;
123 for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
125 (*it)->integrableForceBasisChanged(matrixMode);
130 void CPSForce::cancelIntegrable(void)
132 NL_PS_FUNC(CPSForce_cancelIntegrable)
133 nlassert(_Owner);
134 for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
136 if ((*it)->getMatrixMode() == _Owner->getMatrixMode())
138 (*it)->unregisterIntegrableForce(this);
139 (*it)->addNonIntegrableForceRef();
145 void CPSForce::renewIntegrable(void)
147 NL_PS_FUNC(CPSForce_renewIntegrable)
148 nlassert(_Owner);
149 for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
151 if ((*it)->getMatrixMode() == _Owner->getMatrixMode())
153 (*it)->registerIntegrableForce(this);
154 (*it)->releaseNonIntegrableForceRef();
161 ///////////////////////////////////////
162 // CPSForceIntensity implementation //
163 ///////////////////////////////////////
165 void CPSForceIntensity::setIntensity(float value)
167 NL_PS_FUNC(CPSForceIntensity_setIntensity)
168 if (_IntensityScheme)
170 delete _IntensityScheme;
171 _IntensityScheme = NULL;
173 _K = value;
177 CPSForceIntensity::~CPSForceIntensity()
179 NL_PS_FUNC(CPSForceIntensity_CPSForceIntensityDtor)
180 delete _IntensityScheme;
183 void CPSForceIntensity::setIntensityScheme(CPSAttribMaker<float> *scheme)
185 NL_PS_FUNC(CPSForceIntensity_setIntensityScheme)
186 nlassert(scheme);
187 delete _IntensityScheme;
188 _IntensityScheme = scheme;
189 if (getForceIntensityOwner() && scheme->hasMemory()) scheme->resize(getForceIntensityOwner()->getMaxSize(), getForceIntensityOwner()->getSize());
192 void CPSForceIntensity::serialForceIntensity(NLMISC::IStream &f)
194 NL_PS_FUNC(CPSForceIntensity_IStream )
195 f.serialVersion(1);
196 if (!f.isReading())
198 if (_IntensityScheme)
200 bool bFalse = false;
201 f.serial(bFalse);
202 f.serialPolyPtr(_IntensityScheme);
204 else
206 bool bTrue = true;
207 f.serial(bTrue);
208 f.serial(_K);
211 else
213 bool constantIntensity;
214 f.serial(constantIntensity);
215 if (constantIntensity)
217 f.serial(_K);
219 else
221 f.serialPolyPtr(_IntensityScheme);
227 ////////////////////////////////////////
228 // CPSForceIntensityHelper //
229 ////////////////////////////////////////
232 void CPSForceIntensityHelper::serial(NLMISC::IStream &f)
234 NL_PS_FUNC(CPSForceIntensityHelper_serial)
235 f.serialVersion(1);
236 CPSForce::serial(f);
237 serialForceIntensity(f);
238 if (f.isReading())
240 registerToTargets();
246 ////////////////////////////////////////
247 // CPSDirectionalForce implementation //
248 ////////////////////////////////////////
250 void CPSDirectionnalForce::computeForces(CPSLocated &target)
252 NL_PS_FUNC(CPSDirectionnalForce_computeForces)
253 nlassert(CParticleSystem::InsideSimLoop);
254 // perform the operation on each target
255 CVector toAdd;
256 for (uint32 k = 0; k < _Owner->getSize(); ++k)
258 CVector toAddLocal;
259 CVector dir;
261 if (_GlobalValueHandle.isValid()) // is direction a global variable ?
263 dir = _GlobalValueHandle.get(); // takes direction from global variable instead
265 else
267 dir = _Dir;
269 toAddLocal = CParticleSystem::EllapsedTime * (_IntensityScheme ? _IntensityScheme->get(_Owner, k) : _K ) * dir;
270 toAdd = CPSLocated::getConversionMatrix(&target, this->_Owner).mulVector(toAddLocal); // express this in the target basis
271 TPSAttribVector::iterator it = target.getSpeed().begin(), itend = target.getSpeed().end();
272 // 1st case : non-constant mass
273 if (target.getMassScheme())
275 TPSAttribFloat::const_iterator invMassIt = target.getInvMass().begin();
276 for (;it != itend; ++it, ++invMassIt)
278 (*it) += *invMassIt * toAdd;
282 else
284 // the mass is constant
285 toAdd /= target.getInitialMass();
286 for (; it != itend; ++it)
288 (*it) += toAdd;
295 void CPSDirectionnalForce::show()
297 NL_PS_FUNC(CPSDirectionnalForce_show)
298 CPSLocated *loc;
299 uint32 index;
300 CPSLocatedBindable *lb;
301 _Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
303 setupDriverModelMatrix();
305 CVector dir;
306 if (_GlobalValueHandle.isValid()) // is direction a global variable ?
308 dir = _GlobalValueHandle.get(); // takes direction from global variable instead
310 else
312 dir = _Dir;
315 // for each element, see if it is the selected element, and if yes, display in red
316 for (uint k = 0; k < _Owner->getSize(); ++k)
318 const CRGBA col = (((lb == NULL || this == lb) && loc == _Owner && index == k) ? CRGBA::Red : CRGBA(127, 127, 127));
319 CPSUtil::displayArrow(getDriver(), _Owner->getPos()[k], dir, 1.f, col, CRGBA(80, 80, 0));
323 void CPSDirectionnalForce::serial(NLMISC::IStream &f)
325 NL_PS_FUNC(CPSDirectionnalForce_serial)
326 // Version 2 : added link to a global vector value
328 sint ver = f.serialVersion(2);
329 CPSForceIntensityHelper::serial(f);
330 if (ver == 1)
332 f.serial(_Dir);
333 _GlobalValueHandle.reset();
335 else
337 bool useHandle = _GlobalValueHandle.isValid();
338 f.serial(useHandle);
339 if (useHandle)
341 // a global value is used
342 if (f.isReading())
344 std::string handleName;
345 f.serial(handleName);
346 // retrieve a handle to the global value from the particle system
347 _GlobalValueHandle = CParticleSystem::getGlobalVectorValueHandle(handleName);
349 else
351 std::string handleName = _GlobalValueHandle.getName();
352 f.serial(handleName);
355 else
357 f.serial(_Dir);
362 void CPSDirectionnalForce::enableGlobalVectorValue(const std::string &name)
364 NL_PS_FUNC(CPSDirectionnalForce_enableGlobalVectorValue)
365 if (name.empty())
367 _GlobalValueHandle.reset();
368 return;
370 _GlobalValueHandle = CParticleSystem::getGlobalVectorValueHandle(name);
373 std::string CPSDirectionnalForce::getGlobalVectorValueName() const
375 NL_PS_FUNC(CPSDirectionnalForce_getGlobalVectorValueName)
376 return _GlobalValueHandle.isValid() ? _GlobalValueHandle.getName() : "";
379 ////////////////////////////
380 // gravity implementation //
381 ////////////////////////////
382 void CPSGravity::computeForces(CPSLocated &target)
384 NL_PS_FUNC(CPSGravity_computeForces)
385 nlassert(CParticleSystem::InsideSimLoop);
386 // perform the operation on each target
387 CVector toAdd;
388 for (uint32 k = 0; k < _Owner->getSize(); ++k)
390 CVector toAddLocal = CParticleSystem::EllapsedTime * CVector(0, 0, _IntensityScheme ? - _IntensityScheme->get(_Owner, k) : - _K);
391 toAdd = CPSLocated::getConversionMatrix(&target, this->_Owner).mulVector(toAddLocal); // express this in the target basis
392 TPSAttribVector::iterator it = target.getSpeed().begin(), itend = target.getSpeed().end();
394 if (toAdd.x && toAdd.y)
396 for (; it != itend; ++it)
398 (*it) += toAdd;
402 else // only the z component is not null, which should be the majority of cases ...
404 for (; it != itend; ++it)
406 it->z += toAdd.z;
413 void CPSGravity::show()
415 NL_PS_FUNC(CPSGravity_show)
416 CVector I = computeI();
417 CVector K = CVector(0,0,1);
419 // this is not designed for efficiency (target : edition code)
420 CIndexBuffer pb;
421 CVertexBuffer vb;
422 CMaterial material;
423 IDriver *driver = getDriver();
424 const float toolSize = 0.2f;
426 vb.setVertexFormat(CVertexBuffer::PositionFlag);
427 vb.setNumVertices(6);
429 CVertexBufferReadWrite vba;
430 vb.lock (vba);
431 vba.setVertexCoord(0, -toolSize * I);
432 vba.setVertexCoord(1, toolSize * I);
433 vba.setVertexCoord(2, CVector(0, 0, 0));
434 vba.setVertexCoord(3, -6.0f * toolSize * K);
435 vba.setVertexCoord(4, -toolSize * I - 5.0f * toolSize * K);
436 vba.setVertexCoord(5, toolSize * I - 5.0f * toolSize * K);
438 pb.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT);
439 pb.setNumIndexes(2*4);
441 CIndexBufferReadWrite ibaWrite;
442 pb.lock (ibaWrite);
443 ibaWrite.setLine(0, 0, 1);
444 ibaWrite.setLine(2, 2, 3);
445 ibaWrite.setLine(4, 4, 3);
446 ibaWrite.setLine(6, 3, 5);
449 material.setColor(CRGBA(127, 127, 127));
450 material.setLighting(false);
451 material.setBlendFunc(CMaterial::one, CMaterial::one);
452 material.setZWrite(false);
453 material.setBlend(true);
456 CMatrix mat;
458 for (TPSAttribVector::const_iterator it = _Owner->getPos().begin(); it != _Owner->getPos().end(); ++it)
460 mat.identity();
461 mat.translate(*it);
462 mat = getLocalToWorldMatrix() * mat;
464 driver->setupModelMatrix(mat);
465 driver->activeVertexBuffer(vb);
466 driver->activeIndexBuffer(pb);
467 driver->renderLines(material, 0, pb.getNumIndexes()/2);
471 // affiche un g a cote de la force
473 CVector pos = *it + CVector(1.5f * toolSize, 0, -1.2f * toolSize);
475 pos = getLocalToWorldMatrix() * pos;
478 // must have set this
479 nlassert(getFontGenerator() && getFontGenerator());
481 CPSUtil::print(driver, std::string("G")
482 , *getFontGenerator()
483 , *getFontManager()
484 , pos
485 , 80.0f * toolSize );
491 void CPSGravity::serial(NLMISC::IStream &f)
493 NL_PS_FUNC(CPSGravity_IStream )
494 f.serialVersion(1);
495 CPSForceIntensityHelper::serial(f);
499 bool CPSGravity::isIntegrable(void) const
501 NL_PS_FUNC(CPSGravity_isIntegrable)
502 return _IntensityScheme == NULL;
505 void CPSGravity::integrate(float date, CPSLocated *src, uint32 startIndex, uint32 numObjects, NLMISC::CVector *destPos, NLMISC::CVector *destSpeed,
506 bool accumulate,
507 uint posStride, uint speedStride
508 ) const
510 NL_PS_FUNC(CPSGravity_integrate)
511 #define NEXT_SPEED destSpeed = (NLMISC::CVector *) ((uint8 *) destSpeed + speedStride);
512 #define NEXT_POS destPos = (NLMISC::CVector *) ((uint8 *) destPos + posStride);
514 float deltaT;
516 if (!destPos && !destSpeed) return;
518 CPSLocated::TPSAttribParametricInfo::const_iterator it = src->_PInfo.begin() + startIndex,
519 endIt = src->_PInfo.begin() + startIndex + numObjects;
520 if (!accumulate) // compute coords from initial condition, and applying this force
522 if (destPos && !destSpeed) // fills dest pos only
524 while (it != endIt)
526 deltaT = date - it->Date;
527 destPos->x = it->Pos.x + deltaT * it->Speed.x;
528 destPos->y = it->Pos.y + deltaT * it->Speed.y;
529 destPos->z = it->Pos.z + deltaT * it->Speed.z - 0.5f * deltaT * deltaT * _K;
530 ++it;
531 NEXT_POS;
534 else if (!destPos && destSpeed) // fills dest speed only
536 while (it != endIt)
538 deltaT = date - it->Date;
539 destSpeed->x = it->Speed.x;
540 destSpeed->y = it->Speed.y;
541 destSpeed->z = it->Speed.z - deltaT * _K;
542 ++it;
543 NEXT_SPEED;
546 else // fills both speed and pos
548 while (it != endIt)
550 deltaT = date - it->Date;
551 destPos->x = it->Pos.x + deltaT * it->Speed.x;
552 destPos->y = it->Pos.y + deltaT * it->Speed.y;
553 destPos->z = it->Pos.z + deltaT * it->Speed.z - 0.5f * deltaT * deltaT * _K;
555 destSpeed->x = it->Speed.x;
556 destSpeed->y = it->Speed.y;
557 destSpeed->z = it->Speed.z - deltaT * _K;
559 ++it;
560 NEXT_POS;
561 NEXT_SPEED;
565 else // accumulate datas
567 if (destPos && !destSpeed) // fills dest pos only
569 while (it != endIt)
571 deltaT = date - it->Date;
572 destPos->z -= 0.5f * deltaT * deltaT * _K;
573 ++it;
574 NEXT_POS;
577 else if (!destPos && destSpeed) // fills dest speed only
579 while (it != endIt)
581 deltaT = date - it->Date;
582 destSpeed->z -= deltaT * _K;
583 ++it;
584 NEXT_SPEED;
587 else // fills both speed and pos
589 while (it != endIt)
591 deltaT = date - it->Date;
592 destPos->z -= 0.5f * deltaT * deltaT * _K;
593 destSpeed->z -= deltaT * _K;
594 ++it;
595 NEXT_POS;
596 NEXT_SPEED;
605 void CPSGravity::integrateSingle(float startDate, float deltaT, uint numStep,
606 const CPSLocated *src, uint32 indexInLocated,
607 NLMISC::CVector *destPos,
608 bool accumulate /*= false*/,
609 uint stride/* = sizeof(NLMISC::CVector)*/) const
611 NL_PS_FUNC(CPSGravity_CVector )
612 nlassert(src->isParametricMotionEnabled());
613 //nlassert(deltaT > 0);
614 nlassert(numStep > 0);
615 #ifdef NL_DEBUG
616 NLMISC::CVector *endPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride * numStep);
617 #endif
618 const CPSLocated::CParametricInfo &pi = src->_PInfo[indexInLocated];
619 const NLMISC::CVector &startPos = pi.Pos;
620 if (numStep != 0)
622 if (!accumulate)
624 destPos = FillBufUsingSubdiv(startPos, pi.Date, startDate, deltaT, numStep, destPos, stride);
625 if (numStep != 0)
627 float currDate = startDate - pi.Date;
628 nlassert(currDate >= 0);
629 const NLMISC::CVector &startSpeed = pi.Speed;
632 #ifdef NL_DEBUG
633 nlassert(destPos < endPos);
634 #endif
635 float halfTimeSquare = 0.5f * currDate * currDate;
636 destPos->x = startPos.x + currDate * startSpeed.x;
637 destPos->y = startPos.y + currDate * startSpeed.y;
638 destPos->z = startPos.z + currDate * startSpeed.z - _K * halfTimeSquare;
639 currDate += deltaT;
640 destPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride);
642 while (--numStep);
645 else
647 uint numToSkip = ScaleFloatGE(startDate, deltaT, pi.Date, numStep);
648 if (numToSkip < numStep)
650 numStep -= numToSkip;
651 float currDate = startDate + deltaT * numToSkip - pi.Date;
654 #ifdef NL_DEBUG
655 nlassert(destPos < endPos);
656 #endif
657 float halfTimeSquare = 0.5f * currDate * currDate;
658 destPos->z -= _K * halfTimeSquare;
659 currDate += deltaT;
660 destPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride);
662 while (--numStep);
669 void CPSGravity::setIntensity(float value)
671 NL_PS_FUNC(CPSGravity_setIntensity)
672 if (_IntensityScheme)
674 CPSForceIntensityHelper::setIntensity(value);
675 renewIntegrable(); // integrable again
677 else
679 CPSForceIntensityHelper::setIntensity(value);
683 void CPSGravity::setIntensityScheme(CPSAttribMaker<float> *scheme)
685 NL_PS_FUNC(CPSGravity_setIntensityScheme)
686 if (!_IntensityScheme)
688 cancelIntegrable(); // not integrable anymore
690 CPSForceIntensityHelper::setIntensityScheme(scheme);
694 /////////////////////////////////////////
695 // CPSCentralGravity implementation //
696 /////////////////////////////////////////
698 void CPSCentralGravity::computeForces(CPSLocated &target)
700 NL_PS_FUNC(CPSCentralGravity_computeForces)
701 nlassert(CParticleSystem::InsideSimLoop);
702 // for each central gravity, and each target, we check if they are in the same basis
703 // if not, we need to transform the central gravity attachment pos into the target basis
704 uint32 size = _Owner->getSize();
705 // a vector that goes from the gravity to the object
706 CVector centerToObj;
707 float dist;
709 for (uint32 k = 0; k < size; ++k)
711 const float ellapsedTimexK = CParticleSystem::EllapsedTime * (_IntensityScheme ? _IntensityScheme->get(_Owner, k) : _K);
712 const CMatrix &m = CPSLocated::getConversionMatrix(&target, this->_Owner);
713 const CVector center = m * (_Owner->getPos()[k]);
714 TPSAttribVector::iterator it2 = target.getSpeed().begin(), it2End = target.getSpeed().end();
715 TPSAttribFloat::const_iterator invMassIt = target.getInvMass().begin();
716 TPSAttribVector::const_iterator posIt = target.getPos().begin();
717 for (; it2 != it2End; ++it2, ++invMassIt, ++posIt)
719 // our equation does 1 / r attenuation, which is not realistic, but fast ...
720 centerToObj = center - *posIt;
722 dist = centerToObj * centerToObj;
723 if (dist > 10E-6f)
725 (*it2) += (*invMassIt) * ellapsedTimexK * (1.f / dist) * centerToObj;
731 void CPSCentralGravity::show()
733 NL_PS_FUNC(CPSCentralGravity_show)
734 CVector I = CVector::I;
735 CVector J = CVector::J;
737 const CVector tab[] = { -I - J, I - J
738 ,-I + J, I + J
739 , I - J, I + J
740 , -I - J, -I + J
741 , I + J, -I - J
742 , I - J, J - I
744 const uint tabSize = sizeof(tab) / (2 * sizeof(CVector));
746 const float sSize = 0.08f;
747 displayIcon2d(tab, tabSize, sSize);
750 void CPSCentralGravity::serial(NLMISC::IStream &f)
752 NL_PS_FUNC(CPSCentralGravity_IStream )
753 f.serialVersion(1);
754 CPSForceIntensityHelper::serial(f);
758 /////////////////////////////////
759 // CPSSpring implementation //
760 /////////////////////////////////
764 void CPSSpring::computeForces(CPSLocated &target)
766 NL_PS_FUNC(CPSSpring_computeForces)
767 nlassert(CParticleSystem::InsideSimLoop);
768 // for each spring, and each target, we check if they are in the same basis
769 // if not, we need to transform the spring attachment pos into the target basis
770 uint32 size = _Owner->getSize();
771 for (uint32 k = 0; k < size; ++k)
773 const float ellapsedTimexK = CParticleSystem::EllapsedTime * (_IntensityScheme ? _IntensityScheme->get(_Owner, k) : _K);
774 const CMatrix &m = CPSLocated::getConversionMatrix(&target, this->_Owner);
775 const CVector center = m * (_Owner->getPos()[k]);
776 TPSAttribVector::iterator it = target.getSpeed().begin(), itEnd = target.getSpeed().end();
777 TPSAttribFloat::const_iterator invMassIt = target.getInvMass().begin();
778 TPSAttribVector::const_iterator posIt = target.getPos().begin();
779 for (; it != itEnd; ++it, ++invMassIt, ++posIt)
781 // apply the spring equation
782 (*it) += (*invMassIt) * ellapsedTimexK * (center - *posIt);
788 void CPSSpring::serial(NLMISC::IStream &f)
790 NL_PS_FUNC(CPSSpring_serial)
791 f.serialVersion(1);
792 CPSForceIntensityHelper::serial(f);
797 void CPSSpring::show()
799 NL_PS_FUNC(CPSSpring_show)
800 CVector I = CVector::I;
801 CVector J = CVector::J;
802 static const CVector tab[] =
804 -I + 2 * J,
805 I + 2 * J,
806 I + 2 * J, -I + J,
807 -I + J, I + J,
808 I + J, -I,
809 -I, I,
810 I, -I - J,
811 -I - J, I - J,
812 I - J,
813 - I - 2 * J,
814 - I - 2 * J,
815 I - 2 * J
817 const uint tabSize = sizeof(tab) / (2 * sizeof(CVector));
818 const float sSize = 0.08f;
819 displayIcon2d(tab, tabSize, sSize);
823 /////////////////////////////////////////
824 // CPSCylindricVortex implementation //
825 /////////////////////////////////////////
826 void CPSCylindricVortex::computeForces(CPSLocated &target)
828 NL_PS_FUNC(CPSCylindricVortex_computeForces)
829 nlassert(CParticleSystem::InsideSimLoop);
830 uint32 size = _Owner->getSize();
831 for (uint32 k = 0; k < size; ++k) // for each vortex
833 const float invR = 1.f / _Radius[k];
834 const float radius2 = _Radius[k] * _Radius[k];
835 // intensity for this vortex
836 nlassert(_Owner);
837 float intensity = (_IntensityScheme ? _IntensityScheme->get(_Owner, k) : _K);
838 // express the vortex position and plane normal in the located basis
839 const CMatrix &m = CPSLocated::getConversionMatrix(&target, this->_Owner);
840 const CVector center = m * (_Owner->getPos()[k]);
841 const CVector n = m.mulVector(_Normal[k]);
842 TPSAttribVector::iterator speedIt = target.getSpeed().begin(), speedItEnd = target.getSpeed().end();
843 TPSAttribFloat::const_iterator invMassIt = target.getInvMass().begin();
844 TPSAttribVector::const_iterator posIt = target.getPos().begin();
845 // projection of the current located pos on the vortex axis
846 CVector p;
847 // a vector that go from the vortex center to the point we're dealing with
848 CVector v2p;
849 // the square of the dist of the projected pos
850 float d2 , d;
851 CVector realTangentialSpeed;
852 CVector tangentialSpeed;
853 CVector radialSpeed;
854 for (; speedIt != speedItEnd; ++speedIt, ++invMassIt, ++posIt)
856 v2p = *posIt - center;
857 p = v2p - (v2p * n) * n;
858 d2 = p * p;
859 if (d2 < radius2) // not out of range ?
861 if (d2 > 10E-6)
863 d = sqrtf(d2);
864 p *= 1.f / d;
865 // compute the speed vect that we should have (normalized)
866 realTangentialSpeed = n ^ p;
867 tangentialSpeed = (*speedIt * realTangentialSpeed) * realTangentialSpeed;
868 radialSpeed = (p * *speedIt) * p;
869 // update radial speed;
870 *speedIt -= _RadialViscosity * CParticleSystem::EllapsedTime * radialSpeed;
871 // update tangential speed
872 *speedIt -= _TangentialViscosity * intensity * CParticleSystem::EllapsedTime * (tangentialSpeed - (1.f - d * invR) * realTangentialSpeed);
880 void CPSCylindricVortex::show()
882 NL_PS_FUNC(CPSCylindricVortex_show)
883 CPSLocated *loc;
884 uint32 index;
885 CPSLocatedBindable *lb;
886 _Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
889 // must have set this
890 nlassert(getFontGenerator() && getFontGenerator());
891 setupDriverModelMatrix();
893 for (uint k = 0; k < _Owner->getSize(); ++k)
895 const CRGBA col = ((lb == NULL || this == lb) && loc == _Owner && index == k ? CRGBA::Red : CRGBA(127, 127, 127));
896 CMatrix m;
897 CPSUtil::buildSchmidtBasis(_Normal[k], m);
898 CPSUtil::displayDisc(*getDriver(), _Radius[k], _Owner->getPos()[k], m, 32, col);
899 CPSUtil::displayArrow(getDriver(), _Owner->getPos()[k], _Normal[k], 1.f, col, CRGBA(200, 0, 200));
900 // display a V letter at the center
901 CPSUtil::print(getDriver(), std::string("v"), *getFontGenerator(), *getFontManager(), _Owner->getPos()[k], 80.f);
906 void CPSCylindricVortex::setMatrix(uint32 index, const CMatrix &m)
908 NL_PS_FUNC(CPSCylindricVortex_setMatrix)
909 nlassert(index < _Normal.getSize());
910 _Normal[index] = m.getK();
911 _Owner->getPos()[index] = m.getPos();
914 CMatrix CPSCylindricVortex::getMatrix(uint32 index) const
916 NL_PS_FUNC(CPSCylindricVortex_getMatrix)
917 CMatrix m;
918 CPSUtil::buildSchmidtBasis(_Normal[index], m);
919 m.setPos(_Owner->getPos()[index] );
920 return m;
924 void CPSCylindricVortex::serial(NLMISC::IStream &f)
926 NL_PS_FUNC(CPSCylindricVortex_IStream )
927 f.serialVersion(1);
928 CPSForceIntensityHelper::serial(f);
929 f.serial(_Normal);
930 f.serial(_Radius);
931 f.serial(_RadialViscosity);
932 f.serial(_TangentialViscosity);
935 void CPSCylindricVortex::newElement(const CPSEmitterInfo &info)
937 NL_PS_FUNC(CPSCylindricVortex_newElement)
938 CPSForceIntensityHelper::newElement(info);
939 _Normal.insert(CVector::K);
940 _Radius.insert(1.f);
942 void CPSCylindricVortex::deleteElement(uint32 index)
944 NL_PS_FUNC(CPSCylindricVortex_deleteElement)
945 CPSForceIntensityHelper::deleteElement(index);
946 _Normal.remove(index);
947 _Radius.remove(index);
949 void CPSCylindricVortex::resize(uint32 size)
951 NL_PS_FUNC(CPSCylindricVortex_resize)
952 nlassert(size < (1 << 16));
953 CPSForceIntensityHelper::resize(size);
954 _Normal.resize(size);
955 _Radius.resize(size);
960 * a magnetic field that has the given direction
963 void CPSMagneticForce::serial(NLMISC::IStream &f)
965 NL_PS_FUNC(CPSMagneticForce_serial)
966 f.serialVersion(1);
967 CPSDirectionnalForce::serial(f);
970 void CPSMagneticForce::computeForces(CPSLocated &target)
972 NL_PS_FUNC(CPSMagneticForce_computeForces)
973 nlassert(CParticleSystem::InsideSimLoop);
974 // perform the operation on each target
975 for (uint32 k = 0; k < _Owner->getSize(); ++k)
977 float intensity = CParticleSystem::EllapsedTime * (_IntensityScheme ? _IntensityScheme->get(_Owner, k) : _K);
978 NLMISC::CVector toAdd = CPSLocated::getConversionMatrix(&target, this->_Owner).mulVector(_Dir); // express this in the target basis
979 TPSAttribVector::iterator it = target.getSpeed().begin(), itend = target.getSpeed().end();
980 // 1st case : non-constant mass
981 if (target.getMassScheme())
983 TPSAttribFloat::const_iterator invMassIt = target.getInvMass().begin();
984 for (; it != itend; ++it, ++invMassIt)
986 (*it) += intensity * *invMassIt * (*it ^ toAdd);
989 else
991 float i = intensity / target.getInitialMass();
992 for (; it != itend; ++it)
994 (*it) += i * (*it ^ toAdd);
1002 * Brownian force implementation
1005 const uint BFNumPredefinedPos = 8192; // should be a power of 2
1006 const uint BFPredefinedNumInterp = 256; /** this should divide BFNumPredefinedPos. This define the number
1007 * of values used to interpolate between 2 position of the npose
1008 * (because we don't filter values when we access them)
1010 const uint BFNumPrecomputedImpulsions = 1024; /// used to avoid to have to call rand for each particle the force applies on...
1012 NLMISC::CVector CPSBrownianForce::PrecomputedPos[BFNumPredefinedPos]; // after the sequence we must be back to the start position
1013 NLMISC::CVector CPSBrownianForce::PrecomputedSpeed[BFNumPredefinedPos];
1014 NLMISC::CVector CPSBrownianForce::PrecomputedImpulsions[BFNumPrecomputedImpulsions];
1016 ///==========================================================
1017 CPSBrownianForce::CPSBrownianForce(float intensity /* = 1.f*/) : _ParametricFactor(1.f)
1019 NL_PS_FUNC(CPSBrownianForce_CPSBrownianForce)
1020 setIntensity(intensity);
1021 if (CParticleSystem::getSerializeIdentifierFlag()) _Name = std::string("BrownianForce");
1025 ///==========================================================
1026 bool CPSBrownianForce::isIntegrable(void) const
1028 NL_PS_FUNC(CPSBrownianForce_isIntegrable)
1029 return _IntensityScheme == NULL;
1033 ///==========================================================
1034 void CPSBrownianForce::integrate(float date, CPSLocated *src,
1035 uint32 startIndex,
1036 uint32 numObjects,
1037 NLMISC::CVector *destPos,
1038 NLMISC::CVector *destSpeed,
1039 bool accumulate,
1040 uint posStride, uint speedStride
1041 ) const
1043 NL_PS_FUNC(CPSBrownianForce_integrate)
1044 /// MASS DIFFERENT FROM 1 IS NOT SUPPORTED
1045 float deltaT;
1046 if (!destPos && !destSpeed) return;
1047 CPSLocated::TPSAttribParametricInfo::const_iterator it = src->_PInfo.begin() + startIndex,
1048 endIt = src->_PInfo.begin() + startIndex + numObjects;
1049 float lookUpFactor = _ParametricFactor * BFNumPredefinedPos;
1050 float speedFactor = _ParametricFactor * _K;
1051 if (!accumulate) // compute coords from initial condition, and applying this force
1053 if (destPos && !destSpeed) // fills dest pos only
1055 while (it != endIt)
1057 float deltaT = date - it->Date;
1058 uint index = (uint) (lookUpFactor * deltaT) & (BFNumPredefinedPos - 1);
1059 destPos->set(it->Pos.x + deltaT * it->Speed.x + _K * PrecomputedPos[index].x,
1060 it->Pos.y + deltaT * it->Speed.y + _K * PrecomputedPos[index].y,
1061 it->Pos.z + deltaT * it->Speed.z + _K * PrecomputedPos[index].z );
1062 ++it;
1063 NEXT_POS;
1066 else if (!destPos && destSpeed) // fills dest speed only
1068 while (it != endIt)
1070 deltaT = date - it->Date;
1071 uint index = (uint) (lookUpFactor * deltaT) & (BFNumPredefinedPos - 1);
1072 destSpeed->x = it->Speed.x + speedFactor * PrecomputedSpeed[index].x;
1073 destSpeed->y = it->Speed.y + speedFactor * PrecomputedSpeed[index].y;
1074 destSpeed->z = it->Speed.z + speedFactor * PrecomputedSpeed[index].z;
1075 ++it;
1076 NEXT_SPEED;
1079 else // fills both speed and pos
1081 while (it != endIt)
1083 deltaT = date - it->Date;
1084 uint index = (uint) (lookUpFactor * deltaT) & (BFNumPredefinedPos - 1);
1085 destPos->x = it->Pos.x + deltaT * it->Speed.x + _K * PrecomputedPos[index].x;
1086 destPos->y = it->Pos.y + deltaT * it->Speed.y + _K * PrecomputedPos[index].y;
1087 destPos->z = it->Pos.z + deltaT * it->Speed.z + _K * PrecomputedPos[index].z;
1089 destSpeed->x = it->Speed.x + speedFactor * PrecomputedSpeed[index].x;
1090 destSpeed->y = it->Speed.y + speedFactor * PrecomputedSpeed[index].y;
1091 destSpeed->z = it->Speed.z + speedFactor * PrecomputedSpeed[index].z;
1093 ++it;
1094 NEXT_POS;
1095 NEXT_SPEED;
1099 else // accumulate datas
1101 if (destPos && !destSpeed) // fills dest pos only
1103 while (it != endIt)
1105 deltaT = date - it->Date;
1106 uint index = (uint) (lookUpFactor * deltaT) & (BFNumPredefinedPos - 1);
1107 destPos->set(destPos->x + _K * PrecomputedPos[index].x,
1108 destPos->y + _K * PrecomputedPos[index].y,
1109 destPos->z + _K * PrecomputedPos[index].z);
1110 ++it;
1111 NEXT_POS;
1114 else if (!destPos && destSpeed) // fills dest speed only
1116 while (it != endIt)
1118 deltaT = date - it->Date;
1119 uint index = (uint) (lookUpFactor * deltaT) & (BFNumPredefinedPos - 1);
1120 destSpeed->set(destSpeed->x + speedFactor * PrecomputedSpeed[index].x,
1121 destSpeed->y + speedFactor * PrecomputedSpeed[index].y,
1122 destSpeed->z + speedFactor * PrecomputedSpeed[index].z);
1123 ++it;
1124 NEXT_SPEED;
1127 else // fills both speed and pos
1129 while (it != endIt)
1131 deltaT = date - it->Date;
1132 uint index = (uint) (lookUpFactor * deltaT) & (BFNumPredefinedPos - 1);
1133 destPos->set(destPos->x + _K * PrecomputedPos[index].x,
1134 destPos->y + _K * PrecomputedPos[index].y,
1135 destPos->z + _K * PrecomputedPos[index].z);
1136 destSpeed->set(destSpeed->x + speedFactor * PrecomputedSpeed[index].x,
1137 destSpeed->y + speedFactor * PrecomputedSpeed[index].y,
1138 destSpeed->z + speedFactor * PrecomputedSpeed[index].z);
1139 ++it;
1140 NEXT_POS;
1141 NEXT_SPEED;
1148 ///==========================================================
1149 void CPSBrownianForce::integrateSingle(float startDate, float deltaT, uint numStep,
1150 const CPSLocated *src, uint32 indexInLocated,
1151 NLMISC::CVector *destPos,
1152 bool accumulate,
1153 uint stride) const
1155 NL_PS_FUNC(CPSBrownianForce_integrateSingle)
1156 nlassert(src->isParametricMotionEnabled());
1157 //nlassert(deltaT > 0);
1158 nlassert(numStep > 0);
1159 #ifdef NL_DEBUG
1160 NLMISC::CVector *endPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride * numStep);
1161 #endif
1162 const CPSLocated::CParametricInfo &pi = src->_PInfo[indexInLocated];
1163 const NLMISC::CVector &startPos = pi.Pos;
1164 if (numStep != 0)
1166 float lookUpFactor = _ParametricFactor * BFPredefinedNumInterp;
1167 if (!accumulate)
1169 /// fill start of datas (particle didn't exist at that time, so we fill by the start position)
1170 destPos = FillBufUsingSubdiv(startPos, pi.Date, startDate, deltaT, numStep, destPos, stride);
1171 if (numStep != 0)
1173 float currDate = startDate - pi.Date;
1174 nlassert(currDate >= 0);
1175 const NLMISC::CVector &startSpeed = pi.Speed;
1178 #ifdef NL_DEBUG
1179 nlassert(destPos < endPos);
1180 #endif
1181 uint index = (uint) (lookUpFactor * currDate) & (BFNumPredefinedPos - 1);
1182 destPos->x = startPos.x + currDate * startSpeed.x + _K * PrecomputedPos[index].x;
1183 destPos->y = startPos.y + currDate * startSpeed.y + _K * PrecomputedPos[index].y;
1184 destPos->z = startPos.z + currDate * startSpeed.z + _K * PrecomputedPos[index].z;
1185 currDate += deltaT;
1186 destPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride);
1188 while (--numStep);
1191 else
1193 uint numToSkip = ScaleFloatGE(startDate, deltaT, pi.Date, numStep);
1194 if (numToSkip < numStep)
1196 numStep -= numToSkip;
1197 float currDate = startDate + deltaT * numToSkip - pi.Date;
1200 #ifdef NL_DEBUG
1201 nlassert(destPos < endPos);
1202 #endif
1203 uint index = (uint) (lookUpFactor * currDate) & (BFNumPredefinedPos - 1);
1204 destPos->x += _K * PrecomputedPos[index].x;
1205 destPos->y += _K * PrecomputedPos[index].y;
1206 destPos->z += _K * PrecomputedPos[index].z;
1207 currDate += deltaT;
1208 destPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride);
1210 while (--numStep);
1217 ///==========================================================
1218 void CPSBrownianForce::initPrecalc()
1220 NL_PS_FUNC(CPSBrownianForce_initPrecalc)
1221 /// create the pos table
1222 nlassert(BFNumPredefinedPos % BFPredefinedNumInterp == 0);
1224 NLMISC::CVector p0(0, 0, 0), p1;
1225 const uint numStep = BFNumPredefinedPos / BFPredefinedNumInterp;
1226 NLMISC::CVector *dest = PrecomputedPos;
1227 uint k, l;
1228 for (k = 0; k < numStep; ++k)
1230 if (k != numStep - 1)
1232 p1.set(2.f * (NLMISC::frand(1.f) - 0.5f),
1233 2.f * (NLMISC::frand(1.f) - 0.5f),
1234 2.f * (NLMISC::frand(1.f) - 0.5f));
1236 else
1238 p1.set(0, 0, 0);
1240 float lambda = 0.f;
1241 float lambdaStep = 1.f / BFPredefinedNumInterp;
1242 for (l = 0; l < BFPredefinedNumInterp; ++l)
1244 *dest++ = lambda * p1 + (1.f - lambda) * p0;
1245 lambda += lambdaStep;
1247 p0 = p1;
1250 // now, filter the table several time to get something more smooth
1251 for (k = 0; k < (BFPredefinedNumInterp << 2) ; ++k)
1253 for (l = 1; l < (BFNumPredefinedPos - 1); ++l)
1255 PrecomputedPos[l] = 0.5f * (PrecomputedPos[l - 1] + PrecomputedPos[l + 1]);
1260 // compute the table of speeds, by using on a step of 1.s
1261 for (l = 1; l < (BFNumPredefinedPos - 1); ++l)
1263 PrecomputedSpeed[l] = 0.5f * (PrecomputedPos[l + 1] - PrecomputedPos[l - 1]);
1265 PrecomputedSpeed[BFNumPredefinedPos - 1] = NLMISC::CVector::Null;
1267 // compute the table of impulsion
1268 for (k = 0; k < BFNumPrecomputedImpulsions; ++k)
1270 static double divRand = (2.f / RAND_MAX);
1271 PrecomputedImpulsions[k].set( (float) (rand() * divRand - 1),
1272 (float) (rand() * divRand - 1),
1273 (float) (rand() * divRand - 1)
1278 ///==========================================================
1279 void CPSBrownianForce::setIntensity(float value)
1281 NL_PS_FUNC(CPSBrownianForce_setIntensity)
1282 if (_IntensityScheme)
1284 CPSForceIntensity::setIntensity(value);
1285 renewIntegrable(); // integrable again
1287 else
1289 CPSForceIntensity::setIntensity(value);
1294 ///==========================================================
1295 void CPSBrownianForce::setIntensityScheme(CPSAttribMaker<float> *scheme)
1297 NL_PS_FUNC(CPSBrownianForce_setIntensityScheme)
1298 if (!_IntensityScheme)
1300 cancelIntegrable(); // not integrable anymore
1302 CPSForceIntensity::setIntensityScheme(scheme);
1305 ///==========================================================
1306 void CPSBrownianForce::computeForces(CPSLocated &target)
1308 NL_PS_FUNC(CPSBrownianForce_computeForces)
1309 nlassert(CParticleSystem::InsideSimLoop);
1310 // perform the operation on each target
1311 for (uint32 k = 0; k < _Owner->getSize(); ++k)
1313 float intensity = _IntensityScheme ? _IntensityScheme->get(_Owner, k) : _K;
1314 uint32 size = target.getSize();
1315 if (!size) continue;
1316 TPSAttribVector::iterator it2 = target.getSpeed().begin(), it2End;
1317 /// start at a random position in the precomp impulsion tab
1318 uint startPos = (uint) ::rand() % BFNumPrecomputedImpulsions;
1319 NLMISC::CVector *imp = PrecomputedImpulsions + startPos;
1321 if (target.getMassScheme())
1323 float intensityXtime = intensity * CParticleSystem::EllapsedTime;
1324 TPSAttribFloat::const_iterator invMassIt = target.getInvMass().begin();
1327 uint toProcess = std::min((uint) (BFNumPrecomputedImpulsions - startPos), (uint) size);
1328 it2End = it2 + toProcess;
1331 float factor = intensityXtime * *invMassIt;
1332 it2->set(it2->x + factor * imp->x,
1333 it2->y + factor * imp->y,
1334 it2->z + factor * imp->x);
1335 ++invMassIt;
1336 ++imp;
1337 ++it2;
1339 while (it2 != it2End);
1340 startPos = 0;
1341 imp = PrecomputedImpulsions;
1342 size -= toProcess;
1344 while (size != 0);
1346 else
1350 uint toProcess = std::min((uint) (BFNumPrecomputedImpulsions - startPos) , (uint) size);
1351 it2End = it2 + toProcess;
1352 float factor = intensity * CParticleSystem::EllapsedTime / target.getInitialMass();
1355 it2->set(it2->x + factor * imp->x,
1356 it2->y + factor * imp->y,
1357 it2->z + factor * imp->x);
1358 ++imp;
1359 ++it2;
1361 while (it2 != it2End);
1362 startPos = 0;
1363 imp = PrecomputedImpulsions;
1364 size -= toProcess;
1366 while (size != 0);
1371 ///=======================================================================
1372 void CPSBrownianForce::serial(NLMISC::IStream &f)
1374 NL_PS_FUNC(CPSBrownianForce_serial)
1375 sint ver = f.serialVersion(3);
1376 if (ver <= 2)
1378 uint8 dummy;
1379 f.serial(dummy); // old data in version 2 not used anymore
1380 CPSForce::serial(f);
1381 f.serial(dummy); // old data in version 2 not used anymore
1382 serialForceIntensity(f);
1383 if (f.isReading())
1385 registerToTargets();
1389 if (ver >= 2)
1391 CPSForceIntensityHelper::serial(f);
1392 f.serial(_ParametricFactor);
1397 } // NL3D