# Home    # nevrax.com   
Nevrax
Nevrax.org
#News
#Mailing-list
#Documentation
#CVS
#Bugs
#License
Docs
 
Documentation  
Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages   Search  

build_indoor.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2001 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
00024  */
00025 
00026 #include <map>
00027 #include <vector>
00028 
00029 #include "stdpacs.h"
00030 
00031 #include "pacs/collision_mesh_build.h"
00032 #include "pacs/local_retriever.h"
00033 #include "pacs/exterior_mesh.h"
00034 
00035 #include "build_indoor.h"
00036 
00037 using namespace std;
00038 using namespace NLMISC;
00039 
00040 namespace NLPACS
00041 {
00042 
00049 class CInteriorSurface
00050 {
00051 public:
00053         CCollisionMeshBuild                             *CollisionMeshBuild;
00054 
00056         std::vector<uint32>                             Faces;
00057 
00059         sint32                                                  Id;
00060 
00062         NLMISC::CVector                                 Center;
00063 
00065         sint32                                                  Material;
00066 
00067 public:
00068         CCollisionFace                                  &getFace(uint face) { return CollisionMeshBuild->Faces[Faces[face]]; }
00069         CCollisionFace                                  &getNeighbor(uint face, uint edge)
00070         {
00071                 return CollisionMeshBuild->Faces[getFace(face).Edge[edge]];
00072         }
00073 };
00074 
00075 
00082 class CInteriorBorder
00083 {
00084 public:
00086         std::vector<NLMISC::CVector>    Vertices;
00087 
00089         sint32                                                  Left, Right;
00090 
00091 public:
00092 };
00093 
00094 
00095 // how to build interior snapping data
00096 void    buildSnapping(CCollisionMeshBuild &cmb, CLocalRetriever &lr);
00097 
00098 
00099 // how to build surfaces
00100 void    buildSurfaces(CCollisionMeshBuild &cmb, CLocalRetriever &lr);
00101 
00102 
00103 
00104 //
00105 // functions to build interior surfaces and borders from mesh
00106 //
00107 
00108 // how to generate connex surfaces
00109 void    floodFillSurfaces(CCollisionMeshBuild &cmb, vector<CInteriorSurface> &surfaces)
00110 {
00111         sint32  currentId = 0;
00112 
00113         uint    i;
00114 
00115         for (i=0; i<cmb.Faces.size(); ++i)
00116                 cmb.Faces[i].InternalSurface = -1;
00117 
00118         for (i=0; i<cmb.Faces.size(); ++i)
00119         {
00120                 CCollisionFace  &face = cmb.Faces[i];
00121                 if (face.Surface == CCollisionFace::ExteriorSurface || face.InternalSurface != -1)
00122                         continue;
00123 
00124                 vector<uint>    stack;
00125                 stack.push_back(i);
00126                 face.InternalSurface = currentId;
00127 
00128                 surfaces.resize(surfaces.size()+1);
00129                 surfaces.back().Id = currentId;
00130                 surfaces.back().CollisionMeshBuild = &cmb;
00131                 surfaces.back().Material = face.Material;
00132 
00133                 while (!stack.empty())
00134                 {
00135                         uint    pop = stack.back();
00136                         stack.pop_back();
00137 
00138                         surfaces.back().Faces.push_back(pop);
00139                         CCollisionFace  &popFace = cmb.Faces[pop];
00140 
00141                         uint    edge, neighb;
00142                         for (edge=0; edge<3; ++edge)
00143                         {
00144                                 if ((neighb = popFace.Edge[edge]) != -1 && 
00145                                         cmb.Faces[neighb].InternalSurface == -1 &&
00146                                         cmb.Faces[neighb].Surface == popFace.Surface)
00147                                 {
00148                                         cmb.Faces[neighb].InternalSurface = currentId;
00149                                         stack.push_back(neighb);
00150                                 }
00151                         }
00152                 }
00153 
00154                 ++currentId;
00155         }
00156 }
00157 
00158 
00159 // reset the edge flags of the whole collision mesh build
00160 void    resetEdgeFlags(CCollisionMeshBuild &cmb)
00161 {
00162         uint    face, edge;
00163 
00164         for (face=0; face<cmb.Faces.size(); ++face)
00165                 for (edge=0; edge<3; ++edge)
00166                         cmb.Faces[face].EdgeFlags[edge] = false;
00167 }
00168 
00169 
00170 
00171 // how to generate the borders of a given surface
00172 
00173 void    followBorder(CInteriorSurface &surface, uint first, uint edge, uint sens, vector<CVector> &vstore, bool &loop)
00174 {
00175         CCollisionFace  *current = &surface.getFace(first);
00176         CCollisionFace  *next = (current->Edge[edge] == -1) ? NULL : &surface.CollisionMeshBuild->Faces[current->Edge[edge]];
00177         current->EdgeFlags[edge] = true;
00178         sint32                  currentFace = surface.Faces[first];
00179 
00180         const sint32    currentSurfId = current->InternalSurface;
00181         const sint32    oppositeSurfId = (next != NULL) ? next->InternalSurface : (current->Visibility[edge] ? -1 : -2);
00182         sint                    oedge;
00183 
00184         sint                    pivot = (edge+sens)%3;
00185         sint                    nextEdge = edge;
00186 
00187         bool                    allowThis = true;
00188 
00189         // adds the pivot to the border and its normal
00190         vstore.push_back(surface.CollisionMeshBuild->Vertices[current->V[pivot]]);
00191 
00192         while (true)
00193         {
00194                 loop = false;
00195                 // -1 means no neighbor at all, -2 means a neighbor that is not available yet
00196                 sint32  thisOpposite = (next != NULL) ? next->InternalSurface : (current->Visibility[nextEdge] ? -1 : -2);
00197                 if (thisOpposite != currentSurfId && thisOpposite != oppositeSurfId ||
00198                         (loop = (current->EdgeFlags[nextEdge] && !allowThis)))
00199                 {
00200                         // if reaches the end of the border, then quits.
00201                         break;
00202                 }
00203                 else if (thisOpposite == oppositeSurfId)
00204                 {
00205                         // if the next edge belongs to the border, then go on the same element
00206                         current->EdgeFlags[nextEdge] = true;
00207                         if (oppositeSurfId >= 0)
00208                         {
00209                                 for (oedge=0; oedge<3 && next->Edge[oedge]!=currentFace; ++oedge)
00210                                         ;
00211                                 nlassert(oedge != 3);
00212                                 nlassert(allowThis || !next->EdgeFlags[oedge]);
00213                                 next->EdgeFlags[oedge] = true;
00214                         }
00215                         pivot = (pivot+sens)%3;
00216                         nextEdge = (nextEdge+sens)%3;
00217                         next = (current->Edge[nextEdge] == -1) ? NULL : &surface.CollisionMeshBuild->Faces[current->Edge[nextEdge]];
00218                         vstore.push_back(surface.CollisionMeshBuild->Vertices[current->V[pivot]]);
00219                 }
00220                 else 
00221                 {
00222                         // if the next element is inside the surface, then go to the next element
00223                         nlassert(next->InternalSurface == currentSurfId);
00224 
00225                         for (oedge=0; oedge<3 && next->Edge[oedge]!=currentFace; ++oedge)
00226                                 ;
00227                         nlassert(oedge != 3);
00228                         currentFace = current->Edge[nextEdge];
00229                         current = next;
00230                         pivot = (oedge+3-sens)%3;
00231                         nextEdge = (oedge+sens)%3;
00232                         next = (current->Edge[nextEdge] == -1) ? NULL : &surface.CollisionMeshBuild->Faces[current->Edge[nextEdge]];
00233                 }
00234 
00235                 allowThis = false;
00236         }
00237 }
00238 
00239 
00240 void    computeSurfaceBorders(CInteriorSurface &surface, vector<CInteriorBorder> &borders)
00241 {
00242         uint    face, edge;
00243 
00244         for (face=0; face<surface.Faces.size(); ++face)
00245         {
00246                 // for each element,
00247                 // scan for a edge that points to a different surface
00248                 CCollisionFace  &cf = surface.getFace(face);
00249 
00250                 for (edge=0; edge<3; ++edge)
00251                 {
00252                         if ((cf.Edge[edge] == -1 || surface.getNeighbor(face, edge).InternalSurface != surface.Id) &&
00253                                 !cf.EdgeFlags[edge])
00254                         {
00255                                 borders.resize(borders.size()+1);
00256                                 CInteriorBorder &border = borders.back();
00257 
00258                                 border.Left = cf.InternalSurface;
00259 
00260                                 if (cf.Edge[edge] == -1)
00261                                 {
00262                                         // link on a neighbor retriever or not at all
00263                                         border.Right = cf.Visibility[edge] ? -1 : -2;
00264                                 }
00265                                 else
00266                                 {
00267                                         // link on the neighbor surface
00268                                         border.Right = surface.CollisionMeshBuild->Faces[cf.Edge[edge]].InternalSurface;
00269                                 }
00270 
00271                                 nldebug("generate border %d (%d-%d)", borders.size()-1, border.Left, border.Right);
00272 
00273                                 bool                            loop;
00274                                 vector<CVector>         bwdVerts;
00275                                 vector<CVector>         &fwdVerts = border.Vertices;
00276 
00277                                 followBorder(surface, face, edge, 2, bwdVerts, loop);
00278 
00279                                 sint    i;
00280 
00281                                 fwdVerts.reserve(bwdVerts.size());
00282                                 fwdVerts.clear();
00283 
00284                                 for (i=(sint)(bwdVerts.size()-1); i>=0; --i)
00285                                 {
00286                                         fwdVerts.push_back(bwdVerts[i]);
00287                                 }
00288 
00289                                 if (loop)
00290                                 {
00291                                         fwdVerts.push_back(fwdVerts.front());
00292                                 }
00293                                 else
00294                                 {
00295                                         fwdVerts.resize(fwdVerts.size()-2);
00296                                         followBorder(surface, face, edge, 1, fwdVerts, loop);
00297                                 }
00298                         }
00299                 }
00300         }
00301 }
00302 
00303 
00304 void    computeSurfaceCenter(CInteriorSurface &surface)
00305 {
00306         CCollisionMeshBuild     &cmb = *(surface.CollisionMeshBuild);
00307 
00308         CVector         center = CVector::Null;
00309         float           totalWeight = 0.0f;
00310 
00311         uint    i, j;
00312 
00313         for (i=0; i<surface.Faces.size(); ++i)
00314         {
00315                 CCollisionFace  &face = surface.getFace(i);
00316                 CVector                 v[3];
00317                 
00318                 for (j=0; j<3; ++j)
00319                         v[j] = cmb.Vertices[face.V[j]];
00320 
00321                 float   weight = ((v[2]-v[0])^(v[1]-v[0])).norm();
00322 
00323                 center += (v[0]+v[1]+v[2])*(weight/3.0f);
00324                 totalWeight += weight;
00325         }
00326 
00327         surface.Center = center/totalWeight;
00328 }
00329 
00330 
00331 void    computeSurfaceQuadTree(CInteriorSurface &surface, CSurfaceQuadTree &quad)
00332 {
00333         uint    i, j;
00334 
00335         CAABBox box;
00336         bool    first = true;
00337         for (i=0; i<surface.Faces.size(); ++i)
00338         {
00339                 for (j=0; j<3; ++j)
00340                 {
00341                         const CVector   &v = surface.CollisionMeshBuild->Vertices[surface.CollisionMeshBuild->Faces[surface.Faces[i]].V[j]];
00342                         if (first)
00343                                 box.setCenter(v), first=false;
00344                         else
00345                                 box.extend(v);
00346                 }
00347         }
00348 
00349         quad.clear();
00350         quad.init(4.0f, 6, box.getCenter(), std::max(box.getHalfSize().x, box.getHalfSize().y));
00351 
00352         for (i=0; i<surface.Faces.size(); ++i)
00353         {
00354                 for (j=0; j<3; ++j)
00355                 {
00356                         const CVector   &v = surface.CollisionMeshBuild->Vertices[surface.CollisionMeshBuild->Faces[surface.Faces[i]].V[j]];
00357                         quad.addVertex(v);
00358                 }
00359         }
00360 
00361         quad.compile();
00362 }
00363 
00364 
00365 void    buildSurfaces(CCollisionMeshBuild &cmb, CLocalRetriever &lr)
00366 {
00367         vector<CInteriorSurface>        surfaces;
00368         vector<CInteriorBorder>         borders;
00369 
00370         floodFillSurfaces(cmb, surfaces);
00371 
00372         resetEdgeFlags(cmb);
00373 
00374         uint    surf, bord;
00375 
00377         for (surf=0; surf<surfaces.size(); ++surf)
00378         {
00379                 CSurfaceQuadTree        quad;
00380                 computeSurfaceBorders(surfaces[surf], borders);
00381                 computeSurfaceCenter(surfaces[surf]);
00382                 computeSurfaceQuadTree(surfaces[surf], quad);
00383                 lr.addSurface(0, 0, (uint8)surfaces[surf].Material, 0, 0, false, 0.0f, surfaces[surf].Center, quad);
00384         }
00385 
00386         sint    numBorderChains = 0;
00387 
00388         for (bord=0; bord<borders.size(); ++bord)
00389         {
00390                 sint32  left = borders[bord].Left;
00391                 sint32  right = (borders[bord].Right == -2) ? CChain::convertBorderChainId(numBorderChains++) : borders[bord].Right;
00392 
00393                 if (left<0 || left>=(sint)surfaces.size() ||
00394                         right>(sint)surfaces.size())
00395                         nlstop;
00396 
00397                 lr.addChain(borders[bord].Vertices, left, right);
00398         }
00399 }
00400 
00401 
00402 //
00403 
00404 void    buildSnapping(CCollisionMeshBuild &cmb, CLocalRetriever &lr)
00405 {
00406         // copy the vertices
00407         lr.getInteriorVertices() = cmb.Vertices;
00408 
00409         // create the faces
00410         uint    i;
00411         vector<CLocalRetriever::CInteriorFace>  &faces = lr.getInteriorFaces();
00412         for (i=0; i<cmb.Faces.size(); ++i)
00413         {
00414                 faces.push_back(CLocalRetriever::CInteriorFace());
00415                 faces.back().Verts[0] = cmb.Faces[i].V[0];
00416                 faces.back().Verts[1] = cmb.Faces[i].V[1];
00417                 faces.back().Verts[2] = cmb.Faces[i].V[2];
00418                 faces.back().Surface = cmb.Faces[i].InternalSurface;
00419         }
00420 
00421         // create the face grid
00422         lr.initFaceGrid();
00423 }
00424 
00425 
00426 //
00427 // functions to build local retrievers
00428 //
00429 
00430 void    buildExteriorMesh(CCollisionMeshBuild &cmb, CExteriorMesh &em)
00431 {
00432         // find the first non interior face
00433         uint    i, edge;
00434         bool    found = false;
00435         for (i=0; i<cmb.Faces.size() && !found; ++i)
00436         {
00437                 if (cmb.Faces[i].Surface != CCollisionFace::ExteriorSurface)
00438                         continue;
00439 
00440                 for (edge=0; edge<3 && !found; ++edge)
00441                         if (cmb.Faces[i].Edge[edge] == -1)
00442                         {
00443                                 found = true;
00444                                 break;
00445                         }
00446 
00447                 if (found)
00448                         break;
00449         }
00450 
00451         //
00452         if (!found)
00453                 return;
00454 
00455         sint32                          current = i;
00456         sint32                          next = cmb.Faces[current].Edge[edge];
00457         for (i=0; i<cmb.Faces.size(); ++i)
00458         {
00459                 cmb.Faces[i].EdgeFlags[0] = false;
00460                 cmb.Faces[i].EdgeFlags[1] = false;
00461                 cmb.Faces[i].EdgeFlags[2] = false;
00462         }
00463         
00464         sint                    oedge;
00465         sint                    pivot = (edge+1)%3;
00466         sint                    nextEdge = edge;
00467 
00468         bool                    allowThis = true;
00469 
00470         vector<CExteriorMesh::CEdge>    edges;
00471         uint                                                    numLink = 0;
00472 
00473         while (true)
00474         {
00475                 if (cmb.Faces[current].EdgeFlags[nextEdge])
00476                 {
00477                         // if reaches the end of the border, then quits.
00478                         break;
00479                 }
00480                 else if (next == -1)
00481                 {
00482                         // if the next edge belongs to the border, then go on the same element
00483                         cmb.Faces[current].EdgeFlags[nextEdge] = true;
00485                         sint    link = (cmb.Faces[current].Visibility[nextEdge]) ? -1 : (numLink++);
00486                         edges.push_back(CExteriorMesh::CEdge(cmb.Vertices[cmb.Faces[current].V[pivot]], link));
00487                         nldebug("border: vertex=%d (%.2f,%.2f,%.2f) link=%d", cmb.Faces[current].V[pivot], cmb.Vertices[cmb.Faces[current].V[pivot]].x, cmb.Vertices[cmb.Faces[current].V[pivot]].y, cmb.Vertices[cmb.Faces[current].V[pivot]].z, link);
00488                         pivot = (pivot+1)%3;
00489                         nextEdge = (nextEdge+1)%3;
00490                         next = cmb.Faces[current].Edge[nextEdge];
00491                 }
00492                 else
00493                 {
00494                         // if the next element is inside the surface, then go to the next element
00495                         for (oedge=0; oedge<3 && cmb.Faces[next].Edge[oedge]!=current; ++oedge)
00496                                 ;
00497                         nlassert(oedge != 3);
00498                         current = next;
00499                         pivot = (oedge+2)%3;
00500                         nextEdge = (oedge+1)%3;
00501                         next = cmb.Faces[current].Edge[nextEdge];
00502                 }
00503         }
00504 
00505         edges.push_back(edges.front());
00506         edges.back().Link = -1;
00507 
00508         em.setEdges(edges);
00509 }
00510 
00511 
00512 //
00513 void    linkExteriorToInterior(CLocalRetriever &lr)
00514 {
00515         CExteriorMesh                                   em = lr.getExteriorMesh();
00516         vector<CExteriorMesh::CEdge>    edges = em.getEdges();
00517         vector<CExteriorMesh::CLink>    links;
00518         const vector<CChain>                    &chains = lr.getChains();
00519         const vector<COrderedChain3f>   &ochains = lr.getFullOrderedChains();
00520         const vector<uint16>                    &bchains = lr.getBorderChains();
00521 
00522         {
00523                 uint    i;
00524 
00525                 for (i=0; i<bchains.size(); ++i)
00526                 {
00527                         static char     buf[512], w[256];
00528                         const CChain    &chain = chains[bchains[i]];
00529                         sprintf(buf, "chain=%d ", bchains[i]);
00530                         uint    och;
00531                         for (och=0; och<chain.getSubChains().size(); ++och)
00532                         {
00533                                 const COrderedChain3f   &ochain = ochains[chain.getSubChain(och)];
00534                                 sprintf(w, "subchain=%d", chain.getSubChain(och));
00535                                 strcat(buf, w);
00536                                 uint    v;
00537                                 for (v=0; v<ochain.getVertices().size(); ++v)
00538                                 {
00539                                         sprintf(w, " (%.2f,%.2f)", ochain[v].x, ochain[v].y);
00540                                         strcat(buf, w);
00541                                 }
00542                         }
00543 
00544                         nlinfo("%s", buf);
00545                 }
00546         }
00547 
00548         uint    edge, ch;
00549         for (edge=0; edge+1<edges.size(); ++edge)
00550         {
00551                 if (edges[edge].Link == -1)
00552                         continue;
00553 
00554                 CVector start = edges[edge].Start, stop = edges[edge+1].Start;
00555                 bool    found = false;
00556 
00557                 for (ch=0; ch<bchains.size() && !found; ++ch)
00558                 {
00559                         // get the border chain.
00560                         //const CChain  &chain = chains[bchains[ch]];
00561 
00562                         const CVector   &cstart = lr.getStartVector(bchains[ch]),
00563                                                         &cstop = lr.getStopVector(bchains[ch]);
00564 
00565                         float d = (start-cstart).norm()+(stop-cstop).norm();
00566                         if (d < 1.0e-1f)
00567                         {
00568                                 found = true;
00569                                 break;
00570                         }
00571                 }
00572 
00573                 // create a link
00574                 CExteriorMesh::CLink    link;
00575 
00576                 if (!found)
00577                 {
00578                         nlwarning("in linkInteriorToExterior():");
00579                         nlwarning("couldn't find any link to the exterior edge %d!!", edge);
00580                 }
00581                 else
00582                 {
00583                         // set it up to point on the chain and surface
00584                         link.BorderChainId = ch;
00585                         link.ChainId = bchains[ch];
00586                         link.SurfaceId = (uint16)chains[link.ChainId].getLeft();
00587                 }
00588 
00589                 // enlarge the links
00590                 if (edges[edge].Link >= (sint)links.size())
00591                         links.resize(edges[edge].Link+1);
00592 
00593                 // if the link already exists, warning
00594                 if (links[edges[edge].Link].BorderChainId != 0xFFFF ||
00595                         links[edges[edge].Link].ChainId != 0xFFFF ||
00596                         links[edges[edge].Link].SurfaceId != 0xFFFF)
00597                 {
00598                         nlwarning("in linkInteriorToExterior():");
00599                         nlwarning("link %d already set!!", edges[edge].Link);
00600                 }
00601 
00602                 // setup the link
00603                 links[edges[edge].Link] = link;
00604         }
00605 
00606         em.setEdges(edges);
00607         em.setLinks(links);
00608         lr.setExteriorMesh(em);
00609 }
00610 
00611 
00612 
00613 
00614 
00615 //
00616 bool    computeRetriever(CCollisionMeshBuild &cmb, CLocalRetriever &lr, CVector &translation, string &error, bool useCmbTrivialTranslation)
00617 {
00618         // set the retriever
00619         lr.setType(CLocalRetriever::Interior);
00620 
00621         // if should use the own cmb bbox, then compute it
00622         if (useCmbTrivialTranslation)
00623         {
00624                 translation = cmb.computeTrivialTranslation();
00625                 // snap the translation vector to a meter wide grid
00626                 translation.x = (float)ceil(translation.x);
00627                 translation.y = (float)ceil(translation.y);
00628                 translation.z = 0.0f;
00629         }
00630         
00631         vector<string>  errors;
00632         
00633         cmb.link(false, errors);
00634         cmb.link(true, errors);
00635 
00636         if (!errors.empty())
00637         {
00638                 nlwarning("Edge issues reported !!");
00639                 uint    i;
00640                 error = "";
00641                 for (i=0; i<errors.size(); ++i)
00642                         error += errors[i]+"\n";
00643                 return false;
00644         }
00645         
00646         // translate the meshbuild to the local axis
00647         cmb.translate(translation);
00648 
00649         // find the exterior mesh border
00650         CExteriorMesh   extMesh;
00651         buildExteriorMesh(cmb, extMesh);
00652         lr.setExteriorMesh(extMesh);
00653 
00654         // build the surfaces in the local retriever
00655         buildSurfaces(cmb, lr);
00656 
00657         // create the snapping faces and vertices
00658         // after the build surfaces because the InternalSurfaceId is filled within buildSurfaces()...
00659         buildSnapping(cmb, lr);
00660 
00661         //
00662         lr.computeLoopsAndTips();
00663 
00664         lr.findBorderChains();
00665         lr.updateChainIds();
00666         lr.computeTopologies();
00667 
00668         lr.unify();
00669 
00670         lr.computeCollisionChainQuad();
00671 /*
00672         //
00673         for (i=0; i<lr.getSurfaces().size(); ++i)
00674                 lr.dumpSurface(i);
00675 */
00676         //
00677         linkExteriorToInterior(lr);
00678 
00679         // compute the bbox of the retriever
00680         uint    i, j;
00681         CAABBox bbox;
00682         bool    first = true;
00683 
00684         for (i=0; i<extMesh.getEdges().size(); ++i)
00685                 if (!first)
00686                         bbox.extend(extMesh.getEdge(i).Start);
00687                 else
00688                         bbox.setCenter(extMesh.getEdge(i).Start), first=false;
00689 
00690         for (i=0; i<lr.getOrderedChains().size(); ++i)
00691                 for (j=0; j<lr.getOrderedChain(i).getVertices().size(); ++j)
00692                         if (!first)
00693                                 bbox.extend(lr.getOrderedChain(i)[j].unpack3f());
00694                         else
00695                                 bbox.setCenter(lr.getOrderedChain(i)[j].unpack3f()), first=false;
00696 
00697         CVector bboxhs = bbox.getHalfSize();
00698         bboxhs.z = 10000.0f;
00699         bbox.setHalfSize(bboxhs);
00700 
00701         lr.setBBox(bbox);
00702 
00703         return true;
00704 }
00705 
00706 } // NLPACS