Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / ps_zone.cpp
blob7adf48b8fdcbf9abb0ce97e5f3e7e6eb81630d43
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_zone.h"
20 #include "nel/3d/vertex_buffer.h"
21 #include "nel/3d/index_buffer.h"
22 #include "nel/3d/material.h"
23 #include "nel/3d/ps_util.h"
24 #include "nel/3d/dru.h"
25 #include "nel/3d/particle_system.h"
26 #include "nel/misc/plane.h"
28 // tmp
30 #include "nel/3d/particle_system_model.h"
32 #include <cmath>
33 #include <limits>
35 #ifdef DEBUG_NEW
36 #define new DEBUG_NEW
37 #endif
39 namespace NL3D {
43 * Constructor
45 CPSZone::CPSZone() : _BounceFactor(1.f), _CollisionBehaviour(bounce)
47 NL_PS_FUNC(CPSZone_CPSZone)
50 void CPSZone::serial(NLMISC::IStream &f)
52 NL_PS_FUNC(CPSZone_serial)
53 f.serialVersion(1);
54 CPSTargetLocatedBindable::serial(f);
55 f.serialEnum(_CollisionBehaviour);
56 f.serial(_BounceFactor);
57 if (f.isReading())
59 for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
61 // though this is not a force, this prevent parametric motion
62 (*it)->addNonIntegrableForceRef();
67 /** Add a new type of located for this zone to apply on.
68 * We override this to queery the target to allocate the CollisionInfo attribute
70 void CPSZone::attachTarget(CPSLocated *ptr)
72 NL_PS_FUNC(CPSZone_attachTarget)
73 CPSTargetLocatedBindable::attachTarget(ptr);
74 ptr->queryCollisionInfo();
75 ptr->addNonIntegrableForceRef();
82 /// inherit from CPSTargetLocatedBindable. Its called when one of the targets has been detroyed
83 void CPSZone::releaseTargetRsc(CPSLocated *target)
85 NL_PS_FUNC(CPSZone_releaseTargetRsc)
86 // tell the target that we were using collision infos and that we won't use them anymore
87 target->releaseCollisionInfo();
88 target->releaseNonIntegrableForceRef();
93 void CPSZone::step(TPSProcessPass pass)
95 NL_PS_FUNC(CPSZone_step)
96 // for zone, the PSCollision pass and the PSToolRenderPass are processed
97 switch(pass)
99 case PSToolRender:
100 show();
101 break;
102 default: break;
107 // build a basis with K = the normal of the plane
108 CMatrix CPSZonePlane::buildBasis(uint32 index) const
110 NL_PS_FUNC(CPSZonePlane_buildBasis)
111 CMatrix m;
112 m.setPos(_Owner->getPos()[index]);
113 CPSUtil::buildSchmidtBasis(_Normal[index], m);
114 return m;
118 /// this compute a new speed vector, so that the located will reach the correct position at the next speed integration
119 /// this create the illusion of collision
120 /*void CPSZone::bounce(uint32 locatedIndex, const CVector &bouncePoint, const CVector &surfNormal, float elasticity, TAnimationTime ellapsedTime)
122 CVector &speed = _Owner->getSpeed()[locatedIndex];
123 const CVector &pos = _Owner->getPos()[locatedIndex];
124 CVector &bounceVect = elasticity * (speed - 2.0f * (speed * surfNormal) * surfNormal); // speed vector after collision
125 // now check where the located will be after integration
126 CVector d = bouncePoint - pos;
127 TAnimationTime collideDelay = speed.norm() / d.norm();
128 CVector finalPos = bouncePoint + (ellapsedTime - collideDelay) * bounceVect;
129 // now, we must have pos + ellapsedTime * newSpeed = finalPos
130 // newSpeed = alpha * (finalPos - pos)
131 // so alpha = 1 / ellapsedTime
133 speed = (1.0f / ellapsedTime) * (finalPos - pos);
137 void CPSZonePlane::show()
139 NL_PS_FUNC(CPSZonePlane_show)
140 const float planeSize = 2.0f;
141 setupDriverModelMatrix();
142 IDriver *driver = getDriver();
143 uint k = 0;
144 setupDriverModelMatrix();
145 CPSLocated *loc;
146 uint32 index;
147 CPSLocatedBindable *lb;
148 _Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
149 for (TPSAttribVector::const_iterator it = _Owner->getPos().begin(); it != _Owner->getPos().end(); ++it, ++k)
151 const CRGBA col = ((lb == NULL || this == lb) && loc == _Owner && index == k ? CRGBA::Red : CRGBA(127, 127, 127));
152 CMatrix mat = buildBasis(k);
153 CPSUtil::displayBasis(getDriver(), getLocalToWorldMatrix(), mat, 1.f, *getFontGenerator(), *getFontManager());
154 setupDriverModelMatrix();
155 CDRU::drawLine(*it + (planeSize + 3) * mat.getI() + planeSize * mat.getJ(),
156 *it - (planeSize + 3) * mat.getI() + planeSize * mat.getJ(),
157 col,
158 *driver);
160 CDRU::drawLine(*it + (planeSize + 3) * mat.getI() - planeSize * mat.getJ(),
161 *it - (planeSize + 3) * mat.getI() - planeSize * mat.getJ(),
162 col,
163 *driver);
165 CDRU::drawLine(*it + planeSize * mat.getI() + (planeSize + 3) * mat.getJ(),
166 *it + planeSize * mat.getI() - (planeSize + 3) * mat.getJ(),
167 col,
168 *driver);
169 CDRU::drawLine(*it - planeSize * mat.getI() + (planeSize + 3) * mat.getJ(),
170 *it - planeSize * mat.getI() - (planeSize + 3) * mat.getJ(),
171 col,
172 *driver);
178 void CPSZonePlane::resize(uint32 size)
180 NL_PS_FUNC(CPSZonePlane_resize)
181 nlassert(size < (1 << 16));
182 _Normal.resize(size);
186 void CPSZonePlane::newElement(const CPSEmitterInfo &info)
188 NL_PS_FUNC(CPSZonePlane_newElement)
189 nlassert(_Normal.getSize() != _Normal.getMaxSize());
190 _Normal.insert(CVector(0, 0, 1));
194 void CPSZonePlane::deleteElement(uint32 index)
196 NL_PS_FUNC(CPSZonePlane_deleteElement)
197 _Normal.remove(index);
201 void CPSZonePlane::computeCollisions(CPSLocated &target, uint firstInstanceIndex, const NLMISC::CVector *posBefore, const NLMISC::CVector *posAfter)
203 NL_PS_FUNC(CPSZonePlane_computeCollisions)
204 MINI_TIMER(PSStatsZonePlane)
205 // for each target, we must check whether they are going through the plane
206 // if so they must bounce
207 TPSAttribVector::const_iterator planePosIt, planePosEnd, normalIt;
208 CPSCollisionInfo ci;
209 // cycle through the planes
210 planePosEnd = _Owner->getPos().end();
211 for (planePosIt = _Owner->getPos().begin(), normalIt = _Normal.begin(); planePosIt != planePosEnd; ++planePosIt, ++normalIt)
214 // we must setup the plane in the good basis
215 const CMatrix &m = CPSLocated::getConversionMatrix(&target, this->_Owner);
216 const float epsilon = 0.5f * PSCollideEpsilon;
217 NLMISC::CPlane p;
218 p.make(m.mulVector(*normalIt), m * (*planePosIt));
219 // deals with each particle
220 const NLMISC::CVector *itPosBefore = posBefore + firstInstanceIndex;
221 const NLMISC::CVector *itPosBeforeEnd = posBefore + target.getSize();
222 const NLMISC::CVector *itPosAfter = posAfter + firstInstanceIndex;
223 while (itPosBefore != itPosBeforeEnd)
225 float posSide = p * *itPosBefore;
226 float negSide = p * *itPosAfter;
227 if (posSide >= - epsilon && negSide <= epsilon)
229 float alpha;
230 if (fabsf(posSide - negSide) > std::numeric_limits<float>::min())
232 alpha = posSide / (posSide - negSide);
234 else
236 alpha = 0.f;
238 CVector startEnd = alpha * (*itPosAfter - *itPosBefore);
239 ci.Dist = startEnd.norm();
240 // we translate the particle from an epsilon so that it won't get hooked to the plane
241 ci.NewPos = *itPosBefore + startEnd + PSCollideEpsilon * p.getNormal();
242 const CVector &speed = target.getSpeed()[(uint32)(itPosBefore - posBefore)];
243 ci.NewSpeed = _BounceFactor * (speed - 2.0f * (speed * p.getNormal()) * p.getNormal());
244 ci.CollisionZone = this;
245 CPSLocated::_Collisions[itPosBefore - posBefore].update(ci);
247 ++ itPosBefore;
248 ++ itPosAfter;
254 void CPSZonePlane::setMatrix(uint32 index, const CMatrix &m)
256 NL_PS_FUNC(CPSZonePlane_setMatrix)
257 nlassert(index < _Normal.getSize());
258 _Normal[index] = m.getK();
259 _Owner->getPos()[index] = m.getPos();
262 CMatrix CPSZonePlane::getMatrix(uint32 index) const
264 NL_PS_FUNC(CPSZonePlane_getMatrix)
265 return buildBasis(index);
269 CVector CPSZonePlane::getNormal(uint32 index)
271 NL_PS_FUNC(CPSZonePlane_getNormal)
272 return _Normal[index];
274 void CPSZonePlane::setNormal(uint32 index, CVector n)
276 NL_PS_FUNC(CPSZonePlane_setNormal)
277 _Normal[index] = n;
283 void CPSZonePlane::serial(NLMISC::IStream &f)
285 NL_PS_FUNC(CPSZonePlane_serial)
286 f.serialVersion(1);
287 CPSZone::serial(f);
288 f.serial(_Normal);
293 ///////////////////////////
294 // sphere implementation //
295 ///////////////////////////
297 void CPSZoneSphere::computeCollisions(CPSLocated &target, uint firstInstanceIndex, const NLMISC::CVector *posBefore, const NLMISC::CVector *posAfter)
299 NL_PS_FUNC(CPSZoneSphere_computeCollisions)
300 MINI_TIMER(PSStatsZoneSphere)
301 // for each target, we must check whether they are going through the plane
302 // if so they must bounce
303 TPSAttribRadiusPair::const_iterator radiusIt = _Radius.begin();
304 TPSAttribVector::const_iterator spherePosIt, spherePosEnd;
305 CPSCollisionInfo ci;
306 float rOut, rIn;
307 // cycle through the spheres
308 spherePosEnd = _Owner->getPos().end();
309 for (spherePosIt = _Owner->getPos().begin(), radiusIt = _Radius.begin(); spherePosIt != spherePosEnd; ++spherePosIt, ++radiusIt)
311 // we must setup the sphere in the good basis
312 const CMatrix &m = CPSLocated::getConversionMatrix(&target, this->_Owner);
313 CVector center = m * *spherePosIt;
314 // deals with each particle
315 const NLMISC::CVector *itPosBefore = posBefore + firstInstanceIndex;
316 const NLMISC::CVector *itPosBeforeEnd = posBefore + target.getSize();
317 const NLMISC::CVector *itPosAfter = posAfter + firstInstanceIndex;
318 while (itPosBefore != itPosBeforeEnd)
320 // check whether the located is going through the sphere
321 // we don't use raytracing for now because it is too slow ...
322 rOut = (*itPosBefore - center) * (*itPosBefore - center);
323 // initial position outside the sphere ?
324 if (rOut > radiusIt->R2)
326 rIn = (*itPosAfter - center) * (*itPosAfter - center);
327 const CVector &pos = *itPosBefore;
328 const CVector &dest = *itPosAfter;
329 const CVector D = dest - pos;
330 // final position inside the sphere ?
331 if ( rIn <= radiusIt->R2)
333 // discriminant of the intersection equation
334 const float b = 2.f * (pos * D - D * center), a = D * D
335 , c = (pos * pos) + (center * center) - 2.f * (pos * center) - radiusIt->R2;
336 float d = b * b - 4 * a * c;
337 if (d > 0.f)
339 d = sqrtf(d);
340 // roots of the equation, we take the smallest
341 const float r1 = .5f * (-b + 2.f * d) * a,
342 r2 = .5f * (-b - 2.f * d) * a;
343 const float r = std::min(r1, r2);
344 // collision point
345 const CVector C = pos + r * D;
346 const float alpha = ((C - pos) * D) * a;
347 const CVector startEnd = alpha * (dest - pos);
348 CVector normal = C - center;
349 normal = normal * (1.f / radiusIt->R);
350 ci.Dist = startEnd.norm();
351 // we translate the particle from an epsilon so that it won't get hooked to the sphere
352 ci.NewPos = pos + startEnd + PSCollideEpsilon * normal;
353 const CVector &speed = target.getSpeed()[(uint32)(itPosBefore - posBefore)];
354 ci.NewSpeed = _BounceFactor * (speed - 2.0f * (speed * normal) * normal);
355 ci.CollisionZone = this;
356 CPSLocated::_Collisions[itPosBefore - posBefore].update(ci);
360 ++ itPosBefore;
361 ++ itPosAfter;
369 void CPSZoneSphere::show()
371 NL_PS_FUNC(CPSZoneSphere_show)
373 CPSLocated *loc;
374 uint32 index;
375 CPSLocatedBindable *lb;
376 _Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
379 TPSAttribRadiusPair::const_iterator radiusIt = _Radius.begin();
380 TPSAttribVector::const_iterator posIt = _Owner->getPos().begin(), endPosIt = _Owner->getPos().end();
381 setupDriverModelMatrix();
382 for (uint k = 0; posIt != endPosIt; ++posIt, ++radiusIt, ++k)
384 const CRGBA col = ((lb == NULL || this == lb) && loc == _Owner && index == k ? CRGBA::Red : CRGBA(127, 127, 127));
385 CPSUtil::displaySphere(*getDriver(), radiusIt->R, *posIt, 5, col);
389 void CPSZoneSphere::setMatrix(uint32 index, const CMatrix &m)
391 NL_PS_FUNC(CPSZoneSphere_setMatrix)
392 nlassert(index < _Radius.getSize());
394 // compute new pos
395 _Owner->getPos()[index] = m.getPos();
400 CMatrix CPSZoneSphere::getMatrix(uint32 index) const
402 NL_PS_FUNC(CPSZoneSphere_getMatrix)
403 nlassert(index < _Radius.getSize());
404 CMatrix m;
405 m.identity();
406 m.translate(_Owner->getPos()[index]);
407 return m;
410 void CPSZoneSphere::setScale(uint32 k, float scale)
412 NL_PS_FUNC(CPSZoneSphere_setScale)
413 _Radius[k].R = scale;
414 _Radius[k].R2 = scale * scale;
416 CVector CPSZoneSphere::getScale(uint32 k) const
418 NL_PS_FUNC(CPSZoneSphere_getScale)
419 return CVector(_Radius[k].R, _Radius[k].R, _Radius[k].R);
423 void CPSZoneSphere::serial(NLMISC::IStream &f)
425 NL_PS_FUNC(CPSZoneSphere_serial)
426 f.serialVersion(1);
427 CPSZone::serial(f);
428 f.serial(_Radius);
431 void CPSZoneSphere::resize(uint32 size)
433 NL_PS_FUNC(CPSZoneSphere_resize)
434 nlassert(size < (1 << 16));
435 _Radius.resize(size);
438 void CPSZoneSphere::newElement(const CPSEmitterInfo &info)
440 NL_PS_FUNC(CPSZoneSphere_newElement)
441 CRadiusPair rp;
442 rp.R = rp.R2 = 1.f;
443 nlassert(_Radius.getSize() != _Radius.getMaxSize());
444 _Radius.insert(rp);
447 void CPSZoneSphere::deleteElement(uint32 index)
449 NL_PS_FUNC(CPSZoneSphere_deleteElement)
450 _Radius.remove(index);
454 ////////////////////////////////
455 // CPSZoneDisc implementation //
456 ////////////////////////////////
457 void CPSZoneDisc::computeCollisions(CPSLocated &target, uint firstInstanceIndex, const NLMISC::CVector *posBefore, const NLMISC::CVector *posAfter)
459 NL_PS_FUNC(CPSZoneDisc_computeCollisions)
460 MINI_TIMER(PSStatsZoneDisc)
461 // for each target, we must check whether they are going through the disc
462 // if so they must bounce
463 TPSAttribVector::const_iterator discPosIt, discPosEnd, normalIt;
464 TPSAttribRadiusPair::const_iterator radiusIt;
465 CPSCollisionInfo ci;
466 // the square of radius at the hit point
467 float hitRadius2;
468 // alpha is the ratio that gives the percent of endPos - startPos that hit the disc
469 CVector center;
470 // cycle through the disc
471 discPosEnd = _Owner->getPos().end();
472 for (discPosIt = _Owner->getPos().begin(), radiusIt = _Radius.begin(), normalIt = _Normal.begin(); discPosIt != discPosEnd; ++discPosIt, ++normalIt, ++radiusIt)
474 // we must setup the disc in the good basis
475 const CMatrix &m = CPSLocated::getConversionMatrix(&target, this->_Owner);
476 NLMISC::CPlane p;
477 center = m * (*discPosIt);
478 p.make(m.mulVector(*normalIt), center);
480 // deals with each particle
481 const float epsilon = 0.5f * PSCollideEpsilon;
483 // deals with each particle
484 const NLMISC::CVector *itPosBefore = posBefore + firstInstanceIndex;
485 const NLMISC::CVector *itPosBeforeEnd = posBefore + target.getSize();
486 const NLMISC::CVector *itPosAfter = posAfter + firstInstanceIndex;
487 while (itPosBefore != itPosBeforeEnd)
489 float posSide = p * *itPosBefore;
490 float negSide = p * *itPosAfter;
491 if (posSide >= - epsilon && negSide <= epsilon)
493 float alpha;
494 if (fabsf(posSide - negSide) > std::numeric_limits<float>::min())
496 alpha = posSide / (posSide - negSide);
498 else
500 alpha = 0.f;
502 CVector startEnd = alpha * (*itPosAfter - *itPosBefore);
503 ci.Dist = startEnd.norm();
504 // we translate the particle from an epsilon so that it won't get hooked to the disc
505 ci.NewPos = *itPosBefore + startEnd + PSCollideEpsilon * p.getNormal();
506 // now, check the collision pos against radius
507 hitRadius2 = (ci.NewPos - center) * (ci.NewPos - center);
508 if (hitRadius2 < radiusIt->R2) // check collision against disc
510 const CVector &speed = target.getSpeed()[(uint32)(itPosBefore - posBefore)];
511 ci.NewSpeed = _BounceFactor * (speed - 2.0f * (speed * p.getNormal()) * p.getNormal());
512 ci.CollisionZone = this;
513 CPSLocated::_Collisions[itPosBefore - posBefore].update(ci);
517 ++ itPosBefore;
518 ++ itPosAfter;
523 void CPSZoneDisc::show()
525 NL_PS_FUNC(CPSZoneDisc_show)
526 TPSAttribRadiusPair::const_iterator radiusIt = _Radius.begin();
527 TPSAttribVector::const_iterator posIt = _Owner->getPos().begin(), endPosIt = _Owner->getPos().end()
528 , normalIt = _Normal.begin();
529 setupDriverModelMatrix();
530 CMatrix mat;
534 CPSLocated *loc;
535 uint32 index;
536 CPSLocatedBindable *lb;
537 _Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
541 for (uint k = 0; posIt != endPosIt; ++posIt, ++radiusIt, ++normalIt, ++k)
543 const CRGBA col = ((lb == NULL || this == lb) && loc == _Owner && index == k ? CRGBA::Red : CRGBA(127, 127, 127));
544 CPSUtil::buildSchmidtBasis(*normalIt, mat);
545 CPSUtil::displayDisc(*getDriver(), radiusIt->R, *posIt, mat, 32, col);
547 mat.setPos(*posIt);
548 CPSUtil::displayBasis(getDriver() ,getLocalToWorldMatrix(), mat, 1.f, *getFontGenerator(), *getFontManager());
549 setupDriverModelMatrix();
558 void CPSZoneDisc::setMatrix(uint32 index, const CMatrix &m)
560 NL_PS_FUNC(CPSZoneDisc_setMatrix)
561 nlassert(index < _Radius.getSize());
562 // compute new pos
563 _Owner->getPos()[index] = m.getPos();
564 // compute new normal
565 _Normal[index] = m.getK();
568 CMatrix CPSZoneDisc::getMatrix(uint32 index) const
570 NL_PS_FUNC(CPSZoneDisc_getMatrix)
571 CMatrix m, b;
572 m.translate(_Owner->getPos()[index]);
573 CPSUtil::buildSchmidtBasis(_Normal[index], b);
574 m = m * b;
575 return m;
578 CVector CPSZoneDisc::getNormal(uint32 index)
580 NL_PS_FUNC(CPSZoneDisc_getNormal)
581 return _Normal[index];
583 void CPSZoneDisc::setNormal(uint32 index, CVector n)
585 NL_PS_FUNC(CPSZoneDisc_setNormal)
586 _Normal[index] = n;
589 void CPSZoneDisc::setScale(uint32 k, float scale)
591 NL_PS_FUNC(CPSZoneDisc_setScale)
592 _Radius[k].R = scale;
593 _Radius[k].R2 = scale * scale;
596 CVector CPSZoneDisc::getScale(uint32 k) const
598 NL_PS_FUNC(CPSZoneDisc_getScale)
599 return CVector(_Radius[k].R, _Radius[k].R, _Radius[k].R);
603 void CPSZoneDisc::serial(NLMISC::IStream &f)
605 NL_PS_FUNC(CPSZoneDisc_serial)
606 f.serialVersion(1);
607 CPSZone::serial(f);
608 f.serial(_Normal);
609 f.serial(_Radius);
612 void CPSZoneDisc::resize(uint32 size)
614 NL_PS_FUNC(CPSZoneDisc_resize)
615 nlassert(size < (1 << 16));
616 _Radius.resize(size);
617 _Normal.resize(size);
620 void CPSZoneDisc::newElement(const CPSEmitterInfo &info)
622 NL_PS_FUNC(CPSZoneDisc_newElement)
623 CRadiusPair rp;
624 rp.R = rp.R2 = 1.f;
625 nlassert(_Radius.getSize() != _Radius.getMaxSize());
626 _Radius.insert(rp);
627 _Normal.insert(CVector::K);
630 void CPSZoneDisc::deleteElement(uint32 index)
632 NL_PS_FUNC(CPSZoneDisc_deleteElement)
633 _Radius.remove(index);
634 _Normal.remove(index);
638 ////////////////////////////////////
639 // CPSZoneCylinder implementation //
640 ////////////////////////////////////
644 void CPSZoneCylinder::performMotion(TAnimationTime ellapsedTime)
646 TPSAttribVector::const_iterator dimIt = _Dim.begin();
647 CPSAttrib<CPlaneBasis>::const_iterator basisIt = _Basis.begin();
648 TPSAttribVector::const_iterator cylinderPosIt, cylinderPosEnd, targetPosIt, targetPosEnd;
649 CVector dest;
650 CPSCollisionInfo ci;
651 CVector startEnd;
652 uint32 k;
653 const TPSAttribVector *speedAttr;
657 for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
660 speedAttr = &((*it)->getSpeed());
663 // cycle through the cylinders
665 cylinderPosEnd = _Owner->getPos().end();
666 for (cylinderPosIt = _Owner->getPos().begin(); cylinderPosIt != cylinderPosEnd
667 ; ++cylinderPosIt, ++dimIt, ++basisIt)
670 // we must setup the cylinder in the good basis
672 const CMatrix &m = CPSLocated::getConversionMatrix(*it, this->_Owner);
676 // compute the new center pos
677 CVector center = m * *cylinderPosIt;
679 // compute a basis for the cylinder
680 CVector I = m.mulVector(basisIt->X);
681 CVector J = m.mulVector(basisIt->Y);
682 CVector K = m.mulVector(basisIt->X ^ basisIt->Y);
685 // the pos projected (and scale) over the plane basis of the cylinder, the pos minus the center
686 CVector projectedPos, tPos;
688 // the same, but with the final position
689 CVector destProjectedPos, destTPos;
692 // deals with each particle
693 targetPosEnd = (*it)->getPos().end();
694 for (targetPosIt = (*it)->getPos().begin(), k = 0; targetPosIt != targetPosEnd; ++targetPosIt, ++k)
696 const CVector &speed = (*speedAttr)[k];
697 const CVector &pos = *targetPosIt;
701 // check whether current pos was outside the cylinder
704 tPos = pos - center;
705 projectedPos = (1 / dimIt->x) * (I * tPos) * I + (1 / dimIt->y) * (J * tPos) * J;
707 if (!
709 ((tPos * K) < dimIt->z)
710 && ((tPos * K) > -dimIt->z)
711 && (projectedPos * projectedPos < 1.f)
715 dest = pos + ellapsedTime * speed;
716 destTPos = dest - center;
717 destProjectedPos = (1.f / dimIt->x) * (I * tPos) * I + (1.f / dimIt->y) * (J * tPos) * J;
719 // test whether the new position is inside the cylinder
722 if (!
724 ((destTPos * K) < dimIt->z)
725 && ((destTPos * K) > -dimIt->z)
726 && (destProjectedPos * destProjectedPos < 1.f)
730 // now, detect the closest hit point (the smallest alpha, with alpha, the percent of the move vector
731 // to reach the hit point)
733 const float epsilon = 10E-6f;
735 float alphaTop, alphaBottom, alphaCyl;
737 const float denum = (dest - pos) * K;
739 // top plane
741 if (fabs(denum) < epsilon)
743 alphaTop = (dimIt->z - (tPos * K)) / denum;
744 if (alphaTop < 0.f) alphaTop = 1.f;
746 else
748 alphaTop = 1.f;
751 // bottom plane
753 if (fabs(denum) < epsilon)
755 alphaBottom = (- dimIt->z - (tPos * K)) / denum;
756 if (alphaBottom < 0.f) alphaBottom = 1.f;
758 else
760 alphaBottom = 1.f;
763 // cylinder
765 //expressed the src and dest positions in the cylinder basis
767 const float ox = tPos * I, oy = tPos * J, dx = (destTPos - tPos) * I, dy = (destTPos - tPos) * J;
769 // coefficients of the equation : a * alpha ^ 2 + b * alpha + c = 0
770 const float a = (dx * dx) / (dimIt->x * dimIt->x)
771 + (dy * dy) / (dimIt->y * dimIt->y);
772 const float b = 2.f * (ox * dx) / (dimIt->x * dimIt->x)
773 + (oy * dy) / (dimIt->y * dimIt->y);
774 const float c = ox * ox + oy * oy - 1;
776 // discriminant
777 const float delta = b * b - 4.f * a * c;
779 if (delta < epsilon)
781 alphaCyl = 1.f;
783 else
785 const float deltaRoot = sqrtf(delta);
786 const float r1 = (- b - deltaRoot) / (2.f / a);
787 const float r2 = (- b - deltaRoot) / (2.f / a);
789 if (r1 < 0.f) alphaCyl = r2;
790 else if (r2 < 0.f) alphaCyl = r1;
791 else alphaCyl = r1 < r2 ? r1 : r2;
795 // now, choose the minimum positive dist
797 if (alphaTop < alphaBottom && alphaTop < alphaCyl)
799 // collision with the top plane
800 CVector startEnd = alphaTop * (dest - pos);
801 ci.newPos = pos + startEnd + PSCollideEpsilon * K;
802 ci.dist = startEnd.norm();
803 ci.newSpeed = (-2.f * (speed * K)) * K + speed;
804 ci.collisionZone = this;
806 (*it)->collisionUpdate(ci, k);
808 else
809 if (alphaBottom < alphaCyl)
811 // collision with the bottom plane
812 CVector startEnd = alphaBottom * (dest - pos);
813 ci.newPos = pos + startEnd - PSCollideEpsilon * K;
814 ci.dist = startEnd.norm();
815 ci.newSpeed = (-2.f * (speed * K)) * K + speed;
816 ci.collisionZone = this;
819 (*it)->collisionUpdate(ci, k);
821 else
823 // collision with the cylinder
825 CVector startEnd = alphaCyl * (dest - pos);
827 // normal at the hit point. It is the gradient of the implicit equation x^2 / a^2 + y^2 / b^2 - R^ 2= 0
828 // so we got unormalized n = (2 x / a ^ 2, 2 y / b ^ 2, 0) in the basis of the cylinder
829 // As we'll normalize it, we don't need the 2 factor
831 float px = ox + alphaCyl * dx;
832 float py = oy + alphaCyl * dy;
834 CVector normal = px / (dimIt->x * dimIt->x) * I + py / (dimIt->y * dimIt->y) * J;
835 normal.normalize();
837 ci.newPos = pos + startEnd - PSCollideEpsilon * normal;
838 ci.dist = startEnd.norm();
839 ci.newSpeed = (-2.f * (speed * normal)) * normal + speed;
840 ci.collisionZone = this;
842 (*it)->collisionUpdate(ci, k);
854 void CPSZoneCylinder::computeCollisions(CPSLocated &target, uint firstInstanceIndex, const NLMISC::CVector *posBefore, const NLMISC::CVector *posAfter)
856 NL_PS_FUNC(CPSZoneCylinder_computeCollisions)
857 MINI_TIMER(PSStatsZoneCylinder)
858 TPSAttribVector::const_iterator dimIt;
859 CPSAttrib<CPlaneBasis>::const_iterator basisIt;
860 TPSAttribVector::const_iterator cylinderPosIt, cylinderPosEnd;
861 CPSCollisionInfo ci;
862 // cycle through the cylinders
863 cylinderPosEnd = _Owner->getPos().end();
864 for (cylinderPosIt = _Owner->getPos().begin(), basisIt = _Basis.begin(), dimIt = _Dim.begin(); cylinderPosIt != cylinderPosEnd; ++cylinderPosIt, ++dimIt, ++basisIt)
866 // we must setup the cylinder in the good basis
867 const CMatrix &m = CPSLocated::getConversionMatrix(&target, this->_Owner);
868 // compute the new center pos
869 CVector center = m * *cylinderPosIt;
870 // compute a basis for the cylinder
871 CVector I = m.mulVector(basisIt->X);
872 CVector J = m.mulVector(basisIt->Y);
873 CVector K = m.mulVector(basisIt->X ^ basisIt->Y);
874 // the pos projected (and scale) over the plane basis of the cylinder, the pos minus the center
875 CVector projectedPos, tPos;
876 // the same, but with the final position
877 CVector destProjectedPos, destTPos;
878 // deals with each particle
879 // deals with each particle
880 const NLMISC::CVector *itPosBefore = posBefore + firstInstanceIndex;
881 const NLMISC::CVector *itPosBeforeEnd = posBefore + target.getSize();
882 const NLMISC::CVector *itPosAfter = posAfter + firstInstanceIndex;
883 while (itPosBefore != itPosBeforeEnd)
885 const CVector &pos = *itPosBefore;
886 // check whether current pos was outside the cylinder
887 tPos = pos - center;
888 projectedPos = (1 / dimIt->x) * (I * tPos) * I + (1 / dimIt->y) * (J * tPos) * J;
889 if (!
891 ((tPos * K) < dimIt->z)
892 && ((tPos * K) > -dimIt->z)
893 && (projectedPos * projectedPos < 1.f)
897 const CVector &dest = *itPosAfter;
898 destTPos = dest - center;
899 destProjectedPos = (1.f / dimIt->x) * (I * destTPos) * I + (1.f / dimIt->y) * (J * destTPos) * J;
900 // test whether the new position is inside the cylinder
901 if (
902 ((destTPos * K) < dimIt->z)
903 && ((destTPos * K) > -dimIt->z)
904 && (destProjectedPos * destProjectedPos < 1.f)
907 // now, detect the closest hit point (the smallest alpha, with alpha, the percent of the move vector
908 // to reach the hit point)
909 const float epsilon = 10E-3f;
910 float alphaTop, alphaBottom, alphaCyl;
911 const float denum = (dest - pos) * K;
912 // top plane
913 if (fabs(denum) < epsilon)
915 alphaTop = (dimIt->z - (tPos * K)) / denum;
916 if (alphaTop < 0.f) alphaTop = 1.f;
918 else
920 alphaTop = 1.f;
922 // bottom plane
923 if (fabs(denum) < epsilon)
925 alphaBottom = (- dimIt->z - (tPos * K)) / denum;
926 if (alphaBottom < 0.f) alphaBottom = 1.f;
928 else
930 alphaBottom = 1.f;
932 // cylinder
933 //expressed the src and dest positions in the cylinder basis
934 const float ox = tPos * I, oy = tPos * J, dx = (destTPos - tPos) * I, dy = (destTPos - tPos) * J;
935 // coefficients of the equation : a * alpha ^ 2 + b * alpha + c = 0
936 const float a = (dx * dx) / (dimIt->x * dimIt->x)
937 + (dy * dy) / (dimIt->y * dimIt->y);
938 const float b = 2.f * ((ox * dx) / (dimIt->x * dimIt->x)
939 + (oy * dy) / (dimIt->y * dimIt->y));
940 const float c = (ox * ox) / (dimIt->x * dimIt->x) + (oy * oy) / (dimIt->y * dimIt->y) - 1;
941 // discriminant
942 const float delta = b * b - 4.f * a * c;
944 if (delta < epsilon)
946 alphaCyl = 1.f;
948 else
950 const float deltaRoot = sqrtf(delta);
951 const float r1 = (- b + 2.f * deltaRoot) / (2.f * a);
952 const float r2 = (- b - 2.f * deltaRoot) / (2.f * a);
954 if (r1 < 0.f) alphaCyl = r2;
955 else if (r2 < 0.f) alphaCyl = r1;
956 else alphaCyl = r1 < r2 ? r1 : r2;
958 if (alphaCyl < 0.f) alphaCyl = 1.f;
960 const CVector &speed = target.getSpeed()[(uint32)(itPosBefore - posBefore)];
961 // now, choose the minimum positive dist
962 if (alphaTop < alphaBottom && alphaTop < alphaCyl)
964 // collision with the top plane
965 CVector startEnd = alphaTop * (dest - pos);
966 ci.NewPos = pos + startEnd + PSCollideEpsilon * K;
967 ci.Dist = startEnd.norm();
968 ci.NewSpeed = (-2.f * (speed * K)) * K + speed;
969 ci.CollisionZone = this;
970 CPSLocated::_Collisions[itPosBefore - posBefore].update(ci);
972 else
973 if (alphaBottom < alphaCyl)
975 // collision with the bottom plane
976 CVector startEnd = alphaBottom * (dest - pos);
977 ci.NewPos = pos + startEnd - PSCollideEpsilon * K;
978 ci.Dist = startEnd.norm();
979 ci.NewSpeed = (-2.f * (speed * K)) * K + speed;
980 ci.CollisionZone = this;
981 CPSLocated::_Collisions[itPosBefore - posBefore].update(ci);
983 else
985 // collision with the cylinder
986 CVector startEnd = alphaCyl * (dest - pos);
987 // normal at the hit point. It is the gradient of the implicit equation x^2 / a^2 + y^2 / b^2 - R^ 2= 0
988 // so we got unormalized n = (2 x / a ^ 2, 2 y / b ^ 2, 0) in the basis of the cylinder
989 // As we'll normalize it, we don't need the 2 factor
990 float px = ox + alphaCyl * dx;
991 float py = oy + alphaCyl * dy;
992 CVector normal = px / (dimIt->x * dimIt->x) * I + py / (dimIt->y * dimIt->y) * J;
993 normal.normalize();
994 ci.NewPos = pos + startEnd + PSCollideEpsilon * normal;
995 ci.Dist = startEnd.norm();
996 ci.NewSpeed = (-2.f * (speed * normal)) * normal + speed;
997 ci.CollisionZone = this;
998 CPSLocated::_Collisions[itPosBefore - posBefore].update(ci);
1003 ++ itPosBefore;
1004 ++ itPosAfter;
1009 void CPSZoneCylinder::show()
1011 NL_PS_FUNC(CPSZoneCylinder_show)
1012 TPSAttribVector::const_iterator dimIt = _Dim.begin()
1013 ,posIt = _Owner->getPos().begin()
1014 , endPosIt = _Owner->getPos().end();
1016 CPSAttrib<CPlaneBasis>::const_iterator basisIt = _Basis.begin();
1018 setupDriverModelMatrix();
1019 CMatrix mat;
1023 CPSLocated *loc;
1024 uint32 index;
1025 CPSLocatedBindable *lb;
1026 _Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
1029 for (uint32 k = 0; posIt != endPosIt; ++posIt, ++dimIt, ++basisIt, ++k)
1031 mat.setRot(basisIt->X, basisIt->Y, basisIt->X ^ basisIt->Y);
1032 mat.setPos(CVector::Null);
1034 const CRGBA col = ((lb == NULL || this == lb) && loc == _Owner && index == k ? CRGBA::Red : CRGBA(127, 127, 127));
1037 CPSUtil::displayCylinder(*getDriver(), *posIt, mat, *dimIt, 32, col);
1039 mat.setPos(*posIt);
1040 CPSUtil::displayBasis(getDriver() ,getLocalToWorldMatrix(), mat, 1.f, *getFontGenerator(), *getFontManager());
1041 setupDriverModelMatrix();
1048 void CPSZoneCylinder::setMatrix(uint32 index, const CMatrix &m)
1050 NL_PS_FUNC(CPSZoneCylinder_setMatrix)
1051 // transform the basis
1052 _Basis[index].X = m.getI();
1053 _Basis[index].Y = m.getJ();
1055 // compute new pos
1056 _Owner->getPos()[index] = m.getPos();
1063 CMatrix CPSZoneCylinder::getMatrix(uint32 index) const
1065 NL_PS_FUNC(CPSZoneCylinder_getMatrix)
1066 CMatrix m;
1067 m.setRot(_Basis[index].X, _Basis[index].Y, _Basis[index].X ^_Basis[index].Y);
1068 m.setPos(_Owner->getPos()[index]);
1069 return m;
1073 void CPSZoneCylinder::setScale(uint32 k, float scale)
1075 NL_PS_FUNC(CPSZoneCylinder_setScale)
1076 _Dim[k] = CVector(scale, scale, scale);
1079 CVector CPSZoneCylinder::getScale(uint32 k) const
1081 NL_PS_FUNC(CPSZoneCylinder_getScale)
1082 return _Dim[k];
1085 void CPSZoneCylinder::setScale(uint32 index, const CVector &s)
1087 NL_PS_FUNC(CPSZoneCylinder_setScale)
1088 _Dim[index] = s;
1092 void CPSZoneCylinder::serial(NLMISC::IStream &f)
1094 NL_PS_FUNC(CPSZoneCylinder_serial)
1095 f.serialVersion(1);
1096 CPSZone::serial(f);
1097 f.serial(_Basis);
1098 f.serial(_Dim);
1103 void CPSZoneCylinder::resize(uint32 size)
1105 NL_PS_FUNC(CPSZoneCylinder_resize)
1106 nlassert(size < (1 << 16));
1107 _Basis.resize(size);
1108 _Dim.resize(size);
1111 void CPSZoneCylinder::newElement(const CPSEmitterInfo &info)
1113 NL_PS_FUNC(CPSZoneCylinder_newElement)
1114 _Basis.insert(CPlaneBasis(CVector::K));
1115 _Dim.insert(CVector(1, 1, 1));
1118 void CPSZoneCylinder::deleteElement(uint32 index)
1120 NL_PS_FUNC(CPSZoneCylinder_deleteElement)
1121 _Basis.remove(index);
1122 _Dim.remove(index);
1126 //////////////////////////////////////////////
1127 // implementation of CPSZoneRectangle //
1128 //////////////////////////////////////////////
1130 void CPSZoneRectangle::computeCollisions(CPSLocated &target, uint firstInstanceIndex, const NLMISC::CVector *posBefore, const NLMISC::CVector *posAfter)
1132 NL_PS_FUNC(CPSZoneRectangle_computeCollisions)
1133 MINI_TIMER(PSStatsZoneRectangle)
1134 // for each target, we must check whether they are going through the rectangle
1135 // if so they must bounce
1136 TPSAttribVector::const_iterator rectanglePosIt, rectanglePosEnd;
1137 CPSAttrib<CPlaneBasis>::const_iterator basisIt;
1138 TPSAttribFloat::const_iterator widthIt, heightIt;
1139 CPSCollisionInfo ci;
1140 // alpha is the ratio that gives the percent of endPos - startPos that hit the rectangle
1141 basisIt = _Basis.begin();
1142 heightIt = _Height.begin();
1143 widthIt = _Width.begin();
1144 rectanglePosEnd = _Owner->getPos().end();
1145 for (rectanglePosIt = _Owner->getPos().begin(); rectanglePosIt != rectanglePosEnd; ++rectanglePosIt, ++basisIt, ++widthIt, ++heightIt)
1147 // we must setup the rectangle in the good basis
1148 const CMatrix &m = CPSLocated::getConversionMatrix(&target, this->_Owner);
1149 NLMISC::CPlane p;
1150 CVector center = m * (*rectanglePosIt);
1151 const CVector X = m.mulVector(basisIt->X);
1152 const CVector Y = m.mulVector(basisIt->Y);
1153 p.make(X ^ Y, center);
1154 // deals with each particle
1155 const float epsilon = 0.5f * PSCollideEpsilon;
1156 const NLMISC::CVector *itPosBefore = posBefore + firstInstanceIndex;
1157 const NLMISC::CVector *itPosBeforeEnd = posBefore + target.getSize();
1158 const NLMISC::CVector *itPosAfter = posAfter + firstInstanceIndex;
1159 while (itPosBefore != itPosBeforeEnd)
1161 float posSide = p * *itPosBefore;
1162 float negSide = p * *itPosAfter;
1163 if (posSide >= - epsilon && negSide <= epsilon)
1165 float alpha;
1166 if (fabsf(posSide - negSide) > std::numeric_limits<float>::min())
1168 alpha = posSide / (posSide - negSide);
1170 else
1172 alpha = 0.f;
1174 CVector startEnd = alpha * (*itPosAfter - *itPosBefore);
1175 ci.Dist = startEnd.norm();
1176 // we translate the particle from an epsilon so that it won't get hooked to the rectangle
1177 ci.NewPos = *itPosBefore + startEnd;
1178 // tmp
1179 if ( fabs( (ci.NewPos - center) * X ) < *widthIt && fabs( (ci.NewPos - center) * Y ) < *heightIt) // check collision against rectangle
1181 ci.NewPos += PSCollideEpsilon * p.getNormal();
1182 const CVector &speed = target.getSpeed()[(uint32)(itPosBefore - posBefore)];
1183 ci.NewSpeed = _BounceFactor * (speed - 2.0f * (speed * p.getNormal()) * p.getNormal());
1184 ci.CollisionZone = this;
1185 CPSLocated::_Collisions[itPosBefore - posBefore].update(ci);
1188 ++ itPosBefore;
1189 ++ itPosAfter;
1196 void CPSZoneRectangle::show()
1198 NL_PS_FUNC(CPSZoneRectangle_show)
1199 nlassert(_Owner);
1200 const uint size = _Owner->getSize();
1201 if (!size) return;
1202 setupDriverModelMatrix();
1203 CMatrix mat;
1205 CPSLocated *loc;
1206 uint32 index;
1207 CPSLocatedBindable *lb;
1208 _Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
1210 for (uint k = 0; k < size; ++k)
1212 const CVector &I = _Basis[k].X;
1213 const CVector &J = _Basis[k].Y;
1214 mat.setRot(I, J , I ^J);
1215 mat.setPos(_Owner->getPos()[k]);
1216 CPSUtil::displayBasis(getDriver(), getLocalToWorldMatrix(), mat, 1.f, *getFontGenerator(), *getFontManager());
1217 setupDriverModelMatrix();
1219 const CRGBA col = ((lb == NULL || this == lb) && loc == _Owner && index == k ? CRGBA::Red : CRGBA(127, 127, 127));
1223 const CVector &pos = _Owner->getPos()[k];
1224 CPSUtil::display3DQuad(*getDriver(), pos + I * _Width[k] + J * _Height[k]
1225 , pos + I * _Width[k] - J * _Height[k]
1226 , pos - I * _Width[k] - J * _Height[k]
1227 , pos - I * _Width[k] + J * _Height[k], col);
1231 void CPSZoneRectangle::setMatrix(uint32 index, const CMatrix &m)
1233 NL_PS_FUNC(CPSZoneRectangle_setMatrix)
1234 nlassert(_Owner);
1236 _Owner->getPos()[index] = m.getPos();
1237 _Basis[index].X = m.getI();
1238 _Basis[index].Y = m.getJ();
1242 CMatrix CPSZoneRectangle::getMatrix(uint32 index) const
1244 NL_PS_FUNC(CPSZoneRectangle_getMatrix)
1245 nlassert(_Owner);
1246 CMatrix m;
1247 m.setRot(_Basis[index].X, _Basis[index].Y, _Basis[index].X ^ _Basis[index].Y);
1248 m.setPos(_Owner->getPos()[index]);
1249 return m;
1252 void CPSZoneRectangle::setScale(uint32 index, float scale)
1254 NL_PS_FUNC(CPSZoneRectangle_setScale)
1255 _Width[index] = scale;
1256 _Height[index] = scale;
1258 void CPSZoneRectangle::setScale(uint32 index, const CVector &s)
1260 NL_PS_FUNC(CPSZoneRectangle_setScale)
1261 _Width[index] = s.x;
1262 _Height[index] = s.y;
1264 CVector CPSZoneRectangle::getScale(uint32 index) const
1266 NL_PS_FUNC(CPSZoneRectangle_getScale)
1267 return CVector(_Width[index], _Height[index], 1.f);
1272 void CPSZoneRectangle::serial(NLMISC::IStream &f)
1274 NL_PS_FUNC(CPSZoneRectangle_IStream )
1275 f.serialVersion(1);
1276 CPSZone::serial(f);
1277 f.serial(_Basis);
1278 f.serial(_Width);
1279 f.serial(_Height);
1283 void CPSZoneRectangle::resize(uint32 size)
1285 NL_PS_FUNC(CPSZoneRectangle_resize)
1286 nlassert(size < (1 << 16));
1287 _Basis.resize(size);
1288 _Width.resize(size);
1289 _Height.resize(size);
1292 void CPSZoneRectangle::newElement(const CPSEmitterInfo &info)
1294 NL_PS_FUNC(CPSZoneRectangle_newElement)
1295 _Basis.insert(CPlaneBasis(CVector::K));
1296 _Width.insert(1.f);
1297 _Height.insert(1.f);
1300 void CPSZoneRectangle::deleteElement(uint32 index)
1302 NL_PS_FUNC(CPSZoneRectangle_deleteElement)
1303 _Basis.remove(index);
1304 _Width.remove(index);
1305 _Height.remove(index);
1310 } // NL3D