Revision control
++ // Copyright (C) 2017 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
++
++ #include "fbitem.h"
++ #include <QOpenGLFramebufferObject>
++ #include <QOpenGLFunctions>
++ #include <QMatrix4x4>
++
++ 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<FbItem *>(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<void *>(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");
++ }