Difference between revisions of "Canvas View Camera Element"

From FlightGear wiki
Jump to: navigation, search
(Use Cases)
(15 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 +
{{FGCquote
 +
  |It would make sense to integrate all efforts ([[CompositeViewer Support|CompositeViewer]], [[Canvas]] and [[Compositor]]) to create a [[Canvas_Development#Elements|Canvas Element]] that can render an [[Canvas_Development#Supporting_Cameras|arbitrary view]].
 +
 +
Some time ago I wrote what I think are the required steps to get something like cockpit mirrors working: [[#Roadmap]]
 +
 +
The main showstopper was having [[CompositeViewer Support]], but since that effort is underway, all the parts are there for someone willing to dive in.
 +
  |{{cite web |url=https://sourceforge.net/p/flightgear/mailman/message/37089780/
 +
    |title=<nowiki>Independent view windows</nowiki>
 +
    |author=<nowiki>Fernando García Liñán </nowiki>
 +
    |date=<nowiki>2020-08-20 14:27:58</nowiki>
 +
  }}
 +
}}
 +
 
{{infobox subsystem
 
{{infobox subsystem
 
|name        = Canvas Camera Views
 
|name        = Canvas Camera Views
Line 10: Line 23:
  
 
Several aircraft developers have manifested their interest in being able to render scene views to a texture (RTT) {{Wikipedia|Render target}} and use it inside cockpits as mirrors, external cameras (so called tail cams) and other uses.
 
Several aircraft developers have manifested their interest in being able to render scene views to a texture (RTT) {{Wikipedia|Render target}} and use it inside cockpits as mirrors, external cameras (so called tail cams) and other uses.
 +
 +
With the [[Compositor]] framework it's possible to configure custom render pipelines in XML space, which together with Effect schemes allow complete control over how a rendered scene looks. A synthetic terrain can be rendered by assigning an Effect scheme to a <tt>scene</tt> type pass, which textures all terrain with a brown checkerboard pattern and paints the sky in a solid blue color. All other objects are ignored and aren't rendered, saving a lot of computation time. Custom render distances independent from the main LOD settings can also be used to further optimize the performance of the display.
  
 
== Use Cases ==
 
== Use Cases ==
* Tail Cams
+
* Tail Cams (gear view)
 
* Mirrors
 
* Mirrors
 
* Dynamic in-sim view configuration
 
* Dynamic in-sim view configuration
 
* On demand creation of views and windows (e.g. [[FGCamera]] previews)
 
* On demand creation of views and windows (e.g. [[FGCamera]] previews)
* missile/payload views
+
* Missile/payload views
* Prototyping/testing HUDs or PFDs requiring synthetic terrain to work properly (will require support for effects/shaders)
+
* Prototyping/testing HUDs or PFDs requiring synthetic terrain to work properly
 +
 
 +
== Proof of Concept ==
 +
{{Note|This is inspired by the long standing idea to render camera views to a texture for use as tailcams, gear views etc <ref>https://forum.flightgear.org/viewtopic.php?f=71&t=23929</ref>, it's based on code originally provided by F-JJTH<ref>https://forum.flightgear.org/viewtopic.php?f=71&t=23929#p317448</ref>, and subsequently reworked by Icecode GL and Hooray to integrate it with the Canvas system back in 2017<ref>https://forum.flightgear.org/viewtopic.php?f=47&t=32846&p=318046</ref>
 +
At the time, the main issue was that the new view element needed to be explicitly updated, but that should no longer be the case thanks to James' work on the setPixel() API <ref>https://sourceforge.net/p/flightgear/mailman/message/36926486/</ref> which also means that sc::Image now has a <code>dirtyPixels()</code> API which we can reuse <ref>https://sourceforge.net/p/flightgear/simgear/ci/ad3621e23b4056e1be7e8d9eb8dd4d513455add8/</ref>
 +
Due to the adoption of the [[Compositor]] framework, this will probably need to reworked to be properly integrated.
 +
Furthermore, beginning in mid 2020 Julian Smith has started working on optional [[CompositeViewer Support]] so that independent scene views can be rendered.  }}
 +
 
 +
SimGear:
 +
<syntaxhighlight lang="diff">
 +
diff --git a/simgear/canvas/CanvasSystemAdapter.hxx b/simgear/canvas/CanvasSystemAdapter.hxx
 +
index 43b3f780..0fb62170 100644
 +
--- a/simgear/canvas/CanvasSystemAdapter.hxx
 +
+++ b/simgear/canvas/CanvasSystemAdapter.hxx
 +
@@ -35,11 +35,13 @@ namespace canvas
 +
 +
      virtual ~SystemAdapter() {}
 +
      virtual FontPtr getFont(const std::string& name) const = 0;
 +
-      virtual void addCamera(osg::Camera* camera) const = 0;
 +
+      virtual void addCamera(osg::Camera* camera, bool useSceneData = false) const = 0;
 +
      virtual void removeCamera(osg::Camera* camera) const = 0;
 +
      virtual osg::ref_ptr<osg::Image> getImage(const std::string& path) const = 0;
 +
+      virtual osg::Node* getModel(const std::string& path) const = 0;
 +
      virtual SGSubsystem* getSubsystem(const std::string& name) const = 0;
 +
      virtual HTTP::Client* getHTTPClient() const = 0;
 +
+      virtual osg::Matrix getViewMatrix(int view_number, double dt) const = 0;
 +
  };
 +
 +
} // namespace canvas
 +
diff --git a/simgear/canvas/canvas_fwd.hxx b/simgear/canvas/canvas_fwd.hxx
 +
index 3b1b4646..97e9bd64 100644
 +
--- a/simgear/canvas/canvas_fwd.hxx
 +
+++ b/simgear/canvas/canvas_fwd.hxx
 +
@@ -24,6 +24,7 @@
 +
 +
#include <osg/ref_ptr>
 +
#include <osgText/Font>
 +
+#include <osg/Node>
 +
 +
#include <boost/function.hpp>
 +
#include <boost/shared_ptr.hpp>
 +
diff --git a/simgear/canvas/elements/CMakeLists.txt b/simgear/canvas/elements/CMakeLists.txt
 +
index 2b537c0c..4eb1e955 100644
 +
--- a/simgear/canvas/elements/CMakeLists.txt
 +
+++ b/simgear/canvas/elements/CMakeLists.txt
 +
@@ -7,6 +7,7 @@ set(HEADERS
 +
  CanvasMap.hxx
 +
  CanvasPath.hxx
 +
  CanvasText.hxx
 +
+  CanvasView.hxx
 +
)
 +
 +
set(DETAIL_HEADERS
 +
@@ -20,6 +21,7 @@ set(SOURCES
 +
  CanvasMap.cxx
 +
  CanvasPath.cxx
 +
  CanvasText.cxx
 +
+  CanvasView.cxx
 +
)
 +
 +
simgear_scene_component(canvas-elements canvas/elements "${SOURCES}" "${HEADERS}")
 +
@@ -28,4 +30,4 @@ simgear_component(canvas-elements/detail canvas/elements/detail "" "${DETAIL_HEA
 +
add_boost_test(canvas_element
 +
  SOURCES canvas_element_test.cpp
 +
  LIBRARIES ${TEST_LIBS}
 +
-)
 +
\ No newline at end of file
 +
+)
 +
diff --git a/simgear/canvas/elements/CanvasGroup.cxx b/simgear/canvas/elements/CanvasGroup.cxx
 +
index c753c2e2..6cec072f 100644
 +
--- a/simgear/canvas/elements/CanvasGroup.cxx
 +
+++ b/simgear/canvas/elements/CanvasGroup.cxx
 +
@@ -23,6 +23,7 @@
 +
#include "CanvasMap.hxx"
 +
#include "CanvasPath.hxx"
 +
#include "CanvasText.hxx"
 +
+#include "CanvasView.hxx"
 +
#include <simgear/canvas/CanvasEventVisitor.hxx>
 +
#include <simgear/canvas/events/MouseEvent.hxx>
 +
 +
@@ -66,6 +67,7 @@ namespace canvas
 +
    add<Map  >(_child_factories);
 +
    add<Path >(_child_factories);
 +
    add<Text >(_child_factories);
 +
+    add<View >(_child_factories);
 +
  }
 +
 +
  //----------------------------------------------------------------------------
 +
diff --git a/simgear/canvas/elements/CanvasView.cxx b/simgear/canvas/elements/CanvasView.cxx
 +
new file mode 100644
 +
index 00000000..502e82d7
 +
--- /dev/null
 +
+++ b/simgear/canvas/elements/CanvasView.cxx
 +
@@ -0,0 +1,91 @@
 +
+#include <simgear/canvas/Canvas.hxx>
 +
+#include <simgear/canvas/CanvasMgr.hxx>
 +
+#include <simgear/canvas/CanvasSystemAdapter.hxx>
 +
+
 +
+#include <simgear/scene/util/OsgMath.hxx>
 +
+
 +
+#include "CanvasView.hxx"
 +
+
 +
+#include <iostream>
 +
+#include <osg/io_utils>
 +
+
 +
+namespace simgear
 +
+{
 +
+namespace canvas
 +
+{
 +
+
 +
+const std::string View::TYPE_NAME = "view";
 +
+
 +
+void View::staticInit()
 +
+{
 +
+    if ( isInit<View>() )
 +
+        return;
 +
+}
 +
+
 +
+View::View( const CanvasWeakPtr& canvas,
 +
+              const SGPropertyNode_ptr& node,
 +
+              const Style& parent_style,
 +
+              ElementWeakPtr parent ) :
 +
+    Element(canvas, node, parent_style, parent),
 +
+    _width(512),
 +
+    _height(512)
 +
+{
 +
+    staticInit();
 +
+
 +
+    _texture = new osg::Texture2D;
 +
+    _texture->setDataVariance(osg::Object::DYNAMIC);
 +
+    _texture->setTextureSize(_width, _height);
 +
+    _texture->setInternalFormat(GL_RGBA);
 +
+    _texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
 +
+    _texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
 +
+
 +
+    _camera = new osg::Camera;
 +
+    _camera->setDataVariance(osg::Object::DYNAMIC);
 +
+    _camera->setViewport(0, 0, _width, _height);
 +
+    _camera->setClearColor(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
 +
+    _camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 +
+    _camera->setRenderOrder(osg::Camera::PRE_RENDER);
 +
+    _camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::FRAME_BUFFER);
 +
+    _camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
 +
+    double aspect_ratio = (double)_width / _height;
 +
+    _camera->setProjectionMatrixAsPerspective(85.0, aspect_ratio, 0.1, 120000.0);
 +
+
 +
+    _camera->attach(osg::Camera::COLOR_BUFFER, _texture.get());
 +
+
 +
+    if (Canvas::getSystemAdapter())
 +
+        Canvas::getSystemAdapter()->addCamera(_camera.get(), true);
 +
+
 +
+    _quad = osg::createTexturedQuadGeometry(osg::Vec3(0.0,    0.0,    0.0),
 +
+                                            osg::Vec3(_width, 0.0,    0.0),
 +
+                                            osg::Vec3(0.0,    _height, 0.0),
 +
+                                            // In Canvas (0,0) is the top left corner, while in OSG/OpenGL
 +
+                                            // it's the bottom left corner.
 +
+                                            0.0, 1.0, 1.0, 0.0);
 +
+    _quad->getOrCreateStateSet()->setTextureAttributeAndModes(0, _texture.get(), osg::StateAttribute::ON);
 +
+
 +
+    setDrawable(_quad.get());
 +
+}
 +
+
 +
+View::~View()
 +
+{
 +
+    if( _camera.valid() && Canvas::getSystemAdapter() )
 +
+        Canvas::getSystemAdapter()->removeCamera(_camera.get());
 +
+    _camera.release();
 +
+    _texture.release();
 +
+}
 +
+
 +
+void View::update(double dt)
 +
+{
 +
+    osg::Matrix view_matrix = Canvas::getSystemAdapter()->getViewMatrix(0, dt);
 +
+    _camera->setViewMatrix(view_matrix);
 +
+
 +
+    Element::update(dt);
 +
+}
 +
+
 +
+void View::resize(int width, int height)
 +
+{
 +
+
 +
+}
 +
+
 +
+} // namespace canvas
 +
+} // namespace simgear
 +
diff --git a/simgear/canvas/elements/CanvasView.hxx b/simgear/canvas/elements/CanvasView.hxx
 +
new file mode 100644
 +
index 00000000..8cb3cc49
 +
--- /dev/null
 +
+++ b/simgear/canvas/elements/CanvasView.hxx
 +
@@ -0,0 +1,39 @@
 +
+#ifndef CANVAS_VIEW_HXX_
 +
+#define CANVAS_VIEW_HXX_
 +
+
 +
+#include "CanvasElement.hxx"
 +
+
 +
+namespace simgear
 +
+{
 +
+namespace canvas
 +
+{
 +
+
 +
+class View : public Element {
 +
+public:
 +
+    static const std::string TYPE_NAME;
 +
+    static void staticInit();
 +
+
 +
+    View( const CanvasWeakPtr& canvas,
 +
+          const SGPropertyNode_ptr& node,
 +
+          const Style& parent_style = Style(),
 +
+          ElementWeakPtr parent = 0 );
 +
+    virtual ~View();
 +
+
 +
+    virtual void update(double dt);
 +
+
 +
+    virtual void resize(int width, int height);
 +
+
 +
+protected:
 +
+
 +
+    osg::ref_ptr<osg::Camera> _camera;
 +
+    osg::ref_ptr<osg::Texture2D> _texture;
 +
+
 +
+    osg::ref_ptr<osg::Geometry> _quad;
 +
+
 +
+    int _width, _height;
 +
+};
 +
+
 +
+} // namespace canvas
 +
+} // namespace simgear
 +
+
 +
+#endif // CANVAS_VIEW_HXX_
 +
</syntaxhighlight>
 +
 
 +
FlightGear
 +
<syntaxhighlight lang="diff">   
 +
diff --git a/src/Canvas/FGCanvasSystemAdapter.cxx b/src/Canvas/FGCanvasSystemAdapter.cxx
 +
index 72b20747e..9e04dbd14 100644
 +
--- a/src/Canvas/FGCanvasSystemAdapter.cxx
 +
+++ b/src/Canvas/FGCanvasSystemAdapter.cxx
 +
@@ -23,9 +23,18 @@
 +
#include <Network/HTTPClient.hxx>
 +
#include <Viewer/renderer.hxx>
 +
 
 +
+
 +
+#include <Viewer/view.hxx>
 +
+#include <Viewer/viewmgr.hxx>
 +
+#include <simgear/scene/util/OsgMath.hxx>
 +
+
 +
+
 +
+
 +
#include <osgDB/ReadFile>
 +
#include <stdexcept>
 +
 
 +
+#include <simgear/scene/model/modellib.hxx>
 +
+
 +
namespace canvas
 +
{
 +
//----------------------------------------------------------------------------
 +
@@ -64,9 +73,9 @@ namespace canvas
 +
}
 +
 
 +
//----------------------------------------------------------------------------
 +
-  void FGCanvasSystemAdapter::addCamera(osg::Camera* camera) const
 +
+  void FGCanvasSystemAdapter::addCamera(osg::Camera* camera, bool useSceneData) const
 +
{
 +
-    globals->get_renderer()->addCamera(camera, false);
 +
+    globals->get_renderer()->addCamera(camera, useSceneData);
 +
}
 +
 
 +
//----------------------------------------------------------------------------
 +
@@ -100,6 +109,46 @@ namespace canvas
 +
return 0;
 +
}
 +
 
 +
+    //----------------------------------------------------------------------------
 +
+    // From http://wiki.flightgear.org/Howto:Extending_Canvas_to_support_rendering_3D_models#Extending_FGCanvasSystemAdapter
 +
+    osg::Node* FGCanvasSystemAdapter::getModel(const std::string& path) const
 +
+    {
 +
+        const char *model_path = "Models/Geometry/glider.ac";
 +
+        SGPath p(SGPath::fromUtf8(path));
 +
+
 +
+        if( p.isAbsolute() )
 +
+        {
 +
+            SGPath valid_path = fgValidatePath(p, false);
 +
+            if( !valid_path.isNull() )
 +
+            try {
 +
+                std::string fullPath = simgear::SGModelLib::findDataFile(valid_path.local8BitStr());
 +
+                osg::Node * object = simgear::SGModelLib::loadDeferredModel(fullPath, globals->get_props());
 +
+                return object;
 +
+            } catch (const sg_throwable& t) {
 +
+                SG_LOG(SG_IO, SG_ALERT, "Error loading " << model_path << ":\n  " << t.getFormattedMessage() << t.getOrigin());
 +
+                return 0;
 +
+            } // error loading from absolute path
 +
+            SG_LOG(SG_IO, SG_ALERT, "canvas::Model: reading '" << path << "' denied");
 +
+        } // absolute path handling
 +
+        else
 +
+        {
 +
+            SGPath tpath = globals->resolve_resource_path(path);
 +
+            if( !tpath.isNull() )
 +
+            try {
 +
+                std::string fullPath = simgear::SGModelLib::findDataFile(path.c_str());
 +
+                osg::Node * object = simgear::SGModelLib::loadDeferredModel(fullPath, globals->get_props());
 +
+                return object;
 +
+            } catch (const sg_throwable& t) {
 +
+                SG_LOG(SG_IO, SG_ALERT, "Error loading " << model_path << ":\n  " << t.getFormattedMessage() << t.getOrigin());
 +
+                return 0;
 +
+            } // error loading from relative path
 +
+
 +
+            SG_LOG(SG_IO, SG_ALERT, "canvas::Model: No such model: '" << path << "'");
 +
+        } // relative path handling
 +
+
 +
+        return 0;
 +
+    }
 +
+
 +
//----------------------------------------------------------------------------
 +
SGSubsystem*
 +
FGCanvasSystemAdapter::getSubsystem(const std::string& name) const
 +
@@ -121,4 +170,16 @@ namespace canvas
 +
return 0;
 +
}
 +
 
 +
+  osg::Matrix FGCanvasSystemAdapter::getViewMatrix(int view_number, double dt) const
 +
+  {
 +
+        flightgear::View *view = globals->get_viewmgr()->get_view(view_number);
 +
+        view->update(dt);
 +
+
 +
+        osg::Vec3f position( toOsg(view->getViewPosition()) );
 +
+        osg::Quat orientation( toOsg(view->getViewOrientation()) );
 +
+        osg::Matrix viewMatrix( osg::Matrix::translate(-position) * osg::Matrix::rotate(orientation.inverse()) );
 +
+
 +
+        return viewMatrix;
 +
+  }
 +
+
 +
}
 +
diff --git a/src/Canvas/FGCanvasSystemAdapter.hxx b/src/Canvas/FGCanvasSystemAdapter.hxx
 +
index 4c1fd6210..24ececb41 100644
 +
--- a/src/Canvas/FGCanvasSystemAdapter.hxx
 +
+++ b/src/Canvas/FGCanvasSystemAdapter.hxx
 +
@@ -28,11 +28,13 @@ namespace canvas
 +
{
 +
public:
 +
virtual simgear::canvas::FontPtr getFont(const std::string& name) const;
 +
-      virtual void addCamera(osg::Camera* camera) const;
 +
+      virtual void addCamera(osg::Camera* camera, bool useSceneData = false) const;
 +
virtual void removeCamera(osg::Camera* camera) const;
 +
virtual osg::ref_ptr<osg::Image> getImage(const std::string& path) const;
 +
+      virtual osg::Node* getModel(const std::string& path) const;
 +
virtual SGSubsystem* getSubsystem(const std::string& name) const;
 +
virtual simgear::HTTP::Client* getHTTPClient() const;
 +
+      virtual osg::Matrix getViewMatrix(int view_number, double dt) const;
 +
};
 +
}
 +
 
 +
diff --git a/src/Viewer/renderer.cxx b/src/Viewer/renderer.cxx
 +
index d9e8f351f..b9b3fc6a0 100644
 +
--- a/src/Viewer/renderer.cxx
 +
+++ b/src/Viewer/renderer.cxx
 +
@@ -1845,6 +1845,8 @@ FGRenderer::setEventHandler(FGEventHandler* eventHandler_)
 +
void
 +
FGRenderer::addCamera(osg::Camera* camera, bool useSceneData)
 +
{
 +
+    if (useSceneData)
 +
+        camera->addChild(globals->get_scenery()->get_scene_graph());
 +
_viewerSceneRoot->addChild(camera);
 +
}
 +
 
 +
 
 +
</syntaxhighlight>
 +
 
 +
fgdata
 +
<syntaxhighlight lang="diff">
 +
diff --git a/Nasal/canvas/api.nas b/Nasal/canvas/api.nas
 +
index 3f7f5a17..433a41f9 100644
 +
--- a/Nasal/canvas/api.nas
 +
+++ b/Nasal/canvas/api.nas
 +
@@ -1132,10 +1132,10 @@ var Image = {
 +
  }
 +
};
 +
 +
-var Model = {
 +
+var View = {
 +
  new: func(ghost)
 +
  {
 +
-    return {parents: [Model, Element.new(ghost)]};
 +
+    return {parents: [View, Element.new(ghost)]};
 +
  },
 +
};
 +
 +
@@ -1146,7 +1146,7 @@ Group._element_factories = {
 +
  "text": Text.new,
 +
  "path": Path.new,
 +
  "image": Image.new,
 +
-  "model": Model.new
 +
+  "view": View.new
 +
};
 +
 +
# Canvas
 +
</syntaxhighlight>
  
 
== Roadmap ==
 
== Roadmap ==
Line 53: Line 464:
 
* {{forum link|t=18905|text=Progress on synthetic terrain}}
 
* {{forum link|t=18905|text=Progress on synthetic terrain}}
 
* {{forum link|t=17184|text=Instruments with heightmaps}}
 
* {{forum link|t=17184|text=Instruments with heightmaps}}
 +
 +
[[Category:Canvas Element Proposals]]

Revision as of 12:49, 23 August 2020

Cquote1.png It would make sense to integrate all efforts (CompositeViewer, Canvas and Compositor) to create a Canvas Element that can render an arbitrary view.

Some time ago I wrote what I think are the required steps to get something like cockpit mirrors working: #Roadmap

The main showstopper was having CompositeViewer Support, but since that effort is underway, all the parts are there for someone willing to dive in.


— Fernando García Liñán  (2020-08-20 14:27:58). Independent view windows.
(powered by Instant-Cquotes)
Cquote2.png
Canvas Camera Views
Description Rendering arbitrary views to a texture/Canvas
Maintainer(s) none
Contributor(s) none
Status planned/prototype

Background

Several aircraft developers have manifested their interest in being able to render scene views to a texture (RTT) Render target This is a link to a Wikipedia article and use it inside cockpits as mirrors, external cameras (so called tail cams) and other uses.

With the Compositor framework it's possible to configure custom render pipelines in XML space, which together with Effect schemes allow complete control over how a rendered scene looks. A synthetic terrain can be rendered by assigning an Effect scheme to a scene type pass, which textures all terrain with a brown checkerboard pattern and paints the sky in a solid blue color. All other objects are ignored and aren't rendered, saving a lot of computation time. Custom render distances independent from the main LOD settings can also be used to further optimize the performance of the display.

Use Cases

  • Tail Cams (gear view)
  • Mirrors
  • Dynamic in-sim view configuration
  • On demand creation of views and windows (e.g. FGCamera previews)
  • Missile/payload views
  • Prototyping/testing HUDs or PFDs requiring synthetic terrain to work properly

Proof of Concept

Note  This is inspired by the long standing idea to render camera views to a texture for use as tailcams, gear views etc [1], it's based on code originally provided by F-JJTH[2], and subsequently reworked by Icecode GL and Hooray to integrate it with the Canvas system back in 2017[3]

At the time, the main issue was that the new view element needed to be explicitly updated, but that should no longer be the case thanks to James' work on the setPixel() API [4] which also means that sc::Image now has a dirtyPixels() API which we can reuse [5] Due to the adoption of the Compositor framework, this will probably need to reworked to be properly integrated. Furthermore, beginning in mid 2020 Julian Smith has started working on optional CompositeViewer Support so that independent scene views can be rendered.

SimGear:

diff --git a/simgear/canvas/CanvasSystemAdapter.hxx b/simgear/canvas/CanvasSystemAdapter.hxx
index 43b3f780..0fb62170 100644
--- a/simgear/canvas/CanvasSystemAdapter.hxx
+++ b/simgear/canvas/CanvasSystemAdapter.hxx
@@ -35,11 +35,13 @@ namespace canvas
 
       virtual ~SystemAdapter() {}
       virtual FontPtr getFont(const std::string& name) const = 0;
-      virtual void addCamera(osg::Camera* camera) const = 0;
+      virtual void addCamera(osg::Camera* camera, bool useSceneData = false) const = 0;
       virtual void removeCamera(osg::Camera* camera) const = 0;
       virtual osg::ref_ptr<osg::Image> getImage(const std::string& path) const = 0;
+      virtual osg::Node* getModel(const std::string& path) const = 0;
       virtual SGSubsystem* getSubsystem(const std::string& name) const = 0;
       virtual HTTP::Client* getHTTPClient() const = 0;
+      virtual osg::Matrix getViewMatrix(int view_number, double dt) const = 0;
   };
 
 } // namespace canvas
diff --git a/simgear/canvas/canvas_fwd.hxx b/simgear/canvas/canvas_fwd.hxx
index 3b1b4646..97e9bd64 100644
--- a/simgear/canvas/canvas_fwd.hxx
+++ b/simgear/canvas/canvas_fwd.hxx
@@ -24,6 +24,7 @@
 
 #include <osg/ref_ptr>
 #include <osgText/Font>
+#include <osg/Node>
 
 #include <boost/function.hpp>
 #include <boost/shared_ptr.hpp>
diff --git a/simgear/canvas/elements/CMakeLists.txt b/simgear/canvas/elements/CMakeLists.txt
index 2b537c0c..4eb1e955 100644
--- a/simgear/canvas/elements/CMakeLists.txt
+++ b/simgear/canvas/elements/CMakeLists.txt
@@ -7,6 +7,7 @@ set(HEADERS
   CanvasMap.hxx
   CanvasPath.hxx
   CanvasText.hxx
+  CanvasView.hxx
 )
 
 set(DETAIL_HEADERS
@@ -20,6 +21,7 @@ set(SOURCES
   CanvasMap.cxx
   CanvasPath.cxx
   CanvasText.cxx
+  CanvasView.cxx
 )
 
 simgear_scene_component(canvas-elements canvas/elements "${SOURCES}" "${HEADERS}")
@@ -28,4 +30,4 @@ simgear_component(canvas-elements/detail canvas/elements/detail "" "${DETAIL_HEA
 add_boost_test(canvas_element
   SOURCES canvas_element_test.cpp
   LIBRARIES ${TEST_LIBS}
-)
\ No newline at end of file
+)
diff --git a/simgear/canvas/elements/CanvasGroup.cxx b/simgear/canvas/elements/CanvasGroup.cxx
index c753c2e2..6cec072f 100644
--- a/simgear/canvas/elements/CanvasGroup.cxx
+++ b/simgear/canvas/elements/CanvasGroup.cxx
@@ -23,6 +23,7 @@
 #include "CanvasMap.hxx"
 #include "CanvasPath.hxx"
 #include "CanvasText.hxx"
+#include "CanvasView.hxx"
 #include <simgear/canvas/CanvasEventVisitor.hxx>
 #include <simgear/canvas/events/MouseEvent.hxx>
 
@@ -66,6 +67,7 @@ namespace canvas
     add<Map  >(_child_factories);
     add<Path >(_child_factories);
     add<Text >(_child_factories);
+    add<View >(_child_factories);
   }
 
   //----------------------------------------------------------------------------
diff --git a/simgear/canvas/elements/CanvasView.cxx b/simgear/canvas/elements/CanvasView.cxx
new file mode 100644
index 00000000..502e82d7
--- /dev/null
+++ b/simgear/canvas/elements/CanvasView.cxx
@@ -0,0 +1,91 @@
+#include <simgear/canvas/Canvas.hxx>
+#include <simgear/canvas/CanvasMgr.hxx>
+#include <simgear/canvas/CanvasSystemAdapter.hxx>
+
+#include <simgear/scene/util/OsgMath.hxx>
+
+#include "CanvasView.hxx"
+
+#include <iostream>
+#include <osg/io_utils>
+
+namespace simgear
+{
+namespace canvas
+{
+
+const std::string View::TYPE_NAME = "view";
+
+void View::staticInit()
+{
+    if ( isInit<View>() )
+        return;
+}
+
+View::View( const CanvasWeakPtr& canvas,
+              const SGPropertyNode_ptr& node,
+              const Style& parent_style,
+              ElementWeakPtr parent ) :
+    Element(canvas, node, parent_style, parent),
+    _width(512),
+    _height(512)
+{
+    staticInit();
+
+    _texture = new osg::Texture2D;
+    _texture->setDataVariance(osg::Object::DYNAMIC);
+    _texture->setTextureSize(_width, _height);
+    _texture->setInternalFormat(GL_RGBA);
+    _texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
+    _texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
+
+    _camera = new osg::Camera;
+    _camera->setDataVariance(osg::Object::DYNAMIC);
+    _camera->setViewport(0, 0, _width, _height);
+    _camera->setClearColor(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
+    _camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    _camera->setRenderOrder(osg::Camera::PRE_RENDER);
+    _camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::FRAME_BUFFER);
+    _camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
+    double aspect_ratio = (double)_width / _height;
+    _camera->setProjectionMatrixAsPerspective(85.0, aspect_ratio, 0.1, 120000.0);
+
+    _camera->attach(osg::Camera::COLOR_BUFFER, _texture.get());
+
+    if (Canvas::getSystemAdapter())
+        Canvas::getSystemAdapter()->addCamera(_camera.get(), true);
+
+    _quad = osg::createTexturedQuadGeometry(osg::Vec3(0.0,    0.0,     0.0),
+                                            osg::Vec3(_width, 0.0,     0.0),
+                                            osg::Vec3(0.0,    _height, 0.0),
+                                            // In Canvas (0,0) is the top left corner, while in OSG/OpenGL
+                                            // it's the bottom left corner.
+                                            0.0, 1.0, 1.0, 0.0);
+    _quad->getOrCreateStateSet()->setTextureAttributeAndModes(0, _texture.get(), osg::StateAttribute::ON);
+
+    setDrawable(_quad.get());
+}
+
+View::~View()
+{
+    if( _camera.valid() && Canvas::getSystemAdapter() )
+        Canvas::getSystemAdapter()->removeCamera(_camera.get());
+    _camera.release();
+    _texture.release();
+}
+
+void View::update(double dt)
+{
+    osg::Matrix view_matrix = Canvas::getSystemAdapter()->getViewMatrix(0, dt);
+    _camera->setViewMatrix(view_matrix);
+
+    Element::update(dt);
+}
+
+void View::resize(int width, int height)
+{
+
+}
+
+} // namespace canvas
+} // namespace simgear
diff --git a/simgear/canvas/elements/CanvasView.hxx b/simgear/canvas/elements/CanvasView.hxx
new file mode 100644
index 00000000..8cb3cc49
--- /dev/null
+++ b/simgear/canvas/elements/CanvasView.hxx
@@ -0,0 +1,39 @@
+#ifndef CANVAS_VIEW_HXX_
+#define CANVAS_VIEW_HXX_
+
+#include "CanvasElement.hxx"
+
+namespace simgear
+{
+namespace canvas
+{
+
+class View : public Element {
+public:
+    static const std::string TYPE_NAME;
+    static void staticInit();
+
+    View( const CanvasWeakPtr& canvas,
+           const SGPropertyNode_ptr& node,
+           const Style& parent_style = Style(),
+           ElementWeakPtr parent = 0 );
+    virtual ~View();
+
+    virtual void update(double dt);
+
+    virtual void resize(int width, int height);
+
+protected:
+
+    osg::ref_ptr<osg::Camera> _camera;
+    osg::ref_ptr<osg::Texture2D> _texture;
+
+    osg::ref_ptr<osg::Geometry> _quad;
+
+    int _width, _height;
+};
+
+} // namespace canvas
+} // namespace simgear
+
+#endif // CANVAS_VIEW_HXX_

FlightGear

    
diff --git a/src/Canvas/FGCanvasSystemAdapter.cxx b/src/Canvas/FGCanvasSystemAdapter.cxx
index 72b20747e..9e04dbd14 100644
--- a/src/Canvas/FGCanvasSystemAdapter.cxx
+++ b/src/Canvas/FGCanvasSystemAdapter.cxx
@@ -23,9 +23,18 @@
#include <Network/HTTPClient.hxx>
#include <Viewer/renderer.hxx>

+
+#include <Viewer/view.hxx>
+#include <Viewer/viewmgr.hxx>
+#include <simgear/scene/util/OsgMath.hxx>
+
+
+
#include <osgDB/ReadFile>
#include <stdexcept>

+#include <simgear/scene/model/modellib.hxx>
+
namespace canvas
{
//----------------------------------------------------------------------------
@@ -64,9 +73,9 @@ namespace canvas
}

//----------------------------------------------------------------------------
-  void FGCanvasSystemAdapter::addCamera(osg::Camera* camera) const
+  void FGCanvasSystemAdapter::addCamera(osg::Camera* camera, bool useSceneData) const
{
-    globals->get_renderer()->addCamera(camera, false);
+    globals->get_renderer()->addCamera(camera, useSceneData);
}

//----------------------------------------------------------------------------
@@ -100,6 +109,46 @@ namespace canvas
return 0;
}

