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