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