1 // groundcache.cxx -- carries a small subset of the scenegraph near the vehicle
3 // Written by Mathias Froehlich, started Nov 2004.
5 // Copyright (C) 2004, 2009 Mathias Froehlich - Mathias.Froehlich@web.de
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.
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.
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.
29 #include <osg/Drawable>
31 #include <osg/Geometry>
33 #include <osg/Transform>
34 #include <osg/MatrixTransform>
35 #include <osg/PositionAttitudeTransform>
36 #include <osg/CameraView>
38 #include <simgear/sg_inlines.h>
39 #include <simgear/constants.h>
40 #include <simgear/debug/logstream.hxx>
41 #include <simgear/math/sg_geodesy.hxx>
42 #include <simgear/scene/util/SGNodeMasks.hxx>
43 #include <simgear/scene/util/SGSceneUserData.hxx>
45 #include <simgear/scene/bvh/BVHNode.hxx>
46 #include <simgear/scene/bvh/BVHGroup.hxx>
47 #include <simgear/scene/bvh/BVHTransform.hxx>
48 #include <simgear/scene/bvh/BVHMotionTransform.hxx>
49 #include <simgear/scene/bvh/BVHLineGeometry.hxx>
50 #include <simgear/scene/bvh/BVHStaticGeometry.hxx>
51 #include <simgear/scene/bvh/BVHStaticData.hxx>
52 #include <simgear/scene/bvh/BVHStaticNode.hxx>
53 #include <simgear/scene/bvh/BVHStaticTriangle.hxx>
54 #include <simgear/scene/bvh/BVHStaticBinary.hxx>
55 #include <simgear/scene/bvh/BVHSubTreeCollector.hxx>
56 #include <simgear/scene/bvh/BVHLineSegmentVisitor.hxx>
57 #include <simgear/scene/bvh/BVHNearestPointVisitor.hxx>
59 #include <Main/globals.hxx>
60 #include <Scenery/scenery.hxx>
61 #include <Scenery/tilemgr.hxx>
64 #include "groundcache.hxx"
66 using namespace simgear;
68 class FGGroundCache::CacheFill : public osg::NodeVisitor {
70 CacheFill(const SGVec3d& center, const double& radius,
71 const double& startTime, const double& endTime) :
72 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
75 _startTime(startTime),
78 setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
80 virtual void apply(osg::Node& node)
82 if (!testBoundingSphere(node.getBound()))
85 addBoundingVolume(node);
88 virtual void apply(osg::Group& group)
90 if (!testBoundingSphere(group.getBound()))
93 simgear::BVHSubTreeCollector::NodeList parentNodeList;
94 mSubTreeCollector.pushNodeList(parentNodeList);
97 addBoundingVolume(group);
99 mSubTreeCollector.popNodeList(parentNodeList);
102 virtual void apply(osg::Transform& transform)
103 { handleTransform(transform); }
104 virtual void apply(osg::Camera& camera)
106 if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
108 handleTransform(camera);
110 virtual void apply(osg::CameraView& transform)
111 { handleTransform(transform); }
112 virtual void apply(osg::MatrixTransform& transform)
113 { handleTransform(transform); }
114 virtual void apply(osg::PositionAttitudeTransform& transform)
115 { handleTransform(transform); }
117 void handleTransform(osg::Transform& transform)
119 // Hmm, may be this needs to be refined somehow ...
120 if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
123 if (!testBoundingSphere(transform.getBound()))
126 osg::Matrix inverseMatrix;
127 if (!transform.computeWorldToLocalMatrix(inverseMatrix, this))
130 if (!transform.computeLocalToWorldMatrix(matrix, this))
133 // Look for a velocity note
134 const SGSceneUserData::Velocity* velocity = getVelocity(transform);
136 SGVec3d center = _center;
137 _center = SGVec3d(inverseMatrix.preMult(_center.osg()));
138 double radius = _radius;
140 _radius += (_endTime - _startTime)*norm(velocity->linear);
142 simgear::BVHSubTreeCollector::NodeList parentNodeList;
143 mSubTreeCollector.pushNodeList(parentNodeList);
145 addBoundingVolume(transform);
148 if (mSubTreeCollector.haveChildren()) {
150 simgear::BVHMotionTransform* bvhTransform;
151 bvhTransform = new simgear::BVHMotionTransform;
152 bvhTransform->setToWorldTransform(SGMatrixd(matrix.ptr()));
153 bvhTransform->setLinearVelocity(velocity->linear);
154 bvhTransform->setAngularVelocity(velocity->angular);
155 bvhTransform->setReferenceTime(_startTime);
156 bvhTransform->setEndTime(_endTime);
157 bvhTransform->setId(velocity->id);
159 mSubTreeCollector.popNodeList(parentNodeList, bvhTransform);
161 simgear::BVHTransform* bvhTransform;
162 bvhTransform = new simgear::BVHTransform;
163 bvhTransform->setToWorldTransform(SGMatrixd(matrix.ptr()));
165 mSubTreeCollector.popNodeList(parentNodeList, bvhTransform);
168 mSubTreeCollector.popNodeList(parentNodeList);
174 const SGSceneUserData::Velocity* getVelocity(osg::Node& node)
176 SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
179 return userData->getVelocity();
181 simgear::BVHNode* getNodeBoundingVolume(osg::Node& node)
183 SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
186 return userData->getBVHNode();
188 void addBoundingVolume(osg::Node& node)
190 simgear::BVHNode* bvNode = getNodeBoundingVolume(node);
194 // Get that part of the local bv tree that intersects our sphere
196 mSubTreeCollector.setSphere(SGSphered(_center, _radius));
197 bvNode->accept(mSubTreeCollector);
200 bool testBoundingSphere(const osg::BoundingSphere& bound) const
205 double maxDist = bound._radius + _radius;
206 return distSqr(SGVec3d(bound._center), _center) <= maxDist*maxDist;
209 SGSharedPtr<simgear::BVHNode> getBVHNode() const
210 { return mSubTreeCollector.getNode(); }
219 simgear::BVHSubTreeCollector mSubTreeCollector;
222 FGGroundCache::FGGroundCache() :
227 reference_wgs84_point(SGVec3d(0, 0, 0)),
228 reference_vehicle_radius(0),
234 FGGroundCache::~FGGroundCache()
239 FGGroundCache::prepare_ground_cache(double ref_time, const SGVec3d& pt,
243 found_ground = false;
245 SGGeod geodPt = SGGeod::fromCart(pt);
246 // Don't blow away the cache ground_radius and stuff if there's no
248 if (!globals->get_tile_mgr()->scenery_available(geodPt, rad)) {
249 SG_LOG(SG_FLIGHT, SG_WARN, "prepare_ground_cache(): scenery_available "
250 "returns false at " << geodPt << " " << pt << " " << rad);
255 // If we have an active wire, get some more area into the groundcache
257 rad = SGMiscd::max(200, rad);
259 // Store the parameters we used to build up that cache.
260 reference_wgs84_point = pt;
261 reference_vehicle_radius = rad;
262 // Store the time reference used to compute movements of moving triangles.
263 cache_ref_time = ref_time;
265 // Get a normalized down vector valid for the whole cache
266 SGQuatd hlToEc = SGQuatd::fromLonLat(geodPt);
267 down = hlToEc.rotate(SGVec3d(0, 0, 1));
269 // Get the ground cache, that is a local collision tree of the environment
270 double endTime = cache_ref_time + 1; //FIXME??
271 CacheFill subtreeCollector(pt, rad, cache_ref_time, endTime);
272 globals->get_scenery()->get_scene_graph()->accept(subtreeCollector);
273 _localBvhTree = subtreeCollector.getBVHNode();
275 // Try to get a croase altitude value for the ground cache
276 SGLineSegmentd line(pt, pt + 2*reference_vehicle_radius*down);
277 simgear::BVHLineSegmentVisitor lineSegmentVisitor(line, ref_time);
279 _localBvhTree->accept(lineSegmentVisitor);
281 // If this is successful, store this altitude for croase altitude values
282 if (!lineSegmentVisitor.empty()) {
283 SGGeod geodPt = SGGeod::fromCart(lineSegmentVisitor.getPoint());
284 _altitude = geodPt.getElevationM();
285 _material = lineSegmentVisitor.getMaterial();
288 // Else do a crude scene query for the current point
290 found_ground = globals->get_scenery()->
291 get_cart_elevation_m(pt, rad, alt, &_material);
296 // Still not sucessful??
298 SG_LOG(SG_FLIGHT, SG_WARN, "prepare_ground_cache(): trying to build "
299 "cache without any scenery below the aircraft");
305 FGGroundCache::is_valid(double& ref_time, SGVec3d& pt, double& rad)
307 pt = reference_wgs84_point;
308 rad = reference_vehicle_radius;
309 ref_time = cache_ref_time;
313 class FGGroundCache::BodyFinder : public BVHVisitor {
315 BodyFinder(BVHNode::Id id, const double& t) :
317 _bodyToWorld(SGMatrixd::unit()),
318 _linearVelocity(0, 0, 0),
319 _angularVelocity(0, 0, 0),
323 virtual void apply(BVHGroup& leaf)
327 leaf.traverse(*this);
329 virtual void apply(BVHTransform& transform)
334 transform.traverse(*this);
337 _linearVelocity = transform.vecToWorld(_linearVelocity);
338 _angularVelocity = transform.vecToWorld(_angularVelocity);
339 _bodyToWorld = transform.getToWorldTransform()*_bodyToWorld;
342 virtual void apply(BVHMotionTransform& transform)
347 if (_id == transform.getId()) {
352 transform.traverse(*this);
355 SGMatrixd toWorld = transform.getToWorldTransform(_time);
356 SGVec3d referencePoint = _bodyToWorld.xformPt(SGVec3d::zeros());
357 _linearVelocity += transform.getLinearVelocityAt(referencePoint);
358 _angularVelocity += transform.getAngularVelocity();
359 _linearVelocity = toWorld.xformVec(_linearVelocity);
360 _angularVelocity = toWorld.xformVec(_angularVelocity);
361 _bodyToWorld = toWorld*_bodyToWorld;
364 virtual void apply(BVHLineGeometry& node) { }
365 virtual void apply(BVHStaticGeometry& node) { }
367 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
368 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
370 const SGMatrixd& getBodyToWorld() const
371 { return _bodyToWorld; }
372 const SGVec3d& getLinearVelocity() const
373 { return _linearVelocity; }
374 const SGVec3d& getAngularVelocity() const
375 { return _angularVelocity; }
378 { return !_foundId; }
381 simgear::BVHNode::Id _id;
383 SGMatrixd _bodyToWorld;
385 SGVec3d _linearVelocity;
386 SGVec3d _angularVelocity;
394 FGGroundCache::get_body(double t, SGMatrixd& bodyToWorld, SGVec3d& linearVel,
395 SGVec3d& angularVel, simgear::BVHNode::Id id)
397 // Get the transform matrix and velocities of a moving body with id at t.
400 BodyFinder bodyFinder(id, t);
401 _localBvhTree->accept(bodyFinder);
402 if (bodyFinder.empty())
405 bodyToWorld = bodyFinder.getBodyToWorld();
406 linearVel = bodyFinder.getLinearVelocity();
407 angularVel = bodyFinder.getAngularVelocity();
412 class FGGroundCache::CatapultFinder : public BVHVisitor {
414 CatapultFinder(const SGSphered& sphere, const double& t) :
417 _haveLineSegment(false)
420 virtual void apply(BVHGroup& leaf)
422 if (!intersects(_sphere, leaf.getBoundingSphere()))
424 leaf.traverse(*this);
426 virtual void apply(BVHTransform& transform)
428 if (!intersects(_sphere, transform.getBoundingSphere()))
431 SGSphered sphere = _sphere;
432 _sphere = transform.sphereToLocal(sphere);
433 bool haveLineSegment = _haveLineSegment;
434 _haveLineSegment = false;
436 transform.traverse(*this);
438 if (_haveLineSegment) {
439 _lineSegment = transform.lineSegmentToWorld(_lineSegment);
440 _linearVelocity = transform.vecToWorld(_linearVelocity);
441 _angularVelocity = transform.vecToWorld(_angularVelocity);
443 _haveLineSegment |= haveLineSegment;
444 _sphere.setCenter(sphere.getCenter());
446 virtual void apply(BVHMotionTransform& transform)
448 if (!intersects(_sphere, transform.getBoundingSphere()))
451 SGSphered sphere = _sphere;
452 _sphere = transform.sphereToLocal(sphere, _time);
453 bool haveLineSegment = _haveLineSegment;
454 _haveLineSegment = false;
456 transform.traverse(*this);
458 if (_haveLineSegment) {
459 SGMatrixd toWorld = transform.getToWorldTransform(_time);
461 += transform.getLinearVelocityAt(_lineSegment.getStart());
462 _angularVelocity += transform.getAngularVelocity();
463 _linearVelocity = toWorld.xformVec(_linearVelocity);
464 _angularVelocity = toWorld.xformVec(_angularVelocity);
465 _lineSegment = _lineSegment.transform(toWorld);
467 _haveLineSegment |= haveLineSegment;
468 _sphere.setCenter(sphere.getCenter());
470 virtual void apply(BVHLineGeometry& node)
472 if (node.getType() != BVHLineGeometry::CarrierCatapult)
475 SGLineSegmentd lineSegment(node.getLineSegment());
476 if (!intersects(_sphere, lineSegment))
479 _lineSegment = lineSegment;
480 double dist = distSqr(lineSegment, getSphere().getCenter());
481 _sphere.setRadius(sqrt(dist));
482 _linearVelocity = SGVec3d::zeros();
483 _angularVelocity = SGVec3d::zeros();
484 _haveLineSegment = true;
486 virtual void apply(BVHStaticGeometry& node)
489 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
490 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
492 void setSphere(const SGSphered& sphere)
493 { _sphere = sphere; }
494 const SGSphered& getSphere() const
497 const SGLineSegmentd& getLineSegment() const
498 { return _lineSegment; }
499 const SGVec3d& getLinearVelocity() const
500 { return _linearVelocity; }
501 const SGVec3d& getAngularVelocity() const
502 { return _angularVelocity; }
504 bool getHaveLineSegment() const
505 { return _haveLineSegment; }
508 SGLineSegmentd _lineSegment;
509 SGVec3d _linearVelocity;
510 SGVec3d _angularVelocity;
512 bool _haveLineSegment;
519 FGGroundCache::get_cat(double t, const SGVec3d& pt,
520 SGVec3d end[2], SGVec3d vel[2])
522 double maxDistance = 1000;
524 // Get the wire in question
525 CatapultFinder catapultFinder(SGSphered(pt, maxDistance), t);
527 _localBvhTree->accept(catapultFinder);
529 if (!catapultFinder.getHaveLineSegment())
532 // prepare the returns
533 end[0] = catapultFinder.getLineSegment().getStart();
534 end[1] = catapultFinder.getLineSegment().getEnd();
536 // The linear velocity is the one at the start of the line segment ...
537 vel[0] = catapultFinder.getLinearVelocity();
538 // ... so the end point has the additional cross product.
539 vel[1] = catapultFinder.getLinearVelocity();
540 vel[1] += cross(catapultFinder.getAngularVelocity(),
541 catapultFinder.getLineSegment().getDirection());
543 // Return the distance to the cat
544 return sqrt(distSqr(catapultFinder.getLineSegment(), pt));
548 FGGroundCache::get_agl(double t, const SGVec3d& pt, SGVec3d& contact,
549 SGVec3d& normal, SGVec3d& linearVel, SGVec3d& angularVel,
550 simgear::BVHNode::Id& id, const SGMaterial*& material)
552 // Just set up a ground intersection query for the given point
553 SGLineSegmentd line(pt, pt + 10*reference_vehicle_radius*down);
554 simgear::BVHLineSegmentVisitor lineSegmentVisitor(line, t);
556 _localBvhTree->accept(lineSegmentVisitor);
558 if (!lineSegmentVisitor.empty()) {
559 // Have an intersection
560 contact = lineSegmentVisitor.getPoint();
561 normal = lineSegmentVisitor.getNormal();
562 if (0 < dot(normal, down))
564 linearVel = lineSegmentVisitor.getLinearVelocity();
565 angularVel = lineSegmentVisitor.getAngularVelocity();
566 material = lineSegmentVisitor.getMaterial();
567 id = lineSegmentVisitor.getId();
571 // Whenever we did not have a ground triangle for the requested point,
572 // take the ground level we found during the current cache build.
573 // This is as good as what we had before for agl.
574 SGGeod geodPt = SGGeod::fromCart(pt);
575 geodPt.setElevationM(_altitude);
576 contact = SGVec3d::fromGeod(geodPt);
578 linearVel = SGVec3d(0, 0, 0);
579 angularVel = SGVec3d(0, 0, 0);
580 material = _material;
589 FGGroundCache::get_nearest(double t, const SGVec3d& pt, double maxDist,
590 SGVec3d& contact, SGVec3d& linearVel,
591 SGVec3d& angularVel, simgear::BVHNode::Id& id,
592 const SGMaterial*& material)
597 // Just set up a ground intersection query for the given point
598 SGSphered sphere(pt, maxDist);
599 simgear::BVHNearestPointVisitor nearestPointVisitor(sphere, t);
600 _localBvhTree->accept(nearestPointVisitor);
602 if (nearestPointVisitor.empty())
605 // Have geometry in the range of maxDist
606 contact = nearestPointVisitor.getPoint();
607 linearVel = nearestPointVisitor.getLinearVelocity();
608 angularVel = nearestPointVisitor.getAngularVelocity();
609 material = nearestPointVisitor.getMaterial();
610 id = nearestPointVisitor.getId();
616 class FGGroundCache::WireIntersector : public BVHVisitor {
618 WireIntersector(const SGVec3d pt[4], const double& t) :
619 _linearVelocity(SGVec3d::zeros()),
620 _angularVelocity(SGVec3d::zeros()),
624 // Build the two triangles spanning the area where the hook has moved
625 // during the past step.
626 _triangles[0].set(pt[0], pt[1], pt[2]);
627 _triangles[1].set(pt[0], pt[2], pt[3]);
630 virtual void apply(BVHGroup& leaf)
632 if (!_intersects(leaf.getBoundingSphere()))
635 leaf.traverse(*this);
637 virtual void apply(BVHTransform& transform)
639 if (!_intersects(transform.getBoundingSphere()))
642 SGTriangled triangles[2] = { _triangles[0], _triangles[1] };
643 _triangles[0] = triangles[0].transform(transform.getToLocalTransform());
644 _triangles[1] = triangles[1].transform(transform.getToLocalTransform());
646 transform.traverse(*this);
649 _lineSegment = transform.lineSegmentToWorld(_lineSegment);
650 _linearVelocity = transform.vecToWorld(_linearVelocity);
651 _angularVelocity = transform.vecToWorld(_angularVelocity);
653 _triangles[0] = triangles[0];
654 _triangles[1] = triangles[1];
656 virtual void apply(BVHMotionTransform& transform)
658 if (!_intersects(transform.getBoundingSphere()))
661 SGMatrixd toLocal = transform.getToLocalTransform(_time);
663 SGTriangled triangles[2] = { _triangles[0], _triangles[1] };
664 _triangles[0] = triangles[0].transform(toLocal);
665 _triangles[1] = triangles[1].transform(toLocal);
667 transform.traverse(*this);
670 SGMatrixd toWorld = transform.getToWorldTransform(_time);
672 += transform.getLinearVelocityAt(_lineSegment.getStart());
673 _angularVelocity += transform.getAngularVelocity();
674 _linearVelocity = toWorld.xformVec(_linearVelocity);
675 _angularVelocity = toWorld.xformVec(_angularVelocity);
676 _lineSegment = _lineSegment.transform(toWorld);
678 _triangles[0] = triangles[0];
679 _triangles[1] = triangles[1];
681 virtual void apply(BVHLineGeometry& node)
683 if (node.getType() != BVHLineGeometry::CarrierWire)
685 SGLineSegmentd lineSegment(node.getLineSegment());
686 if (!_intersects(lineSegment))
689 _lineSegment = lineSegment;
690 _linearVelocity = SGVec3d::zeros();
691 _angularVelocity = SGVec3d::zeros();
694 virtual void apply(BVHStaticGeometry& node)
697 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
698 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
700 bool _intersects(const SGSphered& sphere) const
704 if (intersects(_triangles[0], sphere))
706 if (intersects(_triangles[1], sphere))
710 bool _intersects(const SGLineSegmentd& lineSegment) const
714 if (intersects(_triangles[0], lineSegment))
716 if (intersects(_triangles[1], lineSegment))
721 const SGLineSegmentd& getLineSegment() const
722 { return _lineSegment; }
723 const SGVec3d& getLinearVelocity() const
724 { return _linearVelocity; }
725 const SGVec3d& getAngularVelocity() const
726 { return _angularVelocity; }
728 const BVHLineGeometry* getWire() const
732 SGLineSegmentd _lineSegment;
733 SGVec3d _linearVelocity;
734 SGVec3d _angularVelocity;
735 const BVHLineGeometry* _wire;
737 SGTriangled _triangles[2];
741 bool FGGroundCache::caught_wire(double t, const SGVec3d pt[4])
743 // Get the wire in question
744 WireIntersector wireIntersector(pt, t);
746 _localBvhTree->accept(wireIntersector);
748 _wire = wireIntersector.getWire();
752 class FGGroundCache::WireFinder : public BVHVisitor {
754 WireFinder(const BVHLineGeometry* wire, const double& t) :
757 _lineSegment(SGVec3d::zeros(), SGVec3d::zeros()),
758 _linearVelocity(SGVec3d::zeros()),
759 _angularVelocity(SGVec3d::zeros()),
760 _haveLineSegment(false)
763 virtual void apply(BVHGroup& leaf)
765 if (_haveLineSegment)
767 leaf.traverse(*this);
769 virtual void apply(BVHTransform& transform)
771 if (_haveLineSegment)
774 transform.traverse(*this);
776 if (_haveLineSegment) {
777 _linearVelocity = transform.vecToWorld(_linearVelocity);
778 _angularVelocity = transform.vecToWorld(_angularVelocity);
779 _lineSegment = transform.lineSegmentToWorld(_lineSegment);
782 virtual void apply(BVHMotionTransform& transform)
784 if (_haveLineSegment)
787 transform.traverse(*this);
789 if (_haveLineSegment) {
790 SGMatrixd toWorld = transform.getToWorldTransform(_time);
792 += transform.getLinearVelocityAt(_lineSegment.getStart());
793 _angularVelocity += transform.getAngularVelocity();
794 _linearVelocity = toWorld.xformVec(_linearVelocity);
795 _angularVelocity = toWorld.xformVec(_angularVelocity);
796 _lineSegment = _lineSegment.transform(toWorld);
799 virtual void apply(BVHLineGeometry& node)
801 if (_haveLineSegment)
805 if (node.getType() != BVHLineGeometry::CarrierWire)
807 _lineSegment = SGLineSegmentd(node.getLineSegment());
808 _linearVelocity = SGVec3d::zeros();
809 _angularVelocity = SGVec3d::zeros();
810 _haveLineSegment = true;
812 virtual void apply(BVHStaticGeometry&) { }
814 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
815 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
817 const SGLineSegmentd& getLineSegment() const
818 { return _lineSegment; }
820 bool getHaveLineSegment() const
821 { return _haveLineSegment; }
823 const SGVec3d& getLinearVelocity() const
824 { return _linearVelocity; }
825 const SGVec3d& getAngularVelocity() const
826 { return _angularVelocity; }
829 const BVHLineGeometry* _wire;
832 SGLineSegmentd _lineSegment;
833 SGVec3d _linearVelocity;
834 SGVec3d _angularVelocity;
836 bool _haveLineSegment;
839 bool FGGroundCache::get_wire_ends(double t, SGVec3d end[2], SGVec3d vel[2])
841 // Fast return if we do not have an active wire.
845 // Get the wire in question
846 WireFinder wireFinder(_wire, t);
848 _localBvhTree->accept(wireFinder);
850 if (!wireFinder.getHaveLineSegment())
853 // prepare the returns
854 end[0] = wireFinder.getLineSegment().getStart();
855 end[1] = wireFinder.getLineSegment().getEnd();
857 // The linear velocity is the one at the start of the line segment ...
858 vel[0] = wireFinder.getLinearVelocity();
859 // ... so the end point has the additional cross product.
860 vel[1] = wireFinder.getLinearVelocity();
861 vel[1] += cross(wireFinder.getAngularVelocity(),
862 wireFinder.getLineSegment().getDirection());
867 void FGGroundCache::release_wire(void)