+    //----------------------------------------------------------------------------
+    // From http://wiki.flightgear.org/Howto:Extending_Canvas_to_support_rendering_3D_models#Extending_FGCanvasSystemAdapter
+    osg::Node* FGCanvasSystemAdapter::getModel(const std::string& path) const
+    {
+        const char *model_path = "Models/Geometry/glider.ac";
+        SGPath p(SGPath::fromUtf8(path));
+
+        if( p.isAbsolute() )
+        {
+            SGPath valid_path = fgValidatePath(p, false);
+            if( !valid_path.isNull() )
+            try {
+                std::string fullPath = simgear::SGModelLib::findDataFile(valid_path.local8BitStr());
+                osg::Node * object = simgear::SGModelLib::loadDeferredModel(fullPath, globals->get_props());
+                return object;
+            } catch (const sg_throwable& t) {
+                SG_LOG(SG_IO, SG_ALERT, "Error loading " << model_path << ":\n  " << t.getFormattedMessage() << t.getOrigin());
+                return 0;
+            } // error loading from absolute path
+            SG_LOG(SG_IO, SG_ALERT, "canvas::Model: reading '" << path << "' denied");
+        } // absolute path handling
+        else
+        {
+            SGPath tpath = globals->resolve_resource_path(path);
+            if( !tpath.isNull() )
+            try {
+                std::string fullPath = simgear::SGModelLib::findDataFile(path.c_str());
+                osg::Node * object = simgear::SGModelLib::loadDeferredModel(fullPath, globals->get_props());
+                return object;
+            } catch (const sg_throwable& t) {
+                SG_LOG(SG_IO, SG_ALERT, "Error loading " << model_path << ":\n  " << t.getFormattedMessage() << t.getOrigin());
+                return 0;
+            } // error loading from relative path
+
+            SG_LOG(SG_IO, SG_ALERT, "canvas::Model: No such model: '" << path << "'");
+        } // relative path handling
+
+        return 0;
+    }
+
//----------------------------------------------------------------------------
SGSubsystem*
FGCanvasSystemAdapter::getSubsystem(const std::string& name) const
@@ -121,4 +170,16 @@ namespace canvas
return 0;
}

