From af64f953d4f01a7c7bd3415ff5edfff8d4f7c48d Mon Sep 17 00:00:00 2001 From: Stefan Suhren Date: Tue, 6 Oct 2015 12:52:40 +0200 Subject: Initial commit --- src/glcolorrgba.cpp | 43 +++ src/glcolorrgba.h | 38 +++ src/gldefines.h | 49 ++++ src/glesrenderer.cpp | 776 +++++++++++++++++++++++++++++++++++++++++++++++++ src/glesrenderer.h | 472 ++++++++++++++++++++++++++++++ src/glitem.cpp | 408 ++++++++++++++++++++++++++ src/glitem.h | 290 ++++++++++++++++++ src/glpoint.cpp | 13 + src/glpoint.h | 45 +++ src/main.cpp | 15 + src/mmscene.cpp | 89 ++++++ src/mmscene.h | 43 +++ src/shaderdebugger.cpp | 103 +++++++ src/shaderdebugger.h | 68 +++++ 14 files changed, 2452 insertions(+) create mode 100644 src/glcolorrgba.cpp create mode 100644 src/glcolorrgba.h create mode 100644 src/gldefines.h create mode 100644 src/glesrenderer.cpp create mode 100644 src/glesrenderer.h create mode 100644 src/glitem.cpp create mode 100644 src/glitem.h create mode 100644 src/glpoint.cpp create mode 100644 src/glpoint.h create mode 100644 src/main.cpp create mode 100644 src/mmscene.cpp create mode 100644 src/mmscene.h create mode 100644 src/shaderdebugger.cpp create mode 100644 src/shaderdebugger.h (limited to 'src') diff --git a/src/glcolorrgba.cpp b/src/glcolorrgba.cpp new file mode 100644 index 0000000..28d1008 --- /dev/null +++ b/src/glcolorrgba.cpp @@ -0,0 +1,43 @@ +#include "glcolorrgba.h" + +GLColorRgba GLColorRgba::clBlack = GLColorRgba(0.0,0.0,0.0,1.0); +GLColorRgba GLColorRgba::clRed = GLColorRgba(1.0,0.0,0.0,1.0); +GLColorRgba GLColorRgba::clYellow = GLColorRgba(1.0,1.0,0.0,1.0); +GLColorRgba GLColorRgba::clGreen = GLColorRgba(0.0,1.0,0.0,1.0); +GLColorRgba GLColorRgba::clCyan = GLColorRgba(0.0,1.0,1.0,1.0); +GLColorRgba GLColorRgba::clBlue = GLColorRgba(0.0,0.0,1.0,1.0); +GLColorRgba GLColorRgba::clMagenta = GLColorRgba(1.0,0.0,1.0,1.0); +GLColorRgba GLColorRgba::clGray = GLColorRgba(0.5,0.5,0.5,1.0); +GLColorRgba GLColorRgba::clWhite = GLColorRgba(1.0,1.0,1.0,1.0); + +GLColorRgba::GLColorRgba(float r, float g, float b, float a) + :QVector4D(r,g,b,a) +{ + //nothing else to do +} + +GLColorRgba::GLColorRgba(const GLColorRgba &other) + :QVector4D(other.red(),other.green(),other.blue(),other.alpha()) +{ +} + +GLColorRgba::GLColorRgba(const QVector4D &other) + :QVector4D(other.x(),other.y(),other.z(),other.w()) +{ +} + +/** + * Returns the inverted color with a= 1.0 + */ +GLColorRgba GLColorRgba::inverted() +{ + return GLColorRgba(1.0 - x(), 1.0 - y(), 1.0 - z(), 1.0); +} + +GLColorRgba GLColorRgba::operator *(float factor) +{ + return GLColorRgba(red()*factor, + green() *factor, + blue() * factor, + alpha()); +} diff --git a/src/glcolorrgba.h b/src/glcolorrgba.h new file mode 100644 index 0000000..53efcc2 --- /dev/null +++ b/src/glcolorrgba.h @@ -0,0 +1,38 @@ +#ifndef GLCOLORRGBA_H +#define GLCOLORRGBA_H + +#include +/** + * @brief The GlColorRgba class is a convenience class to replace QVector4D for color variables. + */ +class GLColorRgba: public QVector4D{ +public: + GLColorRgba(float r = 0.0, float g= 0.0, float b = 0.0, float a = 1.0); + GLColorRgba(const GLColorRgba & other); + GLColorRgba(const QVector4D & other); + /** + * Returns the inverted color with a= 1.0 + */ + GLColorRgba inverted(); + + GLColorRgba operator * (float factor); + //convenience getters + float red()const{return x();} + float green()const{return y();} + float blue()const{return z();} + float alpha()const{return w();} + +//predefined colors + static GLColorRgba clBlack; + static GLColorRgba clRed; + static GLColorRgba clYellow; + static GLColorRgba clGreen; + static GLColorRgba clCyan; + static GLColorRgba clBlue; + static GLColorRgba clMagenta; + static GLColorRgba clGray; + static GLColorRgba clWhite; +}; + + +#endif // GLCOLORRGBA_H diff --git a/src/gldefines.h b/src/gldefines.h new file mode 100644 index 0000000..3425b43 --- /dev/null +++ b/src/gldefines.h @@ -0,0 +1,49 @@ +/******************************************************** + * Convenience objects for building OpenGL surfaces. + * + * Created on: 24.04.2012 + * Author: Walter Roth + * Copyright by Walter Roth 2012, Licence: GPL + * + *******************************************************/ + +/** \file + * \brief Convenience objects for building OpenGL surfaces. + * + * Predefined 3D-vectors and colors that are frequently used. + */ + +#ifndef GLDEFINES_H +#define GLDEFINES_H + +#include +#include "glcolorrgba.h" + +//Predefined vectors + +const static QVector3D v_Zero = QVector3D(0.0, 0.0, 0.0); +const static QVector3D v_X = QVector3D(1.0, 0.0, 0.0); +const static QVector3D v_Y = QVector3D(0.0, 1.0, 0.0); +const static QVector3D v_Z = QVector3D(0.0, 0.0, 1.0); +const static QVector3D v_XY = QVector3D(1.0, 1.0, 0.0); +const static QVector3D v_XZ = QVector3D(1.0, 0.0, 1.0); +const static QVector3D v_YZ = QVector3D(0.0, 1.0, 1.0); +const static QVector3D v_XYZ = QVector3D(1.0, 1.0, 1.0); + + +/** Predefined global colors for convenience. + */ +const static GLColorRgba cl_White = GLColorRgba(1.0,1.0,1.0); +const static GLColorRgba cl_Black = GLColorRgba(0.0,0.0,0.0); +const static GLColorRgba cl_Red = GLColorRgba(1.0,0.0,0.0); +const static GLColorRgba cl_Green = GLColorRgba(0.0,1.0,0.0); +const static GLColorRgba cl_Blue = GLColorRgba(0.0,0.0,1.0); +const static GLColorRgba cl_LightBlue = GLColorRgba(0.5,0.5,1.0); +const static GLColorRgba cl_Gray = GLColorRgba(0.5,0.5,0.5); +const static GLColorRgba cl_Yellow = GLColorRgba(1.0,1.0,0.0); +const static GLColorRgba cl_Magenta = GLColorRgba(1.0,0.0,1.0); +const static GLColorRgba cl_Cyan = GLColorRgba(0.0,1.0,1.0); + + + +#endif // GLDEFINES_H diff --git a/src/glesrenderer.cpp b/src/glesrenderer.cpp new file mode 100644 index 0000000..7d7339d --- /dev/null +++ b/src/glesrenderer.cpp @@ -0,0 +1,776 @@ +/*************************************************************************** + * Copyright (C) 2008, 2012 by Walter Roth * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "glesrenderer.h" + +#include + +//#define DEBUG_GLESRENDERER + +//#ifdef DEBUG_GLESRENDERER +#include "shaderdebugger.h" +//#endif + +#ifdef Q_OS_ANDROID +#ifndef GLES + #define GLES //Android uses GLES 2.0 +#endif +#endif + + +GLESRenderer::GLESRenderer(QObject *parent, + const QString & vShaderFilename, + const QString & fShaderFilename ) + :QObject(parent) +{ + this->m_vShaderFileName = vShaderFilename; + this->m_fShaderFileName = fShaderFilename; + + //flags + m_initialized = false; + m_bound = false; + m_lightingEnabled = false; // flag for lighting + m_colorArrayEnabled = false; + m_invertedMvpMatrixValid = false; //delay matrix inversion until it is really neccessary + m_textureEnabled = false; + + m_lightDirection = QVector3D(1.0,1.0,1.0); //position of directional light + m_pointSize = 4.0; + m_shininess = 200.0; + m_ambientAndDiffuseColor = cl_White; + m_ambientLightBrightness = 0.2; + m_specularColor = cl_White; + + m_vShader = NULL; //vertex shader, render thread + m_fShader = NULL; // fragment shader, render thread + m_renderProgram = NULL;// GUI-Thread + + // Locations of shader variables + //attributes + m_location_aVertex = -1; + m_location_aColor = -1; + m_location_aNormal = -1; + m_location_aTexCoord = -1; + //uniforms + //flags + m_location_uColorArrayEnabled = -1; + m_location_uTextureEnabled = -1; + m_location_uLightingEnabled = -1; + //matrices + m_location_uNormalMatrix = -1; + m_location_uMvpMatrix = -1; + //lighting + m_location_uAmbientAndDiffuseColor = -1; + m_location_uAmbientLightBrightness = -1; + m_location_uSpecularColor = -1; + m_location_uLightDirection = -1; + m_location_uHalfPlaneVector = -1; + //texture + m_location_uTextureSampler = -1; + + //viewport + m_viewportX = 0; + m_viewportY = 0; + m_viewportW = 100; + m_viewportH = 100; + + //clippings + m_fovy = 45.0; + m_aspect = 1.0; + m_nearClip = 1.0; + m_farClip = 10.0; + +} +/** + * @brief GLESRenderer::~GLESRenderer + * We need to delete the shaders here. They can not be added to the child list, because they live in render thread. + */ +GLESRenderer::~GLESRenderer() +{ + if(m_vShader) + delete m_vShader; + if(m_fShader) + delete m_fShader; +} + +/** + * Set modelview matrix. Updates mvpMatrix and normalMatrix too. + * Call setPMatrix first. + */ +void GLESRenderer::setMvMatrix(const QMatrix4x4 newVal) +{ + m_mvMatrix = newVal; + m_normalMatrix = m_mvMatrix.normalMatrix(); //invert and transpose mvMatrix + m_invertedMvpMatrixValid = false; //delay matrix inversion until it is really neccessary + + if(m_bound && (m_location_uNormalMatrix != -1)) + m_renderProgram->setUniformValue(m_location_uNormalMatrix, m_normalMatrix); + + m_mvpMatrix = m_pMatrix * m_mvMatrix; + if(m_bound && (m_location_uMvpMatrix != -1)) + m_renderProgram->setUniformValue(m_location_uMvpMatrix, m_mvpMatrix); +#ifdef DEBUG_GLESRENDERER + ShaderDebugger::debugMatrix4x4(m_mvMatrix, "GLESRenderer uses modelview matrix:"); + ShaderDebugger::debugMatrix3x3(m_normalMatrix, "GLESRenderer uses normal matrix:"); + ShaderDebugger::debugMatrix4x4(m_mvpMatrix, "GLESRenderer uses MVP matrix:"); +#endif +} + +/** + * Sets mvMatrix to a lookAt transformation. + * Call setPMatrix or setPerspective first. + */ +void GLESRenderer::setLookAt(const QVector3D & eye,const QVector3D & center,const QVector3D & up ) +{ + QMatrix4x4 m; + m.setToIdentity(); + m.lookAt(eye, center, up); + setMvMatrix(m); +} + +/** + * Set projection matrix. Call setMvMatrix after this. + */ +void GLESRenderer::setPMatrix(const QMatrix4x4 newVal) +{ + m_pMatrix = newVal; + m_mvpMatrix = m_pMatrix * m_mvMatrix; + if(m_bound && (m_location_uMvpMatrix != -1)) + m_renderProgram->setUniformValue(m_location_uMvpMatrix, m_mvpMatrix); +} + +/** + * Setup projection matrix. Call setMvMatrix after this. + */ +void GLESRenderer::setPerspective(GLfloat fovy, GLfloat aspect, GLfloat nearClip, GLfloat farClip) +{ + m_fovy = fovy; + m_aspect = aspect; + m_nearClip = nearClip; + m_farClip = farClip; + m_pMatrix.setToIdentity(); + m_pMatrix.perspective(m_fovy, m_aspect, m_nearClip, m_farClip); + m_mvpMatrix = m_pMatrix * m_mvMatrix; + if(m_bound && (m_location_uMvpMatrix != -1)) + m_renderProgram->setUniformValue(m_location_uMvpMatrix, m_mvpMatrix); +#ifdef DEBUG_GLESRENDERER + ShaderDebugger::debugMatrix4x4(m_pMatrix, "GLESRenderer uses projection matrix:"); +#endif +} + +void GLESRenderer::setOrtho(float left, float right, float bottom, float top, float nearPlane, float farPlane) +{ + m_fovy = 0.0; + m_aspect = (left - right) / (top - bottom); + m_nearClip = nearPlane; + m_farClip = farPlane; + m_pMatrix.setToIdentity(); + m_pMatrix.ortho(left, right, bottom, top, nearPlane, farPlane); + m_mvpMatrix = m_pMatrix * m_mvMatrix; + if(m_bound && (m_location_uMvpMatrix != -1)) + m_renderProgram->setUniformValue(m_location_uMvpMatrix, m_mvpMatrix); +#ifdef DEBUG_GLESRENDERER + ShaderDebugger::debugMatrix4x4(m_pMatrix, "GLESRenderer uses projection matrix:"); +#endif +} + +/** + * Set viewport + */ +void GLESRenderer::setViewport(int x, int y, int w, int h) +{ + m_viewportX = x; + m_viewportY = y; + m_viewportW = w; + m_viewportH = h; + //qDebug() << "GLESRenderer::setViewport x:" << x << " y: " << y << " w: " << w << " h: " << h; +} + +/** + * Uses viewport and matrix settings to reverse the vertex transformation chain. + * WinX, winY, winZ are the coordinates in window coordinates. Lower left of viewport + * is winX = 0, winY = 0. Use winZ = 0.0 for a point on near clipping plane + * and winZ = 1.0 for a point on far clipping plane. + * This function assumes, that the vertex shader computes gl_positon as v * mvpMatrix. + * Use a custom shader reverse function, if this is not true. + * Returns coordinate in object space. + */ +QVector3D GLESRenderer::unProjectViewportPoint(const QVector3D & vWin) +{ + QVector3D vClip = viewportToClip(vWin); + if(!m_invertedMvpMatrixValid) + { + m_invertedMvpMatrix = m_mvpMatrix.inverted(); + m_invertedMvpMatrixValid = true; + } + ShaderDebugger::debugMatrix4x4(m_mvpMatrix, "MVP Matrix:"); + ShaderDebugger::debugMatrix4x4(m_invertedMvpMatrix, "Inverted MVP Matrix:"); + QVector3D result = m_invertedMvpMatrix * vClip; + ShaderDebugger::debugVector3D(result, "Vector in model space:"); + return result; +} + +/** + * Performs inverse viewport transform. + */ +QVector3D GLESRenderer::viewportToClip(const QVector3D & vWin) +{ + //reverse viewport transformation, for original code see below + float xClip = (vWin.x() - (float)m_viewportX) / (float)m_viewportW * 2.0 - 1.0; + float yClip = (vWin.y() - (float)m_viewportY) / (float)m_viewportH * 2.0 - 1.0; + float zClip = 2.0 * vWin.z() - 1.0; + + // original code from gluUnproject + //Transformation of normalized coordinates between -1 and 1 + // in[0]=(winx-(float)viewport[0])/(float)viewport[2]*2.0-1.0; + // in[1]=(winy-(float)viewport[1])/(float)viewport[3]*2.0-1.0; + // in[2]=2.0*winz-1.0; + // in[3]=1.0; + + QVector3D result = QVector3D(xClip, yClip, zClip); + ShaderDebugger::debugVector3D(result, "Vector in clip space:"); + return result; +} + +/** + * Returns the points on near and far clipping plane, that correspond to the + * mouseX and mouseY coordinates of a mouse click. + * mouseX and mouseY are coordinates as delivered by QMouseEvent or QDeclarativeMouseEvent. + */ +void GLESRenderer::calculateMousePoints(QVector3D * nearPoint, QVector3D * farPoint, const QPoint & mousePos) +{ + float winX = m_viewportX + mousePos.x(); + float winY = m_viewportY + (m_viewportH - mousePos.y()); + * nearPoint = unProjectViewportPoint(QVector3D(winX, winY, 0.0)); + * farPoint = unProjectViewportPoint(QVector3D(winX, winY, 1.0)); +} + +/** + * Calculate the distance of the "mouse ray line" to point p in model space. + * mouseX, mouseY are the coordinates of the mouse click as delivered by QMouseEvent. + * Returns distance. + */ +float GLESRenderer::distanceToMouseClick(QVector3D p, const QPoint & mousePos) +{ + QVector3D nearPoint, farPoint; + calculateMousePoints(&nearPoint, &farPoint, mousePos); + + QVector3D lineVector = nearPoint - farPoint; + float area = QVector3D::crossProduct(p - nearPoint, lineVector).length(); + float distance = area / lineVector.length(); + + return distance; +} + +/** + * Calculates intersection of "mouse ray line" with the plane defined by normal and d. + * mouseX, mouseY are the coordinates of the mouse click as delivered by QMouseEvent. + * Returns true, if intersection is in the visible frustum, else returns false. + * If return value is true, *intersection is the intersection of the mouse ray line with + * the plane normal*(x,y,z) + d = 0. + * If return value is false, *intersection is not modified. + */ +bool GLESRenderer::mouseIntersection(QVector3D * intersection, QVector3D normal, float d, const QPoint & mousePos) +{ + float m0, m15; + QVector3D pNear, pFar; //mouse intersections on near and far clipping plane + calculateMousePoints(&pNear, &pFar, mousePos); + QVector3D v = pFar - pNear; //vector from near to far clipping plane + m15 = d + QVector3D::dotProduct(normal, pNear); + m0 = QVector3D::dotProduct( normal, v); + if(fabs(m0) < fabs(m15)) //we have no intersection in frustum (lambda will be > 1.0) + { + return false; + } + float lambda = -m15 / m0; + * intersection = pNear + lambda * v; + return true; +} + +/** + * Set the color for ambient and diffuse lighting (or no lighting). + * Alternatively use a color array and color attribute. + */ +void GLESRenderer::setAmbientAndDiffuseColor(const GLColorRgba newVal) +{ + m_ambientAndDiffuseColor = newVal; + if(m_bound && m_location_uAmbientAndDiffuseColor != -1) + m_renderProgram->setUniformValue(m_location_uAmbientAndDiffuseColor, m_ambientAndDiffuseColor); +} + +/** + * Set the dimming factor for ambient light. + * Defaults to 0.2. + */ +void GLESRenderer::setAmbientLightBrightness(float newVal) +{ + m_ambientLightBrightness = newVal; + if(m_bound && m_location_uAmbientLightBrightness != -1) + m_renderProgram->setUniformValue(m_location_uAmbientLightBrightness, m_ambientLightBrightness); +} + +/** + * Set the color for specular lighting. + */ +void GLESRenderer::setSpecularColor(const GLColorRgba newVal) +{ + m_specularColor = newVal; + if(m_bound && (m_location_uSpecularColor != -1)) + m_renderProgram->setUniformValue(m_location_uSpecularColor, + m_specularColor.red(), m_specularColor.green(), + m_specularColor.blue(), m_specularColor.alpha()); +} + +/** + * Set the shininess for specular lighting. + */ +void GLESRenderer::setShininess(float newVal) +{ + m_shininess = newVal; + if(m_bound && (m_location_uShininess != -1)) + m_renderProgram->setUniformValue(m_location_uShininess, m_shininess); +} + +/** + * Enable / disable lighting. + */ +void GLESRenderer::setLightingEnabled(bool newVal) +{ + m_lightingEnabled = newVal; + if(m_bound && (m_location_uLightingEnabled != -1)) + m_renderProgram->setUniformValue(m_location_uLightingEnabled, m_lightingEnabled); +} + +/** + * Enable / disable color array. + */ +void GLESRenderer::setColorArrayEnabled(bool newVal) +{ + m_colorArrayEnabled = newVal; + if(m_bound && (m_location_uColorArrayEnabled != -1)) + m_renderProgram->setUniformValue(m_location_uColorArrayEnabled, m_colorArrayEnabled); +} + +/** + * Set the texture flag. + */ +void GLESRenderer::setTextureEnabled(bool newVal) +{ + m_textureEnabled = newVal; + if(m_bound && (m_location_uTextureEnabled != -1)) + m_renderProgram->setUniformValue(m_location_uTextureEnabled, m_textureEnabled); +} + +/** + * Set light direction. + */ +void GLESRenderer::setLightDirection(const QVector3D & newVal) +{ + m_lightDirection = newVal; +#ifdef DEBUG_GLESRENDERER + ShaderDebugger::debugVector3D(m_lightDirection, "GLESRenderer uses lightDirection in object space:"); +#endif + QMatrix4x4 nMatrix = QMatrix4x4(m_normalMatrix); + m_lightDirection = (nMatrix * m_lightDirection).normalized();//transform to eye space + m_halfPlaneVector = (m_lightDirection + QVector3D(0.0,0.0,1.0)).normalized();//eye direction is 0,0,1 in eye space +#ifdef DEBUG_GLESRENDERER + ShaderDebugger::debugVector3D(m_lightDirection, "GLESRenderer uses lightDirection in eye space:"); + ShaderDebugger::debugVector3D(m_lightDirection, "GLESRenderer uses halfplane vector in eye space:"); +#endif + if(m_bound && (m_location_uLightDirection != -1)) + m_renderProgram->setUniformValue(m_location_uLightDirection, m_lightDirection); + if(m_location_uHalfPlaneVector != -1) + m_renderProgram->setUniformValue(m_location_uHalfPlaneVector, m_halfPlaneVector); +} + +/** + * Set size of points drawn with GL_POINTS. + */ +void GLESRenderer::setPointSize(int newVal) +{ + m_pointSize = newVal; + if(m_bound && (m_location_uPointSize != -1)) + m_renderProgram->setUniformValue(m_location_uPointSize, m_pointSize); +#ifndef GLES + glPointSize(m_pointSize); //set point size independent of vertex shader +#endif +} + +/** + * Pops mvMatrix from stack and updates normalMatrix and mvpMatrix. + */ +void GLESRenderer::popMvMatrix() +{ + setMvMatrix(m_mvMatrixStack.pop()); +} + +/** + * Translates mvMatrix by v + */ +void GLESRenderer::translate(const QVector3D & v) +{ + m_mvMatrix.translate(v); + setMvMatrix(m_mvMatrix); //update normalMatrix and mvpMatrix and copy to shader +} + +/** + * Rotates mvMatrix by angle around axis + */ +void GLESRenderer::rotate(GLfloat angle, const QVector3D & axis) +{ + m_mvMatrix.rotate(angle, axis); + setMvMatrix(m_mvMatrix); //update normalMatrix and mvpMatrix and copy to shader +} + +void GLESRenderer::addTransformation(const QMatrix4x4 additionalTransformation) +{ + setMvMatrix(m_mvMatrix * additionalTransformation); //update normalMatrix and mvpMatrix and copy to shader +} + +/** + * Scales mvMatrix. + */ +void GLESRenderer::scale(const QVector3D & v ) +{ + m_mvMatrix.scale(v); + setMvMatrix(m_mvMatrix);//update normalMatrix and mvpMatrix and copy to shader +} + +void GLESRenderer::rotate( float angle, float x, float y, float z ) { + m_mvMatrix.rotate ( angle, x, y, z ); + setMvMatrix( m_mvMatrix ); +} /* ----- end of method mvRotate ----- */ + +void GLESRenderer::zoom( float zoomFactor ) { + // Es muss vor dem Zoom die Einheitsmatrix generiert werden. Ansonsten wird das vorherige Zoomen dazu addiert + m_pMatrix.setToIdentity(); + m_pMatrix.perspective ( m_fovy * zoomFactor, m_aspect, m_nearClip, m_farClip ); + // Auch hier muss die ProjectionModelView Matrix neu berechnet werden + m_mvpMatrix = m_pMatrix * m_mvMatrix; +} + + +void GLESRenderer::translate( float x, float y, float z ) { + m_mvMatrix.translate( x, y, z ); + setMvMatrix( m_mvMatrix); +} /* ----- end of method translate ----- */ + + +/** + * Compile shaders, get attribute and uniform locations. + * This function needs an active OpenGL context. + */ +bool GLESRenderer::initialize() +{ + if(m_initialized) + return true; + //Setup shaders and program + m_vShader = new QOpenGLShader(QOpenGLShader::Vertex,this); //vertex shader + m_vShader->compileSourceFile(m_vShaderFileName); + if(!m_vShader->isCompiled()) + { + qDebug("GLESRenderer::initialize: Compiling vertex shader failed. Log follows:\n%s", + qPrintable(m_vShader->log())); + return false; + } + m_fShader = new QOpenGLShader(QOpenGLShader::Fragment,this); // fragment shader + m_fShader->compileSourceFile(m_fShaderFileName); + if(!m_fShader->isCompiled()) + { + qDebug("GLESRenderer::initialize: Compiling fragment shader failed. Log follows:\n%s", + qPrintable(m_fShader->log())); + return false; + } + + m_renderProgram = new QOpenGLShaderProgram(this); + m_renderProgram->addShader(m_vShader); + m_renderProgram->addShader(m_fShader); + m_renderProgram->link(); + if(!m_renderProgram->isLinked()) + { + qDebug("GLESRenderer::initialize: Linking program failed. Log follows:\n%s", + qPrintable(m_renderProgram->log())); + return false; + } + + // Get all locations of shader variables + //Get locations of attributes and uniforms + //Non existing attributes and uniforms will return -1 + //Attributes + m_location_aVertex = m_renderProgram->attributeLocation("a_Vertex"); + m_location_aColor = m_renderProgram->attributeLocation("a_Color"); + m_location_aNormal = m_renderProgram->attributeLocation("a_Normal"); + m_location_aTexCoord = m_renderProgram->attributeLocation("a_TexCoord"); + //Uniforms + //flags + m_location_uLightingEnabled = m_renderProgram->uniformLocation("u_LightingEnabled"); + m_location_uColorArrayEnabled = m_renderProgram->uniformLocation("u_ColorArrayEnabled"); + m_location_uTextureEnabled = m_renderProgram->uniformLocation("u_TextureEnabled"); + //matrices + m_location_uNormalMatrix = m_renderProgram->uniformLocation("u_NormalMatrix"); + m_location_uMvpMatrix = m_renderProgram->uniformLocation("u_MvpMatrix"); + //lighting + m_location_uAmbientAndDiffuseColor = m_renderProgram->uniformLocation("u_AmbientAndDiffuseColor"); + m_location_uAmbientLightBrightness = m_renderProgram->uniformLocation("u_AmbientLightBrightness"); + m_location_uLightDirection = m_renderProgram->uniformLocation("u_LightDirection"); + m_location_uSpecularColor = m_renderProgram->uniformLocation("u_SpecularColor"); + m_location_uShininess = m_renderProgram->uniformLocation("u_Shininess"); + m_location_uHalfPlaneVector = m_renderProgram->uniformLocation("u_HalfPlaneVector"); + //texture + m_location_uTextureSampler = m_renderProgram->uniformLocation("s_Texture"); + //point size + m_location_uPointSize = m_renderProgram->uniformLocation("u_PointSize"); + +#ifdef DEBUG_GLESRENDERER + ShaderDebugger::setEnabled(true); + ShaderDebugger::debugUniforms(m_renderProgram->programId()); + ShaderDebugger::setEnabled(false); +#endif + + //get present viewport settings + readGLViewportSettings(); + m_initialized = true; + return true; +} + +/** + * Bind program and transfer attribute and uniform data to the shaders. + * Calls initialize, if not already initialized. + */ +bool GLESRenderer::bind() +{ + bool ok = true; + if(!m_initialized) + ok = initialize(); + if(!ok) + return false; + m_renderProgram->bind(); + //Activate uniforms + //flags + if(m_location_uColorArrayEnabled != -1) + m_renderProgram->setUniformValue(m_location_uColorArrayEnabled, m_colorArrayEnabled); + if(m_location_uLightingEnabled != -1) + m_renderProgram->setUniformValue(m_location_uLightingEnabled, m_lightingEnabled); + if(m_location_uTextureEnabled != -1) + m_renderProgram->setUniformValue(m_location_uTextureEnabled, m_textureEnabled); + //matrices + if( m_location_uNormalMatrix != -1) + m_renderProgram->setUniformValue(m_location_uNormalMatrix, m_normalMatrix); + if(m_location_uMvpMatrix != -1) + m_renderProgram->setUniformValue(m_location_uMvpMatrix, m_mvpMatrix); + //lighting + if(m_location_uAmbientAndDiffuseColor != -1) + m_renderProgram->setUniformValue(m_location_uAmbientAndDiffuseColor, + m_ambientAndDiffuseColor.red(), m_ambientAndDiffuseColor.green(), + m_ambientAndDiffuseColor.blue(), m_ambientAndDiffuseColor.alpha()); + if(m_location_uAmbientLightBrightness != -1) + m_renderProgram->setUniformValue(m_location_uAmbientLightBrightness, m_ambientLightBrightness); + if(m_location_uLightDirection != -1) + m_renderProgram->setUniformValue(m_location_uLightDirection, m_lightDirection); + if(m_location_uSpecularColor != -1) + m_renderProgram->setUniformValue(m_location_uSpecularColor, + m_specularColor.red(), m_specularColor.green(), + m_specularColor.blue(), m_specularColor.alpha()); + if(m_location_uShininess != -1) + m_renderProgram->setUniformValue(m_location_uShininess, m_shininess); + if(m_location_uHalfPlaneVector != -1) + m_renderProgram->setUniformValue(m_location_uHalfPlaneVector, m_halfPlaneVector); + //texture + if(m_location_uTextureSampler != -1) + m_renderProgram->setUniformValue(m_location_uTextureSampler, 0); //set sampler to use texture unit 0 + //point size + if(m_location_uPointSize != -1) + m_renderProgram->setUniformValue(m_location_uPointSize, m_pointSize); +#ifndef GLES + glPointSize(m_pointSize); //set point size independent of vertex shader +#endif + + m_renderProgram->setUniformValue("u_viewPortCenter", + QVector2D(m_viewportW / 2, m_viewportH /2)); + GLfloat diameterSquare = 200.0 * 200.0; + m_renderProgram->setUniformValue("u_diameterSquare", diameterSquare); + + + m_bound = true; + return m_bound; +} + +/** + * Enables Vertex, normal, color or texCoord arrays and sets start adresses of arrays + * arrayLocation may be: VERTEX_LOCATION, NORMAL_LOCATION, COLOR_LOCATION, TEXCOORD_LOCATION + */ +bool GLESRenderer::activateAttributeArray (AttributeLocation arrayLocation, const QVector2D *values, int stride ) +{ + return activateAttributeArray(arrayLocation, (float*)values, 2, stride); +} +/** + * Enables Vertex, normal, color or texCoord arrays and sets start adresses of arrays + * arrayLocation may be: VERTEX_LOCATION, NORMAL_LOCATION, COLOR_LOCATION, TEXCOORD_LOCATION + */ +bool GLESRenderer::activateAttributeArray (AttributeLocation arrayLocation, const QVector3D *values, int stride ) +{ + return activateAttributeArray(arrayLocation, (float*)values, 3, stride); +} +/** + * Enables Vertex, normal, color or texCoord arrays and sets start adresses of arrays + * arrayLocation may be: VERTEX_LOCATION, NORMAL_LOCATION, COLOR_LOCATION, TEXCOORD_LOCATION + */ +bool GLESRenderer::activateAttributeArray (AttributeLocation arrayLocation, const QVector4D *values, int stride ) +{ + return activateAttributeArray(arrayLocation, (float*)values, 4, stride); +} + +/** + * Enables Vertex, normal, color or texCoord arrays and sets start adresses of arrays + * arrayLocation may be: VERTEX_LOCATION, NORMAL_LOCATION, COLOR_LOCATION, TEXCOORD_LOCATION + */ +bool GLESRenderer::activateAttributeArray (AttributeLocation arrayLocation, const float * values, int tupleSize, int stride ) +{ + int location = -1; + switch(arrayLocation){ + case VERTEX_LOCATION: location = m_location_aVertex; break; + case NORMAL_LOCATION: location = m_location_aNormal; break; + case COLOR_LOCATION : location = m_location_aColor; break; + case TEXCOORD_LOCATION : location = m_location_aTexCoord; break; + default: return false; + } + + if(values && (location != -1)) + { + m_renderProgram->enableAttributeArray(location); + m_renderProgram->setAttributeArray(location, values, tupleSize, stride); + m_activeAttributeLocations.append(location); + return true; + } + else return false; +} + +///** +// * Enables Vertex, normal, color or texCoord arrays and sets start adresses of arrays +// * Type may be: VERTEX_LOCATION, COLOR_LOCATION +// */ +//bool GLESRenderer::activateAttributeArray (AttributeLocation arrayLocation, const QVector4D * values, int stride ) +//{ +// if(!m_initialized) +// return false; +// int location = -1; +// switch(arrayLocation){ +// case VERTEX_LOCATION: location = m_location_aVertex; break; +// case COLOR_LOCATION : location = m_location_aColor; break; +// default: return false; +// } + +// if(values && (location != -1)) +// { +// m_renderProgram->enableAttributeArray(location); +// m_renderProgram->setAttributeArray(location, GL_FLOAT, values, 4, stride); +// m_activeAttributeLocations.append(location); +// return true; +// } +// else return false; +//} + +bool GLESRenderer::activateAttributeBuffer(GLESRenderer::AttributeLocation bufferLocation, int bufferId, int stride) +{ + int location = -1; + int elements = 3; + int offset = 0; + switch(bufferLocation){ + case VERTEX_LOCATION: location = m_location_aVertex; break; + case NORMAL_LOCATION: location = m_location_aNormal; break; + case COLOR_LOCATION : { + location = m_location_aColor; + elements = 4; //RGBA colors + }break; + case TEXCOORD_LOCATION : location = m_location_aTexCoord; break; + default: return false; + } + if( bufferId != 0 ) { + m_renderProgram->setAttributeBuffer(location, GL_FLOAT, offset, elements, stride); + m_renderProgram->enableAttributeArray( location ); + return true; + } + else { + qDebug() << "GLESRenderer::activateAttributeBuffer: Error, invalid buffer id"; + return false; + } +} + +/** + * Disables all enabled attribute arrays. + */ +void GLESRenderer::disableAttributeArrays() +{ + for(int i = 0; i < m_activeAttributeLocations.size(); i++) + m_renderProgram->disableAttributeArray(m_activeAttributeLocations[i]); + m_activeAttributeLocations.clear(); +} + +/** + * Releases program. To be called, when all rendering is finished. + */ +void GLESRenderer::release() +{ + disableAttributeArrays(); + if(m_renderProgram) + m_renderProgram->release(); + else qDebug() << "GLESRenderer::release() called without valid render program."; + m_bound = false; +} + +/** + * get the present settings from GL engine + */ +void GLESRenderer::readGLViewportSettings() +{ + GLint vp[4]; + glGetIntegerv(GL_VIEWPORT, vp); + m_viewportX = vp[0]; + m_viewportY = vp[1]; + m_viewportW = vp[2]; + m_viewportH = vp[3]; +} + +/** + * Multiplies current mvp matrix with v. Mainly for debugging. + */ +QVector3D GLESRenderer::modelToClip(const QVector3D & v) +{ + ShaderDebugger::debugVector3D(v, "Vector in model space:"); + QVector3D result = m_mvpMatrix * v; + ShaderDebugger::debugVector3D(result, "Vector in clip space:"); + return result; +} + +/** + * Performs viewport transform. Mainly for debugging. + */ +QVector3D GLESRenderer::clipToViewport(const QVector3D & v) +{ + float ox = (m_viewportX + m_viewportW) / 2.0; + float oy = (m_viewportY + m_viewportH) / 2.0; + ShaderDebugger::debugVector3D(v, "Vector in clip space:"); + float xw = (m_viewportW / 2.0) * v.x() + ox; + float yw = (m_viewportH / 2.0) * v.y() + oy; + float zw = ((m_farClip - m_nearClip) / 2.0) * v.z() + (m_nearClip + m_farClip) / 2.0; + QVector3D result = QVector3D(xw, yw, zw); + ShaderDebugger::debugVector3D(result, "Vector in viewport space:"); + return result; +} + + diff --git a/src/glesrenderer.h b/src/glesrenderer.h new file mode 100644 index 0000000..e5cc723 --- /dev/null +++ b/src/glesrenderer.h @@ -0,0 +1,472 @@ +/*************************************************************************** + * Copyright (C) 2012 by Walter Roth * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GLESRENDERER_H +#define GLESRENDERER_H + +#include +#include +#include + +#include "glcolorrgba.h" +#include "gldefines.h" + +/** \brief A render program that can be used as a simple replacement for the OpenGL fixed + * function pipeline. + * \author Walter Roth, 2014 + * + * GLESRenderer supports vertex, colors, normals, texCoords, textures and matrix manipulation. + * It owns a modelview and a projection matrix which can be used like in the old days of OpenGL 1. + * The vertex shader program needs to stick to the following variable names: + * attribute highp vec4 a_Vertex; + * attribute highp vec4 a_Color; + * attribute highp vec3 a_Normal; + * attribute highp vec2 a_TexCoord; + * + * uniform bool u_ColorArrayEnabled + * uniform bool u_LightingEnabled; + * uniform highp mat4 u_MvpMatrix; + * uniform highp mat3 u_NormalMatrix; + * uniform highp vec3 u_LightDirection; + * uniform highp vec4 u_AmbientAndDiffuseColor; + * uniform highp vec4 u_SpecularColor; + * uniform highp float u_Shininess; + * uniform mediump float int u_PointSize; + * + * The fragment shader texture variable must have the following name: + * uniform sampler2d s_Texture; + * + * These names are used to determine the locations of the varables in the shaders. + * If you need more variables, subclass GLESRenderer and add new variable(s) to + * location_XXX variables. Extend virtual intialize(), bind() and disableAttributeArrays() functions + * to support the new variable(s). + * + * The shader source should be in a separate file (e.g. added to the resources). + * Example shaders vshader1 and fshader1 emulate a simple fixed-function lighted OpenGL 1 scene. + * + * Call initialize, after a valid OpenGL context has been made current. + * Setup viewing with setViewport, setLookAt and setPerspective. + * Viewport coordinates must be given as + * GL-viewport coordinates, where lower left is (0,0). + * Call bind to bind the program and set the uniform values you need. + * After this, call activateAttributeLocation to enable the attribute arrays. + * To change attribute arrays, call disableAttributeArrays and then again activateAttributeLocations. + * Finally call release. + * + * GLESRenderer contains functions for projecting points from modelspace to screen and unprojecting points + * from screen to model space. These functions use the current matrix settings. Therefore, you have to + * (re)set the matrices to the values that are used to paint the model, before you call the project and + * unproject functions. + * + * For convenient mouse interaction, GLESRenderer supplies the calculateMousePoints(), distanceToMouseClick() + * and mouseIntersection() functions. These functions use the unproject functions and therefore do also need + * valid model painting matrix settings. + */ +class GLESRenderer : public QObject +{ + Q_OBJECT +public: + typedef enum{ + VERTEX_LOCATION, + NORMAL_LOCATION, + COLOR_LOCATION, + TEXCOORD_LOCATION + }AttributeLocation; + + explicit GLESRenderer(QObject *parent, + const QString & vShaderFilename = ":/src/vshader.vsh", + const QString & fShaderFilename = ":/src/fshader.fsh"); + + virtual ~GLESRenderer(); + + //========================================================================= + /** + * \name Initialization, binding and releasing + */ + ///@{ + /** + * Compile shaders, get attribute and uniform locations of shaders. + * This function needs an active OpenGL context. + * Returns false, if initialization failed. + */ + virtual bool initialize(); + + /** + * Bind program and transfer attribute and uniform data to the shaders. + * Calls initialize, if not alraedy initialized. + * Returns false, if binding failed. + */ + virtual bool bind(); + + /** + * Releases program. To be called, when all rendering is finished. + * Calls disableAttributeArrays and releases program. + */ + void release(); + ///@} + + //================================================================= + /** + * \name Unproject, project and mouse interaction functions + */ + ///@{ + /** + * Uses viewport and matrix settings to reverse the vertex transformation chain. + * vWin x,y and z are the coordinates in window coordinates. Lower left of viewport + * is winX = 0, winY = 0. Use winZ = 0.0 for a point on near clipping plane + * and winZ = 1.0 for a point on far clipping plane. + * This function assumes, that the vertex shader computes gl_positon as v * mvpMatrix. + * Use a custom shader reverse function, if this is not true. + * Returns coordinate in object space. + */ + QVector3D unProjectViewportPoint(const QVector3D & vWin); + + /** + * Performs inverse viewport transform. + */ + QVector3D viewportToClip(const QVector3D & v); + + /** + * Returns the points on near and far clipping plane, that correspond to the + * mouseX and mouseY coordinates of a mouse click. + * mouseX and mouseY are coordinates as delivered by QMouseEvent or QDeclarativeMouseEvent. + */ + void calculateMousePoints(QVector3D * nearPoint, QVector3D * farPoint, const QPoint &mousePos ); + + /** + * Calculate the distance of the "mouse ray line" to point p in model space. + * mouseX, mouseY are the coordinates of the mouse click as delivered by QMouseEvent. + * Returns distance. + */ + float distanceToMouseClick(QVector3D p, const QPoint &mousePos); + + /** + * Calculates intersection of "mouse ray line" with the plane defined by normal and d. + * mouseX, mouseY are the coordinates of the mouse click as delivered by QMouseEvent. + * Returns true, if intersection is in the visible frustum, else returns false. + * If return value is true, *intersection is the intersection of the mouse ray line with + * the plane normal*(x,y,z) + d = 0. + * If return value is false, *intersection is not modified. + */ + bool mouseIntersection(QVector3D * intersection, QVector3D normal, float d, const QPoint &mousePos); + ///@} + + + //====================================================================================== + ///@{ + + /** Getters and setters for uniforms + */ + /** + * Set modelview matrix. Updates mvpMatrix and normalMatrix too. + * Call setPMatrix or setPerspective first. + */ + void setMvMatrix(const QMatrix4x4 newVal); + /** + * Getter for debugging. + */ + const QMatrix4x4 & getMvMatrix(){return m_mvMatrix;} + /** + * Getter for debugging. + */ + const QMatrix3x3 & getNormalMatrix(){return m_normalMatrix;} + + /** + * Sets mvMatrix to a lookAt transformation. + * Call setPMatrix or setPerspective first. + */ + void setLookAt(const QVector3D & eye,const QVector3D & center,const QVector3D & up ); + + /** + * Set projection matrix. Call setMvMatrix after this. + */ + void setPMatrix(const QMatrix4x4 newVal); + /** + * Getter for debugging. + */ + const QMatrix4x4 & getPMatrix(){return m_pMatrix;} + /** + * Setup projection matrix. Call setMvMatrix after this. + */ + void setPerspective(GLfloat fovy, GLfloat aspect, GLfloat nearClip, GLfloat farClip); + /** + * @brief setOrtho Set pMatrix to an orthogonal projection. + * @param left + * @param right + * @param bottom + * @param top + * @param nearPlane + * @param farPlane + */ + void setOrtho(float left, float right, float bottom, float top, float nearPlane, float farPlane); + /** + * Set viewport + * Viewport coordinates must be given as + * GL-viewport coordinates, where lower left is (0,0). + */ + void setViewport(int x, int y, int w, int h); + /** + * Get the present viewport settings from GL engine + */ + void readGLViewportSettings(); + + /** + * Set the color for ambient and diffuse lighting (or no lighting). + * Alternatively use a color array and color attribute. + * Defaults to white. + */ + void setAmbientAndDiffuseColor(const GLColorRgba newVal); + /** + * Set the dimming factor for ambient light. + * Defaults to 0.2. + */ + void setAmbientLightBrightness(float newVal); + + /** + * Set the color for specular lighting. + * Defaults to white. + */ + void setSpecularColor(const GLColorRgba newVal); + /** + * Set the shininess (exponent) for specular lighting. + * Defaults to 200. + */ + void setShininess(float newVal); + + /** + * Enable / disable lighting. + * Defaults to false. + */ + void setLightingEnabled(bool newVal); + /** + * Returns lighting flag. + */ + bool isLightingEnabled(){return m_lightingEnabled;} + /** + * Enable / disable color array. + * Defaults to false. + */ + void setColorArrayEnabled(bool newVal); + /** + * Returns lighting flag. + */ + bool isColorArrayEnabled(){return m_colorArrayEnabled;} + /** + * Set the texture flag. + * Defaults to false. + */ + void setTextureEnabled(bool newVal); + /** + * Returns texture flag; + */ + bool isTextureEnabled(){return m_textureEnabled;} + /** + * Set light direction. + * Defaults to (1.0, 1.0, 1.0) + */ + void setLightDirection(const QVector3D & newVal); + /** + * Getter. + */ + const QVector3D getLightDirection(){return m_lightDirection;} + + /** + * Set size of points drawn with GL_POINTS. + * Defaults to 2.0. + */ + void setPointSize(int newVal); + + /** + * Returns pointSize; + */ + int getPointSize(){return m_pointSize;} + + /** + * Pushes mvMatrix. + */ + void pushMvMatrix(){m_mvMatrixStack.push(m_mvMatrix);} + + /** + * Pops mvMatrix from stack and updates normalMatrix and mvpMatrix. + */ + void popMvMatrix(); + + /** + * Translates mvMatrix by v + */ + void translate(const QVector3D & v); + void translate(float x, float y, float z); + /** + * Rotates mvMatrix by angle around axis + */ + void rotate(GLfloat angle, const QVector3D & axis); + /** + * Rotates mvMatrix by angle around axis (x,y,z) + */ + void rotate(float angle, float x, float y, float z); + void zoom(float zoomFactor); + + /** + * @brief addTransformation + * Multiplies additionalTransformation to mvMatrix. + * @param additionalTransformation + */ + void addTransformation(const QMatrix4x4 additionalTransformation); + + /** + * Scales mvMatrix. + */ + void scale(const QVector3D & v ); + + ///@} + + //====================================================================================== + + ///@{ + /** + * \name Attribute setters + */ + + /** + * Enables 2D Vertex, normal, color or texCoord arrays and sets start adresses of arrays + * Type may be: VERTEX_LOCATION, NORMAL_LOCATION, COLOR_LOCATION, TEXCOORD_LOCATION + */ + bool activateAttributeArray (AttributeLocation location, const QVector2D * values, int stride = 0 ); + /** + * Enables 3D Vertex, normal, color or texCoord arrays and sets start adresses of arrays + * Type may be: VERTEX_LOCATION, NORMAL_LOCATION, COLOR_LOCATION, TEXCOORD_LOCATION + */ + bool activateAttributeArray (AttributeLocation location, const QVector3D * values, int stride = 0 ); + /** + * Enables 4D Vertex or color arrays and sets start adresses of arrays + * Type may be: VERTEX_LOCATION, COLOR_LOCATION + */ + bool activateAttributeArray (AttributeLocation location, const QVector4D * values, int stride = 0 ); + /** + * Enables 3D Vertex, normal, color or texCoord buffer + * location may be: VERTEX_LOCATION, NORMAL_LOCATION, COLOR_LOCATION, TEXCOORD_LOCATION + * numbersPerElement: 3 for vertex, 2 or 3 for texture, 3 or 4 for color + */ + bool activateAttributeBuffer (AttributeLocation location, int numbersPerElement, int bufferId); + /** + * Disables all enabled attribute arrays. + * To be called after rendering an object. + */ + virtual void disableAttributeArrays(); + ///@} + + //============================================================== + ///@{ + + /** + * \name Debugging + */ + + /** + * Multiplies current mvp matrix with v. Mainly for debugging. + */ + QVector3D modelToClip(const QVector3D & v); + /** + * Performs viewport transform. Mainly for debugging. + */ + QVector3D clipToViewport(const QVector3D & v); + ///@} + +protected: + + /** + * @brief activateAttributeArray + * Enables Vertex, normal, color or texCoord arrays and sets start adresses of arrays + * location may be: VERTEX_LOCATION, NORMAL_LOCATION, COLOR_LOCATION, TEXCOORD_LOCATION + * @param location + * @param values Address of first number + * @param tupleSize Numbers per vertex + * @param stride Distance to next tuple + * @return + */ + bool activateAttributeArray (AttributeLocation location, const float * values, int tupleSize, int stride = 0 ); + + bool m_initialized; + bool m_bound; + + //Uniforms to be passed to the shader + //flags + bool m_lightingEnabled; // flag for lighting + bool m_colorArrayEnabled; //flag for a valid color array + bool m_textureEnabled; //flag for active texturing + bool m_invertedMvpMatrixValid; //flag for a valid inverted ModelViewProjection matrix + //matrices + QMatrix4x4 m_mvpMatrix; //combined modelview and projection matrix + //The inverted mvp matrix for unproject purposes + //Use the invertedMvpMatrixValid flag for calculating it when required. + QMatrix4x4 m_invertedMvpMatrix; + QStack m_mvMatrixStack; + QMatrix4x4 m_mvMatrix; //modelview matrix + QMatrix4x4 m_pMatrix; //projection matrix + float m_fovy; + float m_aspect; + float m_nearClip; //near clipping plane + float m_farClip; //far clipping plane + bool m_OrthoMode; + QMatrix3x3 m_normalMatrix; //matrix for normal transformation + //lighting + QVector3D m_lightDirection; //direction of directional light + QVector3D m_halfPlaneVector; //half plane vector for specular light calculation + GLColorRgba m_ambientAndDiffuseColor; + float m_ambientLightBrightness; + GLColorRgba m_specularColor; + float m_shininess; + //texturing + int m_pointSize; + + //Shaders and program + QString m_vShaderFileName; //vertex shader + QString m_fShaderFileName; //fragment shader + QOpenGLShader * m_vShader; //vertex shader + QOpenGLShader * m_fShader; // fragment shader + QOpenGLShaderProgram * m_renderProgram; + + // Locations of shader variables + int m_location_aVertex; + int m_location_aColor; + int m_location_aNormal; + int m_location_aTexCoord; + int m_location_uColorArrayEnabled; + int m_location_uLightingEnabled; + int m_location_uNormalMatrix; + int m_location_uMvpMatrix; + int m_location_uAmbientAndDiffuseColor; + int m_location_uAmbientLightBrightness; + int m_location_uSpecularColor; + int m_location_uShininess; + int m_location_uLightDirection; + int m_location_uHalfPlaneVector; + int m_location_uPointSize; + int m_location_uTextureSampler; + int m_location_uTextureEnabled; + + //The list of enabled attribute locations + QListm_activeAttributeLocations; + //viewport + int m_viewportX; + int m_viewportY; + int m_viewportW; + int m_viewportH; +}; + +#endif // GLESRENDERER_H diff --git a/src/glitem.cpp b/src/glitem.cpp new file mode 100644 index 0000000..6e3984c --- /dev/null +++ b/src/glitem.cpp @@ -0,0 +1,408 @@ +#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::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(); +} + diff --git a/src/glitem.h b/src/glitem.h new file mode 100644 index 0000000..268dbeb --- /dev/null +++ b/src/glitem.h @@ -0,0 +1,290 @@ +#ifndef GLITEM_H +#define GLITEM_H + +#include +#include +#include +#include +#include + +#include "glpoint.h" +#include "glesrenderer.h" + +/** + * @brief The GlItem class is a 3D-scene item designed for use in QML SceneGraphs. + * It should be subclassed according to your requirements, registered with qmlRegisterType + * and used as a component in QML files. + * The default constructor will use src/vshader.vsh and src/fshader.fsh as shaders. + * Fell free to use shaders of your own. Refer to the GLESRenderer class for shader requirements. + * Geometry data should be put into m_points and m_vertices containers for maximum performance. + * Use paintUnderQml or paintOnTopOfQml to paint a 3D scene under or on top of the QML elements of the QML SceneGraph. + * For debugging geometries, call toggleMove to let the scene rotate around m_rotationAxis. + * Warning: + * Updating geometry data must be carried out in synchronizeThreads, because drawing will be performed + * on the rendering thread. The rendering thread is waiting when synchronizeThreads is called. + * + */ +class GLItem : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(QString vertexShaderFilename READ vertexShaderFilename WRITE setVertexShaderFilename NOTIFY vertexShaderFilenameChanged) + Q_PROPERTY(QString fragmentShaderFilename READ fragmentShaderFilename WRITE setFragmentShaderFilename NOTIFY fragmentShaderFilenameChanged) + Q_PROPERTY(bool movementActivated READ movementActivated NOTIFY movementActivatedChanged) + Q_PROPERTY(int viewportX READ viewportX WRITE setViewportX NOTIFY viewportXChanged) + Q_PROPERTY(int viewportY READ viewportY WRITE setViewportY NOTIFY viewportYChanged) + + typedef enum{ + VERTEX_LOCATION, + NORMAL_LOCATION, + COLOR_LOCATION, + TEXCOORD_LOCATION + }AttributeLocation; + + /** + * @brief m_viewportX The viewport position in window() coordinates to be used + * for OpenGL rendering. width and height are taken from QQuickItem. + */ + int m_viewportX; + + /** + * @brief m_viewportX The viewport position in window() coordinates to be used + * for OpenGL rendering. width and height are taken from QQuickItem. + * ! Positive y axis is upwards! + */ + int m_viewportY; + /** + * @brief createAxis creates a coordinate axis + * @param length Total length of axis, starting from origin + * @param origin Start coordinate of axis + * @param axis Direction of axis + * @param normal Direction of ticks and dummy normal. + * @param texCoord Dummy texture coordinate + * @param color Color of axis + */ + void createAxis(double length, const QVector3D &origin, const QVector3D &axis, + const QVector3D &normal, const QVector3D &texCoord, + const GLColorRgba &color); + +public: + explicit GLItem(QQuickItem *parent = 0, + const QString &vertexShaderFilename = ":/shaders/vshader.vsh", + const QString &fragmentShaderFilename = ":/shaders/fshader.fsh"); + virtual ~GLItem(); + + /** + * @brief updatePaintNode Overwrite this function, if you want to add items to the scenegraph. + * Do not call this function. It will be called automatically. + * @param node + * @return + */ + QSGNode * updatePaintNode(QSGNode *node, UpdatePaintNodeData *updatePaintNodeData); + + //simple getters + QString fragmentShaderFilename() const{return m_fragmentShaderFilename;} + QString vertexShaderFilename() const{return m_vertexShaderFilename;} + + bool movementActivated(); + + void setEye (const QVector3D & newVal) + { + m_eye = newVal; + } + + int viewportX() const + { + return m_viewportX; + } + + int viewportY() const + { + return m_viewportY; + } + +protected: + /** + * @brief paintUnderQmlScene + * Virtual function for painting under a QML scene. This function is called by paintBefore after + * calling createGeometries and initializing and binding the renderer. + * Overwrite in subclasses for painting geometries in m_points with the renderer. + */ + virtual void paintUnderQmlScene(); + /** + * @brief paintUnderQmlScene + * Virtual function for painting on top of a QML scene. This function is called by paintAfter after + * calling createGeometries and initializing and binding the renderer. + * Overwrite in subclasses for painting geometries in m_points with the renderer. + */ + virtual void paintOnTopOfQmlScene(); + + /** + * @brief drawAxes Draw the axes in GL_LINES mode without lighting. + * If axes points do not exist, call createAxes with length parameter + * @param length Axes length + */ + virtual void drawAxes(double length); + + /** + * @brief createAxes Creates x, y and z axis with specified length starting for (0,0,0) + * @param length Axes length. + */ + virtual void createAxes(double length); + +signals: + + //NOTIFY signals + void vertexShaderFilenameChanged(QString arg); + void fragmentShaderFilenameChanged(QString arg); + void movementActivatedChanged(); + void viewportXChanged(int arg); + void viewportYChanged(int arg); + +public slots: + /** + * @brief paintBefore + * Activates renderer, clear color and depth buffers and calls paintUnderQmlScene. + * This function should not be overwritten + */ + void paintBefore(); + /** + * @brief paintAfter + * Activates renderer, clear color and depth buffers and calls paintOnTopOfQmlScene. + * This function should not be overwritten + */ + void paintAfter(); + + /** + * @brief toggleMove + * Start or stop movement by starting or stopping the redraw timer. + */ + void toggleMove(); + + /** Mouse event handler to be called from QML + **/ + void mousePressed(int x, int y); + void mousePositionChanged(int x, int y); + void mouseReleased(int x, int y); + + // Simple setters + void setVertexShaderFilename(QString arg) + { + if (m_vertexShaderFilename != arg) { + m_vertexShaderFilename = arg; + emit vertexShaderFilenameChanged(arg); + } + } + void setFragmentShaderFilename(QString arg) + { + if (m_fragmentShaderFilename != arg) { + m_fragmentShaderFilename = arg; + emit fragmentShaderFilenameChanged(arg); + } + } + + + void setViewportX(int arg); + + void setViewportY(int arg); + +protected slots: + /** + * @brief handleWindowChanged + * @param win This function is called when the parent Window changes. + * This is also the case, when a parent window is set for the first time. + */ + void handleWindowChanged(QQuickWindow *win); + /** + * @brief onTimerTimeout Overwrite for moving the scene. + */ + void onTimerTimeout(); + + /** + * @brief slotSynchronizeThreads This slot is connected to the beforeSysnchronizing signal. + * Calls virtual synchronizeThreads() function. + */ + void slotSynchronizeThreads(); + + /** + * @brief deleteRenderer + * Delete renderer unloads shader program and deletes renderer. + */ + void deleteRenderer(); + +protected: + + /** + * @brief synchronizeThreads + * Render thread is sleeping when this function is called. + * Copy geometry modifications from GuiThread owned variables here. + */ + virtual void synchronizeThreads(); + /** + * @brief timeOut Virtual function, called in slot onTimerTimeout + * To be overwritten in subclasses. + */ + virtual void timeOut(); + QVector * points(){return &m_points;} + QVector * indices(){return &m_indices;} + GLESRenderer * renderer() {return m_renderer;} + + + /** + * @brief setupGeometry Put the geometric data into the points array and set m_geometryIsValid flag. + * MUST be overridden in subclasses. GlItem::setupGeometry() does nothing. + */ + virtual void setupGeometry(); + /** + * @brief setupView Setup matrices, lighting and basic GL rendering settings + * GlItem::setupView sets up a basic view with (0,0,0) in the center of the screen. + * You may override this function to fit your requirements. + */ + virtual void setupView(bool clearColor, bool clearDepth); + +protected: + //flags + bool m_geometryIsValid; + bool m_colorArrayEnabled; + bool m_texturesEnabled; + bool m_lightingEnabled; + bool m_activatePaintBeforeQml; + bool m_activatePaintAfterQml; + bool m_orthoMode;//orthogonal projection for debugging + + //lighting and colors + QVector3D m_lightDirection; + GLfloat m_ambientLightBrightness; + GLColorRgba m_backgroundColor; + //vectors for lookAt + QVector3D m_eye; + QVector3D m_center; + QVector3D m_up; + //pespective + double m_fovy; + double m_aspect; + double m_near; + double m_far; + //rotation + QTimer * m_timer; + double m_guiThreadRotation; + double m_renderThreadRotation; + //shaders + QString m_vertexShaderFilename; + QString m_fragmentShaderFilename; + //Camera transformation matrix + QMatrix4x4 m_cameraTransform; + + /** + * @brief createShaderProgram + * creates and links the shader program using vshader. + */ + virtual void initializeRenderer(); + //containers for geometry + QVector m_points; + QVector m_indices; + int m_firstAxesPoint; + int m_lastAxesPoint; + //renderer + GLESRenderer * m_renderer; +}; + +#endif // GLITEM_H diff --git a/src/glpoint.cpp b/src/glpoint.cpp new file mode 100644 index 0000000..a8a1a8d --- /dev/null +++ b/src/glpoint.cpp @@ -0,0 +1,13 @@ +#include "glpoint.h" + +GLPoint::GLPoint(const QVector3D & vertex, const QVector3D & normal, const QVector3D & texCoord, + const GLColorRgba & color ) { + this->vertex = vertex; + this->normal = normal; + this->texCoord = texCoord; + this->color = color ; +} /* ----- end of constructor ----- */ + +GLPoint::~GLPoint() { + // Es gibt momentan keinen Speicherplatz der freigegebene werden muss +} /* ----- end of destructor ----- */ diff --git a/src/glpoint.h b/src/glpoint.h new file mode 100644 index 0000000..317cc95 --- /dev/null +++ b/src/glpoint.h @@ -0,0 +1,45 @@ +#ifndef GLPOINT_H +#define GLPOINT_H +#include +#include "glcolorrgba.h" +// ---------------------------------------------------------------------------- +class GLPoint { + public : GLPoint( const QVector3D & vertex = QVector3D (0.0, 0.0, 0.0), + const QVector3D & normal = QVector3D (0.0, 1.0, 0.0), + const QVector3D & texCoord = QVector3D (0.0, 0.0, 0.0), + const GLColorRgba & color = GLColorRgba(1.0, 1.0, 1.0, 1.0) ); + ~GLPoint(); + + QVector3D * vertexPointer (); // Returns address of vertex coordinate + QVector3D * normalPointer (); // Returns address of normal coordinate + QVector3D * texCoordPointer(); // Returns address of texture coordinate + QVector4D * colorPointer (); // Returns address of color + + void move( QVector3D vMove ); + + private: QVector3D vertex; + QVector3D normal; + QVector3D texCoord; + GLColorRgba color ; +}; /* ----- end of class GLPoint ----- */ + +inline QVector3D * GLPoint::vertexPointer() { + return & vertex; +} /* ----- end of method vertexPointer ----- */ + +inline QVector3D * GLPoint::normalPointer() { + return & normal; +} /* ----- end of method normalPointer ----- */ + +inline QVector3D * GLPoint::texCoordPointer() { + return & texCoord; +} /* ----- end of method texCoordPointer ----- */ + +inline QVector4D * GLPoint::colorPointer() { + return (QVector4D *) & color; +} /* ----- end of method colorPointer ----- */ + +inline void GLPoint::move( QVector3D vMove ) { + vertex = vertex + vMove; +} /* ----- end of method move ----- */ +#endif // GLPOINT_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..cbdc925 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include "mmscene.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + QQmlApplicationEngine engine; + qmlRegisterType("MultiMediaProgrammierung", 1, 0, "MMScene"); + engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); + + return app.exec(); +} diff --git a/src/mmscene.cpp b/src/mmscene.cpp new file mode 100644 index 0000000..1aca89f --- /dev/null +++ b/src/mmscene.cpp @@ -0,0 +1,89 @@ +#include "mmscene.h" +#include "glpoint.h" +#include "glcolorrgba.h" + +MMScene::MMScene(QQuickItem *parent) + :GLItem(parent) +{ + m_vertexShaderFilename = ":/shaders/vshader.vsh"; + m_eye = 2.5 * v_XYZ; + +} + +MMScene::~MMScene() +{ + +} + +void MMScene::paintUnderQmlScene() +{ + m_renderer->pushMvMatrix(); + // drawAxes(2.5); + drawTriangles(); + m_renderer->popMvMatrix(); +} + +void MMScene::paintOnTopOfQmlScene() +{ + m_renderer->pushMvMatrix(); + drawAxes(2.5); + drawTriangles(); + m_renderer->popMvMatrix(); +} + +void MMScene::setupGeometry() +{ + //Now we create the geometry. Every corner needs a vertex, a normal, a color and a texture, + //although normal and texture are not used for basic rendering. + QVector3D v00 = QVector3D(0.0, 0.0, 0.0); + QVector3D v01 = QVector3D(1.0, 0.0, 0.0); + QVector3D v02 = QVector3D(0.0, 1.0, 0.0); + QVector3D n0 = QVector3D(0.0, 0.0, 1.0); //dummy normal + QVector3D t0 = QVector3D(0.0,0.0,0.0); //dummy texture + + //append the vertices for the triangle + m_points.append(GLPoint(v00, n0, t0, GLColorRgba::clRed)); + m_points.append(GLPoint(v01, n0, t0, GLColorRgba::clGreen)); + m_points.append(GLPoint(v02, n0, t0, GLColorRgba::clBlue)); + + QVector3D v10 = QVector3D(1.0, 1.0, 0.0); + QVector3D v11 = QVector3D(1.0, 0.0, 0.0); + QVector3D v12 = QVector3D(0.0, 1.0, 0.0); + + //append the vertices for the triangle + m_points.append(GLPoint(v10, n0, t0, GLColorRgba::clRed)); + m_points.append(GLPoint(v11, n0, t0, GLColorRgba::clGreen)); + m_points.append(GLPoint(v12, n0, t0, GLColorRgba::clBlue)); + + m_colorArrayEnabled = true; + + m_geometryIsValid = true; +} + +void MMScene::drawTriangles() +{ + renderer()->setLightingEnabled(m_lightingEnabled); + renderer()->setColorArrayEnabled(m_colorArrayEnabled); + renderer()->setTextureEnabled(false); + + //enable required arrays + int stride = sizeof(GLPoint); + for(int i = 0; i < m_points.length(); i += 3) + { + if(i > m_lastAxesPoint || i < m_firstAxesPoint) + { + renderer()->activateAttributeArray(GLESRenderer::VERTEX_LOCATION, + m_points[i].vertexPointer(), stride); + renderer()->activateAttributeArray(GLESRenderer::NORMAL_LOCATION, + m_points[i].normalPointer(), stride); + renderer()->activateAttributeArray(GLESRenderer::TEXCOORD_LOCATION, + m_points[i].texCoordPointer(), stride); + renderer()->activateAttributeArray(GLESRenderer::COLOR_LOCATION, + m_points[i].colorPointer(), stride); + + glDrawArrays(GL_TRIANGLES, 0, 3); + } + } + renderer()->disableAttributeArrays(); +} + diff --git a/src/mmscene.h b/src/mmscene.h new file mode 100644 index 0000000..8bfa74e --- /dev/null +++ b/src/mmscene.h @@ -0,0 +1,43 @@ +#ifndef MMSCENE_H +#define MMSCENE_H + +#include "glitem.h" + +class MMScene : public GLItem +{ + Q_OBJECT + +public: + MMScene(QQuickItem *parent = 0); + ~MMScene(); + +protected: + /** + * @brief paintUnderQmlScene + * Virtual function for painting under a QML scene. This function is called by paintBefore after + * calling createGeometries and initializing and binding the renderer. + * Overwrite in subclasses for painting geometries in m_points with the renderer. + */ + virtual void paintUnderQmlScene(); + /** + * @brief paintUnderQmlScene + * Virtual function for painting on top of a QML scene. This function is called by paintAfter after + * calling createGeometries and initializing and binding the renderer. + * Overwrite in subclasses for painting geometries in m_points with the renderer. + */ + virtual void paintOnTopOfQmlScene(); + /** + * @brief setupGeometry puts the geometric data into the arrays (m_Vertices etc.) and sets geometryIsValid flag. + */ + void setupGeometry(); + + /** + * @brief drawTriangles + * Draws two triangles. + */ + void drawTriangles(); + + +}; + +#endif // MMSCENE_H diff --git a/src/shaderdebugger.cpp b/src/shaderdebugger.cpp new file mode 100644 index 0000000..8132654 --- /dev/null +++ b/src/shaderdebugger.cpp @@ -0,0 +1,103 @@ +#include "shaderdebugger.h" + +bool ShaderDebugger::enabled = false; + +ShaderDebugger::ShaderDebugger() +{ +} + +void ShaderDebugger::debugUniforms(int programId) +{ + if(!enabled) + return; +#ifdef GLES + char name[256]; + int length; + int size; + GLenum type; + int nUniforms; + glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &nUniforms); + for (int i = 0; i < nUniforms; i++) + { + glGetActiveUniform(programId, + i, + sizeof(name), + &length, + &size, + &type, + name ); + qDebug("Properties of uniform at index %i : %s", i, name); + switch (type) + { + case GL_FLOAT_MAT3: qDebug("Type : GL_FLOAT_MAT3");break; + case GL_FLOAT_MAT4: qDebug("Type : GL_FLOAT_MAT4");break; + case GL_FLOAT_VEC2: qDebug("Type : GL_FLOAT_VEC2");break; + case GL_FLOAT_VEC3: qDebug("Type : GL_FLOAT_VEC3");break; + case GL_FLOAT_VEC4: qDebug("Type : GL_FLOAT_VEC4");break; + case GL_FLOAT: qDebug("Type : GL_FLOAT");break; + case GL_BOOL: qDebug("Type : GL_BOOL");break; + default: qDebug("Type: %i", type); + } + qDebug("Array size: %i", size); + int location = glGetUniformLocation(programId, name); + qDebug("Location: %i", location); + } +#else + qDebug("ShaderDebugger::debugUniforms works on GLES platforms only."); + Q_UNUSED(programId); +#endif +} + + +void ShaderDebugger::debugMatrix4x4(const QMatrix4x4 & m, const QString & caption) +{ + if(!enabled) + return; + + qDebug("%s",qPrintable(caption)); + for(int row = 0; row < 4; row ++) + { + QVector4D vRow = m.row(row); + QString sRow = QString(" Row %1: %2\t %3\t%4\t%5")\ + .arg(row).arg(vRow.x(),4,'g',4).arg(vRow.y(),4,'g',4).arg(vRow.z(),4,'g',4).arg(vRow.w(),4,'g',4); + qDebug("%s",qPrintable(sRow)); + } +} + +void ShaderDebugger::debugMatrix3x3(const QMatrix3x3 & m, const QString & caption) +{ + if(!enabled) + return; + + QMatrix4x4 m4x4 = QMatrix4x4(m); + qDebug("%s",qPrintable(caption)); + for(int row = 0; row < 3; row ++) + { + QVector4D vRow = m4x4.row(row); + QString sRow = QString(" Row %1: %2\t %3\t%4")\ + .arg(row).arg(vRow.x(),4,'g',4).arg(vRow.y(),4,'g',4).arg(vRow.z(),4,'g',4); + qDebug("%s",qPrintable(sRow)); + } +} + +void ShaderDebugger::debugVector4D(const QVector4D & v, const QString & caption) +{ + if(!enabled) + return; + + QVector4D vRow = v; + QString sRow = QString(" %1\t%2\t%3\t%4")\ + .arg(vRow.x(),4,'g',4).arg(vRow.y(),4,'g',4).arg(vRow.z(),4,'g',4).arg(vRow.w(),4,'g',4); + qDebug("%s",qPrintable(caption + sRow)); +} + +void ShaderDebugger::debugVector3D(const QVector3D & v, const QString & caption) +{ + if(!enabled) + return; + + QVector3D vRow = v; + QString sRow = QString(" %1\t%2\t%3")\ + .arg(vRow.x(),4,'g',4).arg(vRow.y(),4,'g',4).arg(vRow.z(),4,'g',4); + qDebug("%s",qPrintable(caption + sRow)); +} diff --git a/src/shaderdebugger.h b/src/shaderdebugger.h new file mode 100644 index 0000000..db6f9e3 --- /dev/null +++ b/src/shaderdebugger.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2012 by Walter Roth * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SHADERDEBUGGER_H +#define SHADERDEBUGGER_H + +#include +#include +#include + +/** \brief A simple debugging tool for uniforms passed to a shader. + * + * There is no way to get the values of variables inside a shader, therefore you have to look + * at the variables, before they are passed to the shader. + */ +class ShaderDebugger +{ +public: + ShaderDebugger(); + static void setEnabled(bool newVal){enabled = newVal;} + /** + * Works on GLES platforms only. + */ + static void debugUniforms(int programId); + /** + * @brief debugMatrix4x4 Send debug output to stderr. + * @param m The matrix + * @param caption + */ + static void debugMatrix4x4(const QMatrix4x4 & m, const QString & caption = "QMatrix4x4"); + /** + * @brief debugMatrix3x3 Send debug output to stderr. + * @param m The matrix + * @param caption + */ + static void debugMatrix3x3(const QMatrix3x3 & m, const QString & caption = "QMatrix3x3"); + /** + * @brief debugVector4D Send debug output to stderr. + * @param m The vector + * @param caption + */ + static void debugVector4D(const QVector4D & v, const QString & caption = "QVector4D"); + /** + * @brief debugVector4D Send debug output to stderr. + * @param m The vector + * @param caption + */ + static void debugVector3D(const QVector3D & v, const QString & caption = "QVector3D"); +private: + static bool enabled; +}; + +#endif // SHADERDEBUGGER_H -- cgit v1.2.3-70-g09d2