// Copyright (C) 2017 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "fbitem.h" #include #include #include FbItem::FbItem(QQuickItem *parent) : QQuickFramebufferObject(parent), m_target(0, 0, -1), m_syncState(AllNeedsSync), m_multisample(false) { } QQuickFramebufferObject::Renderer *FbItem::createRenderer() const { return new FbItemRenderer(m_multisample); } void FbItem::setEye(const QVector3D &v) { if (m_eye != v) { m_eye = v; m_syncState |= CameraNeedsSync; update(); } } void FbItem::setTarget(const QVector3D &v) { if (m_target != v) { m_target = v; m_syncState |= CameraNeedsSync; update(); } } void FbItem::setRotation(const QVector3D &v) { if (m_rotation != v) { m_rotation = v; m_syncState |= RotationNeedsSync; update(); } } int FbItem::swapSyncState() { int s = m_syncState; m_syncState = 0; return s; } FbItemRenderer::FbItemRenderer(bool multisample) : m_inited(false), m_multisample(multisample), m_dirty(DirtyAll) { m_camera.setToIdentity(); m_baseWorld.setToIdentity(); m_baseWorld.translate(0, 0, -1); m_world = m_baseWorld; } void FbItemRenderer::synchronize(QQuickFramebufferObject *qfbitem) { FbItem *item = static_cast(qfbitem); int syncState = item->swapSyncState(); if (syncState & FbItem::CameraNeedsSync) { m_camera.setToIdentity(); m_camera.lookAt(item->eye(), item->eye() + item->target(), QVector3D(0, 1, 0)); m_dirty |= DirtyCamera; } if (syncState & FbItem::RotationNeedsSync) { m_rotation = item->rotation(); m_dirty |= DirtyWorld; } } struct StateBinder { StateBinder(FbItemRenderer *r) : m_r(r) { QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); f->glEnable(GL_DEPTH_TEST); f->glEnable(GL_CULL_FACE); f->glDepthMask(GL_TRUE); f->glDepthFunc(GL_LESS); f->glFrontFace(GL_CCW); f->glCullFace(GL_BACK); m_r->m_program->bind(); } ~StateBinder() { QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); m_r->m_program->release(); f->glDisable(GL_CULL_FACE); f->glDisable(GL_DEPTH_TEST); } FbItemRenderer *m_r; }; void FbItemRenderer::updateDirtyUniforms() { if (m_dirty & DirtyProjection) m_program->setUniformValue(m_projMatrixLoc, m_proj); if (m_dirty & DirtyCamera) m_program->setUniformValue(m_camMatrixLoc, m_camera); if (m_dirty & DirtyWorld) { m_program->setUniformValue(m_worldMatrixLoc, m_world); QMatrix3x3 normalMatrix = m_world.normalMatrix(); m_program->setUniformValue(m_normalMatrixLoc, normalMatrix); } if (m_dirty & DirtyLight) m_program->setUniformValue(m_lightPosLoc, QVector3D(0, 0, 70)); m_dirty = 0; } void FbItemRenderer::render() { ensureInit(); if (m_vao.isCreated()) m_vao.bind(); else setupVertexAttribs(); StateBinder state(this); QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); f->glClearColor(0, 0, 0, 0); f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (m_dirty & DirtyWorld) { m_world = m_baseWorld; m_world.rotate(m_rotation.x(), 1, 0, 0); m_world.rotate(m_rotation.y(), 0, 1, 0); m_world.rotate(m_rotation.z(), 0, 0, 1); } updateDirtyUniforms(); f->glDrawArrays(GL_TRIANGLES, 0, m_logo.vertexCount()); if (m_vao.isCreated()) m_vao.release(); } QOpenGLFramebufferObject *FbItemRenderer::createFramebufferObject(const QSize &size) { m_dirty |= DirtyProjection; m_proj.setToIdentity(); m_proj.perspective(45.0f, GLfloat(size.width()) / size.height(), 0.01f, 100.0f); QOpenGLFramebufferObjectFormat format; format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); format.setSamples(m_multisample ? 4 : 0); return new QOpenGLFramebufferObject(size, format); } void FbItemRenderer::ensureInit() { if (m_inited) return; m_inited = true; initBuf(); initProgram(); } void FbItemRenderer::initBuf() { QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); m_logoVbo.create(); m_logoVbo.bind(); m_logoVbo.allocate(m_logo.constData(), m_logo.count() * sizeof(GLfloat)); setupVertexAttribs(); } void FbItemRenderer::setupVertexAttribs() { m_logoVbo.bind(); QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); f->glEnableVertexAttribArray(0); f->glEnableVertexAttribArray(1); f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), nullptr); f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), reinterpret_cast(3 * sizeof(GLfloat))); m_logoVbo.release(); } static const char *vertexShaderSource = "attribute vec4 vertex;\n" "attribute vec3 normal;\n" "varying vec3 vert;\n" "varying vec3 vertNormal;\n" "uniform mat4 projMatrix;\n" "uniform mat4 camMatrix;\n" "uniform mat4 worldMatrix;\n" "uniform mat3 normalMatrix;\n" "void main() {\n" " vert = vertex.xyz;\n" " vertNormal = normalMatrix * normal;\n" " gl_Position = projMatrix * camMatrix * worldMatrix * vertex;\n" "}\n"; static const char *fragmentShaderSource = "varying highp vec3 vert;\n" "varying highp vec3 vertNormal;\n" "uniform highp vec3 lightPos;\n" "void main() {\n" " highp vec3 L = normalize(lightPos - vert);\n" " highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n" " highp vec3 color = vec3(0.39, 1.0, 0.0);\n" " highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n" " gl_FragColor = vec4(col, 1.0);\n" "}\n"; void FbItemRenderer::initProgram() { m_program.reset(new QOpenGLShaderProgram); m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource); m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource); m_program->bindAttributeLocation("vertex", 0); m_program->bindAttributeLocation("normal", 1); m_program->link(); m_projMatrixLoc = m_program->uniformLocation("projMatrix"); m_camMatrixLoc = m_program->uniformLocation("camMatrix"); m_worldMatrixLoc = m_program->uniformLocation("worldMatrix"); m_normalMatrixLoc = m_program->uniformLocation("normalMatrix"); m_lightPosLoc = m_program->uniformLocation("lightPos"); }