1 #include <osg/ClusterCullingCallback>
2 #include <osgDB/ReadFile>
3 #include <osgDB/WriteFile>
4 #include <string.h>
5 #include <iostream>
6 #include <sstream>
7 #include "PosterPrinter.h"
8 
9 /* PagedLoadingCallback: Callback for loading paged nodes while doing intersecting test */
10 struct PagedLoadingCallback : public osgUtil::IntersectionVisitor::ReadCallback
11 {
readNodeFilePagedLoadingCallback12     virtual osg::Node* readNodeFile( const std::string& filename )
13     {
14         return osgDB::readRefNodeFile( filename ).release();
15     }
16 };
17 static osg::ref_ptr<PagedLoadingCallback> g_pagedLoadingCallback = new PagedLoadingCallback;
18 
19 /* LodCullingCallback: Callback for culling LODs and selecting the highest level */
20 class LodCullingCallback : public osg::NodeCallback
21 {
22 public:
operator ()(osg::Node * node,osg::NodeVisitor * nv)23     virtual void operator()( osg::Node* node, osg::NodeVisitor* nv )
24     {
25         osg::LOD* lod = static_cast<osg::LOD*>(node);
26         if ( lod && lod->getNumChildren()>0 )
27             lod->getChild(lod->getNumChildren()-1)->accept(*nv);
28     }
29 };
30 static osg::ref_ptr<LodCullingCallback> g_lodCullingCallback = new LodCullingCallback;
31 
32 /* PagedCullingCallback: Callback for culling paged nodes and selecting the highest level */
33 class PagedCullingCallback : public osg::NodeCallback
34 {
35 public:
operator ()(osg::Node * node,osg::NodeVisitor * nv)36     virtual void operator()( osg::Node* node, osg::NodeVisitor* nv )
37     {
38         osg::PagedLOD* pagedLOD = static_cast<osg::PagedLOD*>(node);
39         if ( pagedLOD && pagedLOD->getNumChildren()>0 )
40         {
41             unsigned int numChildren = pagedLOD->getNumChildren();
42             bool updateTimeStamp = nv->getVisitorType()==osg::NodeVisitor::CULL_VISITOR;
43             if ( nv->getFrameStamp() && updateTimeStamp )
44             {
45                 double timeStamp = nv->getFrameStamp()?nv->getFrameStamp()->getReferenceTime():0.0;
46                 unsigned int frameNumber = nv->getFrameStamp()?nv->getFrameStamp()->getFrameNumber():0;
47 
48                 pagedLOD->setFrameNumberOfLastTraversal( frameNumber );
49                 pagedLOD->setTimeStamp( numChildren-1, timeStamp );
50                 pagedLOD->setFrameNumber( numChildren-1, frameNumber );
51                 pagedLOD->getChild(numChildren-1)->accept(*nv);
52             }
53 
54             // Request for new child
55             if ( !pagedLOD->getDisableExternalChildrenPaging() &&
56                  nv->getDatabaseRequestHandler() &&
57                  numChildren<pagedLOD->getNumRanges() )
58             {
59                 if ( pagedLOD->getDatabasePath().empty() )
60                 {
61                     nv->getDatabaseRequestHandler()->requestNodeFile(
62                         pagedLOD->getFileName(numChildren), nv->getNodePath(),
63                         1.0, nv->getFrameStamp(),
64                         pagedLOD->getDatabaseRequest(numChildren), pagedLOD->getDatabaseOptions() );
65                 }
66                 else
67                 {
68                     nv->getDatabaseRequestHandler()->requestNodeFile(
69                         pagedLOD->getDatabasePath()+pagedLOD->getFileName(numChildren), nv->getNodePath(),
70                         1.0, nv->getFrameStamp(),
71                         pagedLOD->getDatabaseRequest(numChildren), pagedLOD->getDatabaseOptions() );
72                 }
73             }
74         }
75         //node->traverse(*nv);
76     }
77 };
78 static osg::ref_ptr<PagedCullingCallback> g_pagedCullingCallback = new PagedCullingCallback;
79 
80 /* PosterVisitor: A visitor for adding culling callbacks to newly allocated paged nodes */
PosterVisitor()81 PosterVisitor::PosterVisitor()
82 :   osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
83     _appliedCount(0), _needToApplyCount(0),
84     _addingCallbacks(true)
85 {
86 }
87 
apply(osg::LOD & node)88 void PosterVisitor::apply( osg::LOD& node )
89 {
90     /*if ( !hasCullCallback(node.getCullCallback(), g_lodCullingCallback.get()) )
91     {
92         if ( !node.getName().empty() )
93         {
94             PagedNodeNameSet::iterator itr = _pagedNodeNames.find( node.getName() );
95             if ( itr!=_pagedNodeNames.end() )
96             {
97                 insertCullCallback( node, g_lodCullingCallback.get() );
98                 _appliedCount++;
99             }
100         }
101     }
102     else if ( !_addingCallbacks )
103     {
104         node.removeCullCallback( g_lodCullingCallback.get() );
105         _appliedCount--;
106     }*/
107     traverse( node );
108 }
109 
apply(osg::PagedLOD & node)110 void PosterVisitor::apply( osg::PagedLOD& node )
111 {
112     if ( !hasCullCallback(node.getCullCallback(), g_pagedCullingCallback.get()) )
113     {
114         for ( unsigned int i=0; i<node.getNumFileNames(); ++i )
115         {
116             if ( node.getFileName(i).empty() ) continue;
117 
118             PagedNodeNameSet::iterator itr = _pagedNodeNames.find( node.getFileName(i) );
119             if ( itr!=_pagedNodeNames.end() )
120             {
121                 node.addCullCallback( g_pagedCullingCallback.get() );
122                 _appliedCount++;
123             }
124             break;
125         }
126     }
127     else if ( !_addingCallbacks )
128     {
129         node.removeCullCallback( g_pagedCullingCallback.get() );
130         if ( _appliedCount>0 ) _appliedCount--;
131     }
132     traverse( node );
133 }
134 
135 /* PosterIntersector: A simple polytope intersector for updating pagedLODs in each image-tile */
PosterIntersector(const osg::Polytope & polytope)136 PosterIntersector::PosterIntersector( const osg::Polytope& polytope )
137 :   _intersectionVisitor(0), _parent(0), _polytope(polytope)
138 {}
139 
PosterIntersector(double xMin,double yMin,double xMax,double yMax)140 PosterIntersector::PosterIntersector( double xMin, double yMin, double xMax, double yMax )
141 :   Intersector(osgUtil::Intersector::PROJECTION),
142     _intersectionVisitor(0), _parent(0)
143 {
144     _polytope.add( osg::Plane( 1.0, 0.0, 0.0,-xMin) );
145     _polytope.add( osg::Plane(-1.0, 0.0, 0.0, xMax) );
146     _polytope.add( osg::Plane( 0.0, 1.0, 0.0,-yMin) );
147     _polytope.add( osg::Plane( 0.0,-1.0, 0.0, yMax) );
148 }
149 
clone(osgUtil::IntersectionVisitor & iv)150 osgUtil::Intersector* PosterIntersector::clone( osgUtil::IntersectionVisitor& iv )
151 {
152     osg::Matrix matrix;
153     if ( iv.getProjectionMatrix() ) matrix.preMult( *iv.getProjectionMatrix() );
154     if ( iv.getViewMatrix() ) matrix.preMult( *iv.getViewMatrix() );
155     if ( iv.getModelMatrix() ) matrix.preMult( *iv.getModelMatrix() );
156 
157     osg::Polytope transformedPolytope;
158     transformedPolytope.setAndTransformProvidingInverse( _polytope, matrix );
159 
160     osg::ref_ptr<PosterIntersector> pi = new PosterIntersector( transformedPolytope );
161     pi->_intersectionVisitor = &iv;
162     pi->_parent = this;
163     return pi.release();
164 }
165 
enter(const osg::Node & node)166 bool PosterIntersector::enter( const osg::Node& node )
167 {
168     if ( !node.isCullingActive() ) return true;
169     if ( _polytope.contains(node.getBound()) )
170     {
171         if ( node.getCullCallback() )
172         {
173             const osg::ClusterCullingCallback* cccb =
174                 dynamic_cast<const osg::ClusterCullingCallback*>( node.getCullCallback() );
175             if ( cccb && cccb->cull(_intersectionVisitor, 0, NULL) ) return false;
176         }
177         return true;
178     }
179     return false;
180 }
181 
reset()182 void PosterIntersector::reset()
183 {
184     _intersectionVisitor = NULL;
185     Intersector::reset();
186 }
187 
intersect(osgUtil::IntersectionVisitor & iv,osg::Drawable * drawable)188 void PosterIntersector::intersect( osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable )
189 {
190     if ( !_polytope.contains(drawable->getBoundingBox()) ) return;
191     if ( iv.getDoDummyTraversal() ) return;
192 
193     // Find and collect all paged LODs in the node path
194     osg::NodePath& nodePath = iv.getNodePath();
195     for ( osg::NodePath::iterator itr=nodePath.begin(); itr!=nodePath.end(); ++itr )
196     {
197         osg::PagedLOD* pagedLOD = dynamic_cast<osg::PagedLOD*>(*itr);
198         if ( pagedLOD )
199         {
200             // FIXME: The first non-empty getFileName() is used as the identity of this paged node.
201             // This should work with VPB-generated terrains but maybe unusable with others.
202             for ( unsigned int i=0; i<pagedLOD->getNumFileNames(); ++i )
203             {
204                 if ( pagedLOD->getFileName(i).empty() ) continue;
205                 if ( _parent->_visitor.valid() )
206                     _parent->_visitor->insertName( pagedLOD->getFileName(i) );
207                 break;
208             }
209             continue;
210         }
211 
212         /*osg::LOD* lod = dynamic_cast<osg::LOD*>(*itr);
213         if ( lod )
214         {
215             if ( !lod->getName().empty() && _parent->_visitor.valid() )
216                 _parent->_visitor->insertName( lod->getName() );
217         }*/
218     }
219 }
220 
221 /* PosterPrinter: The implementation class of high-res rendering */
PosterPrinter()222 PosterPrinter::PosterPrinter():
223     _outputTiles(false), _outputTileExt("bmp"),
224     _isRunning(false), _isFinishing(false), _lastBindingFrame(0),
225     _currentRow(0), _currentColumn(0),
226     _camera(0), _finalPoster(0)
227 {
228     _intersector = new PosterIntersector(-1.0, -1.0, 1.0, 1.0);
229     _visitor = new PosterVisitor;
230     _intersector->setPosterVisitor( _visitor.get() );
231 }
232 
init(const osg::Camera * camera)233 void PosterPrinter::init( const osg::Camera* camera )
234 {
235     if ( _camera.valid() )
236         init( camera->getViewMatrix(), camera->getProjectionMatrix() );
237 }
238 
init(const osg::Matrixd & view,const osg::Matrixd & proj)239 void PosterPrinter::init( const osg::Matrixd& view, const osg::Matrixd& proj )
240 {
241     if ( _isRunning ) return;
242     _images.clear();
243     _visitor->clearNames();
244     _tileRows = (int)(_posterSize.y() / _tileSize.y());
245     _tileColumns = (int)(_posterSize.x() / _tileSize.x());
246     _currentRow = 0;
247     _currentColumn = 0;
248     _currentViewMatrix = view;
249     _currentProjectionMatrix = proj;
250     _lastBindingFrame = 0;
251     _isRunning = true;
252     _isFinishing = false;
253 }
254 
frame(const osg::FrameStamp * fs,osg::Node * node)255 void PosterPrinter::frame( const osg::FrameStamp* fs, osg::Node* node )
256 {
257     // Add cull callbacks to all existing paged nodes,
258     // and advance frame when all callbacks are dispatched.
259     if ( addCullCallbacks(fs, node) )
260         return;
261 
262     if ( _isFinishing )
263     {
264         if ( (fs->getFrameNumber()-_lastBindingFrame)>2 )
265         {
266             // Record images and the final poster
267             recordImages();
268             if ( _finalPoster.valid() )
269             {
270                 std::cout << "Writing final result to file..." << std::endl;
271                 osgDB::writeImageFile( *_finalPoster, _outputPosterName );
272             }
273 
274             // Release all cull callbacks to free unused paged nodes
275             removeCullCallbacks( node );
276             _visitor->clearNames();
277 
278             _isFinishing = false;
279             std::cout << "Recording images finished." << std::endl;
280         }
281     }
282 
283     if ( _isRunning )
284     {
285         // Every "copy-to-image" process seems to be finished in 2 frames.
286         // So record them and dispatch camera to next tiles.
287         if ( (fs->getFrameNumber()-_lastBindingFrame)>2 )
288         {
289             // Record images and unref them to free memory
290             recordImages();
291 
292             // Release all cull callbacks to free unused paged nodes
293             removeCullCallbacks( node );
294             _visitor->clearNames();
295 
296             if ( _camera.valid() )
297             {
298                 std::cout << "Binding sub-camera " << _currentRow << "_" << _currentColumn
299                           << " to image..." << std::endl;
300                 bindCameraToImage( _camera.get(), _currentRow, _currentColumn );
301                 if ( _currentColumn<_tileColumns-1 )
302                 {
303                     _currentColumn++;
304                 }
305                 else
306                 {
307                     if ( _currentRow<_tileRows-1 )
308                     {
309                         _currentRow++;
310                         _currentColumn = 0;
311                     }
312                     else
313                     {
314                         _isRunning = false;
315                         _isFinishing = true;
316                     }
317                 }
318             }
319             _lastBindingFrame = fs->getFrameNumber();
320         }
321     }
322 }
323 
addCullCallbacks(const osg::FrameStamp * fs,osg::Node * node)324 bool PosterPrinter::addCullCallbacks( const osg::FrameStamp* fs, osg::Node* node )
325 {
326     if ( !_visitor->inQueue() || done() )
327         return false;
328 
329     _visitor->setAddingCallbacks( true );
330     _camera->accept( *_visitor );
331     _lastBindingFrame = fs->getFrameNumber();
332 
333     std::cout << "Dispatching callbacks to paged nodes... "
334               << _visitor->inQueue() << std::endl;
335     return true;
336 }
337 
removeCullCallbacks(osg::Node * node)338 void PosterPrinter::removeCullCallbacks( osg::Node* node )
339 {
340     _visitor->setAddingCallbacks( false );
341     _camera->accept( *_visitor );
342 }
343 
bindCameraToImage(osg::Camera * camera,int row,int col)344 void PosterPrinter::bindCameraToImage( osg::Camera* camera, int row, int col )
345 {
346     std::stringstream stream;
347     stream << "image_" << row << "_" << col;
348 
349     osg::ref_ptr<osg::Image> image = new osg::Image;
350     image->setName( stream.str() );
351     image->allocateImage( (int)_tileSize.x(), (int)_tileSize.y(), 1, GL_RGBA, GL_UNSIGNED_BYTE );
352     _images[TilePosition(row,col)] = image.get();
353 
354     // Calculate projection matrix offset of each tile
355     osg::Matrix offsetMatrix =
356         osg::Matrix::scale(_tileColumns, _tileRows, 1.0) *
357         osg::Matrix::translate(_tileColumns-1-2*col, _tileRows-1-2*row, 0.0);
358     camera->setViewMatrix( _currentViewMatrix );
359     camera->setProjectionMatrix( _currentProjectionMatrix * offsetMatrix );
360 
361     // Check intersections between the image-tile box and the model
362     osgUtil::IntersectionVisitor iv( _intersector.get() );
363     iv.setReadCallback( g_pagedLoadingCallback.get() );
364     _intersector->reset();
365     camera->accept( iv );
366     if ( _intersector->containsIntersections() )
367     {
368         // Apply a cull callback to every paged node obtained, to force the highest level displaying.
369         // This will be done by the PosterVisitor, who already records all the paged nodes.
370     }
371 
372     // Reattach cameras and new allocated images
373     camera->setRenderingCache( NULL );  // FIXME: Uses for reattaching camera with image, maybe inefficient?
374     camera->detach( osg::Camera::COLOR_BUFFER );
375     camera->attach( osg::Camera::COLOR_BUFFER, image.get(), 0, 0 );
376 }
377 
recordImages()378 void PosterPrinter::recordImages()
379 {
380     for ( TileImages::iterator itr=_images.begin(); itr!=_images.end(); ++itr )
381     {
382         osg::Image* image = (itr->second).get();
383         if ( _finalPoster.valid() )
384         {
385             // FIXME: A stupid way to combine tile images to final result. Any better ideas?
386             unsigned int row = itr->first.first, col = itr->first.second;
387             for ( int t=0; t<image->t(); ++t )
388             {
389                 unsigned char* source = image->data( 0, t );
390                 unsigned char* target = _finalPoster->data( col*(int)_tileSize.x(), t + row*(int)_tileSize.y() );
391                 memcpy( target, source, image->s() * 4 * sizeof(unsigned char) );
392             }
393         }
394 
395         if ( _outputTiles )
396             osgDB::writeImageFile( *image, image->getName()+"."+_outputTileExt );
397     }
398     _images.clear();
399 }
400