summaryrefslogtreecommitdiffstats
path: root/src/datavisualization/engine
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@digia.com>2014-09-09 11:30:15 +0300
committerMiikka Heikkinen <miikka.heikkinen@digia.com>2014-09-10 12:30:54 +0300
commitddb9be979d93b7e17f1067dc6056de54d9828b29 (patch)
treedf612c56e4a804481297ae55b29f0ee379c5f8ab /src/datavisualization/engine
parentbb30ea555c71604de9a2bc5096fa35c9532b26bd (diff)
Limit volume to axis ranges
The volume object that would go partially outside axis ranges is scale and repositioned so that it only renders the portion that is inside the axis ranges. Change-Id: I792494e437998ba6276f58fab645767276c1476d Reviewed-by: Mika Salmela <mika.salmela@digia.com>
Diffstat (limited to 'src/datavisualization/engine')
-rw-r--r--src/datavisualization/engine/abstract3drenderer.cpp94
-rw-r--r--src/datavisualization/engine/abstract3drenderer_p.h3
-rw-r--r--src/datavisualization/engine/bars3drenderer.cpp52
-rw-r--r--src/datavisualization/engine/bars3drenderer_p.h1
-rw-r--r--src/datavisualization/engine/scatter3drenderer.cpp40
-rw-r--r--src/datavisualization/engine/scatter3drenderer_p.h1
-rw-r--r--src/datavisualization/engine/shaders/texture3d.frag64
-rw-r--r--src/datavisualization/engine/shaders/texture3d.vert26
-rw-r--r--src/datavisualization/engine/shaders/texture3dlowdef.frag64
-rw-r--r--src/datavisualization/engine/shaders/texture3dslice.frag97
-rw-r--r--src/datavisualization/engine/surface3drenderer.cpp40
-rw-r--r--src/datavisualization/engine/surface3drenderer_p.h1
12 files changed, 336 insertions, 147 deletions
diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp
index 18384872..305d3df0 100644
--- a/src/datavisualization/engine/abstract3drenderer.cpp
+++ b/src/datavisualization/engine/abstract3drenderer.cpp
@@ -960,7 +960,7 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
newItem->setRenderer(this);
newItem->setItemPointer(item); // Store pointer for render item updates
newItem->setMesh(item->meshFile());
- newItem->setPosition(item->position());
+ newItem->setOrigPosition(item->position());
newItem->setOrigScaling(item->scaling());
newItem->setScalingAbsolute(item->isScalingAbsolute());
newItem->setPositionAbsolute(item->isPositionAbsolute());
@@ -1013,7 +1013,7 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
newItem->setUseHighDefShader(volumeItem->useHighDefShader());
#endif
}
- recalculateCustomItemScaling(newItem);
+ recalculateCustomItemScalingAndPos(newItem);
newItem->setRotation(item->rotation());
#if !defined(QT_OPENGL_ES_2)
// In OpenGL ES we simply draw volumes as regular custom item placeholders.
@@ -1025,9 +1025,6 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
}
newItem->setTexture(texture);
item->d_ptr->clearTextureImage();
- QVector3D translation = convertPositionToTranslation(newItem->position(),
- newItem->isPositionAbsolute());
- newItem->setTranslation(translation);
newItem->setVisible(item->isVisible());
newItem->setShadowCasting(item->isShadowCasting());
newItem->setFacingCamera(facingCamera);
@@ -1035,25 +1032,71 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
return newItem;
}
-void Abstract3DRenderer::recalculateCustomItemScaling(CustomRenderItem *item)
+void Abstract3DRenderer::recalculateCustomItemScalingAndPos(CustomRenderItem *item)
{
if (!m_polarGraph && !item->isLabel() && !item->isScalingAbsolute()
&& !item->isPositionAbsolute()) {
QVector3D scale = item->origScaling() / 2.0f;
- QVector3D pos = item->position();
+ QVector3D pos = item->origPosition();
QVector3D minBounds(pos.x() - scale.x(),
pos.y() - scale.y(),
- pos.z() - scale.z());
+ pos.z() + scale.z());
QVector3D maxBounds(pos.x() + scale.x(),
pos.y() + scale.y(),
- pos.z() + scale.z());
- QVector3D min = convertPositionToTranslation(minBounds, false);
- QVector3D max = convertPositionToTranslation(maxBounds, false);
- item->setScaling(QVector3D(qAbs(max.x() - min.x()), qAbs(max.y() - min.y()),
- qAbs(max.z() - min.z())) / 2.0f);
+ pos.z() - scale.z());
+ QVector3D minCorner = convertPositionToTranslation(minBounds, false);
+ QVector3D maxCorner = convertPositionToTranslation(maxBounds, false);
+ scale = QVector3D(qAbs(maxCorner.x() - minCorner.x()),
+ qAbs(maxCorner.y() - minCorner.y()),
+ qAbs(maxCorner.z() - minCorner.z())) / 2.0f;
+ if (item->isVolume()) {
+ // Only volume items need to scale and reposition according to bounds
+ QVector3D minBoundsNormal = minCorner;
+ QVector3D maxBoundsNormal = maxCorner;
+ // getVisibleItemBounds returns bounds normalized for fragment shader [-1,1]
+ // Y and Z are also flipped.
+ getVisibleItemBounds(minBoundsNormal, maxBoundsNormal);
+ item->setMinBounds(minBoundsNormal);
+ item->setMaxBounds(maxBoundsNormal);
+ // For scaling calculations, we want [0,1] normalized values
+ minBoundsNormal = item->minBoundsNormal();
+ maxBoundsNormal = item->maxBoundsNormal();
+
+ // Rescale and reposition the item so that it doesn't go over the edges
+ QVector3D adjScaling =
+ QVector3D(scale.x() * (maxBoundsNormal.x() - minBoundsNormal.x()),
+ scale.y() * (maxBoundsNormal.y() - minBoundsNormal.y()),
+ scale.z() * (maxBoundsNormal.z() - minBoundsNormal.z()));
+
+ item->setScaling(adjScaling);
+
+ QVector3D adjPos = item->origPosition();
+ QVector3D dataExtents = QVector3D(maxBounds.x() - minBounds.x(),
+ maxBounds.y() - minBounds.y(),
+ maxBounds.z() - minBounds.z()) / 2.0f;
+ adjPos.setX(adjPos.x() + (dataExtents.x() * minBoundsNormal.x())
+ - (dataExtents.x() * (1.0f - maxBoundsNormal.x())));
+ adjPos.setY(adjPos.y() + (dataExtents.y() * minBoundsNormal.y())
+ - (dataExtents.y() * (1.0f - maxBoundsNormal.y())));
+ adjPos.setZ(adjPos.z() + (dataExtents.z() * minBoundsNormal.z())
+ - (dataExtents.z() * (1.0f - maxBoundsNormal.z())));
+ item->setPosition(adjPos);
+ } else {
+ // Only scale for non-volume items, and do not readjust position
+ item->setScaling(scale);
+ item->setPosition(item->origPosition());
+ }
} else {
item->setScaling(item->origScaling());
+ item->setPosition(item->origPosition());
+ if (item->isVolume()) {
+ item->setMinBounds(-1.0f * zeroVector);
+ item->setMaxBounds(oneVector);
+ }
}
+ QVector3D translation = convertPositionToTranslation(item->position(),
+ item->isPositionAbsolute());
+ item->setTranslation(translation);
}
void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
@@ -1064,11 +1107,10 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
item->d_ptr->m_dirtyBits.meshDirty = false;
}
if (item->d_ptr->m_dirtyBits.positionDirty) {
- renderItem->setPosition(item->position());
+ renderItem->setOrigPosition(item->position());
renderItem->setPositionAbsolute(item->isPositionAbsolute());
- QVector3D translation = convertPositionToTranslation(renderItem->position(),
- renderItem->isPositionAbsolute());
- renderItem->setTranslation(translation);
+ if (!item->d_ptr->m_dirtyBits.scalingDirty)
+ recalculateCustomItemScalingAndPos(renderItem);
item->d_ptr->m_dirtyBits.positionDirty = false;
}
if (item->d_ptr->m_dirtyBits.scalingDirty) {
@@ -1099,7 +1141,7 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
item->d_ptr->clearTextureImage();
renderItem->setOrigScaling(scaling);
}
- recalculateCustomItemScaling(renderItem);
+ recalculateCustomItemScalingAndPos(renderItem);
item->d_ptr->m_dirtyBits.scalingDirty = false;
}
if (item->d_ptr->m_dirtyBits.rotationDirty) {
@@ -1195,10 +1237,7 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
void Abstract3DRenderer::updateCustomItemPositions()
{
foreach (CustomRenderItem *renderItem, m_customRenderCache) {
- recalculateCustomItemScaling(renderItem);
- QVector3D translation = convertPositionToTranslation(renderItem->position(),
- renderItem->isPositionAbsolute());
- renderItem->setTranslation(translation);
+ recalculateCustomItemScalingAndPos(renderItem);
}
}
@@ -1243,7 +1282,7 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state,
continue;
}
- // Check if the render item is in data coordinates and not within axis ranges, and skip drawing if it is
+ // If the render item is in data coordinates and not within axis ranges, skip it
if (!item->isPositionAbsolute()
&& (item->position().x() < m_axisCacheX.min()
|| item->position().x() > m_axisCacheX.max()
@@ -1353,9 +1392,12 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state,
// Set shadowless shader bindings
#if !defined(QT_OPENGL_ES_2)
if (item->isVolume()) {
- // Volume shaders repurpose light position for camera position relative to item
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);
GLint color8Bit = (item->textureFormat() == QImage::Format_Indexed8) ? 1 : 0;
if (color8Bit) {
@@ -1366,6 +1408,10 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state,
shader->setUniformValue(shader->alphaMultiplier(), item->alphaMultiplier());
shader->setUniformValue(shader->preserveOpacity(),
item->preserveOpacity() ? 1 : 0);
+
+ shader->setUniformValue(shader->minBounds(), item->minBounds());
+ shader->setUniformValue(shader->maxBounds(), item->maxBounds());
+
if (shader == m_volumeTextureSliceShader) {
QVector3D slices((float(item->sliceIndexX()) + 0.5f)
/ float(item->textureWidth()) * 2.0 - 1.0,
diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h
index 8152e0c9..c8bfa7af 100644
--- a/src/datavisualization/engine/abstract3drenderer_p.h
+++ b/src/datavisualization/engine/abstract3drenderer_p.h
@@ -205,7 +205,8 @@ protected:
virtual void fixCameraTarget(QVector3D &target) = 0;
void updateCameraViewport();
- void recalculateCustomItemScaling(CustomRenderItem *item);
+ void recalculateCustomItemScalingAndPos(CustomRenderItem *item);
+ virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) = 0;
bool m_hasNegativeValues;
Q3DTheme *m_cachedTheme;
diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp
index 1614b563..3e83a830 100644
--- a/src/datavisualization/engine/bars3drenderer.cpp
+++ b/src/datavisualization/engine/bars3drenderer.cpp
@@ -138,6 +138,46 @@ void Bars3DRenderer::fixCameraTarget(QVector3D &target)
target.setZ(target.z() * -m_zScaleFactor);
}
+void Bars3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
+{
+ // The inputs are the item bounds in OpenGL coordinates.
+ // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
+ // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
+ float itemRangeX = (maxBounds.x() - minBounds.x());
+ float itemRangeY = (maxBounds.y() - minBounds.y());
+ float itemRangeZ = (maxBounds.z() - minBounds.z());
+
+ if (minBounds.x() < -m_xScaleFactor)
+ minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_xScaleFactor) / itemRangeX));
+ else
+ minBounds.setX(-1.0f);
+
+ if (minBounds.y() < -1.0f + m_backgroundAdjustment)
+ minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + 1.0f - m_backgroundAdjustment) / itemRangeY)));
+ else
+ minBounds.setY(1.0f);
+
+ if (minBounds.z() < -m_zScaleFactor)
+ minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_zScaleFactor) / itemRangeZ)));
+ else
+ minBounds.setZ(1.0f);
+
+ if (maxBounds.x() > m_xScaleFactor)
+ maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_xScaleFactor) / itemRangeX));
+ else
+ maxBounds.setX(1.0f);
+
+ if (maxBounds.y() > 1.0f + m_backgroundAdjustment)
+ maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - 1.0f - m_backgroundAdjustment) / itemRangeY)));
+ else
+ maxBounds.setY(-1.0f);
+
+ if (maxBounds.z() > m_zScaleFactor)
+ maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_zScaleFactor) / itemRangeZ)));
+ else
+ maxBounds.setZ(-1.0f);
+}
+
void Bars3DRenderer::updateData()
{
int minRow = m_axisCacheZ.min();
@@ -1042,7 +1082,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
if (m_reflectionEnabled && ((m_yFlipped && item.height() > 0.0)
- || (!m_yFlipped && item.height() < 0.0))) {
+ || (!m_yFlipped && item.height() < 0.0))) {
continue;
}
@@ -1564,7 +1604,7 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar,
&& (reflection == 1.0f
|| (reflection != 1.0f
&& ((m_yFlipped && item.height() < 0.0)
- || (!m_yFlipped && item.height() > 0.0)))))
+ || (!m_yFlipped && item.height() > 0.0)))))
|| !m_reflectionEnabled) {
// Skip drawing of 0-height bars and reflections of bars on the "wrong side"
// Set shader bindings
@@ -2775,10 +2815,10 @@ QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position
float zTrans = 0.0f;
if (!isAbsolute) {
// Convert row and column to translation on graph
- xTrans = (((position.x() + 0.5f) * m_cachedBarSpacing.width()) - m_rowWidth)
- / m_scaleFactor;
- zTrans = (m_columnDepth - ((position.z() + 0.5f) * m_cachedBarSpacing.height()))
- / m_scaleFactor;
+ xTrans = (((position.x() - m_axisCacheX.min() + 0.5f) * m_cachedBarSpacing.width())
+ - m_rowWidth) / m_scaleFactor;
+ zTrans = (m_columnDepth - ((position.z() - m_axisCacheZ.min() + 0.5f)
+ * m_cachedBarSpacing.height())) / m_scaleFactor;
yTrans = m_axisCacheY.positionAt(position.y());
} else {
xTrans = position.x() * m_xScaleFactor;
diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h
index 44837ba2..7f1e83bb 100644
--- a/src/datavisualization/engine/bars3drenderer_p.h
+++ b/src/datavisualization/engine/bars3drenderer_p.h
@@ -120,6 +120,7 @@ public:
protected:
virtual void initializeOpenGL();
virtual void fixCameraTarget(QVector3D &target);
+ virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds);
public slots:
void updateMultiSeriesScaling(bool uniform);
diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp
index ba1019ad..34386ca6 100644
--- a/src/datavisualization/engine/scatter3drenderer.cpp
+++ b/src/datavisualization/engine/scatter3drenderer.cpp
@@ -141,6 +141,46 @@ void Scatter3DRenderer::fixCameraTarget(QVector3D &target)
target.setZ(target.z() * -m_scaleZ);
}
+void Scatter3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
+{
+ // The inputs are the item bounds in OpenGL coordinates.
+ // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
+ // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
+ float itemRangeX = (maxBounds.x() - minBounds.x());
+ float itemRangeY = (maxBounds.y() - minBounds.y());
+ float itemRangeZ = (maxBounds.z() - minBounds.z());
+
+ if (minBounds.x() < -m_scaleX)
+ minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_scaleX) / itemRangeX));
+ else
+ minBounds.setX(-1.0f);
+
+ if (minBounds.y() < -m_scaleY)
+ minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + m_scaleY) / itemRangeY)));
+ else
+ minBounds.setY(1.0f);
+
+ if (minBounds.z() < -m_scaleZ)
+ minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_scaleZ) / itemRangeZ)));
+ else
+ minBounds.setZ(1.0f);
+
+ if (maxBounds.x() > m_scaleX)
+ maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_scaleX) / itemRangeX));
+ else
+ maxBounds.setX(1.0f);
+
+ if (maxBounds.y() > m_scaleY)
+ maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - m_scaleY) / itemRangeY)));
+ else
+ maxBounds.setY(-1.0f);
+
+ if (maxBounds.z() > m_scaleZ)
+ maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_scaleZ) / itemRangeZ)));
+ else
+ maxBounds.setZ(-1.0f);
+}
+
void Scatter3DRenderer::updateData()
{
calculateSceneScalingFactors();
diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h
index 3fc517d0..0852f262 100644
--- a/src/datavisualization/engine/scatter3drenderer_p.h
+++ b/src/datavisualization/engine/scatter3drenderer_p.h
@@ -114,6 +114,7 @@ public slots:
protected:
virtual void initializeOpenGL();
virtual void fixCameraTarget(QVector3D &target);
+ virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds);
private:
virtual void initShaders(const QString &vertexShader, const QString &fragmentShader);
diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag
index 460cbcc6..3f9c42ff 100644
--- a/src/datavisualization/engine/shaders/texture3d.frag
+++ b/src/datavisualization/engine/shaders/texture3d.frag
@@ -1,15 +1,17 @@
#version 120
varying highp vec3 pos;
+varying highp vec3 rayDir;
uniform highp sampler3D textureSampler;
-uniform highp vec3 cameraPositionRelativeToModel;
uniform highp vec4 colorIndex[256];
uniform highp int color8Bit;
uniform highp vec3 textureDimensions;
uniform highp int sampleCount; // This is the maximum sample count
uniform highp float alphaMultiplier;
uniform highp int preserveOpacity;
+uniform highp vec3 minBounds;
+uniform highp vec3 maxBounds;
// Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha.
// Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over
@@ -17,51 +19,38 @@ uniform highp int preserveOpacity;
const highp float alphaThicknesses = 32.0;
void main() {
- highp vec3 rayDir = -(cameraPositionRelativeToModel - pos);
vec3 rayStart = pos;
- // Flip Y and Z so QImage bits work directly for texture and first image is in the front
- rayDir.yz = -rayDir.yz;
- rayStart.yz = -rayStart.yz;
- // Calculate ray intersection endpoint
- vec3 rayStop;
- if (rayDir.x == 0.0) {
- rayStop.yz = rayStart.yz;
- rayStop.x = -rayStart.x;
- } else if (rayDir.y == 0.0) {
- rayStop.xz = rayStart.xz;
- rayStop.y = -rayStart.y;
- } else if (rayDir.z == 0.0) {
- rayStop.xy = rayStart.xy;
- rayStop.z = -rayStart.z;
- } else {
- highp vec3 boxBounds = vec3(1.0, 1.0, 1.0);
- highp vec3 invRayDir = 1.0 / rayDir;
- if (rayDir.x < 0)
- boxBounds.x = -1.0;
- if (rayDir.y < 0)
- boxBounds.y = -1.0;
- if (rayDir.z < 0)
- boxBounds.z = -1.0;
- highp vec3 t = (boxBounds - rayStart) * invRayDir;
- highp float minT = min(t.x, min(t.y, t.z));
- rayStop = rayStart + minT * rayDir;
+ highp vec3 startBounds = minBounds;
+ highp vec3 endBounds = maxBounds;
+ if (rayDir.x < 0.0) {
+ startBounds.x = maxBounds.x;
+ endBounds.x = minBounds.x;
+ }
+ if (rayDir.y > 0.0) {
+ startBounds.y = maxBounds.y;
+ endBounds.y = minBounds.y;
+ }
+ if (rayDir.z > 0.0) {
+ startBounds.z = maxBounds.z;
+ endBounds.z = minBounds.z;
}
+ // Calculate ray intersection endpoint
+ highp vec3 rayStop;
+ highp vec3 invRayDir = 1.0 / rayDir;
+ highp vec3 t = (endBounds - rayStart) * invRayDir;
+ highp float endT = min(t.x, min(t.y, t.z));
+ rayStop = rayStart + endT * rayDir;
+ if (endT <= 0.0)
+ discard;
+
// Convert intersections to texture coords
rayStart = 0.5 * (rayStart + 1.0);
rayStop = 0.5 * (rayStop + 1.0);
highp vec3 ray = rayStop - rayStart;
- // Avoid artifacts from divisions by zero
- if (ray.x == 0)
- ray.x = 0.000000001;
- if (ray.y == 0)
- ray.y = 0.000000001;
- if (ray.z == 0)
- ray.z = 0.000000001;
-
highp vec3 absRay = abs(ray);
highp vec3 invAbsRay = 1.0 / absRay;
highp float fullDist = length(ray);
@@ -154,6 +143,9 @@ void main() {
break;
}
+ if (totalOpacity == 1.0)
+ discard;
+
// Brighten up the final color if there is some transparency left
if (totalOpacity >= 0.0 && totalOpacity < 1.0)
destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity);
diff --git a/src/datavisualization/engine/shaders/texture3d.vert b/src/datavisualization/engine/shaders/texture3d.vert
index cad1ce06..ef3f1b25 100644
--- a/src/datavisualization/engine/shaders/texture3d.vert
+++ b/src/datavisualization/engine/shaders/texture3d.vert
@@ -1,12 +1,36 @@
uniform highp mat4 MVP;
+uniform highp vec3 minBounds;
+uniform highp vec3 maxBounds;
+uniform highp vec3 cameraPositionRelativeToModel;
attribute highp vec3 vertexPosition_mdl;
attribute highp vec2 vertexUV;
attribute highp vec3 vertexNormal_mdl;
varying highp vec3 pos;
+varying highp vec3 rayDir;
void main() {
gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
- pos = vertexPosition_mdl;
+
+ highp vec3 minBoundsNorm = minBounds;
+ highp vec3 maxBoundsNorm = maxBounds;
+
+ // Y and Z are flipped in bounds to be directly usable in texture calculations,
+ // so flip them back to normal for position calculations
+ minBoundsNorm.yz = -minBoundsNorm.yz;
+ maxBoundsNorm.yz = -maxBoundsNorm.yz;
+
+ minBoundsNorm = 0.5 * (minBoundsNorm + 1.0);
+ maxBoundsNorm = 0.5 * (maxBoundsNorm + 1.0);
+
+ pos = vertexPosition_mdl
+ + ((1.0 - vertexPosition_mdl) * minBoundsNorm)
+ - ((1.0 + vertexPosition_mdl) * (1.0 - maxBoundsNorm));
+
+ rayDir = -(cameraPositionRelativeToModel - pos);
+
+ // Flip Y and Z so QImage bits work directly for texture and first image is in the front
+ rayDir.yz = -rayDir.yz;
+ pos.yz = -pos.yz;
}
diff --git a/src/datavisualization/engine/shaders/texture3dlowdef.frag b/src/datavisualization/engine/shaders/texture3dlowdef.frag
index 72b959fc..ed0d41ce 100644
--- a/src/datavisualization/engine/shaders/texture3dlowdef.frag
+++ b/src/datavisualization/engine/shaders/texture3dlowdef.frag
@@ -1,15 +1,17 @@
#version 120
varying highp vec3 pos;
+varying highp vec3 rayDir;
uniform highp sampler3D textureSampler;
-uniform highp vec3 cameraPositionRelativeToModel;
uniform highp vec4 colorIndex[256];
uniform highp int color8Bit;
uniform highp vec3 textureDimensions;
uniform highp int sampleCount; // This is the maximum sample count
uniform highp float alphaMultiplier;
uniform highp int preserveOpacity;
+uniform highp vec3 minBounds;
+uniform highp vec3 maxBounds;
// Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha.
// Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over
@@ -18,36 +20,30 @@ const highp float alphaThicknesses = 32.0;
const highp float SQRT3 = 1.73205081;
void main() {
- highp vec3 rayDir = -(cameraPositionRelativeToModel - pos);
vec3 rayStart = pos;
- // Flip Y and Z so QImage bits work directly for texture and first image is in the front
- rayDir.yz = -rayDir.yz;
- rayStart.yz = -rayStart.yz;
+ highp vec3 startBounds = minBounds;
+ highp vec3 endBounds = maxBounds;
+ if (rayDir.x < 0.0) {
+ startBounds.x = maxBounds.x;
+ endBounds.x = minBounds.x;
+ }
+ if (rayDir.y > 0.0) {
+ startBounds.y = maxBounds.y;
+ endBounds.y = minBounds.y;
+ }
+ if (rayDir.z > 0.0) {
+ startBounds.z = maxBounds.z;
+ endBounds.z = minBounds.z;
+ }
// Calculate ray intersection endpoint
- vec3 rayStop;
- if (rayDir.x == 0.0) {
- rayStop.yz = rayStart.yz;
- rayStop.x = -rayStart.x;
- } else if (rayDir.y == 0.0) {
- rayStop.xz = rayStart.xz;
- rayStop.y = -rayStart.y;
- } else if (rayDir.z == 0.0) {
- rayStop.xy = rayStart.xy;
- rayStop.z = -rayStart.z;
- } else {
- highp vec3 boxBounds = vec3(1.0, 1.0, 1.0);
- highp vec3 invRayDir = 1.0 / rayDir;
- if (rayDir.x < 0)
- boxBounds.x = -1.0;
- if (rayDir.y < 0)
- boxBounds.y = -1.0;
- if (rayDir.z < 0)
- boxBounds.z = -1.0;
- highp vec3 t = (boxBounds - rayStart) * invRayDir;
- highp float minT = min(t.x, min(t.y, t.z));
- rayStop = rayStart + minT * rayDir;
- }
+ highp vec3 rayStop;
+ highp vec3 invRayDir = 1.0 / rayDir;
+ highp vec3 t = (endBounds - rayStart) * invRayDir;
+ highp float endT = min(t.x, min(t.y, t.z));
+ if (endT <= 0.0)
+ discard;
+ rayStop = rayStart + endT * rayDir;
// Convert intersections to texture coords
rayStart = 0.5 * (rayStart + 1.0);
@@ -55,15 +51,6 @@ void main() {
highp vec3 ray = rayStop - rayStart;
- // Avoid artifacts from divisions by zero
- if (ray.x == 0)
- ray.x = 0.000000001;
- if (ray.y == 0)
- ray.y = 0.000000001;
- if (ray.z == 0)
- ray.z = 0.000000001;
-
-
highp float fullDist = length(ray);
highp float stepSize = SQRT3 / sampleCount;
highp vec3 step = (SQRT3 * normalize(ray)) / sampleCount;
@@ -110,6 +97,9 @@ void main() {
break;
}
+ if (totalOpacity == 1.0)
+ discard;
+
// Brighten up the final color if there is some transparency left
if (totalOpacity >= 0.0 && totalOpacity < 1.0)
destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity);
diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag
index 00584744..c555af98 100644
--- a/src/datavisualization/engine/shaders/texture3dslice.frag
+++ b/src/datavisualization/engine/shaders/texture3dslice.frag
@@ -1,14 +1,16 @@
#version 120
varying highp vec3 pos;
+varying highp vec3 rayDir;
uniform highp sampler3D textureSampler;
-uniform highp vec3 cameraPositionRelativeToModel;
uniform highp vec3 volumeSliceIndices;
uniform highp vec4 colorIndex[256];
uniform highp int color8Bit;
uniform highp float alphaMultiplier;
uniform highp int preserveOpacity;
+uniform highp vec3 minBounds;
+uniform highp vec3 maxBounds;
const highp vec3 xPlaneNormal = vec3(1.0, 0, 0);
const highp vec3 yPlaneNormal = vec3(0, 1.0, 0);
@@ -16,21 +18,17 @@ const highp vec3 zPlaneNormal = vec3(0, 0, 1.0);
void main() {
// Find out where ray intersects the slice planes
- highp vec3 rayDir = -(cameraPositionRelativeToModel - pos);
- rayDir = normalize(rayDir);
+ vec3 normRayDir = normalize(rayDir);
highp vec3 rayStart = pos;
- // Flip Y and Z so QImage bits work directly for texture and first image is in the front
- rayStart.yz = -rayStart.yz;
- rayDir.yz = -rayDir.yz;
highp float minT = 2.0f;
- if (rayDir.x != 0.0 && rayDir.y != 0.0 && rayDir.z != 0.0) {
+ if (normRayDir.x != 0.0 && normRayDir.y != 0.0 && normRayDir.z != 0.0) {
highp vec3 boxBounds = vec3(1.0, 1.0, 1.0);
- highp vec3 invRayDir = 1.0 / rayDir;
- if (rayDir.x < 0)
+ highp vec3 invRayDir = 1.0 / normRayDir;
+ if (normRayDir.x < 0)
boxBounds.x = -1.0;
- if (rayDir.y < 0)
+ if (normRayDir.y < 0)
boxBounds.y = -1.0;
- if (rayDir.z < 0)
+ if (normRayDir.z < 0)
boxBounds.z = -1.0;
highp vec3 t = (boxBounds - rayStart) * invRayDir;
minT = min(t.x, min(t.y, t.z));
@@ -43,12 +41,12 @@ void main() {
highp float secondD = firstD;
highp float thirdD = firstD;
if (volumeSliceIndices.x >= -1.0) {
- highp float dx = dot(xPoint - rayStart, xPlaneNormal) / dot(rayDir, xPlaneNormal);
+ highp float dx = dot(xPoint - rayStart, xPlaneNormal) / dot(normRayDir, xPlaneNormal);
if (dx >= 0.0 && dx <= minT)
firstD = min(dx, firstD);
}
if (volumeSliceIndices.y >= -1.0) {
- highp float dy = dot(yPoint - rayStart, yPlaneNormal) / dot(rayDir, yPlaneNormal);
+ highp float dy = dot(yPoint - rayStart, yPlaneNormal) / dot(normRayDir, yPlaneNormal);
if (dy >= 0.0 && dy <= minT) {
if (dy < firstD) {
secondD = firstD;
@@ -59,7 +57,7 @@ void main() {
}
}
if (volumeSliceIndices.z >= -1.0) {
- highp float dz = dot(zPoint - rayStart, zPlaneNormal) / dot(rayDir, zPlaneNormal);
+ highp float dz = dot(zPoint - rayStart, zPlaneNormal) / dot(normRayDir, zPlaneNormal);
if (dz >= 0.0) {
if (dz < firstD && dz <= minT) {
thirdD = secondD;
@@ -83,43 +81,35 @@ void main() {
// Convert intersection to texture coords
if (firstD <= minT) {
- highp vec3 firstTex = rayStart + rayDir * firstD;
- firstTex = 0.5 * (firstTex + 1.0);
- curColor = texture3D(textureSampler, firstTex);
- if (color8Bit != 0)
- curColor = colorIndex[int(curColor.r * 255.0)];
-
- if (curColor.a > 0.0) {
- curAlpha = curColor.a;
- if (curColor.a == 1.0 && preserveOpacity != 0)
- curAlpha = 1.0;
- else
- curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0);
- destColor.rgb = curColor.rgb * curAlpha;
- totalAlpha = curAlpha;
- }
- if (secondD <= minT && totalAlpha < 1.0) {
- highp vec3 secondTex = rayStart + rayDir * secondD;
- secondTex = 0.5 * (secondTex + 1.0);
- curColor = texture3D(textureSampler, secondTex);
+ highp vec3 texelVec = rayStart + normRayDir * firstD;
+ if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x
+ && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y
+ && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) {
+ texelVec = 0.5 * (texelVec + 1.0);
+ curColor = texture3D(textureSampler, texelVec);
if (color8Bit != 0)
curColor = colorIndex[int(curColor.r * 255.0)];
+
if (curColor.a > 0.0) {
+ curAlpha = curColor.a;
if (curColor.a == 1.0 && preserveOpacity != 0)
curAlpha = 1.0;
else
curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0);
- curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha);
- destColor.rgb += curRgb;
- totalAlpha += curAlpha;
+ destColor.rgb = curColor.rgb * curAlpha;
+ totalAlpha = curAlpha;
}
- if (thirdD <= minT && totalAlpha < 1.0) {
- highp vec3 thirdTex = rayStart + rayDir * thirdD;
- thirdTex = 0.5 * (thirdTex + 1.0);
- curColor = texture3D(textureSampler, thirdTex);
+ }
+ if (secondD <= minT && totalAlpha < 1.0) {
+ texelVec = rayStart + normRayDir * secondD;
+ if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x
+ && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y
+ && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) {
+ texelVec = 0.5 * (texelVec + 1.0);
+ curColor = texture3D(textureSampler, texelVec);
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
if (curColor.a > 0.0) {
- if (color8Bit != 0)
- curColor = colorIndex[int(curColor.r * 255.0)];
if (curColor.a == 1.0 && preserveOpacity != 0)
curAlpha = 1.0;
else
@@ -129,9 +119,32 @@ void main() {
totalAlpha += curAlpha;
}
}
+ if (thirdD <= minT && totalAlpha < 1.0) {
+ texelVec = rayStart + normRayDir * thirdD;
+ if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x
+ && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y
+ && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) {
+ texelVec = 0.5 * (texelVec + 1.0);
+ curColor = texture3D(textureSampler, texelVec);
+ if (curColor.a > 0.0) {
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
+ if (curColor.a == 1.0 && preserveOpacity != 0)
+ curAlpha = 1.0;
+ else
+ curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0);
+ curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha);
+ destColor.rgb += curRgb;
+ totalAlpha += curAlpha;
+ }
+ }
+ }
}
}
+ if (totalAlpha == 0.0)
+ discard;
+
// Brighten up the final color if there is some transparency left
if (totalAlpha > 0.0 && totalAlpha < 1.0)
destColor *= 1.0 / totalAlpha;
diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp
index ced4c789..fb7322cc 100644
--- a/src/datavisualization/engine/surface3drenderer.cpp
+++ b/src/datavisualization/engine/surface3drenderer.cpp
@@ -158,6 +158,46 @@ void Surface3DRenderer::fixCameraTarget(QVector3D &target)
target.setZ(target.z() * -m_scaleZ);
}
+void Surface3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
+{
+ // The inputs are the item bounds in OpenGL coordinates.
+ // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
+ // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
+ float itemRangeX = (maxBounds.x() - minBounds.x());
+ float itemRangeY = (maxBounds.y() - minBounds.y());
+ float itemRangeZ = (maxBounds.z() - minBounds.z());
+
+ if (minBounds.x() < -m_scaleX)
+ minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_scaleX) / itemRangeX));
+ else
+ minBounds.setX(-1.0f);
+
+ if (minBounds.y() < -m_scaleY)
+ minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + m_scaleY) / itemRangeY)));
+ else
+ minBounds.setY(1.0f);
+
+ if (minBounds.z() < -m_scaleZ)
+ minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_scaleZ) / itemRangeZ)));
+ else
+ minBounds.setZ(1.0f);
+
+ if (maxBounds.x() > m_scaleX)
+ maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_scaleX) / itemRangeX));
+ else
+ maxBounds.setX(1.0f);
+
+ if (maxBounds.y() > m_scaleY)
+ maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - m_scaleY) / itemRangeY)));
+ else
+ maxBounds.setY(-1.0f);
+
+ if (maxBounds.z() > m_scaleZ)
+ maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_scaleZ) / itemRangeZ)));
+ else
+ maxBounds.setZ(-1.0f);
+}
+
void Surface3DRenderer::updateData()
{
calculateSceneScalingFactors();
diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h
index 9c53757d..623951d4 100644
--- a/src/datavisualization/engine/surface3drenderer_p.h
+++ b/src/datavisualization/engine/surface3drenderer_p.h
@@ -111,6 +111,7 @@ public:
protected:
void initializeOpenGL();
virtual void fixCameraTarget(QVector3D &target);
+ virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds);
signals:
void flatShadingSupportedChanged(bool supported);