1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
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/>.
22 #include "reynolds_manager.h"
25 using namespace NLMISC
;
26 using namespace NLPACS
;
29 // ------------------------------------
30 // Attraction/Repulsion functions
32 // strong repulsion limited
33 inline double softPointing(double dist
, double strength
, double amp
= 1.0)
35 return amp
*atan(strength
*dist
)/1.5707963268;
38 // strong repulsion limited
39 inline double softTargetting(double dist
, double moderateDist
, double strength
, double amp
= 1.0)
41 return amp
*( atan(strength
*(dist
-moderateDist
-0.2)) + atan(strength
*(dist
-moderateDist
+0.2)) )/3.1415927;
44 // strong repulsion limited
45 inline double softRepulse(double dist
, double moderateDist
, double strength
, double amp
= 1.0)
47 return amp
*(0.5 - atan(strength
*(dist
-moderateDist
)-1.0)/3.1415927);
52 // Required target spacing
53 double CTrack::TargetSpacing
= 0.5;
55 // Target attraction strength
56 double CTrack::TargetAttraction
= 3.0;
58 // Target attraction amplification
59 double CTrack::TargetAmp
= 2.0;
61 // Fast obstacle exclusion distance
62 double CTrack::ObstacleExcludeDistance
= 6.0;
64 // Required obstacle spacing
65 double CTrack::ObstacleSpacing
= 0.5;
67 // Obstacle repulsion strength
68 double CTrack::ObstacleRepulsion
= 3.0;
70 // Obstacle repulsion amplification
71 double CTrack::ObstacleAmp
= 2.0;
74 // Minimum motion distance
75 double CTrack::MinimumMotion
= 0.2;
77 // Lock distance threshold
78 double CTrack::LockThreshold
= 0.1;
80 // Lose distance threshold
81 double CTrack::LoseThreshold
= 2.0;
84 uint32
CTrack::StabiliseCycle
= 5;
88 CTrack::CSheet
CTrack::CSheet::DefaultSheet
;
94 // ------------------------------------
98 nldebug("ReynoldsLib:CTrack:~CTrack(): Delete Track %s", _Id
.toString().c_str());
100 // remove this track from the manager map
101 CReynoldsManager::removeTrackFromMap(_Id
);
103 // delete move primitive
104 deleteMovePrimitive();
106 nldebug("ReynoldsLib:CTrack:~CTrack(): Track %s deleted", _Id
.toString().c_str());
109 // ------------------------------------
111 void CTrack::setId(const NLMISC::CEntityId
&id
, const NLMISC::CSheetId
&sheet
)
116 _IdRequested
= false;
118 _Sheet
= CReynoldsManager::lookup(_SheetId
);
120 _Sheet
= &(CSheet::DefaultSheet
);
124 // ------------------------------------
126 void CTrack::follow(CTrack
*followed
)
131 _Followed
= followed
;
134 // ------------------------------------
143 // warn reynolds manager the track left its target
144 CReynoldsManager::trackStop(this);
150 // ------------------------------------
152 void CTrack::update(double dt
)
154 // do not update not owned tracks
158 // check if target is forced to leave
159 CTrack
*target
= (CTrack
*)_Followed
;
160 if (target
== NULL
|| target
->_ForceRelease
)
166 // check if entity has id
167 if (!hasId() || !hasPosition())
170 // check if has a move primitive
171 if (_MovePrimitive
== NULL
)
173 createMovePrimitive();
174 // if failed, just leave
175 if (_MovePrimitive
== NULL
)
180 // if target hasn't position and is not owned by manager, request for position updates
181 if (!target
->isValid())
185 CVectorD
motion(CVectorD::Null
);
189 // --------------------------
190 // move toward target
191 double targetObjective
;
192 if (target
->isStatic())
197 ddist
= rawDistance(target
, vdist
);
200 targetObjective
= ddist
;
202 double strength
= softPointing(ddist
, TargetAttraction
, TargetAmp
);
204 motion
+= (vdist
* (_Sheet
->RunSpeed
*dt
*strength
/(ddist
+0.01)));
211 double cdist
= contactDistance(target
, vdist
, ddist
);
213 targetObjective
= cdist
-TargetSpacing
;
215 double strength
= softTargetting(cdist
, TargetSpacing
, TargetAttraction
, TargetAmp
);
217 motion
+= (vdist
* (_Sheet
->RunSpeed
*dt
*strength
/(ddist
+0.01)));
221 // --------------------------
222 // check target not lost
223 if (_State
== TargetLocked
&&
224 targetObjective
> LoseThreshold
)
227 CReynoldsManager::stateChanged(_Id
, _State
);
233 // --------------------------
234 // check target reachable
235 const double SmoothFactor
= 0.7;
236 double tddelta
= (_LastTargetDistance
<0) ? _SmoothedTargetDistanceDelta
: _LastTargetDistance
-targetObjective
;
237 _SmoothedTargetDistanceDelta
= _SmoothedTargetDistanceDelta
*SmoothFactor
+ tddelta
*(1.0-SmoothFactor
);
238 _LastTargetDistance
= targetObjective
;
240 // if actor seems not to move fast enough, leave
241 if (_State
== MovingTowardsTarget
&&
242 _SmoothedTargetDistanceDelta
< 0.1 &&
243 targetObjective
> 1.0)
245 _State
= TargetUnreachable
;
246 CReynoldsManager::stateChanged(_Id
, _State
);
253 // --------------------------
254 // avoid obstacles in vision
255 TVision::iterator itv
;
256 for (itv
=_Vision
.begin(); itv
!=_Vision
.end(); )
258 CTrack
*obstacle
= (CTrack
*)((*itv
).second
);
260 // if obstacle is forced to be release, delete it
261 if (obstacle
->_ForceRelease
)
263 TVision::iterator itr
= itv
++;
268 // if obstacle not yet ready, don't avoid it
269 if (!obstacle
->isValid())
272 // don't avoid static obstacles (virtual tracks)
273 if (!obstacle
->isStatic())
278 ddist
= rawDistance(obstacle
, vdist
);
280 if (ddist
> ObstacleExcludeDistance
)
283 double cdist
= contactDistanceWithRawDistance(obstacle
, vdist
, ddist
);
284 double strength
= softRepulse(cdist
, ObstacleSpacing
, ObstacleRepulsion
, ObstacleAmp
);
286 motion
-= (vdist
* (_Sheet
->WalkSpeed
*dt
*strength
/(ddist
+0.01)));
292 // --------------------------
294 CVectorD front
= motion
.normed();
295 CVectorD
lateral(-front
.y
, front
.x
, 0.0);
297 float thetaProbe
= frand(3.1415926535f
) - 1.570796f
;
299 if (!_MoveContainer
->testMove(_MovePrimitive
, front
*cos(thetaProbe
)*2.0 + lateral
*sin(thetaProbe
)*1.0, 1, 0, &normal
))
300 motion
+= normal
*_Sheet
->WalkSpeed
*dt
;
306 // --------------------------
307 // other motion applied by user
308 CReynoldsManager::applyUserMotion(this, motion
);
314 // --------------------------
316 double motionNorm
= motion
.norm();
318 // check if enough motion
319 if (motionNorm
< MinimumMotion
)
321 if (targetObjective
< LockThreshold
&&
322 _State
== MovingTowardsTarget
&&
323 CReynoldsManager::getCycle()-_LastMoveCycle
> StabiliseCycle
)
325 _State
= TargetLocked
;
326 CReynoldsManager::stateChanged(_Id
, _State
);
331 // reset last moving cycle
332 _LastMoveCycle
= CReynoldsManager::getCycle();
334 // renorm motion if needed
335 if (motionNorm
> dt
*_Sheet
->RunSpeed
)
336 motion
*= dt
*_Sheet
->RunSpeed
/motionNorm
;
339 _MovePrimitive
->move(motion
, 0);
340 _MoveContainer
->evalNCPrimitiveCollision(1, _MovePrimitive
, 0);
342 // store new position/heading
343 _Heading
= (float)atan2(heading
.y
, heading
.x
);
344 _Position
= _MovePrimitive
->getFinalPosition(0);
349 // ------------------------------------
351 void CTrack::updateVision(const std::vector
<NLMISC::CEntityId
> &in
, const std::vector
<NLMISC::CEntityId
> &out
)
356 for (i
=0; i
<in
.size(); ++i
)
358 CTrack
*newin
= CReynoldsManager::createTrack(in
[i
]);
362 _Vision
.insert(make_pair
<CEntityId
, CSmartPtr
<CTrack
> >(in
[i
], newin
));
365 // remove old of vision
366 for (i
=0; i
<out
.size(); ++i
)
368 _Vision
.erase(out
[i
]);
372 // ------------------------------------
374 void CTrack::updateVision(const std::vector
<NLMISC::CEntityId
> &vision
)
379 TVision copy
= _Vision
;
381 for (i
=0; i
<vision
.size(); ++i
)
383 CTrack
*newin
= CReynoldsManager::createTrack(vision
[i
]);
387 _Vision
.insert(make_pair
<CEntityId
, CSmartPtr
<CTrack
> >(vision
[i
], newin
));
396 // ------------------------------------
398 void CTrack::acquireControl()
400 // set state to moving
401 _State
= MovingTowardsTarget
;
402 CReynoldsManager::stateChanged(_Id
, _State
);
407 // unrequest for position updates if necessary
408 if (_PositionUpdatesRequested
)
410 CReynoldsManager::unrequestPositionUpdates(_Id
);
411 _PositionUpdatesRequested
= false;
414 // invalidate position
417 // and request for valid position
418 CReynoldsManager::requestPosition(_Id
);
421 CReynoldsManager::initUserMotion(this);
425 // init last move cycle
426 _LastMoveCycle
= CReynoldsManager::getCycle();
429 // ------------------------------------
431 void CTrack::releaseControl()
433 // invalidate position
437 deleteMovePrimitive();
439 // release user motion
440 CReynoldsManager::releaseUserMotion(this);
442 // set state to moving
444 CReynoldsManager::stateChanged(_Id
, _State
);
450 // ------------------------------------
452 void CTrack::acquireVision()
457 _ReceiveVision
= true;
458 CReynoldsManager::requestVision(_Id
);
461 // ------------------------------------
463 void CTrack::releaseVision()
468 _ReceiveVision
= false;
469 CReynoldsManager::unrequestVision(_Id
);
475 // ------------------------------------
476 // Create Move primitive
477 void CTrack::createMovePrimitive()
481 nlwarning("ReynoldsLib:CTrack:createMovePrimitive(): Can't create move primitive, Track %s has no valid position", _Id
.toString().c_str());
487 nlwarning("ReynoldsLib:CTrack:createMovePrimitive(): Can't create move primitive, Track %s has no valid id", _Id
.toString().c_str());
491 deleteMovePrimitive();
493 CReynoldsManager::createMovePrimitive(_Position
, _MovePrimitive
, _MoveContainer
);
495 _MovePrimitive
->setPrimitiveType(UMovePrimitive::_2DOrientedCylinder
);
496 _MovePrimitive
->setReactionType(UMovePrimitive::Slide
);
497 _MovePrimitive
->setTriggerType(UMovePrimitive::NotATrigger
);
498 _MovePrimitive
->setCollisionMask(0);
499 _MovePrimitive
->setOcclusionMask(0);
500 _MovePrimitive
->setObstacle(false);
501 _MovePrimitive
->setDontSnapToGround(false);
502 _MovePrimitive
->setRadius(_Sheet
->Radius
);
503 _MovePrimitive
->setHeight(_Sheet
->Height
);
505 _MovePrimitive
->insertInWorldImage(0);
506 _MovePrimitive
->setGlobalPosition(_Position
, 0);
507 _MovePrimitive
->setOrientation(_Heading
, 0);
508 _MoveContainer
->evalCollision(1, 0);
511 // ------------------------------------
512 // Delete Move primitive
513 void CTrack::deleteMovePrimitive()
515 if (_MovePrimitive
!= NULL
)
517 if (_MoveContainer
== NULL
)
519 nlwarning("ReynoldsLib:CTrack:deleteMovePrimitive(): Track %s has a MovePrimitive, but MoveContainer not set", _Id
.toString().c_str());
523 _MoveContainer
->removePrimitive(_MovePrimitive
);
527 _MovePrimitive
= NULL
;
528 _MoveContainer
= NULL
;
531 // ------------------------------------
533 void CTrack::requestId()
536 CReynoldsManager::requestSheet(_Id
);
539 // ------------------------------------
541 void CTrack::requestPositionUpdates()
543 _PositionUpdatesRequested
= true;
544 CReynoldsManager::requestPositionUpdates(_Id
);