#include "glitem.h" #include "glesrenderer.h" #include "gldefines.h" #include "glcolorrgba.h" #include #include #include #include #include #include #include #include #ifdef Q_OS_ANDROID #ifndef GLES #define GLES //Android uses GLES 2.0 #endif #endif GLItem::GLItem(QQuickItem *parent, const QString &vertexShaderFilename, const QString &fragmentShaderFilename) : QQuickItem(parent) { m_fragmentShaderFilename = fragmentShaderFilename; m_vertexShaderFilename = vertexShaderFilename; setFlag(ItemHasContents); m_geometryIsValid = false; m_colorArrayEnabled = false; m_texturesEnabled = false; m_lightingEnabled = true; m_activatePaintBeforeQml = false; m_activatePaintAfterQml = true; m_lightDirection = v_XYZ; m_backgroundColor = GLColorRgba::clBlue; m_eye = 1.5 * v_XYZ; m_center = v_Zero; m_up = v_Y; //pespective m_fovy = 45.0; m_aspect = 1.0; m_near = 1.0; m_far = 100.0; m_orthoMode = false; //The windowChanged signal is emitted by QQuickItem when it is added to the scenegraph. //This is the first time when a valid window exists. connect(this, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(handleWindowChanged(QQuickWindow*))); m_geometryIsValid = false; //invalidate geometry, we may need to set it up for the new window m_timer = new QTimer(this); m_timer->setInterval(20); connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimerTimeout()), Qt::DirectConnection); m_timer->start(50); m_guiThreadRotation = 0.0; m_renderThreadRotation = 0.0; m_renderer = NULL; m_firstAxesPoint = 0; m_lastAxesPoint = 0; m_viewportX = 0; m_viewportY = 0; setFlags(flags() | QQuickItem::ItemHasContents); } GLItem::~GLItem() { deleteRenderer(); } /** * @brief GlItem::updatePaintNode * @param node Returns the root of the subtree to be rendered ON TOP of scene rendered * in paint(). * @return */ QSGNode * GLItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *updatePaintNodeData) { //qDebug() << "GlItem::updatePaintNode called"; return QQuickItem::updatePaintNode(node, updatePaintNodeData); //remove the above line and uncomment the rest of this function to draw a blue rectangle //Example code for a blue rectangle /* QSGSimpleRectNode *rect = static_cast(node); if (!rect) { rect = new QSGSimpleRectNode(); } double w = 100; if(window()) w = window()->width(); rect->setRect(0, 0, w, 100); rect->setColor(Qt::blue); return rect; */ } bool GLItem::movementActivated() { return (m_timer && m_timer->isActive()); } void GLItem::paintBefore() { // qDebug() << "GlItem::paintBefore() called"; if(!m_renderer) initializeRenderer(); if(!m_geometryIsValid) setupGeometry(); if(!isVisible()) return; setupView(true, true); m_renderer->bind(); paintUnderQmlScene(); m_renderer->release(); window()->resetOpenGLState(); } void GLItem::paintAfter() { // qDebug() << "GlItem::paintBefore() called"; if(!m_renderer) initializeRenderer(); if(!m_geometryIsValid) setupGeometry(); if(!isVisible()) return; setupView(false, true); // enables scissor test m_renderer->bind(); paintOnTopOfQmlScene(); m_renderer->release(); glDisable(GL_SCISSOR_TEST); window()->resetOpenGLState(); } void GLItem::initializeRenderer() { qDebug() <<"GlItem::initializeRenderer called."; if(!m_renderer) m_renderer = new GLESRenderer(NULL, m_vertexShaderFilename, m_fragmentShaderFilename); } void GLItem::deleteRenderer() { qDebug() <<"GlItem::deleteRenderer() called."; if(m_renderer) { delete m_renderer; m_renderer = NULL; } } void GLItem::synchronizeThreads() { // qDebug() << "GLItem::synchronizeThreads"; m_renderThreadRotation = m_guiThreadRotation; } void GLItem::timeOut() { m_guiThreadRotation += 2.0; } void GLItem::toggleMove() { qDebug() << "GlItem::move() called"; if(m_timer->isActive()) m_timer->stop(); else m_timer->start(); emit movementActivatedChanged(); } void GLItem::mousePressed(int x, int y) { qDebug() << "GlItem::mousePressed at x:" << x << " y: " << y; } void GLItem::mousePositionChanged(int x, int y) { qDebug() << "GlItem::mouse position changed to x:" << x << " y: " << y; } void GLItem::mouseReleased(int x, int y) { qDebug() << "GlItem::mouse released at x:" << x << " y: " << y; } void GLItem::setViewportX(int arg) { if (m_viewportX == arg) return; m_viewportX = arg; emit viewportXChanged(arg); } void GLItem::setViewportY(int arg) { if (m_viewportY == arg) return; m_viewportY = arg; emit viewportYChanged(arg); } void GLItem::setupView(bool clearColor, bool clearDepth) { if(!m_renderer) return; qreal ratio = window()->devicePixelRatio(); int vx = int(ratio * m_viewportX); int vy = int(ratio * m_viewportY); // y from bottom upwards! int vw = int(ratio * width()); int vh = int(ratio * height()); glViewport(vx, vy, vw, vh); //qDebug() << "GLItem::setupView viewport x: " << vx << " y: " << vy << " w: " << vw << " h: " << vh; m_renderer->setViewport(vx, vy, vw, vh); glEnable(GL_SCISSOR_TEST); glScissor(vx,vy,vw,vh); m_aspect = (double)vw / (double)vh; if(! m_orthoMode ){ m_renderer->setPerspective(m_fovy, m_aspect, m_near, m_far); } else m_renderer->setOrtho(-2.0, 2.0, -2.0, 2.0, 0.1, 100.0); // for debugging m_renderer->setLookAt(m_eye, //eye m_center, //center m_up);//up //setup light before turning m_renderer->setLightDirection(m_lightDirection); m_renderer->setLightingEnabled(true); //now turn m_renderer->addTransformation(m_cameraTransform); m_renderer->rotate(m_renderThreadRotation, QVector3D(0.0,0.0,1.0)); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glDepthMask(true); //Clear buffers as required #ifndef GLES glClearDepth(1.0); #endif glClearColor(m_backgroundColor.red(), m_backgroundColor.green(), m_backgroundColor.blue(), m_backgroundColor.alpha()); GLuint clearMode = 0; if(clearColor) clearMode |= GL_COLOR_BUFFER_BIT; if(clearDepth) clearMode |= GL_DEPTH_BUFFER_BIT; glClear(clearMode); glEnable(GL_BLEND); glBlendFunc(1.0, 0.0); // glPolygonMode(GL_FRONT, GL_FILL); // glPolygonMode(GL_BACK, GL_LINES); } /** * @brief GlItem::handleWindowChanged * * Connect to the window's signals. This can not be done in the constructor, because at that time * there is no valid window yet. * @param win The window in which this QQuickItem will be painted. */ void GLItem::handleWindowChanged(QQuickWindow *win) { qDebug() << "GlItem::handleWindowChanged() called."; if (win) { // Connect the beforeRendering signal to our paint function. // Since this call is executed on the rendering thread it must be // a Qt::DirectConnection if(m_activatePaintBeforeQml) connect(win, SIGNAL(beforeRendering()), this, SLOT(paintBefore()), Qt::DirectConnection); if(m_activatePaintAfterQml) connect(win, SIGNAL(afterRendering()), this, SLOT(paintAfter()), Qt::DirectConnection); connect(win, SIGNAL(beforeSynchronizing()), this, SLOT(slotSynchronizeThreads()), Qt::DirectConnection); if(win->openglContext()) connect(win->openglContext(), SIGNAL(aboutToBeDestroyed()), //cleanup context before destroying window this, SLOT(deleteRenderer()), Qt::DirectConnection); // If we allow QML to do the clearing, they would clear what we paint // and nothing would show. win->setClearBeforeRendering(false); } } void GLItem::onTimerTimeout() { //qDebug() << "GlItem::onTimerTimeout() called"; timeOut(); if (window()) window()->update(); } void GLItem::slotSynchronizeThreads() { synchronizeThreads(); } void GLItem::setupGeometry() { m_geometryIsValid = true; } void GLItem::paintUnderQmlScene() { qDebug() << "GlItem::paintUnderQmlScene() was called. Overwrite this function to paint your scene!"; } void GLItem::paintOnTopOfQmlScene() { qDebug() << "GlItem::paintOnTopOfQmlScene() was called. Overwrite this function to paint your scene!"; } void GLItem::drawAxes(double length) { if(m_lastAxesPoint == 0) createAxes(length); GLfloat lineWidth[4]; //4 floats, just for safety glGetFloatv(GL_LINE_WIDTH, lineWidth); glLineWidth(3.0); m_renderer->setColorArrayEnabled(true); m_renderer->setLightingEnabled(false); int stride = sizeof(GLPoint); m_renderer->activateAttributeArray(GLESRenderer::VERTEX_LOCATION, m_points[0].vertexPointer(), stride); m_renderer->activateAttributeArray(GLESRenderer::COLOR_LOCATION, m_points[0].colorPointer(), stride); glDrawArrays(GL_LINES, m_firstAxesPoint, m_lastAxesPoint - m_firstAxesPoint); //Coordinate Axes m_renderer->disableAttributeArrays(); //restore old settings m_renderer->setColorArrayEnabled(false); m_renderer->setLightingEnabled(true); glLineWidth(lineWidth[0]); } void GLItem::createCube(const QVector3D &lbb, const QVector3D &rtf) { QVector3D n0 = QVector3D(0.0, 0.0, 1.0); //dummy normal QVector3D t0 = QVector3D(0.0,0.0,0.0); //dummy texture // Create all the vectors for caching QVector3D lbf = QVector3D(lbb.x(), lbb.y(), rtf.z()); QVector3D rbb = QVector3D(rtf.x(), lbb.y(), lbb.z()); QVector3D rbf = QVector3D(rtf.x(), lbb.y(), rtf.y()); QVector3D ltb = QVector3D(lbb.x(), rtf.y(), lbb.z()); QVector3D ltf = QVector3D(lbb.x(), rtf.y(), rtf.z()); QVector3D rtb = QVector3D(rtf.x(), rtf.y(), lbb.z()); // Bottom plane m_points.append(GLPoint(lbb, n0, t0, GLColorRgba::clRed)); m_points.append(GLPoint(lbf, n0, t0, GLColorRgba::clRed)); m_points.append(GLPoint(rbb, n0, t0, GLColorRgba::clRed)); m_points.append(GLPoint(rbf, n0, t0, GLColorRgba::clRed)); m_points.append(GLPoint(lbf, n0, t0, GLColorRgba::clRed)); m_points.append(GLPoint(rbb, n0, t0, GLColorRgba::clRed)); // Back plane m_points.append(GLPoint(lbb, n0, t0, GLColorRgba::clGreen)); m_points.append(GLPoint(rbb, n0, t0, GLColorRgba::clGreen)); m_points.append(GLPoint(ltb, n0, t0, GLColorRgba::clGreen)); m_points.append(GLPoint(rtb, n0, t0, GLColorRgba::clGreen)); m_points.append(GLPoint(rbb, n0, t0, GLColorRgba::clGreen)); m_points.append(GLPoint(ltb, n0, t0, GLColorRgba::clGreen)); // Left plane m_points.append(GLPoint(lbb, n0, t0, GLColorRgba::clBlue)); m_points.append(GLPoint(lbf, n0, t0, GLColorRgba::clBlue)); m_points.append(GLPoint(ltb, n0, t0, GLColorRgba::clBlue)); m_points.append(GLPoint(ltf, n0, t0, GLColorRgba::clBlue)); m_points.append(GLPoint(lbf, n0, t0, GLColorRgba::clBlue)); m_points.append(GLPoint(ltb, n0, t0, GLColorRgba::clBlue)); // Right plane m_points.append(GLPoint(rbb, n0, t0, GLColorRgba::clBlue)); m_points.append(GLPoint(rbf, n0, t0, GLColorRgba::clBlue)); m_points.append(GLPoint(rtb, n0, t0, GLColorRgba::clBlue)); m_points.append(GLPoint(rtf, n0, t0, GLColorRgba::clBlue)); m_points.append(GLPoint(rbf, n0, t0, GLColorRgba::clBlue)); m_points.append(GLPoint(rtb, n0, t0, GLColorRgba::clBlue)); // Top plane m_points.append(GLPoint(ltb, n0, t0, GLColorRgba::clRed)); m_points.append(GLPoint(ltf, n0, t0, GLColorRgba::clRed)); m_points.append(GLPoint(rtb, n0, t0, GLColorRgba::clRed)); m_points.append(GLPoint(rtf, n0, t0, GLColorRgba::clRed)); m_points.append(GLPoint(ltf, n0, t0, GLColorRgba::clRed)); m_points.append(GLPoint(rtb, n0, t0, GLColorRgba::clRed)); // Front plane m_points.append(GLPoint(lbf, n0, t0, GLColorRgba::clGreen)); m_points.append(GLPoint(rbf, n0, t0, GLColorRgba::clGreen)); m_points.append(GLPoint(ltf, n0, t0, GLColorRgba::clGreen)); m_points.append(GLPoint(rtf, n0, t0, GLColorRgba::clGreen)); m_points.append(GLPoint(rbf, n0, t0, GLColorRgba::clGreen)); m_points.append(GLPoint(ltf, n0, t0, GLColorRgba::clGreen)); } void GLItem::createAxis(double length, const QVector3D & origin, const QVector3D & axis, const QVector3D & normal, const QVector3D & texCoord, const GLColorRgba& color) { int ticks = floor(length); double tickLength = 0.1; double tickLongLength = tickLength * 2; m_points.append(GLPoint(origin, normal, texCoord,color)); m_points.append(GLPoint(axis * length, normal, texCoord,color)); for(int tick = 1; tick <= ticks; tick++) { if(tick % 5 == 0) { m_points.append(GLPoint(origin + axis * tick, normal, texCoord, color)); m_points.append(GLPoint(origin + axis * tick + normal * tickLongLength, normal, texCoord, color)); } else { m_points.append(GLPoint(origin + axis * tick, normal, texCoord, color)); m_points.append(GLPoint(origin + axis * tick + normal * tickLength, normal, texCoord, color)); } } } void GLItem::createAxes(double length) { m_firstAxesPoint = m_points.size(); //X-axis QVector3D origin = v_Zero; QVector3D axis = v_X; QVector3D normal = v_Y; QVector3D texCoord = v_Zero; GLColorRgba color = cl_Red; createAxis(length, origin, axis, normal, texCoord, color); //Y-Axis axis = v_Y; normal = v_Z; color= cl_Green; createAxis(length, origin, axis, normal, texCoord, color); //Z-Axis axis = v_Z; normal = v_X; color= cl_Blue; createAxis(length, origin, axis, normal, texCoord, color); m_lastAxesPoint = m_points.size(); }