summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMika Salmela <mika.salmela@digia.com>2013-08-23 17:58:31 +0300
committerMiikka Heikkinen <miikka.heikkinen@digia.com>2013-08-26 09:14:25 +0300
commit792d11e1093aa4e3ac249c57691c3bea6d07bc41 (patch)
tree0e4a58bd8c1c5aaf4e528d4aded4f6da3f64df48
parent43ecfbc2ca07428874df5497cead68c895dfb115 (diff)
AbstractRenderer into use and grid lines
Modified renderer to use AbstractRenderer and copied grid lines mostly from scatter. Feel free to fix bugs. See you in a week. Change-Id: I54c79b134be4ed5d7814c395a008a42003037307 Reviewed-by: Tomi Korpipää <tomi.korpipaa@digia.com> Reviewed-by: Miikka Heikkinen <miikka.heikkinen@digia.com>
-rw-r--r--examples/surfacechart/chartmodifier.cpp10
-rw-r--r--src/datavis3d/engine/q3dsurface.cpp87
-rw-r--r--src/datavis3d/engine/q3dsurface.h25
-rw-r--r--src/datavis3d/engine/q3dsurface_p.h3
-rw-r--r--src/datavis3d/engine/surface3dcontroller.cpp46
-rw-r--r--src/datavis3d/engine/surface3dcontroller_p.h11
-rw-r--r--src/datavis3d/engine/surface3drenderer.cpp479
-rw-r--r--src/datavis3d/engine/surface3drenderer_p.h71
8 files changed, 460 insertions, 272 deletions
diff --git a/examples/surfacechart/chartmodifier.cpp b/examples/surfacechart/chartmodifier.cpp
index b685a680..03f55dc9 100644
--- a/examples/surfacechart/chartmodifier.cpp
+++ b/examples/surfacechart/chartmodifier.cpp
@@ -17,6 +17,8 @@
****************************************************************************/
#include "chartmodifier.h"
+#include <QCategoryAxis>
+#include <QValueAxis>
#include <qmath.h>
@@ -70,9 +72,11 @@ void ChartModifier::toggleSqrtSin(bool enable)
}
}
- m_chart->setSegmentCount(4, 0.5f);
+ m_chart->setSegmentCount(4, 0.5f); // Going to be obsolete
m_chart->appendSeries(series, m_xCount, m_zCount);
+ m_chart->valueAxisZ()->setSegmentCount(7);
+
qDebug() << "biggest = " << biggest << ", smallest = " << smallest;
} else {
qDebug() << "Remove surface";
@@ -94,6 +98,10 @@ void ChartModifier::togglePlane(bool enable)
m_chart->setSegmentCount(4, 0.5f);
m_chart->appendSeries(series, m_xCount, m_zCount);
+ m_chart->valueAxisX()->setSegmentCount(m_xCount - 1);
+ m_chart->valueAxisY()->setSegmentCount(4);
+ m_chart->valueAxisY()->setRange(0.0, 2.0);
+ m_chart->valueAxisZ()->setSegmentCount(m_zCount - 1);
}
}
diff --git a/src/datavis3d/engine/q3dsurface.cpp b/src/datavis3d/engine/q3dsurface.cpp
index e3a30720..d4b5f2f1 100644
--- a/src/datavis3d/engine/q3dsurface.cpp
+++ b/src/datavis3d/engine/q3dsurface.cpp
@@ -18,6 +18,7 @@
#include "q3dsurface.h"
#include "q3dsurface_p.h"
+#include "qvalueaxis.h"
#include <QMouseEvent>
@@ -37,6 +38,7 @@ Q3DSurface::~Q3DSurface()
void Q3DSurface::render()
{
+ d_ptr->m_shared->synchDataToRenderer();
d_ptr->m_shared->render();
}
@@ -78,6 +80,40 @@ void Q3DSurface::resizeEvent(QResizeEvent *event)
d_ptr->m_shared->setHeight(height());
}
+/*!
+ * \property Q3DSurface::gridVisible
+ *
+ * \a visible Flag to enable or disable grid. \c true by default.
+ *
+ * Sets grid drawing on or off.
+ */
+void Q3DSurface::setGridVisible(bool visible)
+{
+ d_ptr->m_shared->setGridEnabled(visible);
+}
+
+bool Q3DSurface::isGridVisible() const
+{
+ return d_ptr->m_shared->gridEnabled();
+}
+
+/*!
+ * \property Q3DSurface::backgroundVisible
+ *
+ * \a visible Flag to enable or disable background. \c true by default.
+ *
+ * Sets backround rendering on or off.
+ */
+void Q3DSurface::setBackgroundVisible(bool visible)
+{
+ d_ptr->m_shared->setBackgroundEnabled(visible);
+}
+
+bool Q3DSurface::isBackgroundVisible() const
+{
+ return d_ptr->m_shared->backgroundEnabled();
+}
+
void Q3DSurface::setSmoothSurface(bool enable)
{
d_ptr->m_shared->setSmoothSurface(enable);
@@ -111,21 +147,49 @@ void Q3DSurface::setHeight(const int height)
}
/*!
- * \a segmentCount How many segments will be drawn. \c 5 by default.
- *
- * \a step How large a step each segment is.
- *
- * \a minimum Minimum value a bar in data set can have. Setting this correctly is especially
- * important if values can be negative, or autoscaling won't work correctly.
- *
- * Sets segment count and step. Note; segmentCount * step should be the maximum possible value of data
- * set.
+ TODO: REMOVE
*/
void Q3DSurface::setSegmentCount(int segmentCount, qreal step, qreal minimum)
{
d_ptr->m_shared->setSegmentCount(GLint(segmentCount), GLfloat(step), GLfloat(minimum));
}
+void Q3DSurface::setValueAxisX(QValueAxis *axis)
+{
+ Q_ASSERT(axis);
+
+ return d_ptr->m_shared->setAxisX(axis);
+}
+
+QValueAxis *Q3DSurface::valueAxisX()
+{
+ return static_cast<QValueAxis *>(d_ptr->m_shared->axisX());
+}
+
+void Q3DSurface::setValueAxisY(QValueAxis *axis)
+{
+ Q_ASSERT(axis);
+
+ return d_ptr->m_shared->setAxisY(axis);
+}
+
+QValueAxis *Q3DSurface::valueAxisY()
+{
+ return static_cast<QValueAxis *>(d_ptr->m_shared->axisY());
+}
+
+void Q3DSurface::setValueAxisZ(QValueAxis *axis)
+{
+ Q_ASSERT(axis);
+
+ return d_ptr->m_shared->setAxisZ(axis);
+}
+
+QValueAxis *Q3DSurface::valueAxisZ()
+{
+ return static_cast<QValueAxis *>(d_ptr->m_shared->axisZ());
+}
+
void Q3DSurface::setGradientColorAt(qreal pos, const QColor &color)
{
d_ptr->m_shared->setGradientColorAt(pos, color);
@@ -154,9 +218,8 @@ void Q3DSurface::showData() const
/////////////////// PRIVATE ///////////////////////////////////
Q3DSurfacePrivate::Q3DSurfacePrivate(Q3DSurface *q, QRect rect)
- : m_shared(new Surface3dController(rect)),
- q_ptr(q)
-
+ : q_ptr(q),
+ m_shared(new Surface3dController(rect))
{
}
diff --git a/src/datavis3d/engine/q3dsurface.h b/src/datavis3d/engine/q3dsurface.h
index c2deea9a..357d65bd 100644
--- a/src/datavis3d/engine/q3dsurface.h
+++ b/src/datavis3d/engine/q3dsurface.h
@@ -25,10 +25,13 @@
QT_DATAVIS3D_BEGIN_NAMESPACE
class Q3DSurfacePrivate;
+class QValueAxis;
class QT_DATAVIS3D_EXPORT Q3DSurface : public Q3DWindow
{
Q_OBJECT
+ Q_PROPERTY(bool gridVisible READ isGridVisible WRITE setGridVisible)
+ Q_PROPERTY(bool backgroundVisible READ isBackgroundVisible WRITE setBackgroundVisible)
Q_PROPERTY(bool smoothSurface READ smoothSurface WRITE setSmoothSurface)
Q_PROPERTY(bool surfaceGrid READ surfaceGrid WRITE setSurfaceGrid)
@@ -36,6 +39,14 @@ public:
explicit Q3DSurface();
~Q3DSurface();
+ // Enable or disable background grid
+ void setGridVisible(bool visible);
+ bool isGridVisible() const;
+
+ // Enable or disable background mesh
+ void setBackgroundVisible(bool visible);
+ bool isBackgroundVisible() const;
+
// Enable or disable the smoothes of the surface
void setSmoothSurface(bool enable);
bool smoothSurface() const;
@@ -46,9 +57,17 @@ public:
void setGradientColorAt(qreal pos, const QColor &color);
- // Set segment count and step. Note; segmentCount * step should be the maximum possible value of data
- // set. Minimum is the absolute minimum possible value a bar can have. This is especially
- // important to set if values can be negative.
+ // Axes
+ void setValueAxisX(QValueAxis *axis);
+ QValueAxis *valueAxisX();
+
+ void setValueAxisY(QValueAxis *axis);
+ QValueAxis *valueAxisY();
+
+ void setValueAxisZ(QValueAxis *axis);
+ QValueAxis *valueAxisZ();
+
+ // TODO: Remove when axes handling in use
void setSegmentCount(int segmentCount, qreal step, qreal minimum = 0.0f);
//TODO part
diff --git a/src/datavis3d/engine/q3dsurface_p.h b/src/datavis3d/engine/q3dsurface_p.h
index 1e132416..1e92483b 100644
--- a/src/datavis3d/engine/q3dsurface_p.h
+++ b/src/datavis3d/engine/q3dsurface_p.h
@@ -50,10 +50,9 @@ public:
int numOfSeries();
// TODO
+ Q3DSurface *q_ptr;
Surface3dController *m_shared;
-private:
- Q3DSurface *q_ptr;
QList<QList<qreal> > m_seriesList; // Temp to be replaced by dataset
};
diff --git a/src/datavis3d/engine/surface3dcontroller.cpp b/src/datavis3d/engine/surface3dcontroller.cpp
index 0bb17c6d..89ec4fe6 100644
--- a/src/datavis3d/engine/surface3dcontroller.cpp
+++ b/src/datavis3d/engine/surface3dcontroller.cpp
@@ -19,6 +19,9 @@
#include "surface3dcontroller_p.h"
#include "surface3drenderer_p.h"
#include "camerahelper_p.h"
+#include "qabstractaxis_p.h"
+#include "qvalueaxis_p.h"
+#include "qcategoryaxis.h"
#include <QMatrix4x4>
#include <QMouseEvent>
@@ -30,12 +33,15 @@ QT_DATAVIS3D_BEGIN_NAMESPACE
Surface3dController::Surface3dController(QRect rect)
: Abstract3DController(rect),
m_renderer(0),
- m_isInitialized(false),
m_smoothSurface(false),
m_surfaceGrid(true),
m_mouseState(MouseNone),
m_mousePos(QPoint(0, 0))
{
+ // Default axes
+ setAxisX(new QValueAxis()); // Or QCategoryAxis
+ setAxisY(new QValueAxis());
+ setAxisZ(new QValueAxis()); // Or QCategoryAxis
}
Surface3dController::~Surface3dController()
@@ -45,24 +51,22 @@ Surface3dController::~Surface3dController()
void Surface3dController::initializeOpenGL()
{
// Initialization is called multiple times when Qt Quick components are used
- if (m_isInitialized)
+ if (isInitialized())
return;
m_renderer = new Surface3dRenderer(this);
- m_isInitialized = true;
+ setRenderer(m_renderer);
+ synchDataToRenderer();
}
void Surface3dController::synchDataToRenderer()
{
- // TODO: Implement
-}
+ Abstract3DController::synchDataToRenderer();
-void Surface3dController::render(const GLuint defaultFboHandle)
-{
- if (!m_isInitialized)
+ if (!isInitialized())
return;
- m_renderer->render(m_cameraHelper, defaultFboHandle);
+ // Notify changes to renderer
}
void Surface3dController::handleAxisAutoAdjustRangeChangedInOrientation(QAbstractAxis::AxisOrientation orientation, bool autoAdjust)
@@ -82,18 +86,6 @@ QMatrix4x4 Surface3dController::calculateViewMatrix(int zoom, int viewPortWidth,
showUnder);
}
-void Surface3dController::setWidth(const int width)
-{
- qDebug() << "Surface3dController::setWidth";
- m_renderer->setWidth(width);
-}
-
-void Surface3dController::setHeight(const int height)
-{
- qDebug() << "Surface3dController::setHeight";
- m_renderer->setHeight(height);
-}
-
void Surface3dController::setSmoothSurface(bool enable)
{
m_smoothSurface = enable;
@@ -132,7 +124,7 @@ void Surface3dController::mousePressEvent(QMouseEvent *event, const QPoint &mous
{
if (Qt::LeftButton == event->button()) {
m_mousePos = mousePos;
- emit leftMousePressed();
+ emit leftMousePressed(mousePos);
} else if (Qt::RightButton == event->button()) {
#if !defined(Q_OS_ANDROID)
m_mouseState = Abstract3DController::MouseRotating;
@@ -173,16 +165,6 @@ QPoint Surface3dController::mousePosition()
return m_mousePos;
}
-
-// TODO: abstract renderer should have accessor for Drawer instead
-Drawer *Surface3dController::drawer()
-{
- if (m_renderer)
- return m_renderer->drawer();
- else
- return 0;
-}
-
void Surface3dController::setSegmentCount(GLint segmentCount, GLfloat step, GLfloat minimum)
{
m_segmentCount = segmentCount;
diff --git a/src/datavis3d/engine/surface3dcontroller_p.h b/src/datavis3d/engine/surface3dcontroller_p.h
index d8c36d08..a9c0945c 100644
--- a/src/datavis3d/engine/surface3dcontroller_p.h
+++ b/src/datavis3d/engine/surface3dcontroller_p.h
@@ -44,7 +44,6 @@ class QT_DATAVIS3D_EXPORT Surface3dController : public Abstract3DController
private:
Surface3dRenderer *m_renderer;
- bool m_isInitialized;
QList<qreal> m_series; // TODO: TEMP
int m_dataWidth;
int m_dataDepth;
@@ -65,16 +64,12 @@ public:
~Surface3dController();
void initializeOpenGL();
- void synchDataToRenderer();
- void render(const GLuint defaultFboHandle = 0);
+ virtual void synchDataToRenderer();
QPoint mousePosition();
QMatrix4x4 calculateViewMatrix(int zoom, int viewPortWidth, int viewPortHeight, bool showUnder = false);
- void setWidth(const int width);
- void setHeight(const int height);
-
// Enable or disable the smoothes of the surface
void setSmoothSurface(bool enable);
bool smoothSurface();
@@ -102,15 +97,13 @@ public:
void mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos);
void wheelEvent(QWheelEvent *event);
- // TODO: abstract renderer should have accessor for Drawer instead
- virtual Drawer *drawer();
virtual void handleAxisAutoAdjustRangeChangedInOrientation(QAbstractAxis::AxisOrientation orientation, bool autoAdjust);
signals:
void smoothStatusChanged(bool enable);
void surfaceGridChanged(bool enable);
void segmentCountChanged(GLint segmentCount, GLfloat step, GLfloat minimum);
- void leftMousePressed();
+ void leftMousePressed(const QPoint &point); // My temp solution
private:
Q_DISABLE_COPY(Surface3dController)
diff --git a/src/datavis3d/engine/surface3drenderer.cpp b/src/datavis3d/engine/surface3drenderer.cpp
index 5337aa54..b98b4347 100644
--- a/src/datavis3d/engine/surface3drenderer.cpp
+++ b/src/datavis3d/engine/surface3drenderer.cpp
@@ -41,26 +41,27 @@ static const int ID_TO_RGBA_MASK = 0xff;
QT_DATAVIS3D_BEGIN_NAMESPACE
+const GLfloat backgroundMargin = 1.1f; // Margin for background (1.1f = make it 10% larger to avoid items being drawn inside background)
+const GLfloat backgroundBottom = 1.0f;
+const GLfloat gridLineWidth = 0.005f;
+const GLfloat coordRatio = 2.0f;
+
Surface3dRenderer::Surface3dRenderer(Surface3dController *controller)
- : QObject(controller),
+ : Abstract3DRenderer(controller),
m_controller(controller),
- m_mousePressed(MouseNone),
- m_mousePos(QPoint(0, 0)),
- m_labelTransparency(QDataVis::TransparencyFromTheme),
- m_font(QFont(QStringLiteral("Arial"))),
m_isGridEnabled(true),
- m_isBackgroundEnabled(true),
m_shadowQuality(QDataVis::ShadowLow),
- m_hasNegativeValues(false),
+ m_labelTransparency(QDataVis::TransparencyFromTheme),
+ m_font(QFont(QStringLiteral("Arial"))),
m_segmentYCount(0),
m_segmentYStep(0.0f),
m_segmentXCount(0),
m_segmentZCount(0),
+ m_shader(0),
m_backgroundShader(0),
m_surfaceShader(0),
m_surfaceGridShader(0),
m_selectionShader(0),
- m_isInitialized(false),
m_yRange(0.0f), // m_heightNormalizer
m_yAdjustment(0.0f),
m_xLength(0.0f),
@@ -84,7 +85,9 @@ Surface3dRenderer::Surface3dRenderer(Surface3dController *controller)
m_querySelection(false),
m_selectionPointer(0),
m_selectionActive(false),
- m_drawer(new Drawer(m_cachedTheme, m_font, m_labelTransparency))
+ m_xFlipped(false),
+ m_zFlipped(false),
+ m_yFlipped(false)
{
// Listen to changes in the controller
QObject::connect(m_controller, &Surface3dController::smoothStatusChanged, this,
@@ -93,15 +96,13 @@ Surface3dRenderer::Surface3dRenderer(Surface3dController *controller)
&Surface3dRenderer::updateSurfaceGridStatus);
QObject::connect(m_controller, &Surface3dController::segmentCountChanged, this,
&Surface3dRenderer::updateSegmentCount);
- QObject::connect(m_controller, &Surface3dController::themeChanged, this,
- &Surface3dRenderer::updateTheme);
QObject::connect(m_controller, &Surface3dController::leftMousePressed, this,
- &Surface3dRenderer::getSelection);
+ &Surface3dRenderer::requestSelectionAtPoint); // TODO: Possible temp
m_cachedSmoothSurface = m_controller->smoothSurface();
updateSurfaceGridStatus(m_controller->surfaceGrid());
- updateTheme(m_controller->theme());
+ initializeOpenGLFunctions();
initializeOpenGL();
}
@@ -117,6 +118,7 @@ Surface3dRenderer::~Surface3dRenderer()
m_textureHelper->deleteTexture(&m_selectionTexture);
m_textureHelper->deleteTexture(&m_selectionResultTexture);
+ delete m_shader;
delete m_backgroundShader;
delete m_selectionShader;
delete m_surfaceShader;
@@ -124,8 +126,7 @@ Surface3dRenderer::~Surface3dRenderer()
delete m_backgroundObj;
delete m_surfaceObj;
- delete m_textureHelper;
- delete m_drawer;
+ delete m_gridLineObj;
if (m_selectionPointer)
delete m_selectionPointer;
@@ -133,28 +134,10 @@ Surface3dRenderer::~Surface3dRenderer()
void Surface3dRenderer::initializeOpenGL()
{
- // Initialization is called multiple times when Qt Quick components are used
- if (m_isInitialized)
- return;
-
- initializeOpenGLFunctions();
-
- m_textureHelper = new TextureHelper();
- m_drawer->initializeOpenGL();
+ Abstract3DRenderer::initializeOpenGL();
// Initialize shaders
-#if !defined(QT_OPENGL_ES_2)
- if (m_shadowQuality > QDataVis::ShadowNone) {
- initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"),
- QStringLiteral(":/shaders/fragmentShadowNoTex"));
- } else {
- initBackgroundShaders(QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragment"));
- }
-#else
- initBackgroundShaders(QStringLiteral(":/shaders/vertexES2"),
- QStringLiteral(":/shaders/fragmentES2"));
-#endif
+ handleShadowQualityChange();
initSurfaceShaders();
@@ -180,11 +163,8 @@ void Surface3dRenderer::initializeOpenGL()
#endif
// Set view port
- glViewport(m_sliceViewPort.x(), m_sliceViewPort.y(),
- m_sliceViewPort.width(), m_sliceViewPort.height());
-
- // Set initialized -flag
- m_isInitialized = true;
+ glViewport(m_mainViewPort.x(), m_mainViewPort.y(),
+ m_mainViewPort.width(), m_mainViewPort.height());
// Resize in case we've missed resize events
// Resize calls initSelectionBuffer and initDepthBuffer, so they don't need to be called here
@@ -193,14 +173,11 @@ void Surface3dRenderer::initializeOpenGL()
// Load background mesh (we need to be initialized first)
loadBackgroundMesh();
- //loadSurfaceObj();
+ updateSurfaceGradient();
}
void Surface3dRenderer::render(CameraHelper *camera, const GLuint defaultFboHandle)
{
- if (!m_isInitialized)
- return;
-
if (defaultFboHandle) {
glDepthMask(true);
glEnable(GL_DEPTH_TEST);
@@ -222,6 +199,7 @@ void Surface3dRenderer::render(CameraHelper *camera, const GLuint defaultFboHand
drawScene(camera, defaultFboHandle);
+ // If selection pointer is active, pass the render request for it also
if (m_selectionPointer && m_selectionActive)
m_selectionPointer->render(camera, defaultFboHandle);
}
@@ -248,6 +226,17 @@ void Surface3dRenderer::drawScene(CameraHelper *camera, const GLuint defaultFboH
m_mainViewPort.height(),
m_hasNegativeValues);
+ // Calculate flipping indicators
+ if (viewMatrix.row(0).x() > 0)
+ m_zFlipped = false;
+ else
+ m_zFlipped = true;
+ if (viewMatrix.row(0).z() <= 0)
+ m_xFlipped = false;
+ else
+ m_xFlipped = true;
+
+
// calculate background rotation based on view matrix rotation
if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() <= 0)
backgroundRotation = 270.0f;
@@ -287,9 +276,7 @@ void Surface3dRenderer::drawScene(CameraHelper *camera, const GLuint defaultFboH
QMatrix4x4 MVPMatrix;
modelMatrix.translate(0.0f, 1.0f - m_yAdjustment, zComp);
- modelMatrix.scale(QVector3D(m_xLength / m_scaleFactor,
- 1.0f,
- m_zLength / m_scaleFactor));
+ modelMatrix.scale(QVector3D(m_scaleX, 1.0f, m_scaleZ));
MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
@@ -330,7 +317,7 @@ void Surface3dRenderer::drawScene(CameraHelper *camera, const GLuint defaultFboH
QPoint point = m_controller->mousePosition();
GLubyte pixel[4] = {0};
- glReadPixels(point.x(), height() - point.y(), 1, 1,
+ glReadPixels(point.x(), m_cachedBoundingRect.height() - point.y(), 1, 1,
GL_RGBA, GL_UNSIGNED_BYTE, (void *)pixel);
glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
@@ -361,12 +348,8 @@ void Surface3dRenderer::drawScene(CameraHelper *camera, const GLuint defaultFboH
QMatrix4x4 itModelMatrix;
modelMatrix.translate(0.0f, 1.0f - m_yAdjustment, zComp);
- modelMatrix.scale(QVector3D(m_xLength / m_scaleFactor,
- 1.0f,
- m_zLength / m_scaleFactor));
- itModelMatrix.scale(QVector3D(m_xLength / m_scaleFactor,
- 1.0f,
- m_zLength / m_scaleFactor));
+ modelMatrix.scale(QVector3D(m_scaleX, 1.0f, m_scaleZ));
+ itModelMatrix.scale(QVector3D(m_scaleX, 1.0f, m_scaleZ));
#ifdef SHOW_DEPTH_TEXTURE_SCENE
MVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
@@ -431,20 +414,16 @@ void Surface3dRenderer::drawScene(CameraHelper *camera, const GLuint defaultFboH
glCullFace(GL_BACK);
// Draw background
- if (m_isBackgroundEnabled && m_backgroundObj) {
+ if (m_cachedIsBackgroundEnabled && m_backgroundObj) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 depthMVPMatrix;
QMatrix4x4 itModelMatrix;
modelMatrix.translate(0.0f, 1.0f - m_yAdjustment, zComp);
- modelMatrix.scale(QVector3D(m_xLength / m_scaleFactor,
- 1.0f,
- m_zLength / m_scaleFactor));
+ modelMatrix.scale(QVector3D(m_scaleX * backgroundMargin, 1.0f, m_scaleZ * backgroundMargin));
modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
- itModelMatrix.scale(QVector3D(m_xLength / m_scaleFactor,
- 1.0f,
- m_zLength / m_scaleFactor));
+ itModelMatrix.scale(QVector3D(m_scaleX * backgroundMargin, 1.0f, m_scaleZ * backgroundMargin));
#ifdef SHOW_DEPTH_TEXTURE_SCENE
MVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
@@ -500,6 +479,227 @@ void Surface3dRenderer::drawScene(CameraHelper *camera, const GLuint defaultFboH
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
}
+
+ // Draw grid lines
+ if (m_cachedIsGridEnabled && m_surfaceObj /*&& m_heightNormalizer*/) {
+ // Bind shader
+ m_shader->bind();
+
+ // Set unchanging shader bindings
+ QVector3D color = Utils::vectorFromColor(m_cachedTheme.m_gridLine);
+ m_shader->setUniformValue(m_shader->lightP(), lightPos);
+ m_shader->setUniformValue(m_shader->view(), viewMatrix);
+ m_shader->setUniformValue(m_shader->color(), color);
+ m_shader->setUniformValue(m_shader->ambientS(), m_cachedTheme.m_ambientStrength);
+
+ GLfloat yPos = -backgroundBottom;
+ if (m_yFlipped)
+ yPos = backgroundBottom;
+
+ if (m_axisCacheZ.segmentCount() > 0) {
+ GLfloat lineStep = 2.0f / (m_axisCacheZ.segmentCount());
+ GLfloat linePos = -1.0;
+
+ for (int segment = 0; segment <= m_axisCacheZ.segmentCount(); segment++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(0.0f, yPos, linePos * m_scaleZ + zComp);
+
+ modelMatrix.scale(QVector3D(m_scaleX * backgroundMargin, gridLineWidth, gridLineWidth));
+ itModelMatrix.scale(QVector3D(m_scaleX * backgroundMargin, gridLineWidth, gridLineWidth));
+
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+
+ // Set the rest of the shader bindings
+ m_shader->setUniformValue(m_shader->model(), modelMatrix);
+ m_shader->setUniformValue(m_shader->nModel(),
+ itModelMatrix.inverted().transposed());
+ m_shader->setUniformValue(m_shader->MVP(), MVPMatrix);
+
+ #if !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ // Set shadow shader bindings
+ m_shader->setUniformValue(m_shader->shadowQ(), m_shadowQualityToShader);
+ m_shader->setUniformValue(m_shader->depth(), depthMVPMatrix);
+ m_shader->setUniformValue(m_shader->lightS(),
+ m_cachedTheme.m_lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(m_shader, m_gridLineObj, 0, m_depthTexture);
+ } else
+ #endif
+ {
+ // Set shadowless shader bindings
+ m_shader->setUniformValue(m_shader->lightS(),
+ m_cachedTheme.m_lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(m_shader, m_gridLineObj);
+ }
+
+ linePos += lineStep;
+ }
+ }
+
+ // Floor lines: columns (= X)
+ if (m_axisCacheX.segmentCount() > 0) {
+ GLfloat lineStep = 2.0f / (m_axisCacheX.segmentCount());
+ GLfloat linePos = -1.0;
+
+ for (int segment = 0; segment <= m_axisCacheX.segmentCount(); segment++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(linePos * m_scaleX, yPos, zComp);
+
+ modelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth, m_scaleZ * backgroundMargin));
+ itModelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth, m_scaleZ * backgroundMargin));
+
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+
+ // Set the rest of the shader bindings
+ m_shader->setUniformValue(m_shader->model(), modelMatrix);
+ m_shader->setUniformValue(m_shader->nModel(),
+ itModelMatrix.inverted().transposed());
+ m_shader->setUniformValue(m_shader->MVP(), MVPMatrix);
+
+ #if !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ // Set shadow shader bindings
+ m_shader->setUniformValue(m_shader->shadowQ(), m_shadowQualityToShader);
+ m_shader->setUniformValue(m_shader->depth(), depthMVPMatrix);
+ m_shader->setUniformValue(m_shader->lightS(),
+ m_cachedTheme.m_lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(m_shader, m_gridLineObj, 0, m_depthTexture);
+ } else
+ #endif
+ {
+ // Set shadowless shader bindings
+ m_shader->setUniformValue(m_shader->lightS(),
+ m_cachedTheme.m_lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(m_shader, m_gridLineObj);
+ }
+
+ linePos += lineStep;
+ }
+ }
+
+ // Wall lines: back wall
+ if (m_axisCacheY.segmentCount() > 0) {
+ GLfloat lineStep = 2.0f / (m_axisCacheY.segmentCount());
+ GLfloat linePos = -1.0;
+ GLfloat zPos = -backgroundMargin;
+
+ if (m_zFlipped)
+ zPos = backgroundMargin;
+
+ for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(0.0f, linePos, zPos * m_scaleZ + zComp);
+
+ modelMatrix.scale(QVector3D(m_scaleX * backgroundMargin, gridLineWidth, gridLineWidth));
+ itModelMatrix.scale(QVector3D(m_scaleX * backgroundMargin, gridLineWidth, gridLineWidth));
+
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+
+ // Set the rest of the shader bindings
+ m_shader->setUniformValue(m_shader->model(), modelMatrix);
+ m_shader->setUniformValue(m_shader->nModel(),
+ itModelMatrix.inverted().transposed());
+ m_shader->setUniformValue(m_shader->MVP(), MVPMatrix);
+
+ #if !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ // Set shadow shader bindings
+ m_shader->setUniformValue(m_shader->shadowQ(), m_shadowQualityToShader);
+ m_shader->setUniformValue(m_shader->depth(), depthMVPMatrix);
+ m_shader->setUniformValue(m_shader->lightS(),
+ m_cachedTheme.m_lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(m_shader, m_gridLineObj, 0, m_depthTexture);
+ } else
+ #endif
+ {
+ // Set shadowless shader bindings
+ m_shader->setUniformValue(m_shader->lightS(),
+ m_cachedTheme.m_lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(m_shader, m_gridLineObj);
+ }
+
+ linePos += lineStep;
+ }
+
+ // Wall lines: side wall
+ linePos = -1.0;
+ GLfloat xPos = -backgroundMargin;
+
+ if (m_xFlipped)
+ xPos = backgroundMargin;
+
+ for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(xPos * m_scaleX, linePos, 0.0f + zComp);
+
+ modelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth, m_scaleZ * backgroundMargin));
+ itModelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth, m_scaleZ * backgroundMargin));
+
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+
+ // Set the rest of the shader bindings
+ m_shader->setUniformValue(m_shader->model(), modelMatrix);
+ m_shader->setUniformValue(m_shader->nModel(),
+ itModelMatrix.inverted().transposed());
+ m_shader->setUniformValue(m_shader->MVP(), MVPMatrix);
+
+ #if !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ // Set shadow shader bindings
+ m_shader->setUniformValue(m_shader->shadowQ(), m_shadowQualityToShader);
+ m_shader->setUniformValue(m_shader->depth(), depthMVPMatrix);
+ m_shader->setUniformValue(m_shader->lightS(),
+ m_cachedTheme.m_lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(m_shader, m_gridLineObj, 0, m_depthTexture);
+ } else
+ #endif
+ {
+ // Set shadowless shader bindings
+ m_shader->setUniformValue(m_shader->lightS(),
+ m_cachedTheme.m_lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(m_shader, m_gridLineObj);
+ }
+
+ linePos += lineStep;
+ }
+ }
+ }
}
void Surface3dRenderer::updateSegmentCount(GLint segmentCount, GLfloat step, GLfloat minimum)
@@ -524,14 +724,6 @@ void Surface3dRenderer::setXZStuff(GLint segmentXCount, GLint segmentZCount)
calculateSceneScalingFactors();
}
-void Surface3dRenderer::updateTheme(Theme theme)
-{
- m_cachedTheme.setFromTheme(theme);
-
- // Update things depending from the theme
- updateSurfaceGradient();
-}
-
void Surface3dRenderer::updateSurfaceGradient()
{
QImage image(QSize(4, 100), QImage::Format_RGB32);
@@ -548,6 +740,19 @@ void Surface3dRenderer::updateSurfaceGradient()
m_gradientTexture = m_textureHelper->create2DTexture(image, false, true);
}
+void Surface3dRenderer::requestSelectionAtPoint(const QPoint &point)
+{
+ Q_UNUSED(point)
+
+// QMutexLocker locker(&m_mutex);
+// m_selectionPointRequest.setX(point.x());
+// m_selectionPointRequest.setY(point.y());
+// m_isSelectionPointRequestActive = true;
+
+ m_querySelection = true;
+}
+
+// This one needs to be called when the data size changes
void Surface3dRenderer::updateSelectionTexture()
{
// Create the selection ID image. Each grid corner gets 2x2 pixel area of
@@ -595,7 +800,10 @@ void Surface3dRenderer::updateSelectionTexture()
// Release the temp bits allocation
delete bits;
+}
+void Surface3dRenderer::initSelectionBuffer()
+{
// Create the result selection texture and buffers
if (m_selectionResultTexture) {
m_textureHelper->deleteTexture(&m_selectionResultTexture);
@@ -635,11 +843,6 @@ void Surface3dRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a
*a = (id >> 24) & ID_TO_RGBA_MASK;
}
-void Surface3dRenderer::getSelection()
-{
- m_querySelection = true;
-}
-
void Surface3dRenderer::setSeries(QList<qreal> series)
{
m_series = series;
@@ -656,24 +859,27 @@ void Surface3dRenderer::setSeries(QList<qreal> series)
updateSelectionTexture();
}
+void Surface3dRenderer::updateTextures()
+{
+ qDebug() << "Surface3dRenderer::updateTextures() NEED TO DO SOMETHING";
+ // Drawer has changed; this flag needs to be checked when checking if we need to update labels
+ //m_updateLabels = true;
+}
+
void Surface3dRenderer::calculateSceneScalingFactors()
{
// Calculate scene scaling and translation factors
- // m_rowWidth = ((m_columnCount + 1) * m_barSpacing.width()) / 2.0f;
- // m_columnDepth = ((m_rowCount + 1) * m_barSpacing.height()) / 2.0f;
- // m_maxDimension = qMax(m_rowWidth, m_columnDepth);
- // m_scaleFactor = qMin((m_columnCount * (m_maxDimension / m_maxSceneSize)),
- // (m_rowCount * (m_maxDimension / m_maxSceneSize)));
- // m_scaleX = m_barThickness.width() / m_scaleFactor;
- // m_scaleZ = m_barThickness.height() / m_scaleFactor;
-
m_xLength = m_segmentXCount;
m_zLength = m_segmentZCount;
m_maxDimension = qMax(m_xLength, m_zLength);
- m_scaleFactor = qMin((m_segmentXCount * (m_maxDimension / m_maxSceneSize)),
- (m_segmentZCount * (m_maxDimension / m_maxSceneSize)));
- m_scaleX = 1.0f / m_scaleFactor; // TODO: correspondance to m_barThickness
- m_scaleZ = 1.0f / m_scaleFactor; // TODO: correspondance to m_barThickness
+// m_scaleFactor = qMin((m_segmentXCount * (m_maxDimension / m_maxSceneSize)),
+// (m_segmentZCount * (m_maxDimension / m_maxSceneSize)));
+// m_scaleX = 1.0f / m_scaleFactor;
+// m_scaleZ = 1.0f / m_scaleFactor;
+
+ m_scaleFactor = qMax(m_xLength, m_zLength);
+ m_scaleX = (coordRatio * m_xLength) / m_scaleFactor;
+ m_scaleZ = (coordRatio * m_zLength) / m_scaleFactor;
//qDebug() << "m_scaleX" << m_scaleX << "m_scaleFactor" << m_scaleFactor;
//qDebug() << "m_scaleZ" << m_scaleZ << "m_scaleFactor" << m_scaleFactor;
@@ -702,9 +908,6 @@ void Surface3dRenderer::updateSurfaceGridStatus(bool enable)
void Surface3dRenderer::loadBackgroundMesh()
{
- if (!m_isInitialized)
- return;
-
if (m_backgroundObj)
delete m_backgroundObj;
if (m_hasNegativeValues)
@@ -716,9 +919,6 @@ void Surface3dRenderer::loadBackgroundMesh()
void Surface3dRenderer::loadSurfaceObj()
{
- if (!m_isInitialized)
- return;
-
if (m_surfaceObj)
delete m_surfaceObj;
m_surfaceObj = new SurfaceObject();
@@ -733,81 +933,17 @@ void Surface3dRenderer::loadGridLineMesh()
m_gridLineObj->load();
}
-const QSize Surface3dRenderer::size()
-{
- return m_boundingRect.size();
-}
-
-const QRect Surface3dRenderer::boundingRect()
-{
- return m_boundingRect;
-}
-
-void Surface3dRenderer::setBoundingRect(const QRect boundingRect)
-{
- m_boundingRect = boundingRect;
- handleResize();
-}
-
-void Surface3dRenderer::setWidth(const int width)
-{
- m_boundingRect.setWidth(width);
- handleResize();
-}
-
-int Surface3dRenderer::width()
-{
- return m_boundingRect.width();
-}
-
-void Surface3dRenderer::setHeight(const int height)
-{
- m_boundingRect.setHeight(height);
- handleResize();
-}
-
-int Surface3dRenderer::height()
-{
- return m_boundingRect.height();
-}
-
-void Surface3dRenderer::setX(const int x)
-{
- m_boundingRect.setX(x);
-}
-
-int Surface3dRenderer::x()
-{
- return m_boundingRect.x();
-}
-
-void Surface3dRenderer::setY(const int y)
-{
- m_boundingRect.setY(y);
-}
-
-int Surface3dRenderer::y()
-{
- return m_boundingRect.y();
-}
-
void Surface3dRenderer::handleResize()
{
- if (!m_isInitialized)
+ if (m_cachedBoundingRect.width() == 0 || m_cachedBoundingRect.height() == 0)
return;
- qDebug() << "Surface3dRenderer::handleResize " << width() << "x" << height();
-
- m_mainViewPort = QRect(0, 0, width(), height());
- m_sliceViewPort = QRect(0, 0, width(), height());
-
-#if !defined(QT_OPENGL_ES_2)
- // Re-init depth buffer
- updateDepthBuffer();
-#endif
+ m_mainViewPort = QRect(0, 0, m_cachedBoundingRect.width(), m_cachedBoundingRect.height());
if (m_selectionPointer)
m_selectionPointer->updateBoundingRect(m_mainViewPort);
+
+ Abstract3DRenderer::handleResize();
}
#if !defined(QT_OPENGL_ES_2)
@@ -853,9 +989,7 @@ void Surface3dRenderer::surfacePointSelected(qreal value, int column, int row)
m_selectionPointer = new SelectionPointer(m_controller);
m_selectionPointer->setPosition(normalize(float(column), value, float(row)));
- m_selectionPointer->setScaling(QVector3D(m_xLength / m_scaleFactor,
- 1.0f,
- m_zLength / m_scaleFactor));
+ m_selectionPointer->setScaling(QVector3D(m_scaleX, 1.0f, m_scaleZ));
m_selectionPointer->setLabel(QString::number(value));
m_selectionPointer->updateBoundingRect(m_mainViewPort);
@@ -881,6 +1015,25 @@ void Surface3dRenderer::surfacePointCleared()
}
}
+void Surface3dRenderer::loadMeshFile()
+{
+ qDebug() << "Surface3dRenderer::loadMeshFile() should we do something";
+}
+
+void Surface3dRenderer::updateShadowQuality(QDataVis::ShadowQuality quality)
+{
+ Q_UNUSED(quality)
+ qDebug() << "Surface3dRenderer::updateShadowQuality NEED TO DO SOMETHING";
+}
+
+void Surface3dRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader)
+{
+ if (m_shader)
+ delete m_shader;
+ m_shader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_shader->initialize();
+}
+
void Surface3dRenderer::initBackgroundShaders(const QString &vertexShader,
const QString &fragmentShader)
{
diff --git a/src/datavis3d/engine/surface3drenderer_p.h b/src/datavis3d/engine/surface3drenderer_p.h
index b681db87..eee66705 100644
--- a/src/datavis3d/engine/surface3drenderer_p.h
+++ b/src/datavis3d/engine/surface3drenderer_p.h
@@ -38,6 +38,7 @@
#include "datavis3dglobal_p.h"
#include "surface3dcontroller_p.h"
+#include "abstract3drenderer_p.h"
class QOpenGLShaderProgram;
@@ -52,36 +53,20 @@ class Drawer;
class CameraHelper;
class SelectionPointer;
-class QT_DATAVIS3D_EXPORT Surface3dRenderer : public QObject, protected QOpenGLFunctions
+class QT_DATAVIS3D_EXPORT Surface3dRenderer : public Abstract3DRenderer
{
Q_OBJECT
public:
- enum MousePressType {
- MouseNone = 0,
- MouseOnScene,
- MouseOnOverview,
- MouseOnZoom,
- MouseRotating,
- MouseOnPinch
- };
-
Surface3dController *m_controller;
- // Interaction related parameters // TODO: Moved to controller
- MousePressType m_mousePressed;
- QPoint m_mousePos;
- QDataVis::SelectionMode m_selectionMode;
-
// Visual parameters
QRect m_boundingRect;
Theme m_cachedTheme;
QDataVis::LabelTransparency m_labelTransparency;
QFont m_font;
bool m_isGridEnabled;
- bool m_isBackgroundEnabled;
QDataVis::ShadowQuality m_shadowQuality;
- bool m_hasNegativeValues;
private:
// Data parameters
@@ -93,13 +78,11 @@ private:
// Internal attributes purely related to how the scene is drawn with GL.
QRect m_mainViewPort;
- QRect m_sliceViewPort;
+ ShaderHelper *m_shader;
ShaderHelper *m_backgroundShader;
ShaderHelper *m_surfaceShader;
ShaderHelper *m_surfaceGridShader;
ShaderHelper *m_selectionShader;
- TextureHelper *m_textureHelper;
- bool m_isInitialized;
GLfloat m_yRange; // m_heightNormalizer
GLfloat m_yAdjustment;
GLfloat m_xLength;
@@ -125,8 +108,12 @@ private:
bool m_cachedSurfaceGridOn;
SelectionPointer *m_selectionPointer;
bool m_selectionActive;
+ bool m_xFlipped;
+ bool m_zFlipped;
+ bool m_yFlipped;
- Drawer *m_drawer;
+protected:
+ virtual void loadMeshFile();
public:
explicit Surface3dRenderer(Surface3dController *controller);
@@ -135,57 +122,41 @@ public:
void initializeOpenGL();
void render(CameraHelper *camera, const GLuint defaultFboHandle = 0);
- // TODO: Not thread-safe, needs rethinking how axes create labels
- Drawer *drawer() { return m_drawer; }
+ // TODO: temp
+ void setXZStuff(GLint segmentXCount, GLint segmentZCount);
+ void setSeries(QList<qreal> series);
public slots:
- void updateTheme(Theme theme);
void updateSmoothStatus(bool enable);
void updateSurfaceGridStatus(bool enable);
void updateSurfaceGradient();
void updateSegmentCount(GLint segmentCount, GLfloat step, GLfloat minimum = 0.0f);
+ virtual void requestSelectionAtPoint(const QPoint &point);
- void getSelection();
-
-public:
- // Size
- const QSize size();
- const QRect boundingRect();
- void setBoundingRect(const QRect boundingRect);
- void setWidth(const int width);
- int width();
- void setHeight(const int height);
- int height();
- void setX(const int x);
- int x();
- void setY(const int y);
- int y();
-
- void handleResize();
-#if !defined(QT_OPENGL_ES_2)
- void updateDepthBuffer();
-#endif
+private:
+ virtual void updateShadowQuality(QDataVis::ShadowQuality quality);
+ virtual void updateTextures();
+ virtual void initShaders(const QString &vertexShader, const QString &fragmentShader);
void loadBackgroundMesh();
void loadGridLineMesh();
void loadSurfaceObj();
-
- // TODO: temp
- void setXZStuff(GLint segmentXCount, GLint segmentZCount);
- void setSeries(QList<qreal> series);
-
-private:
void drawScene(CameraHelper *camera, const GLuint defaultFboHandle);
+ void handleResize();
void calculateSceneScalingFactors();
void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader);
void initSelectionShaders();
void initSurfaceShaders();
+ void initSelectionBuffer();
void updateSelectionTexture();
void idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a);
void fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a, int stride);
void surfacePointSelected(qreal value, int column, int row);
void surfacePointCleared();
QVector3D normalize(float x, float y, float z);
+#if !defined(QT_OPENGL_ES_2)
+ void updateDepthBuffer();
+#endif
Q_DISABLE_COPY(Surface3dRenderer)
};