From e5f6ab99b413ad9b8481ad923c5a4a5bc6513ff2 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 12 Sep 2014 11:27:24 +0300 Subject: Implement volume slice frames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I409f3c95892b26ca6097dd4509109fc9978b9900 Reviewed-by: Tomi Korpipää --- examples/datavisualization/volumetric/main.cpp | 9 +- .../datavisualization/volumetric/volumetric.cpp | 47 +++- examples/datavisualization/volumetric/volumetric.h | 5 + src/datavisualization/data/abstractrenderitem_p.h | 6 +- src/datavisualization/data/customrenderitem.cpp | 32 ++- src/datavisualization/data/customrenderitem_p.h | 78 ++++-- src/datavisualization/data/qcustom3ditem.cpp | 10 +- src/datavisualization/data/qcustom3dvolume.cpp | 303 +++++++++++++++++++-- src/datavisualization/data/qcustom3dvolume.h | 26 ++ src/datavisualization/data/qcustom3dvolume_p.h | 11 +- .../engine/abstract3drenderer.cpp | 179 ++++++++++-- .../engine/abstract3drenderer_p.h | 7 +- src/datavisualization/engine/engine.qrc | 2 + .../engine/shaders/3dsliceframes.frag | 15 + .../engine/shaders/colorandposition.vert | 10 + src/datavisualization/utils/shaderhelper.cpp | 14 + src/datavisualization/utils/shaderhelper_p.h | 3 + tests/volumetrictest/volumetrictest.cpp | 4 +- 18 files changed, 669 insertions(+), 92 deletions(-) create mode 100644 src/datavisualization/engine/shaders/3dsliceframes.frag create mode 100644 src/datavisualization/engine/shaders/colorandposition.vert diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp index 98330396..faf379ec 100644 --- a/examples/datavisualization/volumetric/main.cpp +++ b/examples/datavisualization/volumetric/main.cpp @@ -168,6 +168,10 @@ int main(int argc, char **argv) "Note: A high end graphics card is\nrecommended with the HD shader\nwhen the volume contains a lot of\ntransparent areas.")); performanceNoteLabel->setFrameShape(QFrame::Box); + QCheckBox *drawSliceFramesCheckBox = new QCheckBox(widget); + drawSliceFramesCheckBox->setText(QStringLiteral("Draw slice frames")); + drawSliceFramesCheckBox->setChecked(false); + vLayout->addWidget(sliceXCheckBox); vLayout->addWidget(sliceXSlider); vLayout->addWidget(sliceImageXLabel); @@ -176,7 +180,8 @@ int main(int argc, char **argv) vLayout->addWidget(sliceImageYLabel); vLayout->addWidget(sliceZCheckBox); vLayout->addWidget(sliceZSlider); - vLayout->addWidget(sliceImageZLabel, 1, Qt::AlignTop); + vLayout->addWidget(sliceImageZLabel); + vLayout->addWidget(drawSliceFramesCheckBox, 1, Qt::AlignTop); vLayout2->addWidget(fpsCheckBox); vLayout2->addWidget(fpsLabel); vLayout2->addWidget(textureDetailGroupBox); @@ -234,6 +239,8 @@ int main(int argc, char **argv) &VolumetricModifier::toggleAreaMine); QObject::connect(areaMountainRB, &QRadioButton::toggled, modifier, &VolumetricModifier::toggleAreaMountain); + QObject::connect(drawSliceFramesCheckBox, &QCheckBox::stateChanged, modifier, + &VolumetricModifier::setDrawSliceFrames); widget->show(); return app.exec(); diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index 54e930d2..80df4bd5 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -58,6 +58,9 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_sliceIndexX(lowDetailSize / 2), m_sliceIndexY(lowDetailSize / 4), m_sliceIndexZ(lowDetailSize / 2), + m_slicingX(false), + m_slicingY(false), + m_slicingZ(false), m_mediumDetailRB(0), m_highDetailRB(0), m_lowDetailData(0), @@ -79,6 +82,7 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone); m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront); m_graph->setOrthoProjection(true); + m_graph->activeTheme()->setBackgroundEnabled(false); toggleAreaAll(true); @@ -162,6 +166,12 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_volumeItem->setColorTable(m_colorTable1); + m_volumeItem->setSliceFrameGaps(QVector3D(0.01f, 0.02f, 0.01f)); + m_volumeItem->setSliceFrameThicknesses(QVector3D(0.0025f, 0.005f, 0.0025f)); + m_volumeItem->setSliceFrameWidths(QVector3D(0.0025f, 0.005f, 0.0025f)); + m_volumeItem->setDrawSliceFrames(false); + handleSlicingChanges(); + m_graph->addCustomItem(m_volumeItem); m_timer.start(0); @@ -226,20 +236,20 @@ void VolumetricModifier::setAlphaMultiplierLabel(QLabel *label) void VolumetricModifier::sliceX(int enabled) { - if (m_volumeItem) - m_volumeItem->setSliceIndexX(enabled ? m_sliceIndexX : -1); + m_slicingX = enabled; + handleSlicingChanges(); } void VolumetricModifier::sliceY(int enabled) { - if (m_volumeItem) - m_volumeItem->setSliceIndexY(enabled ? m_sliceIndexY : -1); + m_slicingY = enabled; + handleSlicingChanges(); } void VolumetricModifier::sliceZ(int enabled) { - if (m_volumeItem) - m_volumeItem->setSliceIndexZ(enabled ? m_sliceIndexZ : -1); + m_slicingZ = enabled; + handleSlicingChanges(); } void VolumetricModifier::adjustSliceX(int value) @@ -482,6 +492,12 @@ void VolumetricModifier::toggleAreaMountain(bool enabled) } } +void VolumetricModifier::setDrawSliceFrames(int enabled) +{ + if (m_volumeItem) + m_volumeItem->setDrawSliceFrames(enabled); +} + void VolumetricModifier::initHeightMap(QString fileName, QVector &layerData) { QImage heightImage(fileName); @@ -607,6 +623,25 @@ void VolumetricModifier::excavateMineBlock(int textureSize, int dataIndex, int s } } +void VolumetricModifier::handleSlicingChanges() +{ + if (m_volumeItem) { + if (m_slicingX || m_slicingY || m_slicingZ) { + // Only show slices of selected dimensions + m_volumeItem->setDrawSlices(true); + m_volumeItem->setSliceIndexX(m_slicingX ? m_sliceIndexX : -1); + m_volumeItem->setSliceIndexY(m_slicingY ? m_sliceIndexY : -1); + m_volumeItem->setSliceIndexZ(m_slicingZ ? m_sliceIndexZ : -1); + } else { + // Show slice frames for all dimenstions when not actually slicing + m_volumeItem->setDrawSlices(false); + m_volumeItem->setSliceIndexX(m_sliceIndexX); + m_volumeItem->setSliceIndexY(m_sliceIndexY); + m_volumeItem->setSliceIndexZ(m_sliceIndexZ); + } + } +} + void VolumetricModifier::initMineShaftArray() { m_mineShaftArray << QPair(QVector3D(0.7f, 0.1f, 0.7f), diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h index 180686fb..7a4e7179 100644 --- a/examples/datavisualization/volumetric/volumetric.h +++ b/examples/datavisualization/volumetric/volumetric.h @@ -66,6 +66,7 @@ public slots: void toggleAreaAll(bool enabled); void toggleAreaMine(bool enabled); void toggleAreaMountain(bool enabled); + void setDrawSliceFrames(int enabled); private: @@ -76,12 +77,16 @@ private: int excavateMineShaft(int textureSize, int startIndex, int count, QVector *textureData); void excavateMineBlock(int textureSize, int dataIndex, int size, QVector *textureData); + void handleSlicingChanges(); Q3DScatter *m_graph; QCustom3DVolume *m_volumeItem; int m_sliceIndexX; int m_sliceIndexY; int m_sliceIndexZ; + bool m_slicingX; + bool m_slicingY; + bool m_slicingZ; QLabel *m_fpsLabel; QRadioButton *m_mediumDetailRB; QRadioButton *m_highDetailRB; diff --git a/src/datavisualization/data/abstractrenderitem_p.h b/src/datavisualization/data/abstractrenderitem_p.h index 57977a3c..ccee2b00 100644 --- a/src/datavisualization/data/abstractrenderitem_p.h +++ b/src/datavisualization/data/abstractrenderitem_p.h @@ -48,10 +48,12 @@ public: inline void setTranslation(const QVector3D &translation) { m_translation = translation; } inline const QVector3D &translation() const {return m_translation; } - inline QQuaternion rotation() const { return m_rotation; } + inline const QQuaternion &rotation() const { return m_rotation; } inline void setRotation(const QQuaternion &rotation) { - if (m_rotation != rotation) + if (rotation.isNull()) + m_rotation = identityQuaternion; + else m_rotation = rotation; } diff --git a/src/datavisualization/data/customrenderitem.cpp b/src/datavisualization/data/customrenderitem.cpp index 64194bac..f56ca86e 100644 --- a/src/datavisualization/data/customrenderitem.cpp +++ b/src/datavisualization/data/customrenderitem.cpp @@ -45,7 +45,10 @@ CustomRenderItem::CustomRenderItem() m_sliceIndexZ(-1), m_alphaMultiplier(1.0f), m_preserveOpacity(true), - m_useHighDefShader(true) + m_useHighDefShader(true), + m_drawSlices(false), + m_drawSliceFrames(false) + { } @@ -75,4 +78,31 @@ void CustomRenderItem::setColorTable(const QVector &colors) } } +void CustomRenderItem::setMinBounds(const QVector3D &bounds) +{ + m_minBounds = bounds; + m_minBoundsNormal = m_minBounds; + m_minBoundsNormal.setY(-m_minBoundsNormal.y()); + m_minBoundsNormal.setZ(-m_minBoundsNormal.z()); + m_minBoundsNormal = 0.5f * (m_minBoundsNormal + oneVector); +} + +void CustomRenderItem::setMaxBounds(const QVector3D &bounds) +{ + m_maxBounds = bounds; + m_maxBoundsNormal = m_maxBounds; + m_maxBoundsNormal.setY(-m_maxBoundsNormal.y()); + m_maxBoundsNormal.setZ(-m_maxBoundsNormal.z()); + m_maxBoundsNormal = 0.5f * (m_maxBoundsNormal + oneVector); +} + +void CustomRenderItem::setSliceFrameColor(const QColor &color) +{ + const QRgb &rgb = color.rgba(); + m_sliceFrameColor = QVector4D(float(qRed(rgb)) / 255.0f, + float(qGreen(rgb)) / 255.0f, + float(qBlue(rgb)) / 255.0f, + float(1.0f)); // Alpha not supported for frames +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/data/customrenderitem_p.h b/src/datavisualization/data/customrenderitem_p.h index 84fc898a..ad868fac 100644 --- a/src/datavisualization/data/customrenderitem_p.h +++ b/src/datavisualization/data/customrenderitem_p.h @@ -33,6 +33,7 @@ #include "objecthelper_p.h" #include #include +#include QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -50,13 +51,13 @@ public: void setMesh(const QString &meshFile); inline ObjectHelper *mesh() const { return m_object; } inline void setScaling(const QVector3D &scaling) { m_scaling = scaling; } - inline QVector3D scaling() const { return m_scaling; } + inline const QVector3D &scaling() const { return m_scaling; } inline void setOrigScaling(const QVector3D &scaling) { m_origScaling = scaling; } - inline QVector3D origScaling() const { return m_origScaling; } + inline const QVector3D &origScaling() const { return m_origScaling; } inline void setPosition(const QVector3D &position) { m_position = position; } - inline QVector3D position() const { return m_position; } + inline const QVector3D &position() const { return m_position; } inline void setOrigPosition(const QVector3D &position) { m_origPosition = position; } - inline QVector3D origPosition() const { return m_origPosition; } + inline const QVector3D &origPosition() const { return m_origPosition; } inline void setPositionAbsolute(bool absolute) { m_positionAbsolute = absolute; } inline bool isPositionAbsolute() const { return m_positionAbsolute; } inline void setScalingAbsolute(bool absolute) { m_scalingAbsolute = absolute; } @@ -80,11 +81,11 @@ public: inline bool isLabel() const { return m_labelItem; } // Volume specific - inline void setTextureWidth(int width) { m_textureWidth = width; } + inline void setTextureWidth(int width) { m_textureWidth = width; setSliceIndexX(m_sliceIndexX); } inline int textureWidth() const { return m_textureWidth; } - inline void setTextureHeight(int height) { m_textureHeight = height; } + inline void setTextureHeight(int height) { m_textureHeight = height; setSliceIndexY(m_sliceIndexY); } inline int textureHeight() const { return m_textureHeight; } - inline void setTextureDepth(int depth) { m_textureDepth = depth; } + inline void setTextureDepth(int depth) { m_textureDepth = depth; setSliceIndexZ(m_sliceIndexZ); } inline int textureDepth() const { return m_textureDepth; } inline int textureSize() const { return m_textureWidth * m_textureHeight * m_textureDepth; } inline void setColorTable(const QVector &colors) { m_colorTable = colors; } @@ -94,9 +95,22 @@ public: inline bool isVolume() const { return m_isVolume; } inline void setTextureFormat(QImage::Format format) { m_textureFormat = format; } inline QImage::Format textureFormat() const { return m_textureFormat; } - inline void setSliceIndexX(int index) { m_sliceIndexX = index; } - inline void setSliceIndexY(int index) { m_sliceIndexY = index; } - inline void setSliceIndexZ(int index) { m_sliceIndexZ = index; } + inline void setSliceIndexX(int index) + { + m_sliceIndexX = index; + m_sliceFractions.setX((float(index) + 0.5f) / float(m_textureWidth) * 2.0 - 1.0); + } + inline void setSliceIndexY(int index) + { + m_sliceIndexY = index; + m_sliceFractions.setY((float(index) + 0.5f) / float(m_textureHeight) * 2.0 - 1.0); + } + inline void setSliceIndexZ(int index) + { + m_sliceIndexZ = index; + m_sliceFractions.setZ((float(index) + 0.5f) / float(m_textureDepth) * 2.0 - 1.0); + } + inline const QVector3D &sliceFractions() const { return m_sliceFractions; } inline int sliceIndexX() const { return m_sliceIndexX; } inline int sliceIndexY() const { return m_sliceIndexY; } inline int sliceIndexZ() const { return m_sliceIndexZ; } @@ -106,25 +120,24 @@ public: inline bool preserveOpacity() const { return m_preserveOpacity; } inline void setUseHighDefShader(bool enable) { m_useHighDefShader = enable; } inline bool useHighDefShader() const {return m_useHighDefShader; } - inline void setMinBounds(const QVector3D &bounds) { - m_minBounds = bounds; - m_minBoundsNormal = m_minBounds; - m_minBoundsNormal.setY(-m_minBoundsNormal.y()); - m_minBoundsNormal.setZ(-m_minBoundsNormal.z()); - m_minBoundsNormal = 0.5f * (m_minBoundsNormal + oneVector); - - } - inline QVector3D minBounds() const { return m_minBounds; } - inline void setMaxBounds(const QVector3D &bounds) { - m_maxBounds = bounds; - m_maxBoundsNormal = m_maxBounds; - m_maxBoundsNormal.setY(-m_maxBoundsNormal.y()); - m_maxBoundsNormal.setZ(-m_maxBoundsNormal.z()); - m_maxBoundsNormal = 0.5f * (m_maxBoundsNormal + oneVector); - } - inline QVector3D maxBounds() const { return m_maxBounds; } - inline QVector3D minBoundsNormal() const { return m_minBoundsNormal; } - inline QVector3D maxBoundsNormal() const { return m_maxBoundsNormal; } + void setMinBounds(const QVector3D &bounds); + inline const QVector3D &minBounds() const { return m_minBounds; } + void setMaxBounds(const QVector3D &bounds); + inline const QVector3D &maxBounds() const { return m_maxBounds; } + inline const QVector3D &minBoundsNormal() const { return m_minBoundsNormal; } + inline const QVector3D &maxBoundsNormal() const { return m_maxBoundsNormal; } + inline void setDrawSlices(bool enable) { m_drawSlices = enable; } + inline bool drawSlices() const {return m_drawSlices; } + inline void setDrawSliceFrames(bool enable) { m_drawSliceFrames = enable; } + inline bool drawSliceFrames() const {return m_drawSliceFrames; } + void setSliceFrameColor(const QColor &color); + inline const QVector4D &sliceFrameColor() const { return m_sliceFrameColor; } + inline void setSliceFrameWidths(const QVector3D &widths) { m_sliceFrameWidths = widths * 2.0f; } + inline const QVector3D &sliceFrameWidths() const { return m_sliceFrameWidths; } + inline void setSliceFrameGaps(const QVector3D &gaps) { m_sliceFrameGaps = gaps * 2.0f; } + inline const QVector3D &sliceFrameGaps() const { return m_sliceFrameGaps; } + inline void setSliceFrameThicknesses(const QVector3D &thicknesses) { m_sliceFrameThicknesses = thicknesses; } + inline const QVector3D &sliceFrameThicknesses() const { return m_sliceFrameThicknesses; } private: Q_DISABLE_COPY(CustomRenderItem) @@ -157,6 +170,7 @@ private: int m_sliceIndexX; int m_sliceIndexY; int m_sliceIndexZ; + QVector3D m_sliceFractions; float m_alphaMultiplier; bool m_preserveOpacity; bool m_useHighDefShader; @@ -164,6 +178,12 @@ private: QVector3D m_maxBounds; QVector3D m_minBoundsNormal; QVector3D m_maxBoundsNormal; + bool m_drawSlices; + bool m_drawSliceFrames; + QVector4D m_sliceFrameColor; + QVector3D m_sliceFrameWidths; + QVector3D m_sliceFrameGaps; + QVector3D m_sliceFrameThicknesses; }; typedef QHash CustomRenderItemArray; diff --git a/src/datavisualization/data/qcustom3ditem.cpp b/src/datavisualization/data/qcustom3ditem.cpp index 4e82d47a..b9825732 100644 --- a/src/datavisualization/data/qcustom3ditem.cpp +++ b/src/datavisualization/data/qcustom3ditem.cpp @@ -101,7 +101,9 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * values. Defaults to \c{true}. Items with absolute scaling will be rendered at the same * size, regardless of axis ranges. Items with data scaling will change their apparent size * according to the axis ranges. If positionAbsolute value is \c{true}, this property is ignored - * and scaling is interpreted as an absolute value. + * and scaling is interpreted as an absolute value. If the item has rotation, the data scaling + * is calculated on the unrotated item. Similarly, for Custom3DVolume items, the range clipping + * is calculated on the unrotated item. * * \note: Only absolute scaling is supported for Custom3DLabel items or for custom items used in * \l{AbstractGraph3D::polar}{polar} graphs. @@ -282,7 +284,9 @@ QVector3D QCustom3DItem::scaling() const * values. Defaults to \c{true}. Items with absolute scaling will be rendered at the same * size, regardless of axis ranges. Items with data scaling will change their apparent size * according to the axis ranges. If positionAbsolute value is \c{true}, this property is ignored - * and scaling is interpreted as an absolute value. + * and scaling is interpreted as an absolute value. If the item has rotation, the data scaling + * is calculated on the unrotated item. Similarly, for QCustom3DVolume items, the range clipping + * is calculated on the unrotated item. * * \note: Only absolute scaling is supported for QCustom3DLabel items or for custom items used in * \l{QAbstract3DGraph::polar}{polar} graphs. @@ -438,7 +442,7 @@ QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q) : m_positionAbsolute(false), m_scaling(QVector3D(0.1f, 0.1f, 0.1f)), m_scalingAbsolute(true), - m_rotation(QQuaternion(0.0f, 0.0f, 0.0f, 0.0f)), + m_rotation(identityQuaternion), m_visible(true), m_shadowCasting(true), m_isLabelItem(false), diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp index 78c91802..563af31a 100644 --- a/src/datavisualization/data/qcustom3dvolume.cpp +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -104,31 +104,34 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION /*! \qmlproperty int Custom3DVolume::sliceIndexX * * The X dimension index into the texture data indicating which vertical slice to show. - * Setting any dimension to negative indicates no slice for that dimension is drawn. - * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn. + * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn + * normally. * Defaults to \c{-1}. * - * \sa QCustom3DVolume::textureData + * \sa QCustom3DVolume::textureData, drawSlices, drawSliceFrames */ /*! \qmlproperty int Custom3DVolume::sliceIndexY * * The Y dimension index into the texture data indicating which horizontal slice to show. - * Setting any dimension to negative indicates no slice for that dimension is drawn. - * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn. + * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn + * normally. * Defaults to \c{-1}. * - * \sa QCustom3DVolume::textureData + * \sa QCustom3DVolume::textureData, drawSlices, drawSliceFrames */ /*! \qmlproperty int Custom3DVolume::sliceIndexZ * * The Z dimension index into the texture data indicating which vertical slice to show. - * Setting any dimension to negative indicates no slice for that dimension is drawn. - * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn. + * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn + * normally. * Defaults to \c{-1}. * - * \sa QCustom3DVolume::textureData + * \sa QCustom3DVolume::textureData, drawSlices, drawSliceFrames */ /*! @@ -173,6 +176,79 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * Defaults to \c{true}. */ +/*! + * \qmlproperty bool Custom3DVolume::drawSlices + * + * If this property value is \c{true}, the slices indicated by slice index properties + * will be drawn instead of the full volume. + * If it is \c{false}, the full volume will always be drawn. + * Defaults to \c{false}. + * + * \note The slices are always drawn along the item axes, so if the item is rotated, the slices are + * rotated as well. + * + * \sa sliceIndexX, sliceIndexY, sliceIndexZ + */ + +/*! + * \qmlproperty bool Custom3DVolume::drawSliceFrames + * + * If this property value is \c{true}, the frames of slices indicated by slice index properties + * will be drawn around the volume. + * If it is \c{false}, no slice frames will be drawn. + * Drawing slice frames is independent of drawing slices, so you can show the full volume and + * still draw the slice frames around it. + * Defaults to \c{false}. + * + * \sa sliceIndexX, sliceIndexY, sliceIndexZ, drawSlices + */ + +/*! + * \qmlproperty color Custom3DVolume::sliceFrameColor + * + * Indicates the color of the slice frame. Transparent slice frame color is not supported. + * + * Defaults to black. + * + * \sa drawSliceFrames + */ + +/*! + * \qmlproperty vector3d Custom3DVolume::sliceFrameWidths + * + * Indicates the widths of the slice frame. The width can be different on different dimensions, + * so you can for example omit drawing the frames on certain sides of the volume by setting the + * value for that dimension to zero. The values are fractions of the volume thickness in the same + * dimension. The values cannot be negative. + * + * Defaults to \c{vector3d(0.01, 0.01, 0.01)}. + * + * \sa drawSliceFrames + */ + +/*! + * \qmlproperty vector3d Custom3DVolume::sliceFrameGaps + * + * Indicates the amount of air gap left between the volume itself and the frame in each dimension. + * The gap can be different on different dimensions. The values are fractions of the volume + * thickness in the same dimension. The values cannot be negative. + * + * Defaults to \c{vector3d(0.01, 0.01, 0.01)}. + * + * \sa drawSliceFrames + */ + +/*! + * \qmlproperty vector3d Custom3DVolume::sliceFrameThicknesses + * + * Indicates the thickness of the slice frames for each dimension. The values are fractions of + * the volume thickness in the same dimension. The values cannot be negative. + * + * Defaults to \c{vector3d(0.01, 0.01, 0.01)}. + * + * \sa drawSliceFrames + */ + /*! * Constructs QCustom3DVolume with given \a parent. */ @@ -324,17 +400,18 @@ int QCustom3DVolume::textureDataWidth() const /*! \property QCustom3DVolume::sliceIndexX * * The X dimension index into the texture data indicating which vertical slice to show. - * Setting any dimension to negative indicates no slice for that dimension is drawn. - * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn. + * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn + * normally. * Defaults to \c{-1}. * - * \sa textureData + * \sa textureData, drawSlices, drawSliceFrames */ void QCustom3DVolume::setSliceIndexX(int value) { if (dptr()->m_sliceIndexX != value) { dptr()->m_sliceIndexX = value; - dptr()->m_dirtyBitsVolume.sliceIndicesDirty = true; + dptr()->m_dirtyBitsVolume.slicesDirty = true; emit sliceIndexXChanged(value); emit dptr()->needUpdate(); } @@ -348,17 +425,18 @@ int QCustom3DVolume::sliceIndexX() const /*! \property QCustom3DVolume::sliceIndexY * * The Y dimension index into the texture data indicating which horizontal slice to show. - * Setting any dimension to negative indicates no slice for that dimension is drawn. - * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn. + * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn + * normally. * Defaults to \c{-1}. * - * \sa textureData + * \sa textureData, drawSlices, drawSliceFrames */ void QCustom3DVolume::setSliceIndexY(int value) { if (dptr()->m_sliceIndexY != value) { dptr()->m_sliceIndexY = value; - dptr()->m_dirtyBitsVolume.sliceIndicesDirty = true; + dptr()->m_dirtyBitsVolume.slicesDirty = true; emit sliceIndexYChanged(value); emit dptr()->needUpdate(); } @@ -372,17 +450,18 @@ int QCustom3DVolume::sliceIndexY() const /*! \property QCustom3DVolume::sliceIndexZ * * The Z dimension index into the texture data indicating which vertical slice to show. - * Setting any dimension to negative indicates no slice for that dimension is drawn. - * If all dimensions are negative, no slices are drawn and the volume is drawn normally. + * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn. + * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn + * normally. * Defaults to \c{-1}. * - * \sa textureData + * \sa textureData, drawSlices, drawSliceFrames */ void QCustom3DVolume::setSliceIndexZ(int value) { if (dptr()->m_sliceIndexZ != value) { dptr()->m_sliceIndexZ = value; - dptr()->m_dirtyBitsVolume.sliceIndicesDirty = true; + dptr()->m_dirtyBitsVolume.slicesDirty = true; emit sliceIndexZChanged(value); emit dptr()->needUpdate(); } @@ -816,6 +895,170 @@ bool QCustom3DVolume::useHighDefShader() const return dptrc()->m_useHighDefShader; } +/*! + * \property QCustom3DVolume::drawSlices + * + * If this property value is \c{true}, the slices indicated by slice index properties + * will be drawn instead of the full volume. + * If it is \c{false}, the full volume will always be drawn. + * Defaults to \c{false}. + * + * \note The slices are always drawn along the item axes, so if the item is rotated, the slices are + * rotated as well. + * + * \sa sliceIndexX, sliceIndexY, sliceIndexZ + */ +void QCustom3DVolume::setDrawSlices(bool enable) +{ + if (dptr()->m_drawSlices != enable) { + dptr()->m_drawSlices = enable; + dptr()->m_dirtyBitsVolume.slicesDirty = true; + emit drawSlicesChanged(enable); + emit dptr()->needUpdate(); + } +} + +bool QCustom3DVolume::drawSlices() const +{ + return dptrc()->m_drawSlices; +} + +/*! + * \property QCustom3DVolume::drawSliceFrames + * + * If this property value is \c{true}, the frames of slices indicated by slice index properties + * will be drawn around the volume. + * If it is \c{false}, no slice frames will be drawn. + * Drawing slice frames is independent of drawing slices, so you can show the full volume and + * still draw the slice frames around it. This is useful when using renderSlice() to display the + * slices outside the graph itself. + * Defaults to \c{false}. + * + * \sa sliceIndexX, sliceIndexY, sliceIndexZ, drawSlices, renderSlice() + */ +void QCustom3DVolume::setDrawSliceFrames(bool enable) +{ + if (dptr()->m_drawSliceFrames != enable) { + dptr()->m_drawSliceFrames = enable; + dptr()->m_dirtyBitsVolume.slicesDirty = true; + emit drawSliceFramesChanged(enable); + emit dptr()->needUpdate(); + } +} + +bool QCustom3DVolume::drawSliceFrames() const +{ + return dptrc()->m_drawSliceFrames; +} + +/*! + * \property QCustom3DVolume::sliceFrameColor + * + * Indicates the color of the slice frame. Transparent slice frame color is not supported. + * + * Defaults to black. + * + * \sa drawSliceFrames + */ +void QCustom3DVolume::setSliceFrameColor(const QColor &color) +{ + if (dptr()->m_sliceFrameColor != color) { + dptr()->m_sliceFrameColor = color; + dptr()->m_dirtyBitsVolume.slicesDirty = true; + emit sliceFrameColorChanged(color); + emit dptr()->needUpdate(); + } +} + +QColor QCustom3DVolume::sliceFrameColor() const +{ + return dptrc()->m_sliceFrameColor; +} + +/*! + * \property QCustom3DVolume::sliceFrameWidths + * + * Indicates the widths of the slice frame. The width can be different on different dimensions, + * so you can for example omit drawing the frames on certain sides of the volume by setting the + * value for that dimension to zero. The values are fractions of the volume thickness in the same + * dimension. The values cannot be negative. + * + * Defaults to \c{QVector3D(0.01, 0.01, 0.01)}. + * + * \sa drawSliceFrames + */ +void QCustom3DVolume::setSliceFrameWidths(const QVector3D &values) +{ + if (values.x() < 0.0f || values.y() < 0.0f || values.z() < 0.0f) { + qWarning() << __FUNCTION__ << "Attempted to set negative values."; + } else if (dptr()->m_sliceFrameWidths != values) { + dptr()->m_sliceFrameWidths = values; + dptr()->m_dirtyBitsVolume.slicesDirty = true; + emit sliceFrameWidthsChanged(values); + emit dptr()->needUpdate(); + } +} + +QVector3D QCustom3DVolume::sliceFrameWidths() const +{ + return dptrc()->m_sliceFrameWidths; +} + +/*! + * \property QCustom3DVolume::sliceFrameGaps + * + * Indicates the amount of air gap left between the volume itself and the frame in each dimension. + * The gap can be different on different dimensions. The values are fractions of the volume + * thickness in the same dimension. The values cannot be negative. + * + * Defaults to \c{QVector3D(0.01, 0.01, 0.01)}. + * + * \sa drawSliceFrames + */ +void QCustom3DVolume::setSliceFrameGaps(const QVector3D &values) +{ + if (values.x() < 0.0f || values.y() < 0.0f || values.z() < 0.0f) { + qWarning() << __FUNCTION__ << "Attempted to set negative values."; + } else if (dptr()->m_sliceFrameGaps != values) { + dptr()->m_sliceFrameGaps = values; + dptr()->m_dirtyBitsVolume.slicesDirty = true; + emit sliceFrameGapsChanged(values); + emit dptr()->needUpdate(); + } +} + +QVector3D QCustom3DVolume::sliceFrameGaps() const +{ + return dptrc()->m_sliceFrameGaps; +} + +/*! + * \property QCustom3DVolume::sliceFrameThicknesses + * + * Indicates the thickness of the slice frames for each dimension. The values are fractions of + * the volume thickness in the same dimension. The values cannot be negative. + * + * Defaults to \c{QVector3D(0.01, 0.01, 0.01)}. + * + * \sa drawSliceFrames + */ +void QCustom3DVolume::setSliceFrameThicknesses(const QVector3D &values) +{ + if (values.x() < 0.0f || values.y() < 0.0f || values.z() < 0.0f) { + qWarning() << __FUNCTION__ << "Attempted to set negative values."; + } else if (dptr()->m_sliceFrameThicknesses != values) { + dptr()->m_sliceFrameThicknesses = values; + dptr()->m_dirtyBitsVolume.slicesDirty = true; + emit sliceFrameThicknessesChanged(values); + emit dptr()->needUpdate(); + } +} + +QVector3D QCustom3DVolume::sliceFrameThicknesses() const +{ + return dptrc()->m_sliceFrameThicknesses; +} + /*! * Renders the slice specified by \a index along \a axis into an image. * The texture format of this object is used. @@ -857,7 +1100,13 @@ QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q) : m_textureData(0), m_alphaMultiplier(1.0f), m_preserveOpacity(true), - m_useHighDefShader(true) + m_useHighDefShader(true), + m_drawSlices(false), + m_drawSliceFrames(false), + m_sliceFrameColor(Qt::black), + m_sliceFrameWidths(QVector3D(0.01f, 0.01f, 0.01f)), + m_sliceFrameGaps(QVector3D(0.01f, 0.01f, 0.01f)), + m_sliceFrameThicknesses(QVector3D(0.01f, 0.01f, 0.01f)) { m_isVolumeItem = true; m_meshFile = QStringLiteral(":/defaultMeshes/barFull"); @@ -882,7 +1131,13 @@ QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector m_textureData(textureData), m_alphaMultiplier(1.0f), m_preserveOpacity(true), - m_useHighDefShader(true) + m_useHighDefShader(true), + m_drawSlices(false), + m_drawSliceFrames(false), + m_sliceFrameColor(Qt::black), + m_sliceFrameWidths(QVector3D(0.01f, 0.01f, 0.01f)), + m_sliceFrameGaps(QVector3D(0.01f, 0.01f, 0.01f)), + m_sliceFrameThicknesses(QVector3D(0.01f, 0.01f, 0.01f)) { m_isVolumeItem = true; m_shadowCasting = false; @@ -909,7 +1164,7 @@ void QCustom3DVolumePrivate::resetDirtyBits() QCustom3DItemPrivate::resetDirtyBits(); m_dirtyBitsVolume.textureDimensionsDirty = false; - m_dirtyBitsVolume.sliceIndicesDirty = false; + m_dirtyBitsVolume.slicesDirty = false; m_dirtyBitsVolume.colorTableDirty = false; m_dirtyBitsVolume.textureDataDirty = false; m_dirtyBitsVolume.textureFormatDirty = false; diff --git a/src/datavisualization/data/qcustom3dvolume.h b/src/datavisualization/data/qcustom3dvolume.h index 04434263..a9dfda86 100644 --- a/src/datavisualization/data/qcustom3dvolume.h +++ b/src/datavisualization/data/qcustom3dvolume.h @@ -42,6 +42,12 @@ class QT_DATAVISUALIZATION_EXPORT QCustom3DVolume : public QCustom3DItem Q_PROPERTY(float alphaMultiplier READ alphaMultiplier WRITE setAlphaMultiplier NOTIFY alphaMultiplierChanged) Q_PROPERTY(bool preserveOpacity READ preserveOpacity WRITE setPreserveOpacity NOTIFY preserveOpacityChanged) Q_PROPERTY(bool useHighDefShader READ useHighDefShader WRITE setUseHighDefShader NOTIFY useHighDefShaderChanged) + Q_PROPERTY(bool drawSlices READ drawSlices WRITE setDrawSlices NOTIFY drawSlicesChanged) + Q_PROPERTY(bool drawSliceFrames READ drawSliceFrames WRITE setDrawSliceFrames NOTIFY drawSliceFramesChanged) + Q_PROPERTY(QColor sliceFrameColor READ sliceFrameColor WRITE setSliceFrameColor NOTIFY sliceFrameColorChanged) + Q_PROPERTY(QVector3D sliceFrameWidths READ sliceFrameWidths WRITE setSliceFrameWidths NOTIFY sliceFrameWidthsChanged) + Q_PROPERTY(QVector3D sliceFrameGaps READ sliceFrameGaps WRITE setSliceFrameGaps NOTIFY sliceFrameGapsChanged) + Q_PROPERTY(QVector3D sliceFrameThicknesses READ sliceFrameThicknesses WRITE setSliceFrameThicknesses NOTIFY sliceFrameThicknessesChanged) public: @@ -90,6 +96,20 @@ public: void setUseHighDefShader(bool enable); bool useHighDefShader() const; + void setDrawSlices(bool enable); + bool drawSlices() const; + void setDrawSliceFrames(bool enable); + bool drawSliceFrames() const; + + void setSliceFrameColor(const QColor &color); + QColor sliceFrameColor() const; + void setSliceFrameWidths(const QVector3D &values); + QVector3D sliceFrameWidths() const; + void setSliceFrameGaps(const QVector3D &values); + QVector3D sliceFrameGaps() const; + void setSliceFrameThicknesses(const QVector3D &values); + QVector3D sliceFrameThicknesses() const; + QImage renderSlice(Qt::Axis axis, int index); signals: @@ -105,6 +125,12 @@ signals: void alphaMultiplierChanged(float mult); void preserveOpacityChanged(bool enabled); void useHighDefShaderChanged(bool enabled); + void drawSlicesChanged(bool enabled); + void drawSliceFramesChanged(bool enabled); + void sliceFrameColorChanged(const QColor &color); + void sliceFrameWidthsChanged(const QVector3D &values); + void sliceFrameGapsChanged(const QVector3D &values); + void sliceFrameThicknessesChanged(const QVector3D &values); protected: QCustom3DVolumePrivate *dptr(); diff --git a/src/datavisualization/data/qcustom3dvolume_p.h b/src/datavisualization/data/qcustom3dvolume_p.h index 8b0b439e..642d2af5 100644 --- a/src/datavisualization/data/qcustom3dvolume_p.h +++ b/src/datavisualization/data/qcustom3dvolume_p.h @@ -36,7 +36,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION struct QCustomVolumeDirtyBitField { bool textureDimensionsDirty : 1; - bool sliceIndicesDirty : 1; + bool slicesDirty : 1; bool colorTableDirty : 1; bool textureDataDirty : 1; bool textureFormatDirty : 1; @@ -45,7 +45,7 @@ struct QCustomVolumeDirtyBitField { QCustomVolumeDirtyBitField() : textureDimensionsDirty(false), - sliceIndicesDirty(false), + slicesDirty(false), colorTableDirty(false), textureDataDirty(false), textureFormatDirty(false), @@ -88,6 +88,13 @@ public: bool m_preserveOpacity; bool m_useHighDefShader; + bool m_drawSlices; + bool m_drawSliceFrames; + QColor m_sliceFrameColor; + QVector3D m_sliceFrameWidths; + QVector3D m_sliceFrameGaps; + QVector3D m_sliceFrameThicknesses; + QCustomVolumeDirtyBitField m_dirtyBitsVolume; private: diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 305d3df0..4dd58490 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -62,6 +62,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_volumeTextureShader(0), m_volumeTextureLowDefShader(0), m_volumeTextureSliceShader(0), + m_volumeSliceFrameShader(0), m_useOrthoProjection(false), m_xFlipped(false), m_yFlipped(false), @@ -105,6 +106,7 @@ Abstract3DRenderer::~Abstract3DRenderer() delete m_customItemShader; delete m_volumeTextureShader; delete m_volumeTextureLowDefShader; + delete m_volumeSliceFrameShader; delete m_volumeTextureSliceShader; foreach (SeriesRenderCache *cache, m_renderCacheList) { @@ -208,7 +210,9 @@ void Abstract3DRenderer::initCustomItemShaders(const QString &vertexShader, void Abstract3DRenderer::initVolumeTextureShaders(const QString &vertexShader, const QString &fragmentShader, const QString &fragmentLowDefShader, - const QString &sliceShader) + const QString &sliceShader, + const QString &sliceFrameVertexShader, + const QString &sliceFrameShader) { delete m_volumeTextureShader; @@ -216,13 +220,16 @@ void Abstract3DRenderer::initVolumeTextureShaders(const QString &vertexShader, m_volumeTextureShader->initialize(); delete m_volumeTextureLowDefShader; - m_volumeTextureLowDefShader = new ShaderHelper(this, vertexShader, - fragmentLowDefShader); + m_volumeTextureLowDefShader = new ShaderHelper(this, vertexShader, fragmentLowDefShader); m_volumeTextureLowDefShader->initialize(); delete m_volumeTextureSliceShader; m_volumeTextureSliceShader = new ShaderHelper(this, vertexShader, sliceShader); m_volumeTextureSliceShader->initialize(); + + delete m_volumeSliceFrameShader; + m_volumeSliceFrameShader = new ShaderHelper(this, sliceFrameVertexShader, sliceFrameShader); + m_volumeSliceFrameShader->initialize(); } void Abstract3DRenderer::updateTheme(Q3DTheme *theme) @@ -327,7 +334,9 @@ void Abstract3DRenderer::reInitShaders() initVolumeTextureShaders(QStringLiteral(":/shaders/vertexTexture3D"), QStringLiteral(":/shaders/fragmentTexture3D"), QStringLiteral(":/shaders/fragmentTexture3DLowDef"), - QStringLiteral(":/shaders/fragmentTexture3DSlice")); + QStringLiteral(":/shaders/fragmentTexture3DSlice"), + QStringLiteral(":/shaders/colorAndPosition"), + QStringLiteral(":/shaders/fragment3DSliceFrames")); #else if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) && qobject_cast(this)) { @@ -1011,6 +1020,13 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) newItem->setAlphaMultiplier(volumeItem->alphaMultiplier()); newItem->setPreserveOpacity(volumeItem->preserveOpacity()); newItem->setUseHighDefShader(volumeItem->useHighDefShader()); + + newItem->setDrawSlices(volumeItem->drawSlices()); + newItem->setDrawSliceFrames(volumeItem->drawSliceFrames()); + newItem->setSliceFrameColor(volumeItem->sliceFrameColor()); + newItem->setSliceFrameWidths(volumeItem->sliceFrameWidths()); + newItem->setSliceFrameGaps(volumeItem->sliceFrameGaps()); + newItem->setSliceFrameThicknesses(volumeItem->sliceFrameThicknesses()); #endif } recalculateCustomItemScalingAndPos(newItem); @@ -1215,11 +1231,17 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) volumeItem->dptr()->m_dirtyBitsVolume.textureDataDirty = false; volumeItem->dptr()->m_dirtyBitsVolume.textureFormatDirty = false; } - if (volumeItem->dptr()->m_dirtyBitsVolume.sliceIndicesDirty) { + if (volumeItem->dptr()->m_dirtyBitsVolume.slicesDirty) { + renderItem->setDrawSlices(volumeItem->drawSlices()); + renderItem->setDrawSliceFrames(volumeItem->drawSliceFrames()); + renderItem->setSliceFrameColor(volumeItem->sliceFrameColor()); + renderItem->setSliceFrameWidths(volumeItem->sliceFrameWidths()); + renderItem->setSliceFrameGaps(volumeItem->sliceFrameGaps()); + renderItem->setSliceFrameThicknesses(volumeItem->sliceFrameThicknesses()); renderItem->setSliceIndexX(volumeItem->sliceIndexX()); renderItem->setSliceIndexY(volumeItem->sliceIndexY()); renderItem->setSliceIndexZ(volumeItem->sliceIndexZ()); - volumeItem->dptr()->m_dirtyBitsVolume.sliceIndicesDirty = false; + volumeItem->dptr()->m_dirtyBitsVolume.slicesDirty = false; } if (volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty) { renderItem->setAlphaMultiplier(volumeItem->alphaMultiplier()); @@ -1236,9 +1258,8 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) void Abstract3DRenderer::updateCustomItemPositions() { - foreach (CustomRenderItem *renderItem, m_customRenderCache) { + foreach (CustomRenderItem *renderItem, m_customRenderCache) recalculateCustomItemScalingAndPos(renderItem); - } } void Abstract3DRenderer::drawCustomItems(RenderingState state, @@ -1345,9 +1366,10 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, #if !defined(QT_OPENGL_ES_2) ShaderHelper *prevShader = shader; if (item->isVolume()) { - if (item->sliceIndexX() >= 0 - || item->sliceIndexY() >= 0 - || item->sliceIndexZ() >= 0) { + if (item->drawSlices() && + (item->sliceIndexX() >= 0 + || item->sliceIndexY() >= 0 + || item->sliceIndexZ() >= 0)) { shader = m_volumeTextureSliceShader; } else if (item->useHighDefShader()) { shader = m_volumeTextureShader; @@ -1395,10 +1417,10 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, QVector3D cameraPos = m_cachedScene->activeCamera()->position(); cameraPos = MVPMatrix.inverted().map(cameraPos); // Adjust camera position according to min/max bounds - cameraPos = cameraPos - + ((oneVector - cameraPos) * item->minBoundsNormal()) - - ((oneVector + cameraPos) * (oneVector - item->maxBoundsNormal())); - shader->setUniformValue(shader->cameraPositionRelativeToModel(), -cameraPos); + cameraPos = -(cameraPos + + ((oneVector - cameraPos) * item->minBoundsNormal()) + - ((oneVector + cameraPos) * (oneVector - item->maxBoundsNormal()))); + shader->setUniformValue(shader->cameraPositionRelativeToModel(), cameraPos); GLint color8Bit = (item->textureFormat() == QImage::Format_Indexed8) ? 1 : 0; if (color8Bit) { shader->setUniformValueArray(shader->colorIndex(), @@ -1412,14 +1434,10 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, shader->setUniformValue(shader->minBounds(), item->minBounds()); shader->setUniformValue(shader->maxBounds(), item->maxBounds()); + QVector3D slices; if (shader == m_volumeTextureSliceShader) { - QVector3D slices((float(item->sliceIndexX()) + 0.5f) - / float(item->textureWidth()) * 2.0 - 1.0, - (float(item->sliceIndexY()) + 0.5f) - / float(item->textureHeight()) * 2.0 - 1.0, - (float(item->sliceIndexZ()) + 0.5f) - / float(item->textureDepth()) * 2.0 - 1.0); - shader->setUniformValue(shader->volumeSliceIndices(), slices); + shader->setUniformValue(shader->volumeSliceIndices(), + item->sliceFractions()); } else { // Precalculate texture dimensions so we can optimize // ray stepping to hit every texture layer. @@ -1443,6 +1461,24 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, shader->setUniformValue(shader->textureDimensions(), textureDimensions); shader->setUniformValue(shader->sampleCount(), sampleCount); } + if (item->drawSliceFrames()) { + // Set up the slice frame shader + glDisable(GL_CULL_FACE); + m_volumeSliceFrameShader->bind(); + m_volumeSliceFrameShader->setUniformValue( + m_volumeSliceFrameShader->color(), item->sliceFrameColor()); + + // Draw individual slice frames. + if (item->sliceIndexX() >= 0) + drawVolumeSliceFrame(item, Qt::XAxis, projectionViewMatrix); + if (item->sliceIndexY() >= 0) + drawVolumeSliceFrame(item, Qt::YAxis, projectionViewMatrix); + if (item->sliceIndexZ() >= 0) + drawVolumeSliceFrame(item, Qt::ZAxis, projectionViewMatrix); + + glEnable(GL_CULL_FACE); + shader->bind(); + } m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture()); } else #endif @@ -1476,6 +1512,105 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state, } } +void Abstract3DRenderer::drawVolumeSliceFrame(const CustomRenderItem *item, Qt::Axis axis, + const QMatrix4x4 &projectionViewMatrix) +{ + QVector2D frameWidth; + QVector3D frameScaling; + QVector3D translation = item->translation(); + QQuaternion rotation = item->rotation(); + float fracTrans; + bool needRotate = !rotation.isIdentity(); + QMatrix4x4 rotationMatrix; + if (needRotate) + rotationMatrix.rotate(rotation); + + if (axis == Qt::XAxis) { + fracTrans = item->sliceFractions().x(); + float range = item->maxBoundsNormal().x() - item->minBoundsNormal().x(); + float minMult = item->minBoundsNormal().x() / range; + float maxMult = (1.0f - item->maxBoundsNormal().x()) / range; + fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult); + if (needRotate) + translation -= rotationMatrix.map(QVector3D(fracTrans * item->scaling().x(), 0.0f, 0.0f)); + else + translation.setX(translation.x() + fracTrans * item->scaling().x()); + frameScaling = QVector3D(item->scaling().z() + + (item->scaling().z() * item->sliceFrameGaps().z()) + + (item->scaling().z() * item->sliceFrameWidths().z()), + item->scaling().y() + + (item->scaling().y() * item->sliceFrameGaps().y()) + + (item->scaling().y() * item->sliceFrameWidths().y()), + item->scaling().x() * item->sliceFrameThicknesses().x()); + frameWidth = QVector2D(item->scaling().z() * item->sliceFrameWidths().z(), + item->scaling().y() * item->sliceFrameWidths().y()); + rotation *= m_yRightAngleRotation; + } else if (axis == Qt::YAxis) { + fracTrans = item->sliceFractions().y(); + float range = item->maxBoundsNormal().y() - item->minBoundsNormal().y(); + // Y axis is logically flipped, so we need to swam min and max bounds + float minMult = (1.0f - item->maxBoundsNormal().y()) / range; + float maxMult = item->minBoundsNormal().y() / range; + fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult); + if (needRotate) + translation -= rotationMatrix.map(QVector3D(0.0f, fracTrans * item->scaling().y(), 0.0f)); + else + translation.setY(translation.y() - fracTrans * item->scaling().y()); + frameScaling = QVector3D(item->scaling().x() + + (item->scaling().x() * item->sliceFrameGaps().x()) + + (item->scaling().x() * item->sliceFrameWidths().x()), + item->scaling().z() + + (item->scaling().z() * item->sliceFrameGaps().z()) + + (item->scaling().z() * item->sliceFrameWidths().z()), + item->scaling().y() * item->sliceFrameThicknesses().y()); + frameWidth = QVector2D(item->scaling().x() * item->sliceFrameWidths().x(), + item->scaling().z() * item->sliceFrameWidths().z()); + rotation *= m_xRightAngleRotation; + } else { // Z axis + fracTrans = item->sliceFractions().z(); + float range = item->maxBoundsNormal().z() - item->minBoundsNormal().z(); + // Z axis is logically flipped, so we need to swam min and max bounds + float minMult = (1.0f - item->maxBoundsNormal().z()) / range; + float maxMult = item->minBoundsNormal().z() / range; + fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult); + if (needRotate) + translation -= rotationMatrix.map(QVector3D(0.0f, 0.0f, fracTrans * item->scaling().z())); + else + translation.setZ(translation.z() - fracTrans * item->scaling().z()); + frameScaling = QVector3D(item->scaling().x() + + (item->scaling().x() * item->sliceFrameGaps().x()) + + (item->scaling().x() * item->sliceFrameWidths().x()), + item->scaling().y() + + (item->scaling().y() * item->sliceFrameGaps().y()) + + (item->scaling().y() * item->sliceFrameWidths().y()), + item->scaling().z() * item->sliceFrameThicknesses().z()); + frameWidth = QVector2D(item->scaling().x() * item->sliceFrameWidths().x(), + item->scaling().y() * item->sliceFrameWidths().y()); + } + + // If the slice is outside the shown area, don't show the frame + if (fracTrans < -1.0 || fracTrans > 1.0) + return; + + // Shader needs the width of clear space in the middle. + frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x())); + frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y())); + + QMatrix4x4 modelMatrix; + QMatrix4x4 mvpMatrix; + + modelMatrix.translate(translation); + modelMatrix.rotate(rotation); + modelMatrix.scale(frameScaling); + mvpMatrix = projectionViewMatrix * modelMatrix; + m_volumeSliceFrameShader->setUniformValue(m_volumeSliceFrameShader->MVP(), mvpMatrix); + m_volumeSliceFrameShader->setUniformValue(m_volumeSliceFrameShader->sliceFrameWidth(), + frameWidth); + + m_drawer->drawObject(m_volumeSliceFrameShader, item->mesh()); + +} + void Abstract3DRenderer::calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const { // x is angular, z is radial diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index c8bfa7af..b31cfbd3 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -95,7 +95,9 @@ public: virtual void initVolumeTextureShaders(const QString &vertexShader, const QString &fragmentShader, const QString &fragmentLowDefShader, - const QString &sliceShader); + const QString &sliceShader, + const QString &sliceFrameVertexShader, + const QString &sliceFrameShader); virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis::AxisType type); virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation, @@ -207,6 +209,8 @@ protected: void recalculateCustomItemScalingAndPos(CustomRenderItem *item); virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) = 0; + void drawVolumeSliceFrame(const CustomRenderItem *item, Qt::Axis axis, + const QMatrix4x4 &projectionViewMatrix); bool m_hasNegativeValues; Q3DTheme *m_cachedTheme; @@ -248,6 +252,7 @@ protected: ShaderHelper *m_volumeTextureShader; ShaderHelper *m_volumeTextureLowDefShader; ShaderHelper *m_volumeTextureSliceShader; + ShaderHelper *m_volumeSliceFrameShader; bool m_useOrthoProjection; bool m_xFlipped; diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc index eeddf92e..af759ba3 100644 --- a/src/datavisualization/engine/engine.qrc +++ b/src/datavisualization/engine/engine.qrc @@ -66,5 +66,7 @@ shaders/defaultNoMatrices.vert shaders/texture3dlowdef.frag shaders/point_ES2_UV.vert + shaders/3dsliceframes.frag + shaders/colorandposition.vert diff --git a/src/datavisualization/engine/shaders/3dsliceframes.frag b/src/datavisualization/engine/shaders/3dsliceframes.frag new file mode 100644 index 00000000..44c080dc --- /dev/null +++ b/src/datavisualization/engine/shaders/3dsliceframes.frag @@ -0,0 +1,15 @@ +#version 120 + +uniform highp vec4 color_mdl; +uniform highp vec2 sliceFrameWidth; + +varying highp vec3 pos; + +void main() { + highp vec2 absPos = min(vec2(1.0, 1.0), abs(pos.xy)); + if (absPos.x > sliceFrameWidth.x || absPos.y > sliceFrameWidth.y) + gl_FragColor = color_mdl; + else + discard; +} + diff --git a/src/datavisualization/engine/shaders/colorandposition.vert b/src/datavisualization/engine/shaders/colorandposition.vert new file mode 100644 index 00000000..34849eae --- /dev/null +++ b/src/datavisualization/engine/shaders/colorandposition.vert @@ -0,0 +1,10 @@ +uniform highp mat4 MVP; + +attribute highp vec3 vertexPosition_mdl; + +varying highp vec3 pos; + +void main() { + gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); + pos = vertexPosition_mdl; +} diff --git a/src/datavisualization/utils/shaderhelper.cpp b/src/datavisualization/utils/shaderhelper.cpp index b6dc1621..aa43c2df 100644 --- a/src/datavisualization/utils/shaderhelper.cpp +++ b/src/datavisualization/utils/shaderhelper.cpp @@ -103,6 +103,7 @@ void ShaderHelper::initialize() m_preserveOpacityUniform = m_program->uniformLocation("preserveOpacity"); m_minBoundsUniform = m_program->uniformLocation("minBounds"); m_maxBoundsUniform = m_program->uniformLocation("maxBounds"); + m_sliceFrameWidthUniform = m_program->uniformLocation("sliceFrameWidth"); m_initialized = true; } @@ -135,6 +136,11 @@ void ShaderHelper::release() m_program->release(); } +void ShaderHelper::setUniformValue(GLuint uniform, const QVector2D &value) +{ + m_program->setUniformValue(uniform, value); +} + void ShaderHelper::setUniformValue(GLuint uniform, const QVector3D &value) { m_program->setUniformValue(uniform, value); @@ -340,6 +346,14 @@ GLuint ShaderHelper::minBounds() return m_minBoundsUniform; } +GLuint ShaderHelper::sliceFrameWidth() +{ + + if (!m_initialized) + qFatal("Shader not initialized"); + return m_sliceFrameWidthUniform; +} + GLuint ShaderHelper::posAtt() { if (!m_initialized) diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h index 853fcc8f..da010cbd 100644 --- a/src/datavisualization/utils/shaderhelper_p.h +++ b/src/datavisualization/utils/shaderhelper_p.h @@ -52,6 +52,7 @@ class ShaderHelper bool testCompile(); void bind(); void release(); + void setUniformValue(GLuint uniform, const QVector2D &value); void setUniformValue(GLuint uniform, const QVector3D &value); void setUniformValue(GLuint uniform, const QVector4D &value); void setUniformValue(GLuint uniform, const QMatrix4x4 &value); @@ -84,6 +85,7 @@ class ShaderHelper GLuint preserveOpacity(); GLuint maxBounds(); GLuint minBounds(); + GLuint sliceFrameWidth(); GLuint posAtt(); GLuint uvAtt(); @@ -128,6 +130,7 @@ class ShaderHelper GLuint m_preserveOpacityUniform; GLuint m_minBoundsUniform; GLuint m_maxBoundsUniform; + GLuint m_sliceFrameWidthUniform; GLboolean m_initialized; }; diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp index a277c8b0..dadb86be 100644 --- a/tests/volumetrictest/volumetrictest.cpp +++ b/tests/volumetrictest/volumetrictest.cpp @@ -360,9 +360,11 @@ void VolumetricModifier::createVolume() { m_volumeItem = new QCustom3DVolume; m_volumeItem->setTextureFormat(QImage::Format_ARGB32); -// m_volumeItem->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 10.0f)); + m_volumeItem->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 20.0f)); m_volumeItem->setPosition(QVector3D(xMiddle - (xRange / 2.0f), yMiddle + (yRange / 2.0f), zMiddle)); //m_volumeItem->setPosition(QVector3D(xMiddle, yMiddle, zMiddle)); + m_volumeItem->setDrawSliceFrames(true); + m_volumeItem->setDrawSlices(true); QImage logo; logo.load(QStringLiteral(":/logo_no_padding.png")); -- cgit v1.2.3