2
* Simulation for obstacle avoidance behavior
4
* ***** BEGIN GPL LICENSE BLOCK *****
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License
8
* as published by the Free Software Foundation; either version 2
9
* of the License, or (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software Foundation,
18
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
* Contributor(s): none yet.
22
* ***** END GPL LICENSE BLOCK *****
25
#include "KX_ObstacleSimulation.h"
26
#include "KX_NavMeshObject.h"
27
#include "KX_PythonInit.h"
28
#include "DNA_object_types.h"
33
inline float perp(const MT_Vector2& a, const MT_Vector2& b) { return a.x()*b.y() - a.y()*b.x(); }
35
inline float sqr(float x) { return x*x; }
36
inline float lerp(float a, float b, float t) { return a + (b-a)*t; }
37
inline float clamp(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); }
39
inline float vdistsqr(const float* a, const float* b) { return sqr(b[0]-a[0]) + sqr(b[1]-a[1]); }
40
inline float vdist(const float* a, const float* b) { return sqrtf(vdistsqr(a,b)); }
41
inline void vcpy(float* a, const float* b) { a[0]=b[0]; a[1]=b[1]; }
42
inline float vdot(const float* a, const float* b) { return a[0]*b[0] + a[1]*b[1]; }
43
inline float vperp(const float* a, const float* b) { return a[0]*b[1] - a[1]*b[0]; }
44
inline void vsub(float* v, const float* a, const float* b) { v[0] = a[0]-b[0]; v[1] = a[1]-b[1]; }
45
inline void vadd(float* v, const float* a, const float* b) { v[0] = a[0]+b[0]; v[1] = a[1]+b[1]; }
46
inline void vscale(float* v, const float* a, const float s) { v[0] = a[0]*s; v[1] = a[1]*s; }
47
inline void vset(float* v, float x, float y) { v[0]=x; v[1]=y; }
48
inline float vlensqr(const float* v) { return vdot(v,v); }
49
inline float vlen(const float* v) { return sqrtf(vlensqr(v)); }
50
inline void vlerp(float* v, const float* a, const float* b, float t) { v[0] = lerp(a[0], b[0], t); v[1] = lerp(a[1], b[1], t); }
51
inline void vmad(float* v, const float* a, const float* b, float s) { v[0] = a[0] + b[0]*s; v[1] = a[1] + b[1]*s; }
52
inline void vnorm(float* v)
63
inline float triarea(const float* a, const float* b, const float* c)
65
return (b[0]*a[1] - a[0]*b[1]) + (c[0]*b[1] - b[0]*c[1]) + (a[0]*c[1] - c[0]*a[1]);
68
static void closestPtPtSeg(const float* pt,
69
const float* sp, const float* sq,
76
if (t <= 0.0f) { t = 0; return; }
77
float d = vdot(dir,dir);
78
if (t >= d) { t = 1; return; }
82
static float distPtSegSqr(const float* pt, const float* sp, const float* sq)
85
closestPtPtSeg(pt, sp,sq, t);
88
return vdistsqr(pt,np);
91
static int sweepCircleCircle(const MT_Vector3& pos0, const MT_Scalar r0, const MT_Vector2& v,
92
const MT_Vector3& pos1, const MT_Scalar r1,
93
float& tmin, float& tmax)
95
static const float EPS = 0.0001f;
96
MT_Vector2 c0(pos0.x(), pos0.y());
97
MT_Vector2 c1(pos1.x(), pos1.y());
98
MT_Vector2 s = c1 - c0;
100
float c = s.length2() - r*r;
101
float a = v.length2();
102
if (a < EPS) return 0; // not moving
104
// Overlap, calc time to exit.
105
float b = MT_dot(v,s);
107
if (d < 0.0f) return 0; // no intersection.
108
tmin = (b - sqrtf(d)) / a;
109
tmax = (b + sqrtf(d)) / a;
113
static int sweepCircleSegment(const MT_Vector3& pos0, const MT_Scalar r0, const MT_Vector2& v,
114
const MT_Vector3& pa, const MT_Vector3& pb, const MT_Scalar sr,
115
float& tmin, float &tmax)
117
// equation parameters
118
MT_Vector2 c0(pos0.x(), pos0.y());
119
MT_Vector2 sa(pa.x(), pa.y());
120
MT_Vector2 sb(pb.x(), pb.y());
121
MT_Vector2 L = sb-sa;
122
MT_Vector2 H = c0-sa;
123
MT_Scalar radius = r0+sr;
124
float l2 = L.length2();
125
float r2 = radius * radius;
126
float dl = perp(v, L);
127
float hl = perp(H, L);
129
float b = 2.0f * hl * dl;
130
float c = hl * hl - (r2 * l2);
131
float d = (b*b) - (4.0f * a * c);
133
// infinite line missed by infinite ray.
138
tmin = (-b - d) / (2.0f * a);
139
tmax = (-b + d) / (2.0f * a);
141
// line missed by ray range.
142
/* if (tmax < 0.0f || tmin > 1.0f)
145
// find what part of the ray was collided.
149
float e0 = MT_dot(H, L) / l2;
152
float e1 = MT_dot(H, L) / l2;
154
if (e0 < 0.0f || e1 < 0.0f)
157
if (sweepCircleCircle(pos0, r0, v, pa, sr, ctmin, ctmax))
159
if (e0 < 0.0f && ctmin > tmin)
161
if (e1 < 0.0f && ctmax < tmax)
170
if (e0 > 1.0f || e1 > 1.0f)
173
if (sweepCircleCircle(pos0, r0, v, pb, sr, ctmin, ctmax))
175
if (e0 > 1.0f && ctmin > tmin)
177
if (e1 > 1.0f && ctmax < tmax)
189
static bool inBetweenAngle(float a, float amin, float amax, float& t)
191
if (amax < amin) amax += (float)M_PI*2;
192
if (a < amin-(float)M_PI) a += (float)M_PI*2;
193
if (a > amin+(float)M_PI) a -= (float)M_PI*2;
194
if (a >= amin && a < amax)
196
t = (a-amin) / (amax-amin);
202
static float interpolateToi(float a, const float* dir, const float* toi, const int ntoi)
204
for (int i = 0; i < ntoi; ++i)
206
int next = (i+1) % ntoi;
208
if (inBetweenAngle(a, dir[i], dir[next], t))
210
return lerp(toi[i], toi[next], t);
216
KX_ObstacleSimulation::KX_ObstacleSimulation(MT_Scalar levelHeight, bool enableVisualization)
217
: m_levelHeight(levelHeight)
218
, m_enableVisualization(enableVisualization)
223
KX_ObstacleSimulation::~KX_ObstacleSimulation()
225
for (size_t i=0; i<m_obstacles.size(); i++)
227
KX_Obstacle* obs = m_obstacles[i];
232
KX_Obstacle* KX_ObstacleSimulation::CreateObstacle(KX_GameObject* gameobj)
234
KX_Obstacle* obstacle = new KX_Obstacle();
235
obstacle->m_gameObj = gameobj;
237
vset(obstacle->vel, 0,0);
238
vset(obstacle->pvel, 0,0);
239
vset(obstacle->dvel, 0,0);
240
vset(obstacle->nvel, 0,0);
241
for (int i = 0; i < VEL_HIST_SIZE; ++i)
242
vset(&obstacle->hvel[i*2], 0,0);
245
gameobj->RegisterObstacle(this);
246
m_obstacles.push_back(obstacle);
250
void KX_ObstacleSimulation::AddObstacleForObj(KX_GameObject* gameobj)
252
KX_Obstacle* obstacle = CreateObstacle(gameobj);
253
struct Object* blenderobject = gameobj->GetBlenderObject();
254
obstacle->m_type = KX_OBSTACLE_OBJ;
255
obstacle->m_shape = KX_OBSTACLE_CIRCLE;
256
obstacle->m_rad = blenderobject->obstacleRad;
259
void KX_ObstacleSimulation::AddObstaclesForNavMesh(KX_NavMeshObject* navmeshobj)
261
dtStatNavMesh* navmesh = navmeshobj->GetNavMesh();
264
int npoly = navmesh->getPolyCount();
265
for (int pi=0; pi<npoly; pi++)
267
const dtStatPoly* poly = navmesh->getPoly(pi);
269
for (int i = 0, j = (int)poly->nv-1; i < (int)poly->nv; j = i++)
271
if (poly->n[j]) continue;
272
const float* vj = navmesh->getVertex(poly->v[j]);
273
const float* vi = navmesh->getVertex(poly->v[i]);
275
KX_Obstacle* obstacle = CreateObstacle(navmeshobj);
276
obstacle->m_type = KX_OBSTACLE_NAV_MESH;
277
obstacle->m_shape = KX_OBSTACLE_SEGMENT;
278
obstacle->m_pos = MT_Point3(vj[0], vj[2], vj[1]);
279
obstacle->m_pos2 = MT_Point3(vi[0], vi[2], vi[1]);
286
void KX_ObstacleSimulation::DestroyObstacleForObj(KX_GameObject* gameobj)
288
for (size_t i=0; i<m_obstacles.size(); )
290
if (m_obstacles[i]->m_gameObj == gameobj)
292
KX_Obstacle* obstacle = m_obstacles[i];
293
obstacle->m_gameObj->UnregisterObstacle();
294
m_obstacles[i] = m_obstacles.back();
295
m_obstacles.pop_back();
303
void KX_ObstacleSimulation::UpdateObstacles()
305
for (size_t i=0; i<m_obstacles.size(); i++)
307
if (m_obstacles[i]->m_type==KX_OBSTACLE_NAV_MESH || m_obstacles[i]->m_shape==KX_OBSTACLE_SEGMENT)
310
KX_Obstacle* obs = m_obstacles[i];
311
obs->m_pos = obs->m_gameObj->NodeGetWorldPosition();
312
obs->vel[0] = obs->m_gameObj->GetLinearVelocity().x();
313
obs->vel[1] = obs->m_gameObj->GetLinearVelocity().y();
315
// Update velocity history and calculate perceived (average) velocity.
316
vcpy(&obs->hvel[obs->hhead*2], obs->vel);
317
obs->hhead = (obs->hhead+1) % VEL_HIST_SIZE;
319
for (int j = 0; j < VEL_HIST_SIZE; ++j)
320
vadd(obs->pvel, obs->pvel, &obs->hvel[j*2]);
321
vscale(obs->pvel, obs->pvel, 1.0f/VEL_HIST_SIZE);
325
KX_Obstacle* KX_ObstacleSimulation::GetObstacle(KX_GameObject* gameobj)
327
for (size_t i=0; i<m_obstacles.size(); i++)
329
if (m_obstacles[i]->m_gameObj == gameobj)
330
return m_obstacles[i];
336
void KX_ObstacleSimulation::AdjustObstacleVelocity(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj,
337
MT_Vector3& velocity, MT_Scalar maxDeltaSpeed,MT_Scalar maxDeltaAngle)
341
void KX_ObstacleSimulation::DrawObstacles()
343
if (!m_enableVisualization)
345
static const MT_Vector3 bluecolor(0,0,1);
346
static const MT_Vector3 normal(0.0, 0.0, 1.0);
347
static const int SECTORS_NUM = 32;
348
for (size_t i=0; i<m_obstacles.size(); i++)
350
if (m_obstacles[i]->m_shape==KX_OBSTACLE_SEGMENT)
352
MT_Point3 p1 = m_obstacles[i]->m_pos;
353
MT_Point3 p2 = m_obstacles[i]->m_pos2;
354
//apply world transform
355
if (m_obstacles[i]->m_type == KX_OBSTACLE_NAV_MESH)
357
KX_NavMeshObject* navmeshobj = static_cast<KX_NavMeshObject*>(m_obstacles[i]->m_gameObj);
358
p1 = navmeshobj->TransformToWorldCoords(p1);
359
p2 = navmeshobj->TransformToWorldCoords(p2);
362
KX_RasterizerDrawDebugLine(p1, p2, bluecolor);
364
else if (m_obstacles[i]->m_shape==KX_OBSTACLE_CIRCLE)
366
KX_RasterizerDrawDebugCircle(m_obstacles[i]->m_pos, m_obstacles[i]->m_rad, bluecolor,
367
normal, SECTORS_NUM);
372
static MT_Point3 nearestPointToObstacle(MT_Point3& pos ,KX_Obstacle* obstacle)
374
switch (obstacle->m_shape)
376
case KX_OBSTACLE_SEGMENT :
378
MT_Vector3 ab = obstacle->m_pos2 - obstacle->m_pos;
381
MT_Vector3 abdir = ab.normalized();
382
MT_Vector3 v = pos - obstacle->m_pos;
383
MT_Scalar proj = abdir.dot(v);
384
CLAMP(proj, 0, ab.length());
385
MT_Point3 res = obstacle->m_pos + abdir*proj;
389
case KX_OBSTACLE_CIRCLE :
391
return obstacle->m_pos;
395
static bool filterObstacle(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj, KX_Obstacle* otherObst,
398
//filter obstacles by type
399
if ( (otherObst == activeObst) ||
400
(otherObst->m_type==KX_OBSTACLE_NAV_MESH && otherObst->m_gameObj!=activeNavMeshObj) )
403
//filter obstacles by position
404
MT_Point3 p = nearestPointToObstacle(activeObst->m_pos, otherObst);
405
if ( fabs(activeObst->m_pos.z() - p.z()) > levelHeight)
411
///////////*********TOI_rays**********/////////////////
412
KX_ObstacleSimulationTOI::KX_ObstacleSimulationTOI(MT_Scalar levelHeight, bool enableVisualization)
413
: KX_ObstacleSimulation(levelHeight, enableVisualization),
418
m_curVelWeight(1.0f),
420
m_collisionWeight(1.0f)
425
void KX_ObstacleSimulationTOI::AdjustObstacleVelocity(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj,
426
MT_Vector3& velocity, MT_Scalar maxDeltaSpeed, MT_Scalar maxDeltaAngle)
428
int nobs = m_obstacles.size();
429
int obstidx = std::find(m_obstacles.begin(), m_obstacles.end(), activeObst) - m_obstacles.begin();
433
vset(activeObst->dvel, velocity.x(), velocity.y());
436
sampleRVO(activeObst, activeNavMeshObj, maxDeltaAngle);
438
// Fake dynamic constraint.
441
vsub(dv, activeObst->nvel, activeObst->vel);
443
if (ds > maxDeltaSpeed || ds<-maxDeltaSpeed)
444
vscale(dv, dv, fabs(maxDeltaSpeed/ds));
445
vadd(vel, activeObst->vel, dv);
447
velocity.x() = vel[0];
448
velocity.y() = vel[1];
451
///////////*********TOI_rays**********/////////////////
452
static const int AVOID_MAX_STEPS = 128;
455
TOICircle() : n(0), minToi(0), maxToi(1) {}
456
float toi[AVOID_MAX_STEPS]; // Time of impact (seconds)
457
float toie[AVOID_MAX_STEPS]; // Time of exit (seconds)
458
float dir[AVOID_MAX_STEPS]; // Direction (radians)
459
int n; // Number of samples
460
float minToi, maxToi; // Min/max TOI (seconds)
463
KX_ObstacleSimulationTOI_rays::KX_ObstacleSimulationTOI_rays(MT_Scalar levelHeight, bool enableVisualization):
464
KX_ObstacleSimulationTOI(levelHeight, enableVisualization)
471
m_collisionWeight = 100.0f;
475
void KX_ObstacleSimulationTOI_rays::sampleRVO(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj,
476
const float maxDeltaAngle)
478
MT_Vector2 vel(activeObst->dvel[0], activeObst->dvel[1]);
479
float vmax = (float) vel.length();
480
float odir = (float) atan2(vel.y(), vel.x());
482
MT_Vector2 ddir = vel;
485
float bestScore = FLT_MAX;
486
float bestDir = odir;
491
tc.minToi = m_minToi;
492
tc.maxToi = m_maxToi;
494
const int iforw = m_maxSamples/2;
495
const float aoff = (float)iforw / (float)m_maxSamples;
497
size_t nobs = m_obstacles.size();
498
for (int iter = 0; iter < m_maxSamples; ++iter)
500
// Calculate sample velocity
501
const float ndir = ((float)iter/(float)m_maxSamples) - aoff;
502
const float dir = odir+ndir*M_PI*2;
504
svel.x() = cosf(dir) * vmax;
505
svel.y() = sinf(dir) * vmax;
507
// Find min time of impact and exit amongst all obstacles.
508
float tmin = m_maxToi;
510
for (int i = 0; i < nobs; ++i)
512
KX_Obstacle* ob = m_obstacles[i];
513
bool res = filterObstacle(activeObst, activeNavMeshObj, ob, m_levelHeight);
519
if (ob->m_shape == KX_OBSTACLE_CIRCLE)
522
if (vlen(ob->vel) < 0.01f*0.01f)
524
// Stationary, use VO
530
vab = 2*svel - vel - ob->vel;
533
if (!sweepCircleCircle(activeObst->m_pos, activeObst->m_rad,
534
vab, ob->m_pos, ob->m_rad, htmin, htmax))
537
else if (ob->m_shape == KX_OBSTACLE_SEGMENT)
539
MT_Point3 p1 = ob->m_pos;
540
MT_Point3 p2 = ob->m_pos2;
541
//apply world transform
542
if (ob->m_type == KX_OBSTACLE_NAV_MESH)
544
KX_NavMeshObject* navmeshobj = static_cast<KX_NavMeshObject*>(ob->m_gameObj);
545
p1 = navmeshobj->TransformToWorldCoords(p1);
546
p2 = navmeshobj->TransformToWorldCoords(p2);
548
if (!sweepCircleSegment(activeObst->m_pos, activeObst->m_rad, svel,
549
p1, p2, ob->m_rad, htmin, htmax))
555
// The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
559
else if (htmax > 0.0f)
561
// The agent overlaps the obstacle, keep track of first safe exit.
567
// Calculate sample penalties and final score.
568
const float apen = m_velWeight * fabsf(ndir);
569
const float tpen = m_toiWeight * (1.0f/(0.0001f+tmin/m_maxToi));
570
const float cpen = m_collisionWeight * (tmine/m_minToi)*(tmine/m_minToi);
571
const float score = apen + tpen + cpen;
573
// Update best score.
574
if (score < bestScore)
583
tc.toie[iter] = tmine;
586
if (vlen(activeObst->vel) > 0.1)
588
// Constrain max turn rate.
589
float cura = atan2(activeObst->vel[1],activeObst->vel[0]);
590
float da = bestDir - cura;
591
if (da < -M_PI) da += (float)M_PI*2;
592
if (da > M_PI) da -= (float)M_PI*2;
593
if (da < -maxDeltaAngle)
595
bestDir = cura - maxDeltaAngle;
596
bestToi = min(bestToi, interpolateToi(bestDir, tc.dir, tc.toi, tc.n));
598
else if (da > maxDeltaAngle)
600
bestDir = cura + maxDeltaAngle;
601
bestToi = min(bestToi, interpolateToi(bestDir, tc.dir, tc.toi, tc.n));
605
// Adjust speed when time of impact is less than min TOI.
606
if (bestToi < m_minToi)
607
vmax *= bestToi/m_minToi;
609
// New steering velocity.
610
activeObst->nvel[0] = cosf(bestDir) * vmax;
611
activeObst->nvel[1] = sinf(bestDir) * vmax;
614
///////////********* TOI_cells**********/////////////////
616
static void processSamples(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj,
617
KX_Obstacles& obstacles, float levelHeight, const float vmax,
618
const float* spos, const float cs, const int nspos, float* res,
619
float maxToi, float velWeight, float curVelWeight, float sideWeight,
624
const float ivmax = 1.0f / vmax;
626
float adir[2] /*, adist */;
627
vcpy(adir, activeObst->pvel);
628
if (vlen(adir) > 0.01f)
632
float activeObstPos[2];
633
vset(activeObstPos, activeObst->m_pos.x(), activeObst->m_pos.y());
634
/* adist = vdot(adir, activeObstPos); */
636
float minPenalty = FLT_MAX;
638
for (int n = 0; n < nspos; ++n)
641
vcpy(vcand, &spos[n*2]);
643
// Find min time of impact and exit amongst all obstacles.
648
for (int i = 0; i < obstacles.size(); ++i)
650
KX_Obstacle* ob = obstacles[i];
651
bool res = filterObstacle(activeObst, activeNavMeshObj, ob, levelHeight);
656
if (ob->m_shape==KX_OBSTACLE_CIRCLE)
661
vscale(vab, vcand, 2);
662
vsub(vab, vab, activeObst->vel);
663
vsub(vab, vab, ob->vel);
666
// NOTE: dp, and dv are constant over the whole calculation,
667
// they can be precomputed per object.
668
const float* pa = activeObstPos;
670
vset(pb, ob->m_pos.x(), ob->m_pos.y());
672
const float orig[2] = {0,0};
673
float dp[2],dv[2],np[2];
676
vsub(dv,ob->dvel, activeObst->dvel);
678
const float a = triarea(orig, dp,dv);
690
side += clamp(min(vdot(dp,vab)*2,vdot(np,vab)*2), 0.0f, 1.0f);
693
if (!sweepCircleCircle(activeObst->m_pos, activeObst->m_rad, vab, ob->m_pos, ob->m_rad,
697
// Handle overlapping obstacles.
698
if (htmin < 0.0f && htmax > 0.0f)
700
// Avoid more when overlapped.
701
htmin = -htmin * 0.5f;
704
else if (ob->m_shape == KX_OBSTACLE_SEGMENT)
706
MT_Point3 p1 = ob->m_pos;
707
MT_Point3 p2 = ob->m_pos2;
708
//apply world transform
709
if (ob->m_type == KX_OBSTACLE_NAV_MESH)
711
KX_NavMeshObject* navmeshobj = static_cast<KX_NavMeshObject*>(ob->m_gameObj);
712
p1 = navmeshobj->TransformToWorldCoords(p1);
713
p2 = navmeshobj->TransformToWorldCoords(p2);
716
vset(p, p1.x(), p1.y());
717
vset(q, p2.x(), p2.y());
719
// NOTE: the segments are assumed to come from a navmesh which is shrunken by
720
// the agent radius, hence the use of really small radius.
721
// This can be handle more efficiently by using seg-seg test instead.
722
// If the whole segment is to be treated as obstacle, use agent->rad instead of 0.01f!
723
const float r = 0.01f; // agent->rad
724
if (distPtSegSqr(activeObstPos, p, q) < sqr(r+ob->m_rad))
726
float sdir[2], snorm[2];
730
// If the velocity is pointing towards the segment, no collision.
731
if (vdot(snorm, vcand) < 0.0f)
733
// Else immediate collision.
739
if (!sweepCircleSegment(activeObstPos, r, vcand, p, q, ob->m_rad, htmin, htmax))
743
// Avoid less when facing walls.
749
// The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
755
// Normalize side bias, to prevent it dominating too much.
759
const float vpen = velWeight * (vdist(vcand, activeObst->dvel) * ivmax);
760
const float vcpen = curVelWeight * (vdist(vcand, activeObst->vel) * ivmax);
761
const float spen = sideWeight * side;
762
const float tpen = toiWeight * (1.0f/(0.1f+tmin/maxToi));
764
const float penalty = vpen + vcpen + spen + tpen;
766
if (penalty < minPenalty)
768
minPenalty = penalty;
774
void KX_ObstacleSimulationTOI_cells::sampleRVO(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj,
775
const float maxDeltaAngle)
777
vset(activeObst->nvel, 0.f, 0.f);
778
float vmax = vlen(activeObst->dvel);
780
float* spos = new float[2*m_maxSamples];
785
const float cvx = activeObst->dvel[0]*m_bias;
786
const float cvy = activeObst->dvel[1]*m_bias;
787
float vmax = vlen(activeObst->dvel);
788
const float vrange = vmax*(1-m_bias);
789
const float cs = 1.0f / (float)m_sampleRadius*vrange;
791
for (int y = -m_sampleRadius; y <= m_sampleRadius; ++y)
793
for (int x = -m_sampleRadius; x <= m_sampleRadius; ++x)
795
if (nspos < m_maxSamples)
797
const float vx = cvx + (float)(x+0.5f)*cs;
798
const float vy = cvy + (float)(y+0.5f)*cs;
799
if (vx*vx+vy*vy > sqr(vmax+cs/2)) continue;
800
spos[nspos*2+0] = vx;
801
spos[nspos*2+1] = vy;
806
processSamples(activeObst, activeNavMeshObj, m_obstacles, m_levelHeight, vmax, spos, cs/2,
807
nspos, activeObst->nvel, m_maxToi, m_velWeight, m_curVelWeight, m_collisionWeight, m_toiWeight);
814
// First sample location.
816
res[0] = activeObst->dvel[0]*m_bias;
817
res[1] = activeObst->dvel[1]*m_bias;
818
cs = vmax*(2-m_bias*2) / (float)(rad-1);
820
for (int k = 0; k < 5; ++k)
822
const float half = (rad-1)*cs*0.5f;
825
for (int y = 0; y < rad; ++y)
827
for (int x = 0; x < rad; ++x)
829
const float vx = res[0] + x*cs - half;
830
const float vy = res[1] + y*cs - half;
831
if (vx*vx+vy*vy > sqr(vmax+cs/2)) continue;
832
spos[nspos*2+0] = vx;
833
spos[nspos*2+1] = vy;
838
processSamples(activeObst, activeNavMeshObj, m_obstacles, m_levelHeight, vmax, spos, cs/2,
839
nspos, res, m_maxToi, m_velWeight, m_curVelWeight, m_collisionWeight, m_toiWeight);
843
vcpy(activeObst->nvel, res);
847
KX_ObstacleSimulationTOI_cells::KX_ObstacleSimulationTOI_cells(MT_Scalar levelHeight, bool enableVisualization)
848
: KX_ObstacleSimulationTOI(levelHeight, enableVisualization)
853
m_maxSamples = (m_sampleRadius*2+1)*(m_sampleRadius*2+1) + 100;
856
m_curVelWeight = 0.75f;
858
m_collisionWeight = 0.75f; //side_weight