first commit

This commit is contained in:
Your Name
2022-10-20 20:29:11 +08:00
commit 4d531f8044
3238 changed files with 1387862 additions and 0 deletions

15
src/Model/CMakeLists.txt Normal file
View File

@@ -0,0 +1,15 @@
include(FlightGearComponent)
set(SOURCES
acmodel.cxx
modelmgr.cxx
panelnode.cxx
)
set(HEADERS
acmodel.hxx
modelmgr.hxx
panelnode.hxx
)
flightgear_component(Model "${SOURCES}" "${HEADERS}")

19
src/Model/README Normal file
View File

@@ -0,0 +1,19 @@
Last updated $Date$
This directory contains code for loading, positioning, orienting, and
animating 3D models.
acmodel.cxx
acmodel.hxx
This module defines the FGAircraftModel subsystem, which manages the 3D
model representing the aircraft the user is flying.
model.cxx
model.hxx
This module defines the FG3DModel class, which represents any 3D
model in the FlightGear world.
modelmgr.cxx
modelmgr.hxx
This module defines the FGModelMgr subsystem, which manages all 3D
models except for the aircraft.

7
src/Model/TODO Normal file
View File

@@ -0,0 +1,7 @@
- add animations for the model as a whole
- add submodels
- add billboards
- add UV animations

322
src/Model/acmodel.cxx Normal file
View File

