Blender V2.61 - r43446

KX_ObstacleSimulation.cpp

Go to the documentation of this file.
00001 /*
00002  * Simulation for obstacle avoidance behavior
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * Contributor(s): none yet.
00021  *
00022  * ***** END GPL LICENSE BLOCK *****
00023  */
00024 
00025 #include "KX_ObstacleSimulation.h"
00026 #include "KX_NavMeshObject.h"
00027 #include "KX_PythonInit.h"
00028 #include "DNA_object_types.h"
00029 #include "BLI_math.h"
00030 
00031 namespace
00032 {
00033     inline float perp(const MT_Vector2& a, const MT_Vector2& b) { return a.x()*b.y() - a.y()*b.x(); }
00034 
00035     inline float sqr(float x) { return x*x; }
00036     inline float lerp(float a, float b, float t) { return a + (b-a)*t; }
00037     inline float clamp(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); }
00038 
00039     inline float vdistsqr(const float* a, const float* b) { return sqr(b[0]-a[0]) + sqr(b[1]-a[1]); }
00040     inline float vdist(const float* a, const float* b) { return sqrtf(vdistsqr(a,b)); }
00041     inline void vcpy(float* a, const float* b) { a[0]=b[0]; a[1]=b[1]; }
00042     inline float vdot(const float* a, const float* b) { return a[0]*b[0] + a[1]*b[1]; }
00043     inline float vperp(const float* a, const float* b) { return a[0]*b[1] - a[1]*b[0]; }
00044     inline void vsub(float* v, const float* a, const float* b) { v[0] = a[0]-b[0]; v[1] = a[1]-b[1]; }
00045     inline void vadd(float* v, const float* a, const float* b) { v[0] = a[0]+b[0]; v[1] = a[1]+b[1]; }
00046     inline void vscale(float* v, const float* a, const float s) { v[0] = a[0]*s; v[1] = a[1]*s; }
00047     inline void vset(float* v, float x, float y) { v[0]=x; v[1]=y; }
00048     inline float vlensqr(const float* v) { return vdot(v,v); }
00049     inline float vlen(const float* v) { return sqrtf(vlensqr(v)); }
00050     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); }
00051     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; }
00052     inline void vnorm(float* v)
00053     {
00054         float d = vlen(v);
00055         if (d > 0.0001f)
00056         {
00057             d = 1.0f/d;
00058             v[0] *= d;
00059             v[1] *= d;
00060         }
00061     }
00062 }
00063 inline float triarea(const float* a, const float* b, const float* c)
00064 {
00065     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]);
00066 }
00067 
00068 static void closestPtPtSeg(const float* pt,
00069                     const float* sp, const float* sq,
00070                     float& t)
00071 {
00072     float dir[2],diff[3];
00073     vsub(dir,sq,sp);
00074     vsub(diff,pt,sp);
00075     t = vdot(diff,dir);
00076     if (t <= 0.0f) { t = 0; return; }
00077     float d = vdot(dir,dir);
00078     if (t >= d) { t = 1; return; }
00079     t /= d;
00080 }
00081 
00082 static float distPtSegSqr(const float* pt, const float* sp, const float* sq)
00083 {
00084     float t;
00085     closestPtPtSeg(pt, sp,sq, t);
00086     float np[2];
00087     vlerp(np, sp,sq, t);
00088     return vdistsqr(pt,np);
00089 }
00090 
00091 static int sweepCircleCircle(const MT_Vector3& pos0, const MT_Scalar r0, const MT_Vector2& v,
00092                       const MT_Vector3& pos1, const MT_Scalar r1,
00093                       float& tmin, float& tmax)
00094 {
00095     static const float EPS = 0.0001f;
00096     MT_Vector2 c0(pos0.x(), pos0.y());
00097     MT_Vector2 c1(pos1.x(), pos1.y());
00098     MT_Vector2 s = c1 - c0;
00099     MT_Scalar  r = r0+r1;
00100     float c = s.length2() - r*r;
00101     float a = v.length2();
00102     if (a < EPS) return 0;  // not moving
00103 
00104     // Overlap, calc time to exit.
00105     float b = MT_dot(v,s);
00106     float d = b*b - a*c;
00107     if (d < 0.0f) return 0; // no intersection.
00108     tmin = (b - sqrtf(d)) / a;
00109     tmax = (b + sqrtf(d)) / a;
00110     return 1;
00111 }
00112 
00113 static int sweepCircleSegment(const MT_Vector3& pos0, const MT_Scalar r0, const MT_Vector2& v,
00114                        const MT_Vector3& pa, const MT_Vector3& pb, const MT_Scalar sr,
00115                        float& tmin, float &tmax)
00116 {
00117     // equation parameters
00118     MT_Vector2 c0(pos0.x(), pos0.y());
00119     MT_Vector2 sa(pa.x(), pa.y());
00120     MT_Vector2 sb(pb.x(), pb.y());
00121     MT_Vector2 L = sb-sa;
00122     MT_Vector2 H = c0-sa;
00123     MT_Scalar radius = r0+sr;
00124     float l2 = L.length2();
00125     float r2 = radius * radius;
00126     float dl = perp(v, L);
00127     float hl = perp(H, L);
00128     float a = dl * dl;
00129     float b = 2.0f * hl * dl;
00130     float c = hl * hl - (r2 * l2);
00131     float d = (b*b) - (4.0f * a * c);
00132 
00133     // infinite line missed by infinite ray.
00134     if (d < 0.0f)
00135         return 0;
00136 
00137     d = sqrtf(d);
00138     tmin = (-b - d) / (2.0f * a);
00139     tmax = (-b + d) / (2.0f * a);
00140 
00141     // line missed by ray range.
00142     /*  if (tmax < 0.0f || tmin > 1.0f)
00143     return 0;*/
00144 
00145     // find what part of the ray was collided.
00146     MT_Vector2 Pedge;
00147     Pedge = c0+v*tmin;
00148     H = Pedge - sa;
00149     float e0 = MT_dot(H, L) / l2;
00150     Pedge = c0 + v*tmax;
00151     H = Pedge - sa;
00152     float e1 = MT_dot(H, L) / l2;
00153 
00154     if (e0 < 0.0f || e1 < 0.0f)
00155     {
00156         float ctmin, ctmax;
00157         if (sweepCircleCircle(pos0, r0, v, pa, sr, ctmin, ctmax))
00158         {
00159             if (e0 < 0.0f && ctmin > tmin)
00160                 tmin = ctmin;
00161             if (e1 < 0.0f && ctmax < tmax)
00162                 tmax = ctmax;
00163         }
00164         else
00165         {
00166             return 0;
00167         }
00168     }
00169 
00170     if (e0 > 1.0f || e1 > 1.0f)
00171     {
00172         float ctmin, ctmax;
00173         if (sweepCircleCircle(pos0, r0, v, pb, sr, ctmin, ctmax))
00174         {
00175             if (e0 > 1.0f && ctmin > tmin)
00176                 tmin = ctmin;
00177             if (e1 > 1.0f && ctmax < tmax)
00178                 tmax = ctmax;
00179         }
00180         else
00181         {
00182             return 0;
00183         }
00184     }
00185 
00186     return 1;
00187 }
00188 
00189 static bool inBetweenAngle(float a, float amin, float amax, float& t)
00190 {
00191     if (amax < amin) amax += (float)M_PI*2;
00192     if (a < amin-(float)M_PI) a += (float)M_PI*2;
00193     if (a > amin+(float)M_PI) a -= (float)M_PI*2;
00194     if (a >= amin && a < amax)
00195     {
00196         t = (a-amin) / (amax-amin);
00197         return true;
00198     }
00199     return false;
00200 }
00201 
00202 static float interpolateToi(float a, const float* dir, const float* toi, const int ntoi)
00203 {
00204     for (int i = 0; i < ntoi; ++i)
00205     {
00206         int next = (i+1) % ntoi;
00207         float t;
00208         if (inBetweenAngle(a, dir[i], dir[next], t))
00209         {
00210             return lerp(toi[i], toi[next], t);
00211         }
00212     }
00213     return 0;
00214 }
00215 
00216 KX_ObstacleSimulation::KX_ObstacleSimulation(MT_Scalar levelHeight, bool enableVisualization)
00217 :   m_levelHeight(levelHeight)
00218 ,   m_enableVisualization(enableVisualization)
00219 {
00220 
00221 }
00222 
00223 KX_ObstacleSimulation::~KX_ObstacleSimulation()
00224 {
00225     for (size_t i=0; i<m_obstacles.size(); i++)
00226     {
00227         KX_Obstacle* obs = m_obstacles[i];
00228         delete obs;
00229     }
00230     m_obstacles.clear();
00231 }
00232 KX_Obstacle* KX_ObstacleSimulation::CreateObstacle(KX_GameObject* gameobj)
00233 {
00234     KX_Obstacle* obstacle = new KX_Obstacle();
00235     obstacle->m_gameObj = gameobj;
00236 
00237     vset(obstacle->vel, 0,0);
00238     vset(obstacle->pvel, 0,0);
00239     vset(obstacle->dvel, 0,0);
00240     vset(obstacle->nvel, 0,0);
00241     for (int i = 0; i < VEL_HIST_SIZE; ++i)
00242         vset(&obstacle->hvel[i*2], 0,0);
00243     obstacle->hhead = 0;
00244 
00245     gameobj->RegisterObstacle(this);
00246     m_obstacles.push_back(obstacle);
00247     return obstacle;
00248 }
00249 
00250 void KX_ObstacleSimulation::AddObstacleForObj(KX_GameObject* gameobj)
00251 {
00252     KX_Obstacle* obstacle = CreateObstacle(gameobj);
00253     struct Object* blenderobject = gameobj->GetBlenderObject();
00254     obstacle->m_type = KX_OBSTACLE_OBJ;
00255     obstacle->m_shape = KX_OBSTACLE_CIRCLE;
00256     obstacle->m_rad = blenderobject->obstacleRad;
00257 }
00258 
00259 void KX_ObstacleSimulation::AddObstaclesForNavMesh(KX_NavMeshObject* navmeshobj)
00260 {   
00261     dtStatNavMesh* navmesh = navmeshobj->GetNavMesh();
00262     if (navmesh)
00263     {
00264         int npoly = navmesh->getPolyCount();
00265         for (int pi=0; pi<npoly; pi++)
00266         {
00267             const dtStatPoly* poly = navmesh->getPoly(pi);
00268 
00269             for (int i = 0, j = (int)poly->nv-1; i < (int)poly->nv; j = i++)
00270             {   
00271                 if (poly->n[j]) continue;
00272                 const float* vj = navmesh->getVertex(poly->v[j]);
00273                 const float* vi = navmesh->getVertex(poly->v[i]);
00274         
00275                 KX_Obstacle* obstacle = CreateObstacle(navmeshobj);
00276                 obstacle->m_type = KX_OBSTACLE_NAV_MESH;
00277                 obstacle->m_shape = KX_OBSTACLE_SEGMENT;
00278                 obstacle->m_pos = MT_Point3(vj[0], vj[2], vj[1]);
00279                 obstacle->m_pos2 = MT_Point3(vi[0], vi[2], vi[1]);
00280                 obstacle->m_rad = 0;
00281             }
00282         }
00283     }
00284 }
00285 
00286 void KX_ObstacleSimulation::DestroyObstacleForObj(KX_GameObject* gameobj)
00287 {
00288     for (size_t i=0; i<m_obstacles.size(); )
00289     {
00290         if (m_obstacles[i]->m_gameObj == gameobj)
00291         {
00292             KX_Obstacle* obstacle = m_obstacles[i];
00293             obstacle->m_gameObj->UnregisterObstacle();
00294             m_obstacles[i] = m_obstacles.back();
00295             m_obstacles.pop_back();
00296             delete obstacle;
00297         }
00298         else
00299             i++;
00300     }
00301 }
00302 
00303 void KX_ObstacleSimulation::UpdateObstacles()
00304 {
00305     for (size_t i=0; i<m_obstacles.size(); i++)
00306     {
00307         if (m_obstacles[i]->m_type==KX_OBSTACLE_NAV_MESH || m_obstacles[i]->m_shape==KX_OBSTACLE_SEGMENT)
00308             continue;
00309 
00310         KX_Obstacle* obs = m_obstacles[i];
00311         obs->m_pos = obs->m_gameObj->NodeGetWorldPosition();
00312         obs->vel[0] = obs->m_gameObj->GetLinearVelocity().x();
00313         obs->vel[1] = obs->m_gameObj->GetLinearVelocity().y();
00314 
00315         // Update velocity history and calculate perceived (average) velocity.
00316         vcpy(&obs->hvel[obs->hhead*2], obs->vel);
00317         obs->hhead = (obs->hhead+1) % VEL_HIST_SIZE;
00318         vset(obs->pvel,0,0);
00319         for (int j = 0; j < VEL_HIST_SIZE; ++j)
00320             vadd(obs->pvel, obs->pvel, &obs->hvel[j*2]);
00321         vscale(obs->pvel, obs->pvel, 1.0f/VEL_HIST_SIZE);
00322     }
00323 }
00324 
00325 KX_Obstacle* KX_ObstacleSimulation::GetObstacle(KX_GameObject* gameobj)
00326 {
00327     for (size_t i=0; i<m_obstacles.size(); i++)
00328     {
00329         if (m_obstacles[i]->m_gameObj == gameobj)
00330             return m_obstacles[i];
00331     }
00332 
00333     return NULL;
00334 }
00335 
00336 void KX_ObstacleSimulation::AdjustObstacleVelocity(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj, 
00337                                         MT_Vector3& velocity, MT_Scalar maxDeltaSpeed,MT_Scalar maxDeltaAngle)
00338 {
00339 }
00340 
00341 void KX_ObstacleSimulation::DrawObstacles()
00342 {
00343     if (!m_enableVisualization)
00344         return;
00345     static const MT_Vector3 bluecolor(0,0,1);
00346     static const MT_Vector3 normal(0.,0.,1.);
00347     static const int SECTORS_NUM = 32;
00348     for (size_t i=0; i<m_obstacles.size(); i++)
00349     {
00350         if (m_obstacles[i]->m_shape==KX_OBSTACLE_SEGMENT)
00351         {
00352             MT_Point3 p1 = m_obstacles[i]->m_pos;
00353             MT_Point3 p2 = m_obstacles[i]->m_pos2;
00354             //apply world transform
00355             if (m_obstacles[i]->m_type == KX_OBSTACLE_NAV_MESH)
00356             {
00357                 KX_NavMeshObject* navmeshobj = static_cast<KX_NavMeshObject*>(m_obstacles[i]->m_gameObj);
00358                 p1 = navmeshobj->TransformToWorldCoords(p1);
00359                 p2 = navmeshobj->TransformToWorldCoords(p2);
00360             }
00361 
00362             KX_RasterizerDrawDebugLine(p1, p2, bluecolor);
00363         }
00364         else if (m_obstacles[i]->m_shape==KX_OBSTACLE_CIRCLE)
00365         {
00366             KX_RasterizerDrawDebugCircle(m_obstacles[i]->m_pos, m_obstacles[i]->m_rad, bluecolor,
00367                                         normal, SECTORS_NUM);
00368         }
00369     }   
00370 }
00371 
00372 static MT_Point3 nearestPointToObstacle(MT_Point3& pos ,KX_Obstacle* obstacle)
00373 {
00374     switch (obstacle->m_shape)
00375     {
00376     case KX_OBSTACLE_SEGMENT :
00377     {
00378         MT_Vector3 ab = obstacle->m_pos2 - obstacle->m_pos;
00379         if (!ab.fuzzyZero())
00380         {
00381             MT_Vector3 abdir = ab.normalized();
00382             MT_Vector3  v = pos - obstacle->m_pos;
00383             MT_Scalar proj = abdir.dot(v);
00384             CLAMP(proj, 0, ab.length());
00385             MT_Point3 res = obstacle->m_pos + abdir*proj;
00386             return res;
00387         }       
00388     }
00389     case KX_OBSTACLE_CIRCLE :
00390     default:
00391         return obstacle->m_pos;
00392     }
00393 }
00394 
00395 static bool filterObstacle(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj, KX_Obstacle* otherObst,
00396                             float levelHeight)
00397 {
00398     //filter obstacles by type
00399     if ( (otherObst == activeObst) ||
00400         (otherObst->m_type==KX_OBSTACLE_NAV_MESH && otherObst->m_gameObj!=activeNavMeshObj) )
00401         return false;
00402 
00403     //filter obstacles by position
00404     MT_Point3 p = nearestPointToObstacle(activeObst->m_pos, otherObst);
00405     if ( fabs(activeObst->m_pos.z() - p.z()) > levelHeight)
00406         return false;
00407 
00408     return true;
00409 }
00410 
00412 KX_ObstacleSimulationTOI::KX_ObstacleSimulationTOI(MT_Scalar levelHeight, bool enableVisualization)
00413 :   KX_ObstacleSimulation(levelHeight, enableVisualization),
00414     m_maxSamples(32),
00415     m_minToi(0.0f),
00416     m_maxToi(0.0f),
00417     m_velWeight(1.0f),
00418     m_curVelWeight(1.0f),
00419     m_toiWeight(1.0f),
00420     m_collisionWeight(1.0f)
00421 {
00422 }
00423 
00424 
00425 void KX_ObstacleSimulationTOI::AdjustObstacleVelocity(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj, 
00426                                                            MT_Vector3& velocity, MT_Scalar maxDeltaSpeed, MT_Scalar maxDeltaAngle)
00427 {
00428     int nobs = m_obstacles.size();
00429     int obstidx = std::find(m_obstacles.begin(), m_obstacles.end(), activeObst) - m_obstacles.begin();
00430     if (obstidx == nobs)
00431         return;
00432 
00433     vset(activeObst->dvel, velocity.x(), velocity.y());
00434 
00435     //apply RVO
00436     sampleRVO(activeObst, activeNavMeshObj, maxDeltaAngle);
00437 
00438     // Fake dynamic constraint.
00439     float dv[2];
00440     float vel[2];
00441     vsub(dv, activeObst->nvel, activeObst->vel);
00442     float ds = vlen(dv);
00443     if (ds > maxDeltaSpeed || ds<-maxDeltaSpeed)
00444         vscale(dv, dv, fabs(maxDeltaSpeed/ds));
00445     vadd(vel, activeObst->vel, dv);
00446 
00447     velocity.x() = vel[0];
00448     velocity.y() = vel[1];  
00449 }
00450 
00452 static const int AVOID_MAX_STEPS = 128;
00453 struct TOICircle
00454 {
00455     TOICircle() : n(0), minToi(0), maxToi(1) {}
00456     float   toi[AVOID_MAX_STEPS];   // Time of impact (seconds)
00457     float   toie[AVOID_MAX_STEPS];  // Time of exit (seconds)
00458     float   dir[AVOID_MAX_STEPS];   // Direction (radians)
00459     int     n;                      // Number of samples
00460     float   minToi, maxToi;         // Min/max TOI (seconds)
00461 };
00462 
00463 KX_ObstacleSimulationTOI_rays::KX_ObstacleSimulationTOI_rays(MT_Scalar levelHeight, bool enableVisualization):
00464     KX_ObstacleSimulationTOI(levelHeight, enableVisualization)
00465 {
00466     m_maxSamples = 32;
00467     m_minToi = 0.5f;
00468     m_maxToi = 1.2f;
00469     m_velWeight = 4.0f;
00470     m_toiWeight = 1.0f;
00471     m_collisionWeight = 100.0f;
00472 }
00473 
00474 
00475 void KX_ObstacleSimulationTOI_rays::sampleRVO(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj, 
00476                                         const float maxDeltaAngle)
00477 {
00478     MT_Vector2 vel(activeObst->dvel[0], activeObst->dvel[1]);
00479     float vmax = (float) vel.length();
00480     float odir = (float) atan2(vel.y(), vel.x());
00481 
00482     MT_Vector2 ddir = vel;
00483     ddir.normalize();
00484 
00485     float bestScore = FLT_MAX;
00486     float bestDir = odir;
00487     float bestToi = 0;
00488 
00489     TOICircle tc;
00490     tc.n = m_maxSamples;
00491     tc.minToi = m_minToi;
00492     tc.maxToi = m_maxToi;
00493 
00494     const int iforw = m_maxSamples/2;
00495     const float aoff = (float)iforw / (float)m_maxSamples;
00496 
00497     size_t nobs = m_obstacles.size();
00498     for (int iter = 0; iter < m_maxSamples; ++iter)
00499     {
00500         // Calculate sample velocity
00501         const float ndir = ((float)iter/(float)m_maxSamples) - aoff;
00502         const float dir = odir+ndir*M_PI*2;
00503         MT_Vector2 svel;
00504         svel.x() = cosf(dir) * vmax;
00505         svel.y() = sinf(dir) * vmax;
00506 
00507         // Find min time of impact and exit amongst all obstacles.
00508         float tmin = m_maxToi;
00509         float tmine = 0;
00510         for (int i = 0; i < nobs; ++i)
00511         {
00512             KX_Obstacle* ob = m_obstacles[i];
00513             bool res = filterObstacle(activeObst, activeNavMeshObj, ob, m_levelHeight);
00514             if (!res)
00515                 continue;
00516 
00517             float htmin,htmax;
00518 
00519             if (ob->m_shape == KX_OBSTACLE_CIRCLE)
00520             {
00521                 MT_Vector2 vab;
00522                 if (vlen(ob->vel) < 0.01f*0.01f)
00523                 {
00524                     // Stationary, use VO
00525                     vab = svel;
00526                 }
00527                 else
00528                 {
00529                     // Moving, use RVO
00530                     vab = 2*svel - vel - ob->vel;
00531                 }
00532 
00533                 if (!sweepCircleCircle(activeObst->m_pos, activeObst->m_rad, 
00534                     vab, ob->m_pos, ob->m_rad, htmin, htmax))
00535                     continue;
00536             }
00537             else if (ob->m_shape == KX_OBSTACLE_SEGMENT)
00538             {
00539                 MT_Point3 p1 = ob->m_pos;
00540                 MT_Point3 p2 = ob->m_pos2;
00541                 //apply world transform
00542                 if (ob->m_type == KX_OBSTACLE_NAV_MESH)
00543                 {
00544                     KX_NavMeshObject* navmeshobj = static_cast<KX_NavMeshObject*>(ob->m_gameObj);
00545                     p1 = navmeshobj->TransformToWorldCoords(p1);
00546                     p2 = navmeshobj->TransformToWorldCoords(p2);
00547                 }
00548                 if (!sweepCircleSegment(activeObst->m_pos, activeObst->m_rad, svel, 
00549                     p1, p2, ob->m_rad, htmin, htmax))
00550                     continue;
00551             }
00552 
00553             if (htmin > 0.0f)
00554             {
00555                 // The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
00556                 if (htmin < tmin)
00557                     tmin = htmin;
00558             }
00559             else if (htmax > 0.0f)
00560             {
00561                 // The agent overlaps the obstacle, keep track of first safe exit.
00562                 if (htmax > tmine)
00563                     tmine = htmax;
00564             }
00565         }
00566 
00567         // Calculate sample penalties and final score.
00568         const float apen = m_velWeight * fabsf(ndir);
00569         const float tpen = m_toiWeight * (1.0f/(0.0001f+tmin/m_maxToi));
00570         const float cpen = m_collisionWeight * (tmine/m_minToi)*(tmine/m_minToi);
00571         const float score = apen + tpen + cpen;
00572 
00573         // Update best score.
00574         if (score < bestScore)
00575         {
00576             bestDir = dir;
00577             bestToi = tmin;
00578             bestScore = score;
00579         }
00580 
00581         tc.dir[iter] = dir;
00582         tc.toi[iter] = tmin;
00583         tc.toie[iter] = tmine;
00584     }
00585 
00586     if (vlen(activeObst->vel) > 0.1)
00587     {
00588         // Constrain max turn rate.
00589         float cura = atan2(activeObst->vel[1],activeObst->vel[0]);
00590         float da = bestDir - cura;
00591         if (da < -M_PI) da += (float)M_PI*2;
00592         if (da > M_PI) da -= (float)M_PI*2;
00593         if (da < -maxDeltaAngle)
00594         {
00595             bestDir = cura - maxDeltaAngle;
00596             bestToi = min(bestToi, interpolateToi(bestDir, tc.dir, tc.toi, tc.n));
00597         }
00598         else if (da > maxDeltaAngle)
00599         {
00600             bestDir = cura + maxDeltaAngle;
00601             bestToi = min(bestToi, interpolateToi(bestDir, tc.dir, tc.toi, tc.n));
00602         }
00603     }
00604 
00605     // Adjust speed when time of impact is less than min TOI.
00606     if (bestToi < m_minToi)
00607         vmax *= bestToi/m_minToi;
00608 
00609     // New steering velocity.
00610     activeObst->nvel[0] = cosf(bestDir) * vmax;
00611     activeObst->nvel[1] = sinf(bestDir) * vmax;
00612 }
00613 
00615 
00616 static void processSamples(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj, 
00617                            KX_Obstacles& obstacles,  float levelHeight, const float vmax,
00618                            const float* spos, const float cs, const int nspos, float* res,                         
00619                            float maxToi, float velWeight, float curVelWeight, float sideWeight,
00620                            float toiWeight)
00621 {
00622     vset(res, 0,0);
00623 
00624     const float ivmax = 1.0f / vmax;
00625 
00626     float adir[2] /*, adist */;
00627     vcpy(adir, activeObst->pvel);
00628     if (vlen(adir) > 0.01f)
00629         vnorm(adir);
00630     else
00631         vset(adir,0,0);
00632     float activeObstPos[2];
00633     vset(activeObstPos, activeObst->m_pos.x(), activeObst->m_pos.y()); 
00634     /* adist = vdot(adir, activeObstPos); */
00635 
00636     float minPenalty = FLT_MAX;
00637 
00638     for (int n = 0; n < nspos; ++n)
00639     {
00640         float vcand[2];
00641         vcpy(vcand, &spos[n*2]);        
00642 
00643         // Find min time of impact and exit amongst all obstacles.
00644         float tmin = maxToi;
00645         float side = 0;
00646         int nside = 0;
00647 
00648         for (int i = 0; i < obstacles.size(); ++i)
00649         {
00650             KX_Obstacle* ob = obstacles[i];
00651             bool res = filterObstacle(activeObst, activeNavMeshObj, ob, levelHeight);
00652             if (!res)
00653                 continue;
00654             float htmin, htmax;
00655 
00656             if (ob->m_shape==KX_OBSTACLE_CIRCLE)
00657             {
00658                 float vab[2];
00659 
00660                 // Moving, use RVO
00661                 vscale(vab, vcand, 2);
00662                 vsub(vab, vab, activeObst->vel);
00663                 vsub(vab, vab, ob->vel);
00664 
00665                 // Side
00666                 // NOTE: dp, and dv are constant over the whole calculation,
00667                 // they can be precomputed per object. 
00668                 const float* pa = activeObstPos;
00669                 float pb[2];
00670                 vset(pb, ob->m_pos.x(), ob->m_pos.y());
00671 
00672                 const float orig[2] = {0,0};
00673                 float dp[2],dv[2],np[2];
00674                 vsub(dp,pb,pa);
00675                 vnorm(dp);
00676                 vsub(dv,ob->dvel, activeObst->dvel);
00677 
00678                 const float a = triarea(orig, dp,dv);
00679                 if (a < 0.01f)
00680                 {
00681                     np[0] = -dp[1];
00682                     np[1] = dp[0];
00683                 }
00684                 else
00685                 {
00686                     np[0] = dp[1];
00687                     np[1] = -dp[0];
00688                 }
00689 
00690                 side += clamp(min(vdot(dp,vab)*2,vdot(np,vab)*2), 0.0f, 1.0f);
00691                 nside++;
00692 
00693                 if (!sweepCircleCircle(activeObst->m_pos, activeObst->m_rad, vab, ob->m_pos, ob->m_rad, 
00694                     htmin, htmax))
00695                     continue;
00696 
00697                 // Handle overlapping obstacles.
00698                 if (htmin < 0.0f && htmax > 0.0f)
00699                 {
00700                     // Avoid more when overlapped.
00701                     htmin = -htmin * 0.5f;
00702                 }
00703             }
00704             else if (ob->m_shape == KX_OBSTACLE_SEGMENT)
00705             {
00706                 MT_Point3 p1 = ob->m_pos;
00707                 MT_Point3 p2 = ob->m_pos2;
00708                 //apply world transform
00709                 if (ob->m_type == KX_OBSTACLE_NAV_MESH)
00710                 {
00711                     KX_NavMeshObject* navmeshobj = static_cast<KX_NavMeshObject*>(ob->m_gameObj);
00712                     p1 = navmeshobj->TransformToWorldCoords(p1);
00713                     p2 = navmeshobj->TransformToWorldCoords(p2);
00714                 }
00715                 float p[2], q[2];
00716                 vset(p, p1.x(), p1.y());
00717                 vset(q, p2.x(), p2.y());
00718 
00719                 // NOTE: the segments are assumed to come from a navmesh which is shrunken by
00720                 // the agent radius, hence the use of really small radius.
00721                 // This can be handle more efficiently by using seg-seg test instead.
00722                 // If the whole segment is to be treated as obstacle, use agent->rad instead of 0.01f!
00723                 const float r = 0.01f; // agent->rad
00724                 if (distPtSegSqr(activeObstPos, p, q) < sqr(r+ob->m_rad))
00725                 {
00726                     float sdir[2], snorm[2];
00727                     vsub(sdir, q, p);
00728                     snorm[0] = sdir[1];
00729                     snorm[1] = -sdir[0];
00730                     // If the velocity is pointing towards the segment, no collision.
00731                     if (vdot(snorm, vcand) < 0.0f)
00732                         continue;
00733                     // Else immediate collision.
00734                     htmin = 0.0f;
00735                     htmax = 10.0f;
00736                 }
00737                 else
00738                 {
00739                     if (!sweepCircleSegment(activeObstPos, r, vcand, p, q, ob->m_rad, htmin, htmax))
00740                         continue;
00741                 }
00742 
00743                 // Avoid less when facing walls.
00744                 htmin *= 2.0f;
00745             }
00746 
00747             if (htmin >= 0.0f)
00748             {
00749                 // The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
00750                 if (htmin < tmin)
00751                     tmin = htmin;
00752             }
00753         }
00754 
00755         // Normalize side bias, to prevent it dominating too much.
00756         if (nside)
00757             side /= nside;
00758 
00759         const float vpen = velWeight * (vdist(vcand, activeObst->dvel) * ivmax);
00760         const float vcpen = curVelWeight * (vdist(vcand, activeObst->vel) * ivmax);
00761         const float spen = sideWeight * side;
00762         const float tpen = toiWeight * (1.0f/(0.1f+tmin/maxToi));
00763 
00764         const float penalty = vpen + vcpen + spen + tpen;
00765 
00766         if (penalty < minPenalty)
00767         {
00768             minPenalty = penalty;
00769             vcpy(res, vcand);
00770         }
00771     }
00772 }
00773 
00774 void KX_ObstacleSimulationTOI_cells::sampleRVO(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj, 
00775                        const float maxDeltaAngle)
00776 {
00777     vset(activeObst->nvel, 0.f, 0.f);
00778     float vmax = vlen(activeObst->dvel);
00779 
00780     float* spos = new float[2*m_maxSamples];
00781     int nspos = 0;
00782 
00783     if (!m_adaptive)
00784     {
00785         const float cvx = activeObst->dvel[0]*m_bias;
00786         const float cvy = activeObst->dvel[1]*m_bias;
00787         float vmax = vlen(activeObst->dvel);
00788         const float vrange = vmax*(1-m_bias);
00789         const float cs = 1.0f / (float)m_sampleRadius*vrange;
00790 
00791         for (int y = -m_sampleRadius; y <= m_sampleRadius; ++y)
00792         {
00793             for (int x = -m_sampleRadius; x <= m_sampleRadius; ++x)
00794             {
00795                 if (nspos < m_maxSamples)
00796                 {
00797                     const float vx = cvx + (float)(x+0.5f)*cs;
00798                     const float vy = cvy + (float)(y+0.5f)*cs;
00799                     if (vx*vx+vy*vy > sqr(vmax+cs/2)) continue;
00800                     spos[nspos*2+0] = vx;
00801                     spos[nspos*2+1] = vy;
00802                     nspos++;
00803                 }
00804             }
00805         }
00806         processSamples(activeObst, activeNavMeshObj, m_obstacles, m_levelHeight, vmax, spos, cs/2, 
00807             nspos,  activeObst->nvel, m_maxToi, m_velWeight, m_curVelWeight, m_collisionWeight, m_toiWeight);
00808     }
00809     else
00810     {
00811         int rad;
00812         float res[2];
00813         float cs;
00814         // First sample location.
00815         rad = 4;
00816         res[0] = activeObst->dvel[0]*m_bias;
00817         res[1] = activeObst->dvel[1]*m_bias;
00818         cs = vmax*(2-m_bias*2) / (float)(rad-1);
00819 
00820         for (int k = 0; k < 5; ++k)
00821         {
00822             const float half = (rad-1)*cs*0.5f;
00823 
00824             nspos = 0;
00825             for (int y = 0; y < rad; ++y)
00826             {
00827                 for (int x = 0; x < rad; ++x)
00828                 {
00829                     const float vx = res[0] + x*cs - half;
00830                     const float vy = res[1] + y*cs - half;
00831                     if (vx*vx+vy*vy > sqr(vmax+cs/2)) continue;
00832                     spos[nspos*2+0] = vx;
00833                     spos[nspos*2+1] = vy;
00834                     nspos++;
00835                 }
00836             }
00837 
00838             processSamples(activeObst, activeNavMeshObj, m_obstacles, m_levelHeight, vmax, spos, cs/2, 
00839                 nspos,  res, m_maxToi, m_velWeight, m_curVelWeight, m_collisionWeight, m_toiWeight);
00840 
00841             cs *= 0.5f;
00842         }
00843         vcpy(activeObst->nvel, res);
00844     }
00845 }
00846 
00847 KX_ObstacleSimulationTOI_cells::KX_ObstacleSimulationTOI_cells(MT_Scalar levelHeight, bool enableVisualization)
00848 :   KX_ObstacleSimulationTOI(levelHeight, enableVisualization)
00849 ,   m_bias(0.4f)
00850 ,   m_adaptive(true)
00851 ,   m_sampleRadius(15)
00852 {
00853     m_maxSamples = (m_sampleRadius*2+1)*(m_sampleRadius*2+1) + 100;
00854     m_maxToi = 1.5f;
00855     m_velWeight = 2.0f;
00856     m_curVelWeight = 0.75f;
00857     m_toiWeight = 2.5f;
00858     m_collisionWeight = 0.75f; //side_weight
00859 }