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 "SurfaceNode"
20 #include "GeometryPool"
21 #include "TileDrawable"
22 
23 #include <osgEarth/TileKey>
24 #include <osgEarth/Registry>
25 #include <osgEarth/Horizon>
26 #include <osgEarth/ImageUtils>
27 #include <osgEarth/LineDrawable>
28 
29 #include <osg/CullStack>
30 #include <osg/Geode>
31 
32 #include <osg/Geometry>
33 #include <osg/TriangleFunctor>
34 #include <osgText/Text>
35 #include <osg/CullStack>
36 #include <osgUtil/CullVisitor>
37 
38 #include <numeric>
39 
40 using namespace osgEarth::Drivers::RexTerrainEngine;
41 using namespace osgEarth;
42 
43 #define LC "[SurfaceNode] "
44 
45 //..............................................................
46 
47 namespace
48 {
makeBBox(const osg::BoundingBox & bbox,const TileKey & key)49     osg::Node* makeBBox(const osg::BoundingBox& bbox, const TileKey& key)
50     {
51         osg::Group* geode = new osg::Group();
52         std::string sizeStr = "(empty)";
53         float zpos = 0.0f;
54 
55         if ( bbox.valid() )
56         {
57             static const int index[24] = {
58                 0,1, 1,3, 3,2, 2,0,
59                 0,4, 1,5, 2,6, 3,7,
60                 4,5, 5,7, 7,6, 6,4
61             };
62 
63             LineDrawable* lines = new LineDrawable(GL_LINES);
64             for(int i=0; i<24; i+=2)
65             {
66                 lines->pushVertex(bbox.corner(index[i]));
67                 lines->pushVertex(bbox.corner(index[i+1]));
68             }
69             lines->setColor(osg::Vec4(1,0,0,1));
70             lines->finish();
71             sizeStr = Stringify() << key.str() << "\nmax="<<bbox.zMax()<<"\nmin="<<bbox.zMin()<<"\n";
72             zpos = bbox.zMax();
73 
74             geode->addChild(lines);
75         }
76 
77 #if 0
78         osgText::Text* textDrawable = new osgText::Text();
79         textDrawable->setDataVariance(osg::Object::DYNAMIC);
80         textDrawable->setText( sizeStr );
81         textDrawable->setFont( osgEarth::Registry::instance()->getDefaultFont() );
82         textDrawable->setCharacterSizeMode(textDrawable->SCREEN_COORDS);
83         textDrawable->setCharacterSize(32.0f);
84         textDrawable->setAlignment(textDrawable->CENTER_BOTTOM);
85         textDrawable->setColor(osg::Vec4(1,1,1,1));
86         textDrawable->setBackdropColor(osg::Vec4(0,0,0,1));
87         textDrawable->setBackdropType(textDrawable->OUTLINE);
88         textDrawable->setPosition(osg::Vec3(0,0,zpos));
89         textDrawable->setAutoRotateToScreen(true);
90         geode->addChild(textDrawable);
91 #endif
92 
93         //geode->getOrCreateStateSet()->setAttributeAndModes(new osg::Program(),0);
94         //geode->getOrCreateStateSet()->setMode(GL_LIGHTING,0); // ok; ffp debugging code
95         //geode->getOrCreateStateSet()->setRenderBinDetails(INT_MAX, "DepthSortedBin");
96 
97         return geode;
98     }
99 
makeSphere(const osg::BoundingSphere & bs)100     osg::Drawable* makeSphere(const osg::BoundingSphere& bs)
101     {
102         osg::Geometry* geom = new osg::Geometry();
103         geom->setUseVertexBufferObjects(true);
104 
105         float r = bs.radius();
106 
107         osg::Vec3Array* v = new osg::Vec3Array();
108         v->reserve(6);
109         v->push_back(osg::Vec3(0, 0, r)); // top
110         v->push_back(osg::Vec3(0, 0, -r)); // bottom
111         v->push_back(osg::Vec3(-r, 0, 0)); // left
112         v->push_back(osg::Vec3(r, 0, 0)); // right
113         v->push_back(osg::Vec3(0, r, 0)); // back
114         v->push_back(osg::Vec3(0, -r, 0)); // front
115         geom->setVertexArray(v);
116 
117         osg::DrawElementsUByte* b = new osg::DrawElementsUByte(GL_LINE_STRIP);
118         b->reserve(24);
119         b->push_back(0); b->push_back(3); b->push_back(4);
120         b->push_back(0); b->push_back(4); b->push_back(2);
121         b->push_back(0); b->push_back(2); b->push_back(5);
122         b->push_back(0); b->push_back(5); b->push_back(3);
123         b->push_back(1); b->push_back(3); b->push_back(5);
124         b->push_back(1); b->push_back(4); b->push_back(3);
125         b->push_back(1); b->push_back(2); b->push_back(4);
126         b->push_back(1); b->push_back(5); b->push_back(2);
127         geom->addPrimitiveSet(b);
128 
129         osg::Vec3Array* n = new osg::Vec3Array(osg::Array::BIND_PER_VERTEX);
130         n->reserve(6);
131         n->push_back(osg::Vec3(0, 0, 1));
132         n->push_back(osg::Vec3(0, 0, -1));
133         n->push_back(osg::Vec3(-1, 0, 0));
134         n->push_back(osg::Vec3(1, 0, 0));
135         n->push_back(osg::Vec3(0, 1, 0));
136         n->push_back(osg::Vec3(0, -1, 0));
137         geom->setNormalArray(n);
138 
139         //MeshSubdivider ms;
140         //ms.run(*geom, osg::DegreesToRadians(maxAngle), GEOINTERP_GREAT_CIRCLE);
141 
142         osg::Vec4Array* c = new osg::Vec4Array(osg::Array::BIND_OVERALL, 1);
143         (*c)[0].set(1,1,0,1);
144         geom->setColorArray(c);
145 
146         return geom;
147     }
148 }
149 
150 //..............................................................
151 
152 void
set(const SpatialReference * srs,const osg::Matrix & local2world,const osg::BoundingBox & bbox)153 HorizonTileCuller::set(const SpatialReference* srs,
154                        const osg::Matrix&      local2world,
155                        const osg::BoundingBox& bbox)
156 {
157     if (!_horizon.valid() && srs->isGeographic())
158     {
159         _horizon = new Horizon();
160     }
161 
162     if (_horizon.valid())
163     {
164         _horizon->setEllipsoid(*srs->getEllipsoid());
165 
166         // Adjust the horizon ellipsoid based on the minimum Z value of the tile;
167         // necessary because a tile that's below the ellipsoid (ocean floor, e.g.)
168         // may be visible even if it doesn't pass the horizon-cone test. In such
169         // cases we need a more conservative ellipsoid.
170         double zMin = static_cast<double>(osg::minimum( bbox.corner(0).z(), static_cast<osg::BoundingBox::value_type>(0.)));
171         zMin = osg::maximum(zMin, -25000.0); // approx the lowest point on earth * 2
172         _horizon->setEllipsoid( osg::EllipsoidModel(
173             srs->getEllipsoid()->getRadiusEquator() + zMin,
174             srs->getEllipsoid()->getRadiusPolar() + zMin) );
175 
176         // consider the uppermost 4 points of the tile-aligned bounding box.
177         // (the last four corners of the bbox are the "zmax" corners.)
178         for(unsigned i=0; i<4; ++i)
179         {
180             _points[i] = bbox.corner(4+i) * local2world;
181         }
182     }
183 }
184 
185 bool
isVisible(const osg::Vec3d & from) const186 HorizonTileCuller::isVisible(const osg::Vec3d& from) const
187 {
188     if (!_horizon.valid())
189         return true;
190 
191     // alternate method (slower)
192     //return _horizon->isVisible(from, _bs.center(), _bs.radius());
193 
194     for (unsigned i = 0; i < 4; ++i)
195         if (_horizon->isVisible(from, _points[i], 0.0))
196             return true;
197 
198     return false;
199 }
200 
201 //..............................................................
202 
203 
204 const bool SurfaceNode::_enableDebugNodes = ::getenv("OSGEARTH_REX_DEBUG") != 0L;
205 
SurfaceNode(const TileKey & tilekey,const MapInfo & mapinfo,const RenderBindings & bindings,TileDrawable * drawable)206 SurfaceNode::SurfaceNode(const TileKey&        tilekey,
207                          const MapInfo&        mapinfo,
208                          const RenderBindings& bindings,
209                          TileDrawable*         drawable)
210 {
211     _tileKey = tilekey;
212 
213     _drawable = drawable;
214 
215     // Create the final node.
216     addChild(_drawable.get());
217 
218     // Establish a local reference frame for the tile:
219     GeoPoint centroid;
220     tilekey.getExtent().getCentroid(centroid);
221 
222     osg::Matrix local2world;
223     centroid.createLocalToWorld( local2world );
224     setMatrix( local2world );
225 
226     // Initialize the cached bounding box.
227     setElevationRaster( 0L, osg::Matrixf::identity() );
228 }
229 
230 osg::BoundingSphere
computeBound() const231 SurfaceNode::computeBound() const
232 {
233     osg::Matrix l2w;
234     computeLocalToWorldMatrix(l2w, 0L);
235     osg::BoundingSphere bs;
236     osg::BoundingBox box = _drawable->getBoundingBox();
237     for (unsigned i=0; i<8; ++i)
238         bs.expandBy(box.corner(i)*l2w);
239 
240     return bs;
241 }
242 
243 float
getPixelSizeOnScreen(osg::CullStack * cull) const244 SurfaceNode::getPixelSizeOnScreen(osg::CullStack* cull) const
245 {
246     return cull->clampedPixelSize(getMatrix().getTrans(), _drawable->getRadius()) / cull->getLODScale();
247 }
248 
249 void
setLastFramePassedCull(unsigned fn)250 SurfaceNode::setLastFramePassedCull(unsigned fn)
251 {
252     _lastFramePassedCull.exchange(fn);
253 }
254 
255 void
setElevationRaster(const osg::Image * raster,const osg::Matrixf & scaleBias)256 SurfaceNode::setElevationRaster(const osg::Image*   raster,
257                                 const osg::Matrixf& scaleBias)
258 {
259     if ( !_drawable.valid() )
260         return;
261 
262     // communicate the raster to the drawable:
263     if ( raster )
264     {
265         _drawable->setElevationRaster( raster, scaleBias );
266     }
267 
268     // next compute the bounding box in local space:
269     const osg::BoundingBox& box = _drawable->getBoundingBox();
270 
271     // Compute the medians of each potential child node:
272 
273     osg::Vec3 minZMedians[4];
274     osg::Vec3 maxZMedians[4];
275 
276     minZMedians[0] = (box.corner(0)+box.corner(1))*0.5;
277     minZMedians[1] = (box.corner(1)+box.corner(3))*0.5;
278     minZMedians[2] = (box.corner(3)+box.corner(2))*0.5;
279     minZMedians[3] = (box.corner(0)+box.corner(2))*0.5;
280 
281     maxZMedians[0] = (box.corner(4)+box.corner(5))*0.5;
282     maxZMedians[1] = (box.corner(5)+box.corner(7))*0.5;
283     maxZMedians[2] = (box.corner(7)+box.corner(6))*0.5;
284     maxZMedians[3] = (box.corner(4)+box.corner(6))*0.5;
285 
286     // Child 0 corners
287     _childrenCorners[0][0] =  box.corner(0);
288     _childrenCorners[0][1] =  minZMedians[0];
289     _childrenCorners[0][2] =  minZMedians[3];
290     _childrenCorners[0][3] = (minZMedians[0]+minZMedians[2])*0.5;
291 
292     _childrenCorners[0][4] =  box.corner(4);
293     _childrenCorners[0][5] =  maxZMedians[0];
294     _childrenCorners[0][6] =  maxZMedians[3];
295     _childrenCorners[0][7] = (maxZMedians[0]+maxZMedians[2])*0.5;
296 
297     // Child 1 corners
298     _childrenCorners[1][0] =  minZMedians[0];
299     _childrenCorners[1][1] =  box.corner(1);
300     _childrenCorners[1][2] = (minZMedians[0]+minZMedians[2])*0.5;
301     _childrenCorners[1][3] =  minZMedians[1];
302 
303     _childrenCorners[1][4] =  maxZMedians[0];
304     _childrenCorners[1][5] =  box.corner(5);
305     _childrenCorners[1][6] = (maxZMedians[0]+maxZMedians[2])*0.5;
306     _childrenCorners[1][7] =  maxZMedians[1];
307 
308     // Child 2 corners
309     _childrenCorners[2][0] =  minZMedians[3];
310     _childrenCorners[2][1] = (minZMedians[0]+minZMedians[2])*0.5;
311     _childrenCorners[2][2] =  box.corner(2);
312     _childrenCorners[2][3] =  minZMedians[2];
313 
314     _childrenCorners[2][4] =  maxZMedians[3];
315     _childrenCorners[2][5] = (maxZMedians[0]+maxZMedians[2])*0.5;
316     _childrenCorners[2][6] =  box.corner(6);
317     _childrenCorners[2][7] =  maxZMedians[2];
318 
319     // Child 3 corners
320     _childrenCorners[3][0] = (minZMedians[0]+minZMedians[2])*0.5;
321     _childrenCorners[3][1] =  minZMedians[1];
322     _childrenCorners[3][2] =  minZMedians[2];
323     _childrenCorners[3][3] =  box.corner(3);
324 
325     _childrenCorners[3][4] = (maxZMedians[0]+maxZMedians[2])*0.5;
326     _childrenCorners[3][5] =  maxZMedians[1];
327     _childrenCorners[3][6] =  maxZMedians[2];
328     _childrenCorners[3][7] =  box.corner(7);
329 
330     // Transform the child corners to world space
331 
332     const osg::Matrix& local2world = getMatrix();
333     for(int i=0; i<4; ++i)
334     {
335         VectorPoints& childCorners = _childrenCorners[i];
336          for(int j=0; j<8; ++j)
337          {
338              osg::Vec3& corner = childCorners[j];
339              corner = corner*local2world;
340          }
341     }
342 
343     if( _enableDebugNodes )
344     {
345         removeDebugNode();
346         addDebugNode(box);
347     }
348 
349     // Update the horizon culler.
350     _horizonCuller.set( _tileKey.getProfile()->getSRS(), getMatrix(), box );
351 
352     // need this?
353     dirtyBound();
354 }
355 
356 const osg::Image*
getElevationRaster() const357 SurfaceNode::getElevationRaster() const
358 {
359     return _drawable->getElevationRaster();
360 }
361 
362 const osg::Matrixf&
getElevationMatrix() const363 SurfaceNode::getElevationMatrix() const
364 {
365     return _drawable->getElevationMatrix();
366 };
367 
368 void
addDebugNode(const osg::BoundingBox & box)369 SurfaceNode::addDebugNode(const osg::BoundingBox& box)
370 {
371     _debugText = 0;
372     _debugNode = makeBBox(box, _tileKey);
373     addChild( _debugNode.get() );
374 }
375 
376 void
removeDebugNode(void)377 SurfaceNode::removeDebugNode(void)
378 {
379     _debugText = 0;
380     if ( _debugNode.valid() )
381     {
382         removeChild( _debugNode.get() );
383     }
384 }
385 
386 void
setDebugText(const std::string & strText)387 SurfaceNode::setDebugText(const std::string& strText)
388 {
389     if (_debugText.valid()==false)
390     {
391         return;
392     }
393     _debugText->setText(strText);
394 }
395 
396 const osg::BoundingBox&
getAlignedBoundingBox() const397 SurfaceNode::getAlignedBoundingBox() const
398 {
399     return _drawable->getBoundingBox();
400 }
401