1 // An image on the canvas
3 // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
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.
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.
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.
19 #include "CanvasImage.hxx"
21 #include <osgDB/ReadFile>
23 #include <Canvas/canvas.hxx>
24 #include <Canvas/canvas_mgr.hxx>
25 #include <Canvas/property_helper.hxx>
27 #include <osg/Geometry>
28 #include <osg/PrimitiveSet>
30 #include <Main/globals.hxx>
31 #include <Main/fg_props.hxx>
33 #include <boost/algorithm/string/predicate.hpp>
36 * Callback to enable/disable rendering of canvas displayed inside windows or
40 public osg::Drawable::CullCallback
43 CullCallback(Canvas::CameraCullCallback* camera_cull);
46 Canvas::CameraCullCallbackWeakPtr _camera_cull;
48 virtual bool cull( osg::NodeVisitor* nv,
49 osg::Drawable* drawable,
50 osg::RenderInfo* renderInfo ) const;
53 //------------------------------------------------------------------------------
54 CullCallback::CullCallback(Canvas::CameraCullCallback* camera_cull):
55 _camera_cull( camera_cull )
60 //------------------------------------------------------------------------------
61 bool CullCallback::cull( osg::NodeVisitor* nv,
62 osg::Drawable* drawable,
63 osg::RenderInfo* renderInfo ) const
65 if( _camera_cull.valid() )
66 _camera_cull->enableRendering();
68 // TODO check if window/image should be culled
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) )
80 _geom = new osg::Geometry;
81 _geom->setUseDisplayList(false);
83 osg::StateSet *stateSet = _geom->getOrCreateStateSet();
84 stateSet->setTextureAttributeAndModes(0, _texture.get());
85 stateSet->setDataVariance(osg::Object::STATIC);
87 // allocate arrays for the image
88 _vertices = new osg::Vec3Array(4);
89 _vertices->setDataVariance(osg::Object::STATIC);
90 _geom->setVertexArray(_vertices);
92 _texCoords = new osg::Vec2Array(4);
93 _texCoords->setDataVariance(osg::Object::STATIC);
94 _geom->setTexCoordArray(0, _texCoords);
96 _colors = new osg::Vec4Array(4);
97 _colors->setDataVariance(osg::Object::STATIC);
98 _geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
99 _geom->setColorArray(_colors);
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);
108 addStyle("fill", &Image::setFill, this);
109 setFill("#ffffff"); // TODO how should we handle default values?
114 //----------------------------------------------------------------------------
120 //----------------------------------------------------------------------------
121 void Image::update(double dt)
125 if( _attributes_dirty & DEST_SIZE )
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);
133 _attributes_dirty &= ~DEST_SIZE;
135 setBoundingBox(_geom->getBound());
138 if( _attributes_dirty & SRC_RECT )
140 double u0 = _src_rect.l(),
145 if( !_node_src_rect->getBoolValue("normalized", true) )
147 const Rect<int>& tex_dim = getTextureDimensions();
149 u0 /= tex_dim.width();
150 u1 /= tex_dim.width();
151 v0 /= tex_dim.height();
152 v1 /= tex_dim.height();
155 (*_texCoords)[0].set(u0, v0);
156 (*_texCoords)[1].set(u1, v0);
157 (*_texCoords)[2].set(u1, v1);
158 (*_texCoords)[3].set(u0, v1);
161 _attributes_dirty &= ~SRC_RECT;
165 //----------------------------------------------------------------------------
166 void Image::setCanvas(CanvasPtr canvas)
169 _geom->getOrCreateStateSet()
170 ->setTextureAttribute(0, canvas ? canvas->getTexture() : 0);
171 _geom->setCullCallback(
172 canvas ? new CullCallback(canvas->getCameraCullCallback()) : 0
175 if( !_canvas.expired() )
176 setupDefaultDimensions();
179 //----------------------------------------------------------------------------
180 CanvasWeakPtr Image::getCanvas() const
185 //----------------------------------------------------------------------------
186 void Image::setImage(osg::Image *img)
189 setCanvas( CanvasPtr() );
191 _texture->setImage(img);
192 _geom->getOrCreateStateSet()
193 ->setTextureAttributeAndModes(0, _texture);
196 setupDefaultDimensions();
199 //----------------------------------------------------------------------------
200 void Image::setFill(const std::string& fill)
202 osg::Vec4 color = parseColor(fill);
203 for( int i = 0; i < 4; ++i )
204 (*_colors)[i] = color;
208 //----------------------------------------------------------------------------
209 const Rect<float>& Image::getRegion() const
214 //----------------------------------------------------------------------------
215 void Image::childChanged(SGPropertyNode* child)
217 const std::string& name = child->getNameString();
219 if( child->getParent() == _node_src_rect )
221 _attributes_dirty |= SRC_RECT;
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() );
234 else if( child->getParent() != _node )
239 _region.setX( child->getFloatValue() );
240 _attributes_dirty |= DEST_SIZE;
242 else if( name == "y" )
244 _region.setY( child->getFloatValue() );
245 _attributes_dirty |= DEST_SIZE;
247 else if( name == "size" )
249 if( child->getIndex() == 0 )
250 _region.setWidth( child->getFloatValue() );
252 _region.setHeight( child->getFloatValue() );
254 _attributes_dirty |= DEST_SIZE;
256 else if( name == "file" )
258 static const std::string CANVAS_PROTOCOL = "canvas://";
259 const std::string& path = child->getStringValue();
261 if( boost::starts_with(path, CANVAS_PROTOCOL) )
263 CanvasMgr* canvas_mgr =
264 dynamic_cast<CanvasMgr*>(globals->get_subsystem("Canvas"));
267 SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Failed to get CanvasMgr");
271 const SGPropertyNode* canvas_node =
272 canvas_mgr->getPropertyRoot()
274 ->getNode( path.substr(CANVAS_PROTOCOL.size()) );
277 SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such canvas: " << path);
281 // TODO add support for other means of addressing canvases (eg. by
283 CanvasPtr canvas = canvas_mgr->getCanvas( canvas_node->getIndex() );
286 SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Invalid canvas: " << path);
294 SGPath tpath = globals->resolve_resource_path(path);
295 if( tpath.isNull() || !tpath.exists() )
297 SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such image: " << path);
301 setImage( osgDB::readImageFile(tpath.c_str()) );
306 //----------------------------------------------------------------------------
307 void Image::setupDefaultDimensions()
309 if( !_src_rect.width() || !_src_rect.height() )
311 const Rect<int>& tex_dim = getTextureDimensions();
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());
318 if( !_region.width() || !_region.height() )
320 _node->setFloatValue("size[0]", _src_rect.width());
321 _node->setFloatValue("size[1]", _src_rect.height());
325 //----------------------------------------------------------------------------
326 Rect<int> Image::getTextureDimensions() const
328 osg::Texture2D *texture = !_canvas.expired()
329 ? _canvas.lock()->getTexture()
338 texture->getTextureWidth(),
339 texture->getTextureHeight()
343 } // namespace canvas