From c99ea20883d54f8f15efda4cc4178913dfb9e3ac Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Tue, 30 Oct 2018 20:10:30 +0100 Subject: [PATCH] LOD ranges rework. Rework the LOD ranges. 1. The scenery ranges are now deltas (avoids overlapping values) 2. The AI/MP pixel mode now has a default radius that is 20 for Aircraft, 200 for ships, 350 for carriers. This is a simple constant in a virtual function. 3. Added the ability to set the AI/MP ranges equal which means use the low detail model. 4. Changed high detail only to be indicated by a -ve number in maxRangeDetail 5. Re-ordered the range list to go from lowest detail at [0] to highest detail at the end. This is because OSG always loads the models starting from zero on the assumption that the detail increases with the index. This fixes the pixel mode, which previously would use the radius of the parent which would be confusingly large, and unrelated to the actual size of the model. With the simple defaults that we have the pixel values set in the ranges won't exactly match the rendered size of the model on screen, but it will be a lot closer and more importantly meaningful. --- src/AIModel/AIBase.cxx | 84 +++++++++++++++++++++++++++---------- src/AIModel/AIBase.hxx | 9 +++- src/AIModel/AICarrier.hxx | 1 + src/AIModel/AIManager.cxx | 4 +- src/AIModel/AIShip.hxx | 1 + src/Scenery/terrain_pgt.cxx | 4 +- src/Scenery/tilemgr.cxx | 9 +++- src/Scenery/tilemgr.hxx | 2 +- 8 files changed, 83 insertions(+), 31 deletions(-) diff --git a/src/AIModel/AIBase.cxx b/src/AIModel/AIBase.cxx index 1498a4435..f48298cec 100644 --- a/src/AIModel/AIBase.cxx +++ b/src/AIModel/AIBase.cxx @@ -310,7 +310,7 @@ void FGAIBase::updateInterior() if(d2 <= _maxRangeInterior){ // if the AI is in-range we load the interior _interior = SGModelLib::loadPagedModel(_modeldata->getInteriorPath(), props, _modeldata); if(_interior.valid()){ - _interior->setRange(0, 0.0, _maxRangeInterior); + _interior->setRange(modelHighDetailIndex, 0.0, _maxRangeInterior); aip.add(_interior.get()); _modeldata->setInteriorLoaded(true); SG_LOG(SG_AI, SG_INFO, "AIBase: Loaded interior model " << _interior->getName()); @@ -322,25 +322,54 @@ void FGAIBase::updateInterior() /** update LOD properties of the model */ void FGAIBase::updateLOD() { - double maxRangeDetail = fgGetDouble("/sim/rendering/static-lod/ai-detailed", 3000.0); - double maxRangeBare = fgGetDouble("/sim/rendering/static-lod/ai-bare", 10000.0); + double maxRangeDetail = fgGetDouble("/sim/rendering/static-lod/aimp-detailed", 3000.0); + double maxRangeBare = fgGetDouble("/sim/rendering/static-lod/aimp-bare", 10000.0); - _maxRangeInterior = fgGetDouble("/sim/rendering/static-lod/ai-interior", 50.0); + _maxRangeInterior = fgGetDouble("/sim/rendering/static-lod/aimp-interior", 50.0); if (_model.valid()) { - if( maxRangeDetail == 0.0 ) + bool pixel_mode = !fgGetBool("/sim/rendering/static-lod/aimp-range-mode-distance", false); + if (pixel_mode) + _model->setRangeMode(osg::LOD::PIXEL_SIZE_ON_SCREEN); + else + _model->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT); + + if (maxRangeDetail < 0) // disable the bare model { - // Disable LOD. The First entry in the LOD node is the most detailed + // Disable LOD. The first entry in the LOD node is the most detailed // so use that. - _model->setRange(0, 0.0, FLT_MAX); if (_model->getNumFileNames() == 2) { - _model->setRange(1, FLT_MAX, FLT_MAX); + _model->setRange(modelHighDetailIndex, 0.0, FLT_MAX); // all ranges. + _model->setRange(modelLowDetailIndex, FLT_MAX, FLT_MAX); } + else + _model->setRange(modelHighDetailIndex , 0.0, FLT_MAX); // all ranges. + } + else if (maxRangeBare == maxRangeDetail) // only use the bare model + { + double start_range, end_range; + if (pixel_mode) { + // pixels, so the start of the range is when we want this to be drawn. this should + // be zero pixels to ensure that something is visible. + start_range = maxRangeDetail; + end_range = FLT_MAX; + } + else { + // meters; so start from 0 end and at the max range. + start_range = maxRangeDetail; + end_range = FLT_MAX; + } + if (_model->getNumFileNames() == 2) { + _model->setRange(modelHighDetailIndex , FLT_MAX, FLT_MAX); + _model->setRange(modelLowDetailIndex , start_range, end_range); + } + else + _model->setRange(modelHighDetailIndex , start_range, end_range); } else { - if( fgGetBool("/sim/rendering/static-lod/ai-range-mode-pixel", false ) ) + if(pixel_mode) { /* In pixel size mode, the range sense is reversed, so we want the * detailed model [0] to be displayed when the "range" is really @@ -356,21 +385,28 @@ void FGAIBase::updateLOD() maxRangeBare = maxRangeDetail; SG_LOG(SG_AI, SG_WARN, - "/sim/rendering/static-lod/ai-bare greater " << - "than /sim/rendering/static-lod/ai-detailed when using " << - "/sim/rendering/static-lod/ai-range-mode-pixel=true. Ignoring ai-bare." + "/sim/rendering/static-lod/aimp-bare greater " << + "than /sim/rendering/static-lod/aimp-detailed when using " << + "/sim/rendering/static-lod/aimp-range-mode-distance=false. Ignoring ai-bare." ); } - _model->setRangeMode( osg::LOD::PIXEL_SIZE_ON_SCREEN ); if (_model->getNumFileNames() == 2) { - _model->setRange(0, maxRangeDetail, 100000 ); - _model->setRange(1, maxRangeBare, maxRangeDetail); + /*if (_model->getRadius() < 0) + { + osg::BoundingSphere bs = _model->computeBound(); + if (bs.radius() > 0) { + _model->setRadius(bs.radius()); + _model->setCenterMode(osg::LOD::CenterMode::USER_DEFINED_CENTER); + } + }*/ + _model->setRange(modelHighDetailIndex , maxRangeDetail, 100000); // most detailed + _model->setRange(modelLowDetailIndex , maxRangeBare, maxRangeDetail); // least detailed } else { /* If we have only one LoD for this model, then we want to * display it from the smallest pixel value */ - _model->setRange(0, min(maxRangeBare, maxRangeDetail), 100000 ); + _model->setRange(modelHighDetailIndex , min(maxRangeBare, maxRangeDetail), 100000 ); } } else { /* In non-pixel range mode we're dealing with straight distance. @@ -384,20 +420,19 @@ void FGAIBase::updateLOD() maxRangeBare = maxRangeDetail; SG_LOG(SG_AI, SG_WARN, - "/sim/rendering/static-lod/ai-bare less than " << - "than /sim/rendering/static-lod/ai-detailed. Ignoring ai-bare." + "/sim/rendering/static-lod/aimp-bare less than " << + "than /sim/rendering/static-lod/aimp-detailed. Ignoring ai-bare." ); } - _model->setRangeMode( osg::LOD:: DISTANCE_FROM_EYE_POINT); if (_model->getNumFileNames() == 2) { - _model->setRange(0, 0, maxRangeDetail); - _model->setRange(1, maxRangeDetail, maxRangeBare); + _model->setRange(modelHighDetailIndex , 0, maxRangeDetail); // most detailed + _model->setRange(modelLowDetailIndex , maxRangeDetail, maxRangeDetail+maxRangeBare); // least detailed } else { /* If we have only one LoD for this model, then we want to * display it from whatever range. */ - _model->setRange(0, 0, max(maxRangeBare, maxRangeDetail)); + _model->setRange(modelHighDetailIndex , 0, max(maxRangeBare, maxRangeDetail)); } } } @@ -488,11 +523,13 @@ std::vector FGAIBase::resolveModelPath(ModelSearchOrder searchOrder } // At this point we're looking for a regular model to display at closer range. + // From experimentation it seems to work best if the LODs are in the range list in terms of detail + // from lowest to highest - so insert this at the end. auto p = simgear::SGModelLib::findDataFile(model_path); if (!p.empty()) { _installed = true; SG_LOG(SG_AI, SG_DEBUG, "Found DATA model " << p); - path_list.insert(path_list.begin(), p); + path_list.insert(path_list.end(), p); } } @@ -520,6 +557,7 @@ bool FGAIBase::init(ModelSearchOrder searchOrder) vector model_list = resolveModelPath(searchOrder); _model= SGModelLib::loadPagedModel(model_list, props, _modeldata); _model->setName("AI-model range animation node"); + _model->setRadius(getDefaultModelRadius()); updateLOD(); initModel(); diff --git a/src/AIModel/AIBase.hxx b/src/AIModel/AIBase.hxx index 3de51a261..919f0b942 100644 --- a/src/AIModel/AIBase.hxx +++ b/src/AIModel/AIBase.hxx @@ -55,6 +55,12 @@ public: FGAIBase(object_type ot, bool enableHot); virtual ~FGAIBase(); + // these depend on the order in which the models are loaded. OSG is a little vague about this, but + // from experimentation it seems to work best if the LODs are in the range list in terms of detail + // from lowest to highest + const int modelLowDetailIndex = 0; + const int modelHighDetailIndex = 1; + virtual void readFromScenario(SGPropertyNode* scFileNode); enum ModelSearchOrder { @@ -69,7 +75,8 @@ public: virtual void bind(); virtual void unbind(); virtual void reinit() {} - + // default model radius for LOD. + virtual double getDefaultModelRadius() { return 20.0; } void updateLOD(); void updateInterior(); void setManager(FGAIManager* mgr, SGPropertyNode* p); diff --git a/src/AIModel/AICarrier.hxx b/src/AIModel/AICarrier.hxx index 75dfdba16..69ba58154 100644 --- a/src/AIModel/AICarrier.hxx +++ b/src/AIModel/AICarrier.hxx @@ -48,6 +48,7 @@ public: void setSign(const string& ); void setDeckAltitude(const double altitude_feet); void setTACANChannelID(const string &); + virtual double getDefaultModelRadius() { return 350.0; } virtual void bind(); void UpdateWind ( double dt ); diff --git a/src/AIModel/AIManager.cxx b/src/AIModel/AIManager.cxx index 87c8197e3..83b447b2c 100644 --- a/src/AIModel/AIManager.cxx +++ b/src/AIModel/AIManager.cxx @@ -105,9 +105,9 @@ private: FGAIManager::FGAIManager() : cb_ai_bare(SGPropertyChangeCallback(this,&FGAIManager::updateLOD, - fgGetNode("/sim/rendering/static-lod/ai-bare", true))), + fgGetNode("/sim/rendering/static-lod/aimp-bare", true))), cb_ai_detailed(SGPropertyChangeCallback(this,&FGAIManager::updateLOD, - fgGetNode("/sim/rendering/static-lod/ai-detailed", true))) + fgGetNode("/sim/rendering/static-lod/aimp-detailed", true))) { } diff --git a/src/AIModel/AIShip.hxx b/src/AIModel/AIShip.hxx index c0f62133a..4bb1862a8 100644 --- a/src/AIModel/AIShip.hxx +++ b/src/AIModel/AIShip.hxx @@ -41,6 +41,7 @@ public: virtual void bind(); virtual void update(double dt); virtual void reinit(); + virtual double getDefaultModelRadius() { return 200.0; } void setRudder(float r); void setRoll(double rl); diff --git a/src/Scenery/terrain_pgt.cxx b/src/Scenery/terrain_pgt.cxx index d3811d28b..a80fd8fe1 100644 --- a/src/Scenery/terrain_pgt.cxx +++ b/src/Scenery/terrain_pgt.cxx @@ -84,8 +84,8 @@ void FGPgtTerrain::init( osg::Group* terrain ) { options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().local8BitStr()); - options->setPluginStringData("SimGear::BARE_LOD_RANGE", fgGetString("/sim/rendering/static-lod/bare", boost::lexical_cast(SG_OBJECT_RANGE_BARE))); - options->setPluginStringData("SimGear::ROUGH_LOD_RANGE", fgGetString("/sim/rendering/static-lod/rough", boost::lexical_cast(SG_OBJECT_RANGE_ROUGH))); + options->setPluginStringData("SimGear::BARE_LOD_RANGE", fgGetString("/sim/rendering/static-lod/bare-delta", boost::lexical_cast(SG_OBJECT_RANGE_BARE))); + options->setPluginStringData("SimGear::ROUGH_LOD_RANGE", fgGetString("/sim/rendering/static-lod/rough-delta", boost::lexical_cast(SG_OBJECT_RANGE_ROUGH))); options->setPluginStringData("SimGear::ROUGH_LOD_DETAILED", fgGetString("/sim/rendering/static-lod/detailed", boost::lexical_cast(SG_OBJECT_RANGE_DETAILED))); options->setPluginStringData("SimGear::RENDER_BUILDING_MESH", fgGetBool("/sim/rendering/building-mesh", false) ? "true" : "false"); diff --git a/src/Scenery/tilemgr.cxx b/src/Scenery/tilemgr.cxx index cb23a0212..9eb762044 100644 --- a/src/Scenery/tilemgr.cxx +++ b/src/Scenery/tilemgr.cxx @@ -116,7 +116,9 @@ FGTileMgr::FGTileMgr(): _terra_sync(NULL), _listener(NULL), _visibilityMeters(fgGetNode("/environment/visibility-m", true)), - _maxTileRangeM(fgGetNode("/sim/rendering/static-lod/bare", true)), + _lodDetailed(fgGetNode("/sim/rendering/static-lod/detailed", true)), + _lodRoughDelta(fgGetNode("/sim/rendering/static-lod/rough-delta", true)), + _lodBareDelta(fgGetNode("/sim/rendering/static-lod/bare-delta", true)), _disableNasalHooks(fgGetNode("/sim/temp/disable-scenery-nasal", true)), _scenery_loaded(fgGetNode("/sim/sceneryloaded", true)), _scenery_override(fgGetNode("/sim/sceneryloaded-override", true)), @@ -295,8 +297,11 @@ void FGTileMgr::schedule_needed(const SGBucket& curr_bucket, double vis) // cout << "tile width = " << tile_width << " tile_height = " // << tile_height << endl; + // starting with 2018.3 we will use deltas rather than absolutes as it is more intuitive for the user + // and somewhat easier to visualise + double maxTileRange = _lodDetailed->getDoubleValue() + _lodRoughDelta->getDoubleValue() + _lodBareDelta->getDoubleValue(); - double tileRangeM = std::min(vis,_maxTileRangeM->getDoubleValue()); + double tileRangeM = std::min(vis, maxTileRange); int xrange = (int)(tileRangeM / tile_width) + 1; int yrange = (int)(tileRangeM / tile_height) + 1; if ( xrange < 1 ) { xrange = 1; } diff --git a/src/Scenery/tilemgr.hxx b/src/Scenery/tilemgr.hxx index 299930562..40a83517c 100644 --- a/src/Scenery/tilemgr.hxx +++ b/src/Scenery/tilemgr.hxx @@ -86,7 +86,7 @@ private: void schedule_tiles_at(const SGGeod& location, double rangeM); SGPropertyNode_ptr _visibilityMeters; - SGPropertyNode_ptr _maxTileRangeM, _disableNasalHooks; + SGPropertyNode_ptr _lodDetailed, _lodRoughDelta, _lodBareDelta, _disableNasalHooks; SGPropertyNode_ptr _scenery_loaded, _scenery_override; osg::ref_ptr _pager;