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.
27 #include "groundcache.hxx"
31 #include <osg/Drawable>
33 #include <osg/Geometry>
35 #include <osg/Transform>
36 #include <osg/MatrixTransform>
37 #include <osg/PositionAttitudeTransform>
38 #include <osg/CameraView>
40 #include <simgear/sg_inlines.h>
41 #include <simgear/constants.h>
42 #include <simgear/debug/logstream.hxx>
43 #include <simgear/math/SGMisc.hxx>
44 #include <simgear/scene/material/mat.hxx>
45 #include <simgear/scene/util/SGNodeMasks.hxx>
46 #include <simgear/scene/util/SGSceneUserData.hxx>
47 #include <simgear/scene/util/OsgMath.hxx>
49 #include <simgear/bvh/BVHNode.hxx>
50 #include <simgear/bvh/BVHGroup.hxx>
51 #include <simgear/bvh/BVHTransform.hxx>
52 #include <simgear/bvh/BVHMotionTransform.hxx>
53 #include <simgear/bvh/BVHLineGeometry.hxx>
54 #include <simgear/bvh/BVHStaticGeometry.hxx>
55 #include <simgear/bvh/BVHStaticData.hxx>
56 #include <simgear/bvh/BVHStaticNode.hxx>
57 #include <simgear/bvh/BVHStaticTriangle.hxx>
58 #include <simgear/bvh/BVHStaticBinary.hxx>
59 #include <simgear/bvh/BVHSubTreeCollector.hxx>
60 #include <simgear/bvh/BVHLineSegmentVisitor.hxx>
61 #include <simgear/bvh/BVHNearestPointVisitor.hxx>
63 #ifdef GROUNDCACHE_DEBUG
64 #include <simgear/scene/model/BVHDebugCollectVisitor.hxx>
65 #include <Main/fg_props.hxx>
68 #include <Main/globals.hxx>
69 #include <Scenery/scenery.hxx>
70 #include <Scenery/tilemgr.hxx>
74 using namespace simgear;
76 class FGGroundCache::CacheFill : public osg::NodeVisitor {
78 CacheFill(const SGVec3d& center, const SGVec3d& down, const double& radius,
79 const double& startTime, const double& endTime) :
80 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
84 _startTime(startTime),
87 _maxDown(SGGeod::fromCart(center).getElevationM() + 9999),
91 setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
93 virtual void apply(osg::Node& node)
95 if (!testBoundingSphere(node.getBound()))
98 addBoundingVolume(node);
101 virtual void apply(osg::Group& group)
103 if (!testBoundingSphere(group.getBound()))
106 simgear::BVHSubTreeCollector::NodeList parentNodeList;
107 mSubTreeCollector.pushNodeList(parentNodeList);
110 addBoundingVolume(group);
112 mSubTreeCollector.popNodeList(parentNodeList);
115 virtual void apply(osg::Transform& transform)
116 { handleTransform(transform); }
117 virtual void apply(osg::Camera& camera)
119 if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
121 handleTransform(camera);
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); }
130 void handleTransform(osg::Transform& transform)
132 // Hmm, may be this needs to be refined somehow ...
133 if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
136 if (!testBoundingSphere(transform.getBound()))
139 osg::Matrix inverseMatrix;
140 if (!transform.computeWorldToLocalMatrix(inverseMatrix, this))
143 if (!transform.computeLocalToWorldMatrix(matrix, this))
146 // Look for a velocity note
147 const SGSceneUserData::Velocity* velocity = getVelocity(transform);
149 SGVec3d center = _center;
150 SGVec3d down = _down;
151 double radius = _radius;
152 bool haveHit = _haveHit;
153 const simgear::BVHMaterial* material = _material;
156 _center = toSG(inverseMatrix.preMult(toOsg(_center)));
157 _down = toSG(osg::Matrix::transform3x3(toOsg(_down), inverseMatrix));
159 SGVec3d staticCenter(_center);
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);
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);
171 _center = 0.5*(startCenter + endCenter);
172 _down = startOr.transform(_down);
173 _radius += 0.5*dist(startCenter, endCenter);
176 simgear::BVHSubTreeCollector::NodeList parentNodeList;
177 mSubTreeCollector.pushNodeList(parentNodeList);
179 addBoundingVolume(transform);
182 if (mSubTreeCollector.haveChildren()) {
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);
194 mSubTreeCollector.popNodeList(parentNodeList, bvhTransform);
196 simgear::BVHTransform* bvhTransform;
197 bvhTransform = new simgear::BVHTransform;
198 bvhTransform->setToWorldTransform(SGMatrixd(matrix.ptr()));
200 mSubTreeCollector.popNodeList(parentNodeList, bvhTransform);
203 mSubTreeCollector.popNodeList(parentNodeList);
208 double dt = _startTime - velocity->referenceTime;
209 SGQuatd ori(SGQuatd::fromAngleAxis(dt*velocity->angular));
210 _sceneryHit = ori.transform(_sceneryHit);
211 _sceneryHit += dt*velocity->linear;
213 _sceneryHit = toSG(matrix.preMult(toOsg(_sceneryHit)));
215 _material = material;
224 const SGSceneUserData::Velocity* getVelocity(osg::Node& node)
226 SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
229 return userData->getVelocity();
231 simgear::BVHNode* getNodeBoundingVolume(osg::Node& node)
233 SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
236 return userData->getBVHNode();
238 void addBoundingVolume(osg::Node& node)
240 simgear::BVHNode* bvNode = getNodeBoundingVolume(node);
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));
255 // Get that part of the local bv tree that intersects our sphere
257 mSubTreeCollector.setSphere(SGSphered(_center, _radius));
258 bvNode->accept(mSubTreeCollector);
261 bool testBoundingSphere(const osg::BoundingSphere& bound) const
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;
272 SGSharedPtr<simgear::BVHNode> getBVHNode() const
273 { return mSubTreeCollector.getNode(); }
275 bool getHaveElevationBelowCache() const
277 double getElevationBelowCache() const
278 { return SGGeod::fromCart(_sceneryHit).getElevationM(); }
279 const simgear::BVHMaterial* getMaterialBelowCache() const
280 { return _material; }
289 simgear::BVHSubTreeCollector mSubTreeCollector;
292 const simgear::BVHMaterial* _material;
296 FGGroundCache::FGGroundCache() :
300 cache_time_offset(0),
302 reference_wgs84_point(SGVec3d(0, 0, 0)),
303 reference_vehicle_radius(0),
307 #ifdef GROUNDCACHE_DEBUG
308 _lookupTime = SGTimeStamp::fromSec(0.0);
310 _buildTime = SGTimeStamp::fromSec(0.0);
315 FGGroundCache::~FGGroundCache()
320 FGGroundCache::prepare_ground_cache(double startSimTime, double endSimTime,
321 const SGVec3d& pt, double rad)
323 #ifdef GROUNDCACHE_DEBUG
324 SGTimeStamp t0 = SGTimeStamp::now();
328 found_ground = false;
330 SGGeod geodPt = SGGeod::fromCart(pt);
331 // Don't blow away the cache ground_radius and stuff if there's no
333 if (!globals->get_tile_mgr()->schedule_scenery(geodPt, rad, 1.0)) {
334 SG_LOG(SG_FLIGHT, SG_WARN, "prepare_ground_cache(): scenery_available "
335 "returns false at " << geodPt << " " << pt << " " << rad);
340 // If we have an active wire, get some more area into the groundcache
342 rad = SGMiscd::max(200, rad);
344 // Store the parameters we used to build up that cache.
345 reference_wgs84_point = pt;
346 reference_vehicle_radius = rad;
347 // Store the time reference used to compute movements of moving triangles.
348 cache_ref_time = startSimTime;
350 // Get a normalized down vector valid for the whole cache
351 SGQuatd hlToEc = SGQuatd::fromLonLat(geodPt);
352 down = hlToEc.rotate(SGVec3d(0, 0, 1));
354 // Get the ground cache, that is a local collision tree of the environment
355 startSimTime += cache_time_offset;
356 endSimTime += cache_time_offset;
357 CacheFill subtreeCollector(pt, down, rad, startSimTime, endSimTime);
358 globals->get_scenery()->get_scene_graph()->accept(subtreeCollector);
359 _localBvhTree = subtreeCollector.getBVHNode();
361 if (subtreeCollector.getHaveElevationBelowCache()) {
362 // Use the altitude value below the cache that we gathered during
364 _altitude = subtreeCollector.getElevationBelowCache();
365 _material = subtreeCollector.getMaterialBelowCache();
367 } else if (_localBvhTree) {
368 // We have nothing below us, so try starting with the lowest point
369 // upwards for a croase altitude value
370 SGLineSegmentd line(pt + reference_vehicle_radius*down, pt - 1e3*down);
371 simgear::BVHLineSegmentVisitor lineSegmentVisitor(line, startSimTime);
372 _localBvhTree->accept(lineSegmentVisitor);
374 if (!lineSegmentVisitor.empty()) {
375 SGGeod geodPt = SGGeod::fromCart(lineSegmentVisitor.getPoint());
376 _altitude = geodPt.getElevationM();
377 _material = lineSegmentVisitor.getMaterial();
383 // Ok, still nothing here?? Last resort ...
385 const SGMaterial* m = NULL;
386 found_ground = globals->get_scenery()->
387 get_elevation_m(SGGeod::fromGeodM(geodPt, 10000), alt, &m);
393 // Still not sucessful??
395 SG_LOG(SG_FLIGHT, SG_WARN, "prepare_ground_cache(): trying to build "
396 "cache without any scenery below the aircraft");
398 #ifdef GROUNDCACHE_DEBUG
399 t0 = SGTimeStamp::now() - t0;
403 if (_buildCount > 60) {
404 double buildTime = 0;
406 buildTime = _buildTime.toSecs()/_buildCount;
407 double lookupTime = 0;
409 lookupTime = _lookupTime.toSecs()/_lookupCount;
410 _buildTime = SGTimeStamp::fromSec(0.0);
412 _lookupTime = SGTimeStamp::fromSec(0.0);
414 SG_LOG(SG_FLIGHT, SG_ALERT, "build time = " << buildTime
415 << ", lookup Time = " << lookupTime);
418 if (!_group.valid()) {
419 _group = new osg::Group;
420 globals->get_scenery()->get_scene_graph()->addChild(_group);
421 fgSetInt("/fdm/groundcache-debug-level", -3);
423 _group->removeChildren(0, _group->getNumChildren());
425 int level = fgGetInt("/fdm/groundcache-debug-level");
427 simgear::BVHDebugCollectVisitor debug(endSimTime, level);
428 _localBvhTree->accept(debug);
429 _group->addChild(debug.getNode());
438 FGGroundCache::is_valid(double& ref_time, SGVec3d& pt, double& rad)
440 pt = reference_wgs84_point;
441 rad = reference_vehicle_radius;
442 ref_time = cache_ref_time;
446 class FGGroundCache::BodyFinder : public BVHVisitor {
448 BodyFinder(BVHNode::Id id, const double& t) :
450 _bodyToWorld(SGMatrixd::unit()),
451 _linearVelocity(0, 0, 0),
452 _angularVelocity(0, 0, 0),
456 virtual void apply(BVHGroup& leaf)
460 leaf.traverse(*this);
462 virtual void apply(BVHPageNode& leaf)
466 leaf.traverse(*this);
468 virtual void apply(BVHTransform& transform)
473 transform.traverse(*this);
476 _linearVelocity = transform.vecToWorld(_linearVelocity);
477 _angularVelocity = transform.vecToWorld(_angularVelocity);
478 _bodyToWorld = transform.getToWorldTransform()*_bodyToWorld;
481 virtual void apply(BVHMotionTransform& transform)
486 if (_id == transform.getId()) {
489 transform.traverse(*this);
493 SGMatrixd toWorld = transform.getToWorldTransform(_time);
494 SGVec3d referencePoint = _bodyToWorld.xformPt(SGVec3d::zeros());
495 _linearVelocity += transform.getLinearVelocityAt(referencePoint);
496 _angularVelocity += transform.getAngularVelocity();
497 _linearVelocity = toWorld.xformVec(_linearVelocity);
498 _angularVelocity = toWorld.xformVec(_angularVelocity);
499 _bodyToWorld = toWorld*_bodyToWorld;
502 virtual void apply(BVHLineGeometry& node) { }
503 virtual void apply(BVHStaticGeometry& node) { }
505 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
506 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
508 const SGMatrixd& getBodyToWorld() const
509 { return _bodyToWorld; }
510 const SGVec3d& getLinearVelocity() const
511 { return _linearVelocity; }
512 const SGVec3d& getAngularVelocity() const
513 { return _angularVelocity; }
516 { return !_foundId; }
519 simgear::BVHNode::Id _id;
521 SGMatrixd _bodyToWorld;
523 SGVec3d _linearVelocity;
524 SGVec3d _angularVelocity;
532 FGGroundCache::get_body(double t, SGMatrixd& bodyToWorld, SGVec3d& linearVel,
533 SGVec3d& angularVel, simgear::BVHNode::Id id)
535 // Get the transform matrix and velocities of a moving body with id at t.
538 t += cache_time_offset;
539 BodyFinder bodyFinder(id, t);
540 _localBvhTree->accept(bodyFinder);
541 if (bodyFinder.empty())
544 bodyToWorld = bodyFinder.getBodyToWorld();
545 linearVel = bodyFinder.getLinearVelocity();
546 angularVel = bodyFinder.getAngularVelocity();
551 class FGGroundCache::CatapultFinder : public BVHVisitor {
553 CatapultFinder(const SGSphered& sphere, const double& t) :
554 _haveLineSegment(false),
559 virtual void apply(BVHGroup& leaf)
561 if (!intersects(_sphere, leaf.getBoundingSphere()))
563 leaf.traverse(*this);
565 virtual void apply(BVHPageNode& leaf)
567 if (!intersects(_sphere, leaf.getBoundingSphere()))
569 leaf.traverse(*this);
571 virtual void apply(BVHTransform& transform)
573 if (!intersects(_sphere, transform.getBoundingSphere()))
576 SGSphered sphere = _sphere;
577 _sphere = transform.sphereToLocal(sphere);
578 bool haveLineSegment = _haveLineSegment;
579 _haveLineSegment = false;
581 transform.traverse(*this);
583 if (_haveLineSegment) {
584 _lineSegment = transform.lineSegmentToWorld(_lineSegment);
585 _linearVelocity = transform.vecToWorld(_linearVelocity);
586 _angularVelocity = transform.vecToWorld(_angularVelocity);
588 _haveLineSegment |= haveLineSegment;
589 _sphere.setCenter(sphere.getCenter());
591 virtual void apply(BVHMotionTransform& transform)
593 if (!intersects(_sphere, transform.getBoundingSphere()))
596 SGSphered sphere = _sphere;
597 _sphere = transform.sphereToLocal(sphere, _time);
598 bool haveLineSegment = _haveLineSegment;
599 _haveLineSegment = false;
601 transform.traverse(*this);
603 if (_haveLineSegment) {
604 SGMatrixd toWorld = transform.getToWorldTransform(_time);
606 += transform.getLinearVelocityAt(_lineSegment.getStart());
607 _angularVelocity += transform.getAngularVelocity();
608 _linearVelocity = toWorld.xformVec(_linearVelocity);
609 _angularVelocity = toWorld.xformVec(_angularVelocity);
610 _lineSegment = _lineSegment.transform(toWorld);
612 _haveLineSegment |= haveLineSegment;
613 _sphere.setCenter(sphere.getCenter());
615 virtual void apply(BVHLineGeometry& node)
617 if (node.getType() != BVHLineGeometry::CarrierCatapult)
620 SGLineSegmentd lineSegment(node.getLineSegment());
621 if (!intersects(_sphere, lineSegment))
624 _lineSegment = lineSegment;
625 double dist = distSqr(lineSegment, getSphere().getCenter());
626 _sphere.setRadius(sqrt(dist));
627 _linearVelocity = SGVec3d::zeros();
628 _angularVelocity = SGVec3d::zeros();
629 _haveLineSegment = true;
631 virtual void apply(BVHStaticGeometry& node)
634 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
635 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
637 void setSphere(const SGSphered& sphere)
638 { _sphere = sphere; }
639 const SGSphered& getSphere() const
642 const SGLineSegmentd& getLineSegment() const
643 { return _lineSegment; }
644 const SGVec3d& getLinearVelocity() const
645 { return _linearVelocity; }
646 const SGVec3d& getAngularVelocity() const
647 { return _angularVelocity; }
649 bool getHaveLineSegment() const
650 { return _haveLineSegment; }
653 SGLineSegmentd _lineSegment;
654 SGVec3d _linearVelocity;
655 SGVec3d _angularVelocity;
657 bool _haveLineSegment;
664 FGGroundCache::get_cat(double t, const SGVec3d& pt,
665 SGVec3d end[2], SGVec3d vel[2])
667 double maxDistance = 1000;
669 // Get the wire in question
670 t += cache_time_offset;
671 CatapultFinder catapultFinder(SGSphered(pt, maxDistance), t);
673 _localBvhTree->accept(catapultFinder);
675 if (!catapultFinder.getHaveLineSegment())
678 // prepare the returns
679 end[0] = catapultFinder.getLineSegment().getStart();
680 end[1] = catapultFinder.getLineSegment().getEnd();
682 // The linear velocity is the one at the start of the line segment ...
683 vel[0] = catapultFinder.getLinearVelocity();
684 // ... so the end point has the additional cross product.
685 vel[1] = catapultFinder.getLinearVelocity();
686 vel[1] += cross(catapultFinder.getAngularVelocity(),
687 catapultFinder.getLineSegment().getDirection());
689 // Return the distance to the cat
690 return sqrt(distSqr(catapultFinder.getLineSegment(), pt));
694 FGGroundCache::get_agl(double t, const SGVec3d& pt, SGVec3d& contact,
695 SGVec3d& normal, SGVec3d& linearVel, SGVec3d& angularVel,
696 simgear::BVHNode::Id& id, const simgear::BVHMaterial*& material)
698 #ifdef GROUNDCACHE_DEBUG
699 SGTimeStamp t0 = SGTimeStamp::now();
702 // Just set up a ground intersection query for the given point
703 SGLineSegmentd line(pt, pt + 10*reference_vehicle_radius*down);
704 t += cache_time_offset;
705 simgear::BVHLineSegmentVisitor lineSegmentVisitor(line, t);
707 _localBvhTree->accept(lineSegmentVisitor);
709 #ifdef GROUNDCACHE_DEBUG
710 t0 = SGTimeStamp::now() - t0;
715 if (!lineSegmentVisitor.empty()) {
716 // Have an intersection
717 contact = lineSegmentVisitor.getPoint();
718 normal = lineSegmentVisitor.getNormal();
719 if (0 < dot(normal, down))
721 linearVel = lineSegmentVisitor.getLinearVelocity();
722 angularVel = lineSegmentVisitor.getAngularVelocity();
723 material = lineSegmentVisitor.getMaterial();
724 id = lineSegmentVisitor.getId();
728 // Whenever we did not have a ground triangle for the requested point,
729 // take the ground level we found during the current cache build.
730 // This is as good as what we had before for agl.
731 SGGeod geodPt = SGGeod::fromCart(pt);
732 geodPt.setElevationM(_altitude);
733 contact = SGVec3d::fromGeod(geodPt);
735 linearVel = SGVec3d(0, 0, 0);
736 angularVel = SGVec3d(0, 0, 0);
737 material = _material;
746 FGGroundCache::get_nearest(double t, const SGVec3d& pt, double maxDist,
747 SGVec3d& contact, SGVec3d& linearVel,
748 SGVec3d& angularVel, simgear::BVHNode::Id& id,
749 const simgear::BVHMaterial*& material)
754 #ifdef GROUNDCACHE_DEBUG
755 SGTimeStamp t0 = SGTimeStamp::now();
758 // Just set up a ground intersection query for the given point
759 SGSphered sphere(pt, maxDist);
760 t += cache_time_offset;
761 simgear::BVHNearestPointVisitor nearestPointVisitor(sphere, t);
762 _localBvhTree->accept(nearestPointVisitor);
764 #ifdef GROUNDCACHE_DEBUG
765 t0 = SGTimeStamp::now() - t0;
770 if (nearestPointVisitor.empty())
773 // Have geometry in the range of maxDist
774 contact = nearestPointVisitor.getPoint();
775 linearVel = nearestPointVisitor.getLinearVelocity();
776 angularVel = nearestPointVisitor.getAngularVelocity();
777 material = nearestPointVisitor.getMaterial();
778 id = nearestPointVisitor.getId();
784 class FGGroundCache::WireIntersector : public BVHVisitor {
786 WireIntersector(const SGVec3d pt[4], const double& t) :
787 _linearVelocity(SGVec3d::zeros()),
788 _angularVelocity(SGVec3d::zeros()),
792 // Build the two triangles spanning the area where the hook has moved
793 // during the past step.
794 _triangles[0].set(pt[0], pt[1], pt[2]);
795 _triangles[1].set(pt[0], pt[2], pt[3]);
798 virtual void apply(BVHGroup& leaf)
800 if (!_intersects(leaf.getBoundingSphere()))
803 leaf.traverse(*this);
805 virtual void apply(BVHPageNode& leaf)
807 if (!_intersects(leaf.getBoundingSphere()))
810 leaf.traverse(*this);
812 virtual void apply(BVHTransform& transform)
814 if (!_intersects(transform.getBoundingSphere()))
817 SGTriangled triangles[2] = { _triangles[0], _triangles[1] };
818 _triangles[0] = triangles[0].transform(transform.getToLocalTransform());
819 _triangles[1] = triangles[1].transform(transform.getToLocalTransform());
821 transform.traverse(*this);
824 _lineSegment = transform.lineSegmentToWorld(_lineSegment);
825 _linearVelocity = transform.vecToWorld(_linearVelocity);
826 _angularVelocity = transform.vecToWorld(_angularVelocity);
828 _triangles[0] = triangles[0];
829 _triangles[1] = triangles[1];
831 virtual void apply(BVHMotionTransform& transform)
833 if (!_intersects(transform.getBoundingSphere()))
836 SGMatrixd toLocal = transform.getToLocalTransform(_time);
838 SGTriangled triangles[2] = { _triangles[0], _triangles[1] };
839 _triangles[0] = triangles[0].transform(toLocal);
840 _triangles[1] = triangles[1].transform(toLocal);
842 transform.traverse(*this);
845 SGMatrixd toWorld = transform.getToWorldTransform(_time);
847 += transform.getLinearVelocityAt(_lineSegment.getStart());
848 _angularVelocity += transform.getAngularVelocity();
849 _linearVelocity = toWorld.xformVec(_linearVelocity);
850 _angularVelocity = toWorld.xformVec(_angularVelocity);
851 _lineSegment = _lineSegment.transform(toWorld);
853 _triangles[0] = triangles[0];
854 _triangles[1] = triangles[1];
856 virtual void apply(BVHLineGeometry& node)
858 if (node.getType() != BVHLineGeometry::CarrierWire)
860 SGLineSegmentd lineSegment(node.getLineSegment());
861 if (!_intersects(lineSegment))
864 _lineSegment = lineSegment;
865 _linearVelocity = SGVec3d::zeros();
866 _angularVelocity = SGVec3d::zeros();
869 virtual void apply(BVHStaticGeometry& node)
872 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
873 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
875 bool _intersects(const SGSphered& sphere) const
879 if (intersects(_triangles[0], sphere))
881 if (intersects(_triangles[1], sphere))
885 bool _intersects(const SGLineSegmentd& lineSegment) const
889 if (intersects(_triangles[0], lineSegment))
891 if (intersects(_triangles[1], lineSegment))
896 const SGLineSegmentd& getLineSegment() const
897 { return _lineSegment; }
898 const SGVec3d& getLinearVelocity() const
899 { return _linearVelocity; }
900 const SGVec3d& getAngularVelocity() const
901 { return _angularVelocity; }
903 const BVHLineGeometry* getWire() const
907 SGLineSegmentd _lineSegment;
908 SGVec3d _linearVelocity;
909 SGVec3d _angularVelocity;
910 const BVHLineGeometry* _wire;
912 SGTriangled _triangles[2];
916 bool FGGroundCache::caught_wire(double t, const SGVec3d pt[4])
918 // Get the wire in question
919 t += cache_time_offset;
920 WireIntersector wireIntersector(pt, t);
922 _localBvhTree->accept(wireIntersector);
924 _wire = wireIntersector.getWire();
928 class FGGroundCache::WireFinder : public BVHVisitor {
930 WireFinder(const BVHLineGeometry* wire, const double& t) :
933 _lineSegment(SGVec3d::zeros(), SGVec3d::zeros()),
934 _linearVelocity(SGVec3d::zeros()),
935 _angularVelocity(SGVec3d::zeros()),
936 _haveLineSegment(false)
939 virtual void apply(BVHGroup& leaf)
941 if (_haveLineSegment)
943 leaf.traverse(*this);
945 virtual void apply(BVHPageNode& leaf)
947 if (_haveLineSegment)
949 leaf.traverse(*this);
951 virtual void apply(BVHTransform& transform)
953 if (_haveLineSegment)
956 transform.traverse(*this);
958 if (_haveLineSegment) {
959 _linearVelocity = transform.vecToWorld(_linearVelocity);
960 _angularVelocity = transform.vecToWorld(_angularVelocity);
961 _lineSegment = transform.lineSegmentToWorld(_lineSegment);
964 virtual void apply(BVHMotionTransform& transform)
966 if (_haveLineSegment)
969 transform.traverse(*this);
971 if (_haveLineSegment) {
972 SGMatrixd toWorld = transform.getToWorldTransform(_time);
974 += transform.getLinearVelocityAt(_lineSegment.getStart());
975 _angularVelocity += transform.getAngularVelocity();
976 _linearVelocity = toWorld.xformVec(_linearVelocity);
977 _angularVelocity = toWorld.xformVec(_angularVelocity);
978 _lineSegment = _lineSegment.transform(toWorld);
981 virtual void apply(BVHLineGeometry& node)
983 if (_haveLineSegment)
987 if (node.getType() != BVHLineGeometry::CarrierWire)
989 _lineSegment = SGLineSegmentd(node.getLineSegment());
990 _linearVelocity = SGVec3d::zeros();
991 _angularVelocity = SGVec3d::zeros();
992 _haveLineSegment = true;
994 virtual void apply(BVHStaticGeometry&) { }
996 virtual void apply(const BVHStaticBinary&, const BVHStaticData&) { }
997 virtual void apply(const BVHStaticTriangle&, const BVHStaticData&) { }
999 const SGLineSegmentd& getLineSegment() const
1000 { return _lineSegment; }
1002 bool getHaveLineSegment() const
1003 { return _haveLineSegment; }
1005 const SGVec3d& getLinearVelocity() const
1006 { return _linearVelocity; }
1007 const SGVec3d& getAngularVelocity() const
1008 { return _angularVelocity; }
1011 const BVHLineGeometry* _wire;
1014 SGLineSegmentd _lineSegment;
1015 SGVec3d _linearVelocity;
1016 SGVec3d _angularVelocity;
1018 bool _haveLineSegment;
1021 bool FGGroundCache::get_wire_ends(double t, SGVec3d end[2], SGVec3d vel[2])
1023 // Fast return if we do not have an active wire.
1027 // Get the wire in question
1028 t += cache_time_offset;
1029 WireFinder wireFinder(_wire, t);
1031 _localBvhTree->accept(wireFinder);
1033 if (!wireFinder.getHaveLineSegment())
1036 // prepare the returns
1037 end[0] = wireFinder.getLineSegment().getStart();
1038 end[1] = wireFinder.getLineSegment().getEnd();
1040 // The linear velocity is the one at the start of the line segment ...
1041 vel[0] = wireFinder.getLinearVelocity();
1042 // ... so the end point has the additional cross product.
1043 vel[1] = wireFinder.getLinearVelocity();
1044 vel[1] += cross(wireFinder.getAngularVelocity(),
1045 wireFinder.getLineSegment().getDirection());
1050 void FGGroundCache::release_wire(void)