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 double radius = _radius;
138 _center = SGVec3d(inverseMatrix.preMult(_center.osg()));
140 SGVec3d staticCenter(_center);
142 double dtStart = velocity->referenceTime - _startTime;
143 SGVec3d startCenter = staticCenter + dtStart*velocity->linear;
144 SGVec3d angle = dtStart*velocity->angular;
145 startCenter = SGQuatd::fromAngleAxis(angle).transform(startCenter);
147 double dtEnd = velocity->referenceTime - _endTime;
148 SGVec3d endCenter = staticCenter + dtEnd*velocity->linear;
149 angle = dtEnd*velocity->angular;
150 endCenter = SGQuatd::fromAngleAxis(angle).transform(endCenter);
152 _center = 0.5*(startCenter + endCenter);
153 _radius += 0.5*dist(startCenter, endCenter);
156 simgear::BVHSubTreeCollector::NodeList parentNodeList;
157 mSubTreeCollector.pushNodeList(parentNodeList);
159 addBoundingVolume(transform);
162 if (mSubTreeCollector.haveChildren()) {
164 simgear::BVHMotionTransform* bvhTransform;
165 bvhTransform = new simgear::BVHMotionTransform;
166 bvhTransform->setToWorldTransform(SGMatrixd(matrix.ptr()));
167 bvhTransform->setLinearVelocity(velocity->linear);
168 bvhTransform->setAngularVelocity(velocity->angular);
169 bvhTransform->setReferenceTime(velocity->referenceTime);
170 bvhTransform->setStartTime(_startTime);
171 bvhTransform->setEndTime(_endTime);
172 bvhTransform->setId(velocity->id);
174 mSubTreeCollector.popNodeList(parentNodeList, bvhTransform);
176 simgear::BVHTransform* bvhTransform;
177 bvhTransform = new simgear::BVHTransform;
178 bvhTransform->setToWorldTransform(SGMatrixd(matrix.ptr()));
180 mSubTreeCollector.popNodeList(parentNodeList, bvhTransform);
183 mSubTreeCollector.popNodeList(parentNodeList);
189 const SGSceneUserData::Velocity* getVelocity(osg::Node& node)
191 SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
194 return userData->getVelocity();
196 simgear::BVHNode* getNodeBoundingVolume(osg::Node& node)
198 SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
201 return userData->getBVHNode();
203 void addBoundingVolume(osg::Node& node)
205 simgear::BVHNode* bvNode = getNodeBoundingVolume(node);
209 // Get that part of the local bv tree that intersects our sphere
211 mSubTreeCollector.setSphere(SGSphered(_center, _radius));
212 bvNode->accept(mSubTreeCollector);
215 bool testBoundingSphere(const osg::BoundingSphere& bound) const
220 double maxDist = bound._radius + _radius;
221 return distSqr(SGVec3d(bound._center), _center) <= maxDist*maxDist;
224 SGSharedPtr<simgear::BVHNode> getBVHNode() const
225 { return mSubTreeCollector.getNode(); }
234 simgear::BVHSubTreeCollector mSubTreeCollector;
237 FGGroundCache::FGGroundCache() :
241 cache_time_offset(0),
243 reference_wgs84_point(SGVec3d(0, 0, 0)),
244 reference_vehicle_radius(0),
250 FGGroundCache::~FGGroundCache()
255 FGGroundCache::prepare_ground_cache(double startSimTime, double endSimTime,
256 const SGVec3d& pt, double rad)
259 found_ground = false;
261 SGGeod geodPt = SGGeod::fromCart(pt);
262 // Don't blow away the cache ground_radius and stuff if there's no
264 if (!globals->get_tile_mgr()->scenery_available(geodPt, rad)) {
265 SG_LOG(SG_FLIGHT, SG_WARN, "prepare_ground_cache(): scenery_available "
266 "returns false at " << geodPt << " " << pt << " " << rad);
271 // If we have an active wire, get some more area into the groundcache
273 rad = SGMiscd::max(200, rad);
275 // Store the parameters we used to build up that cache.
276 reference_wgs84_point = pt;
277 reference_vehicle_radius = rad;
278 // Store the time reference used to compute movements of moving triangles.
279 cache_ref_time = startSimTime;
281 // Get a normalized down vector valid for the whole cache
282 SGQuatd hlToEc = SGQuatd::fromLonLat(geodPt);
283 down = hlToEc.rotate(SGVec3d(0, 0, 1));
285 // Get the ground cache, that is a local collision tree of the environment
286 startSimTime += cache_time_offset;
287 endSimTime += cache_time_offset;
288 CacheFill subtreeCollector(pt, rad, startSimTime, endSimTime);
289 globals->get_scenery()->get_scene_graph()->accept(subtreeCollector);
290 _localBvhTree = subtreeCollector.getBVHNode();
292 // Try to get a croase altitude value for the ground cache
293 SGLineSegmentd line(pt, pt + 2*reference_vehicle_radius*down);
294 simgear::BVHLineSegmentVisitor lineSegmentVisitor(line, startSimTime);
296 _localBvhTree->accept(lineSegmentVisitor);
298 // If this is successful, store this altitude for croase altitude values
299 if (!lineSegmentVisitor.empty()) {
300 SGGeod geodPt = SGGeod::fromCart(lineSegmentVisitor.getPoint());
301 _altitude = geodPt.getElevationM();
302 _material = lineSegmentVisitor.getMaterial();
305 // Else do a crude scene query for the current point
307 found_ground = globals->get_scenery()->
308 get_cart_elevation_m(pt, rad, alt, &_material);
313 // Still not sucessful??
315 SG_LOG(SG_FLIGHT, SG_WARN, "prepare_ground_cache(): trying to build "
316 "cache without any scenery below the aircraft");
322 FGGroundCache::is_valid(double& ref_time, SGVec3d& pt, double& rad)
324 pt = reference_wgs84_point;
325 rad = reference_vehicle_radius;
326 ref_time = cache_ref_time;
330 class FGGroundCache::BodyFinder : public BVHVisitor {
332 BodyFinder(BVHNode::Id id, const double& t) :
334 _bodyToWorld(SGMatrixd::unit()),
335 _linearVelocity(0, 0, 0),
336 _angularVelocity(0, 0, 0),
340 virtual void apply(BVHGroup& leaf)
344 leaf.traverse(*this);
346 virtual void apply(BVHTransform& transform)
351 transform.traverse(*this);
354 _linearVelocity = transform.vecToWorld(_linearVelocity);
355 _angularVelocity = transform.vecToWorld(_angularVelocity);
356 _bodyToWorld = transform.getToWorldTransform()*_bodyToWorld;
359 virtual void apply(BVHMotionTransform& transform)
364 if (_id == transform.getId()) {
369 transform.traverse(*this);
372 SGMatrixd toWorld = transform.getToWorldTransform(_time);
373 SGVec3d referencePoint = _bodyToWorld.xformPt(SGVec3d::zeros());
374 _linearVelocity += transform.getLinearVelocityAt(referencePoint);
375 _angularVelocity += transform.getAngularVelocity();
376 _linearVelocity = toWorld.xformVec(_linearVelocity);
377 _angularVelocity = toWorld.xformVec(_angularVelocity);
378 _bodyToWorld = toWorld*_bodyToWorld;
381 virtual void apply(BVHLineGeometry& node) { }
382 virtual void apply(BVHStaticGeometry& node) { }
384 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
385 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
387 const SGMatrixd& getBodyToWorld() const
388 { return _bodyToWorld; }
389 const SGVec3d& getLinearVelocity() const
390 { return _linearVelocity; }
391 const SGVec3d& getAngularVelocity() const
392 { return _angularVelocity; }
395 { return !_foundId; }
398 simgear::BVHNode::Id _id;
400 SGMatrixd _bodyToWorld;
402 SGVec3d _linearVelocity;
403 SGVec3d _angularVelocity;
411 FGGroundCache::get_body(double t, SGMatrixd& bodyToWorld, SGVec3d& linearVel,
412 SGVec3d& angularVel, simgear::BVHNode::Id id)
414 // Get the transform matrix and velocities of a moving body with id at t.
417 t += cache_time_offset;
418 BodyFinder bodyFinder(id, t);
419 _localBvhTree->accept(bodyFinder);
420 if (bodyFinder.empty())
423 bodyToWorld = bodyFinder.getBodyToWorld();
424 linearVel = bodyFinder.getLinearVelocity();
425 angularVel = bodyFinder.getAngularVelocity();
430 class FGGroundCache::CatapultFinder : public BVHVisitor {
432 CatapultFinder(const SGSphered& sphere, const double& t) :
435 _haveLineSegment(false)
438 virtual void apply(BVHGroup& leaf)
440 if (!intersects(_sphere, leaf.getBoundingSphere()))
442 leaf.traverse(*this);
444 virtual void apply(BVHTransform& transform)
446 if (!intersects(_sphere, transform.getBoundingSphere()))
449 SGSphered sphere = _sphere;
450 _sphere = transform.sphereToLocal(sphere);
451 bool haveLineSegment = _haveLineSegment;
452 _haveLineSegment = false;
454 transform.traverse(*this);
456 if (_haveLineSegment) {
457 _lineSegment = transform.lineSegmentToWorld(_lineSegment);
458 _linearVelocity = transform.vecToWorld(_linearVelocity);
459 _angularVelocity = transform.vecToWorld(_angularVelocity);
461 _haveLineSegment |= haveLineSegment;
462 _sphere.setCenter(sphere.getCenter());
464 virtual void apply(BVHMotionTransform& transform)
466 if (!intersects(_sphere, transform.getBoundingSphere()))
469 SGSphered sphere = _sphere;
470 _sphere = transform.sphereToLocal(sphere, _time);
471 bool haveLineSegment = _haveLineSegment;
472 _haveLineSegment = false;
474 transform.traverse(*this);
476 if (_haveLineSegment) {
477 SGMatrixd toWorld = transform.getToWorldTransform(_time);
479 += transform.getLinearVelocityAt(_lineSegment.getStart());
480 _angularVelocity += transform.getAngularVelocity();
481 _linearVelocity = toWorld.xformVec(_linearVelocity);
482 _angularVelocity = toWorld.xformVec(_angularVelocity);
483 _lineSegment = _lineSegment.transform(toWorld);
485 _haveLineSegment |= haveLineSegment;
486 _sphere.setCenter(sphere.getCenter());
488 virtual void apply(BVHLineGeometry& node)
490 if (node.getType() != BVHLineGeometry::CarrierCatapult)
493 SGLineSegmentd lineSegment(node.getLineSegment());
494 if (!intersects(_sphere, lineSegment))
497 _lineSegment = lineSegment;
498 double dist = distSqr(lineSegment, getSphere().getCenter());
499 _sphere.setRadius(sqrt(dist));
500 _linearVelocity = SGVec3d::zeros();
501 _angularVelocity = SGVec3d::zeros();
502 _haveLineSegment = true;
504 virtual void apply(BVHStaticGeometry& node)
507 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
508 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
510 void setSphere(const SGSphered& sphere)
511 { _sphere = sphere; }
512 const SGSphered& getSphere() const
515 const SGLineSegmentd& getLineSegment() const
516 { return _lineSegment; }
517 const SGVec3d& getLinearVelocity() const
518 { return _linearVelocity; }
519 const SGVec3d& getAngularVelocity() const
520 { return _angularVelocity; }
522 bool getHaveLineSegment() const
523 { return _haveLineSegment; }
526 SGLineSegmentd _lineSegment;
527 SGVec3d _linearVelocity;
528 SGVec3d _angularVelocity;
530 bool _haveLineSegment;
537 FGGroundCache::get_cat(double t, const SGVec3d& pt,
538 SGVec3d end[2], SGVec3d vel[2])
540 double maxDistance = 1000;
542 // Get the wire in question
543 t += cache_time_offset;
544 CatapultFinder catapultFinder(SGSphered(pt, maxDistance), t);
546 _localBvhTree->accept(catapultFinder);
548 if (!catapultFinder.getHaveLineSegment())
551 // prepare the returns
552 end[0] = catapultFinder.getLineSegment().getStart();
553 end[1] = catapultFinder.getLineSegment().getEnd();
555 // The linear velocity is the one at the start of the line segment ...
556 vel[0] = catapultFinder.getLinearVelocity();
557 // ... so the end point has the additional cross product.
558 vel[1] = catapultFinder.getLinearVelocity();
559 vel[1] += cross(catapultFinder.getAngularVelocity(),
560 catapultFinder.getLineSegment().getDirection());
562 // Return the distance to the cat
563 return sqrt(distSqr(catapultFinder.getLineSegment(), pt));
567 FGGroundCache::get_agl(double t, const SGVec3d& pt, SGVec3d& contact,
568 SGVec3d& normal, SGVec3d& linearVel, SGVec3d& angularVel,
569 simgear::BVHNode::Id& id, const SGMaterial*& material)
571 // Just set up a ground intersection query for the given point
572 SGLineSegmentd line(pt, pt + 10*reference_vehicle_radius*down);
573 t += cache_time_offset;
574 simgear::BVHLineSegmentVisitor lineSegmentVisitor(line, t);
576 _localBvhTree->accept(lineSegmentVisitor);
578 if (!lineSegmentVisitor.empty()) {
579 // Have an intersection
580 contact = lineSegmentVisitor.getPoint();
581 normal = lineSegmentVisitor.getNormal();
582 if (0 < dot(normal, down))
584 linearVel = lineSegmentVisitor.getLinearVelocity();
585 angularVel = lineSegmentVisitor.getAngularVelocity();
586 material = lineSegmentVisitor.getMaterial();
587 id = lineSegmentVisitor.getId();
591 // Whenever we did not have a ground triangle for the requested point,
592 // take the ground level we found during the current cache build.
593 // This is as good as what we had before for agl.
594 SGGeod geodPt = SGGeod::fromCart(pt);
595 geodPt.setElevationM(_altitude);
596 contact = SGVec3d::fromGeod(geodPt);
598 linearVel = SGVec3d(0, 0, 0);
599 angularVel = SGVec3d(0, 0, 0);
600 material = _material;
609 FGGroundCache::get_nearest(double t, const SGVec3d& pt, double maxDist,
610 SGVec3d& contact, SGVec3d& linearVel,
611 SGVec3d& angularVel, simgear::BVHNode::Id& id,
612 const SGMaterial*& material)
617 // Just set up a ground intersection query for the given point
618 SGSphered sphere(pt, maxDist);
619 t += cache_time_offset;
620 simgear::BVHNearestPointVisitor nearestPointVisitor(sphere, t);
621 _localBvhTree->accept(nearestPointVisitor);
623 if (nearestPointVisitor.empty())
626 // Have geometry in the range of maxDist
627 contact = nearestPointVisitor.getPoint();
628 linearVel = nearestPointVisitor.getLinearVelocity();
629 angularVel = nearestPointVisitor.getAngularVelocity();
630 material = nearestPointVisitor.getMaterial();
631 id = nearestPointVisitor.getId();
637 class FGGroundCache::WireIntersector : public BVHVisitor {
639 WireIntersector(const SGVec3d pt[4], const double& t) :
640 _linearVelocity(SGVec3d::zeros()),
641 _angularVelocity(SGVec3d::zeros()),
645 // Build the two triangles spanning the area where the hook has moved
646 // during the past step.
647 _triangles[0].set(pt[0], pt[1], pt[2]);
648 _triangles[1].set(pt[0], pt[2], pt[3]);
651 virtual void apply(BVHGroup& leaf)
653 if (!_intersects(leaf.getBoundingSphere()))
656 leaf.traverse(*this);
658 virtual void apply(BVHTransform& transform)
660 if (!_intersects(transform.getBoundingSphere()))
663 SGTriangled triangles[2] = { _triangles[0], _triangles[1] };
664 _triangles[0] = triangles[0].transform(transform.getToLocalTransform());
665 _triangles[1] = triangles[1].transform(transform.getToLocalTransform());
667 transform.traverse(*this);
670 _lineSegment = transform.lineSegmentToWorld(_lineSegment);
671 _linearVelocity = transform.vecToWorld(_linearVelocity);
672 _angularVelocity = transform.vecToWorld(_angularVelocity);
674 _triangles[0] = triangles[0];
675 _triangles[1] = triangles[1];
677 virtual void apply(BVHMotionTransform& transform)
679 if (!_intersects(transform.getBoundingSphere()))
682 SGMatrixd toLocal = transform.getToLocalTransform(_time);
684 SGTriangled triangles[2] = { _triangles[0], _triangles[1] };
685 _triangles[0] = triangles[0].transform(toLocal);
686 _triangles[1] = triangles[1].transform(toLocal);
688 transform.traverse(*this);
691 SGMatrixd toWorld = transform.getToWorldTransform(_time);
693 += transform.getLinearVelocityAt(_lineSegment.getStart());
694 _angularVelocity += transform.getAngularVelocity();
695 _linearVelocity = toWorld.xformVec(_linearVelocity);
696 _angularVelocity = toWorld.xformVec(_angularVelocity);
697 _lineSegment = _lineSegment.transform(toWorld);
699 _triangles[0] = triangles[0];
700 _triangles[1] = triangles[1];
702 virtual void apply(BVHLineGeometry& node)
704 if (node.getType() != BVHLineGeometry::CarrierWire)
706 SGLineSegmentd lineSegment(node.getLineSegment());
707 if (!_intersects(lineSegment))
710 _lineSegment = lineSegment;
711 _linearVelocity = SGVec3d::zeros();
712 _angularVelocity = SGVec3d::zeros();
715 virtual void apply(BVHStaticGeometry& node)
718 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
719 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
721 bool _intersects(const SGSphered& sphere) const
725 if (intersects(_triangles[0], sphere))
727 if (intersects(_triangles[1], sphere))
731 bool _intersects(const SGLineSegmentd& lineSegment) const
735 if (intersects(_triangles[0], lineSegment))
737 if (intersects(_triangles[1], lineSegment))
742 const SGLineSegmentd& getLineSegment() const
743 { return _lineSegment; }
744 const SGVec3d& getLinearVelocity() const
745 { return _linearVelocity; }
746 const SGVec3d& getAngularVelocity() const
747 { return _angularVelocity; }
749 const BVHLineGeometry* getWire() const
753 SGLineSegmentd _lineSegment;
754 SGVec3d _linearVelocity;
755 SGVec3d _angularVelocity;
756 const BVHLineGeometry* _wire;
758 SGTriangled _triangles[2];
762 bool FGGroundCache::caught_wire(double t, const SGVec3d pt[4])
764 // Get the wire in question
765 t += cache_time_offset;
766 WireIntersector wireIntersector(pt, t);
768 _localBvhTree->accept(wireIntersector);
770 _wire = wireIntersector.getWire();
774 class FGGroundCache::WireFinder : public BVHVisitor {
776 WireFinder(const BVHLineGeometry* wire, const double& t) :
779 _lineSegment(SGVec3d::zeros(), SGVec3d::zeros()),
780 _linearVelocity(SGVec3d::zeros()),
781 _angularVelocity(SGVec3d::zeros()),
782 _haveLineSegment(false)
785 virtual void apply(BVHGroup& leaf)
787 if (_haveLineSegment)
789 leaf.traverse(*this);
791 virtual void apply(BVHTransform& transform)
793 if (_haveLineSegment)
796 transform.traverse(*this);
798 if (_haveLineSegment) {
799 _linearVelocity = transform.vecToWorld(_linearVelocity);
800 _angularVelocity = transform.vecToWorld(_angularVelocity);
801 _lineSegment = transform.lineSegmentToWorld(_lineSegment);
804 virtual void apply(BVHMotionTransform& transform)
806 if (_haveLineSegment)
809 transform.traverse(*this);
811 if (_haveLineSegment) {
812 SGMatrixd toWorld = transform.getToWorldTransform(_time);
814 += transform.getLinearVelocityAt(_lineSegment.getStart());
815 _angularVelocity += transform.getAngularVelocity();
816 _linearVelocity = toWorld.xformVec(_linearVelocity);
817 _angularVelocity = toWorld.xformVec(_angularVelocity);
818 _lineSegment = _lineSegment.transform(toWorld);
821 virtual void apply(BVHLineGeometry& node)
823 if (_haveLineSegment)
827 if (node.getType() != BVHLineGeometry::CarrierWire)
829 _lineSegment = SGLineSegmentd(node.getLineSegment());
830 _linearVelocity = SGVec3d::zeros();
831 _angularVelocity = SGVec3d::zeros();
832 _haveLineSegment = true;
834 virtual void apply(BVHStaticGeometry&) { }
836 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
837 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
839 const SGLineSegmentd& getLineSegment() const
840 { return _lineSegment; }
842 bool getHaveLineSegment() const
843 { return _haveLineSegment; }
845 const SGVec3d& getLinearVelocity() const
846 { return _linearVelocity; }
847 const SGVec3d& getAngularVelocity() const
848 { return _angularVelocity; }
851 const BVHLineGeometry* _wire;
854 SGLineSegmentd _lineSegment;
855 SGVec3d _linearVelocity;
856 SGVec3d _angularVelocity;
858 bool _haveLineSegment;
861 bool FGGroundCache::get_wire_ends(double t, SGVec3d end[2], SGVec3d vel[2])
863 // Fast return if we do not have an active wire.
867 // Get the wire in question
868 t += cache_time_offset;
869 WireFinder wireFinder(_wire, t);
871 _localBvhTree->accept(wireFinder);
873 if (!wireFinder.getHaveLineSegment())
876 // prepare the returns
877 end[0] = wireFinder.getLineSegment().getStart();
878 end[1] = wireFinder.getLineSegment().getEnd();
880 // The linear velocity is the one at the start of the line segment ...
881 vel[0] = wireFinder.getLinearVelocity();
882 // ... so the end point has the additional cross product.
883 vel[1] = wireFinder.getLinearVelocity();
884 vel[1] += cross(wireFinder.getAngularVelocity(),
885 wireFinder.getLineSegment().getDirection());
890 void FGGroundCache::release_wire(void)