2,736
edits
Red Leader (talk | contribs) (Indentation, template) |
|||
| Line 2: | Line 2: | ||
{{Stub}} | {{Stub}} | ||
This howto will demonstrate how the FlightGear [[Canvas]] system can be '''extended to load 3D models''' from disk and render them to a texture on demand, as well as how to position the object arbitrarily using properties. | |||
This howto will demonstrate how the FlightGear [[Canvas]] system can be extended to load 3D models from disk and render them to a texture on demand, as well as how to position the object arbitrarily using properties. | |||
{{Canvas Navigation}} | {{Canvas Navigation}} | ||
== Background == | == Background == | ||
There are several scenarios where we cannot currently use Canvas because we're lacking a means for rendering a 3D model to a texture - omega95 once mentioned that some modern avionics are increasingly using 3D representations [http://forum.flightgear.org/viewtopic.php?p=164243#p164243][http://forum.flightgear.org/viewtopic.php?f=71&t=17278&p=164260 | There are several scenarios where we cannot currently use Canvas because we're lacking a means for rendering a 3D model to a texture - omega95 once mentioned that some modern avionics are increasingly using 3D representations<ref>[http://forum.flightgear.org/viewtopic.php?p=164243#p164243]</ref><ref>[http://forum.flightgear.org/viewtopic.php?f=71&t=17278&p=164260]</ref>. Equally, a typical FlightGear GUI front-end will usually require a way for previewing aircraft 3D models. | ||
Currently, we cannot use Canvas for any of these use-cases because there's no way to load 3D models. | Currently, we cannot use Canvas for any of these use-cases because there's no way to load 3D models. | ||
| Line 24: | Line 22: | ||
== Approach == | == Approach == | ||
* We'll be extending FGCanvasSystemAdapter to add a new method for loading a 3D model form $FG_ROOT using fgValidatePath() properly | * We'll be extending FGCanvasSystemAdapter to add a new method for loading a 3D model form $FG_ROOT using fgValidatePath() properly | ||
* Next, we'll be creating a new Canvas Element using the tutorial at [[Canvas Development# | * Next, we'll be creating a new Canvas Element using the tutorial at [[Canvas Development#Adding a new Element]] | ||
* The new element will serve as a container for an [http://trac.openscenegraph.org/documentation/OpenSceneGraphReferenceDocs/a00618.html Osg::PositionAttitudeTransform] (PAT) for positioning the 3D model | * The new element will serve as a container for an [http://trac.openscenegraph.org/documentation/OpenSceneGraphReferenceDocs/a00618.html Osg::PositionAttitudeTransform] (PAT) for positioning the 3D model | ||
* as a child node, we'll add an [http://trac.openscenegraph.org/documentation/OpenSceneGraphReferenceDocs/a00642.html Osg::ProxyNode] | * as a child node, we'll add an [http://trac.openscenegraph.org/documentation/OpenSceneGraphReferenceDocs/a00642.html Osg::ProxyNode] | ||
| Line 33: | Line 30: | ||
The osg::ProxyNode node will reduce the start time of the viewer if there are huge numbers of models to be loaded and displayed in the scene graph. It is able to function as the interface of external files, help applications to start up as soon as possible, and then read those waiting models by using an independent data thread. It uses setFileName() rather than addChild() to set a model file and dynamically load it as a child.[https://www.packtpub.com/books/content/openscenegraph-managing-scene-graph] | The osg::ProxyNode node will reduce the start time of the viewer if there are huge numbers of models to be loaded and displayed in the scene graph. It is able to function as the interface of external files, help applications to start up as soon as possible, and then read those waiting models by using an independent data thread. It uses setFileName() rather than addChild() to set a model file and dynamically load it as a child.[https://www.packtpub.com/books/content/openscenegraph-managing-scene-graph] | ||
{{WIP}} | {{WIP}} | ||
By looking at existing code, we can learn more about the SimGear/FlightGear APIs for loading 3D models from the base package. A commonly-used idiom can | By looking at existing code, we can learn more about the SimGear/FlightGear APIs for loading 3D models from the base package. A commonly-used idiom can be seen in the model-manager ({{flightgear file|src/Model/modelmgr.cxx|l=72}}) (slightly modified for clarity): | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
try { | try { | ||
const char *model_path = "Models/Geometry/glider.ac"; | |||
std::string fullPath = simgear::SGModelLib::findDataFile(model_path); | |||
osg::Node * object = SGModelLib::loadDeferredModel(fullPath, globals->get_props()); | |||
} catch (const sg_throwable& t) { | |||
SG_LOG(SG_AIRCRAFT, SG_ALERT, "Error loading " << model_path << ":\n " | SG_LOG(SG_AIRCRAFT, SG_ALERT, "Error loading " << model_path << ":\n " | ||
<< t.getFormattedMessage() << t.getOrigin()); | << t.getFormattedMessage() << t.getOrigin()); | ||
return; | return; | ||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Next, we'll need to ensure that read/write permissions (as per [[IORules]]) are properly handled by our new code, which is commonly done using <code>fgValidatePath()</code>: | Next, we'll need to ensure that read/write permissions (as per [[IORules]]) are properly handled by our new code, which is commonly done using <code>fgValidatePath()</code>: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
if (!fgValidatePath(file.c_str(), false)) { | if (!fgValidatePath(file.c_str(), false)) { | ||
SG_LOG(SG_IO, SG_ALERT, "load: reading '" << file << "' denied " | |||
"(unauthorized access)"); | |||
return false; | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Once those two snippets are integrated, we end up with something along these lines: | Once those two snippets are integrated, we end up with something along these lines: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
const char *model_path = "Models/Geometry/glider.ac"; | |||
if (!fgValidatePath(mode_path, false)) { | if (!fgValidatePath(mode_path, false)) { | ||
SG_LOG(SG_IO, SG_ALERT, "load: reading '" << file << "' denied " | |||
"(unauthorized access)"); | |||
return false; | |||
} | } | ||
try { | try { | ||
std::string fullPath = simgear::SGModelLib::findDataFile(model_path); | |||
osg::Node * object = SGModelLib::loadDeferredModel(fullPath, globals->get_props()); | |||
} catch (const sg_throwable& t) { | |||
SG_LOG(SG_AIRCRAFT, SG_ALERT, "Error loading " << model_path << ":\n " | SG_LOG(SG_AIRCRAFT, SG_ALERT, "Error loading " << model_path << ":\n " | ||
<< t.getFormattedMessage() << t.getOrigin()); | << t.getFormattedMessage() << t.getOrigin()); | ||
return; | return; | ||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
These are already the main building blocks required for loading a 3D model from disk ([[$FG_ROOT]]) and getting an <code>osg::Node*</code> in return. | These are already the main building blocks required for loading a 3D model from disk ([[$FG_ROOT]]) and getting an <code>osg::Node*</code> in return. | ||
Next, we need to add this as a method to FGCanvasSystemAdapter to expose this FlightGear-specific API to the Canvas subsystem living in SimGear: | Next, we need to add this as a method to FGCanvasSystemAdapter to expose this FlightGear-specific API to the Canvas subsystem living in SimGear: | ||
<syntaxhighlight lang="diff"> | <syntaxhighlight lang="diff"> | ||
diff --git a/simgear/canvas/canvas_fwd.hxx b/simgear/canvas/canvas_fwd.hxx | diff --git a/simgear/canvas/canvas_fwd.hxx b/simgear/canvas/canvas_fwd.hxx | ||
| Line 100: | Line 93: | ||
#include <boost/shared_ptr.hpp> | #include <boost/shared_ptr.hpp> | ||
#include <boost/weak_ptr.hpp> | #include <boost/weak_ptr.hpp> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 116: | Line 108: | ||
virtual HTTP::Client* getHTTPClient() const = 0; | virtual HTTP::Client* getHTTPClient() const = 0; | ||
}; | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 141: | Line 132: | ||
virtual simgear::HTTP::Client* getHTTPClient() const; | virtual simgear::HTTP::Client* getHTTPClient() const; | ||
}; | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 190: | Line 180: | ||
+ return 0; | + return 0; | ||
+ | + | ||
+ } | + } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 197: | Line 186: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
osg::Node* model = Canvas::getSystemAdapter()->getModel("Models/Geometry/glider.ac"); | osg::Node* model = Canvas::getSystemAdapter()->getModel("Models/Geometry/glider.ac"); | ||
if (!model) { | |||
SG_LOG(SG_GL, SG_ALERT, "Adapter not working: getModel()"); | |||
} else { | |||
SG_LOG(SG_GL, SG_ALERT, "Success, model loaded !"); | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 209: | Line 198: | ||
== Turning the filename into a property == | == Turning the filename into a property == | ||
So far, we really only have a proof-of-concept loading a hard-coded 3D model from disk and adding this to a Canvas scene-graph. | So far, we really only have a proof-of-concept loading a hard-coded 3D model from disk and adding this to a Canvas scene-graph. | ||
However, to be really useful, the file name should not be hard-coded, but should be just a property living in the sub-tree of the corresponding Canvas element, which is what we're going to implement next. Again, it makes sense to look at existing/similar use-cases in FlightGear, and specifically the corresponding subsystem, i.e. Canvas in this case - for that, we can simply look at the implementation of CanvasImage, which already supports the notion of a "filename", which will update the whole element once modified: | However, to be really useful, the file name should not be hard-coded, but should be just a property living in the sub-tree of the corresponding Canvas element, which is what we're going to implement next. Again, it makes sense to look at existing/similar use-cases in FlightGear, and specifically the corresponding subsystem, i.e. Canvas in this case - for that, we can simply look at the implementation of CanvasImage, which already supports the notion of a "filename", which will update the whole element once modified: | ||
{{simgear file|simgear/canvas/elements/CanvasImage.cxx|l=615}} | |||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
if( name == "file" ) | if(name == "file"){ | ||
SG_LOG(SG_GL, SG_WARN, "'file' is deprecated. Use 'src' instead"); | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
As can be seen here, the general idea is to use the <code>::childChanged(SGPropertyNode* child)</code> method to look for supported nodes and process updates accordingly. The main thing to keep in mind here is that Canvas/SimGear don't know anything about FlightGear-specific APIs, which is why the CanvasSystemAdapter needs to be used, and called, to expose/access the corresponding APIs: | As can be seen here, the general idea is to use the <code>::childChanged(SGPropertyNode* child)</code> method to look for supported nodes and process updates accordingly. The main thing to keep in mind here is that Canvas/SimGear don't know anything about FlightGear-specific APIs, which is why the CanvasSystemAdapter needs to be used, and called, to expose/access the corresponding APIs: | ||
{{simgear file|simgear/canvas/elements/CanvasImage.cxx|l=680}} | |||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
Canvas::getSystemAdapter() | Canvas::getSystemAdapter() | ||
->getHTTPClient() | |||
->load(url) | |||
// TODO handle capture of 'this' | |||
->done(this, &Image::handleImageLoadDone); | |||
</syntaxhighlight> | </syntaxhighlight> | ||
== Using loadPagedModel() instead == | == Using loadPagedModel() instead == | ||
(see | (see {{flightgear file|AI/Aircraft/AIBase.cxx}}, Nasal animated models) | ||
* | * {{simgear commit|32a6bd78d8bf143f40922f1a0bc7a88ea7706a7d}} | ||
* http://sourceforge.net/p/simgear/mailman/message/18904097/ | * http://sourceforge.net/p/simgear/mailman/message/18904097/ | ||
| Line 246: | Line 235: | ||
* http://www.sm.luth.se/csee/courses/smm/011/l/t1.pdf | * http://www.sm.luth.se/csee/courses/smm/011/l/t1.pdf | ||
{{ | {{Custom Canvas Element|baseclass=Image|elementName=Model|elementTitle=Canvas 3D model viewer|description=Stub for exposing OSG model loader as a dedicated Canvas element }} | ||
[[File:Custom-canvas-element.png|right|thumb|testing a custom canvas element]] | [[File:Custom-canvas-element.png|right|thumb|testing a custom canvas element]] | ||