Fallback AI model implementation
Aircraft can now set a /sim/model/fallback-model-index property that is transmitted over the MP network. Receiving clients use this as an index into AI/Aircraft/fallback_models.xml to determine a model to use if the model path (/sim/model/path) cannot be found under Aircraft or AI/Aircraft. This allows aircraft developers to identify a suitable fallback model to be used for those who do not have their aircraft installed.
This commit is contained in:
parent
23196184cc
commit
e43fe82094
7 changed files with 149 additions and 105 deletions
|
@ -244,6 +244,8 @@ void FGAIBase::readFromScenario(SGPropertyNode* scFileNode)
|
||||||
setPath(scFileNode->getStringValue("model",
|
setPath(scFileNode->getStringValue("model",
|
||||||
fgGetString("/sim/multiplay/default-model", default_model)));
|
fgGetString("/sim/multiplay/default-model", default_model)));
|
||||||
|
|
||||||
|
setFallbackModelIndex(scFileNode->getIntValue("fallback-model-index", 0));
|
||||||
|
|
||||||
setHeading(scFileNode->getDoubleValue("heading", 0.0));
|
setHeading(scFileNode->getDoubleValue("heading", 0.0));
|
||||||
setSpeed(scFileNode->getDoubleValue("speed", 0.0));
|
setSpeed(scFileNode->getDoubleValue("speed", 0.0));
|
||||||
setAltitude(scFileNode->getDoubleValue("altitude", 0.0));
|
setAltitude(scFileNode->getDoubleValue("altitude", 0.0));
|
||||||
|
@ -446,24 +448,51 @@ std::vector<std::string> FGAIBase::resolveModelPath(ModelSearchOrder searchOrder
|
||||||
std::vector<std::string> path_list;
|
std::vector<std::string> path_list;
|
||||||
|
|
||||||
if (searchOrder == DATA_ONLY) {
|
if (searchOrder == DATA_ONLY) {
|
||||||
|
SG_LOG(SG_AI, SG_DEBUG, "Resolving model path: DATA only");
|
||||||
auto p = simgear::SGModelLib::findDataFile(model_path);
|
auto p = simgear::SGModelLib::findDataFile(model_path);
|
||||||
if (!p.empty()) {
|
if (!p.empty()) {
|
||||||
// We've got a model, use it
|
// We've got a model, use it
|
||||||
_installed = true;
|
_installed = true;
|
||||||
|
SG_LOG(SG_AI, SG_DEBUG, "Found model " << p);
|
||||||
path_list.push_back(p);
|
path_list.push_back(p);
|
||||||
} else {
|
} else {
|
||||||
// No model, so fall back to the default
|
// No model, so fall back to the default
|
||||||
path_list.push_back(fgGetString("/sim/multiplay/default-model", default_model));
|
path_list.push_back(fgGetString("/sim/multiplay/default-model", default_model));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
SG_LOG(SG_AI, SG_DEBUG, "Resolving model path: PREFER_AI/PREFER_DATA");
|
||||||
// We're either PREFER_AI or PREFER_DATA. Find an AI model first.
|
// We're either PREFER_AI or PREFER_DATA. Find an AI model first.
|
||||||
for (SGPath p : globals->get_data_paths("AI")) {
|
for (SGPath p : globals->get_data_paths("AI")) {
|
||||||
p.append(model_path);
|
p.append(model_path);
|
||||||
if (p.exists()) {
|
if (p.exists()) {
|
||||||
|
SG_LOG(SG_AI, SG_DEBUG, "Found AI model: " << p.local8BitStr());
|
||||||
path_list.push_back(p.local8BitStr());
|
path_list.push_back(p.local8BitStr());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} // of AI data paths iteration
|
}
|
||||||
|
|
||||||
|
if (path_list.empty()) {
|
||||||
|
// Fall back on the fallback-model-index which is a lookup into
|
||||||
|
// /sim/multiplay/fallback-models/model[]
|
||||||
|
std::string fallback_path;
|
||||||
|
const SGPropertyNode* fallbackNode =
|
||||||
|
globals->get_props()->getNode("/sim/multiplay/fallback-models/model", _getFallbackModelIndex(), false);
|
||||||
|
|
||||||
|
if (fallbackNode != NULL) {
|
||||||
|
fallback_path = fallbackNode->getStringValue();
|
||||||
|
} else {
|
||||||
|
fallback_path = globals->get_props()->getNode("/sim/multiplay/fallback-models/model", 0, true)->getStringValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SGPath p : globals->get_data_paths()) {
|
||||||
|
p.append(fallback_path);
|
||||||
|
if (p.exists()) {
|
||||||
|
SG_LOG(SG_AI, SG_DEBUG, "Found fallback model path for index " << _fallback_model_index << ": " << p.local8BitStr());
|
||||||
|
path_list.push_back(p.local8BitStr());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((searchOrder == PREFER_AI) && !path_list.empty()) {
|
if ((searchOrder == PREFER_AI) && !path_list.empty()) {
|
||||||
// if we prefer AI, and we've got a valid AI path from above, then use it, we're done
|
// if we prefer AI, and we've got a valid AI path from above, then use it, we're done
|
||||||
|
@ -471,18 +500,13 @@ std::vector<std::string> FGAIBase::resolveModelPath(ModelSearchOrder searchOrder
|
||||||
return path_list;
|
return path_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point we've either still to find a valid path, or we're
|
// At this point we're looking for a regular model to display at closer range.
|
||||||
// looking for a regular model to display at closer range.
|
|
||||||
auto p = simgear::SGModelLib::findDataFile(model_path);
|
auto p = simgear::SGModelLib::findDataFile(model_path);
|
||||||
if (!p.empty()) {
|
if (!p.empty()) {
|
||||||
_installed = true;
|
_installed = true;
|
||||||
|
SG_LOG(SG_AI, SG_DEBUG, "Found DATA model " << p);
|
||||||
path_list.insert(path_list.begin(), p);
|
path_list.insert(path_list.begin(), p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path_list.empty()) {
|
|
||||||
// No model found at all, so fall back to the default
|
|
||||||
path_list.push_back(fgGetString("/sim/multiplay/default-model", default_model));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -503,10 +527,10 @@ bool FGAIBase::init(ModelSearchOrder searchOrder)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<string> model_list = resolveModelPath(searchOrder);
|
|
||||||
|
|
||||||
props->addChild("type")->setStringValue("AI");
|
props->addChild("type")->setStringValue("AI");
|
||||||
_modeldata = new FGAIModelData(props);
|
_modeldata = new FGAIModelData(props);
|
||||||
|
|
||||||
|
vector<string> model_list = resolveModelPath(searchOrder);
|
||||||
_model= SGModelLib::loadPagedModel(model_list, props, _modeldata);
|
_model= SGModelLib::loadPagedModel(model_list, props, _modeldata);
|
||||||
_model->setName("AI-model range animation node");
|
_model->setName("AI-model range animation node");
|
||||||
|
|
||||||
|
@ -982,6 +1006,10 @@ const char* FGAIBase::_getSubmodel() const {
|
||||||
return _submodel.c_str();
|
return _submodel.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int FGAIBase::_getFallbackModelIndex() const {
|
||||||
|
return _fallback_model_index;
|
||||||
|
}
|
||||||
|
|
||||||
void FGAIBase::CalculateMach() {
|
void FGAIBase::CalculateMach() {
|
||||||
// Calculate rho at altitude, using standard atmosphere
|
// Calculate rho at altitude, using standard atmosphere
|
||||||
// For the temperature T and the pressure p,
|
// For the temperature T and the pressure p,
|
||||||
|
|
|
@ -74,6 +74,7 @@ public:
|
||||||
void updateInterior();
|
void updateInterior();
|
||||||
void setManager(FGAIManager* mgr, SGPropertyNode* p);
|
void setManager(FGAIManager* mgr, SGPropertyNode* p);
|
||||||
void setPath( const char* model );
|
void setPath( const char* model );
|
||||||
|
void setFallbackModelIndex(const int i );
|
||||||
void setSMPath( const std::string& p );
|
void setSMPath( const std::string& p );
|
||||||
void setCallSign(const std::string& );
|
void setCallSign(const std::string& );
|
||||||
void setSpeed( double speed_KTAS );
|
void setSpeed( double speed_KTAS );
|
||||||
|
@ -208,7 +209,8 @@ protected:
|
||||||
double rotation; // value used by radar display instrument
|
double rotation; // value used by radar display instrument
|
||||||
double ht_diff; // value used by radar display instrument
|
double ht_diff; // value used by radar display instrument
|
||||||
|
|
||||||
std::string model_path; //Path to the 3D model
|
std::string model_path; // Path to the 3D model
|
||||||
|
int _fallback_model_index; // Index into /sim/multiplay/fallback-models[]
|
||||||
SGModelPlacement aip;
|
SGModelPlacement aip;
|
||||||
|
|
||||||
bool delete_me;
|
bool delete_me;
|
||||||
|
@ -316,10 +318,9 @@ public:
|
||||||
const char* _getTriggerNode() const;
|
const char* _getTriggerNode() const;
|
||||||
const char* _getName() const;
|
const char* _getName() const;
|
||||||
const char* _getSubmodel() const;
|
const char* _getSubmodel() const;
|
||||||
|
const int _getFallbackModelIndex() const;
|
||||||
|
|
||||||
// These are used in the Mach number calculations
|
// These are used in the Mach number calculations
|
||||||
|
|
||||||
double rho;
|
double rho;
|
||||||
double T; // temperature, degs farenheit
|
double T; // temperature, degs farenheit
|
||||||
double p; // pressure lbs/sq ft
|
double p; // pressure lbs/sq ft
|
||||||
|
@ -348,6 +349,10 @@ inline void FGAIBase::setPath(const char* model ) {
|
||||||
model_path.append(model);
|
model_path.append(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void FGAIBase::setFallbackModelIndex(const int i ) {
|
||||||
|
_fallback_model_index = i;
|
||||||
|
}
|
||||||
|
|
||||||
inline void FGAIBase::setSMPath(const std::string& p) {
|
inline void FGAIBase::setSMPath(const std::string& p) {
|
||||||
_path = p;
|
_path = p;
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,8 +134,6 @@ FGAIManager::init() {
|
||||||
globals->get_commands()->addCommand("unload-scenario", this, &FGAIManager::unloadScenarioCommand);
|
globals->get_commands()->addCommand("unload-scenario", this, &FGAIManager::unloadScenarioCommand);
|
||||||
_environmentVisiblity = fgGetNode("/environment/visibility-m");
|
_environmentVisiblity = fgGetNode("/environment/visibility-m");
|
||||||
|
|
||||||
_mp_use_detailed_models = fgGetNode("/sim/multiplay/use-detailed-models", true);
|
|
||||||
|
|
||||||
// Create an (invisible) AIAircraft representation of the current
|
// Create an (invisible) AIAircraft representation of the current
|
||||||
// users's aircraft, that mimicks the user aircraft's behavior.
|
// users's aircraft, that mimicks the user aircraft's behavior.
|
||||||
|
|
||||||
|
@ -315,7 +313,7 @@ FGAIManager::attach(FGAIBase *model)
|
||||||
modelPolicy = FGAIBase::PREFER_AI;
|
modelPolicy = FGAIBase::PREFER_AI;
|
||||||
break;
|
break;
|
||||||
case FGAIBase::otMultiplayer:
|
case FGAIBase::otMultiplayer:
|
||||||
modelPolicy = this->_mp_use_detailed_models->getBoolValue() ? FGAIBase::PREFER_DATA : FGAIBase::PREFER_AI;
|
modelPolicy = FGAIBase::PREFER_DATA;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -120,8 +120,6 @@ private:
|
||||||
SGPropertyNode_ptr wind_from_east_node;
|
SGPropertyNode_ptr wind_from_east_node;
|
||||||
SGPropertyNode_ptr wind_from_north_node;
|
SGPropertyNode_ptr wind_from_north_node;
|
||||||
SGPropertyNode_ptr _environmentVisiblity;
|
SGPropertyNode_ptr _environmentVisiblity;
|
||||||
SGPropertyNode_ptr _mp_use_detailed_models;
|
|
||||||
|
|
||||||
|
|
||||||
ai_list_type ai_list;
|
ai_list_type ai_list;
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ FGAIMultiplayer::~FGAIMultiplayer() {
|
||||||
bool FGAIMultiplayer::init(ModelSearchOrder searchOrder)
|
bool FGAIMultiplayer::init(ModelSearchOrder searchOrder)
|
||||||
{
|
{
|
||||||
props->setStringValue("sim/model/path", model_path);
|
props->setStringValue("sim/model/path", model_path);
|
||||||
|
props->setIntValue("sim/model/fallback-model-index", _getFallbackModelIndex());
|
||||||
//refuel_node = fgGetNode("systems/refuel/contact", true);
|
//refuel_node = fgGetNode("systems/refuel/contact", true);
|
||||||
isTanker = false; // do this until this property is
|
isTanker = false; // do this until this property is
|
||||||
// passed over the net
|
// passed over the net
|
||||||
|
|
|
@ -119,6 +119,8 @@ const int MAX_BOOL_BUFFERS = 3;
|
||||||
const int V2018_1_BASE = 11990;
|
const int V2018_1_BASE = 11990;
|
||||||
const int EMESARYBRIDGETYPE_BASE = 12200;
|
const int EMESARYBRIDGETYPE_BASE = 12200;
|
||||||
const int EMESARYBRIDGE_BASE = 12000;
|
const int EMESARYBRIDGE_BASE = 12000;
|
||||||
|
const int V2018_3_BASE = 13000;
|
||||||
|
const int FALLBACK_MODEL_ID = 13000;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* definition of properties that are to be transmitted.
|
* definition of properties that are to be transmitted.
|
||||||
|
@ -661,6 +663,8 @@ static const IdPropertyList sIdPropertyList[] = {
|
||||||
{ EMESARYBRIDGETYPE_BASE + 27, "sim/multiplay/emesary/bridge-type[27]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
|
{ EMESARYBRIDGETYPE_BASE + 27, "sim/multiplay/emesary/bridge-type[27]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
|
||||||
{ EMESARYBRIDGETYPE_BASE + 28, "sim/multiplay/emesary/bridge-type[28]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
|
{ EMESARYBRIDGETYPE_BASE + 28, "sim/multiplay/emesary/bridge-type[28]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
|
||||||
{ EMESARYBRIDGETYPE_BASE + 29, "sim/multiplay/emesary/bridge-type[29]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
|
{ EMESARYBRIDGETYPE_BASE + 29, "sim/multiplay/emesary/bridge-type[29]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
|
||||||
|
|
||||||
|
{ FALLBACK_MODEL_ID, "sim/model/fallback-model-index", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
|
||||||
};
|
};
|
||||||
/*
|
/*
|
||||||
* For the 2017.x version 2 protocol the properties are sent in two partitions,
|
* For the 2017.x version 2 protocol the properties are sent in two partitions,
|
||||||
|
@ -1977,6 +1981,7 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
|
||||||
}
|
}
|
||||||
const T_PositionMsg* PosMsg = Msg.posMsg();
|
const T_PositionMsg* PosMsg = Msg.posMsg();
|
||||||
FGExternalMotionData motionInfo;
|
FGExternalMotionData motionInfo;
|
||||||
|
int fallback_model_index = 0;
|
||||||
motionInfo.time = XDR_decode_double(PosMsg->time);
|
motionInfo.time = XDR_decode_double(PosMsg->time);
|
||||||
motionInfo.lag = XDR_decode_double(PosMsg->lag);
|
motionInfo.lag = XDR_decode_double(PosMsg->lag);
|
||||||
for (unsigned i = 0; i < 3; ++i)
|
for (unsigned i = 0; i < 3; ++i)
|
||||||
|
@ -2222,6 +2227,13 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
|
||||||
}
|
}
|
||||||
if (pData)
|
if (pData)
|
||||||
motionInfo.properties.push_back(pData);
|
motionInfo.properties.push_back(pData);
|
||||||
|
|
||||||
|
// Special case - we need the /sim/model/fallback-model-index to create
|
||||||
|
// the MP model
|
||||||
|
if (pData->id == FALLBACK_MODEL_ID) {
|
||||||
|
fallback_model_index = pData->int_value;
|
||||||
|
SG_LOG(SG_NETWORK, SG_DEBUG, "Found Fallback model index in message " << fallback_model_index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2238,7 +2250,7 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
|
||||||
noprops:
|
noprops:
|
||||||
FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign);
|
FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign);
|
||||||
if (!mp)
|
if (!mp)
|
||||||
mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model);
|
mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model, fallback_model_index);
|
||||||
mp->addMotionInfo(motionInfo, stamp);
|
mp->addMotionInfo(motionInfo, stamp);
|
||||||
} // FGMultiplayMgr::ProcessPosMsg()
|
} // FGMultiplayMgr::ProcessPosMsg()
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
@ -2301,13 +2313,15 @@ FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len)
|
||||||
|
|
||||||
FGAIMultiplayer*
|
FGAIMultiplayer*
|
||||||
FGMultiplayMgr::addMultiplayer(const std::string& callsign,
|
FGMultiplayMgr::addMultiplayer(const std::string& callsign,
|
||||||
const std::string& modelName)
|
const std::string& modelName,
|
||||||
|
const int fallback_model_index)
|
||||||
{
|
{
|
||||||
if (0 < mMultiPlayerMap.count(callsign))
|
if (0 < mMultiPlayerMap.count(callsign))
|
||||||
return mMultiPlayerMap[callsign].get();
|
return mMultiPlayerMap[callsign].get();
|
||||||
|
|
||||||
FGAIMultiplayer* mp = new FGAIMultiplayer;
|
FGAIMultiplayer* mp = new FGAIMultiplayer;
|
||||||
mp->setPath(modelName.c_str());
|
mp->setPath(modelName.c_str());
|
||||||
|
mp->setFallbackModelIndex(fallback_model_index);
|
||||||
mp->setCallSign(callsign);
|
mp->setCallSign(callsign);
|
||||||
mMultiPlayerMap[callsign] = mp;
|
mMultiPlayerMap[callsign] = mp;
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,8 @@ private:
|
||||||
|
|
||||||
union MsgBuf;
|
union MsgBuf;
|
||||||
FGAIMultiplayer* addMultiplayer(const std::string& callsign,
|
FGAIMultiplayer* addMultiplayer(const std::string& callsign,
|
||||||
const std::string& modelName);
|
const std::string& modelName,
|
||||||
|
const int fallback_model_index);
|
||||||
FGAIMultiplayer* getMultiplayer(const std::string& callsign);
|
FGAIMultiplayer* getMultiplayer(const std::string& callsign);
|
||||||
void FillMsgHdr(T_MsgHdr *MsgHdr, int iMsgId, unsigned _len = 0u);
|
void FillMsgHdr(T_MsgHdr *MsgHdr, int iMsgId, unsigned _len = 0u);
|
||||||
void ProcessPosMsg(const MsgBuf& Msg, const simgear::IPAddress& SenderAddress,
|
void ProcessPosMsg(const MsgBuf& Msg, const simgear::IPAddress& SenderAddress,
|
||||||
|
@ -129,4 +130,3 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue