]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/elements/CanvasImage.cxx
Helpers to access view position.
[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::CameraCullCallbackWeakPtr _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   if( _camera_cull.valid() )
66     _camera_cull->enableRendering();
67
68   // TODO check if window/image should be culled
69   return false;
70 }
71
72 namespace canvas
73 {
74   //----------------------------------------------------------------------------
75   Image::Image(SGPropertyNode_ptr node, const Style& parent_style):
76     Element(node, parent_style),
77     _texture(new osg::Texture2D),
78     _node_src_rect( node->getNode("source", 0, true) )
79   {
80     _geom = new osg::Geometry;
81     _geom->setUseDisplayList(false);
82
83     osg::StateSet *stateSet = _geom->getOrCreateStateSet();
84     stateSet->setTextureAttributeAndModes(0, _texture.get());
85     stateSet->setDataVariance(osg::Object::STATIC);
86
87     // allocate arrays for the image
88     _vertices = new osg::Vec3Array(4);
89     _vertices->setDataVariance(osg::Object::STATIC);
90     _geom->setVertexArray(_vertices);
91
92     _texCoords = new osg::Vec2Array(4);
93     _texCoords->setDataVariance(osg::Object::STATIC);
94     _geom->setTexCoordArray(0, _texCoords);
95
96     _colors = new osg::Vec4Array(4);
97     _colors->setDataVariance(osg::Object::STATIC);
98     _geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
99     _geom->setColorArray(_colors);
100
101     osg::DrawArrays* prim = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
102     prim->set(osg::PrimitiveSet::QUADS, 0, 4);
103     prim->setDataVariance(osg::Object::STATIC);
104     _geom->addPrimitiveSet(prim);
105
106     setDrawable(_geom);
107
108     addStyle("fill", &Image::setFill, this);
109     setFill("#ffffff"); // TODO how should we handle default values?
110
111     setupStyle();
112   }
113
114   //----------------------------------------------------------------------------
115   Image::~Image()
116   {
117
118   }
119
120   //----------------------------------------------------------------------------
121   void Image::update(double dt)
122   {
123     Element::update(dt);
124
125     if( _attributes_dirty & DEST_SIZE )
126     {
127       (*_vertices)[0].set(_region.l(), _region.t(), 0);
128       (*_vertices)[1].set(_region.r(), _region.t(), 0);
129       (*_vertices)[2].set(_region.r(), _region.b(), 0);
130       (*_vertices)[3].set(_region.l(), _region.b(), 0);
131       _vertices->dirty();
132
133       _attributes_dirty &= ~DEST_SIZE;
134       _geom->dirtyBound();
135       setBoundingBox(_geom->getBound());
136     }
137
138     if( _attributes_dirty & SRC_RECT )
139     {
140       double u0 = _src_rect.l(),
141              u1 = _src_rect.r(),
142              v0 = _src_rect.b(),
143              v1 = _src_rect.t();
144
145       if( !_node_src_rect->getBoolValue("normalized", true) )
146       {
147         const Rect<int>& tex_dim = getTextureDimensions();
148
149         u0 /= tex_dim.width();
150         u1 /= tex_dim.width();
151         v0 /= tex_dim.height();
152         v1 /= tex_dim.height();
153       }
154
155       (*_texCoords)[0].set(u0, v0);
156       (*_texCoords)[1].set(u1, v0);
157       (*_texCoords)[2].set(u1, v1);
158       (*_texCoords)[3].set(u0, v1);
159       _texCoords->dirty();
160
161       _attributes_dirty &= ~SRC_RECT;
162     }
163   }
164
165   //----------------------------------------------------------------------------
166   void Image::setCanvas(CanvasPtr canvas)
167   {
168     _canvas = canvas;
169     _geom->getOrCreateStateSet()
170          ->setTextureAttribute(0, canvas ? canvas->getTexture() : 0);
171     _geom->setCullCallback(
172       canvas ? new CullCallback(canvas->getCameraCullCallback()) : 0
173     );
174
175     if( !_canvas.expired() )
176       setupDefaultDimensions();
177   }
178
179   //----------------------------------------------------------------------------
180   CanvasWeakPtr Image::getCanvas() const
181   {
182     return _canvas;
183   }
184
185   //----------------------------------------------------------------------------
186   void Image::setImage(osg::Image *img)
187   {
188     // remove canvas...
189     setCanvas( CanvasPtr() );
190
191     _texture->setImage(img);
192     _geom->getOrCreateStateSet()
193          ->setTextureAttributeAndModes(0, _texture);
194
195     if( img )
196       setupDefaultDimensions();
197   }
198
199   //----------------------------------------------------------------------------
200   void Image::setFill(const std::string& fill)
201   {
202     osg::Vec4 color = parseColor(fill);
203     for( int i = 0; i < 4; ++i )
204       (*_colors)[i] = color;
205     _colors->dirty();
206   }
207
208   //----------------------------------------------------------------------------
209   const Rect<float>& Image::getRegion() const
210   {
211     return _region;
212   }
213
214   //----------------------------------------------------------------------------
215   void Image::childChanged(SGPropertyNode* child)
216   {
217     const std::string& name = child->getNameString();
218
219     if( child->getParent() == _node_src_rect )
220     {
221       _attributes_dirty |= SRC_RECT;
222
223       if(      name == "left" )
224         _src_rect.setLeft( child->getFloatValue() );
225       else if( name == "right" )
226         _src_rect.setRight( child->getFloatValue() );
227       else if( name == "top" )
228         _src_rect.setTop( child->getFloatValue() );
229       else if( name == "bottom" )
230         _src_rect.setBottom( child->getFloatValue() );
231
232       return;
233     }
234     else if( child->getParent() != _node )
235       return;
236
237     if( name == "x" )
238     {
239       _region.setX( child->getFloatValue() );
240       _attributes_dirty |= DEST_SIZE;
241     }
242     else if( name == "y" )
243     {
244       _region.setY( child->getFloatValue() );
245       _attributes_dirty |= DEST_SIZE;
246     }
247     else if( name == "size" )
248     {
249       if( child->getIndex() == 0 )
250         _region.setWidth( child->getFloatValue() );
251       else
252         _region.setHeight( child->getFloatValue() );
253
254       _attributes_dirty |= DEST_SIZE;
255     }
256     else if( name == "file" )
257     {
258       static const std::string CANVAS_PROTOCOL = "canvas://";
259       const std::string& path = child->getStringValue();
260
261       if( boost::starts_with(path, CANVAS_PROTOCOL) )
262       {
263         CanvasMgr* canvas_mgr =
264           dynamic_cast<CanvasMgr*>(globals->get_subsystem("Canvas"));
265         if( !canvas_mgr )
266         {
267           SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Failed to get CanvasMgr");
268           return;
269         }
270
271         const SGPropertyNode* canvas_node =
272           canvas_mgr->getPropertyRoot()
273                     ->getParent()
274                     ->getNode( path.substr(CANVAS_PROTOCOL.size()) );
275         if( !canvas_node )
276         {
277           SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such canvas: " << path);
278           return;
279         }
280
281         // TODO add support for other means of addressing canvases (eg. by
282         // name)
283         CanvasPtr canvas = canvas_mgr->getCanvas( canvas_node->getIndex() );
284         if( !canvas )
285         {
286           SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Invalid canvas: " << path);
287           return;
288         }
289
290         setCanvas(canvas);
291       }
292       else
293       {
294         SGPath tpath = globals->resolve_ressource_path(path);
295         if( tpath.isNull() || !tpath.exists() )
296         {
297           SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such image: " << path);
298           return;
299         }
300
301         setImage( osgDB::readImageFile(tpath.c_str()) );
302       }
303     }
304   }
305
306   //----------------------------------------------------------------------------
307   void Image::setupDefaultDimensions()
308   {
309     if( !_src_rect.width() || !_src_rect.height() )
310     {
311       const Rect<int>& tex_dim = getTextureDimensions();
312
313       _node_src_rect->setBoolValue("normalized", false);
314       _node_src_rect->setFloatValue("right", tex_dim.width());
315       _node_src_rect->setFloatValue("bottom", tex_dim.height());
316     }
317
318     if( !_region.width() || !_region.height() )
319     {
320       _node->setFloatValue("size[0]", _src_rect.width());
321       _node->setFloatValue("size[1]", _src_rect.height());
322     }
323   }
324
325   //----------------------------------------------------------------------------
326   Rect<int> Image::getTextureDimensions() const
327   {
328     osg::Texture2D *texture = !_canvas.expired()
329                               ? _canvas.lock()->getTexture()
330                               : _texture.get();
331
332     if( !texture )
333       return Rect<int>();
334
335     return Rect<int>
336     (
337       0,0,
338       texture->getTextureWidth(),
339       texture->getTextureHeight()
340     );
341   }
342
343 } // namespace canvas