plvScene.cc
上传用户:kellyonhid
上传日期:2013-10-12
资源大小:932k
文件大小:18k
源码类别:

3D图形编程

开发平台:

Visual C++

  1. #include <iostream.h>
  2. #include <ctype.h>
  3. #include "plvGlobals.h"
  4. #include "RigidScan.h"
  5. #include "togl.h"
  6. #include "DisplayMesh.h"
  7. #include "plvMeshCmds.h"
  8. #include "VertexFilter.h"
  9. #include "plvDraw.h"
  10. #include "ToglCache.h"
  11. #include "ScanFactory.h"
  12. #include "GlobalReg.h"
  13. #include "Progress.h"
  14. #include <sys/types.h>
  15. #include <sys/stat.h>
  16. #include "plvScene.h"
  17. //
  18. // Initialize the scene.
  19. //
  20. Scene::Scene (Tcl_Interp* _interp)
  21. {
  22.   bbox.clear();
  23.   globalReg = NULL;
  24.   slowPolyCount = 0;
  25.   resOverride = resDefault;
  26.   interp = _interp;
  27.   globalReg = new GlobalReg;
  28. }
  29. Scene::~Scene()
  30. {
  31.   delete globalReg;
  32.   freeMeshes();
  33. }
  34. //
  35. // Add a mesh set to the scene
  36. //
  37. static int TclDictionaryCompare (const char* left, const char* right);
  38. class dmNameSort: public binary_function<DisplayableMesh*,
  39.                           DisplayableMesh*,
  40.                           bool>
  41. {
  42. public:
  43.   bool operator() (DisplayableMesh* d1, DisplayableMesh* d2);
  44. };
  45. DisplayableMesh*
  46. Scene::addMeshSet(RigidScan* scan, bool bRecenterCameraOK, char* nameToUse)
  47. {
  48.   // Prepare bbox if necessary
  49.   if (meshSets.size() == 0) {
  50.     bbox.clear();
  51.   }
  52.   // Add the mesh set
  53.   DisplayableMesh* dm = new DisplayableRealMesh (scan, nameToUse);
  54.   addMeshInternal (dm);
  55.   // Update the bbox
  56.   computeBBox();
  57.   // reset the view trackball according to where this mesh is.
  58.   // don't do it if meshes are already loaded and autoCenter is off.
  59.   if (bRecenterCameraOK) {
  60.     char* pszAutoRecenter = Tcl_GetVar (interp, "autoCenterCamera",
  61. TCL_GLOBAL_ONLY);
  62.     if (meshSets.size() == 1 || !strcmp (pszAutoRecenter, "1"))
  63.       centerCamera();
  64.   }
  65.   return dm;
  66. }
  67. void
  68. Scene::addMeshInternal (DisplayableMesh* dm)
  69. {
  70.   DisplayableMesh** pos = lower_bound (meshSets.begin(), meshSets.end(),
  71.        dm, dmNameSort());
  72.   meshSets.insert (pos, dm);
  73.   AddMeshSetToHash (dm);
  74. }
  75. //static int MeshSetHashDelete(char *name);
  76. DisplayableMesh**
  77. Scene::findSceneMesh (DisplayableMesh* mesh)
  78. {
  79.   for (DisplayableMesh** dm = meshSets.begin();
  80.        dm < meshSets.end();
  81.        dm++) {
  82.     if (*dm == mesh) {
  83.       return dm;
  84.     }
  85.   }
  86.   return NULL;
  87. }
  88. void
  89. Scene::deleteMeshSet (DisplayableMesh* deadMesh)
  90. {
  91.   MeshSetHashDelete((char*)deadMesh->getName());
  92.   cerr << "Deleting mesh " << deadMesh->getName() << endl;
  93.   DisplayableMesh** dm = findSceneMesh (deadMesh);
  94.   assert (dm != NULL);
  95.  
  96.   DisplayableMesh **mydm = find (g_hilitedScans.begin(), g_hilitedScans.end(), *dm);
  97.   if (mydm != g_hilitedScans.end()) g_hilitedScans.erase(mydm);
  98.   
  99.   RigidScan* rs = (*dm)->getMeshData();
  100.   // remove the pointers from the globalreg data structure
  101.   globalReg->deleteAllPairs(rs);
  102.   delete *dm;
  103.   delete rs;
  104.   meshSets.erase (dm);
  105.   // Update the bbox
  106.   computeBBox();
  107. }
  108. bool
  109. Scene::renameMeshSet (DisplayableMesh* mesh, const char* nameToUse)
  110. {
  111.   // TODO
  112.   return false;
  113. }
  114. void
  115. Scene::freeMeshes() 
  116. {
  117.   while (meshSets.size()) {
  118.     RigidScan* rs = meshSets.back()->getMeshData();  // save...
  119.     delete meshSets.back();    // while we delete the DisplayableMesh
  120.     delete rs;                 // before deleting RigidScan it references
  121.     meshSets.pop_back();
  122.   }
  123. }
  124. //
  125. // Compute the bounding box for the scene
  126. //
  127. static void ReallyUpdateBbox (ClientData data)
  128. {
  129.   Scene* scene = (Scene*)data;
  130.   scene->computeBBox (Scene::sync);
  131. }
  132. static Tcl_TimerToken _nextBboxUpdate = 0;
  133. void
  134. Scene::computeBBox (bboxSyncMode synchronous)
  135. {
  136.   // avoid multiple repeated calls to recompute
  137.   if (_nextBboxUpdate) {
  138.     Tcl_DeleteTimerHandler (_nextBboxUpdate);
  139.     _nextBboxUpdate = 0;
  140.   } else if (synchronous == flush) {
  141.     // nothing to do, this just forces existing callbacks to happen now
  142.     return;
  143.   }
  144.   
  145.   // if not explicitly forced to happen now, wait and see if
  146.   // we can batch together multiple calls
  147.   if (synchronous == async) {
  148.     _nextBboxUpdate = Tcl_CreateTimerHandler (20, ReallyUpdateBbox,
  149.       (ClientData)this);
  150.     return;
  151.   }
  152.   // Initialize bbox
  153.   bbox.clear();
  154.   // Loop through meshes
  155.   for (int k = 0; k < meshSets.size(); k++) {
  156.     DisplayableMesh *meshDisp = meshSets[k];
  157.     // Don't include meshes that are not being drawn or have 
  158.     // no vertices (bounding sphere has radius zero)
  159.     if (!meshDisp->getVisible()) continue;
  160.     // Update scene bounding box with mesh set bounding box that 
  161.     // fits around mesh set bounding sphere - conservative
  162.     bbox.add(meshDisp->getMeshData()->worldBbox());
  163.   }
  164.   tbView->setClippingPlanes(worldCenter(), sceneRadius());
  165.   DisplayCache::InvalidateToglCache (toglCurrent);
  166. }
  167. // position camera so entire scene is visible in center of view
  168. void
  169. Scene::centerCamera (void)
  170. {
  171.   computeBBox (sync);
  172.   Pnt3 camera(0,0,sceneRadius()*3);
  173.   Pnt3 up(0,1,0);
  174.   Pnt3 object (worldCenter());
  175.   camera += object;
  176.   tbView->setup(camera, object, up, sceneRadius());
  177. #if 0
  178.   printf ("Resetting camera trackball to obj=%g,%g,%g; "
  179.   "camera=%g,%g,%g; radius=%gn",
  180.   object[0], object[1], object[2], camera[0], camera[1],
  181.   camera[2], theScene->sceneRadius());
  182. #endif
  183. }
  184. void 
  185. Scene::flipNormals()
  186. {
  187.   for (int k = 0; k < meshSets.size(); k++) {
  188.     meshSets[k]->getMeshData()->flipNormals();
  189.   }
  190. }
  191. void
  192. Scene::setMeshResolution (int res)
  193. {
  194.   // if tempHigh, tempLow, implement as override -- individual mesh
  195.   // resolutions don't change.  Otherwise, change individual mesh
  196.   // resolutions and leave override alone.
  197.   int newOverride = resDefault;
  198.   bool bInvisibleToo = 
  199.     atoi (Tcl_GetVar (interp, 
  200.       "selectResIncludesInvisible",  TCL_GLOBAL_ONLY));
  201.   switch (res) {
  202.   case resTempLow:  // temp lo/hi -- just change override
  203.   case resTempHigh:
  204.     newOverride = res;
  205.     break;
  206.   default:
  207.     {
  208.       DisplayableMesh** dmi;
  209.       Progress progress (meshSets.size(), "Change resolution", true);
  210.       for (dmi = meshSets.begin(); dmi != meshSets.end(); dmi++) {
  211. if (bInvisibleToo || (*dmi)->getVisible()) {
  212.   switch (res) {
  213.   case resLow:
  214.     (*dmi)->getMeshData()->select_coarsest();
  215.     break;
  216.     
  217.   case resHigh:
  218.     (*dmi)->getMeshData()->select_finest();
  219.     break;
  220.     
  221.   case resNextLow:
  222.     (*dmi)->getMeshData()->select_coarser();
  223.     break;
  224.     
  225.   case resNextHigh:
  226.     (*dmi)->getMeshData()->select_finer();
  227.     break;
  228.   }
  229. }
  230. if (!progress.updateInc())
  231.   break;
  232.       }
  233.     }
  234.     break;
  235.   }
  236.   // then, if override actually changed, rebuild caches
  237.   if (res != resOverride) {
  238.     invalidateDisplayCaches();
  239.   }
  240.   if (newOverride != resOverride) {
  241.     resOverride = newOverride;
  242.   }
  243.   Tcl_Eval (interp, "buildUI_ResizeAllResBars");
  244. }
  245. int
  246. Scene::getMeshResolution (void)
  247. {
  248.   return resOverride;
  249. }
  250. void
  251. Scene::invalidateDisplayCaches (void)
  252. {
  253.   for (int k = 0; k < meshSets.size(); k++) {
  254.     meshSets[k]->invalidateCachedData();
  255.   }
  256. }
  257. bool
  258. Scene::wantMeshBBox (DisplayableMesh* mesh)
  259. {
  260.   return (theRenderParams->boundSelection && mesh == theActiveScan);
  261. }
  262. void
  263. Scene::setHome (void)
  264. {
  265.   homePos = *tbView;
  266. }
  267. void
  268. Scene::goHome (void)
  269. {
  270.   *tbView = homePos;
  271.   computeBBox();
  272. }
  273. float
  274. Scene::sceneRadius (void) const
  275. {
  276.   return bbox.diag() * .5;
  277. }
  278. Pnt3
  279. Scene::worldCenter (void)
  280. {
  281.   return bbox.center();
  282. }
  283. void
  284. Scene::flattenCameraXform (void)
  285. {
  286.   // applies the camera transformation to each mesh's transformation,
  287.   // then resets the camera transformation to identity
  288.   float q[4], t[3];
  289.   tbView->getXform (q, t);
  290.   Xform<float> viewer;
  291.   viewer.fromQuaternion (q, t[0], t[1], t[2]);
  292.   
  293.   for (int i = 0; i < meshSets.size(); i++) {
  294.     RigidScan* scan = meshSets[i]->getMeshData();
  295.     scan->setXform (viewer * scan->getXform());
  296.   }
  297.   computeBBox();
  298.   centerCamera();
  299. }
  300. bool
  301. Scene::meshes_written_stripped (void)
  302. {
  303.   char* mode = Tcl_GetVar (interp, "meshWriteStrips", TCL_GLOBAL_ONLY);
  304.   if (!strcmp (mode, "always"))
  305.     return true;
  306.   else if (!strcmp (mode, "never"))
  307.     return false;
  308.   else
  309.     return theRenderParams->useTstrips;
  310. }
  311. int
  312. Scene::getSlowPolyCount (void)
  313. {
  314.   return slowPolyCount;
  315. }
  316. void
  317. Scene::setSlowPolyCount (int count)
  318. {
  319.   slowPolyCount = count;
  320. }
  321. static Tcl_HashTable meshSetHash;
  322. static int mesh_set_hash_initted = 0;
  323. int 
  324. MeshSetHashDelete(char *name)
  325. {
  326.   if(!mesh_set_hash_initted)
  327.     return 1;
  328.   Tcl_HashEntry *ent = Tcl_FindHashEntry(&meshSetHash, name);
  329.   if (ent) Tcl_DeleteHashEntry(ent);
  330.   return 1;
  331. }
  332. int 
  333. AddMeshSetToHash(DisplayableMesh* mesh)
  334. {
  335.   if(!mesh_set_hash_initted) {
  336.     Tcl_InitHashTable(&meshSetHash, TCL_STRING_KEYS);
  337.     mesh_set_hash_initted = 1;
  338.   }
  339.   int isnew;
  340.   Tcl_HashEntry *ent = Tcl_CreateHashEntry(&meshSetHash, 
  341.    mesh->getName(), &isnew);
  342.   if(!isnew)
  343.     return(mesh == (DisplayableMesh *)Tcl_GetHashValue(ent) ? 0 : 1);
  344.   Tcl_SetHashValue(ent, (ClientData)mesh);
  345.   return 1;
  346. }
  347. DisplayableMesh *
  348. FindMeshDisplayInfo(const char *name)
  349. {
  350.   if(!mesh_set_hash_initted)
  351.     return NULL;
  352.   Tcl_HashEntry *ent = Tcl_FindHashEntry(&meshSetHash, name);
  353.   if (ent == NULL)
  354.     return NULL;
  355.   return (DisplayableMesh *)Tcl_GetHashValue(ent);
  356. }
  357. DisplayableMesh *
  358. FindMeshDisplayInfo(RigidScan* scan)
  359. {
  360.   for (DisplayableMesh** pdm = theScene->meshSets.begin();
  361.        pdm < theScene->meshSets.end();
  362.        pdm++) {
  363.     if ((*pdm)->getMeshData() == scan)
  364.       return *pdm;
  365.   }
  366.   return NULL;
  367. }
  368. // FindMeshDisplayInfo(RigidScan* scan) only searches the meshSets
  369. // vector which only returns top level ("root") matches - if we need a 
  370. // match with leaves as well, then we need to look at the hashtable 
  371. // instead because it contains all the meshes in the scene, including
  372. // non-roots.
  373. DisplayableMesh *
  374. GetMeshForRigidScan (RigidScan *scan) 
  375. {
  376.   DisplayableMesh *dm;
  377.   Tcl_HashSearch searchPtr;
  378.   Tcl_HashEntry *entry = Tcl_FirstHashEntry(&meshSetHash, &searchPtr);
  379.   while (entry) {
  380.     dm = (DisplayableMesh *)Tcl_GetHashValue(entry); 
  381.     assert (dm);
  382.     if (dm->getMeshData() == scan) {
  383.       cerr << "Returning " << dm->getName() << endl;
  384.       return dm;
  385.     }
  386.     entry = Tcl_NextHashEntry(&searchPtr);
  387.   }
  388.   return NULL; 
  389. }
  390. const char *
  391. GetTbObjName(TbObj *tb)
  392. {
  393.   RigidScan* obj = dynamic_cast<RigidScan*> (tb);
  394.   if (!obj) return NULL;
  395.   DisplayableMesh* dm = FindMeshDisplayInfo (obj);
  396.   if (!dm) return NULL;
  397.   return dm->getName();
  398. }
  399. bool dmNameSort::operator() (DisplayableMesh* d1, DisplayableMesh* d2)
  400. {
  401.     int res = TclDictionaryCompare (d1->getName(), d2->getName());
  402.     return (res < 0);
  403. }
  404. static int TclDictionaryCompare (const char* left, const char* right)
  405. {
  406.     // copied from DictionaryCompare, Tcl 8.0 source, generic/tclCmdIL.c
  407.     // so that we get the same behavior as the interface code that
  408.     // displays mesh lists using [lsort -dictionary]
  409.     int diff, zeros;
  410.     int secondaryDiff = 0;
  411. #define UCHAR(c) ((unsigned char) (c))
  412.     while (1) {
  413.         if (isdigit(UCHAR(*right)) && isdigit(UCHAR(*left))) {
  414.             /*
  415.              * There are decimal numbers embedded in the two
  416.              * strings.  Compare them as numbers, rather than
  417.              * strings.  If one number has more leading zeros than
  418.              * the other, the number with more leading zeros sorts
  419.              * later, but only as a secondary choice.
  420.              */
  421.   /* Broken TCL code alert -- magi -- Tcl 8's DictionaryCompare
  422.      routine breaks on strings containing embedded 0's, because
  423.      it takes the whole string of zeros as "leading" and skips
  424.      over them as if they weren't there.  Instead, it should
  425.      skip over leading zeros, but if it comes to a non-digit,
  426.      sort using the final (or only) zero, instead of skipping
  427.      ALL zeros.  Fix incorporated here.
  428.   */
  429.             zeros = 0;
  430. #if 1       // Tcl's way
  431.             while ((*right == '0') && (*(right + 1) != '')) {
  432.                 right++;
  433.                 zeros--;
  434.             }
  435.             while ((*left == '0') && (*(left + 1) != '')) {
  436.                 left++;
  437.                 zeros++;
  438.             }
  439. #else       // magi's way
  440.             while ((right[0] == '0') && (isdigit(right[1]))) {
  441.                 right++;
  442.                 zeros--;
  443.             }
  444.             while ((left[0] == '0') && (isdigit(left[1]))) {
  445.                 left++;
  446.                 zeros++;
  447.             }
  448. #endif
  449.             if (secondaryDiff == 0) {
  450.                 secondaryDiff = zeros;
  451.             }
  452.             /*
  453.              * The code below compares the numbers in the two
  454.              * strings without ever converting them to integers.  It
  455.              * does this by first comparing the lengths of the
  456.              * numbers and then comparing the digit values.
  457.              */
  458.             diff = 0;
  459.             while (1) {
  460.                 if (diff == 0) {
  461.                     diff = *left - *right;
  462.                 }
  463.                 right++;
  464.                 left++;
  465.                 if (!isdigit(UCHAR(*right))) {
  466.                     if (isdigit(UCHAR(*left))) {
  467.                         return 1;
  468.                     } else {
  469.                         /*
  470.                          * The two numbers have the same length. See
  471.                          * if their values are different.
  472.                          */
  473.                         if (diff != 0) {
  474.                             return diff;
  475.                         }
  476.                         break;
  477.                     }
  478.                 } else if (!isdigit(UCHAR(*left))) {
  479.                     return -1;
  480.                 }
  481.             }
  482.             continue;
  483.         }
  484.         diff = *left - *right;
  485.         if (diff) {
  486.             if (isupper(UCHAR(*left)) && islower(UCHAR(*right))) {
  487.                 diff = tolower(*left) - *right;
  488.                 if (diff) {
  489.                     return diff;
  490.                 } else if (secondaryDiff == 0) {
  491.                     secondaryDiff = -1;
  492.                 }
  493.             } else if (isupper(UCHAR(*right)) && islower(UCHAR(*left))) {
  494.                 diff = *left - tolower(UCHAR(*right));
  495.                 if (diff) {
  496.                     return diff;
  497.                 } else if (secondaryDiff == 0) {
  498.                     secondaryDiff = 1;
  499.                 }
  500.             } else {
  501.                 return diff;
  502.             }
  503.         }
  504.         if (*left == 0) {
  505.             break;
  506.         }
  507.         left++;
  508.         right++;
  509.     }
  510.     if (diff == 0) {
  511.         diff = secondaryDiff;
  512.     }
  513.     return diff;
  514. }
  515. bool
  516. Scene::readSessionFile (char* sessionName)
  517. {
  518.   ifstream file (sessionName);
  519.   if (file.fail())
  520.     return false;
  521.   char name [PATH_MAX];
  522.   Pnt3 min, max;
  523.   while (!file.eof()) {
  524.     file.get (name, PATH_MAX, ':');
  525.     file.ignore (10, ':');
  526.     file >> min;
  527.     file.ignore (10, '*');
  528.     file >> max;
  529.     file >> ws;
  530.     if (file.fail())
  531.       return false;
  532.     RigidScan* rs = CreateScanFromBbox (name, min, max);
  533.     DisplayableMesh* dm = addMeshSet (rs);
  534.     Tcl_VarEval (g_tclInterp, "addMeshToWindow ",
  535.  dm->getName(), " 0", NULL);
  536.   }
  537.   return true;
  538. }
  539. bool
  540. Scene::writeSessionFile (char* sessionName)
  541. {
  542.   ofstream file (sessionName);
  543.   if (file.fail())
  544.     return false;
  545.   for (DisplayableMesh** pdm = meshSets.begin();
  546.        pdm < meshSets.end(); pdm++) {
  547.     RigidScan* scan = (*pdm)->getMeshData();
  548.     Bbox bbox = scan->localBbox();
  549.     file << scan->get_name() << ": "
  550.  << bbox.min() << " * " << bbox.max()
  551.  << endl;
  552.     if (file.fail())
  553.       return false;
  554.   }
  555.   return true;
  556. }
  557. bool
  558. Scene::setScanLoadedStatus (DisplayableMesh* dm, bool load)
  559. {
  560.   RigidScan* scan = NULL;
  561.   RigidScan* oldScan = dm->getMeshData();
  562.   const crope& name = oldScan->get_name();
  563.   if (load) {
  564.     scan = CreateScanFromFile (name);
  565.   } else {
  566.     Bbox bbox = oldScan->localBbox();
  567.     scan = CreateScanFromBbox (name, bbox.min(), bbox.max());
  568.   }
  569.   if (!scan)
  570.     return false;
  571.   scan->setXform (oldScan->getXform());
  572.   dm->resetMeshData (scan);
  573.   delete oldScan;
  574.   return true;
  575. }
  576. class sceneMeshSort: public binary_function<DisplayableMesh*,
  577.      DisplayableMesh*,
  578.      bool>
  579. {
  580. public:
  581.   bool operator() (DisplayableMesh* d1, DisplayableMesh* d2);
  582. };
  583. static vector<Scene::sortCriteria>* s_pCriteria;
  584. static bool s_bDictSort;
  585. void
  586. Scene::sortSceneMeshes (vector<DisplayableMesh*>& sortedList,
  587. vector<sortCriteria>& criteria,
  588. bool bDictionarySort,
  589. bool bListInvisible)
  590. {
  591.   if (bListInvisible) {
  592.     sortedList = meshSets;
  593.   } else {
  594.     sortedList.reserve (meshSets.size());
  595.     for (int i = 0; i < meshSets.size(); i++)
  596.       if (meshSets[i]->getVisible())
  597. sortedList.push_back (meshSets[i]);
  598.   }
  599.   s_pCriteria = &criteria;
  600.   s_bDictSort = bDictionarySort;
  601.   sort (sortedList.begin(), sortedList.end(), sceneMeshSort());
  602. }
  603. bool
  604. sceneMeshSort::operator() (DisplayableMesh* d1, DisplayableMesh* d2)
  605. {
  606.   int res;
  607.   for (int i = 0; i < s_pCriteria->size(); i++) {
  608.     switch (s_pCriteria->operator[](i)) {
  609.     case Scene::name:
  610.       if (s_bDictSort)
  611. res = TclDictionaryCompare (d1->getName(), d2->getName());
  612.       else
  613. res = strcmp (d1->getName(), d2->getName());
  614.       if (res == 0)
  615. break;
  616.       //cerr << d1->getName() << " and " << d2->getName()
  617.       //   << ": decided by name" << endl;
  618.       return (res < 0);
  619.     case Scene::date:
  620.       {
  621. struct stat s1, s2;
  622. stat (d1->getMeshData()->get_name().c_str(), &s1);
  623. stat (d2->getMeshData()->get_name().c_str(), &s2);
  624. #ifdef sgi
  625. if (s1.st_mtim.tv_sec == s2.st_mtim.tv_sec)
  626. #else
  627. if (s1.st_mtime == s2.st_mtime)
  628. #endif
  629.   break;
  630. //cerr << d1->getName() << " and " << d2->getName()
  631. //     << ": decided by date" << endl;
  632. #ifdef sgi
  633. if (s1.st_mtim.tv_sec < s2.st_mtim.tv_sec)
  634. #else
  635. if (s1.st_mtime < s2.st_mtime)
  636. #endif
  637.   return true;
  638. return false;
  639.       }
  640.     case Scene::visibility:
  641.       if (d1->getVisible() == d2->getVisible())
  642. break;
  643.       //cerr << d1->getName() << " and " << d2->getName()
  644.       //   << ": decided by vis" << endl;
  645.       if (d1->getVisible())
  646. return true;  // d1 vis, d2 not, so d1 wins
  647.       else
  648. return false;
  649.     case Scene::polycount:
  650.       {
  651. vector<ResolutionCtrl::res_info> ri;
  652. d1->getMeshData()->existing_resolutions (ri);
  653. int count1 = ri[0].abs_resolution;
  654. ri.clear();
  655. d2->getMeshData()->existing_resolutions (ri);
  656. int count2 = ri[0].abs_resolution;
  657. if (count1 == count2)
  658.   break;
  659. return (count1 < count2);
  660.       }
  661.     case Scene::loaded:
  662.       {
  663. bool loaded1 = isScanLoaded (d1->getMeshData());
  664. bool loaded2 = isScanLoaded (d2->getMeshData());
  665. if (loaded1 == loaded2)
  666.   break;
  667. //cerr << d1->getName() << " and " << d2->getName()
  668. //     << ": decided by load" << endl;
  669. if (loaded1)
  670.   return true;
  671. else
  672.   return false;
  673.       }
  674.     }
  675.   }
  676.   return false;
  677. }