1 /* -*-c++-*- */
2 /* osgEarth - Geospatial SDK for OpenSceneGraph
3  * Copyright 2008-2014 Pelican Mapping
4  * http://osgearth.org
5  *
6  * osgEarth is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>
18  */
19 #include "TerrainCuller"
20 #include "TileNode"
21 #include "SurfaceNode"
22 #include "SelectionInfo"
23 #include <osgEarth/TraversalData>
24 
25 #define LC "[TerrainCuller] "
26 
27 using namespace osgEarth::Drivers::RexTerrainEngine;
28 
29 
TerrainCuller(osgUtil::CullVisitor * cullVisitor,EngineContext * context)30 TerrainCuller::TerrainCuller(osgUtil::CullVisitor* cullVisitor, EngineContext* context) :
31 _camera(0L),
32 _currentTileNode(0L),
33 _orphanedPassesDetected(0u),
34 _cv(cullVisitor),
35 _context(context)
36 {
37     setVisitorType(CULL_VISITOR);
38     setTraversalMode(TRAVERSE_ALL_CHILDREN);
39     setCullingMode(cullVisitor->getCullingMode());
40 
41     setFrameStamp(new osg::FrameStamp(*_cv->getFrameStamp()));
42     setDatabaseRequestHandler(_cv->getDatabaseRequestHandler());
43     pushReferenceViewPoint(_cv->getReferenceViewPoint());
44     pushViewport(_cv->getViewport());
45     pushProjectionMatrix(_cv->getProjectionMatrix());
46     pushModelViewMatrix(_cv->getModelViewMatrix(), _cv->getCurrentCamera()->getReferenceFrame());
47     setLODScale(_cv->getLODScale());
48     _camera = _cv->getCurrentCamera();
49     _isSpy = VisitorData::isSet(*cullVisitor, "osgEarth.Spy");
50 }
51 
52 void
setup(const Map * map,LayerExtentVector & layerExtents,const RenderBindings & bindings)53 TerrainCuller::setup(const Map* map, LayerExtentVector& layerExtents, const RenderBindings& bindings)
54 {
55     unsigned frameNum = getFrameStamp() ? getFrameStamp()->getFrameNumber() : 0u;
56     _layerExtents = &layerExtents;
57     _terrain.setup(map, bindings, frameNum, _cv);
58 }
59 
60 float
getDistanceToViewPoint(const osg::Vec3 & pos,bool withLODScale) const61 TerrainCuller::getDistanceToViewPoint(const osg::Vec3& pos, bool withLODScale) const
62 {
63     // pass through, in case developer has overridden the method in the prototype CV
64     return _cv->getDistanceToViewPoint(pos, withLODScale);
65 }
66 
67 DrawTileCommand*
addDrawCommand(UID uid,const TileRenderModel * model,const RenderingPass * pass,TileNode * tileNode)68 TerrainCuller::addDrawCommand(UID uid, const TileRenderModel* model, const RenderingPass* pass, TileNode* tileNode)
69 {
70     SurfaceNode* surface = tileNode->getSurfaceNode();
71 
72     const RenderBindings& bindings = _context->getRenderBindings();
73 
74     // skip layers that are not visible:
75     if (pass &&
76         pass->visibleLayer() &&
77         pass->visibleLayer()->getVisible() == false)
78     {
79         //OE_DEBUG << LC << "Skipping " << pass->visibleLayer()->getName() << " because it's not visible." << std::endl;
80         return 0L;
81     }
82 
83     // add a new Draw command to the appropriate layer
84     osg::ref_ptr<LayerDrawable> drawable = _terrain.layer(uid);
85     if (drawable.valid())
86     {
87         // Layer marked for drawing?
88         if (drawable->_draw)
89         {
90             // Cull based on the layer extent.
91             if (drawable->_layer)
92             {
93                 const LayerExtent& le = (*_layerExtents)[drawable->_layer->getUID()];
94                 if (le._computed &&
95                     le._extent.isValid() &&
96                     le._extent.intersects(tileNode->getKey().getExtent()) == false)
97                 {
98                     // culled out!
99                     //OE_DEBUG << LC << "Skippping " << drawable->_layer->getName()
100                     //    << " key " << tileNode->getKey().str()
101                     //    << " because it was culled by extent." << std::endl;
102                     return 0L;
103                 }
104             }
105 
106             drawable->_tiles.push_back(DrawTileCommand());
107             DrawTileCommand* tile = &drawable->_tiles.back();
108 
109             // install everything we need in the Draw Command:
110             tile->_colorSamplers = pass ? &(pass->samplers()) : 0L;
111             tile->_sharedSamplers = &model->_sharedSamplers;
112             tile->_modelViewMatrix = _cv->getModelViewMatrix();
113             tile->_keyValue = tileNode->getTileKeyValue();
114             tile->_geom = surface->getDrawable()->_geom.get();
115             tile->_morphConstants = tileNode->getMorphConstants();
116             tile->_key = &tileNode->getKey();
117 
118             osg::Vec3 c = surface->getBound().center() * surface->getInverseMatrix();
119             tile->_range = getDistanceToViewPoint(c, true);
120 
121             tile->_layerOrder = drawable->_drawOrder;
122 
123             const osg::Image* elevRaster = tileNode->getElevationRaster();
124             if (elevRaster)
125             {
126                 float bias = _context->getUseTextureBorder() ? 1.5 : 0.5;
127 
128                 // Compute an elevation texture sampling scale/bias so we sample elevation data on center
129                 // instead of on edge (as we do with color, etc.)
130                 //
131                 // This starts out as:
132                 //   scale = (size-1)/size : this shrinks the sample area by one texel since we're sampling on center
133                 //   bias = 0.5/size : this shifts the sample area over 1/2 texel to the center.
134                 //
135                 // But, since we also have a 1-texel border, we need to further reduce the scale by 2 texels to
136                 // remove the border, and shift an extra texel over as well. Giving us this:
137                 float size = (float)elevRaster->s();
138                 tile->_elevTexelCoeff.set((size - (2.0*bias)) / size, bias / size);
139             }
140 
141             return tile;
142         }
143     }
144     else if (pass)
145     {
146         // The pass exists but it's layer is not in the render data draw list.
147         // This means that the layer is no longer in the map. Detect and record
148         // this information so we can run a cleanup visitor later on.
149         ++_orphanedPassesDetected;
150     }
151     else
152     {
153         OE_WARN << "Added nothing for a UID -1 darw command" << std::endl;
154     }
155 
156     return 0L;
157 }
158 
159 void
apply(osg::Node & node)160 TerrainCuller::apply(osg::Node& node)
161 {
162     TileNode* tileNode = dynamic_cast<TileNode*>(&node);
163     if (tileNode)
164     {
165         apply(*tileNode);
166     }
167     else
168     {
169         SurfaceNode* surfaceNode = dynamic_cast<SurfaceNode*>(&node);
170         if (surfaceNode)
171         {
172             apply(*surfaceNode);
173             return; // no need to traverse further
174         }
175     }
176 
177     // Handle any CullCallbacks and traverse.
178     osg::Callback* cullCallback = node.getCullCallback();
179     if (cullCallback) cullCallback->run(&node, this);
180     else traverse(node);
181 }
182 
183 bool
isCulledToBBox(osg::Transform * node,const osg::BoundingBox & box)184 TerrainCuller::isCulledToBBox(osg::Transform* node, const osg::BoundingBox& box)
185 {
186     osg::RefMatrix* matrix = createOrReuseMatrix(*_cv->getModelViewMatrix());
187     node->computeLocalToWorldMatrix(*matrix, this);
188     _cv->pushModelViewMatrix(matrix, node->getReferenceFrame());
189     bool culled = _cv->isCulled(box);
190     _cv->popModelViewMatrix();
191     return culled;
192 }
193 
194 void
apply(TileNode & node)195 TerrainCuller::apply(TileNode& node)
196 {
197     _currentTileNode = &node;
198 
199     // reset the pointer to the first DrawTileCommand. We keep track of this so
200     // we can set it's "layerOrder" member to zero at the end, so the rendering engine
201     // knows to blend it with the terrain geometry color.
202     _firstDrawCommandForTile = 0L;
203 
204     if (!_terrain.patchLayers().empty())
205     {
206         // todo: check for patch/virtual
207         const RenderBindings& bindings = _context->getRenderBindings();
208         TileRenderModel& renderModel = _currentTileNode->renderModel();
209 
210         bool pushedMatrix = false;
211 
212         // Render patch layers if applicable.
213         // A patch layer is one rendered using GL_PATCHES.
214         for (PatchLayerVector::const_iterator i = _terrain.patchLayers().begin(); i != _terrain.patchLayers().end(); ++i)
215         {
216             PatchLayer* layer = i->get();
217 
218             // is the layer accepting this key?
219             if (layer->getAcceptCallback() && !layer->getAcceptCallback()->acceptKey(_currentTileNode->getKey()))
220                 continue;
221 
222             // is the tile in visible range?
223             float range = _cv->getDistanceToViewPoint(node.getBound().center(), true) - node.getBound().radius();
224             if (layer->getMaxVisibleRange() < range)
225                 continue;
226 
227             // Push this tile's matrix if we haven't already done so:
228             if (!pushedMatrix)
229             {
230                 SurfaceNode* surface = node.getSurfaceNode();
231 
232                 // push the surface matrix:
233                 osg::RefMatrix* matrix = createOrReuseMatrix(*_cv->getModelViewMatrix());
234                 surface->computeLocalToWorldMatrix(*matrix,this);
235                 _cv->pushModelViewMatrix(matrix, surface->getReferenceFrame());
236 
237                 pushedMatrix = true;
238             }
239 
240             // Add the draw command:
241             DrawTileCommand* cmd = addDrawCommand(layer->getUID(), &renderModel, 0L, &node);
242             if (cmd)
243             {
244                 cmd->_drawPatch = true;
245                 cmd->_drawCallback = layer->getDrawCallback();
246             }
247         }
248 
249         if (pushedMatrix)
250         {
251            _cv->popModelViewMatrix();
252         }
253     }
254 }
255 
256 void
apply(SurfaceNode & node)257 TerrainCuller::apply(SurfaceNode& node)
258 {
259     TileRenderModel& renderModel = _currentTileNode->renderModel();
260 
261     // push the surface matrix:
262     osg::RefMatrix* matrix = createOrReuseMatrix(*getModelViewMatrix());
263     node.computeLocalToWorldMatrix(*matrix,this);
264     _cv->pushModelViewMatrix(matrix, node.getReferenceFrame());
265 
266     // now test against the local bounding box for tighter culling:
267     if (!_cv->isCulled(node.getAlignedBoundingBox()))
268     {
269         if (!_isSpy)
270         {
271             node.setLastFramePassedCull(getFrameStamp()->getFrameNumber());
272         }
273 
274         int order = 0;
275         unsigned count = 0;
276 
277         // First go through any legit rendering pass data in the Tile and
278         // and add a DrawCommand for each.
279         for (unsigned p = 0; p < renderModel._passes.size(); ++p)
280         {
281             const RenderingPass& pass = renderModel._passes[p];
282             DrawTileCommand* cmd = addDrawCommand(pass.sourceUID(), &renderModel, &pass, _currentTileNode);
283             if (cmd)
284             {
285                 if (_firstDrawCommandForTile == 0L)
286                 {
287                     _firstDrawCommandForTile = cmd;
288                 }
289                 else if (cmd->_layerOrder < _firstDrawCommandForTile->_layerOrder)
290                 {
291                     _firstDrawCommandForTile = cmd;
292                 }
293             }
294         }
295 
296         // If the culler added no draw commands for this tile... we still need
297         // to draw something or else there will be a hole! So draw a blank tile.
298         // UID = -1 is the special UID code for a blank.
299         if (_firstDrawCommandForTile == 0L)
300         {
301             //OE_INFO << LC << "Adding blank render for tile " << _currentTileNode->getKey().str() << std::endl;
302             DrawTileCommand* cmd = addDrawCommand(-1, &renderModel, 0L, _currentTileNode);
303             if (cmd)
304             {
305                 _firstDrawCommandForTile = cmd;
306             }
307         }
308 
309         // Set the layer order of the first draw command for this tile to zero,
310         // to support proper terrain blending.
311         if (_firstDrawCommandForTile)
312         {
313             _firstDrawCommandForTile->_layerOrder = 0;
314         }
315 
316         // update our bounds
317         _terrain._drawState->_bs.expandBy(node.getBound());
318         _terrain._drawState->_box.expandBy(_terrain._drawState->_bs);
319     }
320 
321     // pop the matrix from the cull stack
322     _cv->popModelViewMatrix();
323 
324     if (node.getDebugNode())
325     {
326         node.accept(*_cv);
327     }
328 }
329 
330