FlightGear next
groundcache.cxx
Go to the documentation of this file.
1// groundcache.cxx -- carries a small subset of the scenegraph near the vehicle
2//
3// Written by Mathias Froehlich, started Nov 2004.
4//
5// Copyright (C) 2004, 2009 Mathias Froehlich - Mathias.Froehlich@web.de
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#ifdef HAVE_CONFIG_H
24# include "config.h"
25#endif
26
27#include "groundcache.hxx"
28
29#include <utility>
30
31#include <osg/Drawable>
32#include <osg/Geode>
33#include <osg/Geometry>
34#include <osg/Camera>
35#include <osg/Transform>
36#include <osg/MatrixTransform>
37#include <osg/PositionAttitudeTransform>
38#include <osg/CameraView>
39#include <osgTerrain/TerrainTile>
40#include <osgTerrain/Terrain>
41
42#include <simgear/sg_inlines.h>
43#include <simgear/constants.h>
44#include <simgear/debug/logstream.hxx>
45#include <simgear/math/SGMisc.hxx>
46#include <simgear/scene/material/mat.hxx>
47#include <simgear/scene/util/SGNodeMasks.hxx>
48#include <simgear/scene/util/SGSceneUserData.hxx>
49#include <simgear/scene/util/OsgMath.hxx>
50
51#include <simgear/bvh/BVHNode.hxx>
52#include <simgear/bvh/BVHGroup.hxx>
53#include <simgear/bvh/BVHTransform.hxx>
54#include <simgear/bvh/BVHMotionTransform.hxx>
55#include <simgear/bvh/BVHLineGeometry.hxx>
56#include <simgear/bvh/BVHStaticGeometry.hxx>
57#include <simgear/bvh/BVHStaticData.hxx>
58#include <simgear/bvh/BVHStaticNode.hxx>
59#include <simgear/bvh/BVHStaticTriangle.hxx>
60#include <simgear/bvh/BVHStaticBinary.hxx>
61#include <simgear/bvh/BVHSubTreeCollector.hxx>
62#include <simgear/bvh/BVHLineSegmentVisitor.hxx>
63#include <simgear/bvh/BVHNearestPointVisitor.hxx>
64
65#ifdef GROUNDCACHE_DEBUG
66#include <simgear/scene/model/BVHDebugCollectVisitor.hxx>
67#include <Main/fg_props.hxx>
68#endif
69
70#include <Main/globals.hxx>
71#include <Scenery/scenery.hxx>
72
73#include "flight.hxx"
74
75using namespace simgear;
76
77class FGGroundCache::CacheFill : public osg::NodeVisitor {
78public:
79 CacheFill(const SGVec3d& center, const SGVec3d& down, const double& radius,
80 const double& startTime, const double& endTime) :
81 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
82 _center(center),
83 _down(down),
84 _radius(radius),
85 _startTime(startTime),
86 _endTime(endTime),
87 _sceneryHit(0, 0, 0),
88 _maxDown(SGGeod::fromCart(center).getElevationM() + 9999),
89 _material(0),
90 _haveHit(false)
91 {
92 setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
93 }
94 virtual void apply(osg::Node& node)
95 {
96 if (!testBoundingSphere(node.getBound()))
97 return;
98
100 }
101
102 virtual void apply(osg::Group& group)
103 {
104 if (!testBoundingSphere(group.getBound()))
105 return;
106
107 simgear::BVHSubTreeCollector::NodeList parentNodeList;
108 mSubTreeCollector.pushNodeList(parentNodeList);
109
110 traverse(group);
111 addBoundingVolume(group);
112
113 mSubTreeCollector.popNodeList(parentNodeList);
114 }
115 virtual void apply(osg::Transform& transform)
116 { handleTransform(transform); }
117 virtual void apply(osg::Camera& camera)
118 {
119 if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
120 return;
121 handleTransform(camera);
122 }
123 virtual void apply(osg::CameraView& transform)
124 { handleTransform(transform); }
125 virtual void apply(osg::MatrixTransform& transform)
126 { handleTransform(transform); }
127 virtual void apply(osg::PositionAttitudeTransform& transform)
128 { handleTransform(transform); }
129
130 void handleTransform(osg::Transform& transform)
131 {
132 // Hmm, may be this needs to be refined somehow ...
133 if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
134 return;
135
136 if (!testBoundingSphere(transform.getBound()))
137 return;
138
139 osg::Matrix inverseMatrix;
140 if (!transform.computeWorldToLocalMatrix(inverseMatrix, this))
141 return;
142 osg::Matrix matrix;
143 if (!transform.computeLocalToWorldMatrix(matrix, this))
144 return;
145
146 // Look for a velocity note
147 const SGSceneUserData::Velocity* velocity = getVelocity(transform);
148
149 SGVec3d center = _center;
150 SGVec3d down = _down;
151 double radius = _radius;
152 bool haveHit = _haveHit;
153 const simgear::BVHMaterial* material = _material;
154
155 _haveHit = false;
156 _center = toSG(inverseMatrix.preMult(toOsg(_center)));
157 _down = toSG(osg::Matrix::transform3x3(toOsg(_down), inverseMatrix));
158 if (velocity) {
159 SGVec3d staticCenter(_center);
160
161 double dtStart = velocity->referenceTime - _startTime;
162 SGVec3d startCenter = staticCenter + dtStart*velocity->linear;
163 SGQuatd startOr(SGQuatd::fromAngleAxis(dtStart*velocity->angular));
164 startCenter = startOr.transform(startCenter);
165
166 double dtEnd = velocity->referenceTime - _endTime;
167 SGVec3d endCenter = staticCenter + dtEnd*velocity->linear;
168 SGQuatd endOr(SGQuatd::fromAngleAxis(dtEnd*velocity->angular));
169 endCenter = endOr.transform(endCenter);
170
171 _center = 0.5*(startCenter + endCenter);
172 _down = startOr.transform(_down);
173 _radius += 0.5*dist(startCenter, endCenter);
174 }
175
176 simgear::BVHSubTreeCollector::NodeList parentNodeList;
177 mSubTreeCollector.pushNodeList(parentNodeList);
178
179 addBoundingVolume(transform);
180 traverse(transform);
181
182 if (mSubTreeCollector.haveChildren()) {
183 if (velocity) {
184 simgear::BVHMotionTransform* bvhTransform;
185 bvhTransform = new simgear::BVHMotionTransform;
186 bvhTransform->setToWorldTransform(SGMatrixd(matrix.ptr()));
187 bvhTransform->setLinearVelocity(velocity->linear);
188 bvhTransform->setAngularVelocity(velocity->angular);
189 bvhTransform->setReferenceTime(velocity->referenceTime);
190 bvhTransform->setStartTime(_startTime);
191 bvhTransform->setEndTime(_endTime);
192 bvhTransform->setId(velocity->id);
193
194 mSubTreeCollector.popNodeList(parentNodeList, bvhTransform);
195 } else {
196 simgear::BVHTransform* bvhTransform;
197 bvhTransform = new simgear::BVHTransform;
198 bvhTransform->setToWorldTransform(SGMatrixd(matrix.ptr()));
199
200 mSubTreeCollector.popNodeList(parentNodeList, bvhTransform);
201 }
202 } else {
203 mSubTreeCollector.popNodeList(parentNodeList);
204 }
205
206 if (_haveHit) {
207 if (velocity) {
208 double dt = _startTime - velocity->referenceTime;
209 SGQuatd ori(SGQuatd::fromAngleAxis(dt*velocity->angular));
210 _sceneryHit = ori.transform(_sceneryHit);
211 _sceneryHit += dt*velocity->linear;
212 }
213 _sceneryHit = toSG(matrix.preMult(toOsg(_sceneryHit)));
214 } else {
215 _material = material;
216 _haveHit = haveHit;
217 }
218
219 _center = center;
220 _down = down;
221 _radius = radius;
222 }
223
224 const SGSceneUserData::Velocity* getVelocity(osg::Node& node)
225 {
226 SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
227 if (!userData)
228 return 0;
229 return userData->getVelocity();
230 }
231 simgear::BVHNode* getNodeBoundingVolume(osg::Node& node)
232 {
233 SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
234 if (!userData)
235 return 0;
236 return userData->getBVHNode();
237 }
238 void addBoundingVolume(osg::Node& node)
239 {
240 simgear::BVHNode* bvNode = getNodeBoundingVolume(node);
241 if (!bvNode)
242 return;
243
244 // Find a croase ground intersection
245 SGLineSegmentd line(_center + _radius*_down, _center + _maxDown*_down);
246 simgear::BVHLineSegmentVisitor lineSegmentVisitor(line, _startTime);
247 bvNode->accept(lineSegmentVisitor);
248 if (!lineSegmentVisitor.empty()) {
249 _sceneryHit = lineSegmentVisitor.getPoint();
250 _material = lineSegmentVisitor.getMaterial();
251 _maxDown = SGMiscd::max(_radius, dot(_down, _sceneryHit - _center));
252 _haveHit = true;
253 }
254
255 // Get that part of the local bv tree that intersects our sphere
256 // of interrest.
257 mSubTreeCollector.setSphere(SGSphered(_center, _radius));
258 bvNode->accept(mSubTreeCollector);
259 }
260
261 bool testBoundingSphere(const osg::BoundingSphere& bound) const
262 {
263 if (!bound.valid())
264 return false;
265
266 SGLineSegmentd downSeg(_center, _center + _maxDown*_down);
267 double maxDist = bound._radius + _radius;
268 SGVec3d boundCenter(toVec3d(toSG(bound._center)));
269 return distSqr(downSeg, boundCenter) <= maxDist*maxDist;
270 }
271
272 SGSharedPtr<simgear::BVHNode> getBVHNode() const
273 { return mSubTreeCollector.getNode(); }
274
276 { return _haveHit; }
278 { return SGGeod::fromCart(_sceneryHit).getElevationM(); }
279 const simgear::BVHMaterial* getMaterialBelowCache() const
280 { return _material; }
281
282private:
283 SGVec3d _center;
284 SGVec3d _down;
285 double _radius;
286 double _startTime;
287 double _endTime;
288
289 simgear::BVHSubTreeCollector mSubTreeCollector;
290 SGVec3d _sceneryHit;
291 double _maxDown;
292 const simgear::BVHMaterial* _material;
293 bool _haveHit;
294};
295
297 _altitude(0),
298 _material(0),
299 cache_ref_time(0),
300 cache_time_offset(0),
301 _wire(0),
302 reference_wgs84_point(SGVec3d(0, 0, 0)),
303 reference_vehicle_radius(0),
304 down(0.0, 0.0, 0.0),
305 found_ground(false)
306{
307#ifdef GROUNDCACHE_DEBUG
308 _lookupTime = SGTimeStamp::fromSec(0.0);
309 _lookupCount = 0;
310 _buildTime = SGTimeStamp::fromSec(0.0);
311 _buildCount = 0;
312#endif
313}
314
318
319bool
320FGGroundCache::prepare_ground_cache(double startSimTime, double endSimTime,
321 const SGVec3d& pt, double rad)
322{
323 if (rad > 10000.0) {
324 SG_LOG(SG_FLIGHT, SG_DEV_WARN, "FGGroundCache::prepare_ground_cache passed an excessive radius");
325 rad = 10000.0;
326 }
327
328#ifdef GROUNDCACHE_DEBUG
329 SGTimeStamp t0 = SGTimeStamp::now();
330#endif
331
332 // Empty cache.
333 found_ground = false;
334
335 SGGeod geodPt = SGGeod::fromCart(pt);
336 // Don't blow away the cache ground_radius and stuff if there's no
337 // scenery
338 if (!globals->get_scenery()->schedule_scenery(geodPt, rad, 1.0)) {
339 SG_LOG(SG_FLIGHT, SG_BULK, "prepare_ground_cache(): scenery_available "
340 "returns false at " << geodPt << " " << pt << " " << rad);
341 return false;
342 }
343 _material = 0;
344
345 // If we have an active wire, get some more area into the groundcache
346 if (_wire)
347 rad = SGMiscd::max(200, rad);
348
349 // Store the parameters we used to build up that cache.
350 reference_wgs84_point = pt;
351 reference_vehicle_radius = rad;
352 // Store the time reference used to compute movements of moving triangles.
353 cache_ref_time = startSimTime;
354
355 // Get a normalized down vector valid for the whole cache
356 SGQuatd hlToEc = SGQuatd::fromLonLat(geodPt);
357 down = hlToEc.rotate(SGVec3d(0, 0, 1));
358
359 // Get the ground cache, that is a local collision tree of the environment
360 startSimTime += cache_time_offset;
361 endSimTime += cache_time_offset;
362 CacheFill subtreeCollector(pt, down, rad, startSimTime, endSimTime);
363 globals->get_scenery()->get_scene_graph()->accept(subtreeCollector);
364 _localBvhTree = subtreeCollector.getBVHNode();
365
366 if (subtreeCollector.getHaveElevationBelowCache()) {
367 // Use the altitude value below the cache that we gathered during
368 // cache collection
369 _altitude = subtreeCollector.getElevationBelowCache();
370 _material = subtreeCollector.getMaterialBelowCache();
371 found_ground = true;
372 } else if (_localBvhTree) {
373 // We have nothing below us, so try starting with the lowest point
374 // upwards for a coarse altitude value
375 SGLineSegmentd line(pt + reference_vehicle_radius*down, pt - 1e3*down);
376 simgear::BVHLineSegmentVisitor lineSegmentVisitor(line, startSimTime);
377 _localBvhTree->accept(lineSegmentVisitor);
378
379 if (!lineSegmentVisitor.empty()) {
380 SGGeod geodPt = SGGeod::fromCart(lineSegmentVisitor.getPoint());
381 _altitude = geodPt.getElevationM();
382 _material = lineSegmentVisitor.getMaterial();
383 found_ground = true;
384 }
385 }
386
387 if (!found_ground) {
388 // Ok, still nothing here?? Last resort ...
389 double alt = 0;
390 _material = 0;
391 found_ground = globals->get_scenery()->
392 get_elevation_m(SGGeod::fromGeodM(geodPt, 10000), alt, &_material);
393 if (found_ground)
394 _altitude = alt;
395 }
396
397 // RJH: 2018-12-31: Remove this message as it happens too frequently when flying over areas of missing terrain
398 // and realistically it doesn't really give much information to help identify or resolve a problem
399 // which is evident when looking out of the window.
400 //if (!found_ground)
401 // SG_LOG(SG_FLIGHT, SG_WARN, "prepare_ground_cache(): trying to build "
402 // "cache without any scenery below the aircraft");
403
404#ifdef GROUNDCACHE_DEBUG
405 t0 = SGTimeStamp::now() - t0;
406 _buildTime += t0;
407 _buildCount++;
408
409 if (_buildCount > 60) {
410 double buildTime = 0;
411 if (_buildCount)
412 buildTime = _buildTime.toSecs()/_buildCount;
413 double lookupTime = 0;
414 if (_lookupCount)
415 lookupTime = _lookupTime.toSecs()/_lookupCount;
416 _buildTime = SGTimeStamp::fromSec(0.0);
417 _buildCount = 0;
418 _lookupTime = SGTimeStamp::fromSec(0.0);
419 _lookupCount = 0;
420 SG_LOG(SG_FLIGHT, SG_ALERT, "build time = " << buildTime
421 << ", lookup Time = " << lookupTime);
422 }
423
424 if (!_group.valid()) {
425 _group = new osg::Group;
426 globals->get_scenery()->get_scene_graph()->addChild(_group);
427 fgSetInt("/fdm/groundcache-debug-level", -3);
428 }
429 _group->removeChildren(0, _group->getNumChildren());
430 if (_localBvhTree) {
431 int level = fgGetInt("/fdm/groundcache-debug-level");
432 if (-2 <= level) {
433 simgear::BVHDebugCollectVisitor debug(endSimTime, level);
434 _localBvhTree->accept(debug);
435 _group->addChild(debug.getNode());
436 }
437 }
438#endif
439
440 return found_ground;
441}
442
443bool
444FGGroundCache::is_valid(double& ref_time, SGVec3d& pt, double& rad)
445{
446 pt = reference_wgs84_point;
447 rad = reference_vehicle_radius;
448 ref_time = cache_ref_time;
449 return found_ground;
450}
451
452class FGGroundCache::BodyFinder : public BVHVisitor {
453public:
454 BodyFinder(BVHNode::Id id, const double& t) :
455 _id(id),
456 _bodyToWorld(SGMatrixd::unit()),
457 _linearVelocity(0, 0, 0),
458 _angularVelocity(0, 0, 0),
459 _foundId(false),
460 _time(t)
461 { }
462
463 virtual void apply(BVHGroup& leaf)
464 {
465 if (_foundId)
466 return;
467 leaf.traverse(*this);
468 }
469 virtual void apply(BVHPageNode& leaf)
470 {
471 if (_foundId)
472 return;
473 leaf.traverse(*this);
474 }
475 virtual void apply(BVHTransform& transform)
476 {
477 if (_foundId)
478 return;
479
480 transform.traverse(*this);
481
482 if (_foundId) {
483 _linearVelocity = transform.vecToWorld(_linearVelocity);
484 _angularVelocity = transform.vecToWorld(_angularVelocity);
485 _bodyToWorld = transform.getToWorldTransform()*_bodyToWorld;
486 }
487 }
488 virtual void apply(BVHMotionTransform& transform)
489 {
490 if (_foundId)
491 return;
492
493 if (_id == transform.getId()) {
494 _foundId = true;
495 } else {
496 transform.traverse(*this);
497 }
498
499 if (_foundId) {
500 SGMatrixd toWorld = transform.getToWorldTransform(_time);
501 SGVec3d referencePoint = _bodyToWorld.xformPt(SGVec3d::zeros());
502 _linearVelocity += transform.getLinearVelocityAt(referencePoint);
503 _angularVelocity += transform.getAngularVelocity();
504 _linearVelocity = toWorld.xformVec(_linearVelocity);
505 _angularVelocity = toWorld.xformVec(_angularVelocity);
506 _bodyToWorld = toWorld*_bodyToWorld;
507 }
508 }
509 virtual void apply(BVHLineGeometry& node) { }
510 virtual void apply(BVHStaticGeometry& node) { }
511 virtual void apply(BVHTerrainTile& node) { }
512
513 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
514 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
515
516 const SGMatrixd& getBodyToWorld() const
517 { return _bodyToWorld; }
518 const SGVec3d& getLinearVelocity() const
519 { return _linearVelocity; }
520 const SGVec3d& getAngularVelocity() const
521 { return _angularVelocity; }
522
523 bool empty() const
524 { return !_foundId; }
525
526protected:
527 simgear::BVHNode::Id _id;
528
529 SGMatrixd _bodyToWorld;
530
533
535
536 double _time;
537};
538
539bool
540FGGroundCache::get_body(double t, SGMatrixd& bodyToWorld, SGVec3d& linearVel,
541 SGVec3d& angularVel, simgear::BVHNode::Id id)
542{
543 // Get the transform matrix and velocities of a moving body with id at t.
544 if (!_localBvhTree)
545 return false;
546 t += cache_time_offset;
547 BodyFinder bodyFinder(id, t);
548 _localBvhTree->accept(bodyFinder);
549
550 bodyToWorld = bodyFinder.getBodyToWorld();
551 linearVel = bodyFinder.getLinearVelocity();
552 angularVel = bodyFinder.getAngularVelocity();
553
554 if (bodyFinder.empty())
555 return false;
556
557 return true;
558}
559
560class FGGroundCache::CatapultFinder : public BVHVisitor {
561public:
562 CatapultFinder(const SGSphered& sphere, const double& t) :
563 _haveLineSegment(false),
564 _sphere(sphere),
565 _time(t)
566 { }
567
568 virtual void apply(BVHGroup& leaf)
569 {
570 if (!intersects(_sphere, leaf.getBoundingSphere()))
571 return;
572 leaf.traverse(*this);
573 }
574 virtual void apply(BVHPageNode& leaf)
575 {
576 if (!intersects(_sphere, leaf.getBoundingSphere()))
577 return;
578 leaf.traverse(*this);
579 }
580 virtual void apply(BVHTransform& transform)
581 {
582 if (!intersects(_sphere, transform.getBoundingSphere()))
583 return;
584
585 SGSphered sphere = _sphere;
586 _sphere = transform.sphereToLocal(sphere);
587 bool haveLineSegment = _haveLineSegment;
588 _haveLineSegment = false;
589
590 transform.traverse(*this);
591
592 if (_haveLineSegment) {
593 _lineSegment = transform.lineSegmentToWorld(_lineSegment);
594 _linearVelocity = transform.vecToWorld(_linearVelocity);
595 _angularVelocity = transform.vecToWorld(_angularVelocity);
596 }
597 _haveLineSegment |= haveLineSegment;
598 _sphere.setCenter(sphere.getCenter());
599 }
600 virtual void apply(BVHMotionTransform& transform)
601 {
602 if (!intersects(_sphere, transform.getBoundingSphere()))
603 return;
604
605 SGSphered sphere = _sphere;
606 _sphere = transform.sphereToLocal(sphere, _time);
607 bool haveLineSegment = _haveLineSegment;
608 _haveLineSegment = false;
609
610 transform.traverse(*this);
611
612 if (_haveLineSegment) {
613 SGMatrixd toWorld = transform.getToWorldTransform(_time);
615 += transform.getLinearVelocityAt(_lineSegment.getStart());
616 _angularVelocity += transform.getAngularVelocity();
617 _linearVelocity = toWorld.xformVec(_linearVelocity);
618 _angularVelocity = toWorld.xformVec(_angularVelocity);
619 _lineSegment = _lineSegment.transform(toWorld);
620 }
621 _haveLineSegment |= haveLineSegment;
622 _sphere.setCenter(sphere.getCenter());
623 }
624 virtual void apply(BVHLineGeometry& node)
625 {
626 if (node.getType() != BVHLineGeometry::CarrierCatapult)
627 return;
628
629 SGLineSegmentd lineSegment(node.getLineSegment());
630 if (!intersects(_sphere, lineSegment))
631 return;
632
633 _lineSegment = lineSegment;
634 double dist = distSqr(lineSegment, getSphere().getCenter());
635 _sphere.setRadius(sqrt(dist));
636 _linearVelocity = SGVec3d::zeros();
637 _angularVelocity = SGVec3d::zeros();
638 _haveLineSegment = true;
639 }
640 virtual void apply(BVHStaticGeometry& node) { }
641 virtual void apply(BVHTerrainTile& node) { }
642
643 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
644 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
645
646 void setSphere(const SGSphered& sphere)
647 { _sphere = sphere; }
648 const SGSphered& getSphere() const
649 { return _sphere; }
650
651 const SGLineSegmentd& getLineSegment() const
652 { return _lineSegment; }
653 const SGVec3d& getLinearVelocity() const
654 { return _linearVelocity; }
655 const SGVec3d& getAngularVelocity() const
656 { return _angularVelocity; }
657
659 { return _haveLineSegment; }
660
661protected:
662 SGLineSegmentd _lineSegment;
665
667
668 SGSphered _sphere;
669 double _time;
670};
671
672double
673FGGroundCache::get_cat(double t, const SGVec3d& pt,
674 SGVec3d end[2], SGVec3d vel[2])
675{
676 double maxDistance = 1000;
677
678 // Get the wire in question
679 t += cache_time_offset;
680 CatapultFinder catapultFinder(SGSphered(pt, maxDistance), t);
681 if (_localBvhTree)
682 _localBvhTree->accept(catapultFinder);
683
684 if (!catapultFinder.getHaveLineSegment())
685 return maxDistance;
686
687 // prepare the returns
688 end[0] = catapultFinder.getLineSegment().getStart();
689 end[1] = catapultFinder.getLineSegment().getEnd();
690
691 // The linear velocity is the one at the start of the line segment ...
692 vel[0] = catapultFinder.getLinearVelocity();
693 // ... so the end point has the additional cross product.
694 vel[1] = catapultFinder.getLinearVelocity();
695 vel[1] += cross(catapultFinder.getAngularVelocity(),
696 catapultFinder.getLineSegment().getDirection());
697
698 // Return the distance to the cat
699 return sqrt(distSqr(catapultFinder.getLineSegment(), pt));
700}
701
702bool
703FGGroundCache::get_agl(double t, const SGVec3d& pt, SGVec3d& contact,
704 SGVec3d& normal, SGVec3d& linearVel, SGVec3d& angularVel,
705 simgear::BVHNode::Id& id, const simgear::BVHMaterial*& material)
706{
707 if (isNaN(pt)) {
708 throw sg_range_exception("FGGroundCache::get_agl: NaN position input");
709 }
710
711#ifdef GROUNDCACHE_DEBUG
712 SGTimeStamp t0 = SGTimeStamp::now();
713#endif
714
715 // Just set up a ground intersection query for the given point
716 SGLineSegmentd line(pt, pt + 10*reference_vehicle_radius*down);
717 t += cache_time_offset;
718 simgear::BVHLineSegmentVisitor lineSegmentVisitor(line, t);
719 if (_localBvhTree)
720 _localBvhTree->accept(lineSegmentVisitor);
721
722#ifdef GROUNDCACHE_DEBUG
723 t0 = SGTimeStamp::now() - t0;
724 _lookupTime += t0;
725 _lookupCount++;
726#endif
727
728 if (!lineSegmentVisitor.empty()) {
729 // Have an intersection
730 contact = lineSegmentVisitor.getPoint();
731 normal = lineSegmentVisitor.getNormal();
732 if (0 < dot(normal, down))
733 normal = -normal;
734 linearVel = lineSegmentVisitor.getLinearVelocity();
735 angularVel = lineSegmentVisitor.getAngularVelocity();
736 material = lineSegmentVisitor.getMaterial();
737 id = lineSegmentVisitor.getId();
738
739 return true;
740 } else {
741 // Whenever we did not have a ground triangle for the requested point,
742 // take the ground level we found during the current cache build.
743 // This is as good as what we had before for agl.
744 SGGeod geodPt = SGGeod::fromCart(pt);
745 geodPt.setElevationM(_altitude);
746 contact = SGVec3d::fromGeod(geodPt);
747 normal = -down;
748 linearVel = SGVec3d(0, 0, 0);
749 angularVel = SGVec3d(0, 0, 0);
750 material = _material;
751 id = 0;
752
753 return found_ground;
754 }
755}
756
757
758bool
759FGGroundCache::get_nearest(double t, const SGVec3d& pt, double maxDist,
760 SGVec3d& contact, SGVec3d& linearVel,
761 SGVec3d& angularVel, simgear::BVHNode::Id& id,
762 const simgear::BVHMaterial*& material)
763{
764 if (!_localBvhTree)
765 return false;
766
767#ifdef GROUNDCACHE_DEBUG
768 SGTimeStamp t0 = SGTimeStamp::now();
769#endif
770
771 // Just set up a ground intersection query for the given point
772 SGSphered sphere(pt, maxDist);
773 t += cache_time_offset;
774 simgear::BVHNearestPointVisitor nearestPointVisitor(sphere, t);
775 _localBvhTree->accept(nearestPointVisitor);
776
777#ifdef GROUNDCACHE_DEBUG
778 t0 = SGTimeStamp::now() - t0;
779 _lookupTime += t0;
780 _lookupCount++;
781#endif
782
783 if (nearestPointVisitor.empty())
784 return false;
785
786 // Have geometry in the range of maxDist
787 contact = nearestPointVisitor.getPoint();
788 linearVel = nearestPointVisitor.getLinearVelocity();
789 angularVel = nearestPointVisitor.getAngularVelocity();
790 material = nearestPointVisitor.getMaterial();
791 id = nearestPointVisitor.getId();
792
793 return true;
794}
795
796
797class FGGroundCache::WireIntersector : public BVHVisitor {
798public:
799 WireIntersector(const SGVec3d pt[4], const double& t) :
800 _linearVelocity(SGVec3d::zeros()),
801 _angularVelocity(SGVec3d::zeros()),
802 _wire(0),
803 _time(t)
804 {
805 // Build the two triangles spanning the area where the hook has moved
806 // during the past step.
807 _triangles[0].set(pt[0], pt[1], pt[2]);
808 _triangles[1].set(pt[0], pt[2], pt[3]);
809 }
810
811 virtual void apply(BVHGroup& leaf)
812 {
813 if (!_intersects(leaf.getBoundingSphere()))
814 return;
815
816 leaf.traverse(*this);
817 }
818 virtual void apply(BVHPageNode& leaf)
819 {
820 if (!_intersects(leaf.getBoundingSphere()))
821 return;
822
823 leaf.traverse(*this);
824 }
825 virtual void apply(BVHTransform& transform)
826 {
827 if (!_intersects(transform.getBoundingSphere()))
828 return;
829
830 SGTriangled triangles[2] = { _triangles[0], _triangles[1] };
831 _triangles[0] = triangles[0].transform(transform.getToLocalTransform());
832 _triangles[1] = triangles[1].transform(transform.getToLocalTransform());
833
834 transform.traverse(*this);
835
836 if (_wire) {
837 _lineSegment = transform.lineSegmentToWorld(_lineSegment);
838 _linearVelocity = transform.vecToWorld(_linearVelocity);
839 _angularVelocity = transform.vecToWorld(_angularVelocity);
840 }
841 _triangles[0] = triangles[0];
842 _triangles[1] = triangles[1];
843 }
844 virtual void apply(BVHMotionTransform& transform)
845 {
846 if (!_intersects(transform.getBoundingSphere()))
847 return;
848
849 SGMatrixd toLocal = transform.getToLocalTransform(_time);
850
851 SGTriangled triangles[2] = { _triangles[0], _triangles[1] };
852 _triangles[0] = triangles[0].transform(toLocal);
853 _triangles[1] = triangles[1].transform(toLocal);
854
855 transform.traverse(*this);
856
857 if (_wire) {
858 SGMatrixd toWorld = transform.getToWorldTransform(_time);
859 _linearVelocity
860 += transform.getLinearVelocityAt(_lineSegment.getStart());
861 _angularVelocity += transform.getAngularVelocity();
862 _linearVelocity = toWorld.xformVec(_linearVelocity);
863 _angularVelocity = toWorld.xformVec(_angularVelocity);
864 _lineSegment = _lineSegment.transform(toWorld);
865 }
866 _triangles[0] = triangles[0];
867 _triangles[1] = triangles[1];
868 }
869 virtual void apply(BVHLineGeometry& node)
870 {
871 if (node.getType() != BVHLineGeometry::CarrierWire)
872 return;
873 SGLineSegmentd lineSegment(node.getLineSegment());
874 if (!_intersects(lineSegment))
875 return;
876
877 _lineSegment = lineSegment;
878 _linearVelocity = SGVec3d::zeros();
879 _angularVelocity = SGVec3d::zeros();
880 _wire = &node;
881 }
882 virtual void apply(BVHStaticGeometry& node) { }
883 virtual void apply(BVHTerrainTile& node) { }
884
885 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
886 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
887
888 bool _intersects(const SGSphered& sphere) const
889 {
890 if (_wire)
891 return false;
892 if (intersects(_triangles[0], sphere))
893 return true;
894 if (intersects(_triangles[1], sphere))
895 return true;
896 return false;
897 }
898 bool _intersects(const SGLineSegmentd& lineSegment) const
899 {
900 if (_wire)
901 return false;
902 if (intersects(_triangles[0], lineSegment))
903 return true;
904 if (intersects(_triangles[1], lineSegment))
905 return true;
906 return false;
907 }
908
909 const SGLineSegmentd& getLineSegment() const
910 { return _lineSegment; }
911 const SGVec3d& getLinearVelocity() const
912 { return _linearVelocity; }
913 const SGVec3d& getAngularVelocity() const
914 { return _angularVelocity; }
915
916 const BVHLineGeometry* getWire() const
917 { return _wire; }
918
919private:
920 SGLineSegmentd _lineSegment;
921 SGVec3d _linearVelocity;
922 SGVec3d _angularVelocity;
923 const BVHLineGeometry* _wire;
924
925 SGTriangled _triangles[2];
926 double _time;
927};
928
929bool FGGroundCache::caught_wire(double t, const SGVec3d pt[4])
930{
931 // Get the wire in question
932 t += cache_time_offset;
933 WireIntersector wireIntersector(pt, t);
934 if (_localBvhTree)
935 _localBvhTree->accept(wireIntersector);
936
937 _wire = wireIntersector.getWire();
938 return (_wire != NULL);
939}
940
941class FGGroundCache::WireFinder : public BVHVisitor {
942public:
943 WireFinder(const BVHLineGeometry* wire, const double& t) :
944 _wire(wire),
945 _time(t),
946 _lineSegment(SGVec3d::zeros(), SGVec3d::zeros()),
947 _linearVelocity(SGVec3d::zeros()),
948 _angularVelocity(SGVec3d::zeros()),
949 _haveLineSegment(false)
950 { }
951
952 virtual void apply(BVHGroup& leaf)
953 {
954 if (_haveLineSegment)
955 return;
956 leaf.traverse(*this);
957 }
958 virtual void apply(BVHPageNode& leaf)
959 {
960 if (_haveLineSegment)
961 return;
962 leaf.traverse(*this);
963 }
964 virtual void apply(BVHTransform& transform)
965 {
966 if (_haveLineSegment)
967 return;
968
969 transform.traverse(*this);
970
971 if (_haveLineSegment) {
972 _linearVelocity = transform.vecToWorld(_linearVelocity);
973 _angularVelocity = transform.vecToWorld(_angularVelocity);
974 _lineSegment = transform.lineSegmentToWorld(_lineSegment);
975 }
976 }
977 virtual void apply(BVHMotionTransform& transform)
978 {
979 if (_haveLineSegment)
980 return;
981
982 transform.traverse(*this);
983
984 if (_haveLineSegment) {
985 SGMatrixd toWorld = transform.getToWorldTransform(_time);
986 _linearVelocity
987 += transform.getLinearVelocityAt(_lineSegment.getStart());
988 _angularVelocity += transform.getAngularVelocity();
989 _linearVelocity = toWorld.xformVec(_linearVelocity);
990 _angularVelocity = toWorld.xformVec(_angularVelocity);
991 _lineSegment = _lineSegment.transform(toWorld);
992 }
993 }
994 virtual void apply(BVHLineGeometry& node)
995 {
996 if (_haveLineSegment)
997 return;
998 if (_wire != &node)
999 return;
1000 if (node.getType() != BVHLineGeometry::CarrierWire)
1001 return;
1002 _lineSegment = SGLineSegmentd(node.getLineSegment());
1003 _linearVelocity = SGVec3d::zeros();
1004 _angularVelocity = SGVec3d::zeros();
1005 _haveLineSegment = true;
1006 }
1007 virtual void apply(BVHStaticGeometry&) { }
1008 virtual void apply(BVHTerrainTile&) { }
1009
1010 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
1011 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
1012
1013 const SGLineSegmentd& getLineSegment() const
1014 { return _lineSegment; }
1015
1017 { return _haveLineSegment; }
1018
1019 const SGVec3d& getLinearVelocity() const
1020 { return _linearVelocity; }
1021 const SGVec3d& getAngularVelocity() const
1022 { return _angularVelocity; }
1023
1024private:
1025 const BVHLineGeometry* _wire;
1026 double _time;
1027
1028 SGLineSegmentd _lineSegment;
1029 SGVec3d _linearVelocity;
1030 SGVec3d _angularVelocity;
1031
1032 bool _haveLineSegment;
1033};
1034
1035bool FGGroundCache::get_wire_ends(double t, SGVec3d end[2], SGVec3d vel[2])
1036{
1037 // Fast return if we do not have an active wire.
1038 if (!_wire)
1039 return false;
1040
1041 // Get the wire in question
1042 t += cache_time_offset;
1043 WireFinder wireFinder(_wire, t);
1044 if (_localBvhTree)
1045 _localBvhTree->accept(wireFinder);
1046
1047 if (!wireFinder.getHaveLineSegment())
1048 return false;
1049
1050 // prepare the returns
1051 end[0] = wireFinder.getLineSegment().getStart();
1052 end[1] = wireFinder.getLineSegment().getEnd();
1053
1054 // The linear velocity is the one at the start of the line segment ...
1055 vel[0] = wireFinder.getLinearVelocity();
1056 // ... so the end point has the additional cross product.
1057 vel[1] = wireFinder.getLinearVelocity();
1058 vel[1] += cross(wireFinder.getAngularVelocity(),
1059 wireFinder.getLineSegment().getDirection());
1060
1061 return true;
1062}
1063
1065{
1066 _wire = 0;
1067}
virtual void apply(BVHGroup &leaf)
BodyFinder(BVHNode::Id id, const double &t)
virtual void apply(BVHLineGeometry &node)
const SGMatrixd & getBodyToWorld() const
const SGVec3d & getLinearVelocity() const
virtual void apply(BVHStaticGeometry &node)
const SGVec3d & getAngularVelocity() const
virtual void apply(BVHMotionTransform &transform)
virtual void apply(BVHTerrainTile &node)
virtual void apply(const BVHStaticBinary &, const BVHStaticData &)
virtual void apply(const BVHStaticTriangle &, const BVHStaticData &)
simgear::BVHNode::Id _id
virtual void apply(BVHTransform &transform)
virtual void apply(BVHPageNode &leaf)
virtual void apply(osg::Camera &camera)
void addBoundingVolume(osg::Node &node)
bool getHaveElevationBelowCache() const
bool testBoundingSphere(const osg::BoundingSphere &bound) const
void handleTransform(osg::Transform &transform)
virtual void apply(osg::MatrixTransform &transform)
const SGSceneUserData::Velocity * getVelocity(osg::Node &node)
double getElevationBelowCache() const
CacheFill(const SGVec3d &center, const SGVec3d &down, const double &radius, const double &startTime, const double &endTime)
virtual void apply(osg::CameraView &transform)
virtual void apply(osg::Group &group)
virtual void apply(osg::PositionAttitudeTransform &transform)
virtual void apply(osg::Node &node)
virtual void apply(osg::Transform &transform)
SGSharedPtr< simgear::BVHNode > getBVHNode() const
simgear::BVHNode * getNodeBoundingVolume(osg::Node &node)
const simgear::BVHMaterial * getMaterialBelowCache() const
const SGSphered & getSphere() const
void setSphere(const SGSphered &sphere)
virtual void apply(BVHStaticGeometry &node)
virtual void apply(const BVHStaticBinary &, const BVHStaticData &)
virtual void apply(BVHTransform &transform)
virtual void apply(BVHTerrainTile &node)
virtual void apply(BVHMotionTransform &transform)
virtual void apply(const BVHStaticTriangle &, const BVHStaticData &)
virtual void apply(BVHPageNode &leaf)
virtual void apply(BVHGroup &leaf)
const SGLineSegmentd & getLineSegment() const
const SGVec3d & getLinearVelocity() const
virtual void apply(BVHLineGeometry &node)
CatapultFinder(const SGSphered &sphere, const double &t)
const SGVec3d & getAngularVelocity() const
virtual void apply(const BVHStaticTriangle &, const BVHStaticData &)
virtual void apply(BVHLineGeometry &node)
virtual void apply(BVHGroup &leaf)
virtual void apply(BVHMotionTransform &transform)
WireFinder(const BVHLineGeometry *wire, const double &t)
const SGVec3d & getLinearVelocity() const
virtual void apply(BVHTransform &transform)
const SGVec3d & getAngularVelocity() const
virtual void apply(BVHTerrainTile &)
virtual void apply(BVHStaticGeometry &)
virtual void apply(const BVHStaticBinary &, const BVHStaticData &)
virtual void apply(BVHPageNode &leaf)
const SGLineSegmentd & getLineSegment() const
virtual void apply(BVHLineGeometry &node)
virtual void apply(BVHTransform &transform)
virtual void apply(const BVHStaticBinary &, const BVHStaticData &)
virtual void apply(const BVHStaticTriangle &, const BVHStaticData &)
const SGVec3d & getAngularVelocity() const
bool _intersects(const SGSphered &sphere) const
const BVHLineGeometry * getWire() const
virtual void apply(BVHTerrainTile &node)
const SGVec3d & getLinearVelocity() const
virtual void apply(BVHMotionTransform &transform)
WireIntersector(const SGVec3d pt[4], const double &t)
bool _intersects(const SGLineSegmentd &lineSegment) const
const SGLineSegmentd & getLineSegment() const
virtual void apply(BVHStaticGeometry &node)
virtual void apply(BVHGroup &leaf)
virtual void apply(BVHPageNode &leaf)
bool get_body(double t, SGMatrixd &bodyToWorld, SGVec3d &linearVel, SGVec3d &angularVel, simgear::BVHNode::Id id)
bool get_agl(double t, const SGVec3d &pt, SGVec3d &contact, SGVec3d &normal, SGVec3d &linearVel, SGVec3d &angularVel, simgear::BVHNode::Id &id, const simgear::BVHMaterial *&material)
bool is_valid(double &ref_time, SGVec3d &pt, double &rad)
void release_wire(void)
double get_cat(double t, const SGVec3d &pt, SGVec3d end[2], SGVec3d vel[2])
bool get_nearest(double t, const SGVec3d &pt, double maxDist, SGVec3d &contact, SGVec3d &linearVel, SGVec3d &angularVel, simgear::BVHNode::Id &id, const simgear::BVHMaterial *&material)
bool prepare_ground_cache(double startSimTime, double endSimTime, const SGVec3d &pt, double rad)
bool get_wire_ends(double t, SGVec3d end[2], SGVec3d vel[2])
bool caught_wire(double t, const SGVec3d pt[4])
int fgGetInt(const char *name, int defaultValue)
Get an int value for a property.
Definition fg_props.cxx:532
bool fgSetInt(const char *name, int val)
Set an int value for a property.
Definition fg_props.cxx:568
FGGlobals * globals
Definition globals.cxx:142
Definition AIBase.hxx:25