1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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"
30 #include "nel/3d/particle_system_model.h"
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
)
54 CPSTargetLocatedBindable::serial(f
);
55 f
.serialEnum(_CollisionBehaviour
);
56 f
.serial(_BounceFactor
);
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
107 // build a basis with K = the normal of the plane
108 CMatrix
CPSZonePlane::buildBasis(uint32 index
) const
110 NL_PS_FUNC(CPSZonePlane_buildBasis
)
112 m
.setPos(_Owner
->getPos()[index
]);
113 CPSUtil::buildSchmidtBasis(_Normal
[index
], 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();
144 setupDriverModelMatrix();
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(),
160 CDRU::drawLine(*it
+ (planeSize
+ 3) * mat
.getI() - planeSize
* mat
.getJ(),
161 *it
- (planeSize
+ 3) * mat
.getI() - planeSize
* mat
.getJ(),
165 CDRU::drawLine(*it
+ planeSize
* mat
.getI() + (planeSize
+ 3) * mat
.getJ(),
166 *it
+ planeSize
* mat
.getI() - (planeSize
+ 3) * mat
.getJ(),
169 CDRU::drawLine(*it
- planeSize
* mat
.getI() + (planeSize
+ 3) * mat
.getJ(),
170 *it
- planeSize
* mat
.getI() - (planeSize
+ 3) * mat
.getJ(),
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
;
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
;
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
)
230 if (fabsf(posSide
- negSide
) > std::numeric_limits
<float>::min())
232 alpha
= posSide
/ (posSide
- negSide
);
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
);
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
)
283 void CPSZonePlane::serial(NLMISC::IStream
&f
)
285 NL_PS_FUNC(CPSZonePlane_serial
)
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
;
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
;
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
);
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
);
369 void CPSZoneSphere::show()
371 NL_PS_FUNC(CPSZoneSphere_show
)
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());
395 _Owner
->getPos()[index
] = m
.getPos();
400 CMatrix
CPSZoneSphere::getMatrix(uint32 index
) const
402 NL_PS_FUNC(CPSZoneSphere_getMatrix
)
403 nlassert(index
< _Radius
.getSize());
406 m
.translate(_Owner
->getPos()[index
]);
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
)
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
)
443 nlassert(_Radius
.getSize() != _Radius
.getMaxSize());
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
;
466 // the square of radius at the hit point
468 // alpha is the ratio that gives the percent of endPos - startPos that hit the disc
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
);
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
)
494 if (fabsf(posSide
- negSide
) > std::numeric_limits
<float>::min())
496 alpha
= posSide
/ (posSide
- negSide
);
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
);
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();
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
);
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());
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
)
572 m
.translate(_Owner
->getPos()[index
]);
573 CPSUtil::buildSchmidtBasis(_Normal
[index
], b
);
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
)
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
)
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
)
625 nlassert(_Radius
.getSize() != _Radius
.getMaxSize());
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;
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
705 projectedPos = (1 / dimIt->x) * (I * tPos) * I + (1 / dimIt->y) * (J * tPos) * J;
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
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;
741 if (fabs(denum) < epsilon)
743 alphaTop = (dimIt->z - (tPos * K)) / denum;
744 if (alphaTop < 0.f) alphaTop = 1.f;
753 if (fabs(denum) < epsilon)
755 alphaBottom = (- dimIt->z - (tPos * K)) / denum;
756 if (alphaBottom < 0.f) alphaBottom = 1.f;
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;
777 const float delta = b * b - 4.f * a * c;
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);
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);
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;
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
;
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
888 projectedPos
= (1 / dimIt
->x
) * (I
* tPos
) * I
+ (1 / dimIt
->y
) * (J
* tPos
) * J
;
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
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
;
913 if (fabs(denum
) < epsilon
)
915 alphaTop
= (dimIt
->z
- (tPos
* K
)) / denum
;
916 if (alphaTop
< 0.f
) alphaTop
= 1.f
;
923 if (fabs(denum
) < epsilon
)
925 alphaBottom
= (- dimIt
->z
- (tPos
* K
)) / denum
;
926 if (alphaBottom
< 0.f
) alphaBottom
= 1.f
;
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;
942 const float delta
= b
* b
- 4.f
* a
* c
;
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
);
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
);
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
;
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
);
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();
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
);
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();
1056 _Owner
->getPos()[index
] = m
.getPos();
1063 CMatrix
CPSZoneCylinder::getMatrix(uint32 index
) const
1065 NL_PS_FUNC(CPSZoneCylinder_getMatrix
)
1067 m
.setRot(_Basis
[index
].X
, _Basis
[index
].Y
, _Basis
[index
].X
^_Basis
[index
].Y
);
1068 m
.setPos(_Owner
->getPos()[index
]);
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
)
1085 void CPSZoneCylinder::setScale(uint32 index
, const CVector
&s
)
1087 NL_PS_FUNC(CPSZoneCylinder_setScale
)
1092 void CPSZoneCylinder::serial(NLMISC::IStream
&f
)
1094 NL_PS_FUNC(CPSZoneCylinder_serial
)
1103 void CPSZoneCylinder::resize(uint32 size
)
1105 NL_PS_FUNC(CPSZoneCylinder_resize
)
1106 nlassert(size
< (1 << 16));
1107 _Basis
.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
);
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
);
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
)
1166 if (fabsf(posSide
- negSide
) > std::numeric_limits
<float>::min())
1168 alpha
= posSide
/ (posSide
- negSide
);
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
;
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
);
1196 void CPSZoneRectangle::show()
1198 NL_PS_FUNC(CPSZoneRectangle_show
)
1200 const uint size
= _Owner
->getSize();
1202 setupDriverModelMatrix();
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
)
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
)
1247 m
.setRot(_Basis
[index
].X
, _Basis
[index
].Y
, _Basis
[index
].X
^ _Basis
[index
].Y
);
1248 m
.setPos(_Owner
->getPos()[index
]);
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
)
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
));
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
);