|
Blender
V2.59
|
00001 /* 00002 * $Id: RAS_MaterialBucket.cpp 35174 2011-02-25 13:38:24Z jesterking $ 00003 * ***** BEGIN GPL LICENSE BLOCK ***** 00004 * 00005 * This program is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU General Public License 00007 * as published by the Free Software Foundation; either version 2 00008 * of the License, or (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License 00016 * along with this program; if not, write to the Free Software Foundation, 00017 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 * 00019 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. 00020 * All rights reserved. 00021 * 00022 * The Original Code is: all of this file. 00023 * 00024 * Contributor(s): none yet. 00025 * 00026 * ***** END GPL LICENSE BLOCK ***** 00027 */ 00028 00034 #include "RAS_MaterialBucket.h" 00035 00036 #if defined(WIN32) && !defined(FREE_WINDOWS) 00037 #pragma warning (disable:4786) 00038 #endif 00039 00040 #ifdef WIN32 00041 #include <windows.h> 00042 #endif // WIN32 00043 00044 #include "RAS_Polygon.h" 00045 #include "RAS_TexVert.h" 00046 #include "RAS_IRasterizer.h" 00047 #include "RAS_IRenderTools.h" 00048 #include "RAS_MeshObject.h" 00049 #include "RAS_Deformer.h" // __NLA 00050 00051 /* mesh slot */ 00052 00053 RAS_MeshSlot::RAS_MeshSlot() : SG_QList() 00054 { 00055 m_clientObj = NULL; 00056 m_pDeformer = NULL; 00057 m_OpenGLMatrix = NULL; 00058 m_mesh = NULL; 00059 m_bucket = NULL; 00060 m_bVisible = false; 00061 m_bCulled = true; 00062 m_bObjectColor = false; 00063 m_RGBAcolor = MT_Vector4(0.0, 0.0, 0.0, 0.0); 00064 m_DisplayList = NULL; 00065 m_bDisplayList = true; 00066 m_joinSlot = NULL; 00067 m_pDerivedMesh = NULL; 00068 } 00069 00070 RAS_MeshSlot::~RAS_MeshSlot() 00071 { 00072 RAS_DisplayArrayList::iterator it; 00073 00074 #ifdef USE_SPLIT 00075 Split(true); 00076 00077 while(m_joinedSlots.size()) 00078 m_joinedSlots.front()->Split(true); 00079 #endif 00080 00081 for(it=m_displayArrays.begin(); it!=m_displayArrays.end(); it++) { 00082 (*it)->m_users--; 00083 if((*it)->m_users == 0) 00084 delete *it; 00085 } 00086 00087 if (m_DisplayList) { 00088 m_DisplayList->Release(); 00089 m_DisplayList = NULL; 00090 } 00091 } 00092 00093 RAS_MeshSlot::RAS_MeshSlot(const RAS_MeshSlot& slot) : SG_QList() 00094 { 00095 RAS_DisplayArrayList::iterator it; 00096 00097 m_clientObj = NULL; 00098 m_pDeformer = NULL; 00099 m_pDerivedMesh = NULL; 00100 m_OpenGLMatrix = NULL; 00101 m_mesh = slot.m_mesh; 00102 m_bucket = slot.m_bucket; 00103 m_bVisible = slot.m_bVisible; 00104 m_bCulled = slot.m_bCulled; 00105 m_bObjectColor = slot.m_bObjectColor; 00106 m_RGBAcolor = slot.m_RGBAcolor; 00107 m_DisplayList = NULL; 00108 m_bDisplayList = slot.m_bDisplayList; 00109 m_joinSlot = NULL; 00110 m_currentArray = slot.m_currentArray; 00111 m_displayArrays = slot.m_displayArrays; 00112 m_joinedSlots = slot.m_joinedSlots; 00113 00114 m_startarray = slot.m_startarray; 00115 m_startvertex = slot.m_startvertex; 00116 m_startindex = slot.m_startindex; 00117 m_endarray = slot.m_endarray; 00118 m_endvertex = slot.m_endvertex; 00119 m_endindex = slot.m_endindex; 00120 00121 for(it=m_displayArrays.begin(); it!=m_displayArrays.end(); it++) { 00122 // don't copy display arrays for now because it breaks python 00123 // access to vertices, but we'll need a solution if we want to 00124 // join display arrays for reducing draw calls. 00125 //*it = new RAS_DisplayArray(**it); 00126 //(*it)->m_users = 1; 00127 00128 (*it)->m_users++; 00129 } 00130 } 00131 00132 void RAS_MeshSlot::init(RAS_MaterialBucket *bucket, int numverts) 00133 { 00134 m_bucket = bucket; 00135 00136 SetDisplayArray(numverts); 00137 00138 m_startarray = 0; 00139 m_startvertex = 0; 00140 m_startindex = 0; 00141 m_endarray = 0; 00142 m_endvertex = 0; 00143 m_endindex = 0; 00144 } 00145 00146 void RAS_MeshSlot::begin(RAS_MeshSlot::iterator& it) 00147 { 00148 int startvertex, endvertex; 00149 int startindex, endindex; 00150 00151 it.array = (m_displayArrays.size() > 0)? m_displayArrays[m_startarray]: NULL; 00152 00153 if(it.array == NULL || it.array->m_index.size() == 0 || it.array->m_vertex.size() == 0) { 00154 it.array = NULL; 00155 it.vertex = NULL; 00156 it.index = NULL; 00157 it.startvertex = 0; 00158 it.endvertex = 0; 00159 it.totindex = 0; 00160 } 00161 else { 00162 startvertex = m_startvertex; 00163 endvertex = (m_startarray == m_endarray)? m_endvertex: it.array->m_vertex.size(); 00164 startindex = m_startindex; 00165 endindex = (m_startarray == m_endarray)? m_endindex: it.array->m_index.size(); 00166 00167 it.vertex = &it.array->m_vertex[0]; 00168 it.index = &it.array->m_index[startindex]; 00169 it.startvertex = startvertex; 00170 it.endvertex = endvertex; 00171 it.totindex = endindex-startindex; 00172 it.arraynum = m_startarray; 00173 } 00174 } 00175 00176 void RAS_MeshSlot::next(RAS_MeshSlot::iterator& it) 00177 { 00178 int startvertex, endvertex; 00179 int startindex, endindex; 00180 00181 if(it.arraynum == (size_t)m_endarray) { 00182 it.array = NULL; 00183 it.vertex = NULL; 00184 it.index = NULL; 00185 it.startvertex = 0; 00186 it.endvertex = 0; 00187 it.totindex = 0; 00188 } 00189 else { 00190 it.arraynum++; 00191 it.array = m_displayArrays[it.arraynum]; 00192 00193 startindex = 0; 00194 endindex = (it.arraynum == (size_t)m_endarray)? m_endindex: it.array->m_index.size(); 00195 startvertex = 0; 00196 endvertex = (it.arraynum == (size_t)m_endarray)? m_endvertex: it.array->m_vertex.size(); 00197 00198 it.vertex = &it.array->m_vertex[0]; 00199 it.index = &it.array->m_index[startindex]; 00200 it.startvertex = startvertex; 00201 it.endvertex = endvertex; 00202 it.totindex = endindex-startindex; 00203 } 00204 } 00205 00206 bool RAS_MeshSlot::end(RAS_MeshSlot::iterator& it) 00207 { 00208 return (it.array == NULL); 00209 } 00210 00211 RAS_DisplayArray *RAS_MeshSlot::CurrentDisplayArray() 00212 { 00213 return m_currentArray; 00214 } 00215 00216 void RAS_MeshSlot::SetDisplayArray(int numverts) 00217 { 00218 RAS_DisplayArrayList::iterator it; 00219 RAS_DisplayArray *darray = NULL; 00220 00221 for(it=m_displayArrays.begin(); it!=m_displayArrays.end(); it++) { 00222 darray = *it; 00223 00224 if(darray->m_type == numverts) { 00225 if(darray->m_index.size()+numverts >= RAS_DisplayArray::BUCKET_MAX_INDEX) 00226 darray = NULL; 00227 else if(darray->m_vertex.size()+numverts >= RAS_DisplayArray::BUCKET_MAX_VERTEX) 00228 darray = NULL; 00229 else 00230 break; 00231 } 00232 else 00233 darray = NULL; 00234 } 00235 00236 if(!darray) { 00237 darray = new RAS_DisplayArray(); 00238 darray->m_users = 1; 00239 00240 if(numverts == 2) darray->m_type = RAS_DisplayArray::LINE; 00241 else if(numverts == 3) darray->m_type = RAS_DisplayArray::TRIANGLE; 00242 else darray->m_type = RAS_DisplayArray::QUAD; 00243 00244 m_displayArrays.push_back(darray); 00245 00246 if(numverts == 2) 00247 darray->m_type = RAS_DisplayArray::LINE; 00248 else if(numverts == 3) 00249 darray->m_type = RAS_DisplayArray::TRIANGLE; 00250 else if(numverts == 4) 00251 darray->m_type = RAS_DisplayArray::QUAD; 00252 00253 m_endarray = m_displayArrays.size()-1; 00254 m_endvertex = 0; 00255 m_endindex = 0; 00256 } 00257 00258 m_currentArray = darray; 00259 } 00260 00261 void RAS_MeshSlot::AddPolygon(int numverts) 00262 { 00263 SetDisplayArray(numverts); 00264 } 00265 00266 int RAS_MeshSlot::AddVertex(const RAS_TexVert& tv) 00267 { 00268 RAS_DisplayArray *darray; 00269 int offset; 00270 00271 darray = m_currentArray; 00272 darray->m_vertex.push_back(tv); 00273 offset = darray->m_vertex.size()-1; 00274 00275 if(darray == m_displayArrays[m_endarray]) 00276 m_endvertex++; 00277 00278 return offset; 00279 } 00280 00281 void RAS_MeshSlot::AddPolygonVertex(int offset) 00282 { 00283 RAS_DisplayArray *darray; 00284 00285 darray = m_currentArray; 00286 darray->m_index.push_back(offset); 00287 00288 if(darray == m_displayArrays[m_endarray]) 00289 m_endindex++; 00290 } 00291 00292 void RAS_MeshSlot::SetDeformer(RAS_Deformer* deformer) 00293 { 00294 if (deformer && m_pDeformer != deformer) { 00295 RAS_DisplayArrayList::iterator it; 00296 if (deformer->ShareVertexArray()) { 00297 // this deformer uses the base vertex array, first release the current ones 00298 for(it=m_displayArrays.begin(); it!=m_displayArrays.end(); it++) { 00299 (*it)->m_users--; 00300 if((*it)->m_users == 0) 00301 delete *it; 00302 } 00303 m_displayArrays.clear(); 00304 // then hook to the base ones 00305 RAS_MeshMaterial *mmat = m_mesh->GetMeshMaterial(m_bucket->GetPolyMaterial()); 00306 if (mmat && mmat->m_baseslot) { 00307 m_displayArrays = mmat->m_baseslot->m_displayArrays; 00308 for(it=m_displayArrays.begin(); it!=m_displayArrays.end(); it++) { 00309 (*it)->m_users++; 00310 } 00311 } 00312 } 00313 else { 00314 // no sharing 00315 // we create local copy of RAS_DisplayArray when we have a deformer: 00316 // this way we can avoid conflict between the vertex cache of duplicates 00317 for(it=m_displayArrays.begin(); it!=m_displayArrays.end(); it++) { 00318 if (deformer->UseVertexArray()) { 00319 // the deformer makes use of vertex array, make sure we have our local copy 00320 if ((*it)->m_users > 1) { 00321 // only need to copy if there are other users 00322 // note that this is the usual case as vertex arrays are held by the material base slot 00323 RAS_DisplayArray *newarray = new RAS_DisplayArray(*(*it)); 00324 newarray->m_users = 1; 00325 (*it)->m_users--; 00326 *it = newarray; 00327 } 00328 } else { 00329 // the deformer is not using vertex array (Modifier), release them 00330 (*it)->m_users--; 00331 if((*it)->m_users == 0) 00332 delete *it; 00333 } 00334 } 00335 if (!deformer->UseVertexArray()) { 00336 m_displayArrays.clear(); 00337 m_startarray = 0; 00338 m_startvertex = 0; 00339 m_startindex = 0; 00340 m_endarray = 0; 00341 m_endvertex = 0; 00342 m_endindex = 0; 00343 } 00344 } 00345 } 00346 m_pDeformer = deformer; 00347 } 00348 00349 bool RAS_MeshSlot::Equals(RAS_MeshSlot *target) 00350 { 00351 if(!m_OpenGLMatrix || !target->m_OpenGLMatrix) 00352 return false; 00353 if(m_pDeformer || target->m_pDeformer) 00354 return false; 00355 if(m_bVisible != target->m_bVisible) 00356 return false; 00357 if(m_bObjectColor != target->m_bObjectColor) 00358 return false; 00359 if(m_bObjectColor && !(m_RGBAcolor == target->m_RGBAcolor)) 00360 return false; 00361 00362 return true; 00363 } 00364 00365 bool RAS_MeshSlot::Join(RAS_MeshSlot *target, MT_Scalar distance) 00366 { 00367 RAS_DisplayArrayList::iterator it; 00368 iterator mit; 00369 size_t i; 00370 00371 // verify if we can join 00372 if(m_joinSlot || m_joinedSlots.size() || target->m_joinSlot) 00373 return false; 00374 00375 if(!Equals(target)) 00376 return false; 00377 00378 MT_Vector3 co(&m_OpenGLMatrix[12]); 00379 MT_Vector3 targetco(&target->m_OpenGLMatrix[12]); 00380 00381 if((co - targetco).length() > distance) 00382 return false; 00383 00384 MT_Matrix4x4 mat(m_OpenGLMatrix); 00385 MT_Matrix4x4 targetmat(target->m_OpenGLMatrix); 00386 targetmat.invert(); 00387 00388 MT_Matrix4x4 transform = targetmat*mat; 00389 00390 // m_mesh, clientobj 00391 m_joinSlot = target; 00392 m_joinInvTransform = transform; 00393 m_joinInvTransform.invert(); 00394 target->m_joinedSlots.push_back(this); 00395 00396 MT_Matrix4x4 ntransform = m_joinInvTransform.transposed(); 00397 ntransform[0][3]= ntransform[1][3]= ntransform[2][3]= 0.0f; 00398 00399 for(begin(mit); !end(mit); next(mit)) 00400 for(i=mit.startvertex; i<mit.endvertex; i++) 00401 mit.vertex[i].Transform(transform, ntransform); 00402 00403 /* We know we'll need a list at least this big, reserve in advance */ 00404 target->m_displayArrays.reserve(target->m_displayArrays.size() + m_displayArrays.size()); 00405 00406 for(it=m_displayArrays.begin(); it!=m_displayArrays.end(); it++) { 00407 target->m_displayArrays.push_back(*it); 00408 target->m_endarray++; 00409 target->m_endvertex = target->m_displayArrays.back()->m_vertex.size(); 00410 target->m_endindex = target->m_displayArrays.back()->m_index.size(); 00411 } 00412 00413 if (m_DisplayList) { 00414 m_DisplayList->Release(); 00415 m_DisplayList = NULL; 00416 } 00417 if (target->m_DisplayList) { 00418 target->m_DisplayList->Release(); 00419 target->m_DisplayList = NULL; 00420 } 00421 00422 return true; 00423 #if 0 00424 return false; 00425 #endif 00426 } 00427 00428 bool RAS_MeshSlot::Split(bool force) 00429 { 00430 list<RAS_MeshSlot*>::iterator jit; 00431 RAS_MeshSlot *target = m_joinSlot; 00432 RAS_DisplayArrayList::iterator it, jt; 00433 iterator mit; 00434 size_t i, found0 = 0, found1 = 0; 00435 00436 if(target && (force || !Equals(target))) { 00437 m_joinSlot = NULL; 00438 00439 for(jit=target->m_joinedSlots.begin(); jit!=target->m_joinedSlots.end(); jit++) { 00440 if(*jit == this) { 00441 target->m_joinedSlots.erase(jit); 00442 found0 = 1; 00443 break; 00444 } 00445 } 00446 00447 if(!found0) 00448 abort(); 00449 00450 for(it=m_displayArrays.begin(); it!=m_displayArrays.end(); it++) { 00451 found1 = 0; 00452 for(jt=target->m_displayArrays.begin(); jt!=target->m_displayArrays.end(); jt++) { 00453 if(*jt == *it) { 00454 target->m_displayArrays.erase(jt); 00455 target->m_endarray--; 00456 found1 = 1; 00457 break; 00458 } 00459 } 00460 00461 if(!found1) 00462 abort(); 00463 } 00464 00465 if(target->m_displayArrays.size()) { 00466 target->m_endvertex = target->m_displayArrays.back()->m_vertex.size(); 00467 target->m_endindex = target->m_displayArrays.back()->m_index.size(); 00468 } 00469 else { 00470 target->m_endvertex = 0; 00471 target->m_endindex = 0; 00472 } 00473 00474 MT_Matrix4x4 ntransform = m_joinInvTransform.inverse().transposed(); 00475 ntransform[0][3]= ntransform[1][3]= ntransform[2][3]= 0.0f; 00476 00477 for(begin(mit); !end(mit); next(mit)) 00478 for(i=mit.startvertex; i<mit.endvertex; i++) 00479 mit.vertex[i].Transform(m_joinInvTransform, ntransform); 00480 00481 if (target->m_DisplayList) { 00482 target->m_DisplayList->Release(); 00483 target->m_DisplayList = NULL; 00484 } 00485 00486 return true; 00487 } 00488 00489 return false; 00490 } 00491 00492 00493 #ifdef USE_SPLIT 00494 bool RAS_MeshSlot::IsCulled() 00495 { 00496 if(m_joinSlot) 00497 return true; 00498 if(!m_bCulled) 00499 return false; 00500 list<RAS_MeshSlot*>::iterator it; 00501 for(it=m_joinedSlots.begin(); it!=m_joinedSlots.end(); it++) 00502 if(!(*it)->m_bCulled) 00503 return false; 00504 return true; 00505 } 00506 #endif 00507 00508 /* material bucket sorting */ 00509 00510 struct RAS_MaterialBucket::less 00511 { 00512 bool operator()(const RAS_MaterialBucket* x, const RAS_MaterialBucket* y) const 00513 { 00514 return *x->GetPolyMaterial() < *y->GetPolyMaterial(); 00515 } 00516 }; 00517 00518 /* material bucket */ 00519 00520 RAS_MaterialBucket::RAS_MaterialBucket(RAS_IPolyMaterial* mat) 00521 { 00522 m_material = mat; 00523 } 00524 00525 RAS_MaterialBucket::~RAS_MaterialBucket() 00526 { 00527 } 00528 00529 RAS_IPolyMaterial* RAS_MaterialBucket::GetPolyMaterial() const 00530 { 00531 return m_material; 00532 } 00533 00534 bool RAS_MaterialBucket::IsAlpha() const 00535 { 00536 return (m_material->IsAlpha()); 00537 } 00538 00539 bool RAS_MaterialBucket::IsZSort() const 00540 { 00541 return (m_material->IsZSort()); 00542 } 00543 00544 RAS_MeshSlot* RAS_MaterialBucket::AddMesh(int numverts) 00545 { 00546 RAS_MeshSlot *ms; 00547 00548 m_meshSlots.push_back(RAS_MeshSlot()); 00549 00550 ms = &m_meshSlots.back(); 00551 ms->init(this, numverts); 00552 00553 return ms; 00554 } 00555 00556 RAS_MeshSlot* RAS_MaterialBucket::CopyMesh(RAS_MeshSlot *ms) 00557 { 00558 m_meshSlots.push_back(RAS_MeshSlot(*ms)); 00559 00560 return &m_meshSlots.back(); 00561 } 00562 00563 void RAS_MaterialBucket::RemoveMesh(RAS_MeshSlot* ms) 00564 { 00565 list<RAS_MeshSlot>::iterator it; 00566 00567 for(it=m_meshSlots.begin(); it!=m_meshSlots.end(); it++) { 00568 if(&*it == ms) { 00569 m_meshSlots.erase(it); 00570 return; 00571 } 00572 } 00573 } 00574 00575 list<RAS_MeshSlot>::iterator RAS_MaterialBucket::msBegin() 00576 { 00577 return m_meshSlots.begin(); 00578 } 00579 00580 list<RAS_MeshSlot>::iterator RAS_MaterialBucket::msEnd() 00581 { 00582 return m_meshSlots.end(); 00583 } 00584 00585 bool RAS_MaterialBucket::ActivateMaterial(const MT_Transform& cameratrans, RAS_IRasterizer* rasty, 00586 RAS_IRenderTools *rendertools) 00587 { 00588 bool uselights; 00589 00590 if(!rasty->SetMaterial(*m_material)) 00591 return false; 00592 00593 uselights= m_material->UsesLighting(rasty); 00594 rendertools->ProcessLighting(rasty, uselights, cameratrans); 00595 00596 return true; 00597 } 00598 00599 void RAS_MaterialBucket::RenderMeshSlot(const MT_Transform& cameratrans, RAS_IRasterizer* rasty, 00600 RAS_IRenderTools* rendertools, RAS_MeshSlot &ms) 00601 { 00602 m_material->ActivateMeshSlot(ms, rasty); 00603 00604 if (ms.m_pDeformer) 00605 { 00606 ms.m_pDeformer->Apply(m_material); 00607 // KX_ReInstanceShapeFromMesh(ms.m_mesh); // Recompute the physics mesh. (Can't call KX_* from RAS_) 00608 } 00609 00610 if(IsZSort() && rasty->GetDrawingMode() >= RAS_IRasterizer::KX_SOLID) 00611 ms.m_mesh->SortPolygons(ms, cameratrans*MT_Transform(ms.m_OpenGLMatrix)); 00612 00613 rendertools->PushMatrix(); 00614 if (!ms.m_pDeformer || !ms.m_pDeformer->SkipVertexTransform()) 00615 { 00616 rendertools->applyTransform(rasty,ms.m_OpenGLMatrix,m_material->GetDrawingMode()); 00617 } 00618 00619 if(rasty->QueryLists()) 00620 if(ms.m_DisplayList) 00621 ms.m_DisplayList->SetModified(ms.m_mesh->MeshModified()); 00622 00623 // verify if we can use display list, not for deformed object, and 00624 // also don't create a new display list when drawing shadow buffers, 00625 // then it won't have texture coordinates for actual drawing. also 00626 // for zsort we can't make a display list, since the polygon order 00627 // changes all the time. 00628 if(ms.m_pDeformer && ms.m_pDeformer->IsDynamic()) 00629 ms.m_bDisplayList = false; 00630 else if(!ms.m_DisplayList && rasty->GetDrawingMode() == RAS_IRasterizer::KX_SHADOW) 00631 ms.m_bDisplayList = false; 00632 else if (IsZSort()) 00633 ms.m_bDisplayList = false; 00634 else if(m_material->UsesObjectColor() && ms.m_bObjectColor) 00635 ms.m_bDisplayList = false; 00636 else 00637 ms.m_bDisplayList = true; 00638 00639 // for text drawing using faces 00640 if (m_material->GetDrawingMode() & RAS_IRasterizer::RAS_RENDER_3DPOLYGON_TEXT) 00641 rasty->IndexPrimitives_3DText(ms, m_material, rendertools); 00642 // for multitexturing 00643 else if((m_material->GetFlag() & (RAS_MULTITEX|RAS_BLENDERGLSL))) 00644 rasty->IndexPrimitivesMulti(ms); 00645 // use normal IndexPrimitives 00646 else 00647 rasty->IndexPrimitives(ms); 00648 00649 if(rasty->QueryLists()) 00650 if(ms.m_DisplayList) 00651 ms.m_mesh->SetMeshModified(false); 00652 00653 rendertools->PopMatrix(); 00654 } 00655 00656 void RAS_MaterialBucket::Optimize(MT_Scalar distance) 00657 { 00658 /* TODO: still have to check before this works correct: 00659 * - lightlayer, frontface, text, billboard 00660 * - make it work with physics */ 00661 00662 #if 0 00663 list<RAS_MeshSlot>::iterator it; 00664 list<RAS_MeshSlot>::iterator jt; 00665 00666 // greed joining on all following buckets 00667 for(it=m_meshSlots.begin(); it!=m_meshSlots.end(); it++) 00668 for(jt=it, jt++; jt!=m_meshSlots.end(); jt++) 00669 jt->Join(&*it, distance); 00670 #endif 00671 } 00672