+  osg::Matrix FGCanvasSystemAdapter::getViewMatrix(int view_number, double dt) const
+  {
+        flightgear::View *view = globals->get_viewmgr()->get_view(view_number);
+        view->update(dt);
+
+        osg::Vec3f position( toOsg(view->getViewPosition()) );
+        osg::Quat orientation( toOsg(view->getViewOrientation()) );
+        osg::Matrix viewMatrix( osg::Matrix::translate(-position) * osg::Matrix::rotate(orientation.inverse()) );
+
+        return viewMatrix;
+  }
+
}
diff --git a/src/Canvas/FGCanvasSystemAdapter.hxx b/src/Canvas/FGCanvasSystemAdapter.hxx
index 4c1fd6210..24ececb41 100644
--- a/src/Canvas/FGCanvasSystemAdapter.hxx
+++ b/src/Canvas/FGCanvasSystemAdapter.hxx
@@ -28,11 +28,13 @@ namespace canvas
{
public:
virtual simgear::canvas::FontPtr getFont(const std::string& name) const;
-      virtual void addCamera(osg::Camera* camera) const;
+      virtual void addCamera(osg::Camera* camera, bool useSceneData = false) const;
virtual void removeCamera(osg::Camera* camera) const;
virtual osg::ref_ptr<osg::Image> getImage(const std::string& path) const;
+      virtual osg::Node* getModel(const std::string& path) const;
virtual SGSubsystem* getSubsystem(const std::string& name) const;
virtual simgear::HTTP::Client* getHTTPClient() const;
+      virtual osg::Matrix getViewMatrix(int view_number, double dt) const;
};
}