@@ -0,0 +1,322 @@
// acmodel.cxx - manage a 3D aircraft model.
// Written by David Megginson, started 2002.
//
// This file is in the Public Domain, and comes with no warranty.
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <cstring> // for strcmp()
#include <simgear/compiler.h>
#include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/scene/model/animation.hxx>
#include <simgear/scene/model/placement.hxx>
#include <simgear/scene/util/SGNodeMasks.hxx>
#include <simgear/scene/model/modellib.hxx>
#include <Main/globals.hxx>
#include <Main/fg_props.hxx>
#include <Viewer/renderer.hxx>
#include <Viewer/viewmgr.hxx>
#include <Viewer/view.hxx>
#include <Scenery/scenery.hxx>
#include <Sound/fg_fx.hxx>
#include <GUI/Highlight.hxx>
#include "acmodel.hxx"
static osg::Node *
fgLoad3DModelPanel(const SGPath &path, SGPropertyNode *prop_root)
{
bool loadPanels = true;
bool autoTooltipsMaster = fgGetBool("/sim/rendering/automatic-animation-tooltips/enabled");
int autoTooltipsMasterMax = fgGetInt("/sim/rendering/automatic-animation-tooltips/max-count");
SG_LOG(SG_INPUT, SG_DEBUG, ""
<< " autoTooltipsMaster=" << autoTooltipsMaster
<< " autoTooltipsMasterMax=" << autoTooltipsMasterMax
);
osg::Node* node = simgear::SGModelLib::loadModel(path.utf8Str(), prop_root, NULL, loadPanels, autoTooltipsMaster, autoTooltipsMasterMax);
if (node)
node->setNodeMask(~SG_NODEMASK_TERRAIN_BIT);
return node;
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGAircraftModel
////////////////////////////////////////////////////////////////////////
FGAircraftModel::FGAircraftModel ()
: _velocity(SGVec3d::zeros()),
_fx(0),
_speed_n(0),
_speed_e(0),
_speed_d(0)
{
}
FGAircraftModel::~FGAircraftModel ()
{
// drop reference
_fx = 0;
shutdown();
}
// Gathers information about all animated nodes and the properties that they
// depend on, and registers with Highlight::add_property_node().
//
struct VisitorHighlight : osg::NodeVisitor
{
VisitorHighlight()
{
m_highlight = globals->get_subsystem<Highlight>();
setTraversalMode(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
}
std::string spaces()
{
return std::string(m_level*4, ' ');
}
virtual void apply(osg::Node& node)
{
SG_LOG(SG_GENERAL, SG_DEBUG, spaces() << "node: " << node.libraryName() << "::" << node.className());
m_level += 1;
traverse(node);
m_level -= 1;
}
virtual void apply(osg::Group& group)
{
// Parent nodes of <group> are animated by properties in
// <m_highlight_names>, so register the association between <group> and
// these properties.
SG_LOG(SG_GENERAL, SG_DEBUG, spaces() << "group: " << group.libraryName() << "::" << group.className());
for (auto name: m_highlight_names)
{
if (m_highlight) {
m_highlight->addPropertyNode(name, &group);
}
}
m_level += 1;
traverse(group);
m_level -= 1;
}
virtual void apply( osg::Transform& node )
{
SG_LOG(SG_GENERAL, SG_DEBUG, spaces() << "transform: "
<< node.libraryName() << "::" << node.className()
<< ": " << typeid(node).name()
);
SGSharedPtr<SGExpressiond const> expression = TransformExpression(&node);
int num_added = 0;
if (expression) {
// All <nodes>'s child nodes will be affected by <expression> so
// add all of <expression>'s properties to <m_highlight_names> so
// that we can call Highlight::add_property_node() for each child
// node.
std::set<const SGPropertyNode*> properties;
expression->collectDependentProperties(properties);
SG_LOG(SG_GENERAL, SG_DEBUG, spaces() << typeid(node).name() << ":");
for (auto p: properties) {
SG_LOG(SG_GENERAL, SG_DEBUG, spaces() << " " << p->getPath(true));
num_added += 1;
m_highlight_names.push_back(p->getPath(true /*simplify*/));
}
}
m_level += 1;
traverse(node);
m_level -= 1;
// Remove the names we appended to <m_highlight_names> earlier.
assert((int) m_highlight_names.size() >= num_added);
m_highlight_names.resize(m_highlight_names.size() - num_added);
}
unsigned int m_level = 0; // Only used to indent diagnostics.
std::vector<std::string> m_highlight_names;
Highlight* m_highlight = nullptr;
};
void
FGAircraftModel::init ()
{
if (_aircraft.get()) {
SG_LOG(SG_AIRCRAFT, SG_ALERT, "FGAircraftModel::init: already inited");
return;
}
simgear::ErrorReportContext ec("primary-aircraft", "yes");
_fx = new FGFX("fx");
_fx->init();
SGPropertyNode_ptr sim = fgGetNode("/sim", true);
for (auto model : sim->getChildren("model")) {
std::string path = model->getStringValue("path", "Models/Geometry/glider.ac");
std::string usage = model->getStringValue("usage", "external");
simgear::ErrorReportContext ec("aircraft-model", path);
SGPath resolvedPath = globals->resolve_aircraft_path(path);
if (resolvedPath.isNull()) {
simgear::reportFailure(simgear::LoadFailure::NotFound,
simgear::ErrorCode::XMLModelLoad,
"Failed to find aircraft model", SGPath::fromUtf8(path));
SG_LOG(SG_AIRCRAFT, SG_ALERT, "Failed to find aircraft model: " << path);
continue;
}
osg::Node* node = NULL;
try {
node = fgLoad3DModelPanel( resolvedPath, globals->get_props());
} catch (const sg_exception &ex) {
simgear::reportFailure(simgear::LoadFailure::BadData,
simgear::ErrorCode::XMLModelLoad,
"Failed to load aircraft model:" + ex.getFormattedMessage(),
ex.getLocation());
SG_LOG(SG_AIRCRAFT, SG_ALERT, "Failed to load aircraft from " << path << ':');
SG_LOG(SG_AIRCRAFT, SG_ALERT, " " << ex.getFormattedMessage());
}
if (usage == "interior") {
// interior model
if (!_interior.get()) {
_interior.reset(new SGModelPlacement);
_interior->init(node);
} else {
_interior->add(node);
}
} else {
// normal / exterior model
if (!_aircraft.get()) {
_aircraft.reset(new SGModelPlacement);
_aircraft->init(node);
} else {
_aircraft->add(node);
}
} // of model usage switch
} // of models iteration
// no models loaded, load the glider instead
if (!_aircraft.get()) {
SG_LOG(SG_AIRCRAFT, SG_ALERT, "(Falling back to glider.ac.)");
osg::Node* model = fgLoad3DModelPanel( SGPath::fromUtf8("Models/Geometry/glider.ac"),
globals->get_props());
_aircraft.reset(new SGModelPlacement);
_aircraft->init(model);
}
osg::Node* node = _aircraft->getSceneGraph();
globals->get_scenery()->get_aircraft_branch()->addChild(node);
if (_interior.get()) {
node = _interior->getSceneGraph();
globals->get_scenery()->get_interior_branch()->addChild(node);
}
// Register animated nodes and associated properties with Highlight.
VisitorHighlight visitor_highlight;
visitor_highlight.traverse(*node);
}
void
FGAircraftModel::reinit()
{
shutdown();
_fx->reinit();
init();
// TODO globally create signals for all subsystems (re)initialized
fgSetBool("/sim/signals/model-reinit", true);
}
void
FGAircraftModel::shutdown()
{
FGScenery* scenery = globals->get_scenery();
if (_aircraft.get()) {
if (scenery && scenery->get_aircraft_branch()) {
scenery->get_aircraft_branch()->removeChild(_aircraft->getSceneGraph());
}
}
if (_interior.get()) {
if (scenery && scenery->get_interior_branch()) {
scenery->get_interior_branch()->removeChild(_interior->getSceneGraph());
}
}
_aircraft.reset();
_interior.reset();
}
void
FGAircraftModel::bind ()
{
_speed_n = fgGetNode("velocities/speed-north-fps", true);
_speed_e = fgGetNode("velocities/speed-east-fps", true);
_speed_d = fgGetNode("velocities/speed-down-fps", true);
}
void
FGAircraftModel::unbind ()
{
_fx->unbind();
}
void
FGAircraftModel::update (double dt)
{
int view_number = globals->get_viewmgr()->getCurrentViewIndex();
int is_internal = fgGetBool("/sim/current-view/internal");
if (view_number == 0 && !is_internal) {
_aircraft->setVisible(false);
} else {
_aircraft->setVisible(true);
}
double heading, pitch, roll;
globals->get_aircraft_orientation(heading, pitch, roll);
SGQuatd orient = SGQuatd::fromYawPitchRollDeg(heading, pitch, roll);
SGGeod pos = globals->get_aircraft_position();
_aircraft->setPosition(pos);
_aircraft->setOrientation(orient);
_aircraft->update();
if (_interior.get()) {
_interior->setPosition(pos);
_interior->setOrientation(orient);
_interior->update();
}
// update model's audio sample values
_fx->set_position_geod( pos );
_fx->set_orientation( orient );
_velocity = SGVec3d( _speed_n->getDoubleValue(),
_speed_e->getDoubleValue(),
_speed_d->getDoubleValue() );
_fx->set_velocity( _velocity );
float temp_c = fgGetFloat("/environment/temperature-degc");
float humidity = fgGetFloat("/environment/relative-humidity");
float pressure = fgGetFloat("/environment/pressure-inhg")*SG_INHG_TO_PA/1000.0f;
_fx->set_atmosphere( temp_c, humidity, pressure );
}
// Register the subsystem.
SGSubsystemMgr::Registrant<FGAircraftModel> registrantFGAircraftModel(
SGSubsystemMgr::DISPLAY);
// end of model.cxx

56
src/Model/acmodel.hxx Normal file
View File

@@ -0,0 +1,56 @@
// acmodel.hxx - manage a 3D aircraft model.
// Written by David Megginson, started 2002.
//
// This file is in the Public Domain, and comes with no warranty.
#ifndef __ACMODEL_HXX
#define __ACMODEL_HXX 1
#include <osg/ref_ptr>
#include <osg/Group>
#include <osg/Switch>
#include <memory>
#include <simgear/structure/subsystem_mgr.hxx> // for SGSubsystem
#include <simgear/math/SGVec3.hxx>
// Don't pull in the headers, since we don't need them here.
class SGModelPlacement;
class FGFX;
class FGAircraftModel : public SGSubsystem
{
public:
FGAircraftModel ();
virtual ~FGAircraftModel ();
// Subsystem API.
void bind() override;
void init() override;
void reinit() override;
void shutdown() override;
void unbind() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "aircraft-model"; }
virtual SGModelPlacement * get3DModel() { return _aircraft.get(); }
virtual SGVec3d& getVelocity() { return _velocity; }
private:
void deinit ();
std::unique_ptr<SGModelPlacement> _aircraft;
std::unique_ptr<SGModelPlacement> _interior;
SGVec3d _velocity;
SGSharedPtr<FGFX> _fx;
SGPropertyNode_ptr _speed_n;
SGPropertyNode_ptr _speed_e;
SGPropertyNode_ptr _speed_d;
};
#endif // __ACMODEL_HXX

423
src/Model/modelmgr.cxx Normal file
View File

@@ -0,0 +1,423 @@
// modelmgr.cxx - manage a collection of 3D models.
// Written by David Megginson, started 2002.
//
// This file is in the Public Domain, and comes with no warranty.
#ifdef _MSC_VER
# pragma warning( disable: 4355 )
#endif
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <simgear/compiler.h>
#include <algorithm>
#include <functional>
#include <vector>
#include <cstring>
#include <simgear/scene/model/placement.hxx>
#include <simgear/scene/model/modellib.hxx>
#include <simgear/structure/exception.hxx>
#include <Main/fg_props.hxx>
#include <Scenery/scenery.hxx>
#include <Scenery/marker.hxx>
#include <osg/ProxyNode>
#include <osgText/String>
#include "modelmgr.hxx"
using namespace simgear;
namespace {
class CheckInstanceModelLoadedVisitor : public osg::NodeVisitor
{
public:
CheckInstanceModelLoadedVisitor() :
osg::NodeVisitor(osg::NodeVisitor::NODE_VISITOR, osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
{}
~CheckInstanceModelLoadedVisitor() = default;
void apply(osg::Node& node) override
{
if (!_loaded)
return;
traverse(node);
}
void apply(osg::ProxyNode& node) override
{
if (!_loaded)
return;
for (unsigned i = 0; i < node.getNumFileNames(); ++i) {
if (node.getFileName(i).empty())
continue;
// Check if this is already loaded.
if (i < node.getNumChildren() && node.getChild(i))
continue;
_loaded=false;
return;
}
traverse(node);
}
bool isLoaded() const
{
return _loaded;
}
private:
bool _loaded = true;
};
} // of anonymous namespace
FGModelMgr::FGModelMgr ()
{
}
FGModelMgr::~FGModelMgr ()
{
}
void
FGModelMgr::init ()
{
std::vector<SGPropertyNode_ptr> model_nodes = _models->getChildren("model");
for (unsigned int i = 0; i < model_nodes.size(); i++)
add_model(model_nodes[i]);
}
void FGModelMgr::shutdown()
{
osg::Group *scene_graph = NULL;
if (globals->get_scenery()) {
scene_graph = globals->get_scenery()->get_scene_graph();
}
// always delete instances, even if the scene-graph is gone
for (unsigned int i = 0; i < _instances.size(); i++) {
if (scene_graph) {
scene_graph->removeChild(_instances[i]->model->getSceneGraph());
}
delete _instances[i];
}
}
void
FGModelMgr::add_model (SGPropertyNode * node)
{
const std::string model_path{node->getStringValue("path", "Models/Geometry/glider.ac")};
if (model_path.empty()) {
SG_LOG(SG_AIRCRAFT, SG_WARN, "add_model called with empty path");
return;
}
const std::string internal_model{node->getStringValue("internal-model", "external")};
osg::Node *object;
Instance * instance = new Instance;
instance->loaded_node = node->addChild("loaded");
instance->loaded_node->setBoolValue(false);
if (internal_model == "marker") {
std::string label{node->getStringValue("marker/text", "MARKER")};
float r = node->getFloatValue("marker/color[0]", 1.0f);
float g = node->getFloatValue("marker/color[1]", 1.0f);
float b = node->getFloatValue("marker/color[2]", 1.0f);
osg::Vec4f color(r, g, b, 1.0f);
float font_size = node->getFloatValue("marker/size", 1.0f);
float pin_height = node->getFloatValue("marker/height", 1000.0f);
float tip_height = node->getFloatValue("marker/tip-height", 0.0f);
object = fgCreateMarkerNode(osgText::String(label, osgText::String::ENCODING_UTF8), font_size, pin_height, tip_height, color);
}
else if (internal_model == "external") {
try {
std::string fullPath = simgear::SGModelLib::findDataFile(model_path);
if (fullPath.empty()) {
SG_LOG(SG_AIRCRAFT, SG_ALERT, "add_model: unable to find model with name '" << model_path << "'");
return;
}
object = SGModelLib::loadDeferredModel(fullPath, globals->get_props());
} catch (const sg_throwable& t) {
SG_LOG(SG_AIRCRAFT, SG_ALERT, "Error loading " << model_path << ":\n "
<< t.getFormattedMessage() << t.getOrigin());
return;
}
}
else {
object = new osg::Node;
SG_LOG(SG_AIRCRAFT, SG_WARN, "Unsupported internal-model type " << internal_model);
}
const std::string modelName{node->getStringValue("name", model_path.c_str())};
SG_LOG(SG_AIRCRAFT, SG_INFO, "Adding model " << modelName);
SGModelPlacement *model = new SGModelPlacement;
instance->model = model;
instance->node = node;
model->init( object );
double lon = node->getDoubleValue("longitude-deg"),
lat = node->getDoubleValue("latitude-deg"),
elevFt = node->getDoubleValue("elevation-ft");
model->setPosition(SGGeod::fromDegFt(lon, lat, elevFt));
// Set position and orientation either
// indirectly through property refs
// or directly with static values.
SGPropertyNode * child = node->getChild("longitude-deg-prop");
if (child != 0)
instance->lon_deg_node = fgGetNode(child->getStringValue(), true);
child = node->getChild("latitude-deg-prop");
if (child != 0)
instance->lat_deg_node = fgGetNode(child->getStringValue(), true);
child = node->getChild("elevation-ft-prop");
if (child != 0)
instance->elev_ft_node = fgGetNode(child->getStringValue(), true);
child = node->getChild("roll-deg-prop");
if (child != 0)
instance->roll_deg_node = fgGetNode(child->getStringValue(), true);
else
model->setRollDeg(node->getDoubleValue("roll-deg"));
child = node->getChild("pitch-deg-prop");
if (child != 0)
instance->pitch_deg_node = fgGetNode(child->getStringValue(), true);
else
model->setPitchDeg(node->getDoubleValue("pitch-deg"));
child = node->getChild("heading-deg-prop");
if (child != 0)
instance->heading_deg_node = fgGetNode(child->getStringValue(), true);
else
model->setHeadingDeg(node->getDoubleValue("heading-deg"));
if (node->hasChild("enable-hot")) {
osg::Node::NodeMask mask = model->getSceneGraph()->getNodeMask();
if (node->getBoolValue("enable-hot")) {
mask |= SG_NODEMASK_TERRAIN_BIT;
} else {
mask &= ~SG_NODEMASK_TERRAIN_BIT;
}
model->getSceneGraph()->setNodeMask(mask);
}
// Add this model to the global scene graph
globals->get_scenery()->get_scene_graph()->addChild(model->getSceneGraph());
// Save this instance for updating
add_instance(instance);
}
void
FGModelMgr::bind ()
{
_models = fgGetNode("/models", true);
_listener.reset(new Listener(this));
_models->addChangeListener(_listener.get());
}
void
FGModelMgr::unbind ()
{
// work-around for FLIGHTGEAR-37D : crash when quitting during
// early startup
if (_listener) {
_models->removeChangeListener(_listener.get());
}
_listener.reset();
_models.clear();
}
namespace
{
double testNan(double val)
{
if (SGMisc<double>::isNaN(val))
throw sg_range_exception("value is nan");
return val;
}
} // namespace
void FGModelMgr::update(double dt)
{
std::for_each(_instances.begin(), _instances.end(), [](FGModelMgr::Instance* instance) {
SGModelPlacement* model = instance->model;
double roll, pitch, heading;
roll = pitch = heading = 0.0;
SGGeod pos = model->getPosition();
try {
// Optionally set position from properties
if (instance->lon_deg_node != 0)
pos.setLongitudeDeg(testNan(instance->lon_deg_node->getDoubleValue()));
if (instance->lat_deg_node != 0)
pos.setLatitudeDeg(testNan(instance->lat_deg_node->getDoubleValue()));
if (instance->elev_ft_node != 0)
pos.setElevationFt(testNan(instance->elev_ft_node->getDoubleValue()));
// Optionally set orientation from properties
if (instance->roll_deg_node != 0)
roll = testNan(instance->roll_deg_node->getDoubleValue());
if (instance->pitch_deg_node != 0)
pitch = testNan(instance->pitch_deg_node->getDoubleValue());
if (instance->heading_deg_node != 0)
heading = testNan(instance->heading_deg_node->getDoubleValue());
} catch (const sg_range_exception&) {
string path = instance->node->getStringValue("path", "unknown");
SG_LOG(SG_AIRCRAFT, SG_INFO, "Instance of model " << path
<< " has invalid values");
return;
}
model->setPosition(pos);
// Optionally set orientation from properties
if (instance->roll_deg_node != 0)
model->setRollDeg(roll);
if (instance->pitch_deg_node != 0)
model->setPitchDeg(pitch);
if (instance->heading_deg_node != 0)
model->setHeadingDeg(heading);
instance->model->update();
instance->checkLoaded();
});
}
void
FGModelMgr::add_instance (Instance * instance)
{
_instances.push_back(instance);
}
void
FGModelMgr::remove_instance (Instance * instance)
{
std::vector<Instance *>::iterator it;
for (it = _instances.begin(); it != _instances.end(); it++) {
if (*it == instance) {
_instances.erase(it);
delete instance;
return;
}
}
}
FGModelMgr::Instance*
FGModelMgr::findInstanceByNodePath(const std::string& node_path) const
{
if (node_path.empty())
return nullptr;
SGPropertyNode* node = fgGetNode(node_path, false);
if (!node)
return nullptr;
auto it = std::find_if(_instances.begin(), _instances.end(),
[node](const Instance* instance)
{ return instance->node == node; });
if (it == _instances.end()) {
return nullptr;
}
return *it;
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGModelMgr::Instance
////////////////////////////////////////////////////////////////////////
FGModelMgr::Instance::~Instance ()
{
delete model;
}
bool FGModelMgr::Instance::checkLoaded() const
{
if (!model)
return false;
if (loaded_node->getBoolValue()) {
return true;
}
CheckInstanceModelLoadedVisitor cilv;
model->getSceneGraph()->accept(cilv);
const bool loadedNow = cilv.isLoaded();
if (loadedNow) {
loaded_node->setBoolValue(true);
}
return loadedNow;
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGModelMgr::Listener
////////////////////////////////////////////////////////////////////////
void
FGModelMgr::Listener::childAdded(SGPropertyNode * parent, SGPropertyNode * child)
{
if (parent->getNameString() != "model" || child->getNameString() != "load")
return;
_mgr->add_model(parent);
}
void
FGModelMgr::Listener::childRemoved(SGPropertyNode * parent, SGPropertyNode * child)
{
if (parent->getNameString() != "models" || child->getNameString() != "model")
return;
// search instance by node and remove it from scenegraph
std::vector<Instance *>::iterator it = _mgr->_instances.begin();
std::vector<Instance *>::iterator end = _mgr->_instances.end();
for (; it != end; ++it) {
Instance *instance = *it;
if (instance->node != child)
continue;
_mgr->_instances.erase(it);
osg::Node *branch = instance->model->getSceneGraph();
// OSGFIXME
// if (shadows && instance->shadow)
// shadows->deleteOccluder(branch);
if (globals->get_scenery() && globals->get_scenery()->get_scene_graph()) {
globals->get_scenery()->get_scene_graph()->removeChild(branch);
}
delete instance;
break;
}
}
// Register the subsystem.
SGSubsystemMgr::Registrant<FGModelMgr> registrantFGModelMgr(
SGSubsystemMgr::DISPLAY);
// end of modelmgr.cxx

124
src/Model/modelmgr.hxx Normal file
View File

@@ -0,0 +1,124 @@
// model-mgr.hxx - manage user-specified 3D models.
// Written by David Megginson, started 2002.
//
// This file is in the Public Domain, and comes with no warranty.
#ifndef __MODELMGR_HXX
#define __MODELMGR_HXX 1
#include <vector>
#include <memory>
#include <simgear/compiler.h> // for SG_USING_STD
#include <simgear/structure/subsystem_mgr.hxx>
#include <Scripting/NasalModelData.hxx>
// Don't pull in headers, since we don't need them here.
class SGPropertyNode;
class SGModelPlacement;
/**
* Manage a list of user-specified models.
*/
class FGModelMgr : public SGSubsystem
{
public:
/**
* A dynamically-placed model using properties.
*
* The model manager uses the property nodes to update the model's
* position and orientation; any of the property node pointers may
* be set to zero to avoid update. Normally, a caller should
* load the model by instantiating SGModelPlacement with the path
* to the model or its XML wrapper, then assign any relevant
* property node pointers.
*
* @see SGModelPlacement
* @see FGModelMgr#add_instance
*/
struct Instance
{
virtual ~Instance ();
SGModelPlacement * model = nullptr;
SGPropertyNode_ptr node;
SGPropertyNode_ptr lon_deg_node;
SGPropertyNode_ptr lat_deg_node;
SGPropertyNode_ptr elev_ft_node;
SGPropertyNode_ptr roll_deg_node;
SGPropertyNode_ptr pitch_deg_node;
SGPropertyNode_ptr heading_deg_node;
SGPropertyNode_ptr loaded_node;
bool shadow = false;
bool checkLoaded() const;
};
FGModelMgr ();
virtual ~FGModelMgr ();
// Subsystem API.
void bind() override;
void init() override;
void shutdown() override;
void unbind() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "model-manager"; }
virtual void add_model (SGPropertyNode * node);
/**
* Add an instance of a dynamic model to the manager.
*
* NOTE: pointer ownership is transferred to the model manager!
*
* The caller is responsible for setting up the Instance structure
* as required. The model manager will continuously update the
* location and orientation of the model based on the current
* values of the properties.
*/
virtual void add_instance (Instance * instance);
/**
* Remove an instance of a dynamic model from the manager.
*
* NOTE: the manager will delete the instance as well.
*/
virtual void remove_instance (Instance * instance);
/**
* Finds an instance in the model manager from a given node path in the property tree.
* A possible path could be "models/model[0]"
*
* NOTE: the manager will delete the instance as well.
*/
Instance* findInstanceByNodePath(const std::string& nodePath) const;
private:
/**
* Listener class that adds models at runtime.
*/
class Listener : public SGPropertyChangeListener
{
public:
Listener(FGModelMgr *mgr) : _mgr(mgr) {}
virtual void childAdded (SGPropertyNode * parent, SGPropertyNode * child);
virtual void childRemoved (SGPropertyNode * parent, SGPropertyNode * child);
private:
FGModelMgr * _mgr;
};
SGPropertyNode_ptr _models;
std::unique_ptr<Listener> _listener;
std::vector<Instance *> _instances;
};
#endif // __MODELMGR_HXX

432
src/Model/panelnode.cxx Normal file
View File

@@ -0,0 +1,432 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "panelnode.hxx"
#include <vector>
#include <algorithm>
#include <osg/Geode>
#include <osg/Switch>
#include <osg/BlendFunc>
#include <simgear/compiler.h>
#include <simgear/structure/exception.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <simgear/scene/util/SGPickCallback.hxx>
#include <simgear/scene/util/SGSceneUserData.hxx>
#include <simgear/scene/util/SGNodeMasks.hxx>
#include <GUI/FlightGear_pu.h>
#include <Main/fg_os.hxx>
#include <Cockpit/panel.hxx>
#include <Cockpit/panel_io.hxx>
#include "Viewer/view.hxx"
#include "Viewer/viewmgr.hxx"
using std::vector;
class PanelTransformListener : public SGPropertyChangeListener
{
public:
PanelTransformListener(FGPanelNode* pn) : _panelNode(pn) {}
virtual void valueChanged (SGPropertyNode * node)
{
_panelNode->dirtyBound();
}
private:
FGPanelNode* _panelNode;
};
class PanelPathListener : public SGPropertyChangeListener
{
public:
PanelPathListener(FGPanelNode* pn) : _panelNode(pn) {}
virtual void valueChanged (SGPropertyNode * node)
{
_panelNode->setPanelPath(node->getStringValue());
}
private:
FGPanelNode* _panelNode;
};
class FGPanelPickCallback : public SGPickCallback {
public:
FGPanelPickCallback(FGPanelNode* p) :
panel(p)
{}
virtual bool buttonPressed( int b,
const osgGA::GUIEventAdapter&,
const Info& info )
{
if (!panel->getPanel()) {
return false;
}
button = b;
// convert to panel coordinates
osg::Matrixd m = osg::Matrixd::inverse(panel->transformMatrix());
picked = toOsg(info.local) * m;
SG_LOG( SG_INSTR, SG_DEBUG, "panel pick: " << toSG(picked) );
// send to the panel
return panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_DOWN,
picked.x(), picked.y());
}
virtual void update(double dt, int keyModState)
{
panel->getPanel()->updateMouseDelay(dt);
}
virtual void buttonReleased( int,
const osgGA::GUIEventAdapter&,
const Info* )
{
panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_UP,
picked.x(), picked.y());
}
private:
FGPanelNode* panel;
int button;
osg::Vec3 picked;
};
class FGPanelSwitchCallback : public osg::NodeCallback {
public:
FGPanelSwitchCallback(FGPanelNode* pn) :
panel(pn),
visProp(fgGetNode("/sim/panel/visibility"))
{
}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
assert(dynamic_cast<osg::Switch*>(node));
osg::Switch* sw = static_cast<osg::Switch*>(node);
if (!visProp->getBoolValue()) {
sw->setValue(0, false);
return;
}
panel->lazyLoad(); // isVisible check needs the panel loaded for auto-hide flag
bool enabled = panel->isVisible2d();
sw->setValue(0, enabled);
if (!enabled)
return;
traverse(node, nv);
}
private:
FGPanelNode* panel;
SGPropertyNode_ptr visProp;
};
FGPanelNode::FGPanelNode(SGPropertyNode* props) :
_is2d(false),
_resizeToViewport(false),
_listener(NULL)
{
commonInit();
_panelPath = props->getStringValue("path");
// And the corner points
SGPropertyNode* pt = props->getChild("bottom-left");
_bottomLeft[0] = pt->getFloatValue("x-m");
_bottomLeft[1] = pt->getFloatValue("y-m");
_bottomLeft[2] = pt->getFloatValue("z-m");
pt = props->getChild("top-left");
_topLeft[0] = pt->getFloatValue("x-m");
_topLeft[1] = pt->getFloatValue("y-m");
_topLeft[2] = pt->getFloatValue("z-m");
pt = props->getChild("bottom-right");
_bottomRight[0] = pt->getFloatValue("x-m");
_bottomRight[1] = pt->getFloatValue("y-m");
_bottomRight[2] = pt->getFloatValue("z-m");
_depthTest = props->getBoolValue("depth-test");
}
FGPanelNode::FGPanelNode() :
_is2d(true),
_resizeToViewport(true),
_depthTest(false)
{
globals->get_commands()->addCommand("panel-mouse-click", this, &FGPanelNode::panelMouseClickCommand);
SGPropertyNode* pathNode = fgGetNode("/sim/panel/path");
_pathListener.reset(new PanelPathListener(this));
pathNode->addChangeListener(_pathListener.get());
setPanelPath(pathNode->getStringValue());
// for a 2D panel, various options adjust the transformation
// matrix. We need to pass this data on to OSG or its bounding box
// will be stale, and picking will break.
// http://code.google.com/p/flightgear-bugs/issues/detail?id=864
_listener = new PanelTransformListener(this);
fgGetNode("/sim/panel/x-offset", true)->addChangeListener(_listener);
fgGetNode("/sim/panel/y-offset", true)->addChangeListener(_listener);
fgGetNode("/sim/startup/xsize", true)->addChangeListener(_listener);
fgGetNode("/sim/startup/ysize", true)->addChangeListener(_listener);
commonInit();
}
FGPanelNode::~FGPanelNode()
{
if (_is2d) {
globals->get_commands()->removeCommand("panel-mouse-click");
SGPropertyNode* pathNode = fgGetNode("/sim/panel/path");
pathNode->removeChangeListener(_pathListener.get());
}
if (_listener) {
fgGetNode("/sim/panel/x-offset", true)->removeChangeListener(_listener);
fgGetNode("/sim/panel/y-offset", true)->removeChangeListener(_listener);
fgGetNode("/sim/startup/xsize", true)->removeChangeListener(_listener);
fgGetNode("/sim/startup/ysize", true)->removeChangeListener(_listener);
delete _listener;
}
}
void FGPanelNode::setPanelPath(const std::string& panel)
{
if (panel == _panelPath) {
return;
}
_panelPath = panel;
if (_panel) {
_panel.clear();
}
}
void FGPanelNode::lazyLoad()
{
if (!_panelPath.empty() && !_panel) {
_panel = fgReadPanel(_panelPath);
if (!_panel) {
SG_LOG(SG_COCKPIT, SG_WARN, "failed to read panel from:" << _panelPath);
_panelPath = std::string(); // don't keep trying to read
return;
}
_panel->setDepthTest(_depthTest);
initWithPanel();
}
}
void FGPanelNode::commonInit()
{
setUseDisplayList(false);
setDataVariance(Object::DYNAMIC);
getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
}
void FGPanelNode::initWithPanel()
{
int i;
// Read out the pixel-space info
float panelWidth = _panel->getWidth();
float panelHeight = _panel->getHeight();
_panel->getLogicalExtent(_xmin, _ymin, _xmax, _ymax);
// Now generate our transformation matrix. For shorthand, use
// "a", "b", and "c" as our corners and "m" as the matrix. The
// vector u goes from a to b, v from a to c, and w is a
// perpendicular cross product.
osg::Vec3 a = _bottomLeft;
osg::Vec3 b = _bottomRight;
osg::Vec3 c = _topLeft;
osg::Vec3 u = b - a;
osg::Vec3 v = c - a;
osg::Vec3 w = u^v;
osg::Matrix& m = _xform;
if ((u.length2() == 0.0) || (b.length2() == 0.0)) {
m.makeIdentity();
} else {
// Now generate a trivial basis transformation matrix. If we want
// to map the three unit vectors to three arbitrary vectors U, V,
// and W, then those just become the columns of the 3x3 matrix.
m(0,0) = u[0]; m(1,0) = v[0]; m(2,0) = w[0]; m(3,0) = a[0];// |Ux Vx Wx|
m(0,1) = u[1]; m(1,1) = v[1]; m(2,1) = w[1]; m(3,1) = a[1];//m = |Uy Vy Wy|
m(0,2) = u[2]; m(1,2) = v[2]; m(2,2) = w[2]; m(3,2) = a[2];// |Uz Vz Wz|
m(0,3) = 0; m(1,3) = 0; m(2,3) = 0; m(3,3) = 1;
}
// The above matrix maps the unit (!) square to the panel
// rectangle. Postmultiply scaling factors that match the
// pixel-space size of the panel.
for(i=0; i<4; ++i) {
m(0,i) *= 1.0/panelWidth;
m(1,i) *= 1.0/panelHeight;
}
dirtyBound();
}
osg::Matrix FGPanelNode::transformMatrix() const
{
if (!_panel) {
return osg::Matrix();
}
if (!_resizeToViewport) {
return _xform;
}
double s = _panel->getAspectScale();
osg::Matrix m = osg::Matrix::scale(s, s, 1.0);
m *= osg::Matrix::translate(_panel->getXOffset(), _panel->getYOffset(), 0.0);
return m;
}
void
FGPanelNode::drawImplementation(osg::State& state) const
{
if (!_panel) {
return;
}
osg::ref_ptr<osg::RefMatrix> mv = new osg::RefMatrix;
mv->set(transformMatrix() * state.getModelViewMatrix());
state.applyModelViewMatrix(mv.get());
_panel->draw(state);
}
osg::BoundingBox FGPanelNode::computeBoundingBox() const
{
osg::Vec3 coords[3];
osg::Matrix m(transformMatrix());
coords[0] = m.preMult(osg::Vec3(_xmin,_ymin,0));
coords[1] = m.preMult(osg::Vec3(_xmax,_ymin,0));
coords[2] = m.preMult(osg::Vec3(_xmin,_ymax,0));
osg::BoundingBox bb;
bb.expandBy(coords[0]);
bb.expandBy(coords[1]);
bb.expandBy(coords[2]);
return bb;
}
void FGPanelNode::accept(osg::PrimitiveFunctor& functor) const
{
osg::Vec3 coords[4];
osg::Matrix m(transformMatrix());
coords[0] = m.preMult(osg::Vec3(_xmin,_ymin,0));
coords[1] = m.preMult(osg::Vec3(_xmax,_ymin,0));
coords[2] = m.preMult(osg::Vec3(_xmax, _ymax, 0));
coords[3] = m.preMult(osg::Vec3(_xmin,_ymax,0));
functor.setVertexArray(4, coords);
functor.drawArrays( GL_QUADS, 0, 4);
}
bool FGPanelNode::isVisible2d() const
{
if (!_panel) {
return false;
}
if (!_hideNonDefaultViews) {
_hideNonDefaultViews = fgGetNode("/sim/panel/hide-nonzero-view", true);
}
if (_hideNonDefaultViews->getBoolValue()) {
if (globals->get_viewmgr()->getCurrentViewIndex() != 0) {
return false;
}
}
if (!_autoHide2d) {
_autoHide2d = fgGetNode("/sim/panel/hide-nonzero-heading-offset", true);
}
if (_panel->getAutohide() && _autoHide2d->getBoolValue()) {
if (!globals->get_current_view()) {
return false;
}
return globals->get_current_view()->getHeadingOffset_deg() == 0;
}
return true;
}
static osg::Node* createGeode(FGPanelNode* panel)
{
osg::Geode* geode = new osg::Geode;
geode->addDrawable(panel);
geode->setNodeMask(SG_NODEMASK_PICK_BIT | SG_NODEMASK_2DPANEL_BIT);
SGSceneUserData* userData;
userData = SGSceneUserData::getOrCreateSceneUserData(geode);
userData->setPickCallback(new FGPanelPickCallback(panel));
return geode;
}
osg::Node* FGPanelNode::create2DPanelNode()
{
FGPanelNode* drawable = new FGPanelNode;
osg::Switch* ps = new osg::Switch;
osg::StateSet* stateSet = ps->getOrCreateStateSet();
stateSet->setRenderBinDetails(1000, "RenderBin");
ps->addChild(createGeode(drawable));
// speed optimization?
stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
stateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA));
stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
ps->setUpdateCallback(new FGPanelSwitchCallback(drawable));
return ps;
}
osg::Node* FGPanelNode::load(SGPropertyNode *n)
{
FGPanelNode* drawable = new FGPanelNode(n);
drawable->lazyLoad(); // force load now for 2.5D panels
return createGeode(drawable);
}
/**
* Built-in command: pass a mouse click to the panel.
*
* button: the mouse button number, zero-based.
* is-down: true if the button is down, false if it is up.
* x-pos: the x position of the mouse click.
* y-pos: the y position of the mouse click.
*/
bool
FGPanelNode::panelMouseClickCommand(const SGPropertyNode * arg, SGPropertyNode * root)
{
return _panel->doMouseAction(arg->getIntValue("button"),
arg->getBoolValue("is-down") ? PU_DOWN : PU_UP,
arg->getIntValue("x-pos"),
arg->getIntValue("y-pos"));
}

