From 373d511c6948ae4737b183e0ac38471544b6f499 Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Wed, 4 Jul 2012 13:15:12 +0200 Subject: [PATCH] Canvas: Allow using canvases as PUI widgets. - Add new widget type canvas - Set canvas view dimension from the gui xml. - Expose mouse events to canvas widget properties. --- src/Canvas/canvas.cxx | 42 +++++++- src/Canvas/canvas.hxx | 8 +- src/Canvas/canvas_mgr.cxx | 14 ++- src/Canvas/canvas_mgr.hxx | 8 ++ src/GUI/CMakeLists.txt | 2 + src/GUI/CanvasWidget.cxx | 168 +++++++++++++++++++++++++++++++ src/GUI/CanvasWidget.hxx | 47 +++++++++ src/GUI/FGPUIDialog.cxx | 8 ++ src/Instrumentation/od_gauge.hxx | 4 +- src/Main/fg_init.cxx | 2 +- src/Scripting/NasalSys.cxx | 20 +++- src/Scripting/NasalSys.hxx | 4 + 12 files changed, 312 insertions(+), 15 deletions(-) create mode 100644 src/GUI/CanvasWidget.cxx create mode 100644 src/GUI/CanvasWidget.hxx diff --git a/src/Canvas/canvas.cxx b/src/Canvas/canvas.cxx index 7f2c85902..43878a124 100644 --- a/src/Canvas/canvas.cxx +++ b/src/Canvas/canvas.cxx @@ -18,11 +18,15 @@ #include "canvas.hxx" #include "elements/group.hxx" + #include +#include
+#include #include #include #include +#include #include @@ -97,13 +101,13 @@ Canvas::Canvas(): _status(0), _sampling_dirty(false), _color_dirty(true), - _node(0) + _node(0), + _render_always(false) { setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y); - CameraCullCallback *camera_callback = new CameraCullCallback; - _camera_callback = camera_callback; - _cull_callback = new PlacementCullCallback(this, camera_callback); + _camera_callback = new CameraCullCallback; + _cull_callback = new PlacementCullCallback(this, _camera_callback); } //------------------------------------------------------------------------------ @@ -254,6 +258,9 @@ void Canvas::update(double delta_time_sec) _cull_callback ); } + + if( _render_always ) + _camera_callback->enableRendering(); } //------------------------------------------------------------------------------ @@ -398,11 +405,38 @@ void Canvas::valueChanged(SGPropertyNode* node) || node->getNameString() == "coverage-samples" || node->getNameString() == "color-samples" ) _sampling_dirty = true; + else if( node->getNameString() == "render-always" ) + _render_always = node->getBoolValue(); } _root_group->valueChanged(node); } +//------------------------------------------------------------------------------ +GLuint Canvas::getTexId() const +{ + osg::Texture2D* tex = _texture.getTexture(); + if( !tex ) + return 0; + + osgViewer::Viewer::Contexts contexts; + globals->get_renderer()->getViewer()->getContexts(contexts); + + if( contexts.empty() ) + return 0; + + osg::State* state = contexts[0]->getState(); + if( !state ) + return 0; + + osg::Texture::TextureObject* tobj = + tex->getTextureObject( state->getContextID() ); + if( !tobj ) + return 0; + + return tobj->_id; +} + //------------------------------------------------------------------------------ void Canvas::setStatusFlags(unsigned int flags, bool set) { diff --git a/src/Canvas/canvas.hxx b/src/Canvas/canvas.hxx index 25f87012d..a10713bd4 100644 --- a/src/Canvas/canvas.hxx +++ b/src/Canvas/canvas.hxx @@ -31,6 +31,7 @@ namespace canvas class Group; } +class CameraCullCallback; class Canvas: public SGPropertyChangeListener { @@ -71,6 +72,8 @@ class Canvas: SGPropertyNode * child ); virtual void valueChanged (SGPropertyNode * node); + GLuint getTexId() const; + private: Canvas(const Canvas&); // = delete; @@ -93,8 +96,11 @@ class Canvas: SGPropertyNode_ptr _node; std::vector _color_background; - osg::ref_ptr _camera_callback; + osg::ref_ptr _camera_callback; osg::ref_ptr _cull_callback; + + bool _render_always; // _dirty_placements; std::vector _placements; diff --git a/src/Canvas/canvas_mgr.cxx b/src/Canvas/canvas_mgr.cxx index 72db55fd0..b8300654a 100644 --- a/src/Canvas/canvas_mgr.cxx +++ b/src/Canvas/canvas_mgr.cxx @@ -107,6 +107,16 @@ void CanvasMgr::childRemoved( SGPropertyNode * parent, } } +//------------------------------------------------------------------------------ +unsigned int CanvasMgr::getCanvasTexId(size_t index) const +{ + if( index >= _canvases.size() + || !_canvases[index] ) + return 0; + + return _canvases[index]->getTexId(); +} + //------------------------------------------------------------------------------ void CanvasMgr::textureAdded(SGPropertyNode* node) { @@ -119,10 +129,8 @@ void CanvasMgr::textureAdded(SGPropertyNode* node) _canvases.resize(index + 1); } - else - { + else if( _canvases[index] ) SG_LOG(SG_GL, SG_WARN, "texture[" << index << "] already exists!"); - } _canvases[index].reset( new Canvas() ); _canvases[index]->reset(node); diff --git a/src/Canvas/canvas_mgr.hxx b/src/Canvas/canvas_mgr.hxx index 893b50e88..d1ae92603 100644 --- a/src/Canvas/canvas_mgr.hxx +++ b/src/Canvas/canvas_mgr.hxx @@ -50,6 +50,14 @@ class CanvasMgr: virtual void childRemoved( SGPropertyNode * parent, SGPropertyNode * child ); + /** + * Get OpenGL texture name for given canvas + * + * @param Index of canvas + * @return OpenGL texture name + */ + unsigned int getCanvasTexId(size_t index) const; + private: /** Root node for everything concerning the canvas system */ diff --git a/src/GUI/CMakeLists.txt b/src/GUI/CMakeLists.txt index 0a7fe3fb0..b021d840b 100644 --- a/src/GUI/CMakeLists.txt +++ b/src/GUI/CMakeLists.txt @@ -2,6 +2,7 @@ include(FlightGearComponent) set(SOURCES AirportList.cxx + CanvasWidget.cxx MapWidget.cxx WaypointList.cxx dialog.cxx @@ -21,6 +22,7 @@ set(SOURCES set(HEADERS AirportList.hxx + CanvasWidget.hxx MapWidget.hxx WaypointList.hxx dialog.hxx diff --git a/src/GUI/CanvasWidget.cxx b/src/GUI/CanvasWidget.cxx new file mode 100644 index 000000000..f1647d496 --- /dev/null +++ b/src/GUI/CanvasWidget.cxx @@ -0,0 +1,168 @@ +/* + * CanvasWidget.cxx + * + * Created on: 03.07.2012 + * Author: tom + */ + +#include "CanvasWidget.hxx" + +#include +#include
// fgGetKeyModifiers() +#include + +//------------------------------------------------------------------------------ +CanvasWidget::CanvasWidget( int x, int y, + int width, int height, + SGPropertyNode* props, + const std::string& module ): + puObject(x, y, width, height), + _canvas_mgr( dynamic_cast(globals->get_subsystem("Canvas")) ), + _tex_id(0), + _no_tex_cnt(0) +{ + if( !_canvas_mgr ) + { + SG_LOG(SG_GENERAL, SG_ALERT, "CanvasWidget: failed to get canvas manager!"); + return; + } + + // Get the first unused canvas slot + SGPropertyNode* canvas_root = fgGetNode("/canvas", true); + for(int index = 0;; ++index) + { + if( !canvas_root->getChild("texture", index) ) + { + int view[2] = { + // Get canvas viewport size. If not specified use the widget dimensions + props->getIntValue("view[0]", width), + props->getIntValue("view[1]", height) + }; + _canvas = canvas_root->getChild("texture", index, true); + _canvas->setIntValue("size[0]", view[0] * 2); // use higher resolution + _canvas->setIntValue("size[1]", view[1] * 2); // for antialias + _canvas->setIntValue("view[0]", view[0]); + _canvas->setIntValue("view[1]", view[1]); + _canvas->setBoolValue("render-always", true); + _canvas->setStringValue( "name", + props->getStringValue("name", "gui-anonymous") ); + SGPropertyNode* input = _canvas->getChild("input", 0, true); + _mouse_x = input->getChild("mouse-x", 0, true); + _mouse_y = input->getChild("mouse-y", 0, true); + _mouse_down = input->getChild("mouse-down", 0, true); + _mouse_drag = input->getChild("mouse-drag", 0, true); + + SGPropertyNode *nasal = props->getNode("nasal"); + if( !nasal ) + break; + + FGNasalSys *nas = + dynamic_cast(globals->get_subsystem("nasal")); + if( !nas ) + SG_LOG( SG_GENERAL, + SG_ALERT, + "CanvasWidget: Failed to get nasal subsystem!" ); + + const std::string file = std::string("__canvas:") + + _canvas->getStringValue("name"); + + SGPropertyNode *load = nasal->getNode("load"); + if( load ) + { + const char *s = load->getStringValue(); + nas->handleCommand(module.c_str(), file.c_str(), s, _canvas); + } + break; + } + } +} + +//------------------------------------------------------------------------------ +CanvasWidget::~CanvasWidget() +{ + if( _canvas ) + _canvas->getParent() + ->removeChild(_canvas->getName(), _canvas->getIndex(), false); +} + +//------------------------------------------------------------------------------ +void CanvasWidget::doHit(int button, int updown, int x, int y) +{ + puObject::doHit(button, updown, x, y); + + // CTRL allows resizing and SHIFT allows moving the window + if( fgGetKeyModifiers() & (KEYMOD_CTRL | KEYMOD_SHIFT) ) + return; + + _mouse_x->setIntValue(x - abox.min[0]); + _mouse_y->setIntValue(y - abox.min[1]); + + if( updown == PU_DRAG ) + _mouse_drag->setIntValue(button); + else if( updown == PU_DOWN ) + _mouse_down->setIntValue(button); + + if( button != active_mouse_button ) + return; + + if (updown == PU_UP) + puDeactivateWidget(); + else if (updown == PU_DOWN) + puSetActiveWidget(this, x, y); +} + +//------------------------------------------------------------------------------ +int CanvasWidget::checkKey(int key, int updown) +{ + return puObject::checkKey(key, updown); +} + +//------------------------------------------------------------------------------ +void CanvasWidget::setSize(int w, int h) +{ + puObject::setSize(w, h); + + _canvas->setIntValue("view[0]", w); + _canvas->setIntValue("view[1]", h); +} + +//------------------------------------------------------------------------------ +void CanvasWidget::draw(int dx, int dy) +{ + if( !_tex_id ) + { + _tex_id = _canvas_mgr->getCanvasTexId(_canvas->getIndex()); + + // Normally we should be able to get the texture after one frame. I don't + // know if there are circumstances where it can take longer, so we don't + // log a warning message until we have tried a few times. + if( !_tex_id ) + { + if( ++_no_tex_cnt == 5 ) + SG_LOG(SG_GENERAL, SG_WARN, "CanvasWidget: failed to get texture!"); + return; + } + else + { + if( _no_tex_cnt >= 5 ) + SG_LOG + ( + SG_GENERAL, + SG_INFO, + "CanvasWidget: got texture after " << _no_tex_cnt << " tries." + ); + _no_tex_cnt = 0; + } + } + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, _tex_id); + glBegin( GL_QUADS ); + glColor3f(1,1,1); + glTexCoord2f(0,0); glVertex2f(dx + abox.min[0], dy + abox.min[1]); + glTexCoord2f(1,0); glVertex2f(dx + abox.max[0], dy + abox.min[1]); + glTexCoord2f(1,1); glVertex2f(dx + abox.max[0], dy + abox.max[1]); + glTexCoord2f(0,1); glVertex2f(dx + abox.min[0], dy + abox.max[1]); + glEnd(); + glDisable(GL_TEXTURE_2D); +} diff --git a/src/GUI/CanvasWidget.hxx b/src/GUI/CanvasWidget.hxx new file mode 100644 index 000000000..7f4552cd1 --- /dev/null +++ b/src/GUI/CanvasWidget.hxx @@ -0,0 +1,47 @@ +/* + * CanvasWidget.hxx + * + * Created on: 03.07.2012 + * Author: tom + */ + +#ifndef CANVASWIDGET_HXX_ +#define CANVASWIDGET_HXX_ + +#include
+#include + +class CanvasMgr; + +class CanvasWidget: + public puObject +{ + public: + CanvasWidget( int x, int y, + int width, int height, + SGPropertyNode* props, + const std::string& module ); + virtual ~CanvasWidget(); + + virtual void doHit (int button, int updown, int x, int y); + virtual int checkKey(int key , int updown); + + virtual void setSize ( int w, int h ); + virtual void draw(int dx, int dy); + + private: + + CanvasMgr *_canvas_mgr; // TODO maybe we should store this in some central + // location or make it static... + + GLuint _tex_id; //getBoolValue("editable", false)); diff --git a/src/Instrumentation/od_gauge.hxx b/src/Instrumentation/od_gauge.hxx index 902d20e6a..7d5ebf4ee 100644 --- a/src/Instrumentation/od_gauge.hxx +++ b/src/Instrumentation/od_gauge.hxx @@ -132,9 +132,9 @@ public: /** * Get the OSG camera for drawing this gauge. */ - osg::Camera* getCamera() { return camera.get(); } + osg::Camera* getCamera() const { return camera.get(); } - osg::Texture2D* getTexture() { return texture.get(); } + osg::Texture2D* getTexture() const { return texture.get(); } //void setTexture(osg::Texture2D* t) { texture = t; } // Real initialization function. Bad name. diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 85dbbd4d6..55d5581a5 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -1163,7 +1163,7 @@ bool fgInitSubsystems() { //////////////////////////////////////////////////////////////////// // Initialize the canvas 2d drawing subsystem. //////////////////////////////////////////////////////////////////// - globals->add_subsystem("Canvas2D", new CanvasMgr, SGSubsystemMgr::DISPLAY); + globals->add_subsystem("Canvas", new CanvasMgr, SGSubsystemMgr::DISPLAY); //////////////////////////////////////////////////////////////////// // Initialise the ATIS Manager diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index 1c3ec31cb..b65264a8b 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -816,11 +816,12 @@ naRef FGNasalSys::parse(const char* filename, const char* buf, int len) return naBindFunction(_context, code, _globals); } -bool FGNasalSys::handleCommand(const SGPropertyNode* arg) +bool FGNasalSys::handleCommand( const char* moduleName, + const char* fileName, + const char* src, + const SGPropertyNode* arg ) { - const char* nasal = arg->getStringValue("script"); - const char* moduleName = arg->getStringValue("module"); - naRef code = parse(arg->getPath(true).c_str(), nasal, strlen(nasal)); + naRef code = parse(fileName, src, strlen(src)); if(naIsNil(code)) return false; // Commands can be run "in" a module. Make sure that module @@ -845,6 +846,17 @@ bool FGNasalSys::handleCommand(const SGPropertyNode* arg) return true; } +bool FGNasalSys::handleCommand(const SGPropertyNode* arg) +{ + const char* src = arg->getStringValue("script"); + const char* moduleName = arg->getStringValue("module"); + + return handleCommand( moduleName, + arg ? arg->getPath(true).c_str() : moduleName, + src, + arg ); +} + // settimer(func, dt, simtime) extension function. The first argument // is a Nasal function to call, the second is a delta time (from now), // in seconds. The third, if present, is a boolean value indicating diff --git a/src/Scripting/NasalSys.hxx b/src/Scripting/NasalSys.hxx index 5ca64f109..0dad5fd53 100644 --- a/src/Scripting/NasalSys.hxx +++ b/src/Scripting/NasalSys.hxx @@ -102,6 +102,10 @@ public: naRef cmdArgGhost(); // Callbacks for command and timer bindings + virtual bool handleCommand( const char* moduleName, + const char* fileName, + const char* src, + const SGPropertyNode* arg = 0 ); virtual bool handleCommand(const SGPropertyNode* arg); bool createModule(const char* moduleName, const char* fileName, -- 2.39.5