src/engine/ePath.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 *************************************************************************
00004 
00005 ArmageTron -- Just another Tron Lightcycle Game in 3D.
00006 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
00007 
00008 **************************************************************************
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00023 
00024 ***************************************************************************
00025 
00026 */
00027 
00028 #include "eGrid.h"
00029 #include "eTess2.h"
00030 #include "ePath.h"
00031 #include "eGameObject.h"
00032 #include "tConsole.h"
00033 #include "eWall.h"
00034 #include "eSensor.h"
00035 
00036 // heap of HalfEdges that are considered to be included in a possible path
00037 static tHeap<eHalfEdge> openEdges;
00038 static tHeap<eHalfEdge> closedEdges;
00039 
00040 
00041 
00042 static REAL Distance(const eHalfEdge* a, const eCoord& b)
00043 {
00044     tASSERT( a );
00045     tASSERT( a->Point() );
00046 
00047     return sqrt(((*a->Point()) - b).NormSquared());
00048 }
00049 
00050 
00051 
00052 static REAL Modifier(const eGameObject *gameObject,
00053                      const eHalfEdge   *edge)
00054 {
00055     tASSERT(edge);
00056     tASSERT(gameObject);
00057 
00058     eWall* w = edge->GetWall();
00059     if (!w && edge->Other())
00060         w = edge->Other()->GetWall();
00061 
00062     return gameObject->PathfindingModifier(w);
00063 }
00064 
00065 static bool CanPass(const eGameObject *gameObject,
00066                     const eHalfEdge   *edge)
00067 {
00068     tASSERT(edge);
00069     tASSERT(gameObject);
00070 
00071     eWall* w = edge->GetWall();
00072     if (!w && edge->Other())
00073         w = edge->Other()->GetWall();
00074 
00075     return !gameObject->EdgeIsDangerous(w, gameObject->LastTime(), 0.5f);
00076 }
00077 
00078 
00079 // check whether this edge
00080 // crosses any of the brand-new walls on the grid:
00081 eWall* eHalfEdge::CrossesNewWall(const eGrid *grid) const
00082 {
00083     // check all the currently drawn eWalls:
00084     for(int i=grid->wallsNotYetInserted.Len()-1;i>=0;i--)
00085     {
00086         const eHalfEdge *other_e=grid->wallsNotYetInserted(i)->Edge();
00087         if (
00088             other_e->Point() && other_e->Other() && other_e->Other()->Point())
00089         {
00090             tJUST_CONTROLLED_PTR< ePoint > new_cross_p=IntersectWith(other_e);
00091             if (new_cross_p)
00092             {
00093                 REAL e_ratio =Ratio(*new_cross_p);
00094                 REAL o_ratio =other_e->Ratio(*new_cross_p);
00095                 if (0<=e_ratio && 1>=e_ratio &&
00096                         0<=o_ratio && 1>=o_ratio)
00097                 { // find the fall
00098                     eWall *w = other_e->GetWall();
00099                     if (!w)
00100                         w = other_e->Other()->GetWall();
00101 
00102                     if (w)
00103                         return w;
00104                 }
00105             }
00106         }
00107     }
00108     return NULL;
00109 }
00110 
00111 void eHalfEdge::ClearPathData()
00112 {
00113     while (openEdges.Len())
00114     {
00115         eHalfEdge *e = openEdges.Remove(0);
00116         e->origin_ = PATH_NONE;
00117     }
00118     while (closedEdges.Len())
00119     {
00120         eHalfEdge *e = closedEdges.Remove(0);
00121         e->origin_ = PATH_NONE;
00122     }
00123 }
00124 
00125 
00126 REAL EdgePenalty(const eHalfEdge *e, const eGameObject *g)
00127 {
00128     eCoord probePre = e->Vec().Turn(0, 1);
00129     eCoord probe    = probePre * (1/sqrt(probePre.NormSquared()));
00130 
00131     eSensor s (const_cast<eGameObject * >( g ), *e->Point() + e->Vec() * .5f + probePre * .0001f, probe);
00132     s.SetCurrentFace(e->Face());
00133     s.detect(1);
00134     if (s.hit > 1)
00135         s.hit = 1;
00136 
00137     return 100 * (1/(s.hit + .1) - (1/1.1));
00138 }
00139 
00140 void eHalfEdge::SetMinPathLength( REAL length, tHeapBase& heap, ePATH_ORIGIN origin )
00141 {
00142     origin_ = origin;
00143     tHeapElement::SetVal( length, heap );
00144 
00145     tASSERT( &heap == Heap() );
00146 }
00147 
00148 // pathfinding interface: find a path for gameobject from origin to target.
00149 void eHalfEdge::FindPath(const eCoord& startPoint, const eFace* startFace,
00150                          const eCoord& stopPoint , const eFace* stopFace,
00151                          const eGameObject* gameObject,
00152                          ePath& path)
00153 {
00154     tASSERT( startFace );
00155     tASSERT( stopFace );
00156     tASSERT( gameObject );
00157 
00158     // cleanup previous path data (we'll keep it around for debugging)
00159     ClearPathData();
00160     path.Clear();
00161 
00162     // start: add the three vertices around the origin to the open list
00163     {
00164         eHalfEdge *run = startFace->Edge();
00165         for (int i=2; i>=0; i--)
00166         {
00167             run->SetMinPathLength( Distance(run, startPoint) + Distance(run, stopPoint), openEdges, PATH_START );
00168 
00169             run = run->Next();
00170         }
00171     }
00172 
00173     // search for a path until one of the edges of the goal face is in the closed list or we have to give up
00174     eHalfEdge* stopEdge = NULL;
00175 
00176     while (openEdges.Len() > 0 && !stopEdge)
00177     {
00178         // take the most promising HalfEdge out of the open list, put it
00179         // into the closed list
00180         // and add all possible ways from there to the open list.
00181 
00182         eHalfEdge *e = openEdges.Remove(0);
00183 
00184         int origin = e->origin_;
00185         origin |= PATH_CLOSED;
00186         e->origin_ = static_cast<ePATH_ORIGIN>(origin);
00187 
00188         closedEdges.Insert(e);
00189 
00190         // check if we are at the goal
00191         if (e->Face() == stopFace)
00192             stopEdge = e;
00193 
00194 #ifdef DEBUG
00195         //      con << "examining " << *e->Point() << ", " << e->Vec() << "\n";
00196 #endif
00197 
00198         eHalfEdge *next = e->Next();
00199         eHalfEdge *prev = NULL;
00200 
00201         eHalfEdge *other_next = e->Other()->Next();
00202         eHalfEdge *prev_other = NULL;
00203 
00204         // try the next HalfEdge in this triangle
00205         {
00206             if (next)
00207             {
00208                 prev = next->Next();
00209 
00210                 // check if we cross a new wall on the way
00211                 if (!e->CrossesNewWall(gameObject->Grid()))
00212                 {
00213                     // we get this much closer to the target by going this way:
00214                     REAL closer  = Distance(e, stopPoint) - Distance(next, stopPoint);
00215 
00216                     // but have to go this much to get there:
00217                     REAL way     = sqrt(e->Vec().NormSquared()) * Modifier(gameObject, e);
00218 
00219                     // tell n that there is a possible path to it
00220                     next->PossiblePath(PATH_PREV, e->MinPathLength() + way - closer + EdgePenalty(e, gameObject) );
00221                 }
00222             }
00223         }
00224 
00225 
00226         // try the previous HalfEdge in this triangle
00227         if (prev)
00228         {
00229             tASSERT(prev->Next() == e);
00230             prev_other = prev->Other();
00231 
00232             // check if we cross a new wall on the way
00233             if (!prev->CrossesNewWall(gameObject->Grid()))
00234             {
00235                 // we get this much closer to the target by going this way:
00236                 REAL closer  = Distance(e, stopPoint) - Distance(prev, stopPoint);
00237 
00238                 // but have to go this much to get there:
00239                 REAL way     = sqrt(prev->Vec().NormSquared()) * Modifier(gameObject, e);
00240 
00241                 // tell n that there is a possible path to it
00242                 prev->PossiblePath(PATH_NEXT, e->MinPathLength() + way - closer  + EdgePenalty(e, gameObject));
00243             }
00244         }
00245 
00246 
00247         // now the edges that are just around the corner. They do not
00248         // make the way longer, but require the passed edge to be harmless
00249         // to the object.
00250 
00251         if (other_next && CanPass(gameObject, e))
00252             other_next->PossiblePath(PATH_PREV_OTHER, e->MinPathLength() + EdgePenalty(e, gameObject) + .000001f);
00253         if (prev_other && CanPass(gameObject, prev))
00254             prev_other->PossiblePath(PATH_OTHER_NEXT, e->MinPathLength() + EdgePenalty(e, gameObject) + .000001f);
00255     }
00256 
00257     // no path found...
00258     if (!stopEdge)
00259     {
00260         ClearPathData();
00261         return;
00262     }
00263 
00264     // allright! Now we just go back from the goal to the origin,
00265     // following the crumbs of bread we left behind.
00266 
00267 #ifdef DEBUG
00268     con << "Found path.\n";
00269     //  con << startPoint << "\n";
00270 #endif
00271 
00272     path.Add(stopPoint);
00273 
00274     eHalfEdge* run = stopEdge;
00275     while (run && run->Face() != startFace)
00276     {
00277         path.Add(run);
00278 
00279 #ifdef DEBUG
00280         //      con << *run->Point() << ", " << run->Vec() << "\n";
00281 #endif
00282 
00283         tASSERT(run->origin_ >= PATH_CLOSED);
00284 
00285         switch (run->origin_-PATH_CLOSED)
00286         {
00287         case (PATH_NEXT):
00288                         run = run->Next();
00289             break;
00290         case (PATH_PREV):
00291                         run = run->Next()->Next();
00292             break;
00293         case (PATH_OTHER_NEXT):
00294                         run = run->Other()->Next();
00295             break;
00296         case (PATH_PREV_OTHER):
00297                         run = run->Next()->Next()->Other();
00298             break;
00299         default:
00300             tERR_WARN("Pathfinding error!\n");
00301             run = startFace->Edge();
00302         }
00303     }
00304 
00305     //  path.Add(run);
00306     //  path.Add(startPoint);
00307 
00308 #ifdef DEBUG
00309     //  con << stopPoint << "\n";
00310 #endif
00311 
00312     ClearPathData();
00313 }
00314 
00315 
00316 void eHalfEdge::PossiblePath( ePATH_ORIGIN newOrigin, REAL minLength ) // tell the pathfinder that there might be a path of total length minLength through this HalfEdge, coming from the given origin type.
00317 {
00318     // nothing needs to be done if there is already a shorter path known
00319     if( origin_ == PATH_NONE )
00320     {
00321         // completely new entry.
00322 
00323         SetMinPathLength( minLength, openEdges, newOrigin );
00324 
00325 #ifdef DEBUG
00326         //      con << "adding " << *Point() << ", " << Vec() << ", origin "
00327         //        << origin << " to open list.\n";
00328 #endif
00329     }
00330     else if( minLength <  MinPathLength() && origin_ < PATH_CLOSED)
00331     {
00332         // just update our info; the path got shorter.
00333         SetMinPathLength( minLength, openEdges, newOrigin );
00334 
00335 #ifdef DEBUG
00336         //      con << "updating " << *Point() << ", " << Vec() << ", origin "
00337         //        << origin << " to open list.\n";
00338 #endif
00339 
00340     }
00341 }
00342 
00343 
00344 tHeapBase *eHalfEdge::Heap() const
00345 {
00346     if (origin_ >= PATH_NONE)
00347         return NULL;
00348     if (origin_ >= PATH_CLOSED)
00349         return &closedEdges;
00350 
00351     return &openEdges;
00352 }
00353 
00354 
00355 static ePath* lastPath = NULL;
00356 
00357 #ifdef DEBUG
00358 #include "rRender.h"
00359 
00360 void ePath::RenderLast()  // renders the last found path
00361 {
00362 #ifndef DEDICATED
00363     if (!lastPath)
00364         return;
00365 
00366     glDisable(GL_TEXTURE_2D);
00367     glDisable(GL_LIGHTING);
00368 
00369     glColor4f(1,0,0,1);
00370 
00371     BeginLineStrip();
00372     for (int i = lastPath->positions.Len()-1; i>=0; i--)
00373     {
00374         eCoord c = lastPath->positions(i) + lastPath->offsets(i);
00375         Vertex(c.x, c.y, 0.1f);
00376     }
00377     RenderEnd();
00378 
00379     glColor4f(1,1,0,1);
00380 
00381     BeginLineStrip();
00382     if (lastPath->current >= 0 && lastPath->positions.Len() > 0)
00383     {
00384         eCoord c = lastPath->CurrentPosition();
00385         Vertex(c.x, c.y, 0);
00386         Vertex(c.x, c.y, 50);
00387     }
00388     RenderEnd();
00389 #endif
00390 }
00391 #endif
00392 
00393 bool ePath::Proceed()
00394 {
00395     if (current > 0)
00396     {
00397         current--;
00398         return true;
00399     }
00400     else
00401         return false;
00402 }
00403 
00404 bool ePath::GoBack()
00405 {
00406     if (current < positions.Len()-1)
00407     {
00408         current++;
00409         return true;
00410     }
00411     else
00412         return false;
00413 }
00414 
00415 ePath::ePath(){
00416     current=-1;
00417 }
00418 
00419 ePath::~ePath(){
00420     if ( lastPath == this )
00421         lastPath = NULL;
00422 }
00423 
00424 void ePath::Add(eHalfEdge *e)
00425 {
00426     tASSERT (e->Point() && e->Next() && e->Next()->Point() && e->Next()->Next() && e->Next()->Next()->Point());
00427 
00428     current++;
00429 
00430     eCoord pos    = *e->Point();
00431     //  eCoord dir    = e->Vec();
00432     eCoord offset = ((*e->Next()->Next()->Point() + *e->Next()->Point()) * .5f - pos) * .5f;
00433     //  offset = offset - dir * (eCoord::F(dir, offset) / dir.NormSquared());
00434 
00435     REAL ol = sqrt(offset.NormSquared());
00436     if (ol > 1)
00437         offset = offset * (1/ol);
00438 
00439     positions[current] = pos;
00440     offsets[current]   = offset;
00441 }
00442 
00443 void ePath::Add(const eCoord& point)
00444 {
00445     current++;
00446 
00447     positions[current] = point;
00448     offsets[current]   = eCoord(0, 0);
00449 }
00450 
00451 
00452 void ePath::Clear()
00453 {
00454     lastPath = this;
00455 
00456     current=-1;
00457 
00458     offsets.SetLen(0);
00459     positions.SetLen(0);
00460 }
00461 
00462 
00463 
00464 

Generated on Sat Mar 15 22:55:45 2008 for Armagetron Advanced by  doxygen 1.5.4