1 /* OpenSceneGraph example, osgcluster.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to deal
5 * in the Software without restriction, including without limitation the rights
6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 * copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
16 * THE SOFTWARE.
17 */
18
19 #ifdef USE_MEM_CHECK
20 #include <mcheck.h>
21 #endif
22
23 #include <osg/Group>
24 #include <osg/Notify>
25
26 #include <osgDB/Registry>
27 #include <osgDB/ReadFile>
28
29 #include <osgGA/TrackballManipulator>
30 #include <osgGA/StateSetManipulator>
31 #include <osgViewer/Viewer>
32 #include <osgViewer/ViewerEventHandlers>
33
34 #include <osg/Quat>
35 #include <osg/io_utils>
36
37 #include <iostream>
38
39 #if defined (WIN32) && !defined(__CYGWIN__)
40 #include <winsock.h>
41 #endif
42
43 #include "receiver.h"
44 #include "broadcaster.h"
45
46
47 const unsigned int SWAP_BYTES_COMPARE = 0x12345678;
48 class CameraPacket {
49 public:
50
51
CameraPacket()52 CameraPacket():_masterKilled(false)
53 {
54 _byte_order = SWAP_BYTES_COMPARE;
55 }
56
setPacket(const osg::Matrix & matrix,const osg::FrameStamp * frameStamp)57 void setPacket(const osg::Matrix& matrix,const osg::FrameStamp* frameStamp)
58 {
59 _matrix = matrix;
60 if (frameStamp)
61 {
62 _frameStamp = *frameStamp;
63 }
64 }
65
getModelView(osg::Matrix & matrix,float angle_offset=0.0f)66 void getModelView(osg::Matrix& matrix,float angle_offset=0.0f)
67 {
68
69 matrix = _matrix * osg::Matrix::rotate(osg::DegreesToRadians(angle_offset),0.0f,1.0f,0.0f);
70 }
71
72 void readEventQueue(osgViewer::Viewer& viewer);
73
74 void writeEventQueue(osgViewer::Viewer& viewer);
75
setMasterKilled(const bool flag)76 void setMasterKilled(const bool flag) { _masterKilled = flag; }
getMasterKilled() const77 bool getMasterKilled() const { return _masterKilled; }
78
79 unsigned int _byte_order;
80 bool _masterKilled;
81 osg::Matrix _matrix;
82
83 // note don't use a ref_ptr as used elsewhere for FrameStamp
84 // since we don't want to copy the pointer - but the memory.
85 // FrameStamp doesn't have a private destructor to allow
86 // us to do this, even though its a reference counted object.
87 osg::FrameStamp _frameStamp;
88
89 osgGA::EventQueue::Events _events;
90
91 };
92
93 class DataConverter
94 {
95 public:
96
DataConverter(unsigned int numBytes)97 DataConverter(unsigned int numBytes):
98 _startPtr(0),
99 _endPtr(0),
100 _swapBytes(false),
101 _currentPtr(0)
102 {
103 _currentPtr = _startPtr = new char[numBytes];
104 _endPtr = _startPtr+numBytes;
105 _numBytes = numBytes;
106 }
107
108 char* _startPtr;
109 char* _endPtr;
110 unsigned int _numBytes;
111 bool _swapBytes;
112
113 char* _currentPtr;
114
reset()115 void reset()
116 {
117 _currentPtr = _startPtr;
118 }
119
write1(char * ptr)120 inline void write1(char* ptr)
121 {
122 if (_currentPtr+1>=_endPtr) return;
123
124 *(_currentPtr++) = *(ptr);
125 }
126
read1(char * ptr)127 inline void read1(char* ptr)
128 {
129 if (_currentPtr+1>=_endPtr) return;
130
131 *(ptr) = *(_currentPtr++);
132 }
133
write2(char * ptr)134 inline void write2(char* ptr)
135 {
136 if (_currentPtr+2>=_endPtr) return;
137
138 *(_currentPtr++) = *(ptr++);
139 *(_currentPtr++) = *(ptr);
140 }
141
read2(char * ptr)142 inline void read2(char* ptr)
143 {
144 if (_currentPtr+2>=_endPtr) return;
145
146 if (_swapBytes)
147 {
148 *(ptr+1) = *(_currentPtr++);
149 *(ptr) = *(_currentPtr++);
150 }
151 else
152 {
153 *(ptr++) = *(_currentPtr++);
154 *(ptr) = *(_currentPtr++);
155 }
156 }
157
write4(char * ptr)158 inline void write4(char* ptr)
159 {
160 if (_currentPtr+4>=_endPtr) return;
161
162 *(_currentPtr++) = *(ptr++);
163 *(_currentPtr++) = *(ptr++);
164 *(_currentPtr++) = *(ptr++);
165 *(_currentPtr++) = *(ptr);
166 }
167
read4(char * ptr)168 inline void read4(char* ptr)
169 {
170 if (_currentPtr+4>=_endPtr) return;
171
172 if (_swapBytes)
173 {
174 *(ptr+3) = *(_currentPtr++);
175 *(ptr+2) = *(_currentPtr++);
176 *(ptr+1) = *(_currentPtr++);
177 *(ptr) = *(_currentPtr++);
178 }
179 else
180 {
181 *(ptr++) = *(_currentPtr++);
182 *(ptr++) = *(_currentPtr++);
183 *(ptr++) = *(_currentPtr++);
184 *(ptr) = *(_currentPtr++);
185 }
186 }
187
write8(char * ptr)188 inline void write8(char* ptr)
189 {
190 if (_currentPtr+8>=_endPtr) return;
191
192 *(_currentPtr++) = *(ptr++);
193 *(_currentPtr++) = *(ptr++);
194 *(_currentPtr++) = *(ptr++);
195 *(_currentPtr++) = *(ptr++);
196
197 *(_currentPtr++) = *(ptr++);
198 *(_currentPtr++) = *(ptr++);
199 *(_currentPtr++) = *(ptr++);
200 *(_currentPtr++) = *(ptr);
201 }
202
read8(char * ptr)203 inline void read8(char* ptr)
204 {
205 char* endPtr = _currentPtr+8;
206 if (endPtr>=_endPtr) return;
207
208 if (_swapBytes)
209 {
210 *(ptr+7) = *(_currentPtr++);
211 *(ptr+6) = *(_currentPtr++);
212 *(ptr+5) = *(_currentPtr++);
213 *(ptr+4) = *(_currentPtr++);
214
215 *(ptr+3) = *(_currentPtr++);
216 *(ptr+2) = *(_currentPtr++);
217 *(ptr+1) = *(_currentPtr++);
218 *(ptr) = *(_currentPtr++);
219 }
220 else
221 {
222 *(ptr++) = *(_currentPtr++);
223 *(ptr++) = *(_currentPtr++);
224 *(ptr++) = *(_currentPtr++);
225 *(ptr++) = *(_currentPtr++);
226
227 *(ptr++) = *(_currentPtr++);
228 *(ptr++) = *(_currentPtr++);
229 *(ptr++) = *(_currentPtr++);
230 *(ptr) = *(_currentPtr++);
231 }
232 }
233
writeChar(char c)234 inline void writeChar(char c) { write1(&c); }
writeUChar(unsigned char c)235 inline void writeUChar(unsigned char c) { write1((char*)&c); }
writeShort(short c)236 inline void writeShort(short c) { write2((char*)&c); }
writeUShort(unsigned short c)237 inline void writeUShort(unsigned short c) { write2((char*)&c); }
writeInt(int c)238 inline void writeInt(int c) { write4((char*)&c); }
writeUInt(unsigned int c)239 inline void writeUInt(unsigned int c) { write4((char*)&c); }
writeFloat(float c)240 inline void writeFloat(float c) { write4((char*)&c); }
writeDouble(double c)241 inline void writeDouble(double c) { write8((char*)&c); }
242
readChar()243 inline char readChar() { char c=0; read1(&c); return c; }
readUChar()244 inline unsigned char readUChar() { unsigned char c=0; read1((char*)&c); return c; }
readShort()245 inline short readShort() { short c=0; read2((char*)&c); return c; }
readUShort()246 inline unsigned short readUShort() { unsigned short c=0; read2((char*)&c); return c; }
readInt()247 inline int readInt() { int c=0; read4((char*)&c); return c; }
readUInt()248 inline unsigned int readUInt() { unsigned int c=0; read4((char*)&c); return c; }
readFloat()249 inline float readFloat() { float c=0.0f; read4((char*)&c); return c; }
readDouble()250 inline double readDouble() { double c=0.0; read8((char*)&c); return c; }
251
write(const osg::FrameStamp & fs)252 void write(const osg::FrameStamp& fs)
253 {
254 osg::notify(osg::NOTICE)<<"writeFramestamp = "<<fs.getFrameNumber()<<" "<<fs.getReferenceTime()<<std::endl;
255
256 writeUInt(fs.getFrameNumber());
257 writeDouble(fs.getReferenceTime());
258 writeDouble(fs.getSimulationTime());
259 }
260
read(osg::FrameStamp & fs)261 void read(osg::FrameStamp& fs)
262 {
263 fs.setFrameNumber(readUInt());
264 fs.setReferenceTime(readDouble());
265 fs.setSimulationTime(readDouble());
266
267 osg::notify(osg::NOTICE)<<"readFramestamp = "<<fs.getFrameNumber()<<" "<<fs.getReferenceTime()<<std::endl;
268 }
269
write(const osg::Matrix & matrix)270 void write(const osg::Matrix& matrix)
271 {
272 writeDouble(matrix(0,0));
273 writeDouble(matrix(0,1));
274 writeDouble(matrix(0,2));
275 writeDouble(matrix(0,3));
276
277 writeDouble(matrix(1,0));
278 writeDouble(matrix(1,1));
279 writeDouble(matrix(1,2));
280 writeDouble(matrix(1,3));
281
282 writeDouble(matrix(2,0));
283 writeDouble(matrix(2,1));
284 writeDouble(matrix(2,2));
285 writeDouble(matrix(2,3));
286
287 writeDouble(matrix(3,0));
288 writeDouble(matrix(3,1));
289 writeDouble(matrix(3,2));
290 writeDouble(matrix(3,3));
291
292 osg::notify(osg::NOTICE)<<"writeMatrix = "<<matrix<<std::endl;
293
294 }
295
read(osg::Matrix & matrix)296 void read(osg::Matrix& matrix)
297 {
298 matrix(0,0) = readDouble();
299 matrix(0,1) = readDouble();
300 matrix(0,2) = readDouble();
301 matrix(0,3) = readDouble();
302
303 matrix(1,0) = readDouble();
304 matrix(1,1) = readDouble();
305 matrix(1,2) = readDouble();
306 matrix(1,3) = readDouble();
307
308 matrix(2,0) = readDouble();
309 matrix(2,1) = readDouble();
310 matrix(2,2) = readDouble();
311 matrix(2,3) = readDouble();
312
313 matrix(3,0) = readDouble();
314 matrix(3,1) = readDouble();
315 matrix(3,2) = readDouble();
316 matrix(3,3) = readDouble();
317
318 osg::notify(osg::NOTICE)<<"readMatrix = "<<matrix<<std::endl;
319
320 }
321
write(const osgGA::GUIEventAdapter & event)322 void write(const osgGA::GUIEventAdapter& event)
323 {
324 writeUInt(event.getEventType());
325 writeUInt(event.getKey());
326 writeUInt(event.getButton());
327 writeInt(event.getWindowX());
328 writeInt(event.getWindowY());
329 writeUInt(event.getWindowWidth());
330 writeUInt(event.getWindowHeight());
331 writeFloat(event.getXmin());
332 writeFloat(event.getYmin());
333 writeFloat(event.getXmax());
334 writeFloat(event.getYmax());
335 writeFloat(event.getX());
336 writeFloat(event.getY());
337 writeUInt(event.getButtonMask());
338 writeUInt(event.getModKeyMask());
339 writeDouble(event.getTime());
340 }
341
read(osgGA::GUIEventAdapter & event)342 void read(osgGA::GUIEventAdapter& event)
343 {
344 event.setEventType((osgGA::GUIEventAdapter::EventType)readUInt());
345 event.setKey(readUInt());
346 event.setButton(readUInt());
347 int x = readInt();
348 int y = readInt();
349 int width = readUInt();
350 int height = readUInt();
351 event.setWindowRectangle(x,y,width,height);
352 float xmin = readFloat();
353 float ymin = readFloat();
354 float xmax = readFloat();
355 float ymax = readFloat();
356 event.setInputRange(xmin,ymin,xmax,ymax);
357 event.setX(readFloat());
358 event.setY(readFloat());
359 event.setButtonMask(readUInt());
360 event.setModKeyMask(readUInt());
361 event.setTime(readDouble());
362 }
363
write(CameraPacket & cameraPacket)364 void write(CameraPacket& cameraPacket)
365 {
366 writeUInt(cameraPacket._byte_order);
367
368 writeUInt(cameraPacket._masterKilled);
369
370 write(cameraPacket._matrix);
371 write(cameraPacket._frameStamp);
372
373 writeUInt(cameraPacket._events.size());
374 for(osgGA::EventQueue::Events::iterator itr = cameraPacket._events.begin();
375 itr != cameraPacket._events.end();
376 ++itr)
377 {
378 osgGA::GUIEventAdapter* event = (*itr)->asGUIEventAdapter();
379 if (event) write(*event);
380 }
381 }
382
read(CameraPacket & cameraPacket)383 void read(CameraPacket& cameraPacket)
384 {
385 cameraPacket._byte_order = readUInt();
386 if (cameraPacket._byte_order != SWAP_BYTES_COMPARE)
387 {
388 _swapBytes = !_swapBytes;
389 }
390
391 cameraPacket._masterKilled = readUInt()!=0;
392
393 read(cameraPacket._matrix);
394 read(cameraPacket._frameStamp);
395
396 cameraPacket._events.clear();
397 unsigned int numEvents = readUInt();
398 for(unsigned int i=0;i<numEvents;++i)
399 {
400 osgGA::GUIEventAdapter* event = new osgGA::GUIEventAdapter;
401 read(*(event));
402 cameraPacket._events.push_back(event);
403 }
404 }
405 };
406
readEventQueue(osgViewer::Viewer & viewer)407 void CameraPacket::readEventQueue(osgViewer::Viewer& viewer)
408 {
409 _events.clear();
410
411 osgViewer::ViewerBase::Contexts contexts;
412 viewer.getContexts(contexts);
413
414 for(osgViewer::ViewerBase::Contexts::iterator citr =contexts.begin(); citr != contexts.end(); ++citr)
415 {
416 osgGA::EventQueue::Events gw_events;
417
418 osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(*citr);
419 if (gw)
420 {
421 gw->checkEvents();
422 gw->getEventQueue()->copyEvents(gw_events);
423 }
424 _events.insert(_events.end(), gw_events.begin(), gw_events.end());
425 }
426
427 viewer.getEventQueue()->copyEvents(_events);
428
429 osg::notify(osg::INFO)<<"written events = "<<_events.size()<<std::endl;
430 }
431
writeEventQueue(osgViewer::Viewer & viewer)432 void CameraPacket::writeEventQueue(osgViewer::Viewer& viewer)
433 {
434 osg::notify(osg::INFO)<<"received events = "<<_events.size()<<std::endl;
435
436 viewer.getEventQueue()->appendEvents(_events);
437 }
438
439
440
441 enum ViewerMode
442 {
443 STAND_ALONE,
444 SLAVE,
445 MASTER
446 };
447
main(int argc,char ** argv)448 int main( int argc, char **argv )
449 {
450 // use an ArgumentParser object to manage the program arguments.
451 osg::ArgumentParser arguments(&argc,argv);
452
453 // set up the usage document, in case we need to print out how to use this program.
454 arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates how to approach implementation of clustering.");
455 arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
456 arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
457 arguments.getApplicationUsage()->addCommandLineOption("-m","Set viewer to MASTER mode, sending view via packets.");
458 arguments.getApplicationUsage()->addCommandLineOption("-s","Set viewer to SLAVE mode, receiving view via packets.");
459 arguments.getApplicationUsage()->addCommandLineOption("-n <int>","Socket number to transmit packets");
460 arguments.getApplicationUsage()->addCommandLineOption("-f <float>","Field of view of camera");
461 arguments.getApplicationUsage()->addCommandLineOption("-o <float>","Offset angle of camera");
462
463 // construct the viewer.
464 osgViewer::Viewer viewer(arguments);
465
466
467 // read up the osgcluster specific arguments.
468 ViewerMode viewerMode = STAND_ALONE;
469 while (arguments.read("-m")) viewerMode = MASTER;
470 while (arguments.read("-s")) viewerMode = SLAVE;
471
472 unsigned int messageSize = 1024;
473 while (arguments.read("--size", messageSize)) {}
474
475
476 bool readWriteFrame = false;
477 while (arguments.read("--frame")) readWriteFrame = true;
478
479 int socketNumber=8100;
480 while (arguments.read("-n",socketNumber)) ;
481
482 float camera_fov=-1.0f;
483 while (arguments.read("-f",camera_fov))
484 {
485 }
486
487 float camera_offset=45.0f;
488 while (arguments.read("-o",camera_offset)) ;
489
490
491 // if user request help write it out to cout.
492 if (arguments.read("-h") || arguments.read("--help"))
493 {
494 arguments.getApplicationUsage()->write(std::cout);
495 return 1;
496 }
497
498 // objects for managing the broadcasting and receiving of camera packets.
499 Broadcaster bc;
500 Receiver rc;
501
502 std::string ifrName;
503 if (arguments.read("--ifr-name", ifrName)) { bc.setIFRName(ifrName); }
504
505 bc.setPort(static_cast<short int>(socketNumber));
506 rc.setPort(static_cast<short int>(socketNumber));
507
508
509 // any option left unread are converted into errors to write out later.
510 arguments.reportRemainingOptionsAsUnrecognized();
511
512 // report any errors if they have occurred when parsing the program arguments.
513 if (arguments.errors())
514 {
515 arguments.writeErrorMessages(std::cout);
516 return 1;
517 }
518
519 if (arguments.argc()<=1)
520 {
521 arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
522 return 1;
523 }
524
525 // load model.
526 osg::ref_ptr<osg::Node> rootnode = osgDB::readRefNodeFiles(arguments);
527
528 // set the scene to render
529 viewer.setSceneData(rootnode.get());
530
531 if (camera_fov>0.0f)
532 {
533 double fovy, aspectRatio, zNear, zFar;
534 viewer.getCamera()->getProjectionMatrixAsPerspective(fovy, aspectRatio,zNear, zFar);
535
536 double original_fov = atan(tan(osg::DegreesToRadians(fovy)*0.5)*aspectRatio)*2.0;
537 std::cout << "setting lens perspective : original "<<original_fov<<" "<<fovy<<std::endl;
538
539 fovy = atan(tan(osg::DegreesToRadians(camera_fov)*0.5)/aspectRatio)*2.0;
540 viewer.getCamera()->setProjectionMatrixAsPerspective(fovy, aspectRatio,zNear, zFar);
541
542 viewer.getCamera()->getProjectionMatrixAsPerspective(fovy, aspectRatio,zNear, zFar);
543 original_fov = atan(tan(osg::DegreesToRadians(fovy)*0.5)*aspectRatio)*2.0;
544 std::cout << "setting lens perspective : new "<<original_fov<<" "<<fovy<<std::endl;
545 }
546
547 viewer.setCameraManipulator(new osgGA::TrackballManipulator());
548
549 // add the stats handler
550 viewer.addEventHandler(new osgViewer::StatsHandler);
551
552 // add the state manipulator
553 viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
554
555
556 // create the windows and run the threads.
557 viewer.realize();
558
559 osg::ref_ptr<osg::Image> image;
560 if (readWriteFrame)
561 {
562 osgViewer::Viewer::Windows windows;
563 viewer.getWindows(windows);
564
565 if (windows.empty())
566 {
567 return 1;
568 }
569 unsigned int width = windows.front()->getTraits()->width;
570 unsigned int height = windows.front()->getTraits()->height;
571 image = new osg::Image;
572
573 if (windows.front()->getTraits()->alpha)
574 {
575 OSG_NOTICE<<"Setting up RGBA osg::Image, width = "<<width<<", height = "<<height<<std::endl;
576 image->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE);
577 }
578 else
579 {
580 OSG_NOTICE<<"Setting up RGB osg::Image, width = "<<width<<", height = "<<height<<std::endl;
581 image->allocateImage(width, height, 1, GL_RGB, GL_UNSIGNED_BYTE);
582 }
583
584 // if (image->getImageStepInBytes()>messageSize) messageSize = image->getImageStepInBytes();
585 }
586
587 CameraPacket *cp = new CameraPacket;
588
589
590 bool masterKilled = false;
591
592 DataConverter scratchPad(messageSize);
593
594 while( !viewer.done() && !masterKilled )
595 {
596 osg::Timer_t startTick = osg::Timer::instance()->tick();
597
598 viewer.advance();
599
600 // special handling for working as a cluster.
601 switch (viewerMode)
602 {
603 case(MASTER):
604 {
605
606 // take camera zero as the guide.
607 osg::Matrix modelview(viewer.getCamera()->getViewMatrix());
608
609 cp->setPacket(modelview,viewer.getFrameStamp());
610
611 cp->readEventQueue(viewer);
612
613 scratchPad.reset();
614 scratchPad.write(*cp);
615
616 scratchPad.reset();
617 scratchPad.read(*cp);
618
619 bc.setBuffer(scratchPad._startPtr, scratchPad._numBytes);
620
621 bc.sync();
622
623 if (image.valid())
624 {
625 bc.setBuffer(image->data(), image->getImageStepInBytes());
626 bc.sync();
627 }
628
629
630 }
631 break;
632 case(SLAVE):
633 {
634
635 rc.setBuffer(scratchPad._startPtr, scratchPad._numBytes);
636
637 unsigned int readsize = rc.sync();
638
639 if (readsize) std::cout<<std::endl<<"readsize = "<<readsize<<std::endl;
640
641 scratchPad.reset();
642 scratchPad.read(*cp);
643
644 cp->writeEventQueue(viewer);
645
646 if (cp->getMasterKilled())
647 {
648 std::cout << "Received master killed."<<std::endl;
649 // break out of while (!done) loop since we've now want to shut down.
650 masterKilled = true;
651 }
652
653 if (image)
654 {
655 rc.setBuffer(image->data(), image->getImageStepInBytes());
656 readsize = rc.sync();
657 if (readsize) std::cout<<"image readsize = "<<readsize<<std::endl;
658 }
659
660 }
661 break;
662 default:
663 // no need to anything here, just a normal interactive viewer.
664 break;
665 }
666
667 osg::Timer_t endTick = osg::Timer::instance()->tick();
668
669 osg::notify(osg::INFO)<<"Time to do cluster sync "<<osg::Timer::instance()->delta_m(startTick,endTick)<<std::endl;
670
671 // update the scene by traversing it with the update visitor which will
672 // call all node update callbacks and animations.
673 viewer.eventTraversal();
674 viewer.updateTraversal();
675
676 if (viewerMode==SLAVE)
677 {
678 osg::Matrix modelview;
679 cp->getModelView(modelview,camera_offset);
680
681 viewer.getCamera()->setViewMatrix(modelview);
682 }
683
684 // fire off the cull and draw traversals of the scene.
685 if(!masterKilled)
686 viewer.renderingTraversals();
687
688 }
689
690 // if we are master clean up by telling all slaves that we're going down.
691 if (viewerMode==MASTER)
692 {
693 // need to broadcast my death.
694 cp->setPacket(osg::Matrix::identity(),viewer.getFrameStamp());
695 cp->setMasterKilled(true);
696
697 scratchPad.reset();
698 scratchPad.write(*cp);
699
700 bc.setBuffer(scratchPad._startPtr, scratchPad._numBytes);
701 bc.sync();
702
703 std::cout << "Broadcasting death."<<std::endl;
704
705 }
706
707 return 0;
708 }
709