Did you notice any signiifcant effect on the framerate? (for testing purposes, we might just as well disable most other subsystems or at least reduce scenery complexity to allow further development)
The graphs are neat, we might also want to take a look at using LOWI (Innsbruck airport) as a testbed for terrain sampling because it has interesting (read complex) terrain and is also known to have interesting weather patterns in real life as well (turbulence in the form of foehn winds, thermals, wave lift, ridge lift) and is also pretty nicely modeled in FlightGear by Tuxklok: viewtopic.php?f=5&t=5350
Several people here are using it for soaring scenarios and multiplayer gliding, so it is frequently discussed here, which might also make it easier to get more feedback.
And here are a couple of questions and considerations regarding the Nasal extensions required:
However, all this should probably not go into the main loop but into a worker thread...
That's true, and that would actually be another thing to keep in mind for the C++ code: ensure that the required Nasal/FG APIs are fully threadsafe, so that they can actually be called from worker threads once the need arises (there is some code in available in SimGear to help with this sort of stuff: http://simgear.org/doxygen/classSGMutex.html http://simgear.org/doxygen/classSGGuard.html )
terrain elevation: For thermal convection and barrier clouds, rapid sampling of terrain elevation is needed. Can there be a way to rapidly receive terrain elevation info only in Nasal for a whole set of pre-specified coordinates which performs better than geoinfo() calls?
http://wiki.flightgear.org/index.php/A_ ... 2B.2B_side
Depending on what it is exactly that is required, I think the solution that we discussed a couple of days ago (passing a vector with positions and getting one with normals) might be the easiest and fastest thing to do: viewtopic.php?f=5&p=70999#p70628
The question is how to sanely provide this info, i.e. from a format point of view: would it be sufficient to just pass in a vector of latitude/longitude pairs and get in turn just a vector that contains the computed normal for each tuple of GPS coordinates?
Like I said, this is the easiest approach and probably also the fastest. If on the other hand, we need to be able to lookup the normals by using the lat/lon tuple as a key for a map, we would need to return a nested hash instead.
Just using a Nasal vector would boil down to size(positions)/4 == size(normals) (with all normals in order).
This is straightforward to do, the code for the geoinfo API in Nasal (see NasalSys.cxx) looks like this (the Nasal internals are documented at http://wiki.flightgear.org/index.php/Ho ... tend_Nasal):
- Code: Select all
// For given geodetic point return array with elevation, and a material data
// hash, or nil if there's no information available (tile not loaded). If
// information about the material isn't available, then nil is returned instead
// of the hash.
static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args)
{
#define HASHSET(s,l,n) naHash_set(matdata, naStr_fromdata(naNewString(c),s,l),n)
if(argc < 2 || argc > 3)
naRuntimeError(c, "geodinfo() expects 2 or 3 arguments: lat, lon [, maxalt]");
double lat = naNumValue(args[0]).num;
double lon = naNumValue(args[1]).num;
double elev = argc == 3 ? naNumValue(args[2]).num : 10000;
const SGMaterial *mat;
SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
if(!globals->get_scenery()->get_elevation_m(geod, elev, &mat))
return naNil();
naRef vec = naNewVector(c);
naVec_append(vec, naNum(elev));
naRef matdata = naNil();
if(mat) {
matdata = naNewHash(c);
naRef names = naNewVector(c);
const vector<string> n = mat->get_names();
for(unsigned int i=0; i<n.size(); i++)
naVec_append(names, naStr_fromdata(naNewString(c),
const_cast<char*>(n[i].c_str()), n[i].size()));
HASHSET("names", 5, names);
HASHSET("solid", 5, naNum(mat->get_solid()));
HASHSET("friction_factor", 15, naNum(mat->get_friction_factor()));
HASHSET("rolling_friction", 16, naNum(mat->get_rolling_friction()));
HASHSET("load_resistance", 15, naNum(mat->get_load_resistance()));
HASHSET("bumpiness", 9, naNum(mat->get_bumpiness()));
HASHSET("light_coverage", 14, naNum(mat->get_light_coverage()));
}
naVec_append(vec, matdata);
return vec;
#undef HASHSET
}
So this is obviously doing some more things than just getting the terrain elevation and that would be another possible option for optimizing things, because most of this would not even be required for just computing normals.
Apart from that, it's just calling
- Code: Select all
if(!globals->get_scenery()->get_elevation_m(geod, elev, &mat))
Which is defined in scenery.cxx:
- Code: Select all
bool
FGScenery::get_elevation_m(const SGGeod& geod, double& alt,
const SGMaterial** material,
const osg::Node* butNotFrom)
{
SGVec3d start = SGVec3d::fromGeod(geod);
SGGeod geodEnd = geod;
geodEnd.setElevationM(SGMiscd::min(geod.getElevationM() - 10, -10000));
SGVec3d end = SGVec3d::fromGeod(geodEnd);
osgUtil::IntersectVisitor intersectVisitor;
intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
osg::ref_ptr<osg::LineSegment> lineSegment;
lineSegment = new osg::LineSegment(toOsg(start), toOsg(end));
intersectVisitor.addLineSegment(lineSegment.get());
get_scene_graph()->accept(intersectVisitor);
bool hits = false;
if (intersectVisitor.hits()) {
int nHits = intersectVisitor.getNumHits(lineSegment.get());
alt = -SGLimitsd::max();
for (int i = 0; i < nHits; ++i) {
const osgUtil::Hit& hit
= intersectVisitor.getHitList(lineSegment.get())[i];
if (butNotFrom &&
std::find(hit.getNodePath().begin(), hit.getNodePath().end(),
butNotFrom) != hit.getNodePath().end())
continue;
// We might need the double variant of the intersection point.
// Thus we cannot use the float variant delivered by
// hit.getWorldIntersectPoint() but we have to redo that with osg::Vec3d.
osg::Vec3d point = hit.getLocalIntersectPoint();
if (hit.getMatrix())
point = point*(*hit.getMatrix());
SGGeod geod = SGGeod::fromCart(toSG(point));
double elevation = geod.getElevationM();
if (alt < elevation) {
alt = elevation;
hits = true;
if (material)
*material = SGMaterialLib::findMaterial(hit.getGeode());
}
}
}
return hits;
}
This code could be further simplified, because we only need to compute normals (no material sampling required for the normals, right??) - to reduce the function call overhead in C++ space, we could just as well add another method that directly takes a pointer to lat/lon tuples:
double *
FGScenery::get_elevations_m_vector(size_t size, const SGGeod* geod, double* alt);
(I think, we should not gain anything by using the STL (std::vector) here because everything will be directly converted to Nasal structures which are C only?)
And returns a pointer to a list of computed normals, in the Nasal function this could then be converted to a Nasal vector and returned to the script. That way, we end up with only very few calls, even for large lists of positions.
If you think that the original functionality of the full geoinfo API should be retained, we could at least make it optional so that material info is only checked and provided when requested by the caller, that would keep things fast for situations where no material information is required.
PS: maybe we should quit discussing everything that comes to mind in one monster thread? There are now different things covered here already (weather modeling, textures, Nasal scripting, required extension functions), it might be better to ask a moderator to split this topic.