OpenWalnut  1.4.0
WGEColormapping.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <algorithm>
26 #include <iostream>
27 #include <sstream>
28 #include <string>
29 
30 #include "../common/WLogger.h"
31 #include "../common/WStringUtils.h"
32 
33 #include "WGETextureUtils.h"
34 #include "exceptions/WGESignalSubscriptionFailed.h"
35 
36 #include "WGEColormapping.h"
37 
38 // instance as singleton
39 boost::shared_ptr< WGEColormapping > WGEColormapping::m_instance = boost::shared_ptr< WGEColormapping >();
40 
41 /**
42  * This functions simply sets some defines to a shader. It sets the texture unit and gl_MultiTexCoord variable names properly.
43  *
44  * \param shader the shader where to add the defines
45  * \param start the start index of the unit for colormap0
46  */
47 void setDefines( osg::ref_ptr< WGEShader > shader, size_t start = 0 )
48 {
49  // simply set some proper defines for each colormap -> the unit and multitex coords
50  for( size_t unit = 0; unit < wge::getMaxTexUnits(); ++unit )
51  {
52  // disable textures with invalid unit numbers
53  if( unit < wge::getMaxTexUnits() - start )
54  {
55  shader->setDefine( "Colormap" + string_utils::toString( unit ) + "Enabled", true );
56  shader->setDefine( "Colormap" + string_utils::toString( unit ) + "Unit", start + unit );
57  }
58  }
59 }
60 
61 /**
62  * This functions simply sets the specified pre transformation matrix to the shader. It therefore uses a preprocessor define. This allows a
63  * hard-coded matrix to be optimized be the shader compiler.
64  *
65  * \param shader the shader where to add the defines
66  * \param preTransform the transformation matrix used to pre-multiply with all texture coordinates
67  */
68 void setPreTransform( osg::ref_ptr< WGEShader > shader, osg::Matrixd preTransform )
69 {
70  std::ostringstream out;
71  out << "mat4( ";
72  const osg::Matrixd::value_type* m = preTransform.ptr();
73 
74  out.precision( 10 );
75  out.setf( std::ios::fixed, std::ios::floatfield );
76 
77  // print all 16 values
78  for( size_t i = 0; i < 15; ++i )
79  {
80  out << m[ i ] << ", ";
81  }
82  out << m[ 15 ] << " )";
83 
84  // set as define
85  shader->setDefine( "ColormapPreTransform", out.str() );
86 }
87 
89 {
90  // initialize members
91  m_textures.getChangeCondition()->subscribeSignal( boost::bind( &WGEColormapping::textureUpdate, this ) );
92  m_boundingBox.getWriteTicket()->get().set( 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 );
93 }
94 
96 {
97  // cleanup
98 }
99 
100 boost::shared_ptr< WGEColormapping > WGEColormapping::instance()
101 {
102  if( !m_instance )
103  {
104  m_instance = boost::shared_ptr< WGEColormapping >( new WGEColormapping() );
105  }
106 
107  return m_instance;
108 }
109 
110 void WGEColormapping::apply( osg::ref_ptr< osg::Node > node, osg::ref_ptr< WGEShader > shader, size_t startTexUnit )
111 {
112  instance()->applyInst( NodeList( 1, node ), WMatrix4d( WMatrix4d::identity() ), shader, startTexUnit );
113 }
114 
115 void WGEColormapping::apply( osg::ref_ptr< osg::Node > node, WMatrix4d preTransform, osg::ref_ptr< WGEShader > shader,
116  size_t startTexUnit )
117 {
118  instance()->applyInst( NodeList( 1, node ), preTransform, shader, startTexUnit );
119 }
120 
121 void WGEColormapping::apply( NodeList nodes, WMatrix4d preTransform, osg::ref_ptr< WGEShader > shader, size_t startTexUnit )
122 {
123  instance()->applyInst( nodes, preTransform, shader, startTexUnit );
124 }
125 
126 void WGEColormapping::apply( NodeList nodes, osg::ref_ptr< WGEShader > shader, size_t startTexUnit )
127 {
128  instance()->applyInst( nodes, WMatrix4d( WMatrix4d::identity() ), shader, startTexUnit );
129 }
130 
131 void WGEColormapping::registerTexture( osg::ref_ptr< WGETexture3D > texture, std::string name )
132 {
133  instance()->registerTextureInst( texture, name );
134 }
135 
136 void WGEColormapping::deregisterTexture( osg::ref_ptr< WGETexture3D > texture )
137 {
138  instance()->deregisterTextureInst( texture );
139 }
140 
141 void WGEColormapping::replaceTexture( osg::ref_ptr< WGETexture3D > old, osg::ref_ptr< WGETexture3D > newTex, std::string name )
142 {
143  instance()->replaceTextureInst( old, newTex, name );
144 }
145 
146 void WGEColormapping::applyInst( NodeList nodes, WMatrix4d preTransform, osg::ref_ptr< WGEShader > shader,
147  size_t startTexUnit )
148 {
149  // init shader
150  osg::ref_ptr< WGEShader > s = shader;
151  if( !s )
152  {
153  // we use a new instance of the default shader here because the preTransform is varying between several nodes.
154  s = new WGEShader( "WGEDefaultColormapper" );
155  }
156  setDefines( s, startTexUnit );
157  setPreTransform( s, preTransform );
158 
159  // do this for each node
160  for( NodeList::const_iterator i = nodes.begin(); i != nodes.end(); ++i )
161  {
162  // applying to a node simply means adding a callback :-)
163  NodeInfo* info = new NodeInfo;
164  info->m_rebind = true;
165  info->m_texUnitStart = startTexUnit;
166  info->m_preTransform = preTransform;
167  m_nodeInfo.insert( std::make_pair( *i, info ) );
168 
169  ( *i )->addUpdateCallback( new WGEFunctorCallback< osg::Node >( boost::bind( &WGEColormapping::callback, this, _1 ) ) );
170 
171  // add the default shader if no other shader has been specified.
172  s->apply( *i );
173  }
174 }
175 
176 void WGEColormapping::registerTextureInst( osg::ref_ptr< WGETexture3D > texture, std::string name )
177 {
178  wlog::debug( "WGEColormapping" ) << "Registering texture \"" << name << "\".";
179  if( !m_textures.count( texture ) )
180  {
181  if( !name.empty() )
182  {
183  texture->name()->set( name );
184  }
185  m_textures.push_front( texture );
186  updateBounds();
187  m_registerSignal( texture );
188  }
189 }
190 
191 void WGEColormapping::deregisterTextureInst( osg::ref_ptr< WGETexture3D > texture )
192 {
193  wlog::debug( "WGEColormapping" ) << "De-registering texture \"" << texture->name()->get() << "\".";
194  if( m_textures.count( texture ) )
195  {
196  m_textures.remove( texture );
197  updateBounds();
198  m_deregisterSignal( texture );
199  }
200 }
201 
202 void WGEColormapping::replaceTextureInst( osg::ref_ptr< WGETexture3D > old, osg::ref_ptr< WGETexture3D > newTex, std::string name )
203 {
204  wlog::debug( "WGEColormapping" ) << "Replacing texture.";
205  if( !name.empty() )
206  {
207  newTex->name()->set( name );
208  }
209 
210  // if it exists, replace it
211  if( m_textures.count( old ) )
212  {
213  m_textures.replace( old, newTex );
214  updateBounds();
215  m_replaceSignal( old, newTex );
216  }
217  else // <- if not exists: add
218  {
219  registerTextureInst( newTex, name );
220  }
221 }
222 
224 {
227 
228  bool first = true;
229  for( TextureContainerType::ConstIterator iter = r->get().begin(); iter != r->get().end(); ++iter )
230  {
231  if( first )
232  {
233  bbw->get() = ( *iter )->getBoundingBox();
234  first = false;
235  }
236  else
237  {
238  bbw->get().expandBy( ( *iter )->getBoundingBox() );
239  }
240  }
241 }
242 
244 {
245  return m_boundingBox.getReadTicket()->get();
246 }
247 
249 {
251  for( NodeInfoContainerType::Iterator iter = w->get().begin(); iter != w->get().end(); ++iter )
252  {
253  iter->second->m_rebind = true;
254  }
255 }
256 
257 /**
258  * Custom comparator which uses a textures sortIndex for comparing.
259  *
260  * \param a first element
261  * \param b second element
262  *
263  * \return true if a's sortIndex is smaller than b's.
264  */
265 bool sortIndexComparator( osg::ref_ptr< WGETexture3D > a, osg::ref_ptr< WGETexture3D > b )
266 {
267  return ( a->sortIndex()->get() < b->sortIndex()->get() );
268 }
269 
271 {
272  // use sort with custom comparator
273  stableSort( &sortIndexComparator );
274 }
275 
277 {
279  size_t index = 0;
280  for( TextureContainerType::ConstIterator iter = r->get().begin(); iter != r->get().end(); ++iter )
281  {
282  ( *iter )->sortIndex()->set( index );
283  index++;
284  }
285 }
286 
288 {
290  for( TextureContainerType::ConstIterator iter = r->get().begin(); iter != r->get().end(); ++iter )
291  {
292  ( *iter )->sortIndex()->set( WGETexture3D::getUnsetSortIndex() );
293  }
294 }
295 
296 void WGEColormapping::callback( osg::Node* node )
297 {
298  // get node info
300  NodeInfoContainerType::ConstIterator infoItem = r->get().find( node );
301  if( infoItem == r->get().end() )
302  {
303  return;
304  }
305  r.reset();
306 
307  NodeInfo* info = infoItem->second;
308 
309  // need (re-)binding?
310  if( info->m_rebind )
311  {
312  info->m_rebind = false;
313 
314  size_t maxTexUnits = wge::getMaxTexUnits();
315  wge::unbindTexture( node, info->m_texUnitStart, maxTexUnits - info->m_texUnitStart );
316 
318 
319  // bind each texture, provide all needed uniforms too
320  size_t unit = info->m_texUnitStart;
321  for( TextureContainerType::ConstIterator iter = rt->get().begin();
322  ( unit < maxTexUnits ) && ( iter != rt->get().end() );
323  ++iter )
324  {
325  wge::bindTexture( node, *iter, unit, "u_colormap" + string_utils::toString( unit - info->m_texUnitStart ) );
326  unit++;
327  }
328 
329  rt.reset();
330  }
331 }
332 
333 bool WGEColormapping::moveDown( osg::ref_ptr< WGETexture3D > texture )
334 {
336 
337  // does the texture exist?
338  TextureContainerType::Iterator iter = std::find( w->get().begin(), w->get().end(), texture );
339  if( iter == w->get().end() )
340  {
341  return false;
342  }
343 
344  // is it already the last item?
345  if( iter + 1 == w->get().end() )
346  {
347  return false;
348  }
349 
350  // swap items
351  std::iter_swap( iter, iter + 1 );
352 
353  // unlock and call callbacks
354  w.reset();
355  m_sortSignal();
356 
357  return true;
358 }
359 
360 bool WGEColormapping::moveUp( osg::ref_ptr< WGETexture3D > texture )
361 {
363 
364  // does the texture exist?
365  TextureContainerType::Iterator iter = std::find( w->get().begin(), w->get().end(), texture );
366  if( iter == w->get().end() )
367  {
368  return false;
369  }
370 
371  // is it already the first item?
372  if( iter == w->get().begin() )
373  {
374  return false;
375  }
376 
377  // swap items
378  std::iter_swap( iter, iter - 1 );
379 
380  // unlock and call callbacks
381  w.reset();
382  m_sortSignal();
383 
384  return true;
385 }
386 
387 bool WGEColormapping::moveToTop( osg::ref_ptr< WGETexture3D > texture )
388 {
390 
391  // does the texture exist?
392  TextureContainerType::Iterator iter = std::find( w->get().begin(), w->get().end(), texture );
393  if( iter == w->get().end() )
394  {
395  return false;
396  }
397 
398  // is it already the first item?
399  if( iter == w->get().begin() )
400  {
401  return false;
402  }
403 
404  // do the op
405  w->get().erase( iter );
406  w->get().insert( w->get().begin(), texture );
407 
408  // unlock and call callbacks
409  w.reset();
410  m_sortSignal();
411 
412  return true;
413 }
414 
415 bool WGEColormapping::moveToBottom( osg::ref_ptr< WGETexture3D > texture )
416 {
418 
419  // does the texture exist?
420  TextureContainerType::Iterator iter = std::find( w->get().begin(), w->get().end(), texture );
421  if( iter == w->get().end() )
422  {
423  return false;
424  }
425 
426  // is it already the last item?
427  if( iter + 1 == w->get().end() )
428  {
429  return false;
430  }
431 
432  // do the op
433  w->get().erase( iter );
434  w->get().push_back( texture );
435 
436  // unlock and call callbacks
437  w.reset();
438  m_sortSignal();
439 
440  return true;
441 }
442 
443 bool WGEColormapping::moveTo( osg::ref_ptr< WGETexture3D > texture, size_t idx )
444 {
446 
447  // does the texture exist?
448  TextureContainerType::Iterator iter = std::find( w->get().begin(), w->get().end(), texture );
449  if( iter == w->get().end() )
450  {
451  return false;
452  }
453 
454  // valid index?
455  // NOTE: we accept index == size as the end iterator.
456  if( idx > w->get().size() )
457  {
458  return false;
459  }
460 
461  // is it already there?
462  if( iter == ( w->get().begin() + idx ) )
463  {
464  return false;
465  }
466 
467  // after inserting the item somewhere, the index of the original item might change
468  size_t eraseIdx = iter - w->get().begin(); // item is inserted behind the current one -> index of the original item stays the same
469  size_t eraseShift = 0;
470  // if the inserted element is in front of the old one, the old one's index is increasing
471  if( ( w->get().begin() + idx ) < iter )
472  {
473  eraseShift++;
474  }
475 
476  // do the op
477  // NOTE: this is not the best way to do it. Manually moving items should be better. But as the colormapper has to handle only a small number
478  // of elements, this is not critical.
479  w->get().insert( w->get().begin() + idx, texture );
480  w->get().erase( w->get().begin() + eraseIdx + eraseShift );
481 
482  // unlock and call callbacks
483  w.reset();
484  m_sortSignal();
485  return true;
486 }
487 
488 size_t WGEColormapping::size() const
489 {
490  return m_textures.size();
491 }
492 
493 boost::signals2::connection WGEColormapping::subscribeSignal( TextureListSignal signal, TextureRegisterHandler notifier )
494 {
495  switch( signal )
496  {
497  case Registered:
498  return m_registerSignal.connect( notifier );
499  case Deregistered:
500  return m_deregisterSignal.connect( notifier );
501  default:
502  throw new WGESignalSubscriptionFailed( std::string( "Could not register TextureRegisterHandler to sort signal." ) );
503  }
504 }
505 
506 boost::signals2::connection WGEColormapping::subscribeSignal( TextureListSignal signal, TextureReplaceHandler notifier )
507 {
508  switch( signal )
509  {
510  case Replaced:
511  return m_replaceSignal.connect( notifier );
512  default:
513  throw new WGESignalSubscriptionFailed( std::string( "Could not register TextureReplaceHandler to signal." ) );
514  }
515 }
516 
517 boost::signals2::connection WGEColormapping::subscribeSignal( TextureListSignal signal, TextureSortHandler notifier )
518 {
519  switch( signal )
520  {
521  case Sorted:
522  return m_sortSignal.connect( notifier );
523  default:
524  throw new WGESignalSubscriptionFailed( std::string( "Could not register TextureSortHandler to register/deregister signal." ) );
525  }
526 }
527 
529 {
530  return m_textures.getReadTicket();
531 }
532 
534 {
536 }
537 
boost::shared_ptr< WSharedObjectTicketWrite< T > > WriteTicket
Type for write tickets.
Definition: WSharedObject.h:67
bool moveUp(osg::ref_ptr< WGETexture3D > texture)
Move the specified texture one item up in the list.
TextureContainerType m_textures
The textures managed by this instance.
WGEColormapping()
Default constructor.
static void registerTexture(osg::ref_ptr< WGETexture3D > texture, std::string name="")
Register the specified texture to the colormapper.
size_t size() const
Counts the number of textures in the colormapper.
static WPVBaseTypes::PV_INT getUnsetSortIndex()
Get the index used to refer to an unset sort index.
Definition: WGETexture.h:751
std::pair< Iterator, bool > insert(const value_type &x)
Inserts the specified element.
boost::function< void(void) > TextureSortHandler
The type of handler called whenever the texture list got resorted.
void deregisterTextureInst(osg::ref_ptr< WGETexture3D > texture)
De-register the specified texture to the colormapper.
size_t size() const
The size of the container.
size_t m_texUnitStart
the start index of the texture unit to use
static void deregisterTexture(osg::ref_ptr< WGETexture3D > texture)
De-register the specified texture to the colormapper.
boost::function< void(osg::ref_ptr< WGETexture3D >, osg::ref_ptr< WGETexture3D >) > TextureReplaceHandler
The type of handler used for being notified about replaced textures.
void stableSort(Comparator comp)
Resorts the texture list using the specified comparator using a stable sorting algorithm.
This callback allows you a simple usage of callbacks in your module.
std::map< osg::Node *, NodeInfo *, std::less< osg::Node * > >::const_iterator ConstIterator
A typedef for the correct const iterator useful to traverse this sequence container.
void sortByIndex()
Sort the texture list by the indices that have been stored in each texture's sortIndex.
void registerTextureInst(osg::ref_ptr< WGETexture3D > texture, std::string name)
Register the specified texture to the colormapper.
void callback(osg::Node *node)
This callback handles all the updates needed.
boost::signals2::connection subscribeSignal(TextureListSignal signal, TextureRegisterHandler notifier)
Subscribe to the specified signal.
WCondition::SPtr getChangeCondition() const
Returns the condition firing if the texture list changes (sort, replace, add or remove).
NodeInfoContainerType m_nodeInfo
This map is needed to keep track of several node specific settings.
ReadTicket getReadTicket() const
Returns a ticket to get read access to the contained data.
Represents a axis parallel bounding box and provides some useful operations with them.
Definition: WBoundingBox.h:47
WSharedObject< WBoundingBox > m_boundingBox
The bounding box of all the textures.
std::map< osg::Node *, NodeInfo *, std::less< osg::Node * > >::iterator Iterator
A typedef for the correct iterator to traverse this sequence container.
WriteTicket getWriteTicket(bool suppressNotify=false) const
Returns a ticket to get write access to the contained data.
std::vector< osg::ref_ptr< WGETexture3D > >::const_iterator ConstIterator
A typedef for the correct const iterator useful to traverse this sequence container.
Exception thrown if a notifier could not be subscribed to a signal.
void replace(const typename S::value_type &oldValue, const typename S::value_type &newValue)
Replaces the specified old value by a new one.
boost::signals2::signal< void(void) > m_sortSignal
Called whenever the texture list got resorted.
Simple structure to store some additional node-related info like texture units and so on...
texture list was resorted
Class encapsulating the OSG Program class for a more convenient way of adding and modifying shader...
Definition: WGEShader.h:47
boost::signals2::signal< void(osg::ref_ptr< WGETexture3D >) > m_deregisterSignal
Called whenever a texture got removed.
bool moveDown(osg::ref_ptr< WGETexture3D > texture)
Move the specified texture one item down in the list.
void remove(const typename S::value_type &element)
Searches and removes the specified element.
virtual ~WGEColormapping()
Destructor.
static void replaceTexture(osg::ref_ptr< WGETexture3D > old, osg::ref_ptr< WGETexture3D > newTex, std::string name="")
Replaces the specified texture with the given new one.
WMatrix4d m_preTransform
matrix used for transforming arbitrary texture coordinates to the proper space.
void textureUpdate()
Called whenever the texture list is updated.
TextureContainerType::ReadTicket getReadTicket()
Returns a read ticket to the texture array.
static boost::shared_ptr< WGEColormapping > instance()
Returns instance of the module factory to use to create modules.
void unbindTexture(osg::ref_ptr< osg::Node > node, size_t unit, size_t count=1)
Removes the binding associated with the specified unit.
std::string toString(const T &value)
Convert a given value to a string.
Definition: WStringUtils.h:120
static MatrixType identity()
Returns an identity matrix.
Definition: WMatrixFixed.h:310
bool m_rebind
true if the node has not been callback'ed before
WBoundingBox getBoundingBox() const
This returns the bounding box of all the data textures.
boost::signals2::signal< void(osg::ref_ptr< WGETexture3D >, osg::ref_ptr< WGETexture3D >) > m_replaceSignal
Called whenever a texture got replaced.
bool moveToTop(osg::ref_ptr< WGETexture3D > texture)
Move the specified texture up in the list, directly to the top.
void resetSortIndices()
Reset all sort indices.
std::vector< osg::ref_ptr< osg::Node > > NodeList
a bunch of nodes.
void setSortIndices()
This function sets the index of a texture in the list to this texture's WGETexture::sortIndex().
void replaceTextureInst(osg::ref_ptr< WGETexture3D > old, osg::ref_ptr< WGETexture3D > newTex, std::string name="")
Replaces the specified texture with the given new one.
static void apply(osg::ref_ptr< osg::Node > node, WMatrix4d preTransform=WMatrix4d::identity(), osg::ref_ptr< WGEShader > shader=osg::ref_ptr< WGEShader >(), size_t startTexUnit=0)
Apply the colormapping to the specified node.
void updateBounds()
Updates the bounding box information.
size_t getMaxTexUnits()
Returns the maximum number of textures that can be bound to a node.
void bindTexture(osg::ref_ptr< osg::Node > node, osg::ref_ptr< WDataTexture3D > texture, size_t unit=0, std::string prefix="")
Binds the specified texture to the specified unit.
boost::function< void(osg::ref_ptr< WGETexture3D >) > TextureRegisterHandler
The type of handler used for being notified about added textures.
void push_front(const typename S::value_type &x)
Adds a new element at the beginning of the container.
boost::shared_ptr< WCondition > SPtr
Shared pointer type for WCondition.
Definition: WCondition.h:46
void applyInst(NodeList nodes, WMatrix4d preTransform=WMatrix4d::identity(), osg::ref_ptr< WGEShader > shader=osg::ref_ptr< WGEShader >(), size_t startTexUnit=0)
Apply the colormapping to the specified nodes.
WStreamedLogger debug(const std::string &source)
Logging a debug message.
Definition: WLogger.h:331
std::vector< osg::ref_ptr< WGETexture3D > >::iterator Iterator
A typedef for the correct iterator to traverse this sequence container.
boost::shared_ptr< WSharedObjectTicketRead< std::vector< osg::ref_ptr< WGETexture3D > > > > ReadTicket
Type for read tickets.
Definition: WSharedObject.h:62
static boost::shared_ptr< WGEColormapping > m_instance
Singleton instance of WGEColormapping.
bool moveToBottom(osg::ref_ptr< WGETexture3D > texture)
Move the specified texture down in the list, directly to the bottom.
bool moveTo(osg::ref_ptr< WGETexture3D > texture, size_t idx)
Move the texture to the specified index.
size_t count(const value_type &value)
Counts the number of occurrences of the specified value inside the container.
boost::shared_ptr< WCondition > getChangeCondition() const
This condition fires whenever the encapsulated object changed.
texture got replaced
boost::signals2::signal< void(osg::ref_ptr< WGETexture3D >) > m_registerSignal
Called whenever a texture got registered.
TextureListSignal
Possible signals that can be subscribed for being notified about texture list changes.