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