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