FlightGear next
terrain_stg.cxx
Go to the documentation of this file.
1// scenery.cxx -- data structures and routines for managing scenery.
2//
3// Written by Curtis Olson, started May 1997.
4//
5// Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
6//
7// This program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public License as
9// published by the Free Software Foundation; either version 2 of the
10// License, or (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful, but
13// WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15// General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program; if not, write to the Free Software
19// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20//
21// $Id$
22
23
24#include <config.h>
25
26#include <stdio.h>
27#include <string.h>
28
29#include <osg/Camera>
30#include <osg/Transform>
31#include <osg/MatrixTransform>
32#include <osg/PositionAttitudeTransform>
33#include <osg/CameraView>
34#include <osg/LOD>
35#include <osgTerrain/TerrainTile>
36
37#include <osgViewer/Viewer>
38
39#include <simgear/constants.h>
40#include <simgear/sg_inlines.h>
41#include <simgear/debug/logstream.hxx>
42#include <simgear/scene/tgdb/userdata.hxx>
43#include <simgear/scene/tgdb/VPBTechnique.hxx>
44#include <simgear/scene/material/matlib.hxx>
45#include <simgear/scene/material/mat.hxx>
46#include <simgear/scene/util/SGNodeMasks.hxx>
47#include <simgear/scene/util/OsgMath.hxx>
48#include <simgear/scene/util/SGSceneUserData.hxx>
49#include <simgear/scene/model/CheckSceneryVisitor.hxx>
50#include <simgear/scene/sky/sky.hxx>
51
52#include <simgear/bvh/BVHNode.hxx>
53#include <simgear/bvh/BVHLineSegmentVisitor.hxx>
54#include <simgear/bvh/BVHTerrainTile.hxx>
55#include <simgear/structure/commands.hxx>
56
57#include <Viewer/renderer.hxx>
58#include <Main/fg_props.hxx>
59#include <GUI/MouseCursor.hxx>
60
61#include "terrain_stg.hxx"
62
63using namespace flightgear;
64using namespace simgear;
65
66class FGGroundPickCallback : public SGPickCallback {
67public:
68 FGGroundPickCallback() : SGPickCallback(PriorityScenery)
69 { }
70
71 virtual bool buttonPressed( int button,
72 const osgGA::GUIEventAdapter&,
73 const Info& info )
74 {
75 // only on left mouse button
76 if (button != 0)
77 return false;
78
79 SGGeod geod = SGGeod::fromCart(info.wgs84);
80 SG_LOG( SG_TERRAIN, SG_INFO, "Got ground pick at " << geod );
81
82 SGPropertyNode *c = fgGetNode("/sim/input/click", true);
83 c->setDoubleValue("longitude-deg", geod.getLongitudeDeg());
84 c->setDoubleValue("latitude-deg", geod.getLatitudeDeg());
85 c->setDoubleValue("elevation-m", geod.getElevationM());
86 c->setDoubleValue("elevation-ft", geod.getElevationFt());
87 fgSetBool("/sim/signals/click", 1);
88
89 return true;
90 }
91};
92
93class FGSceneryIntersect : public osg::NodeVisitor {
94public:
95 FGSceneryIntersect(const SGLineSegmentd& lineSegment,
96 const osg::Node* skipNode) :
97 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
98 _lineSegment(lineSegment),
99 _skipNode(skipNode),
100 _material(0),
101 _haveHit(false)
102 { }
103
104 bool getHaveHit() const
105 { return _haveHit; }
106 const SGLineSegmentd& getLineSegment() const
107 { return _lineSegment; }
108 const simgear::BVHMaterial* getMaterial() const
109 { return _material; }
110
111 virtual void apply(osg::Node& node)
112 {
113 if (&node == _skipNode)
114 return;
115 if (!testBoundingSphere(node.getBound()))
116 return;
117
118 addBoundingVolume(node);
119 }
120
121 virtual void apply(osg::Group& group)
122 {
123 if (&group == _skipNode)
124 return;
125 if (!testBoundingSphere(group.getBound()))
126 return;
127
128 traverse(group);
129 addBoundingVolume(group);
130
131 if (_haveHit && dynamic_cast<osgTerrain::TerrainTile*>(&group) && ! _material) {
132 // In the case of TerrainTiles, we do not have material information within the mesh itself.
133 // So instead it must be retrieved from the raster.
134 osgTerrain::TerrainTile* tile = dynamic_cast<osgTerrain::TerrainTile*>(&group);
135 simgear::VPBTechnique* technique = tile ? dynamic_cast<simgear::VPBTechnique*>(tile->getTerrainTechnique()) : 0;
136 _material = technique ? technique->getMaterial(toOsg(_lineSegment.getEnd())) : 0;
137 }
138 }
139
140 virtual void apply(osg::Transform& transform)
141 { handleTransform(transform); }
142 virtual void apply(osg::Camera& camera)
143 {
144 if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
145 return;
146 handleTransform(camera);
147 }
148 virtual void apply(osg::CameraView& transform)
149 { handleTransform(transform); }
150 virtual void apply(osg::MatrixTransform& transform)
151 { handleTransform(transform); }
152 virtual void apply(osg::PositionAttitudeTransform& transform)
153 { handleTransform(transform); }
154
155private:
156 void handleTransform(osg::Transform& transform)
157 {
158 if (&transform == _skipNode)
159 return;
160 // Hmm, may be this needs to be refined somehow ...
161 if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
162 return;
163
164 if (!testBoundingSphere(transform.getBound()))
165 return;
166
167 osg::Matrix inverseMatrix;
168 if (!transform.computeWorldToLocalMatrix(inverseMatrix, this))
169 return;
170 osg::Matrix matrix;
171 if (!transform.computeLocalToWorldMatrix(matrix, this))
172 return;
173
174 SGLineSegmentd lineSegment = _lineSegment;
175 bool haveHit = _haveHit;
176 const simgear::BVHMaterial* material = _material;
177
178 _haveHit = false;
179 _lineSegment = lineSegment.transform(SGMatrixd(inverseMatrix.ptr()));
180
181 addBoundingVolume(transform);
182 traverse(transform);
183
184 if (_haveHit) {
185 _lineSegment = _lineSegment.transform(SGMatrixd(matrix.ptr()));
186 } else {
187 _lineSegment = lineSegment;
188 _material = material;
189 _haveHit = haveHit;
190 }
191 }
192
193 simgear::BVHNode* getNodeBoundingVolume(osg::Node& node)
194 {
195 SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
196 if (!userData)
197 return 0;
198 return userData->getBVHNode();
199 }
200 void addBoundingVolume(osg::Node& node)
201 {
202 simgear::BVHNode* bvNode = getNodeBoundingVolume(node);
203 if (!bvNode)
204 return;
205
206 // Find ground intersection on the bvh nodes
207 simgear::BVHLineSegmentVisitor lineSegmentVisitor(_lineSegment,
208 0/*startTime*/);
209 bvNode->accept(lineSegmentVisitor);
210 if (!lineSegmentVisitor.empty()) {
211 _lineSegment = lineSegmentVisitor.getLineSegment();
212 _material = lineSegmentVisitor.getMaterial();
213 _haveHit = true;
214 }
215 }
216
217 bool testBoundingSphere(const osg::BoundingSphere& bound) const
218 {
219 if (!bound.valid())
220 return false;
221
222 SGSphered sphere(toVec3d(toSG(bound._center)), bound._radius);
223 return intersects(_lineSegment, sphere);
224 }
225
226 SGLineSegmentd _lineSegment;
227 const osg::Node* _skipNode;
228
229 const simgear::BVHMaterial* _material;
230 bool _haveHit;
231};
232
234
235// Terrain Management system
237 _tilemgr()
238{
239 _inited = false;
240}
241
243{
244 SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::dtor");
245}
246
247
248// Initialize the Scenery Management system
249void FGStgTerrain::init( osg::Group* terrain ) {
250 // Already set up.
251 if (_inited)
252 return;
253
254 SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::init - init tilemgr");
255
256 // remember the scene terrain branch on scenegraph
257 terrain_branch = terrain;
258
259 // initialize the tile manager
260 _tilemgr.init();
261
262 // Toggle the setup flag.
263 _inited = true;
264}
265
267{
268 SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::reinit - reinit tilemgr");
269
270 _tilemgr.reinit();
271}
272
274{
275 SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::shutdown - shutdown tilemgr");
276
277 _tilemgr.shutdown();
278
279 terrain_branch = NULL;
280
281 // Toggle the setup flag.
282 _inited = false;
283}
284
285
286void FGStgTerrain::update(double dt)
287{
288 _tilemgr.update(dt);
289}
290
292{
293 SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::bind - noop");
294}
295
297{
298 SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::unbind - noop");
299}
300
301bool
302FGStgTerrain::get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
303 double& alt,
304 const simgear::BVHMaterial** material,
305 const osg::Node* butNotFrom)
306{
307 bool ok;
308
309 SGGeod geod = SGGeod::fromCart(pos);
310 if (!geod.isValid())
311 return false;
312
313 geod.setElevationM(geod.getElevationM() + max_altoff);
314
315 ok = get_elevation_m(geod, alt, material, butNotFrom);
316
317 return ok;
318}
319
320bool
321FGStgTerrain::get_elevation_m(const SGGeod& geod, double& alt,
322 const simgear::BVHMaterial** material,
323 const osg::Node* butNotFrom)
324{
325 if (!geod.isValid())
326 return false;
327
328 SGVec3d start = SGVec3d::fromGeod(geod);
329
330 SGGeod geodEnd = geod;
331 geodEnd.setElevationM(SGMiscd::min(geod.getElevationM() - 10, -10000));
332 SGVec3d end = SGVec3d::fromGeod(geodEnd);
333
334 FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom);
335 intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
336 terrain_branch->accept(intersectVisitor);
337
338 if (!intersectVisitor.getHaveHit())
339 return false;
340
341 geodEnd = SGGeod::fromCart(intersectVisitor.getLineSegment().getEnd());
342 alt = geodEnd.getElevationM();
343 if (material) {
344 *material = intersectVisitor.getMaterial();
345 }
346
347 return true;
348}
349
350bool
351FGStgTerrain::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir,
352 SGVec3d& nearestHit,
353 const osg::Node* butNotFrom)
354{
355 // We assume that starting positions in the center of the earth are invalid
356 if ( norm1(pos) < 1 )
357 return false;
358
359 // Make really sure the direction is normalized, is really cheap compared to
360 // computation of ground intersection.
361 SGVec3d start = pos;
362 SGVec3d end = start + 1e5*normalize(dir); // FIXME visibility ???
363
364 FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom);
365 intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
366 terrain_branch->accept(intersectVisitor);
367
368 if (!intersectVisitor.getHaveHit())
369 return false;
370
371 nearestHit = intersectVisitor.getLineSegment().getEnd();
372 return true;
373}
374
375bool FGStgTerrain::scenery_available(const SGGeod& position, double range_m)
376{
377 if( schedule_scenery(position, range_m, 0.0) )
378 {
379 double elev = 0.0;
380
381 bool use_vpb = globals->get_props()->getNode("scenery/use-vpb")->getBoolValue();
382 bool got_elev = get_elevation_m(SGGeod::fromGeodM(position, SG_MAX_ELEVATION_M), elev, 0, 0);
383
384 if (!use_vpb && !got_elev)
385 {
386 SG_LOG(SG_TERRAIN, SG_DEBUG, "FGStgTerrain::scenery_available - false" );
387 return false;
388 }
389
390 SGVec3f p = SGVec3f::fromGeod(SGGeod::fromGeodM(position, elev));
391 osg::FrameStamp* framestamp
392 = globals->get_renderer()->getFrameStamp();
393
394 FGScenery* pSceneryManager = globals->get_scenery();
395 simgear::CheckSceneryVisitor csnv(pSceneryManager->getPager(), toOsg(p), range_m, framestamp);
396 // currently the PagedLODs will not be loaded by the DatabasePager
397 // while the splashscreen is there, so CheckSceneryVisitor force-loads
398 // missing objects in the main thread
399 terrain_branch->accept(csnv);
400 if(!csnv.isLoaded()) {
401 SG_LOG(SG_TERRAIN, SG_DEBUG, "FGScenery::scenery_available: waiting on CheckSceneryVisitor");
402 return false;
403 }
404
405 SG_LOG(SG_TERRAIN, SG_DEBUG, "FGStgTerrain::scenery_available - true" );
406 return true;
407 } else {
408 SG_LOG(SG_TERRAIN, SG_DEBUG, "FGScenery::scenery_available: waiting on tile manager");
409 }
410 SG_LOG(SG_TERRAIN, SG_DEBUG, "FGStgTerrain::scenery_available - false" );
411 return false;
412}
413
414bool FGStgTerrain::schedule_scenery(const SGGeod& position, double range_m, double duration)
415{
416 SG_LOG(SG_TERRAIN, SG_BULK, "FGStgTerrain::schedule_scenery");
417
418 return _tilemgr.schedule_scenery( position, range_m, duration );
419}
420
422{
423 _tilemgr.materialLibChanged();
424}
#define p(x)
virtual bool buttonPressed(int button, const osgGA::GUIEventAdapter &, const Info &info)
virtual void apply(osg::Camera &camera)
bool getHaveHit() const
const simgear::BVHMaterial * getMaterial() const
virtual void apply(osg::Group &group)
virtual void apply(osg::Node &node)
FGSceneryIntersect(const SGLineSegmentd &lineSegment, const osg::Node *skipNode)
const SGLineSegmentd & getLineSegment() const
virtual void apply(osg::MatrixTransform &transform)
virtual void apply(osg::PositionAttitudeTransform &transform)
virtual void apply(osg::CameraView &transform)
virtual void apply(osg::Transform &transform)
flightgear::SceneryPager * getPager()
Definition scenery.hxx:147
void update(double dt)
bool schedule_scenery(const SGGeod &position, double range_m, double duration=0.0)
bool get_elevation_m(const SGGeod &geod, double &alt, const simgear::BVHMaterial **material, const osg::Node *butNotFrom=0)
Compute the elevation of the scenery at geodetic latitude lat, geodetic longitude lon and not higher ...
bool scenery_available(const SGGeod &position, double range_m)
Returns true if scenery is available for the given lat, lon position within a range of range_m.
bool get_cart_elevation_m(const SGVec3d &pos, double max_altoff, double &elevation, const simgear::BVHMaterial **material, const osg::Node *butNotFrom=0)
Compute the elevation of the scenery below the cartesian point pos.
virtual ~FGStgTerrain()
bool get_cart_ground_intersection(const SGVec3d &start, const SGVec3d &dir, SGVec3d &nearestHit, const osg::Node *butNotFrom=0)
Compute the nearest intersection point of the line starting from start going in direction dir with th...
void init(osg::Group *terrain)
void materialLibChanged()
FGGlobals * globals
Definition globals.cxx:142
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
Definition AIBase.hxx:25
bool fgSetBool(char const *name, bool val)
Set a bool value for a property.
Definition proptest.cpp:24
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27