]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/elements/CanvasImage.cxx
Canvas: Forward mouse events to elements.
[flightgear.git] / src / Canvas / elements / CanvasImage.cxx
1 // An image on the canvas
2 //
3 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
19 #include "CanvasImage.hxx"
20
21 #include <osgDB/ReadFile>
22
23 #include <Canvas/canvas.hxx>
24 #include <Canvas/canvas_mgr.hxx>
25 #include <Canvas/property_helper.hxx>
26 #include <osg/Array>
27 #include <osg/Geometry>
28 #include <osg/PrimitiveSet>
29
30 #include <Main/globals.hxx>
31 #include <Main/fg_props.hxx>
32
33 #include <boost/algorithm/string/predicate.hpp>
34
35 /**
36  * Callback to enable/disable rendering of canvas displayed inside windows or
37  * other canvases.
38  */
39 class CullCallback:
40   public osg::Drawable::CullCallback
41 {
42   public:
43     CullCallback(Canvas::CameraCullCallback* camera_cull);
44
45   private:
46     Canvas::CameraCullCallback *_camera_cull;
47
48     virtual bool cull( osg::NodeVisitor* nv,
49                        osg::Drawable* drawable,
50                        osg::RenderInfo* renderInfo ) const;
51 };
52
53 //------------------------------------------------------------------------------
54 CullCallback::CullCallback(Canvas::CameraCullCallback* camera_cull):
55   _camera_cull( camera_cull )
56 {
57
58 }
59
60 //------------------------------------------------------------------------------
61 bool CullCallback::cull( osg::NodeVisitor* nv,
62                          osg::Drawable* drawable,
63                          osg::RenderInfo* renderInfo ) const
64 {
65   _camera_cull->enableRendering();
66
67   // TODO check if window/image should be culled
68   return false;
69 }
70
71 namespace canvas
72 {
73   //----------------------------------------------------------------------------
74   Image::Image(SGPropertyNode_ptr node):
75     Element(node, COLOR_FILL | BOUNDING_BOX),
76     _texture(new osg::Texture2D),
77     _node_src_rect( node->getNode("source", 0, true) )
78   {
79     _geom = new osg::Geometry;
80     _geom->setUseDisplayList(false);
81
82     osg::StateSet *stateSet = _geom->getOrCreateStateSet();
83     stateSet->setTextureAttributeAndModes(0, _texture.get());
84     stateSet->setDataVariance(osg::Object::STATIC);
85
86     // allocate arrays for the image
87     _vertices = new osg::Vec3Array(4);
88     _vertices->setDataVariance(osg::Object::STATIC);
89     _geom->setVertexArray(_vertices);
90
91     _texCoords = new osg::Vec2Array(4);
92     _texCoords->setDataVariance(osg::Object::STATIC);
93     _geom->setTexCoordArray(0, _texCoords);
94
95     _colors = new osg::Vec4Array(4);
96     _colors->setDataVariance(osg::Object::STATIC);
97     _geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
98     _geom->setColorArray(_colors);
99
100     osg::DrawArrays* prim = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
101     prim->set(osg::PrimitiveSet::QUADS, 0, 4);
102     prim->setDataVariance(osg::Object::STATIC);
103     _geom->addPrimitiveSet(prim);
104
105     setDrawable(_geom);
106   }
107
108   //----------------------------------------------------------------------------
109   Image::~Image()
110   {
111
112   }
113
114   //----------------------------------------------------------------------------
115   void Image::update(double dt)
116   {
117     Element::update(dt);
118
119     if( _attributes_dirty & DEST_SIZE )
120     {
121       (*_vertices)[0].set(_region.l(), _region.t(), 0);
122       (*_vertices)[1].set(_region.r(), _region.t(), 0);
123       (*_vertices)[2].set(_region.r(), _region.b(), 0);
124       (*_vertices)[3].set(_region.l(), _region.b(), 0);
125       _vertices->dirty();
126
127       _attributes_dirty &= ~DEST_SIZE;
128       _geom->dirtyBound();
129     }
130
131     if( _attributes_dirty & SRC_RECT )
132     {
133       double u0 = _src_rect.l(),
134              u1 = _src_rect.r(),
135              v0 = _src_rect.b(),
136              v1 = _src_rect.t();
137
138       if( !_node_src_rect->getBoolValue("normalized", true) )
139       {
140         const Rect<int>& tex_dim = getTextureDimensions();
141
142         u0 /= tex_dim.width();
143         u1 /= tex_dim.width();
144         v0 /= tex_dim.height();
145         v1 /= tex_dim.height();
146       }
147
148       (*_texCoords)[0].set(u0, v0);
149       (*_texCoords)[1].set(u1, v0);
150       (*_texCoords)[2].set(u1, v1);
151       (*_texCoords)[3].set(u0, v1);
152       _texCoords->dirty();
153
154       _attributes_dirty &= ~SRC_RECT;
155     }
156   }
157
158   //----------------------------------------------------------------------------
159   void Image::setCanvas(CanvasPtr canvas)
160   {
161     _canvas = canvas;
162     _geom->getOrCreateStateSet()
163          ->setTextureAttribute(0, canvas ? canvas->getTexture() : 0);
164     _geom->setCullCallback(
165       canvas ? new CullCallback(canvas->getCameraCullCallback()) : 0
166     );
167
168     if( !_canvas.expired() )
169       setupDefaultDimensions();
170   }
171
172   //----------------------------------------------------------------------------
173   CanvasWeakPtr Image::getCanvas() const
174   {
175     return _canvas;
176   }
177
178   //----------------------------------------------------------------------------
179   void Image::setImage(osg::Image *img)
180   {
181     // remove canvas...
182     setCanvas( CanvasPtr() );
183
184     _texture->setImage(img);
185     _geom->getOrCreateStateSet()
186          ->setTextureAttributeAndModes(0, _texture);
187
188     if( img )
189       setupDefaultDimensions();
190   }
191
192   //----------------------------------------------------------------------------
193   const Rect<float>& Image::getRegion() const
194   {
195     return _region;
196   }
197
198   //----------------------------------------------------------------------------
199   void Image::valueChanged(SGPropertyNode *node)
200   {
201     if( node->getParent() == _node_src_rect )
202     {
203       _attributes_dirty |= SRC_RECT;
204
205       if( node->getNameString() == "left" )
206         _src_rect.setLeft( node->getFloatValue() );
207       else if( node->getNameString() == "right" )
208         _src_rect.setRight( node->getFloatValue() );
209       else if( node->getNameString() == "top" )
210         _src_rect.setTop( node->getFloatValue() );
211       else if( node->getNameString() == "bottom" )
212         _src_rect.setBottom( node->getFloatValue() );
213     }
214     else
215       Element::valueChanged(node);
216   }
217
218   //----------------------------------------------------------------------------
219   void Image::childChanged(SGPropertyNode* child)
220   {
221     const std::string& name = child->getNameString();
222
223     if( name == "x" )
224     {
225       _region.setX( child->getFloatValue() );
226       _attributes_dirty |= DEST_SIZE;
227     }
228     else if( name == "y" )
229     {
230       _region.setY( child->getFloatValue() );
231       _attributes_dirty |= DEST_SIZE;
232     }
233     else if( name == "size" )
234     {
235       if( child->getIndex() == 0 )
236         _region.setWidth( child->getFloatValue() );
237       else
238         _region.setHeight( child->getFloatValue() );
239
240       _attributes_dirty |= DEST_SIZE;
241     }
242     else if( name == "file" )
243     {
244       static const std::string CANVAS_PROTOCOL = "canvas://";
245       const std::string& path = child->getStringValue();
246
247       if( boost::starts_with(path, CANVAS_PROTOCOL) )
248       {
249         CanvasMgr* canvas_mgr =
250           dynamic_cast<CanvasMgr*>(globals->get_subsystem("Canvas"));
251         if( !canvas_mgr )
252         {
253           SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Failed to get CanvasMgr");
254           return;
255         }
256
257         const SGPropertyNode* canvas_node =
258           canvas_mgr->getPropertyRoot()
259                     ->getParent()
260                     ->getNode( path.substr(CANVAS_PROTOCOL.size()) );
261         if( !canvas_node )
262         {
263           SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such canvas: " << path);
264           return;
265         }
266
267         // TODO add support for other means of addressing canvases (eg. by
268         // name)
269         CanvasPtr canvas = canvas_mgr->getCanvas( canvas_node->getIndex() );
270         if( !canvas )
271         {
272           SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Invalid canvas: " << path);
273           return;
274         }
275
276         setCanvas(canvas);
277       }
278       else
279       {
280         SGPath tpath = globals->resolve_ressource_path(path);
281         if( tpath.isNull() || !tpath.exists() )
282         {
283           SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such image: " << path);
284           return;
285         }
286
287         setImage( osgDB::readImageFile(tpath.c_str()) );
288       }
289     }
290   }
291
292   //----------------------------------------------------------------------------
293   void Image::colorFillChanged(const osg::Vec4& color)
294   {
295     for( int i = 0; i < 4; ++i )
296       (*_colors)[i] = color;
297     _colors->dirty();
298   }
299
300   //----------------------------------------------------------------------------
301   void Image::setupDefaultDimensions()
302   {
303     if( !_src_rect.width() || !_src_rect.height() )
304     {
305       const Rect<int>& tex_dim = getTextureDimensions();
306
307       _node_src_rect->setBoolValue("normalized", false);
308       _node_src_rect->setFloatValue("right", tex_dim.width());
309       _node_src_rect->setFloatValue("bottom", tex_dim.height());
310     }
311
312     if( !_region.width() || !_region.height() )
313     {
314       _node->setFloatValue("size[0]", _src_rect.width());
315       _node->setFloatValue("size[1]", _src_rect.height());
316     }
317   }
318
319   //----------------------------------------------------------------------------
320   Rect<int> Image::getTextureDimensions() const
321   {
322     osg::Texture2D *texture = !_canvas.expired()
323                               ? _canvas.lock()->getTexture()
324                               : _texture.get();
325
326     return Rect<int>
327     (
328       0,0,
329       texture->getTextureWidth(),
330       texture->getTextureHeight()
331     );
332   }
333
334 } // namespace canvas