- return true;
- }
-
- bool enterNode(osg::Node& node)
- {
- if (!enterBoundingSphere(node.getBound()))
- return false;
-
- updateCullMode(node.getStateSet());
-
- FGGroundCache::GroundProperty& gp = mGroundProperty;
- // get some material information for use in the gear model
- gp.material = globals->get_matlib()->findMaterial(&node);
- if (gp.material) {
- gp.type = gp.material->get_solid() ? FGInterface::Solid : FGInterface::Water;
- return true;
- }
- osg::Referenced* base = node.getUserData();
- if (!base)
- return true;
- FGAICarrierHardware *ud =
- dynamic_cast<FGAICarrierHardware*>(base);
- if (!ud)
- return true;
-
- switch (ud->type) {
- case FGAICarrierHardware::Wire:
- gp.type = FGInterface::Wire;
- gp.wire_id = ud->id;
- break;
- case FGAICarrierHardware::Catapult:
- gp.type = FGInterface::Catapult;
- break;
- default:
- gp.type = FGInterface::Solid;
- break;
- }
- // Copy the velocity from the carrier class.
- ud->carrier->getVelocityWrtEarth(gp.vel, gp.rot, gp.pivot);
-
- return true;
- }
-
- void fillWith(osg::Drawable* drawable)
- {
- bool oldSphIsec = sphIsec;
- if (!enterBoundingSphere(drawable->getBound()))
- return;
-
- bool oldBackfaceCulling = mBackfaceCulling;
- updateCullMode(drawable->getStateSet());
-
- drawable->accept(mTriangleFunctor);
-
- mBackfaceCulling = oldBackfaceCulling;
- sphIsec = oldSphIsec;
- }
-
- virtual void apply(osg::Geode& geode)
- {
- bool oldBackfaceCulling = mBackfaceCulling;
- bool oldSphIsec = sphIsec;
- FGGroundCache::GroundProperty oldGp = mGroundProperty;
- if (!enterNode(geode))
- return;
-
- for(unsigned i = 0; i < geode.getNumDrawables(); ++i)
- fillWith(geode.getDrawable(i));
- sphIsec = oldSphIsec;
- mGroundProperty = oldGp;
- mBackfaceCulling = oldBackfaceCulling;
- }
-
- virtual void apply(osg::Group& group)
- {
- bool oldBackfaceCulling = mBackfaceCulling;
- bool oldSphIsec = sphIsec;
- FGGroundCache::GroundProperty oldGp = mGroundProperty;
- if (!enterNode(group))
- return;
- traverse(group);
- sphIsec = oldSphIsec;
- mBackfaceCulling = oldBackfaceCulling;
- mGroundProperty = oldGp;
- }
-
- virtual void apply(osg::Transform& transform)
- {
- if (!enterNode(transform))
- return;
- bool oldBackfaceCulling = mBackfaceCulling;
- bool oldSphIsec = sphIsec;
- FGGroundCache::GroundProperty oldGp = mGroundProperty;
- /// transform the caches center to local coords
- osg::Matrix oldLocalToGlobal = mLocalToGlobal;
- osg::Matrix oldGlobalToLocal = mGlobalToLocal;
- transform.computeLocalToWorldMatrix(mLocalToGlobal, this);
- transform.computeWorldToLocalMatrix(mGlobalToLocal, this);
-
- SGVec3d oldLocalCacheReference = mLocalCacheReference;
- mLocalCacheReference.osg() = mCacheReference.osg()*mGlobalToLocal;
- SGVec3d oldLocalDown = mLocalDown;
- mLocalDown.osg() = osg::Matrixd::transform3x3(mDown.osg(), mGlobalToLocal);
-
- // walk the children
- traverse(transform);
-
- // Restore that one
- mLocalDown = oldLocalDown;
- mLocalCacheReference = oldLocalCacheReference;
- mLocalToGlobal = oldLocalToGlobal;
- mGlobalToLocal = oldGlobalToLocal;
- sphIsec = oldSphIsec;
- mBackfaceCulling = oldBackfaceCulling;
- mGroundProperty = oldGp;
- }
-
- void addTriangle(const osg::Vec3& v1, const osg::Vec3& v2,
- const osg::Vec3& v3)
- {
- SGVec3d v[3] = {
- SGVec3d(v1),
- SGVec3d(v2),
- SGVec3d(v3)
- };
-
- // a bounding sphere in the node local system
- SGVec3d boundCenter = (1.0/3)*(v[0] + v[1] + v[2]);
-#if 0
- double boundRadius = std::max(norm1(v[0] - boundCenter),
- norm1(v[1] - boundCenter));
- boundRadius = std::max(boundRadius, norm1(v[2] - boundCenter));
- // Ok, we take the 1-norm instead of the expensive 2 norm.
- // Therefore we need that scaling factor - roughly sqrt(3)
- boundRadius = 1.733*boundRadius;
-#else
- double boundRadius = std::max(distSqr(v[0], boundCenter),
- distSqr(v[1], boundCenter));
- boundRadius = std::max(boundRadius, distSqr(v[2], boundCenter));
- boundRadius = sqrt(boundRadius);
+ // If we have an active wire, get some more area into the groundcache
+ if (_wire)
+ rad = SGMiscd::max(200, rad);
+
+ // Store the parameters we used to build up that cache.
+ reference_wgs84_point = pt;
+ reference_vehicle_radius = rad;
+ // Store the time reference used to compute movements of moving triangles.
+ cache_ref_time = startSimTime;
+
+ // Get a normalized down vector valid for the whole cache
+ SGQuatd hlToEc = SGQuatd::fromLonLat(geodPt);
+ down = hlToEc.rotate(SGVec3d(0, 0, 1));
+
+ // Get the ground cache, that is a local collision tree of the environment
+ startSimTime += cache_time_offset;
+ endSimTime += cache_time_offset;
+ CacheFill subtreeCollector(pt, down, rad, startSimTime, endSimTime);
+ globals->get_scenery()->get_scene_graph()->accept(subtreeCollector);
+ _localBvhTree = subtreeCollector.getBVHNode();
+
+ if (subtreeCollector.getHaveElevationBelowCache()) {
+ // Use the altitude value below the cache that we gathered during
+ // cache collection
+ _altitude = subtreeCollector.getElevationBelowCache();
+ _material = subtreeCollector.getMaterialBelowCache();
+ found_ground = true;
+ } else if (_localBvhTree) {
+ // We have nothing below us, so try starting with the lowest point
+ // upwards for a croase altitude value
+ SGLineSegmentd line(pt + reference_vehicle_radius*down, pt - 1e3*down);
+ simgear::BVHLineSegmentVisitor lineSegmentVisitor(line, startSimTime);
+ _localBvhTree->accept(lineSegmentVisitor);
+
+ if (!lineSegmentVisitor.empty()) {
+ SGGeod geodPt = SGGeod::fromCart(lineSegmentVisitor.getPoint());
+ _altitude = geodPt.getElevationM();
+ _material = lineSegmentVisitor.getMaterial();
+ found_ground = true;
+ }
+ }
+
+ if (!found_ground) {
+ // Ok, still nothing here?? Last resort ...
+ double alt = 0;
+ found_ground = globals->get_scenery()->
+ get_elevation_m(SGGeod::fromGeodM(geodPt, 10000), alt, &_material);
+ if (found_ground)
+ _altitude = alt;
+ }
+
+ // Still not sucessful??
+ if (!found_ground)
+ SG_LOG(SG_FLIGHT, SG_WARN, "prepare_ground_cache(): trying to build "
+ "cache without any scenery below the aircraft");
+
+#ifdef GROUNDCACHE_DEBUG
+ t0 = SGTimeStamp::now() - t0;
+ _buildTime += t0;
+ _buildCount++;
+
+ if (_buildCount > 60) {
+ double buildTime = 0;
+ if (_buildCount)
+ buildTime = _buildTime.toSecs()/_buildCount;
+ double lookupTime = 0;
+ if (_lookupCount)
+ lookupTime = _lookupTime.toSecs()/_lookupCount;
+ _buildTime = SGTimeStamp::fromSec(0.0);
+ _buildCount = 0;
+ _lookupTime = SGTimeStamp::fromSec(0.0);
+ _lookupCount = 0;
+ SG_LOG(SG_FLIGHT, SG_ALERT, "build time = " << buildTime
+ << ", lookup Time = " << lookupTime);
+ }
+
+ if (!_group.valid()) {
+ _group = new osg::Group;
+ globals->get_scenery()->get_scene_graph()->addChild(_group);
+ fgSetInt("/fdm/groundcache-debug-level", -3);
+ }
+ _group->removeChildren(0, _group->getNumChildren());
+ if (_localBvhTree) {
+ int level = fgGetInt("/fdm/groundcache-debug-level");
+ if (-2 <= level) {
+ simgear::BVHDebugCollectVisitor debug(endSimTime, level);
+ _localBvhTree->accept(debug);
+ _group->addChild(debug.getNode());
+ }
+ }