changed: gcc8 base update
[opensg.git] / Source / System / Cluster / Window / SortLast / OSGSortLastWindow.cpp
blob07666a8073fee877106d5af07adf62ae393c919b
1 /*---------------------------------------------------------------------------*\
2 * OpenSG *
3 * *
4 * *
5 * Copyright (C) 2000-2002 by the OpenSG Forum *
6 * *
7 * www.opensg.org *
8 * *
9 * contact: dirk@opensg.org, gerrit.voss@vossg.org, jbehr@zgdv.de *
10 * *
11 \*---------------------------------------------------------------------------*/
12 /*---------------------------------------------------------------------------*\
13 * License *
14 * *
15 * This library is free software; you can redistribute it and/or modify it *
16 * under the terms of the GNU Library General Public License as published *
17 * by the Free Software Foundation, version 2. *
18 * *
19 * This library is distributed in the hope that it will be useful, but *
20 * WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
22 * Library General Public License for more details. *
23 * *
24 * You should have received a copy of the GNU Library General Public *
25 * License along with this library; if not, write to the Free Software *
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
27 * *
28 \*---------------------------------------------------------------------------*/
29 /*---------------------------------------------------------------------------*\
30 * Changes *
31 * *
32 * *
33 * *
34 * *
35 * *
36 * *
37 \*---------------------------------------------------------------------------*/
39 //---------------------------------------------------------------------------
40 // Includes
41 //---------------------------------------------------------------------------
43 #include <stdlib.h>
44 #include <stdio.h>
46 #include "OSGViewport.h"
47 #include "OSGStereoBufferViewport.h"
48 #include "OSGGeometry.h"
49 #include "OSGGeoPositionsFields.h"
50 #include "OSGGroup.h"
51 #include "OSGCamera.h"
52 #include "OSGProxyGroup.h"
53 #include "OSGMaterialGroup.h"
54 #include "OSGRemoteAspect.h"
55 #include "OSGImageComposer.h"
56 #include "OSGStatisticsForeground.h"
57 #include "OSGClusterNetwork.h"
58 #include "OSGDrawEnv.h"
59 #include "OSGRenderActionBase.h"
60 #include "OSGMaterial.h"
62 #include "OSGSortLastWindow.h"
64 OSG_USING_NAMESPACE
66 /*! \class OSG::SortLastWindow
67 Cluster rendering configuration for sort first image composition
70 /*----------------------------- static grouping functions -----------------*/
72 void SortLastWindow::buildGroups(void)
74 UInt32 v = 0;
75 DrawableListT drawables;
76 UInt32 groupCount = 0;
77 bool rebuild = false;
79 // check for new nodes.
80 FieldContainerFactoryBase *fcFactory = FieldContainerFactory::the();
82 FieldContainer *fcPtr = NULL;
84 ChangeList::ChangedStoreConstIt createdI;
86 ChangeList *changeList = OSG::Thread::getCurrentChangeList();
88 for(createdI = changeList->beginCreated();
89 createdI != changeList->endCreated();
90 createdI++)
92 UInt32 uiId = (*createdI)->uiContainerId;
94 fcPtr = fcFactory->getContainer(uiId);
96 if(fcPtr != NULL && dynamic_cast<Node *>(fcPtr) != NULL)
97 rebuild = true;
100 // is rebuild neccessary ?
101 if(!rebuild && getMFGroupNodes()->size())
102 return;
104 groupCount = getMFServers()->size32();
106 if(getComposer() != NULL)
108 groupCount = getComposer()->getUsableServers();
109 if(getComposer()->getClientRendering())
110 groupCount++;
113 // build groups for all viewports
115 clearGroupNodes();
116 editMFGroupLengths()->clear();
118 for(v = 0; v < getMFPort()->size(); ++v)
120 Viewport *vp = getPort(v);
121 Node *root = vp->getRoot();
123 drawables.clear();
125 collectDrawables(root, drawables);
127 if(drawables.size())
128 splitDrawables(drawables, groupCount, false);
132 /*----------------------------- server methods ----------------------------*/
134 /*! init composition
136 void SortLastWindow::serverInit(Window *serverWindow,
137 UInt32 id)
139 // create default composer
140 if(getComposer() == NULL)
143 FieldContainerPtr fcPtr =
144 FieldContainerFactory::the()->createFieldContainer("BinarySwapComposer");
145 setComposer(ImageComposerPtr::dcast(fcPtr));
149 if(getComposer() != NULL)
151 // init composer
152 ClusterWindow *clusterWindow = this;
154 getComposer()->setup(false,
156 serverWindow,
157 clusterWindow);
159 getComposer()->open();
163 /*! update server window
166 #ifdef OSG_OLD_RENDER_ACTION
167 void SortLastWindow::serverRender(Window *serverWindow,
168 UInt32 id,
169 DrawActionBase *action )
171 Viewport *serverPort = NULL;
172 Viewport *clientPort = NULL;
173 UInt32 sv = 0;
174 UInt32 cv = 0;
175 UInt32 regionStart = 0;
177 // duplicate viewports
178 for(cv = 0, sv = 0; cv < getPort().size(); ++cv)
180 clientPort = getPort()[cv];
182 if(serverWindow->getPort().size() <= sv)
184 // create new port
185 serverPort = Viewport::create();
187 serverWindow->addPort(serverPort);
189 else
191 serverPort = serverWindow->getPort()[sv];
194 // duplicate values
196 if(getWidth() && getHeight())
198 serverPort->setSize(clientPort->getPixelLeft (),
199 clientPort->getPixelBottom(),
200 clientPort->getPixelRight (),
201 clientPort->getPixelTop ());
203 else
205 serverPort->setSize(0,0,0,0);
208 serverPort->setCamera (clientPort->getCamera ());
209 serverPort->setRoot (clientPort->getRoot ());
210 serverPort->setBackground(clientPort->getBackground());
212 // ignore statistics foreground
213 serverPort->clearForegrounds();
215 for(UInt32 f = 0 ; f < serverPort->getForegrounds().size(); ++f)
217 Foreground *fg = clientPort->getForegrounds()[f];
219 StatisticsForeground *sfg =
220 dynamic_cast<StatisticsForeground *>(fg);
222 if(sfg == NULL)
224 serverPort->addForeground(fg);
228 serverPort->setTravMask(clientPort->getTravMask());
230 sv++;
233 // remove unused ports
234 while(serverWindow->getPort().size() > sv)
236 serverWindow->subPort(sv);
239 // setup visible nodes
240 setupNodes(id);
242 // render the viewports
243 serverWindow->activate();
244 serverWindow->frameInit();
246 action->setWindow(serverWindow);
248 if(getComposer() != NULL)
249 getComposer()->startFrame();
251 for(sv = 0; sv < serverWindow->getPort().size(); ++sv)
253 Viewport *vp = serverWindow->getPort()[sv];
254 Node *root = vp->getRoot();
256 if(getComposer() != NULL)
257 getComposer()->startViewport(vp);
259 // render
260 vp->render(action);
262 // compose single viewport
263 if(getComposer() != NULL)
264 getComposer()->composeViewport(vp);
267 // compose whole window
268 if(getComposer() != NULL)
269 getComposer()->composeWindow();
271 #endif
273 void SortLastWindow::serverRender(Window *serverWindow,
274 UInt32 id,
275 RenderActionBase *action )
277 ViewportUnrecPtr serverPort = NULL;
278 Viewport *clientPort = NULL;
279 UInt32 sv = 0;
280 UInt32 cv = 0;
282 // duplicate viewports
283 for(cv = 0, sv = 0; cv < getMFPort()->size(); ++cv)
285 clientPort = getPort(cv);
287 if(serverWindow->getMFPort()->size() <= sv)
289 // create new port
290 serverPort = Viewport::create();
292 serverWindow->addPort(serverPort);
294 else
296 serverPort = serverWindow->getPort(sv);
299 // duplicate values
301 if(getWidth() && getHeight())
303 serverPort->setSize(clientPort->calcPixelLeft (),
304 clientPort->calcPixelBottom(),
305 clientPort->calcPixelRight (),
306 clientPort->calcPixelTop ());
308 else
310 serverPort->setSize(0,0,0,0);
313 serverPort->setCamera (clientPort->getCamera ());
314 serverPort->setRoot (clientPort->getRoot ());
315 serverPort->setBackground(clientPort->getBackground());
317 // ignore statistics foreground
318 serverPort->clearForegrounds();
320 for(UInt32 f = 0 ; f < serverPort->getMFForegrounds()->size(); ++f)
322 Foreground *fg = clientPort->getForegrounds(f);
324 StatisticsForeground *sfg =
325 dynamic_cast<StatisticsForeground *>(fg);
327 if(sfg == NULL)
329 serverPort->addForeground(fg);
333 serverPort->setTravMask(clientPort->getTravMask());
335 sv++;
338 // remove unused ports
339 while(serverWindow->getMFPort()->size() > sv)
341 serverWindow->subPort(sv);
344 // setup visible nodes
345 setupNodes(id);
347 // render the viewports
348 serverWindow->activate();
349 serverWindow->frameInit();
351 action->setWindow(serverWindow);
353 if(getComposer() != NULL)
354 getComposer()->startFrame();
356 for(sv = 0; sv < serverWindow->getMFPort()->size(); ++sv)
358 Viewport *vp = serverWindow->getPort(sv);
360 if(getComposer() != NULL)
361 getComposer()->startViewport(vp);
363 // render
364 vp->render(action);
366 // compose single viewport
367 if(getComposer() != NULL)
368 getComposer()->composeViewport(vp);
371 // compose whole window
372 if(getComposer() != NULL)
373 getComposer()->composeWindow();
376 /*! swap
378 void SortLastWindow::serverSwap(Window *window,
379 UInt32 id )
381 if(getComposer() == NULL)
383 Connection *connection = getNetwork()->getMainConnection();
384 // tell client that we are finish
385 connection->signal();
386 // wait for swap
387 connection->wait();
389 Inherited::serverSwap(window,id);
392 /*----------------------------- client methods ----------------------------*/
394 /*! read server cababilities
396 void SortLastWindow::clientInit( void )
398 getNetwork()->getAspect()->addFieldFilter(Node::getClassType().getId(),
399 Node::VolumeFieldMask);
400 getNetwork()->getAspect()->addFieldFilter(Node::getClassType().getId(),
401 Node::TravMaskFieldMask);
403 // create default composer
404 if(getComposer() == NULL)
407 FieldContainerPtr fcPtr =
408 FieldContainerFactory::the()->createFieldContainer("BinarySwapComposer");
409 setComposer(ImageComposerPtr::dcast(fcPtr));
412 if(getComposer() != NULL)
414 SortLastWindow *clusterWindow(this);
415 getComposer()->setup(true,
416 getMFServers()->size32(),
417 getClientWindow(),
418 clusterWindow);
419 getComposer()->open();
420 // build node groups
421 buildGroups();
425 /*! client frame init
427 void SortLastWindow::clientPreSync( void )
429 if(getClientWindow() != NULL)
431 UInt32 width =getClientWindow()->getWidth();
432 UInt32 height=getClientWindow()->getHeight();
433 if(width == 0)
434 width = 2;
435 if(height == 0)
436 height = 2;
437 if(width != getWidth() ||
438 height != getHeight())
440 setSize(width,height);
444 Inherited::clientPreSync();
446 // rebuild node groups
447 buildGroups();
450 #ifdef OSG_OLD_RENDER_ACTION
451 /*! client rendering
453 void SortLastWindow::clientRender(DrawActionBase *action)
455 UInt32 p;
456 UInt32 groupId = getServers().size();
457 UInt32 l,b,r,t;
458 UInt32 front,back;
459 SortLastWindow *clusterWindow(this);
461 if(getServers().size())
463 Connection *srcConnection=
464 getNetwork()->getConnection(groupId);
466 if(getClientWindow() != NULL)
468 setupNodes(groupId);
470 getClientWindow()->activate();
471 getClientWindow()->frameInit();
473 action->setWindow(getClientWindow());
475 if(getComposer() != NULL)
476 getComposer()->startFrame();
478 DrawEnv oEnv;
480 oEnv.setWindow(action->getWindow());
482 // render all viewports
483 for(p = 0; p < getPort().size() ; ++p)
485 Viewport *vp=getPort()[p];
486 if(getComposer() != NULL)
488 getComposer()->startViewport(vp);
490 action->setCamera (vp->getCamera ());
491 action->setBackground(vp->getBackground());
492 action->setViewport (vp );
493 action->setTravMask (vp->getTravMask ());
495 action->apply(vp->getRoot());
497 for(UInt16 i=0; i < vp->getForegrounds().size(); i++)
499 if(dynamic_cast<StatisticsForeground *>(
500 vp->getForegrounds(i)) == NULL)
502 vp->getForegrounds(i)->draw(&oEnv, vp);
506 getComposer()->composeViewport(vp);
508 for(UInt16 i=0; i < vp->getForegrounds().size(); i++)
510 if(dynamic_cast<StatisticsForeground *>(
511 vp->getForegrounds(i)) != NULL)
513 vp->getForegrounds(i)->draw(&oEnv, vp);
518 else
520 vp->render(action);
524 // compose whole window
525 if(getComposer() != NULL)
526 getComposer()->composeWindow();
530 #endif
532 void SortLastWindow::clientRender(RenderActionBase *action)
534 UInt32 p;
535 UInt32 groupId = getMFServers()->size32();
537 if(getMFServers()->size())
539 if(getClientWindow() != NULL)
541 setupNodes(groupId);
543 getClientWindow()->activate();
544 getClientWindow()->frameInit();
546 action->setWindow(getClientWindow());
548 if(getComposer() != NULL)
549 getComposer()->startFrame();
551 DrawEnv oEnv;
553 oEnv.setWindow(action->getWindow());
555 // render all viewports
556 for(p = 0; p < getMFPort()->size() ; ++p)
558 Viewport *vp=getPort(p);
560 oEnv.setViewportDimension(vp->calcPixelLeft (),
561 vp->calcPixelBottom (),
562 vp->calcPixelRight (),
563 vp->calcPixelTop (),
564 vp->calcIsFullWindow());
565 if(getComposer() != NULL)
567 getComposer()->startViewport(vp);
569 action->setCamera (vp->getCamera ());
570 action->setBackground(vp->getBackground());
571 action->setViewarea (vp );
572 action->setTravMask (vp->getTravMask ());
574 action->apply(vp->getRoot());
576 for(UInt16 i=0; i < vp->getMFForegrounds()->size(); i++)
578 if(dynamic_cast<StatisticsForeground *>(
579 vp->getForegrounds(i)) == NULL)
581 vp->getForegrounds(i)->draw(&oEnv);
585 getComposer()->composeViewport(vp);
587 for(UInt16 i=0; i < vp->getMFForegrounds()->size(); i++)
589 if(dynamic_cast<StatisticsForeground *>(
590 vp->getForegrounds(i)) != NULL)
592 vp->getForegrounds(i)->draw(&oEnv);
597 else
599 vp->render(action);
603 // compose whole window
604 if(getComposer() != NULL)
605 getComposer()->composeWindow();
610 /*! swap
612 void SortLastWindow::clientSwap( void )
614 if(getComposer() == NULL)
616 Connection *connection=getNetwork()->getMainConnection();
617 // wait for all servers to finish
618 connection->wait();
619 // initiate swap
620 connection->signal();
622 Inherited::clientSwap();
625 /*----------------------- constructors & destructors ----------------------*/
627 SortLastWindow::SortLastWindow(void) :
628 Inherited()
632 SortLastWindow::SortLastWindow(const SortLastWindow &source) :
633 Inherited(source)
637 /*! close composer if there is any
639 SortLastWindow::~SortLastWindow(void)
643 /*----------------------------- class specific ----------------------------*/
645 void SortLastWindow::initMethod(InitPhase ePhase)
647 Inherited::initMethod(ePhase);
650 /*! changed field. Handle new groups
653 void SortLastWindow::changed(ConstFieldMaskArg whichField,
654 UInt32 origin,
655 BitVector details)
657 if(whichField & GroupNodesFieldMask)
658 setGroupsChanged(true);
660 Inherited::changed(whichField, origin, details);
663 void SortLastWindow::dump( UInt32 ,
664 const BitVector ) const
666 SLOG << "Dump SortLastWindow NI" << std::endl;
669 /*! Collext all drawable nodes
672 void SortLastWindow::collectDrawables(Node * const node,
673 DrawableListT &drawables)
675 Material *mat = NULL;
676 NodeCore *core = node->getCore();
678 if(core != NULL)
680 // handle material groups
681 MaterialGroup *matGrp = dynamic_cast<MaterialGroup *>(core);
683 if(matGrp != NULL)
685 mat = matGrp->getMaterial();
687 // ignore transparent material groups
688 if(mat != NULL && mat->isTransparent())
689 return;
692 // handle geometries
693 Geometry *geo = dynamic_cast<Geometry *>(core);
695 if(geo != NULL)
697 mat = geo->getMaterial();
698 // ignore transparent materials
700 if(mat == NULL || mat->isTransparent() == false)
702 DrawableInfo drawableInfo;
704 drawableInfo.node = node;
706 // get transformed volume
707 node->updateVolume();
709 BoxVolume volume;
710 node->getWorldVolume(volume);
712 // get min,max
713 volume.getBounds(drawableInfo.bMin, drawableInfo.bMax);
715 // num of indices
716 drawableInfo.load = 0;
718 GeoIntegralProperty *indicesPtr =
719 geo->getIndex(Geometry::PositionsIndex);
721 if(indicesPtr != NULL)
722 drawableInfo.load = indicesPtr->size();
724 // put to list
725 drawables.push_back(drawableInfo);
729 // handle poxy groups
730 ProxyGroup *proxy = dynamic_cast<ProxyGroup *>(core);
732 if(proxy != NULL)
734 DrawableInfo drawableInfo;
736 drawableInfo.node = node;
738 // get transformed volume
739 node->updateVolume();
741 BoxVolume volume;
742 node->getWorldVolume(volume);
744 // get min,max
745 volume.getBounds(drawableInfo.bMin, drawableInfo.bMax);
747 // num of indices
748 drawableInfo.load = proxy->getIndices();
750 // put to list
751 drawables.push_back(drawableInfo);
755 MFUnrecChildNodePtr::const_iterator nI;
757 for( nI = node->getMFChildren()->begin();
758 nI != node->getMFChildren()->end();
759 ++nI)
761 collectDrawables(*nI, drawables);
765 /*! Split drawables in as many groups as we have servers
766 * Try to get clustered nodes
769 void SortLastWindow::splitDrawables(DrawableListT &src,
770 UInt32 groups,
771 bool cut)
773 BoxVolume vol;
774 // Real32 srcLoad=0;
775 Real32 dst1Load = 0;
776 Real32 dst2Load = 0;
777 DrawableListT::iterator dI;
778 UInt32 dIFront = 0;
779 UInt32 dIBack = 0;
780 UInt32 axis = 0;
781 Vec3f size;
782 DrawableListT dst1;
783 DrawableListT dst2;
784 UInt32 groups1 = 0;
785 UInt32 groups2 = 0;
787 // no group
788 if(groups == 0)
789 return;
791 // only one group
792 if(groups == 1)
794 editMFGroupLengths()->push_back(UInt32(src.size()));
796 for(dI = src.begin() ; dI != src.end() ; ++dI)
798 pushToGroupNodes(dI->node);
799 // srcLoad+=dI->load;
801 // printf("load:%f\n",srcLoad);
802 return;
805 groups1 = groups / 2;
806 groups2 = groups - groups1;
808 // collect all load and get summed volume
809 for(dI = src.begin() ; dI != src.end() ; ++dI)
811 vol.extendBy(dI->bMin);
812 vol.extendBy(dI->bMax);
815 // get longes axis
816 vol.getSize(size);
818 if(size[0] > size[1])
820 if(size[0] > size[2])
821 axis=0;
822 else
823 axis=2;
825 else
827 if(size[1] > size[2])
828 axis=1;
829 else
830 axis=2;
833 // sort by volume
834 if(axis == 0)
836 std::sort(src.begin(),src.end(), DrawableInfo::MaxXOrder());
838 else
840 if(axis == 1)
841 std::sort(src.begin(),src.end(), DrawableInfo::MaxYOrder());
842 else
843 std::sort(src.begin(),src.end(), DrawableInfo::MaxZOrder());
846 // split group
847 if(src.size())
849 dIFront = 0;
850 dIBack = UInt32(src.size()) - 1;
853 // printf("f %d b %d\n",dIFront,dIBack);
854 if(dst2Load < dst1Load)
856 dst2.push_back(src[dIBack]);
858 dst2Load += src[dIBack].load*groups/Real32(groups2);
860 dIBack--;
862 else
864 dst1.push_back(src[dIFront]);
866 dst1Load += src[dIFront].load*groups/Real32(groups1);
868 dIFront++;
870 } while(dIFront <= dIBack);
872 // recourse
873 splitDrawables(dst1, groups1, cut);
874 splitDrawables(dst2, groups2, cut);
877 void SortLastWindow::setupNodes(UInt32 groupId)
879 UInt32 v = 0;
880 Node *root = NULL;
881 UInt32 nI = 0;
882 UInt32 gnI = 0;
883 UInt32 gI = 0;
884 UInt32 group = 0;
885 UInt32 groupCount = 0;
886 UInt32 usableServers = getMFServers()->size32();
888 if(!getGroupsChanged())
889 return;
891 // client and no client rendering
892 if(getMFServers()->size() == groupId &&
893 (getComposer() == NULL ||
894 !getComposer()->getClientRendering()))
896 for(nI = 0 ; nI < getMFGroupNodes()->size() ; ++nI)
898 if(getGroupNodes(nI)->getTravMask())
900 getGroupNodes(nI)->setTravMask(0);
901 getGroupNodes(nI)->invalidateVolume();
905 return;
908 if(getComposer() != NULL)
909 usableServers = getComposer()->getUsableServers();
911 // server but not usable, then invalidate all nodes
912 if((getMFServers()->size() > groupId && usableServers <= groupId))
914 for(v = 0; v < getMFPort()->size(); ++v)
916 root = getPort(v)->getRoot();
918 root->setTravMask(0);
919 root->invalidateVolume();
922 setGroupsChanged(false);
923 return;
926 groupCount = usableServers;
928 if(getComposer() != NULL)
930 groupCount = getComposer()->getUsableServers();
932 if(getComposer()->getClientRendering())
934 groupCount++;
938 if(getMFServers()->size() == groupId)
939 groupId = usableServers;
941 // setup nodes
942 for(nI = 0,gnI = 0,gI = 0,group = 0 ; nI < getMFGroupNodes()->size() ; ++nI)
944 while(nI >= gnI)
946 gnI += getGroupLengths(group);
947 gI++;
948 group = gI % groupCount;
950 if(group == groupId)
952 if(getGroupNodes(nI)->getTravMask() !=
953 TypeTraits<UInt32>::getMax())
955 getGroupNodes(nI)->setTravMask(TypeTraits<UInt32>::getMax());
956 getGroupNodes(nI)->invalidateVolume();
959 else
961 if(getGroupNodes(nI)->getTravMask())
963 getGroupNodes(nI)->setTravMask(0);
964 getGroupNodes(nI)->invalidateVolume();
968 getGroupNodes(nI)->updateVolume();
971 setGroupsChanged(false);
974 /*------------------------------------------------------------------------*/
975 /* constructor / destructor */
977 /*! constructor
979 SortLastWindow::DrawableInfo::DrawableInfo():
980 node(NULL),
981 bMin( ),
982 bMax( ),
983 load(0.f )
987 /*! copy constructor
990 SortLastWindow::DrawableInfo::DrawableInfo(const DrawableInfo &source) :
991 node(source.node),
992 bMin(source.bMin),
993 bMax(source.bMax),
994 load(source.load)
998 /*! assignment
1000 const SortLastWindow::DrawableInfo &SortLastWindow::DrawableInfo::operator =(
1001 const DrawableInfo &source)
1003 node = source.node;
1004 bMin = source.bMin;
1005 bMax = source.bMax;
1006 load = source.load;
1008 return *this;
1011 /*! compare max x in bounding volume
1014 bool SortLastWindow::DrawableInfo::MaxXOrder::operator()
1015 (const DrawableInfo &a, const DrawableInfo &b)
1017 return a.bMax[0] < b.bMax[0];
1020 /*! compare max y in bounding volume
1023 bool SortLastWindow::DrawableInfo::MaxYOrder::operator()
1024 (const DrawableInfo &a, const DrawableInfo &b)
1026 return a.bMax[1] < b.bMax[1];
1029 /*! compare max z in bounding volume
1032 bool SortLastWindow::DrawableInfo::MaxZOrder::operator()
1033 (const DrawableInfo &a, const DrawableInfo &b)
1035 return a.bMax[2] < b.bMax[2];