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