Kindfield
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Properties Friends Pages
volumeeffect.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the Qt3D module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "volumeeffect.h"
43 #include "volumeeffect_p.h"
44 #include <hartreefock.h>
45 #include "qglabstracteffect.h"
46 #include <QOpenGLShaderProgram>
47 #include <GL/gl.h>
48 #include "qglscenenode.h"
49 
50 #include <QWeakPointer>
51 #include <QQmlEngine>
52 #include <QQmlContext>
53 #include <iostream>
54 
55 #include <cmath>
56 
57 #include <boost/math/special_functions/spherical_harmonic.hpp>
58 
59 using namespace std;
60 
214 QT_BEGIN_NAMESPACE
215 
217 {
218 public:
220  : regenerate(false)
221  , shadersSupported(true) // Assume supported until known otherwise.
222  , effect(0)
223  {
224  }
225 
226  QString vertexShader;
227  QString fragmentShader;
231 };
232 
233 
234 /*
235  \internal
236  Construction for the VolumeShaderProgramEffect class consists of setting the key parameter values of the
237  class to undefined. As such, a shader program effect with no further initialisation will do nothing at all
238  until further creation of shader programs for it has been carried out.
239 */
241 {
242  this->parent = parent;
243  nextTextureUnit = 1;
244  propertyListener = new VolumeShaderProgramPropertyListenerEx(parent, this);
245 }
246 
247 /*
248  \internal
249  Destruction entails deletion of the underlying \l QGLVolumeShaderProgram which forms the functional core of the
250  class.
251 */
253 {
254  QList<QGLTexture2D*> textures = texture2Ds.values();
255  QGLTexture2D* texture;
256  foreach (texture, textures)
257  delete texture;
258 }
259 
260 /*
261  \internal
262  The act of shader programe creation can be undertakn in the manner defined for the QGLVolumeShaderProgram class.
263  Failure to successfully carry out creation will result in a warning message. Success will auto-populate the
264  parameter fields of the VolumeShaderProgramEffect with the necessary values based on the shader program.
265 
266  The vertex shader source is defined as a QString in the \a vertexShader parameter, while the fragment shader
267  is provided in the \a fragmentShader parameter.
268 */
270  (const QString& vertexShader, const QString& fragmentShader)
271 {
272  if (!QOpenGLShaderProgram::hasOpenGLShaderPrograms())
273  return false;
274 
275  setVertexShader(vertexShader.toLatin1());
276  setFragmentShader(fragmentShader.toLatin1());
277 
278  return true;
279 }
280 
287 {
288  // Texture3D stuff
289 // qDebug() << "After link!";
290 // qDebug() << program()->uniformLocation("myTexture3D");
291  m_texture3DuniformValue = program()->uniformLocation("myTexture3D");
292  m_eyePositionUniformLocation = program()->uniformLocation("ve_eyePosition");
293 // qDebug() << m_eyePositionUniformLocation;
294 // qDebug() << "Attribute: " << program()->attributeLocation("multiTexCoord3D");
295 // double data[24];
296 // QGLAttributeValue value(3, GL_FLOAT, 0, array.)
297 // glTexCoordPointer(value.tupleSize(), value.type(),
298 // value.stride(), value.data());
299 
300  GLuint g_volTexObj;
301  GLuint *data = parent.data()->texture3Ddata().data;
302  GLuint w = parent.data()->texture3Ddata().width;
303  GLuint h = parent.data()->texture3Ddata().height;
304  GLuint d = parent.data()->texture3Ddata().depth;
305 // qDebug() << "w " << w << " " << h << " " << d;
306 
307  glGenTextures(1, &g_volTexObj);
308  // bind 3D texture target
309  glBindTexture(GL_TEXTURE_3D, g_volTexObj);
310  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
311  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
312  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
313  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
314  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
315  // pixel transfer happens here from client to OpenGL server
316  glPixelStorei(GL_UNPACK_ALIGNMENT,1);
317  glTexImage3D(GL_TEXTURE_3D, 0, GL_INTENSITY, w, h, d, 0, GL_LUMINANCE, GL_UNSIGNED_INT, data);
318 
319  program()->setUniformValue(m_texture3DuniformValue, g_volTexObj);
320  // End Texture3D stuff
321 
322  propertyIdsToUniformLocations.clear();
323  uniformLocationsToTextureUnits.clear();
324  nextTextureUnit = 1;
325  propertyListener->disconnect();
326  if (parent.data() == 0)
327  {
328  return;
329  }
330  QObject::connect(propertyListener, SIGNAL(propertyChanged()), parent.data(), SIGNAL(effectChanged()));
331 
332  const QMetaObject* parentMetaObject = parent.data()->metaObject();
333  int parentMethodCount = parentMetaObject->methodCount();
334 
335  for (int i = parentMetaObject->propertyOffset();
336  i < parentMetaObject->propertyCount(); i++)
337  {
338  QMetaProperty metaProperty = parentMetaObject->property(i);
339  QByteArray propertyName = metaProperty.name();
340  int location = program()->uniformLocation(propertyName);
341  // -1 indicates that the program does not use the variable,
342  // so ignore those variables.
343  if (location != -1)
344  {
345  dirtyProperties.append(i);
346  propertyIdsToUniformLocations[i] = location;
347  if (metaProperty.hasNotifySignal())
348  {
349  QMetaMethod notifySignal = metaProperty.notifySignal();
350 
351  int signalIndex = notifySignal.methodIndex();
352 
353  // Connect the myFooChanged() signal from the VolumeShaderProgram
354  // to the corresponding imaginary slot on the listener
355  // Use the method count to make sure that we don't stomp on
356  // real methods and add the property index to tell the
357  // properties apart.
358  // Warning: Subclasses of VolumeShaderProgramPropertyListener will
359  // generate spurious property updates and lots of warnings
360  // and might even crash
361  QMetaObject::connect(parent.data(), signalIndex,
362  propertyListener, parentMethodCount + i);
363  } else {
364  qWarning() << "Warning: No notification signal found for property: " << propertyName;
365  propertiesWithoutNotificationSignal.append(i);
366  }
367  }
368  }
369 
370  // Refresh everything
371  this->setPropertiesDirty();
372 }
373 
375 {
376 // program()->bindAttributeLocation("multiTexCoord3D", 8);
377  return true;
378 }
379 
384 static inline void setUniformFromFloatList(QOpenGLShaderProgram *program, int uniformLocation, QList<QVariant> list)
385 {
386  switch(list.length())
387  {
388  case 1:
389  program->setUniformValue(uniformLocation, list.at(0).toFloat());
390  break;
391  case 2:
392  program->setUniformValue(uniformLocation,
393  list.at(0).toFloat(),
394  list.at(1).toFloat());
395  break;
396  case 3:
397  program->setUniformValue(uniformLocation,
398  list.at(0).toFloat(),
399  list.at(1).toFloat(),
400  list.at(2).toFloat());
401  break;
402  case 4:
403  program->setUniformValue(uniformLocation,
404  list.at(0).toFloat(),
405  list.at(1).toFloat(),
406  list.at(2).toFloat(),
407  list.at(3).toFloat());
408  break;
409  case 9:
410  {
411  QMatrix3x3 matrix;
412  for (int i = 0; i < 9; i++)
413  {
414  matrix(i / 3, i % 3) = list.at(i).toFloat();
415  }
416  program->setUniformValue(uniformLocation, matrix);
417  }
418  break;
419  case 16:
420  {
421  QMatrix4x4 matrix;
422  for (int i = 0; i < 16; i++)
423  {
424  matrix( i / 4, i % 4) = list.at(i).toFloat();
425  }
426  program->setUniformValue(uniformLocation, matrix);
427  }
428  break;
429  default:
430  // Very little information available to make this warning any more helpful
431  qWarning() << "Warning: unexpected list size: " << list.size() << ", only 1-4, 9 and 16 supported";
432  }
433 }
434 
441  (QGLPainter *painter, QGLPainter::Updates updates)
442 {
443 // painter->glActiveTexture(GL_TEXTURE0 + 0);
444 // program()->setUniformValue(m_eyeLocation, painter->);
445 
446  if (changedTextures.count() > 0)
447  {
448  foreach (int i, changedTextures)
449  {
450  if (!images.contains(i))
451  {
452  changedTextures.remove(i);
453  continue;
454  }
455 
456  if (!images[i].isNull())
457  {
458  setUniform(i, images[i], painter);
459  } else
460  {
461  qWarning() << "Warning: VolumeShaderProgramEffect failed to apply texture for uniform" << i << (urls.contains(i) ? QLatin1String(" url: ") + urls[i] : QString());
462  }
463  changedTextures.remove(i);
464  }
465  }
466 
467  // Update the standard uniform variables.
468  QGLShaderProgramEffect::update(painter, updates);
469 
470  // Assign custom properties if they exist
471  if (!parent.data() || !(propertyIdsToUniformLocations.count() > 0))
472  return;
473 
474  // update dirty properties and remove them from the list
475  int propertyIndex;
476  QList<int> propertiesNotUpdated;
477  foreach (propertyIndex, dirtyProperties)
478  {
479  if (!setUniformForPropertyIndex(propertyIndex, painter))
480  {
481  propertiesNotUpdated.append(propertyIndex);
482  };
483  }
484  dirtyProperties.clear();
485  dirtyProperties.append(propertiesNotUpdated);
486 
487  // always update the properties we can't track
488  foreach (propertyIndex, propertiesWithoutNotificationSignal)
489  {
490  setUniformForPropertyIndex(propertyIndex, painter);
491  }
492 
493  // Added specifically for the volume shader effect.
494  // This could have been passed more directly, but since we need to
495  // know about the scaling, translation and rotation of the item,
496  // we might as well calculate the inverse of the modelView matrix
497  // to get the true eye position.
498  // TODO: For better performance: Intercept the setCamera action in QGLPainter
499  // and calculate the proper eye position while calculating the matrices.
500  QVector4D inverseColumn3 = painter->modelViewMatrix().top().inverted().column(3);
501  program()->setUniformValue(m_eyePositionUniformLocation, inverseColumn3);
502 }
503 
504 inline QGLTexture2D* VolumeShaderProgramEffect::textureForUniformValue(int uniformLocation)
505 {
506  QGLTexture2D* result = texture2Ds.value(uniformLocation);
507  if (result == 0)
508  {
509  result = new QGLTexture2D();
510  texture2Ds[uniformLocation] = result;
511  }
512  return result;
513 }
514 
515 inline bool VolumeShaderProgramEffect::setUniformForPropertyIndex(int propertyIndex, QGLPainter *painter)
516 {
517  QOpenGLShaderProgram *program = this->program();
518  int uniformLocation = propertyIdsToUniformLocations[propertyIndex];
519 
520  QVariant value =
521  parent.data()->metaObject()->property(propertyIndex).read(parent.data());
522 
523  switch(int(value.type()))
524  {
525  case QVariant::Double:
526  // Convert double to float to pass to shader program
527  case QMetaType::Float:
528  program->setUniformValue(uniformLocation, value.toFloat());
529  break;
530  case QVariant::Int:
531  program->setUniformValue(uniformLocation, value.toInt());
532  break;
533  case QVariant::UInt:
534  program->setUniformValue(uniformLocation, value.toUInt());
535  break;
536  case QVariant::Bool:
537  program->setUniformValue(uniformLocation, value.toBool());
538  break;
539  case QVariant::Color:
540  program->setUniformValue(uniformLocation, value.value<QColor>());
541  break;
542  case QVariant::List:
543  setUniformFromFloatList(program, uniformLocation, value.toList());
544  break;
545  case QVariant::Point:
546  program->setUniformValue(uniformLocation, value.toPoint());
547  break;
548  case QVariant::PointF:
549  program->setUniformValue(uniformLocation, value.toPointF());
550  break;
551  case QVariant::Size:
552  program->setUniformValue(uniformLocation, value.toSize());
553  break;
554  case QVariant::SizeF:
555  program->setUniformValue(uniformLocation, value.toSizeF());
556  break;
557  case QVariant::Matrix4x4:
558  program->setUniformValue(uniformLocation, value.value<QMatrix4x4>());
559  break;
560  case QVariant::Vector2D:
561  program->setUniformValue(uniformLocation, value.value<QVector2D>());
562  break;
563  case QVariant::Vector3D:
564  program->setUniformValue(uniformLocation, value.value<QVector3D>());
565  break;
566  case QVariant::Vector4D:
567  program->setUniformValue(uniformLocation, value.value<QVector4D>());
568  break;
569  case QVariant::String:
570  {
571  // We assume strings are URLs to images for textures
572  QString urlString = value.toString();
573  processTextureUrl(uniformLocation, urlString);
574  }
575  break;
576  case QVariant::Image:
577  {
578  QImage image(value.toString());
579  setUniform(uniformLocation, image, painter);
580  }
581  break;
582  default:
583  qWarning() << "Unrecognized variant for property " << parent.data()->metaObject()->property(propertyIndex).name() << " of type " << value.typeName() << ", could not set corresponding shader variable";
584  }
585  return true;
586 }
587 
591 void VolumeShaderProgramEffect::setUniform
592  (int uniformLocation, const QPixmap pixmap, QGLPainter* painter)
593 {
594  // TODO: Perspective correction
595  QGLTexture2D* texture = textureForUniformValue(uniformLocation);
596  int unit = textureUnitForUniformValue(uniformLocation);
597  if (texture != 0)
598  {
599  texture->setPixmap(pixmap);
600  painter->glActiveTexture(GL_TEXTURE0 + unit);
601  texture->bind();
602  program()->setUniformValue(uniformLocation, unit);
603  }
604 }
605 
609 void VolumeShaderProgramEffect::setUniform
610  (int uniformLocation, const QImage& image, QGLPainter* painter)
611 {
612  // TODO: Perspective correction
613  QGLTexture2D* texture = textureForUniformValue(uniformLocation);
614  int unit = textureUnitForUniformValue(uniformLocation);
615  if (texture != 0)
616  {
617  texture->setImage(image);
618  painter->glActiveTexture(GL_TEXTURE0 + unit);
619  texture->bind();
620  program()->setUniformValue(uniformLocation, unit);
621  }
622 }
623 
627 int VolumeShaderProgramEffect::textureUnitForUniformValue(int uniformLocation)
628 {
629  int unit = uniformLocationsToTextureUnits.value(uniformLocation, -1);
630  if (unit == -1) {
631  unit = nextTextureUnit++;
632  uniformLocationsToTextureUnits[uniformLocation] = unit;
633  }
634  return unit;
635 }
636 
642 {
643  dirtyProperties = this->propertyIdsToUniformLocations.keys();
644 }
645 
651 {
652  if (dirtyProperties.indexOf(property) == -1)
653  {
654  dirtyProperties.append(property);
655  }
656 }
657 
665 void VolumeShaderProgramEffect::processTextureUrl(int uniformLocation, QString urlString)
666 {
667  QUrl url(urlString);
668  if (urlString.isEmpty() &&
669  urls.contains(uniformLocation) &&
670  !urls[uniformLocation].isNull())
671  {
672  if (images.contains(uniformLocation) && !images[uniformLocation].isNull())
673  {
674  images[uniformLocation] = QImage();
675  urls.remove(uniformLocation);
676  changedTextures.insert(uniformLocation);
677  return;
678  }
679  };
680 
681  // Try to make path absolute:
682  if (url.isRelative())
683  {
684  // Get the baseUrl from the qml engine
685  QQmlContext *context =
686  QQmlEngine::contextForObject(parent.data());
687 
688  if (context)
689  {
690  QUrl baseurl = context->baseUrl();
691  QUrl absolute = baseurl.resolved(urlString);
692 
693  if (absolute.isValid())
694  {
695  url = absolute;
696  urlString = absolute.toString();
697  } else {
698  qWarning() << "Warning: failed to resolve relative path " <<
699  urlString;
700  }
701  }
702  };
703 
704  if (urlString != urls[uniformLocation])
705  {
706  if (url.scheme() != QLatin1String("file"))
707  {
708  // TODO - support network URL's for loading - note that this feature is for
709  // the Qt3D 1.1 release and there is no point in implementing it until for example
710  // model loading and all other parts of Qt3D support it. Also when it is implemented
711  // it has to be done with a facility that does not depend on private headers in
712  // QtQml which can change within minor dot-point releases.
713  qWarning("Network URL's not yet supported - %s", qPrintable(urlString));
714  }
715  else
716  {
717  QString localFile = url.toLocalFile();
718  if (localFile.endsWith(QLatin1String(".dds")))
719  {
720  qWarning("Shader effects with compressed textures not supported: %s",
721  qPrintable(urlString));
722  }
723  else
724  {
725  QImage im(localFile);
726  if (im.isNull())
727  {
728  qWarning("Could not load image from local file path - %s", qPrintable(localFile));
729  }
730  else
731  {
732  images[uniformLocation] = im;
733  changedTextures.insert(uniformLocation);
734  }
735  }
736  }
737  }
738 }
739 
745  : QQuickEffect(parent),
746  m_positionReader(0)
747 {
748  d = new VolumeShaderProgramPrivate();
749 }
750 
757 {
758  delete d->effect;
759  delete d;
760 }
761 
770 QString VolumeShaderProgram::vertexShader() const
771 {
772  return d->vertexShader;
773 }
774 
775 void VolumeShaderProgram::setVertexShader(const QString& value)
776 {
777  d->vertexShader = value;
778  d->regenerate = true;
779  emit shaderChanged();
780  emit effectChanged();
781 }
782 
783 
792 {
793  return d->fragmentShader;
794 }
795 
796 void VolumeShaderProgram::setFragmentShader(const QString& value)
797 {
798  d->fragmentShader = value;
799  d->regenerate = true;
800  emit shaderChanged();
801  emit effectChanged();
802 }
803 
809 void VolumeShaderProgram::enableEffect(QGLPainter *painter)
810 {
811  if (!d->shadersSupported && !d->regenerate) {
812  // Use a simple fallback effect.
813  QQuickEffect::enableEffect(painter);
814  return;
815  }
816  if (!d->effect) {
817  // note that the VolumeShaderProgramEffect can also be created when this
818  // effect is applied to a QGLSceneNode if that happens first
819  d->effect = new VolumeShaderProgramEffect(this);
820  if (!d->effect->create(d->vertexShader, d->fragmentShader)) {
821  delete d->effect;
822  d->effect = 0;
823  QQuickEffect::enableEffect(painter);
824  d->regenerate = false;
825  d->shadersSupported = false;
826  return;
827  }
828  d->shadersSupported = true;
829  } else if (d->regenerate) {
830  if (!d->effect->create(d->vertexShader, d->fragmentShader)) {
831  delete d->effect;
832  d->effect = 0;
833  QQuickEffect::enableEffect(painter);
834  d->regenerate = false;
835  d->shadersSupported = false;
836  return;
837  }
838  d->shadersSupported = true;
839  }
840  d->regenerate = false;
841  painter->setUserEffect(d->effect);
842 }
843 
851 void VolumeShaderProgram::applyTo(QGLSceneNode *node)
852 {
853  if (!d->effect) {
854  // This function is often called during setup, before glInitilization,
855  // so create the effect now, and then initializion it later.
856  d->effect = new VolumeShaderProgramEffect(this);
857  d->regenerate = true;
858  }
859  node->setUserEffect(d->effect);
860 }
861 
862 //QUrl VolumeShaderProgram::texture3D() const
863 //{
864 // return m_texture3D;
865 //}
866 
868 {
869  return m_vertexShaderSource;
870 }
871 
873 {
874  return m_fragmentShaderSource;
875 }
876 
882 {
884 }
885 
891 {
892  d->effect->setPropertyDirty(property);
893 }
894 
895 //void VolumeShaderProgram::setTexture3D(QUrl arg)
896 //{
897 // if (m_texture3D != arg) {
898 // m_texture3D = arg;
899 // loadTexture3D();
900 // emit texture3DChanged(arg);
901 // }
902 //}
903 
904 //bool VolumeShaderProgram::loadTexture3D() {
905 // FILE *fp;
906 // GLuint w = 150;
907 // GLuint h = 150;
908 // GLuint d = 276;
909 // size_t size = w * h * d;
910 // GLushort *data = new GLushort[size]; // 8bit
911 // if (!(fp = fopen(m_texture3D.path().toStdString().c_str(), "rb")))
912 // {
913 // qWarning() << "Error: Could not open " << m_texture3D;
914 // return false;
915 // }
916 // if ( fread(data, sizeof(GLushort), size, fp)!= size)
917 // {
918 // qWarning() << "Error: Could not read " << m_texture3D;
919 // return false;
920 // }
921 // fclose(fp);
922 // m_texture3Ddata.data = data;
923 // m_texture3Ddata.width = w;
924 // m_texture3Ddata.height = h;
925 // m_texture3Ddata.depth = d;
926 // return true;
927 //}
928 
930  return m_texture3Ddata;
931 }
932 
934 {
935  if (m_vertexShaderSource != arg) {
936  m_vertexShaderSource = arg;
937  QFile file(arg.path());
938  QString vertexShaderFileContents;
939  if (file.open(QIODevice::ReadOnly)) {
940  vertexShaderFileContents = file.readAll();
941  } else {
942  qWarning() << "VolumeShaderProgram::setVertexShaderSource: could not open " << arg;
943  }
944  setVertexShader(vertexShaderFileContents);
945  emit vertexShaderSourceChanged(arg);
946  }
947 }
948 
950 {
951  if (m_fragmentShaderSource != arg) {
952  m_fragmentShaderSource = arg;
953  QFile file(arg.path());
954  QString fragmentShaderFileContents;
955  if (file.open(QIODevice::ReadOnly)) {
956  fragmentShaderFileContents = file.readAll();
957  } else {
958  qWarning() << "VolumeShaderProgram::setFragmentShaderSource: could not open " << arg;
959  }
960  setFragmentShader(fragmentShaderFileContents);
961  emit fragmentShaderSourceChanged(arg);
962  }
963 }
964 
966 {
967  if (m_positionReader != arg) {
968  m_positionReader = arg;
969  connect(m_positionReader, SIGNAL(dataChanged()), this, SLOT(forceUpdate()));
970  forceUpdate();
971  emit positionReaderChanged(arg);
972  }
973 }
974 
976 {
977  m_texture3Ddata.data = m_positionReader->voxelData();
978  m_texture3Ddata.width = m_positionReader->voxelDataWidth();
979  m_texture3Ddata.height = m_positionReader->voxelDataHeight();
980  m_texture3Ddata.depth = m_positionReader->voxelDataDepth();
981  d->regenerate = true;
982  emit effectChanged();
983 }
984 
999  : VolumeShaderProgramPropertyListener(parent), effect(effect)
1000 {
1001  VolumeshaderProgramMethodCount = parent->metaObject()->methodCount();
1002 }
1003 
1008 {
1009 }
1010 
1016 int VolumeShaderProgramPropertyListenerEx::qt_metacall(QMetaObject::Call c, int id, void **a)
1017 {
1018  if (c == QMetaObject::InvokeMetaMethod )
1019  {
1020  if (id >= VolumeshaderProgramMethodCount) {
1021  effect->setPropertyDirty(id - VolumeshaderProgramMethodCount);
1022  emit propertyChanged();
1023  }
1024  // Consume the metacall
1025  return -1;
1026  }
1027 
1028  return VolumeShaderProgramPropertyListener::qt_metacall(c, id, a);
1029 }
1030 
1031 QT_END_NAMESPACE