source-tree refactoring

This commit is contained in:
2023-02-03 21:22:25 +03:00
parent b20068165f
commit 7d30802cbd
153 changed files with 606 additions and 278 deletions

View File

@@ -0,0 +1,209 @@
/*
QGL TextureManager
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gltexture_manager.h"
#include "gltypes.h"
QStringList TextureManager::search_pathes(".");
QVector3D colorVector(QRgb c) {
return QVector3D(((uchar *)(&c))[0] / 255.f, ((uchar *)(&c))[1] / 255.f, ((uchar *)(&c))[2] / 255.f);
}
QString TextureManager::findFile(const QString & path) {
return ::findFile(path, search_pathes);
}
GLuint TextureManager::loadTexture(const QString & path, bool ownership, bool bump) {
QString p = findFile(path);
if (p.isEmpty()) return 0;
int tid = textureID(p, bump);
if (tid > 0) {
// qDebug() << "[TextureManager] Found" << path << "as" << tid;
return tid;
}
QImage image(p);
if (bump) convertToNormal(image);
// qDebug() << p << image.width() << image.height() << image.format() << bump;
GLuint tid_ = tid;
createGLTexture(f, tid_, image);
tid = tid_;
if (tid == 0) {
qDebug() << "[TextureManager] Can`t load" << p;
return tid;
}
qDebug() << "[TextureManager] Loaded" << p << "as" << tid;
if (ownership) {
tex_ids[bump ? 1 : 0].insert(p, tid);
tex_im[bump ? 1 : 0].insert(p, image);
}
return tid;
}
GLuint TextureManager::loadTexture(const QImage & im, bool ownership, bool bump) {
if (im.isNull()) return 0;
QImage image(im);
if (bump) convertToNormal(image);
GLuint tid = 0;
createGLTexture(f, tid, im);
if (tid == 0) {
qDebug() << "[TextureManager] Can`t load image";
return tid;
}
// qDebug() << "[TextureManager] Loaded image as" << tid;
return tid;
}
QImage TextureManager::loadTextureImage(const QString & path, bool bump) {
QString p = findFile(path);
if (p.isEmpty()) return QImage();
QImage ret = tex_im[bump ? 1 : 0].value(p);
if (!ret.isNull()) return ret;
ret = QImage(p);
if (bump) convertToNormal(ret);
tex_im[bump ? 1 : 0].insert(p, ret);
return ret;
}
void TextureManager::reloadTexture(GLuint tid, const QString & path) {
QString p = findFile(path);
if (p.isEmpty() || (tid == 0)) return;
QImage image(p);
createGLTexture(f, tid, image);
if (tid == 0) {
qDebug() << "[TextureManager] Can`t load" << p;
return;
}
qDebug() << "[TextureManager] Reloaded" << p << "as" << tid;
}
void TextureManager::reloadTexture(GLuint tid, const QImage & im) {
if (im.isNull() || (tid == 0)) return;
QImage image(im);
createGLTexture(f, tid, image);
qDebug() << "[TextureManager] Reloaded" << tid;
}
void TextureManager::convertToNormal(QImage & im) {
if (im.isNull()) return;
QImage sim = im.convertToFormat(QImage::Format_ARGB32);
float sum[3] = {0., 0., 0.};
llong a = 0;
const uchar * sd = sim.constBits();
for (int i = 0; i < sim.height(); i++) {
for (int j = 0; j < sim.width(); j++) {
sum[2] += sd[a] / 255.f - 0.5f;
++a;
sum[1] += sd[a] / 255.f - 0.5f;
++a;
sum[0] += sd[a] / 255.f - 0.5f;
++a;
++a;
}
}
float wh = sim.width() * sim.height();
sum[0] /= wh;
sum[1] /= wh;
sum[2] /= wh;
// qDebug() << sum[0] << sum[1] << sum[2];
if ((qAbs(sum[0]) <= 0.05f) && (qAbs(sum[1]) <= 0.05f) && (sum[2] >= 0.25f)) /// already normal
return;
// qDebug() << "convert to normal";
QImage dim = QImage(sim.width(), sim.height(), QImage::Format_ARGB32);
int tx, ty, w = sim.width(), h = sim.height();
a = 0;
uchar * dd = dim.bits();
for (int i = 0; i < sim.height(); i++) {
for (int j = 0; j < sim.width(); j++) {
tx = j - 1;
tx = tx < 0 ? w + tx : tx % w;
ty = i - 1;
ty = ty < 0 ? h + ty : ty % h;
QVector3D p[3], res;
p[0] = colorVector(sim.pixel(j, i));
p[1] = colorVector(sim.pixel(j, ty));
p[2] = colorVector(sim.pixel(tx, i));
res.setY(piClamp(0.5f + (p[0].length() - p[1].length()) / 2.f, 0.f, 1.f));
res.setX(piClamp(0.5f - (p[0].length() - p[2].length()) / 2.f, 0.f, 1.f));
tx = (j + 1) % w;
ty = (i + 1) % h;
p[1] = colorVector(sim.pixel(j, ty));
p[2] = colorVector(sim.pixel(tx, i));
res.setY(piClamp(0.5f + (p[0].length() - p[1].length()) / 2.f, 0.f, 1.f));
res.setX(piClamp(0.5f - (p[0].length() - p[2].length()) / 2.f, 0.f, 1.f));
res.setZ(1.f);
dd[a] = res.z() * 255;
++a;
dd[a] = res.y() * 255;
++a;
dd[a] = res.x() * 255;
++a;
dd[a] = 255;
++a;
}
}
im = dim;
// im.save("_normal.png");
}
bool TextureManager::loadTextures() {
QFileInfoList fil;
foreach(const QString & i, tex_pathes)
loadTexture(i, true);
tex_pathes.clear();
return true;
}
void TextureManager::deleteTextures() {
for (int i = 0; i < 2; ++i) {
QList<GLuint> texs = tex_ids[i].values();
qDebug() << "[TextureManager] Delete" << texs.size() << "textures";
if (!texs.isEmpty()) f->glDeleteTextures(texs.size(), &texs[0]);
tex_ids[i].clear();
tex_im[i].clear();
}
}
void TextureManager::deleteTexture(const QString & name) {
for (int i = 0; i < 2; ++i) {
if (tex_ids[i].contains(name)) {
GLuint id = tex_ids[i][name];
f->glDeleteTextures(1, &id);
tex_ids[i].remove(name);
tex_im[i].remove(name);
}
}
}
void TextureManager::clearImageCache() {
tex_im[0].clear();
tex_im[1].clear();
}

View File

@@ -0,0 +1,67 @@
/*
QGL TextureManager
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLTEXTUREMANAGER_H
#define GLTEXTUREMANAGER_H
#include "qglengine_core_export.h"
#include <QDir>
#include <QFileInfo>
#include <QImage>
#include <QMap>
#include <QOpenGLExtraFunctions>
class QGLENGINE_CORE_EXPORT TextureManager {
public:
TextureManager(QOpenGLExtraFunctions * f_): f(f_) {}
virtual ~TextureManager() {}
static void addSearchPath(const QString & path) {
if (!search_pathes.contains(path)) search_pathes << path;
}
static void clearSearchPathes() { search_pathes.clear(); }
static QStringList searchPathes() { return search_pathes; }
static QString findFile(const QString & path);
GLuint loadTexture(const QString & path, bool ownership = true, bool bump = false);
GLuint loadTexture(const QImage & image, bool ownership = true, bool bump = false);
QImage loadTextureImage(const QString & path, bool bump = false);
void reloadTexture(GLuint tid, const QString & path);
void reloadTexture(GLuint tid, const QImage & image);
int textureID(const QString & path, bool bump = false) { return tex_ids[bump ? 1 : 0][path]; }
QImage textureImage(const QString & path, bool bump = false) { return tex_im[bump ? 1 : 0][path]; }
void addTexture(const QString & path) { tex_pathes << path; }
bool loadTextures();
void deleteTextures();
void deleteTexture(const QString & name);
void clearImageCache();
protected:
static void convertToNormal(QImage & im);
static QStringList search_pathes;
QMap<QString, GLuint> tex_ids[2];
QMap<QString, QImage> tex_im[2];
QStringList tex_pathes;
QOpenGLExtraFunctions * f;
};
#endif // GLTEXTUREMANAGER_H

View File

@@ -0,0 +1,450 @@
/*
QGL Renderer
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define GL_GLEXT_PROTOTYPES
#include "renderer.h"
#include "glmesh.h"
#include "glshaders.h"
#include "gltexture_manager.h"
#include "qglview.h"
#include <QOpenGLExtraFunctions>
#include <qad_types.h>
using namespace QGLEngineShaders;
Renderer::Renderer(QGLView * view_)
: RendererBase(view_)
, fbo_ds(view_, QVector<GLenum>() << GL_RGBA16F << GL_RGBA32F << GL_RGBA16F << GL_RGBA16F << GL_RGBA16F << GL_RGBA32F << GL_RGBA16F)
, fbo_out(view_, obrBuffersCount, false, GL_RGBA16F)
, rend_mat(this)
, rend_service(this)
, rend_selection(this)
, tone_proc(this)
, tex_env(view_, 512) {
quad = Primitive::plane(2., 2.);
cam_light = new Light();
cam_light->intensity = 0.75;
cam_light->setName("Camera_Light");
shader_files[srSelectionFill] = "selection.glsl";
shader_files[srSelectionHalo] = "selection_halo.glsl";
shader_files[srSelectionApply] = "selection_apply.glsl";
shader_files[srSelectionFrame] = "selection_frame.glsl";
shader_files[srServiceFill] = "service_fill.glsl";
shader_files[srServiceFrame] = "service_frame.glsl";
shader_files[srServiceLine] = "service_line.glsl";
shader_files[srGeometryPass] = "ds_geom.glsl";
shader_files[srLightOmniPass] = "ds_light.glsl";
shader_files[srLightSpotPass] = "ds_light.glsl";
shader_defines[srLightSpotPass] << "SPOT";
shader_files[srFinalPass] = "ds_final.glsl";
shader_files[srTonemapPass] = "ds_tonemap.glsl";
edit_mode = need_init_shaders = true;
camera_light_mode = QGLView::clmAuto;
}
Renderer::~Renderer() {
delete quad;
delete cam_light;
qDeleteAll(shaders.values());
if (shader_fxaa) delete shader_fxaa;
}
void Renderer::init(int width, int height) {
fbo_ds.reinit();
fbo_out.reinit();
quad->reinit();
buffer_materials.reinit();
buffer_lights.reinit();
buffer_lights_pos.reinit();
textures_maps.reinit();
textures_empty.reinit();
resize(width, height);
rend_mat.init(width, height);
rend_service.init(width, height);
rend_selection.init(width, height);
tone_proc.init();
initQuad(quad);
initTextureArrays();
initCoeffTextures();
markReloadTextures();
tex_env.init();
if (is_grabbing) fbo_out.enablePixelBuffer();
need_init_shaders = true;
}
void Renderer::resize(int width, int height) {
rend_mat.resize(width, height);
rend_service.resize(width, height);
rend_selection.resize(width, height);
fbo_ds.resize(width, height);
fbo_out.resize(width, height);
tone_proc.resize();
}
void Renderer::reloadShaders() {
QMapIterator<ShaderRole, QString> it(shader_files);
qDeleteAll(shaders.values());
shaders.clear();
if (shader_fxaa) delete shader_fxaa;
shader_fxaa = nullptr;
if (tone_proc.shader_sum) delete tone_proc.shader_sum;
tone_proc.shader_sum = nullptr;
QString dir = ":shaders/";
while (it.hasNext()) {
it.next();
loadShadersMulti(shaders[it.key()], dir + it.value(), true, shader_defines.value(it.key()));
}
loadShadersMulti(tone_proc.shader_sum, dir + "sum.glsl", false);
QStringList fxaa_defs;
fxaa_defs << "FXAA_PC 1"
<< "FXAA_GLSL_130 1"
<< "FXAA_QUALITY__PRESET 15";
loadShaders(shader_fxaa, QStringList() << (dir + "fxaa.vert") << (dir + "fxaa.frag"), true, fxaa_defs);
for (auto * e: fb_effects) {
e->reloadShaders();
e->is_loaded = true;
}
need_init_shaders = true;
view->scene()->setLightsChanged();
view->scene()->setTreeStructChanged();
view->scene()->setMaterialsChanged();
}
bool Renderer::bindShader(Renderer::ShaderRole role, QOpenGLShaderProgram ** ret) {
QOpenGLShaderProgram * prog = shaders.value(role);
if (ret) *ret = prog;
if (!prog) return false;
if (!prog->isLinked()) return false;
prog->bind();
return true;
}
bool Renderer::bindShader(QOpenGLShaderProgram * sp) {
if (!sp) return true;
if (!sp->isLinked()) return true;
if (!sp->bind()) return false;
return true;
}
void Renderer::initShaders() {
if (!need_init_shaders) return;
need_init_shaders = false;
initUniformBuffer(shaders.value(srGeometryPass), &buffer_materials, bpMaterials, "QGLMaterialData");
initUniformBuffer(shaders.value(srLightOmniPass), &buffer_materials, bpMaterials, "QGLMaterialData");
initUniformBuffer(shaders.value(srLightOmniPass), &buffer_lights, bpLightParameters, "QGLLightParameterData");
initUniformBuffer(shaders.value(srLightOmniPass), &buffer_lights_pos, bpLightPositions, "QGLLightPositionData");
initUniformBuffer(shaders.value(srLightSpotPass), &buffer_materials, bpMaterials, "QGLMaterialData");
initUniformBuffer(shaders.value(srLightSpotPass), &buffer_lights, bpLightParameters, "QGLLightParameterData");
initUniformBuffer(shaders.value(srLightSpotPass), &buffer_lights_pos, bpLightPositions, "QGLLightPositionData");
ShaderRole roles[] = {srLightOmniPass, srLightSpotPass};
QOpenGLShaderProgram * prog = 0;
for (int p = 0; p < 2; ++p) {
if (!bindShader(roles[p], &prog)) continue;
for (int i = 0; i < 5; ++i)
prog->setUniformValue(QString("tex_%1").arg(i).toLatin1().constData(), i);
prog->setUniformValue("tex_coeffs[0]", (int)Renderer::dbrBuffersCount);
prog->setUniformValue("tex_env", (int)Renderer::dbrBuffersCount + 1);
}
if (bindShader(srFinalPass, &prog)) {
prog->setUniformValue("tex_g1", 0);
prog->setUniformValue("tex_s_0", 1);
prog->setUniformValue("tex_s_1", 2);
prog->setUniformValue("tex_t_0", 3);
prog->setUniformValue("tex_t_1", 4);
}
if (bindShader(srGeometryPass, &prog)) {
setUniformMaps(prog);
}
if (bindShader(srTonemapPass, &prog)) {
prog->setUniformValue("tex_0", 0);
prog->setUniformValue("tex_sum", 1);
}
}
void Renderer::releaseShader() {
view->glUseProgram(0);
}
QVector<int> Renderer::getFreePlanes(int count) {
prev_write_plane = cur_write_plane;
QVector<int> ret;
bool output_done = false;
const int total_count = 4;
for (int i = 0; i < total_count; ++i) {
int plane = obrGeneral0 + i;
if (prev_write_plane == plane) continue;
if (!output_done) {
fbo_out.setWriteBuffer(plane);
cur_write_plane = plane;
output_done = true;
continue;
}
ret << plane;
if (ret.size() == count) break;
}
return ret;
}
void Renderer::fillObjectsBuffer(const ObjectBaseList & ol, RenderPass pass) {
cur_objects_.resize(ol.size());
for (int i = 0; i < ol.size(); ++i) {
Object & so(cur_objects_[i]);
ObjectBase * o = ol[i];
if (o->material()) {
so.material = o->material()->_index;
so.color = QVector4D(1, 1, 1, 1);
} else {
so.material = 0;
so.color = QColor2QVector(o->color());
}
so.object_id = o->id();
o->worldTransform().transposed().copyDataTo(so.modelmatrix);
// qDebug() << "load obj" << o->name() << o->worldTransform();
}
// qDebug() << "fillObjectsBuffer" << ol.size();
}
void Renderer::renderObjects(Scene & scene, RenderPass pass) {
QOpenGLExtraFunctions * f = view;
QMapIterator<Mesh *, ObjectBaseList> it(scene.geometries_used[pass]);
bool emit_pos_change = false;
while (it.hasNext()) {
it.next();
Mesh * mesh = it.key();
if (mesh->isObjectsChanged(pass)) {
mesh->setObjectsChanged(pass, false);
emit_pos_change = true;
fillObjectsBuffer(it.value(), pass);
// qDebug() << "loadObjects" << pass << cur_objects_.size();
mesh->loadObjects(f, cur_objects_, pass);
}
if (mesh->isSelectionChanged(pass) && edit_mode) {
mesh->setSelectionChanged(pass, false);
fillSelectionsBuffer(rend_selection.cur_selections_, it.value());
// qDebug() << "fillSelectionsBuffer" << pass << rend_selection.cur_selections_.size();
mesh->loadSelections(f, rend_selection.cur_selections_, pass);
}
// qDebug() << "draw" << pass << it.value().size();
mesh->draw(f, it.value().size(), pass);
}
if (emit_pos_change) emit view->objectsPositionChanged();
}
void Renderer::renderLight(int first_wr_buff, bool clear_only) {
QOpenGLShaderProgram * prog = 0;
Camera * cam = view->camera();
for (int i = 0; i < 2; ++i) {
view->glActiveTexture(GL_TEXTURE0 + Renderer::dbrBuffersCount + i);
view->glBindTexture(GL_TEXTURE_2D, tex_coeff[i]);
}
fbo_ds.bindColorTextures();
fbo_out.bind();
tex_env.bind((int)Renderer::dbrBuffersCount + 1);
typedef QPair<Renderer::ShaderRole, Light::Type> PassPair;
QVector<PassPair> passes;
passes << PassPair(srLightOmniPass, Light::Omni) << PassPair(srLightSpotPass, Light::Cone);
QColor back = view->fogColor();
back.setAlpha(0);
foreach(PassPair pass, passes) {
if (bindShader(pass.first, &prog)) {
fbo_out.setWriteBuffer(first_wr_buff + pass.second);
glClearFramebuffer(back, false);
if (clear_only) continue;
setUniformCamera(prog, cam);
setUniformViewCorners(prog, cam);
prog->setUniformValue("lights_start", lights_start[pass.second]);
prog->setUniformValue("lights_count", (int)cur_lights[pass.second].size());
prog->setUniformValue("fog_color", view->fogColor());
prog->setUniformValue("fog_decay", qMax(view->fogDecay(), 0.001f));
prog->setUniformValue("fog_density", view->fogDensity());
prog->setUniformValue("view_mat", cam->viewMatrix().inverted().toGenericMatrix<3, 3>());
renderQuad(prog, quad, cam);
}
}
}
void Renderer::renderScene() {
initShaders();
tex_env.load();
QOpenGLExtraFunctions * f = view;
Scene & scene(*(view->scene()));
Camera * cam = view->camera();
QOpenGLShaderProgram * prog = 0;
bool scene_changed = scene.prepare();
scene.destroyUnused(f);
/// reload materials on change
if (scene_changed || scene.need_reload_materials) {
rend_selection.generateObjectsID(scene);
reloadMaterials(scene);
if (edit_mode) recreateMaterialThumbnails();
emit view->materialsChanged();
}
/// material thumbnails
if (edit_mode && !scene_changed) {
rend_mat.procQueue();
}
/// lights
cur_lights = scene.lights_used;
bool use_camlight = (camera_light_mode == QGLView::clmOn);
if ((camera_light_mode == QGLView::clmAuto) && cur_lights.isEmpty()) {
use_camlight = true;
}
if (use_camlight) {
cur_lights[Light::Omni] << cam_light;
cam_light->setPos(cam->pos());
}
if (scene.lights_changed) {
scene.lights_changed = false;
reloadLightsParameters(cur_lights);
}
reloadLightsPositions(cam);
/// selection
if (edit_mode) {
rend_selection.renderSelection(scene);
}
/// solid geometry pass
fbo_ds.bind();
glEnableDepth();
glClearFramebuffer();
if (bindShader(srGeometryPass, &prog)) {
setUniformCamera(prog, cam);
textures_empty.bind(f, tarEmpty);
textures_maps.bind(f, tarMaps);
renderObjects(scene, rpSolid);
}
fbo_ds.blit(dbrNormalZ, fbo_ds.id(), dbrNormalZSolid, fbo_ds.rect(), fbo_ds.rect());
fbo_ds.blit(dbrSpecularReflect, fbo_ds.id(), dbrSpecularReflectSolid, fbo_ds.rect(), fbo_ds.rect());
fbo_ds.release();
/// lighting passes
renderLight(obrSolidOmni, scene.geometries_used[rpSolid].isEmpty());
/// transparent geometry pass
fbo_ds.bind();
glEnableDepth();
fbo_ds.setWriteBuffers({0, 1, 2, 3, 4});
glClearFramebuffer(Qt::black, false);
fbo_ds.setWriteBuffers();
if (bindShader(srGeometryPass, &prog)) {
renderObjects(scene, rpTransparent);
}
fbo_ds.release();
/// lighting passes
renderLight(obrTransparentOmni, scene.geometries_used[rpTransparent].isEmpty());
/// blending layers
if (bindShader(srFinalPass, &prog)) {
fbo_out.bindColorTexture(obrSolidOmni, 1);
fbo_out.bindColorTexture(obrSolidSpot, 2);
fbo_out.bindColorTexture(obrTransparentOmni, 3);
fbo_out.bindColorTexture(obrTransparentSpot, 4);
fbo_out.setWriteBuffer(obrGeneral0);
renderQuad(prog, quad);
}
cur_write_plane = obrGeneral0;
/// tonemapping
tone_proc.process();
auto free = getFreePlanes(0);
if (bindShader(srTonemapPass, &prog)) {
fbo_out.bind();
prog->setUniformValue("gamma", gamma_);
prog->setUniformValue("frame_max", tone_proc.frameMax());
fbo_out.bindColorTexture(prev_write_plane, 0);
renderQuad(prog, quad);
} else {
fbo_out.blit(prev_write_plane, fbo_out.id(), cur_write_plane, fbo_out.rect(), fbo_out.rect());
}
/// FXAA
if (view->isFeatureEnabled(QGLView::qglFXAA)) {
prog = shader_fxaa;
if (bindShader(prog)) {
auto free = getFreePlanes(0);
setUniformCamera(prog, 0, true, fbo_out.size());
fbo_out.bindColorTexture(prev_write_plane);
renderQuad(prog, quad, 0, false);
}
}
/// custom effects
for (auto * e: fb_effects) {
e->reloadShadersInternal();
e->drawInternal();
}
fbo_out.release();
/// apply hovers and selection frame
if (edit_mode) {
rend_selection.drawSelection(fbo_out, cur_write_plane);
rend_service.renderService();
} else {
fbo_out.blit(cur_write_plane, 0, 0, fbo_out.rect(), QRect(QPoint(), view->size()));
}
if (is_grabbing) {
fbo_out.queryImage(0);
last_img = fbo_out.getImage().mirrored();
// qDebug() << last_img.size();
}
}
void Renderer::setCameraLightMode(int m) {
camera_light_mode = m;
view->scene()->setLightsChanged();
}
void Renderer::setGrabImage(bool on) {
is_grabbing = on;
// fbo_out.enablePixelBuffer();
}
void Renderer::addFramebufferEffect(FramebufferEffectBase * e) {
e->r = this;
fb_effects << e;
}

150
src/core/render/renderer.h Normal file
View File

@@ -0,0 +1,150 @@
/*
QGL Renderer
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RENDERER_H
#define RENDERER_H
#include "glcubemap.h"
#include "glframebuffereffectbase.h"
#include "renderer_base.h"
#include "renderer_material.h"
#include "renderer_selection.h"
#include "renderer_service.h"
#include "tonemapping_proc.h"
#include <QQueue>
class QGLENGINE_CORE_EXPORT Renderer: public RendererBase {
friend class QGLView;
friend class MouseController;
friend class RendererMaterial;
friend class RendererService;
friend class RendererSelection;
friend class FramebufferEffectBase;
friend class TonemappingProc;
enum ShaderRole {
// Selection
srSelectionFill,
srSelectionHalo,
srSelectionApply,
srSelectionFrame,
// Service
srServiceFill,
srServiceFrame,
srServiceLine,
// Deferred shading
srGeometryPass,
srLightOmniPass,
srLightSpotPass,
srFinalPass,
srTonemapPass,
};
enum OutBufferRole {
obrSolidOmni,
obrSolidSpot,
obrTransparentOmni,
obrTransparentSpot,
obrGeneral0,
obrGeneral1,
obrGeneral2,
obrGeneral3,
obrBuffersCount,
};
public:
Renderer(QGLView * view_);
virtual ~Renderer();
enum DeferredBufferRole {
dbrDiffuse,
dbrNormalZ,
dbrSpecularReflect,
dbrEmissionRough,
dbrSpeedBitangXY,
dbrNormalZSolid,
dbrSpecularReflectSolid,
dbrBuffersCount,
};
void init(int width, int height);
void resize(int width, int height);
void reloadShaders();
void renderScene();
void setCameraLightMode(int m);
int cameraLightMode() const { return camera_light_mode; }
void setGrabImage(bool on);
bool isGrabImage() const { return is_grabbing; }
QImage getImage() const { return last_img; }
void addFramebufferEffect(FramebufferEffectBase * e);
void clearFramebufferEffects() { fb_effects.clear(); }
QImage materialThumbnail(Material * m) { return rend_mat.materialThumbnail(m); }
void recreateMaterialThumbnails(bool force_all = false) { rend_mat.recreateMaterialThumbnails(force_all); }
protected:
void fillObjectsBuffer(const ObjectBaseList & ol, RenderPass pass);
void reloadObjects();
void renderObjects(Scene & scene, RenderPass pass);
void renderLight(int first_wr_buff, bool clear_only);
bool bindShader(ShaderRole role, QOpenGLShaderProgram ** ret = 0);
bool bindShader(QOpenGLShaderProgram * sp);
void initShaders();
void releaseShader();
QVector<int> getFreePlanes(int count);
private:
float gamma_ = 1.f;
int camera_light_mode, cur_write_plane = 0, prev_write_plane = 0;
bool edit_mode, need_init_shaders, need_render_sum;
Framebuffer fbo_ds, fbo_out;
QMap<ShaderRole, QString> shader_files;
QMap<ShaderRole, QStringList> shader_defines;
QMap<ShaderRole, QOpenGLShaderProgram *> shaders;
QOpenGLShaderProgram * shader_fxaa = nullptr;
RendererMaterial rend_mat;
RendererService rend_service;
RendererSelection rend_selection;
TonemappingProc tone_proc;
Mesh * quad;
Light * cam_light;
CubeTexture tex_env;
QPoint mouse_pos;
QRect mouse_rect;
QMatrix4x4 prev_view, prev_proj;
QMatrix3x3 nm;
QVector4D corner_dirs[4];
QVector<QVector3D> hcontent;
QMap<int, QList<Light *>> cur_lights;
QVector<FramebufferEffectBase *> fb_effects;
QImage last_img;
bool is_grabbing = false;
};
#endif // RENDERER_H

View File

@@ -0,0 +1,422 @@
/*
QGL RendererBase
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define GL_GLEXT_PROTOTYPES
#include "renderer_base.h"
#include "glmesh.h"
#include "glshaders_headers.h"
#include "gltexture_manager.h"
#include "qglview.h"
#include "renderer.h"
#include <QOpenGLExtraFunctions>
using namespace QGLEngineShaders;
RendererBase::RendererBase(QGLView * view_)
: view(view_)
, buffer_materials(GL_UNIFORM_BUFFER, GL_STREAM_DRAW)
, buffer_lights(GL_UNIFORM_BUFFER, GL_STREAM_DRAW)
, buffer_lights_pos(GL_UNIFORM_BUFFER, GL_STREAM_DRAW)
, textures_empty(false)
, textures_maps(true) {
textures_manager = new TextureManager(view);
maps_size = QSize(512, 512);
maps_hash = 0;
tex_coeff[0] = tex_coeff[1] = 0;
}
RendererBase::~RendererBase() {
delete textures_manager;
}
void RendererBase::initTextureArrays() {
QOpenGLExtraFunctions * f = view;
textures_maps.init(f);
textures_empty.init(f);
textures_empty.resize(f, QSize(1, 1), 2);
textures_empty.bind(f);
QImage im(1, 1, QImage::Format_RGBA8888);
im.fill(0xFFFFFFFF);
textures_empty.load(f, im, emrWhite);
im.fill(0xFF8080);
textures_empty.load(f, im, emrBlue);
}
void RendererBase::initUniformBuffer(QOpenGLShaderProgram * prog, Buffer * buffer, int bind_point, const char * blockName) {
if (!prog || !buffer) return;
if (!prog->isLinked()) return;
QOpenGLExtraFunctions * f = view;
buffer->init(f);
// glClearError();
GLint ubo_ind = f->glGetUniformBlockIndex(prog->programId(), blockName);
f->glUniformBlockBinding(prog->programId(), ubo_ind, bind_point);
f->glBindBufferBase(GL_UNIFORM_BUFFER, bind_point, buffer->ID());
// qDebug() << "initUBO" << QString::number(f->glGetError(), 16);
}
void RendererBase::setUniformHalo(QOpenGLShaderProgram * prog, const char * type, QColor color, float fill) {
prog->setUniformValue((QString(type) + "_color").toLatin1().constData(), color);
prog->setUniformValue((QString(type) + "_fill").toLatin1().constData(), fill);
}
void RendererBase::setUniformMaps(QOpenGLShaderProgram * prog) {
prog->setUniformValue("qgl_texture_array[0]", (int)tarEmpty);
prog->setUniformValue("qgl_texture_array[1]", (int)tarMaps);
}
void RendererBase::setUniformCamera(QOpenGLShaderProgram * prog, Camera * cam, bool matrices, QSize viewport) {
double w = view->width(), h = view->height();
if (viewport.isValid()) {
w = viewport.width();
h = viewport.height();
}
QMatrix4x4 mat_view, mat_proj;
if (cam) {
if (matrices) {
mat_view = cam->fullViewMatrix();
mat_proj = cam->projectionMatrix(w / h);
}
prog->setUniformValue("z_near", cam->depthStart());
}
prog->setUniformValue("dt", QVector2D(1. / w, 1. / h));
prog->setUniformValue("qgl_ViewSize", QVector2D(w, h));
prog->setUniformValue("qgl_ViewMatrix", mat_view);
prog->setUniformValue("qgl_ViewProjMatrix", mat_proj * mat_view);
}
void RendererBase::setUniformViewCorners(QOpenGLShaderProgram * prog, Camera * cam, QSize viewport) {
double w = view->width(), h = view->height();
if (viewport.isValid()) {
w = viewport.width();
h = viewport.height();
}
QMatrix4x4 mproji = cam->projectionMatrix(w / h).inverted();
QMatrix4x4 mviewi = cam->viewMatrix().inverted();
QVector4D corner_dirs[4], world_dirs[4];
corner_dirs[0] = (mproji * QVector4D(-1, -1, 0, 1));
corner_dirs[1] = (mproji * QVector4D(-1, 1, 0, 1));
corner_dirs[2] = (mproji * QVector4D(1, 1, 0, 1));
corner_dirs[3] = (mproji * QVector4D(1, -1, 0, 1));
for (int i = 0; i < 4; ++i) {
world_dirs[i] = QVector4D(mviewi.mapVector(corner_dirs[i].toVector3D()));
prog->setUniformValue(QString("view_corners[%1]").arg(i).toLatin1().constData(), corner_dirs[i]);
prog->setUniformValue(QString("world_corners[%1]").arg(i).toLatin1().constData(), world_dirs[i]);
}
}
void RendererBase::fillSelectionsBuffer(QVector<uchar> & buffer, const ObjectBaseList & ol) {
buffer.resize(ol.size());
for (int i = 0; i < ol.size(); ++i) {
buffer[i] = (ol[i]->isSelected(true) ? 1 : 0);
}
}
void RendererBase::fillSelectionsBuffer(QVector<uchar> & buffer, bool yes, int size) {
buffer.resize(size);
for (int i = 0; i < size; ++i)
buffer[i] = (yes ? 1 : 0);
}
void RendererBase::reloadMaterials(Scene & scene) {
// qDebug() << "reloadMaterias";
QList<Map *> maps[2];
QMap<QString, int> tex_layers[2];
foreach(Material * m, scene.materials) {
if (m->map_diffuse.hasBitmap()) maps[0] << &(m->map_diffuse);
if (m->map_normal.hasBitmap()) maps[1] << &(m->map_normal);
if (m->map_metalness.hasBitmap()) maps[0] << &(m->map_metalness);
if (m->map_roughness.hasBitmap()) maps[0] << &(m->map_roughness);
if (m->map_emission.hasBitmap()) maps[0] << &(m->map_emission);
if (m->map_relief.hasBitmap()) maps[0] << &(m->map_relief);
}
for (int i = 0; i < 2; ++i) {
foreach(Map * m, maps[i])
tex_layers[i][m->bitmap_path] = 0;
}
int layers_count = tex_layers[0].size() + tex_layers[1].size(), cl = -1;
uint cur_maps_hash = qHash(tex_layers[0].keys()) ^ (qHash(tex_layers[1].keys()) + 0xF00FF00F);
if (maps_hash != cur_maps_hash) {
maps_hash = cur_maps_hash;
textures_maps.resize(view, maps_size, layers_count);
textures_maps.bind(view);
for (int i = 0; i < 2; ++i) {
QMutableMapIterator<QString, int> it(tex_layers[i]);
while (it.hasNext()) {
it.next();
QImage im = textures_manager->loadTextureImage(it.key(), i == 1);
textures_maps.load(view, im, ++cl);
it.value() = cl;
}
foreach(Map * m, maps[i]) {
m->_layer = tex_layers[i].value(m->bitmap_path);
// qDebug() << "assign" << m->bitmap_path << "layer" << m->_layer;
}
}
textures_maps.mipmaps(view);
// qDebug() << "load" << (cl+1) << "bitmaps";
}
QGLMaterial glm;
cur_materials_.clear();
cur_materials_ << glm;
foreach(Material * m, scene.materials) {
if (cur_materials_.size() >= max_materials) {
qDebug() << "[QGLEngine] Warning: Too many materials! Maximum" << max_materials;
break;
}
m->_index = cur_materials_.size();
m->_changed = false;
glm.color_diffuse = QColor2QVector(m->color_diffuse);
glm.color_emission = QColor2QVector(m->color_emission);
glm.transparency = m->transparency;
glm.reflectivity = m->reflectivity;
glm.iof = m->iof;
glm.dispersion = m->dispersion;
m->map_diffuse.copyToQGLMap(glm.map[mtDiffuse]);
m->map_normal.copyToQGLMap(glm.map[mtNormal]);
m->map_metalness.copyToQGLMap(glm.map[mtMetalness]);
m->map_roughness.copyToQGLMap(glm.map[mtRoughness]);
m->map_emission.copyToQGLMap(glm.map[mtEmission]);
m->map_relief.copyToQGLMap(glm.map[mtRelief]);
cur_materials_ << glm;
}
// qDebug() << "load" << cur_materials_.size() << "materials";
buffer_materials.bind(view);
buffer_materials.resize(view, cur_materials_.size() * sizeof(QGLMaterial));
buffer_materials.load(view, cur_materials_.constData(), cur_materials_.size() * sizeof(QGLMaterial));
scene.need_reload_materials = false;
}
void RendererBase::reloadLightsParameters(const QMap<int, QList<Light *>> & lights) {
lights_start.clear();
lights_start[Light::Omni] = 0;
QMapIterator<int, QList<Light *>> it(lights);
current_lights.clear();
while (it.hasNext()) {
it.next();
lights_start[it.key()] = current_lights.size();
current_lights.append(it.value());
}
cur_lights_params_.resize(qMin(current_lights.size(), max_lights));
// qDebug() << "reloadLightsParameters" << cur_lights_params_.size();
for (int i = 0; i < cur_lights_params_.size(); ++i) {
QGLLightParameter & so(cur_lights_params_[i]);
Light * l = current_lights[i];
double ang_start = l->angle_start / 2.f, ang_end = l->angle_end / 2.f;
if (l->light_type == Light::Omni) ang_start = ang_end = 180.;
// qDebug() << "light" << light->name() << ulightn << pos;
so.color = QColor2QVector(l->color_);
so.angles[0] = ang_start;
so.angles[1] = cos(ang_start * deg2rad);
so.angles[2] = ang_end;
so.angles[3] = cos(ang_end * deg2rad);
so.decay_intensity[0] = l->decay_const;
so.decay_intensity[1] = l->decay_linear;
so.decay_intensity[2] = l->decay_quadratic;
so.decay_intensity[3] = l->intensity;
}
buffer_lights.bind(view);
buffer_lights.resize(view, cur_lights_params_.size() * sizeof(QGLLightParameter));
buffer_lights.load(view, cur_lights_params_.constData(), cur_lights_params_.size() * sizeof(QGLLightParameter));
}
void RendererBase::reloadLightsPositions(Camera * cam) {
cur_lights_pos_.resize(qMin(current_lights.size(), max_lights));
QMatrix4x4 mat = cam->viewMatrix() * cam->offsetMatrix();
for (int i = 0; i < cur_lights_pos_.size(); ++i) {
QGLLightPosition & so(cur_lights_pos_[i]);
Light * l = current_lights[i];
QMatrix4x4 m = mat * l->worldTransform();
QVector4D pos(0, 0, 0, 1.), dir(QVector3D(0, 0, -1), 1);
pos = m * pos;
dir = (m * QVector4D(QVector3D(0, 0, -1), 0)).normalized();
so.position = pos;
so.direction = dir;
}
buffer_lights_pos.bind(view);
buffer_lights_pos.resize(view, cur_lights_pos_.size() * sizeof(QGLLightPosition));
buffer_lights_pos.load(view, cur_lights_pos_.constData(), cur_lights_pos_.size() * sizeof(QGLLightPosition));
}
void RendererBase::markReloadTextures() {
maps_hash = 0;
textures_manager->clearImageCache();
view->scene()->need_reload_materials = true;
}
void RendererBase::setMapsSize(QSize sz) {
maps_size = sz;
markReloadTextures();
}
void RendererBase::initQuad(Mesh * mesh, QMatrix4x4 mat) {
QGLEngineShaders::Object quab_object;
mat.transposed().copyDataTo(quab_object.modelmatrix);
mesh->init(view);
mesh->loadObject(view, quab_object);
}
void RendererBase::renderQuad(QOpenGLShaderProgram * prog, Mesh * mesh, Camera * cam, bool uniforms) {
glDisableDepth();
if (uniforms) setUniformCamera(prog, cam, false);
mesh->draw(view, 1);
}
// ----------------------------------------------------------------------------
float RadicalInverse_VdC(uint bits) {
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
QVector2D Hammersley(uint i, uint N) {
return QVector2D(float(i) / float(N), RadicalInverse_VdC(i));
}
QVector3D ImportanceSampleGGX(QVector2D Xi, QVector3D N, float roughness) {
float a = roughness * roughness;
float phi = 2.0 * M_PI * Xi[0];
float cosTheta = sqrt((1.0 - Xi[1]) / (1.0 + (a * a - 1.0) * Xi[1]));
float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
// преобразование из сферических в декартовы координаты
QVector3D H;
H[0] = cos(phi) * sinTheta;
H[1] = sin(phi) * sinTheta;
H[2] = cosTheta;
// преобразование из касательного пространства в мировые координаты
QVector3D up = qAbs(N[2]) < 0.999 ? QVector3D(0.0, 0.0, 1.0) : QVector3D(1.0, 0.0, 0.0);
QVector3D tangent = QVector3D::crossProduct(up, N).normalized();
QVector3D bitangent = QVector3D::crossProduct(N, tangent);
QVector3D sampleVec = tangent * H[0] + bitangent * H[1] + N * H[2];
return sampleVec.normalized();
}
float GeometrySchlickGGX(float NdotV, float roughness) {
float k = (roughness * roughness) / 2.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(QVector3D N, QVector3D V, QVector3D L, float roughness) {
float NdotV = piMax(QVector3D::dotProduct(N, V), 0.f);
float NdotL = piMax(QVector3D::dotProduct(N, L), 0.f);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
QVector2D IntegrateBRDF(float NdotV, float roughness) {
QVector3D V;
V[0] = sqrt(1.f - NdotV * NdotV);
V[1] = 0.f;
V[2] = NdotV;
float A = 0.f;
float B = 0.f;
QVector3D N = QVector3D(0.f, 0.f, 1.f);
const uint SAMPLE_COUNT = 256u;
for (uint i = 0u; i < SAMPLE_COUNT; ++i) {
QVector2D Xi = Hammersley(i, SAMPLE_COUNT);
QVector3D H = ImportanceSampleGGX(Xi, N, roughness);
QVector3D L = (2.f * QVector3D::dotProduct(V, H) * H - V).normalized();
float NdotL = piMax(L[2], 0.f);
float NdotH = piMax(H[2], 0.f);
float VdotH = piMax(QVector3D::dotProduct(V, H), 0.f);
if (NdotL > 0.f) {
float G = GeometrySmith(N, V, L, roughness);
float G_Vis = (G * VdotH) / (NdotH * NdotV);
float Fc = pow(1.f - VdotH, 5.f);
A += (1.f - Fc) * G_Vis;
B += Fc * G_Vis;
}
}
A /= float(SAMPLE_COUNT);
B /= float(SAMPLE_COUNT);
return QVector2D(A, B);
}
void RendererBase::initCoeffTextures() {
QImage im = QImage(":/coeffs_brdf.png").mirrored();
int size = im.width();
QVector<QVector2D> data(size * size);
int ind = -1;
for (int x = 0; x < size; ++x) {
for (int y = 0; y < size; ++y) {
QColor p = im.pixelColor(x, y);
data[++ind] = QVector2D(p.redF(), p.greenF());
}
}
createCoeffTexture(tex_coeff[0], data.constData(), size, 2);
}
void RendererBase::createCoeffTexture(GLuint & id, const void * data, int size, int channels) {
QOpenGLExtraFunctions * f = view;
deleteGLTexture(f, id);
f->glGenTextures(1, &id);
f->glBindTexture(GL_TEXTURE_2D, id);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GLenum iformat = GL_R16F, format = GL_RED;
if (channels == 2) {
iformat = GL_RG16F;
format = GL_RG;
}
if (channels == 3) {
iformat = GL_RGB16F;
format = GL_RGB;
}
if (channels == 4) {
iformat = GL_RGBA16F;
format = GL_RGBA;
}
f->glTexImage2D(GL_TEXTURE_2D, 0, iformat, size, size, 0, format, GL_FLOAT, data);
}

View File

@@ -0,0 +1,67 @@
/*
QGL RendererBase
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RENDERER_BASE_H
#define RENDERER_BASE_H
#include "glbuffer.h"
#include "glshaders_types.h"
#include "gltexturearray.h"
class QGLENGINE_CORE_EXPORT RendererBase {
public:
RendererBase(QGLView * view_);
~RendererBase();
protected:
void initTextureArrays();
void initUniformBuffer(QOpenGLShaderProgram * prog, Buffer * buffer, int bind_point, const char * blockName);
void setUniformHalo(QOpenGLShaderProgram * prog, const char * type, QColor color, float fill);
void setUniformMaps(QOpenGLShaderProgram * prog);
void setUniformCamera(QOpenGLShaderProgram * prog, Camera * cam, bool matrices = true, QSize viewport = QSize());
void setUniformViewCorners(QOpenGLShaderProgram * prog, Camera * cam, QSize viewport = QSize());
void fillSelectionsBuffer(QVector<uchar> & buffer, const ObjectBaseList & ol);
void fillSelectionsBuffer(QVector<uchar> & buffer, bool yes, int size);
void reloadMaterials(Scene & scene);
void reloadLightsParameters(const QMap<int, QList<Light *>> & lights);
void reloadLightsPositions(Camera * cam);
void markReloadTextures();
void setMapsSize(QSize sz);
void initQuad(Mesh * mesh, QMatrix4x4 mat = QMatrix4x4());
void renderQuad(QOpenGLShaderProgram * prog, Mesh * mesh, Camera * cam = 0, bool uniforms = true);
void initCoeffTextures();
void createCoeffTexture(GLuint & id, const void * data, int size, int channels = 1);
QGLView * view;
TextureManager * textures_manager;
QVector<QGLEngineShaders::Object> cur_objects_;
QVector<QGLEngineShaders::QGLMaterial> cur_materials_;
QVector<QGLEngineShaders::QGLLightParameter> cur_lights_params_;
QVector<QGLEngineShaders::QGLLightPosition> cur_lights_pos_;
Buffer buffer_materials;
Buffer buffer_lights, buffer_lights_pos;
Texture2DArray textures_empty, textures_maps;
QSize maps_size;
uint maps_hash;
GLuint tex_coeff[2];
QMap<int, int> lights_start;
QList<Light *> current_lights;
};
#endif // RENDERER_BASE_H

View File

@@ -0,0 +1,133 @@
/*
QGL RendererMaterial
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define GL_GLEXT_PROTOTYPES
#include "renderer_material.h"
#include "glmesh.h"
#include "gltexture_manager.h"
#include "qglview.h"
#include "renderer.h"
#include <QOpenGLExtraFunctions>
#include <qad_types.h>
using namespace QGLEngineShaders;
RendererMaterial::RendererMaterial(Renderer * r_): r(r_), fbo_mat_thumb(r->view, 6, true, GL_RGBA16F) {
mat_sphere = Primitive::ellipsoid(16, 16);
mat_camera = new Camera();
mat_camera->setPos(QVector3D(2, 2, 2));
mat_camera->setAim(QVector3D());
mat_camera->setFOV(45.);
mat_light = new Light();
mat_light->setPos(QVector3D(50, 100, 25));
last_thumb_material = 0;
}
RendererMaterial::~RendererMaterial() {
delete mat_sphere;
delete mat_camera;
delete mat_light;
}
void RendererMaterial::init(int width, int height) {
fbo_mat_thumb.reinit();
mat_sphere->reinit();
resize(width, height);
}
void RendererMaterial::resize(int width, int height) {
fbo_mat_thumb.enablePixelBuffer();
fbo_mat_thumb.resize(256, 256);
}
void RendererMaterial::renderMaterial(Material * m) {
// qDebug() << "renderMaterial" << m;
last_thumb_material = m;
QOpenGLShaderProgram * prog = 0;
QOpenGLExtraFunctions * f = r->view;
fbo_mat_thumb.bind();
glEnableDepth();
glClearFramebuffer(QColor(0, 0, 0, 0));
if (r->bindShader(Renderer::srGeometryPass, &prog)) {
r->setUniformMaps(prog);
r->setUniformCamera(prog, mat_camera, true, fbo_mat_thumb.size());
// qDebug() << mat_camera->viewMatrix();
r->textures_empty.bind(f, tarEmpty);
r->textures_maps.bind(f, tarMaps);
Object o;
o.material = m->_index;
mat_sphere->loadObject(f, o);
mat_sphere->draw(f, 1);
}
fbo_mat_thumb.bindColorTextures();
fbo_mat_thumb.bindDepthTexture(5);
fbo_mat_thumb.setWriteBuffer(5);
if (r->bindShader(Renderer::srLightOmniPass, &prog)) {
r->setUniformCamera(prog, mat_camera, true, fbo_mat_thumb.size());
r->setUniformViewCorners(prog, mat_camera, fbo_mat_thumb.size());
for (int i = 0; i < 5; ++i)
prog->setUniformValue(QString("tex_%1").arg(i).toLatin1().constData(), i);
prog->setUniformValue("tex_d", 5);
prog->setUniformValue("lights_start", 0);
prog->setUniformValue("lights_count", 1);
QMap<int, QList<Light *>> mat_l;
mat_l[Light::Omni] << mat_light;
r->reloadLightsParameters(mat_l);
r->reloadLightsPositions(mat_camera);
glClearFramebuffer(Qt::black, false);
r->renderQuad(prog, r->quad, mat_camera);
r->view->scene()->setLightsChanged();
}
fbo_mat_thumb.queryImage(5);
fbo_mat_thumb.release();
}
void RendererMaterial::procQueue() {
if (last_thumb_material) {
mat_thumbnails[last_thumb_material] = fbo_mat_thumb.getImage();
emit r->view->materialThumbnailCreated(last_thumb_material);
last_thumb_material = 0;
}
if (!mat_thumb_queue.isEmpty()) renderMaterial(mat_thumb_queue.dequeue());
}
QImage RendererMaterial::materialThumbnail(Material * m) {
return mat_thumbnails.value(m);
}
void RendererMaterial::recreateMaterialThumbnails(bool force_all) {
if (force_all) {
mat_thumb_queue.clear();
// qDebug() << "recreateMaterialThumbnails" << view->scene_->materials;
foreach(Material * m, r->view->scene()->materials)
mat_thumb_queue.enqueue(m);
} else {
foreach(Material * m, r->view->scene()->changed_materials)
if (!mat_thumb_queue.contains(m)) mat_thumb_queue.enqueue(m);
}
}

View File

@@ -0,0 +1,53 @@
/*
QGL RendererMaterial
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RENDERER_MATERIAL_H
#define RENDERER_MATERIAL_H
#include "glframebuffer.h"
#include <QQueue>
class QGLENGINE_CORE_EXPORT RendererMaterial {
friend class QGLView;
public:
RendererMaterial(Renderer * r_);
virtual ~RendererMaterial();
void init(int width, int height);
void resize(int width, int height);
QImage materialThumbnail(Material * m);
void recreateMaterialThumbnails(bool force_all = false);
void renderMaterial(Material * m);
void procQueue();
private:
Renderer * r;
Framebuffer fbo_mat_thumb;
Mesh * mat_sphere;
Camera * mat_camera;
Light * mat_light;
QMap<Material *, QImage> mat_thumbnails;
Material * last_thumb_material;
QQueue<Material *> mat_thumb_queue;
};
#endif // RENDERER_MATERIAL_H

View File

@@ -0,0 +1,215 @@
/*
QGL RendererSelection
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define GL_GLEXT_PROTOTYPES
#include "renderer_selection.h"
#include "glmesh.h"
#include "qglview.h"
#include <QOpenGLExtraFunctions>
#include <qad_types.h>
using namespace QGLEngineShaders;
RendererSelection::RendererSelection(Renderer * r_): r(r_), fbo_selection(r->view, 6) {
sel_frame = Primitive::plane(2., 2.);
id_hover = 0;
line_thick_ = 2.;
scale_ = 0.5;
}
RendererSelection::~RendererSelection() {
delete sel_frame;
}
void RendererSelection::init(int width, int height) {
fbo_selection.reinit();
sel_frame->reinit();
resize(width, height);
}
void RendererSelection::resize(int width, int height) {
line_thick_ = lineThickness() + 1.;
scale_ = 0.5 / appScale();
fbo_selection.enablePixelBuffer();
fbo_selection.resize(width * scale_, height * scale_);
}
void RendererSelection::generateObjectsID(Scene & scene) {
ids.clear();
aim_ids.clear();
QList<int> passes = scene.geometries_used.keys();
foreach(int p, passes) {
QMapIterator<Mesh *, ObjectBaseList> it(scene.geometries_used[p]);
while (it.hasNext()) {
it.next();
foreach(ObjectBase * o, it.value()) {
uint id = qHash(o);
ids[id] = o;
o->id_ = id;
}
}
QMapIterator<int, QList<Light *>> lit(scene.lights_used);
while (lit.hasNext()) {
lit.next();
foreach(ObjectBase * o, lit.value()) {
uint id = qHash(o);
ids[id] = o;
aim_ids[id + 1] = o;
o->id_ = id;
}
}
foreach(Camera * o, scene.cameras_used) {
uint id = qHash(o);
ids[id] = o;
aim_ids[id + 1] = o;
o->id_ = id;
}
}
}
void RendererSelection::renderSelection(Scene & scene) {
QOpenGLShaderProgram * prog = 0;
QGLView * view = r->view;
MouseController & mc(view->mouse);
if (r->bindShader(Renderer::srSelectionFill, &prog)) {
mc.hov_objects.clear();
mc.hov_aims.clear();
id_hover = 0;
if (fbo_selection.queriedPoints() > 0) {
if (fbo_selection.queriedPoints() == 1) {
id_hover = fbo_selection.getPoint();
ObjectBase * o = ids.value(id_hover);
if (o)
mc.hov_objects << o;
else {
o = aim_ids.value(id_hover);
if (o) mc.hov_aims << o;
}
// qDebug() << id_hover;
} else {
QVector<uint> points = fbo_selection.getPointsByte();
QSet<uint> ids_hover;
foreach(uint i, points)
ids_hover << i;
foreach(uint i, ids_hover) {
ObjectBase * o = ids.value(i);
if (o) mc.hov_objects << o;
o = aim_ids.value(i);
if (o) mc.hov_aims << o;
}
// qDebug() << ids_hover;
}
}
fbo_selection.bind();
fbo_selection.setWriteBuffers();
glEnableDepth();
glClearFramebuffer(QColor(0, 0, 0, 0));
r->setUniformCamera(prog, view->camera());
r->renderObjects(scene, rpSolid);
r->renderObjects(scene, rpTransparent);
view->glClear(GL_DEPTH_BUFFER_BIT);
RendererService & rs(r->rend_service);
rs.drawLights();
rs.drawCameras();
rs.drawCurrentHandleObjects();
if (r->mouse_rect.isNull())
fbo_selection.queryPoint(0, r->mouse_pos * scale_);
else
fbo_selection.queryPoints(0, QRect(r->mouse_rect.topLeft() * scale_, r->mouse_rect.size() * scale_));
// qDebug() << id_hover;
fbo_selection.bindColorTexture(sbrSrcHover, sbrSrcHover);
fbo_selection.bindColorTexture(sbrSrcSelect, sbrSrcSelect);
fbo_selection.setWriteBuffers(QVector<int>() << sbrHovered << sbrSelected);
if (!view->isHoverHaloEnabled() && !view->isSelectionHaloEnabled())
glClearFramebuffer(QColor(0, 0, 0, 0), false);
else {
r->bindShader(Renderer::srSelectionHalo, &prog);
r->setUniformHalo(prog, "hover", view->hoverHaloColor(), view->hoverHaloFillAlpha());
r->setUniformHalo(prog, "selection", view->selectionHaloColor(), view->selectionHaloFillAlpha());
prog->setUniformValue("has_hover", view->isHoverHaloEnabled() && (id_hover > 0) ? 1.f : 0.f);
prog->setUniformValue("has_selection", view->isSelectionHaloEnabled() ? 1.f : 0.f);
prog->setUniformValue("fb_hover", (int)sbrSrcHover);
prog->setUniformValue("fb_selection", (int)sbrSrcSelect);
prog->setUniformValue("hover_id",
QVector4D(float(id_hover & 0xFF) / 255.f,
float((id_hover >> 8) & 0xFF) / 255.f,
float((id_hover >> 16) & 0xFF) / 255.f,
float((id_hover >> 24) & 0xFF) / 255.f));
r->renderQuad(prog, r->quad, view->camera());
}
prog = r->shader_fxaa;
if (r->bindShader(prog)) {
r->setUniformCamera(prog, 0, true, fbo_selection.size());
fbo_selection.bindColorTexture(sbrHovered);
fbo_selection.setWriteBuffer(sbrHoveredFXAA);
r->renderQuad(prog, r->quad, 0, false);
fbo_selection.bindColorTexture(sbrSelected);
fbo_selection.setWriteBuffer(sbrSelectedFXAA);
r->renderQuad(prog, r->quad, 0, false);
}
fbo_selection.release();
}
}
void RendererSelection::renderSelectionFrame() {
QOpenGLShaderProgram * prog = 0;
if (r->bindShader(Renderer::srSelectionFrame, &prog)) {
QMatrix4x4 mat;
double mrx = r->mouse_rect.x(), mrw = r->mouse_rect.width(), vw = r->view->width();
double mry = r->mouse_rect.y(), mrh = r->mouse_rect.height(), vh = r->view->height();
mat.translate(-1. + (mrw + mrx * 2) / vw, 1. - (mrh + mry * 2) / vh, 0.);
mat.scale(mrw / vw, mrh / vh, 0.);
r->initQuad(sel_frame, mat);
prog->setUniformValue("size", QVector2D(mrw / vw, mrh / vh));
prog->setUniformValue("thickness", line_thick_);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
r->renderQuad(prog, sel_frame);
glDisable(GL_BLEND);
}
}
void RendererSelection::drawSelection(Framebuffer & fbo_out, int index_out) {
QOpenGLShaderProgram * prog = 0;
if (r->bindShader(Renderer::srSelectionApply, &prog)) {
fbo_selection.bindColorTextures();
fbo_out.bindColorTexture(index_out);
prog->setUniformValue("fb_out", 0);
prog->setUniformValue("fb_hover", (int)sbrHoveredFXAA);
prog->setUniformValue("fb_select", (int)sbrSelectedFXAA);
r->renderQuad(prog, r->quad, r->view->camera());
if (!r->mouse_rect.isNull()) {
renderSelectionFrame();
}
}
}

View File

@@ -0,0 +1,66 @@
/*
QGL RendererSelection
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RENDERER_SELECTION_H
#define RENDERER_SELECTION_H
#include "renderer_service.h"
#include <QHash>
class QGLENGINE_CORE_EXPORT RendererSelection {
friend class QGLView;
friend class MouseController;
friend class Renderer;
friend class RendererService;
public:
RendererSelection(Renderer * r_);
virtual ~RendererSelection();
void init(int width, int height);
void resize(int width, int height);
protected:
enum SelectionBufferRole {
sbrSrcHover,
sbrSrcSelect,
sbrHovered,
sbrSelected,
sbrHoveredFXAA,
sbrSelectedFXAA,
};
void generateObjectsID(Scene & scene);
void renderSelection(Scene & scene);
void renderSelectionFrame();
void drawSelection(Framebuffer & fbo_out, int index_out = 0);
private:
Renderer * r;
Framebuffer fbo_selection;
Mesh * sel_frame;
float line_thick_, scale_;
QVector<uchar> cur_selections_;
QHash<uint, ObjectBase *> ids;
QHash<uint, ObjectBase *> aim_ids;
uint id_hover;
};
#endif // RENDERER_selection_H

View File

@@ -0,0 +1,528 @@
/*
QGL RendererService
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define GL_GLEXT_PROTOTYPES
#include "renderer_service.h"
#include "glmesh.h"
#include "qglview.h"
#include "renderer.h"
#include <QOpenGLExtraFunctions>
#include <qad_types.h>
using namespace QGLEngineShaders;
RendererService::RendererService(Renderer * r_): r(r_) {
line_width = 1;
current_action = haNoAction;
current_handle = QFlags<HandleMesh>();
mat_xyz.resize(3);
mat_ms2.resize(3);
color_xyz.resize(3);
color_ms2.resize(3);
const QVector3D _rot[3] = {QVector3D(0, 1, 0), QVector3D(-1, 0, 0), QVector3D(0, 0, 1)};
const QVector3D _rot2[3] = {QVector3D(0, 0, 0), QVector3D(1, 0, 0), QVector3D(0, -1, 0)};
for (int i = 0; i < 3; ++i) {
QMatrix4x4 m;
m.rotate(90., _rot[i]);
mat_xyz[i] = m;
m.setToIdentity();
if (!_rot2[i].isNull()) m.rotate(90., _rot2[i]);
mat_ms2[i] = m;
color_xyz[i] = color_ms2[i] = QVector4D(0, 0, 0, 0.8);
color_xyz[i][i] = 1.;
}
color_ms2[0] = (color_xyz[0] + color_xyz[1]) / 2.;
color_ms2[1] = (color_xyz[0] + color_xyz[2]) / 2.;
color_ms2[2] = (color_xyz[1] + color_xyz[2]) / 2.;
axis_camera = new Camera();
axis_camera->setAim(QVector3D());
axis_camera->setFOV(45.);
axis_mesh = Primitive::arrow(12);
size_vp_scale = size_full_scale = 1.;
box_mesh = Primitive::cube(0.8, 0.8, 0.8);
box_mesh_f = Primitive::cubeFrame(0.8, 0.8, 0.8);
omni_mesh = Primitive::ellipsoid(2, 1, 0.5);
omni_mesh_f = Primitive::ellipsoidFrame(2, 1, 0.5);
omni_mesh->scalePoints(1.5);
omni_mesh_f->scalePoints(1.5);
cone_mesh = Primitive::cone(8, 0.5, 1.);
cone_mesh_f = Primitive::coneFrame(8, 0.5, 1.);
QMatrix4x4 mat;
mat.translate(0, 0, -1);
cone_mesh->transformPoints(mat);
cone_mesh_f->transformPoints(mat);
cone_mesh_f->scalePoints(1.5);
box_mesh->scalePoints(1.3);
omni_mesh->scalePoints(1.3);
cone_mesh->translatePoints(0, 0, 0.5);
cone_mesh->scalePoints(1.4);
cone_mesh->translatePoints(0, 0, -0.5);
cone_mesh->scalePoints(1.5);
camera_mesh = Primitive::cube(1.2, 1.2, 1.2);
mat.translate(0, 0, -0.5);
Mesh * m = Primitive::cone(6, 0.6, 1.);
m->scalePoints(1.4);
m->transformPoints(mat);
camera_mesh->append(m);
camera_mesh_f = Primitive::cubeFrame(1., 1., 1.);
camera_mesh_f->append(Primitive::cubeFrame(0.5, 0.5, 0.5));
m = Primitive::coneFrame(6, 0.6, 1.);
m->transformPoints(mat);
camera_mesh_f->append(m);
line_spot_f = Primitive::lineFrame(QVector3D(), QVector3D(0, 0, -1));
line_camera_f = Primitive::lineFrame(QVector3D(), QVector3D(0, 0, -1));
handle_move_mesh = Primitive::arrow(12, 0.06);
handle_ms_2_mesh = Primitive::torus(8, 12, 0.5, 0.02, 90);
m = Primitive::disc(8, 0.5, 90);
handle_ms_2_mesh->append(m);
m->flipNormals();
handle_ms_2_mesh->append(m);
delete m;
handle_rotate_mesh = Primitive::arrow(12, 0.03);
m = Primitive::torus(30, 12, 0.5, 0.06);
m->translatePoints(QVector3D(0., 0., 0.75));
handle_rotate_mesh->append(m);
delete m;
handle_scale_mesh = Primitive::cylinder(12, 0.03, 0.85);
m = Primitive::ellipsoid(12, 12, 0.15);
m->translatePoints(QVector3D(0., 0., 0.85));
handle_scale_mesh->append(m);
delete m;
handle_scale_3_mesh = Primitive::ellipsoid(12, 12, 0.2);
handle_move_mesh->scalePoints(7.5);
handle_ms_2_mesh->scalePoints(7.5);
handle_rotate_mesh->scalePoints(7.5);
handle_scale_mesh->scalePoints(7.5);
handle_scale_3_mesh->scalePoints(7.5);
}
RendererService::~RendererService() {
delete box_mesh;
delete box_mesh_f;
delete omni_mesh;
delete omni_mesh_f;
delete cone_mesh;
delete cone_mesh_f;
delete camera_mesh;
delete camera_mesh_f;
delete line_spot_f;
delete line_camera_f;
delete axis_camera;
delete axis_mesh;
delete handle_move_mesh;
delete handle_ms_2_mesh;
delete handle_rotate_mesh;
delete handle_scale_mesh;
delete handle_scale_3_mesh;
}
void RendererService::init(int width, int height) {
box_mesh->reinit();
box_mesh_f->reinit();
omni_mesh->reinit();
omni_mesh_f->reinit();
cone_mesh->reinit();
cone_mesh_f->reinit();
camera_mesh->reinit();
camera_mesh_f->reinit();
line_spot_f->reinit();
line_camera_f->reinit();
axis_mesh->reinit();
handle_move_mesh->reinit();
handle_ms_2_mesh->reinit();
handle_rotate_mesh->reinit();
handle_scale_mesh->reinit();
handle_scale_3_mesh->reinit();
axis_mesh->reinit();
fillXYZObjects();
axis_mesh->loadObjects(r->view, cur_objects);
resize(width, height);
}
void RendererService::resize(int width, int height) {
axis_viewport = preferredIconSize(10.);
line_width = lineThickness();
size_vp_scale = 25. * appScale() / qMax(qMin(width, height), 1);
// qDebug() << axis_viewport;
}
QMatrix4x4 RendererService::invariantSizeMatrix(QVector3D p, double * ret_scale) {
QVector4D pos = QVector4D(p, 1.);
double dist = -(v_mat * pos).z();
QMatrix4x4 m;
m.translate(pos.toVector3D());
m.scale(dist * size_full_scale);
if (ret_scale) *ret_scale = dist * size_full_scale;
return m;
}
QMatrix4x4 RendererService::parentRotationMatrix(ObjectBase * o, bool self_rotation) {
QMatrix4x4 ret;
if (!o) return ret;
QMatrix4x4 pmat;
if (o->parent()) {
pmat = o->parent()->transform().matrixRotate();
}
if (self_rotation) {
ret *= o->transform().matrixRotate();
}
ret = pmat * ret;
return ret;
}
void RendererService::fillXYZObjects() {
cur_objects.resize(3);
for (int i = 0; i < 3; ++i) {
cur_objects[i].color = color_xyz[i];
mat_xyz[i].transposed().copyDataTo(cur_objects[i].modelmatrix);
}
}
void RendererService::fillOmniObjects() {
QList<Light *> ll = r->view->scene()->lights_used.value(Light::Omni);
Object o;
cur_objects.clear();
foreach(Light * l, ll) {
QMatrix4x4 m = invariantSizeMatrix(l->worldPos());
m.transposed().copyDataTo(o.modelmatrix);
o.object_id = l->id();
cur_objects << o;
}
}
void RendererService::fillAimedObjects(const ObjectBaseList & objects, Mesh * line_mesh) {
Object o;
cur_objects.clear();
cur_aims.clear();
QVector<QVector3D> & lv(line_mesh->vertices());
QVector<QVector3D> & ln(line_mesh->normals());
QVector<QVector2D> & lt(line_mesh->texcoords());
QVector<Vector2i> & lind(line_mesh->indicesLines());
lv.clear();
foreach(ObjectBase * go, objects) {
AimedObject * ao = (AimedObject *)go;
QMatrix4x4 m;
m = invariantSizeMatrix(ao->worldPos()) * parentRotationMatrix(ao);
m.transposed().copyDataTo(o.modelmatrix);
o.object_id = ao->id();
cur_objects << o;
lv << ao->worldPos() << ao->worldAim();
m = invariantSizeMatrix(ao->worldAim());
m.transposed().copyDataTo(o.modelmatrix);
o.object_id = ao->id() + 1;
cur_aims << o;
}
ln.resize(lv.size());
lt.resize(lv.size());
lind.resize(lv.size() / 2);
for (int i = 0; i < lind.size(); ++i) {
lind[i] = Vector2i(i * 2, i * 2 + 1);
}
}
void RendererService::fillHandleObjects(QVector3D center,
HandleMesh ids[],
const QVector<QMatrix4x4> & mats,
const QVector<QVector4D> & colors,
QMatrix4x4 add_mat,
int count) {
QMatrix4x4 m = invariantSizeMatrix(center) * add_mat;
cur_objects.resize(count);
for (int i = 0; i < count; ++i) {
cur_objects[i].color = colors[i];
QMatrix4x4 omat = m * mats[i];
cur_objects[i].object_id = ids[i];
if (current_handle.testFlag(ids[i])) {
cur_objects[i].color = QVector4D(0, 1, 1, 1);
}
omat.transposed().copyDataTo(cur_objects[i].modelmatrix);
}
}
bool RendererService::calculateCenter() {
ObjectBaseList sol = r->view->scene()->selectedObjects(true);
if (sol.isEmpty()) return false;
selection_center = sol[0]->worldPos();
if (sol.size() > 1) {
Box3D bb;
foreach(ObjectBase * o, sol) {
o->calculateBoundingBox();
bb |= o->boundingBox();
}
if (!bb.isEmpty()) {
selection_center = bb.center();
}
}
axis_mat = QMatrix4x4();
if ((sol.size() == 1)) {
if (current_action == haMove) {
if (sol[0]->isAimSelected()) selection_center = ((AimedObject *)sol[0])->worldAim();
} else {
axis_mat = parentRotationMatrix(sol[0]);
}
}
return true;
}
void RendererService::drawCurrentHandleObjects() {
if (current_action == haNoAction) return;
if (calculateCenter()) {
HandleMesh ids[3];
switch (current_action) {
case haMove:
ids[0] = hmMoveX;
ids[1] = hmMoveY;
ids[2] = hmMoveZ;
break;
case haRotate:
ids[0] = hmRotateX;
ids[1] = hmRotateY;
ids[2] = hmRotateZ;
break;
case haScale:
ids[0] = hmScaleX;
ids[1] = hmScaleY;
ids[2] = hmScaleZ;
break;
default: break;
}
fillHandleObjects(selection_center, ids, mat_xyz, color_xyz, axis_mat);
Mesh * hm = currentHandleMesh();
QVector<uchar> sel;
sel.fill(0, 3);
if (hm) {
hm->loadObjects(r->view, cur_objects);
hm->loadSelections(r->view, sel);
hm->draw(r->view, 3);
}
if (current_action == haMove || current_action == haScale) {
switch (current_action) {
case haMove:
ids[0] = hmMoveXY;
ids[1] = hmMoveXZ;
ids[2] = hmMoveYZ;
break;
case haScale:
ids[0] = hmScaleXY;
ids[1] = hmScaleXZ;
ids[2] = hmScaleYZ;
break;
default: break;
}
hm = handle_ms_2_mesh;
fillHandleObjects(selection_center, ids, mat_ms2, color_ms2, axis_mat);
hm->loadObjects(r->view, cur_objects);
hm->loadSelections(r->view, sel);
hm->draw(r->view, 3);
if (current_action == haScale) {
hm = handle_scale_3_mesh;
QVector<QMatrix4x4> mv;
mv.resize(1);
QVector<QVector4D> cv;
cv.fill(QVector4D(1, 1, 0.5, 1), 1);
ids[0] = hmMaxScale;
fillHandleObjects(selection_center, ids, mv, cv, axis_mat, 1);
hm->loadObjects(r->view, cur_objects);
hm->loadSelections(r->view, sel);
hm->draw(r->view, 1);
}
}
}
}
void RendererService::drawLights() {
QGLView * v = r->view;
RendererSelection & rs(r->rend_selection);
fillOmniObjects();
omni_mesh->loadObjects(v, cur_objects);
r->fillSelectionsBuffer(rs.cur_selections_, lights2objectList(v->scene()->lights_used.value(Light::Omni)));
omni_mesh->loadSelections(v, rs.cur_selections_);
omni_mesh->draw(v, cur_objects.size());
ObjectBaseList ll = lights2objectList(r->view->scene()->lights_used.value(Light::Cone));
fillAimedObjects(ll, line_spot_f);
cone_mesh->loadObjects(v, cur_objects);
r->fillSelectionsBuffer(rs.cur_selections_, ll);
cone_mesh->loadSelections(v, rs.cur_selections_);
cone_mesh->draw(v, cur_objects.size());
box_mesh->loadObjects(v, cur_aims);
box_mesh->loadSelections(v, rs.cur_selections_);
box_mesh->draw(v, cur_aims.size());
}
void RendererService::drawLightsFrame(QColor color) {
QGLView * v = r->view;
RendererSelection & rs(r->rend_selection);
fillOmniObjects();
setObjectsColor(cur_objects, color);
omni_mesh_f->loadObjects(v, cur_objects);
r->fillSelectionsBuffer(rs.cur_selections_, lights2objectList(v->scene()->lights_used.value(Light::Omni)));
omni_mesh_f->loadSelections(v, rs.cur_selections_);
omni_mesh_f->draw(v, cur_objects.size());
ObjectBaseList ll = lights2objectList(r->view->scene()->lights_used.value(Light::Cone));
fillAimedObjects(ll, line_spot_f);
setObjectsColor(cur_objects, color);
cone_mesh_f->loadObjects(v, cur_objects);
r->fillSelectionsBuffer(rs.cur_selections_, ll);
cone_mesh_f->loadSelections(v, rs.cur_selections_);
cone_mesh_f->draw(v, cur_objects.size());
setObjectsColor(cur_aims, color);
box_mesh_f->loadObjects(v, cur_aims);
box_mesh_f->loadSelections(v, rs.cur_selections_);
box_mesh_f->draw(v, cur_aims.size());
}
void RendererService::drawCameras() {
QGLView * v = r->view;
RendererSelection & rs(r->rend_selection);
ObjectBaseList cl = cameras2objectList(r->view->scene()->cameras_used);
cl.removeOne(r->view->camera());
fillAimedObjects(cl, line_camera_f);
camera_mesh->loadObjects(v, cur_objects);
r->fillSelectionsBuffer(rs.cur_selections_, cl);
camera_mesh->loadSelections(v, rs.cur_selections_);
camera_mesh->draw(v, cur_objects.size());
box_mesh->loadObjects(v, cur_aims);
box_mesh->loadSelections(v, rs.cur_selections_);
box_mesh->draw(v, cur_aims.size());
}
void RendererService::drawCamerasFrame(QColor color) {
QGLView * v = r->view;
RendererSelection & rs(r->rend_selection);
ObjectBaseList cl = cameras2objectList(r->view->scene()->cameras_used);
cl.removeOne(r->view->camera());
fillAimedObjects(cl, line_camera_f);
setObjectsColor(cur_objects, color);
camera_mesh_f->loadObjects(v, cur_objects);
r->fillSelectionsBuffer(rs.cur_selections_, cl);
camera_mesh_f->loadSelections(v, rs.cur_selections_);
camera_mesh_f->draw(v, cur_objects.size());
setObjectsColor(cur_aims, color);
box_mesh_f->loadObjects(v, cur_aims);
box_mesh_f->loadSelections(v, rs.cur_selections_);
box_mesh_f->draw(v, cur_aims.size());
}
void RendererService::setObjectsColor(QVector<Object> & ol, QColor col) {
QVector4D cv = QColor2QVector(col);
for (int i = 0; i < ol.size(); ++i)
ol[i].color = cv;
}
void RendererService::renderService() {
QOpenGLShaderProgram * prog = 0;
QOpenGLExtraFunctions * f = r->view;
size_full_scale = tan(r->view->camera()->FOV() / 2. * deg2rad) * size_vp_scale;
v_mat = r->view->camera()->fullViewMatrix();
f->glEnable(GL_MULTISAMPLE);
glEnableDepth();
f->glClear(GL_DEPTH_BUFFER_BIT);
glDisable(GL_CULL_FACE);
if (r->bindShader(Renderer::srServiceFrame, &prog)) {
prog->setUniformValue("qgl_ProjMatrix", r->view->camera()->projectionMatrix(r->view->aspect));
/// lights
r->setUniformCamera(prog, r->view->camera());
prog->setUniformValue("line_width", 2.f);
prog->setUniformValue("z_offset", 0.f);
drawLightsFrame(Qt::white);
drawCamerasFrame(Qt::white);
prog->setUniformValue("line_width", 1.f);
prog->setUniformValue("z_offset", -1.E-2f);
drawLightsFrame(Qt::black);
drawCamerasFrame(Qt::black);
}
if (r->bindShader(Renderer::srServiceLine, &prog)) {
r->setUniformCamera(prog, r->view->camera());
line_object.color = QColor2QVector(Qt::white);
line_spot_f->loadObject(f, line_object);
line_camera_f->loadObject(f, line_object);
line_spot_f->draw(f, 1);
line_camera_f->draw(f, 1);
}
glEnable(GL_CULL_FACE);
if (r->bindShader(Renderer::srServiceFill, &prog)) {
r->setUniformCamera(prog, r->view->camera());
/// handles
f->glEnable(GL_BLEND);
f->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
drawCurrentHandleObjects();
f->glDisable(GL_BLEND);
/// axis
f->glViewport(0, 0, axis_viewport.width(), axis_viewport.height());
axis_camera->setPos(-r->view->camera()->direction() * 3.);
axis_camera->setAim(QVector3D());
axis_camera->setRotation(r->view->camera()->rotation());
r->setUniformCamera(prog, axis_camera, true, axis_viewport);
axis_mesh->draw(f, 3);
f->glViewport(0, 0, r->view->width(), r->view->height());
}
f->glDisable(GL_MULTISAMPLE);
}
Mesh * RendererService::currentHandleMesh() {
switch (current_action) {
case haMove: return handle_move_mesh;
case haRotate: return handle_rotate_mesh;
case haScale: return handle_scale_mesh;
default: break;
}
return 0;
}

View File

@@ -0,0 +1,112 @@
/*
QGL RendererService
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RENDERER_SERVICE_H
#define RENDERER_SERVICE_H
#include "glframebuffer.h"
#include "glshaders_types.h"
#include <QQueue>
class QGLENGINE_CORE_EXPORT RendererService {
friend class QGLView;
friend class MouseController;
friend class Renderer;
friend class RendererSelection;
public:
RendererService(Renderer * r_);
virtual ~RendererService();
enum HandleAction {
haNoAction,
haMove,
haRotate,
haScale,
};
enum HandleMesh {
hmMoveX = 0x01,
hmMoveY = 0x02,
hmMoveZ = 0x04,
hmMoveXY = hmMoveX | hmMoveY,
hmMoveXZ = hmMoveX | hmMoveZ,
hmMoveYZ = hmMoveY | hmMoveZ,
hmMaxMove = hmMoveX | hmMoveY | hmMoveZ,
hmRotateX = 0x08,
hmRotateY = 0x10,
hmRotateZ = 0x20,
hmMaxRotate = hmRotateX | hmRotateY | hmRotateZ,
hmScaleX = 0x40,
hmScaleY = 0x80,
hmScaleZ = 0x100,
hmScaleXY = hmScaleX | hmScaleY,
hmScaleXZ = hmScaleX | hmScaleZ,
hmScaleYZ = hmScaleY | hmScaleZ,
hmMaxScale = hmScaleX | hmScaleY | hmScaleZ,
};
void init(int width, int height);
void resize(int width, int height);
QMatrix4x4 invariantSizeMatrix(QVector3D p, double * ret_scale = 0);
QMatrix4x4 parentRotationMatrix(ObjectBase * o, bool self_rotation = true);
void fillXYZObjects();
void fillOmniObjects();
void fillAimedObjects(const ObjectBaseList & objects, Mesh * line_mesh);
void fillHandleObjects(QVector3D center,
HandleMesh ids[],
const QVector<QMatrix4x4> & mats,
const QVector<QVector4D> & colors,
QMatrix4x4 add_mat,
int count = 3);
bool calculateCenter();
void drawCurrentHandleObjects();
void drawLights();
void drawLightsFrame(QColor color);
void drawCameras();
void drawCamerasFrame(QColor color);
void setObjectsColor(QVector<QGLEngineShaders::Object> & ol, QColor col);
void renderService();
void setCurrentAction(HandleAction ha) { current_action = ha; }
Mesh * currentHandleMesh();
private:
Renderer * r;
Mesh *axis_mesh, *handle_move_mesh, *handle_rotate_mesh, *handle_scale_mesh;
Mesh *handle_ms_2_mesh, *handle_scale_3_mesh;
Mesh *box_mesh_f, *omni_mesh_f, *cone_mesh_f, *camera_mesh_f;
Mesh *box_mesh, *omni_mesh, *cone_mesh, *camera_mesh;
Mesh *line_spot_f, *line_camera_f;
QMatrix4x4 v_mat, axis_mat;
QVector3D selection_center;
QVector<QMatrix4x4> mat_xyz, mat_ms2;
QVector<QVector4D> color_xyz, color_ms2;
QVector<QGLEngineShaders::Object> cur_objects, cur_aims;
QGLEngineShaders::Object line_object;
Camera * axis_camera;
QSize axis_viewport;
HandleAction current_action;
QFlags<HandleMesh> current_handle;
int line_width;
double size_vp_scale, size_full_scale;
};
#endif // RENDERER_SERVICE_H

View File

@@ -0,0 +1,187 @@
/*
QGL TonemappingProc
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define GL_GLEXT_PROTOTYPES
#include "tonemapping_proc.h"
#include "qglview.h"
#include <QOpenGLExtraFunctions>
#include <qad_types.h>
using namespace QGLEngineShaders;
TonemappingProc::TonemappingProc(Renderer * rend)
: QThread()
, r(rend)
, fbo_1x1(r->view, 1, false, GL_RGB32F)
, fbomm(r->fbo_out, Renderer::obrGeneral1, 3)
, buffer_vbo(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW) {
shader_sum = 0;
timer_delim = 0;
frame_max = cur_max = 1.;
need_render_sum = exit_ = false;
enabled = false;
timer_tone = startTimer(10);
}
TonemappingProc::~TonemappingProc() {
exit_ = true;
if (!wait(1000)) terminate();
killTimer(timer_tone);
if (shader_sum) delete shader_sum;
}
void TonemappingProc::init() {
buffer_vbo.reinit();
fbo_1x1.reinit();
fbomm.reinit();
QOpenGLExtraFunctions * f = r->view;
buffer_vbo.init(f);
f->glGenVertexArrays(1, &vbo_vao);
f->glBindVertexArray(vbo_vao);
buffer_vbo.bind(f);
f->glEnableVertexAttribArray(1);
f->glVertexAttribIPointer(1, 2, GL_UNSIGNED_INT, 0, 0);
buffer_vbo.release(f);
f->glBindVertexArray(0);
fbomm.lastPlane().enablePixelBuffer();
fbo_1x1.resize(1, 1);
resize();
if (!isRunning()) start();
}
void TonemappingProc::resize() {
QOpenGLExtraFunctions * f = r->view;
fbomm.resize();
int pcnt = fbomm.width(fbomm.lastLevel()) * fbomm.height(fbomm.lastLevel());
QVector<Vector2i> _data;
_data.resize(pcnt);
pcnt = -1;
for (int x = 0; x < fbomm.width(fbomm.lastLevel()); ++x)
for (int y = 0; y < fbomm.height(fbomm.lastLevel()); ++y)
_data[++pcnt] = Vector2i(x, y);
buffer_vbo.bind(f);
buffer_vbo.resize(f, _data.size() * sizeof(Vector2i));
buffer_vbo.load(f, _data.constData(), _data.size() * sizeof(Vector2i));
}
void TonemappingProc::timerEvent(QTimerEvent * e) {
if (!fbo_1x1.isInit() || !enabled) return;
if (timer_delim == 0) need_render_sum = true;
timer_delim = (timer_delim + 1) % 10;
mutex.lock();
float fmax = frame_max;
mutex.unlock();
float dt = 0.01f, a = dt * 5.f, b = 1.f - a;
cur_max = fmax * a + cur_max * b;
}
void TonemappingProc::renderSum(Framebuffer & fbo_src, int index) {
QOpenGLExtraFunctions * f = r->view;
int pcnt = fbo_src.width() * fbo_src.height();
fbo_src.bindColorTexture(index);
fbo_1x1.bind();
glClearFramebuffer();
if (shader_sum) {
if (shader_sum->isLinked()) {
if (shader_sum->bind()) {
shader_sum->setUniformValue("tex", 0);
shader_sum->setUniformValue("pcnt", float(pcnt));
f->glBindVertexArray(vbo_vao);
f->glEnable(GL_BLEND);
f->glBlendFunc(GL_ONE, GL_ONE);
f->glBlendEquation(GL_MAX);
f->glDrawArrays(GL_POINTS, 0, pcnt);
f->glBlendEquation(GL_FUNC_ADD);
f->glDisable(GL_BLEND);
f->glBindVertexArray(0);
}
}
}
fbo_1x1.release();
}
void TonemappingProc::run() {
while (!exit_) {
if (!enabled) {
msleep(100);
continue;
}
mutex.lock();
if (last_data.isEmpty()) {
mutex.unlock();
msleep(10);
continue;
}
QVector<QVector4D> data = last_data;
last_data.clear();
mutex.unlock();
float max = calcHistogram(data);
last_max << max;
if (last_max.size() > 5) last_max.removeAt(0);
float cm = last_max[0];
for (int i = 1; i < last_max.size(); ++i)
cm += last_max[i];
cm /= last_max.size();
mutex.lock();
frame_max = cm;
mutex.unlock();
}
}
float TonemappingProc::calcHistogram(const QVector<QVector4D> & data) {
if (data.isEmpty()) return 1.f;
float max = 0.;
QVector3D luma(0.299, 0.587, 0.114);
foreach(const QVector4D & p, data) {
float l = QVector3D::dotProduct(p.toVector3D(), luma);
max = qMax(max, l);
}
return max;
}
bool TonemappingProc::process() {
if (!need_render_sum || !enabled) return false;
need_render_sum = false;
Framebuffer & fbo(fbomm.lastPlane());
if (fbo.queriedPoints() > 0) {
QVector<QVector4D> data = fbo.getPointsFloat();
mutex.lock();
last_data = data;
mutex.unlock();
}
fbomm.create();
fbo.queryPoints(0, fbo.rect(), GL_FLOAT);
return true;
}
float TonemappingProc::frameMax() {
if (!enabled) return 1.f;
return cur_max;
}

View File

@@ -0,0 +1,64 @@
/*
QGL TonemappingProc
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TONEMAPPING_PROC_H
#define TONEMAPPING_PROC_H
#include "glframebuffer_mipmap.h"
#include <QThread>
class QGLENGINE_CORE_EXPORT TonemappingProc: public QThread {
friend class Renderer;
friend class QGLView;
public:
TonemappingProc(Renderer * rend);
virtual ~TonemappingProc();
void init();
void resize();
void prepareSum();
void renderSum(Framebuffer & fbo_src, int index);
bool process();
float frameMax();
protected:
void timerEvent(QTimerEvent * e) override;
void run() override;
float calcHistogram(const QVector<QVector4D> & data);
private:
Renderer * r;
QMutex mutex;
QVector<QVector4D> last_data;
QVector<float> last_max;
float frame_max, cur_max;
bool need_render_sum, enabled;
std::atomic_bool exit_;
int timer_tone, timer_delim;
Framebuffer fbo_1x1;
FramebufferMipmap fbomm;
QOpenGLShaderProgram * shader_sum;
Buffer buffer_vbo;
GLenum vbo_vao;
};
#endif // RENDERER_H