90
src/Model/panelnode.hxx Normal file
View File

@@ -0,0 +1,90 @@
#ifndef FG_PANELNODE_HXX
#define FG_PANELNODE_HXX
#include <osg/Vec3>
#include <osg/Matrix>
#include <osg/Drawable>
#include <osg/Version>
#include <memory>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/props/props.hxx>
class FGPanel;
class SGPropertyNode;
// PanelNode defines an OSG drawable wrapping the 2D panel drawing code
class FGPanelNode : public osg::Drawable
{
public:
FGPanelNode(SGPropertyNode* props);
virtual ~FGPanelNode();
// OSGFIXME
virtual osg::Object* cloneType() const { return 0; }
virtual osg::Object* clone(const osg::CopyOp& copyop) const { return 0; }
FGPanel* getPanel() { return _panel; }
virtual void drawImplementation(osg::RenderInfo& renderInfo) const
{ drawImplementation(*renderInfo.getState()); }
void drawImplementation(osg::State& state) const;
osg::BoundingBox computeBoundingBox() const override;
/** Return true, FGPanelNode does support accept(PrimitiveFunctor&). */
virtual bool supports(const osg::PrimitiveFunctor&) const { return true; }
virtual void accept(osg::PrimitiveFunctor& functor) const;
static osg::Node* load(SGPropertyNode *n);
static osg::Node* create2DPanelNode();
osg::Matrix transformMatrix() const;
void setPanelPath(const std::string& panel);
void lazyLoad();
/**
* is visible in 2D mode or not?
*/
bool isVisible2d() const;
private:
FGPanelNode(); // for 2D panels
void commonInit();
void initWithPanel();
bool panelMouseClickCommand(const SGPropertyNode * arg, SGPropertyNode * root);
const bool _is2d;
SGSharedPtr<FGPanel> _panel;
std::string _panelPath;
bool _resizeToViewport;
bool _depthTest;
// Panel corner coordinates
osg::Vec3 _bottomLeft, _topLeft, _bottomRight;
int _xmin, _ymin, _xmax, _ymax;
// The matrix that results, which transforms 2D x/y panel
// coordinates into 3D coordinates of the panel quadrilateral.
osg::Matrix _xform;
SGPropertyChangeListener* _listener;
std::unique_ptr<SGPropertyChangeListener> _pathListener;
/// should the 2D panel auto-hide when the view orientation changes
mutable SGPropertyNode_ptr _autoHide2d;
/// should the 2D panel be hidden in views other than the default (view 0)
mutable SGPropertyNode_ptr _hideNonDefaultViews;
};
#endif // FG_PANELNODE_HXX