source-tree refactoring
This commit is contained in:
450
src/core/render/renderer.cpp
Normal file
450
src/core/render/renderer.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user