diff --git a/src/Viewer/renderer.cxx b/src/Viewer/renderer.cxx
index d9e8f351f..b9b3fc6a0 100644
--- a/src/Viewer/renderer.cxx
+++ b/src/Viewer/renderer.cxx
@@ -1845,6 +1845,8 @@ FGRenderer::setEventHandler(FGEventHandler* eventHandler_)
void
FGRenderer::addCamera(osg::Camera* camera, bool useSceneData)
{
+    if (useSceneData)
+        camera->addChild(globals->get_scenery()->get_scene_graph());
_viewerSceneRoot->addChild(camera);
}

fgdata

diff --git a/Nasal/canvas/api.nas b/Nasal/canvas/api.nas
index 3f7f5a17..433a41f9 100644
--- a/Nasal/canvas/api.nas
+++ b/Nasal/canvas/api.nas
@@ -1132,10 +1132,10 @@ var Image = {
   }
 };
 
-var Model = {
+var View = {
   new: func(ghost)
   {
-    return {parents: [Model, Element.new(ghost)]};
+    return {parents: [View, Element.new(ghost)]};
   },
 };
 
@@ -1146,7 +1146,7 @@ Group._element_factories = {
   "text": Text.new,
   "path": Path.new,
   "image": Image.new,
-  "model": Model.new
+  "view": View.new
 };
 
 # Canvas

Roadmap

Use CompositeViewer Pending Pending

Currently FlightGear uses only one instance of osg::Viewer, which is used by CameraGroup to manage the slave cameras. Supporting CompositeViewer would require modifying flightgear/src/Viewer/fg_os_osgviewer.cxx and creating some kind of wrapper class that manages the CompositeViewer instance and assigns a CameraGroup to each osg::View. It's also important to note that currently all FG subsystems assume there is a single instance of CameraGroup.

Standardize the View manager Pending Pending

The first step would be to port the view code to SimGear so it can be used and known by the Compositor and Canvas. The view manager (flightgear/src/Viewer/viewmgr.cxx) currently has some hardcoded assumptions, so it would either need to be rewritten to remove them or a new interface for the Views could be created specifically for the Canvas Camera View.

In FlightGear, the Canvas system (which resides in SimGear) is integrated using the equivalent of a FGCanvasSystemAdapter, which provides all FG APIs to SimGear and makes the Canvas system available inside FG: flightgear/src/Canvas/FGCanvasSystemAdapter.cxx

Instead of using tied properties, the adapted view code would either used propertyObject<> or the propertyBasedMgr abstraction, so that the corresponding canvas element can continue to use well-known conventions to manipulate views: (flightgear/src/Viewer/view.cxx)

Create a Canvas Element subclass Pending Pending

1rightarrow.png See Canvas_Development#Adding_a_new_Element for the main article about this subject.

This derived class would require the following configuration:

  • Compositor to use.
  • View to use. This could be one of the "main" ones (i.e. the ones on the main property tree), or a locally-defined one that is only known to this Canvas Element (think FLIR).
  • Scene graph to use. By default the main scene graph would be used, but an arbitrary XML file can be loaded to render a custom model. A typical use-case would be instruments that need to manipulate/render a 3D object (for which we have working code, too)
  • Several optimization/miscellaneous parameters like framerate cap, etc.

Simplifying a lot, this Canvas Element would be an aggregation of a Compositor instance, a View and a pointer to a osg::Group representing the scene graph to render. This setup could be replicated in flightgear/src/Viewer/fg_os_osgviewer.cxx, with the difference of ignoring Canvas and using native windowing features from OSG.

Related

Forum topics

  • https://forum.flightgear.org/viewtopic.php?f=71&t=23929
  • https://forum.flightgear.org/viewtopic.php?f=71&t=23929#p317448
  • https://forum.flightgear.org/viewtopic.php?f=47&t=32846&p=318046
  • https://sourceforge.net/p/flightgear/mailman/message/36926486/
  • https://sourceforge.net/p/flightgear/simgear/ci/ad3621e23b4056e1be7e8d9eb8dd4d513455add8/