310 lines
9.0 KiB
C++
310 lines
9.0 KiB
C++
/*
|
|
QGLView
|
|
Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru
|
|
|
|
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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <QOpenGLExtraFunctions>
|
|
#include "glframebuffer.h"
|
|
#include <QTime>
|
|
|
|
|
|
Framebuffer::Framebuffer(QOpenGLExtraFunctions * f_, int colorAttachments_, bool withDepth, GLenum colorFormat_, GLenum _target): f(f_),
|
|
pbo(GL_PIXEL_PACK_BUFFER, GL_STREAM_DRAW) {
|
|
is_depth = withDepth;
|
|
target_ = _target;
|
|
color_formats.fill(colorFormat_, colorAttachments_);
|
|
colors.fill(0, colorAttachments_);
|
|
fbo = drbo = 0;
|
|
tex_d = 0;
|
|
wid = hei = 0;
|
|
pbo_queried = 0;
|
|
is_changed = false;
|
|
}
|
|
|
|
|
|
Framebuffer::Framebuffer(QOpenGLExtraFunctions * f_, QVector<GLenum> colors_, bool withDepth, GLenum _target): f(f_),
|
|
pbo(GL_PIXEL_PACK_BUFFER, GL_STREAM_DRAW) {
|
|
is_depth = withDepth;
|
|
target_ = _target;
|
|
color_formats = colors_;
|
|
colors.fill(0, colors_.size());
|
|
fbo = drbo = 0;
|
|
tex_d = 0;
|
|
wid = hei = 0;
|
|
pbo_queried = 0;
|
|
is_changed = false;
|
|
}
|
|
|
|
|
|
Framebuffer::~Framebuffer() {
|
|
deleteGLFramebuffer(fbo);
|
|
deleteGLRenderbuffer(drbo);
|
|
for (int i = 0; i < colors.size(); ++i)
|
|
deleteGLTexture(f, colors[i]);
|
|
deleteGLTexture(f, tex_d);
|
|
}
|
|
|
|
|
|
void Framebuffer::resize(int width, int height, bool force) {
|
|
if ((wid == width) && (hei == height) && !force) return;
|
|
wid = width;
|
|
hei = height;
|
|
deleteGLFramebuffer(fbo);
|
|
f->glGenFramebuffers(1, &fbo);
|
|
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
for (int i = 0; i < colors.size(); ++i) {
|
|
deleteGLTexture(f, colors[i]);
|
|
createGLTexture(f, colors[i], width, height, color_formats[i], target_);
|
|
f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
f->glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
f->glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
f->glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, 4);
|
|
//f->glTexParameteri(target_, GL_GENERATE_MIPMAP_SGIS, GL_FALSE);
|
|
f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, target_, colors[i], 0);
|
|
}
|
|
if (is_depth) {
|
|
deleteGLTexture(f, tex_d);
|
|
deleteGLRenderbuffer(drbo);
|
|
f->glGenRenderbuffers(1, &drbo);
|
|
f->glBindRenderbuffer(GL_RENDERBUFFER, drbo);
|
|
f->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
|
|
f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, drbo);
|
|
createGLTexture(f, tex_d, width, height, GL_DEPTH_COMPONENT);
|
|
f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
f->glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
f->glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
//f->glTexParameteri(target_, GL_GENERATE_MIPMAP_SGIS, GL_FALSE);
|
|
f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, target_, tex_d, 0);
|
|
}
|
|
f->glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
if (pbo.isInit()) {
|
|
pbo.bind(f);
|
|
pbo.resize(f, width*height*4);
|
|
pbo.release(f);
|
|
}
|
|
is_changed = false;
|
|
}
|
|
|
|
|
|
QImage Framebuffer::grab() const {
|
|
//glReadPixels(0, 0, wid, hei, GL_RGBA, );
|
|
//QImage ret();
|
|
return QImage();
|
|
}
|
|
|
|
|
|
void Framebuffer::queryPoint(int index, QPoint p) {
|
|
pbo_queried = 0;
|
|
if (index < 0 || index >= colors.size()) return;
|
|
if (!rect().contains(p) || !pbo.isInit()) return;
|
|
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
|
f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index);
|
|
//QTime tm; tm.restart();
|
|
pbo.bind(f);
|
|
f->glReadPixels(p.x(), height() - p.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
|
pbo_queried = 1;
|
|
pbo.release(f);
|
|
//qDebug() << tm.elapsed();
|
|
}
|
|
|
|
|
|
void Framebuffer::queryPoints(int index, QRect rect_) {
|
|
pbo_queried = 0;
|
|
if (index < 0 || index >= colors.size()) return;
|
|
rect_ &= rect();
|
|
if (rect_.isEmpty() || !pbo.isInit()) return;
|
|
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
|
f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index);
|
|
//QTime tm; tm.restart();
|
|
pbo.bind(f);
|
|
f->glReadPixels(rect_.x(), height() - rect_.bottom(), rect_.width(), rect_.height(), GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
|
pbo_queried = rect_.width() * rect_.height();
|
|
pbo.release(f);
|
|
//qDebug() << tm.elapsed();
|
|
}
|
|
|
|
|
|
void Framebuffer::queryImage(int index) {
|
|
queryPoints(index, rect());
|
|
}
|
|
|
|
|
|
uint Framebuffer::getPoint() const {
|
|
if (!pbo.isInit() || (pbo_queried == 0)) return 0;
|
|
//QTime tm; tm.restart();
|
|
uint ret = 0;
|
|
pbo.bind(f);
|
|
//glClearError();
|
|
void * map = pbo.map(f, GL_MAP_READ_BIT, sizeof(uint));
|
|
//qDebug() << map << QString::number(glGetError(), 16);
|
|
if (map)
|
|
memcpy(&ret, map, sizeof(uint));
|
|
pbo.unmap(f);
|
|
pbo.release(f);
|
|
//qDebug() << tm.elapsed();
|
|
return ret;
|
|
}
|
|
|
|
|
|
QVector<uint> Framebuffer::getPoints() const {
|
|
QVector<uint> ret;
|
|
if (!pbo.isInit() || (pbo_queried == 0)) return ret;
|
|
ret.resize(pbo_queried);
|
|
//QTime tm; tm.restart();
|
|
pbo.bind(f);
|
|
//glClearError();
|
|
void * map = pbo.map(f, GL_MAP_READ_BIT, pbo_queried * sizeof(uint));
|
|
//qDebug() << map << QString::number(glGetError(), 16);
|
|
if (map)
|
|
memcpy(ret.data(), map, pbo_queried * sizeof(uint));
|
|
pbo.unmap(f);
|
|
pbo.release(f);
|
|
//qDebug() << tm.elapsed();
|
|
return ret;
|
|
}
|
|
|
|
|
|
QImage Framebuffer::getImage() const {
|
|
QImage ret;
|
|
if (!pbo.isInit() || (pbo_queried == 0)) return ret;
|
|
ret = QImage(size(), QImage::Format_RGBA8888);
|
|
int bytes = width() * height() * 4;
|
|
//QTime tm; tm.restart();
|
|
pbo.bind(f);
|
|
//glClearError();
|
|
void * map = pbo.map(f, GL_MAP_READ_BIT, bytes);
|
|
//qDebug() << map << QString::number(glGetError(), 16);
|
|
if (map)
|
|
memcpy(ret.bits(), map, bytes);
|
|
pbo.unmap(f);
|
|
pbo.release(f);
|
|
//qDebug() << tm.elapsed();
|
|
return ret;
|
|
}
|
|
|
|
|
|
QVector<float> Framebuffer::grabF(int index) const {
|
|
QVector<float> ret;
|
|
if (index < 0 || index >= colors.size()) return ret;
|
|
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
|
f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index);
|
|
ret.resize(wid * hei * 4);
|
|
f->glReadPixels(0, 0, wid, hei, GL_RGBA, GL_FLOAT, ret.data());
|
|
return ret;
|
|
}
|
|
|
|
|
|
void Framebuffer::blit(int index_from, GLuint fb_to, int index_to, QRect from, QRect to, GLbitfield mask, GLenum filter) {
|
|
if (index_from < 0 || index_from >= colors.size()) return;
|
|
GLenum e = GL_COLOR_ATTACHMENT0 + index_to;
|
|
f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_to);
|
|
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
|
f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index_from);
|
|
f->glDrawBuffers(1, &e);
|
|
f->glBlitFramebuffer(from.x(), from.y(), from.right(), from.bottom(), to.x(), to.y(), to.right(), to.bottom(), mask, filter);
|
|
}
|
|
|
|
|
|
void Framebuffer::bind() {
|
|
if (is_changed) resize(wid, hei);
|
|
if (fbo == 0) return;
|
|
//glFlush();
|
|
f->glGetIntegerv(GL_VIEWPORT, prev_view);
|
|
//glClearError();
|
|
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
//qDebug() << QString::number(glGetError(), 16);
|
|
setWriteBuffers();
|
|
f->glReadBuffer(GL_COLOR_ATTACHMENT0);
|
|
//f->glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
f->glViewport(0, 0, wid, hei);
|
|
}
|
|
|
|
|
|
void Framebuffer::release() {
|
|
is_changed = false;
|
|
if (fbo == 0) return;
|
|
//glFlush();
|
|
f->glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
f->glViewport(prev_view[0], prev_view[1], prev_view[2], prev_view[3]);
|
|
}
|
|
|
|
|
|
void Framebuffer::setWriteBuffer(int index) {
|
|
//QVector<GLenum> buffers; buffers << GL_COLOR_ATTACHMENT0 + index;
|
|
GLenum e = GL_COLOR_ATTACHMENT0 + index;
|
|
f->glDrawBuffers(1, &e);
|
|
}
|
|
|
|
|
|
void Framebuffer::setWriteBuffers(int * indeces, int count) {
|
|
QVector<GLenum> buffers;
|
|
for (int i = 0; i < count; ++i)
|
|
buffers << GL_COLOR_ATTACHMENT0 + indeces[i];
|
|
f->glDrawBuffers(buffers.size(), buffers.constData());
|
|
}
|
|
|
|
|
|
void Framebuffer::setWriteBuffers() {
|
|
QVector<GLenum> buffers;
|
|
for (int i = 0; i < colors.size(); ++i)
|
|
buffers << GL_COLOR_ATTACHMENT0 + i;
|
|
f->glDrawBuffers(buffers.size(), buffers.constData());
|
|
}
|
|
|
|
|
|
void Framebuffer::enablePixelBuffer() {
|
|
pbo.init(f);
|
|
}
|
|
|
|
|
|
void Framebuffer::bindColorTexture(int index, int channel) {
|
|
if (index < 0 || index >= colors.size()) return;
|
|
f->glActiveTexture(GL_TEXTURE0 + channel);
|
|
f->glBindTexture(GL_TEXTURE_2D, colors[index]);
|
|
}
|
|
|
|
|
|
void Framebuffer::bindColorTextures() {
|
|
for (int i = colors.size() - 1; i >= 0; --i) {
|
|
f->glActiveTexture(GL_TEXTURE0 + i);
|
|
f->glBindTexture(GL_TEXTURE_2D, colors[i]);
|
|
//f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
//f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
}
|
|
}
|
|
|
|
|
|
void Framebuffer::bindDepthTexture(int channel) {
|
|
f->glActiveTexture(GL_TEXTURE0 + channel);
|
|
f->glBindTexture(GL_TEXTURE_2D, tex_d);
|
|
}
|
|
|
|
|
|
void Framebuffer::deleteGLRenderbuffer(GLuint & drbo) {
|
|
if (drbo != 0)
|
|
f->glDeleteRenderbuffers(1, &drbo);
|
|
drbo = 0;
|
|
}
|
|
|
|
|
|
void Framebuffer::deleteGLFramebuffer(GLuint & fbo) {
|
|
if (fbo != 0)
|
|
f->glDeleteFramebuffers(1, &fbo);
|
|
fbo = 0;
|
|
}
|