300 lines
10 KiB
C++
300 lines
10 KiB
C++
/*
|
|
QGLView
|
|
Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#define GL_GLEXT_PROTOTYPES
|
|
#include <QOpenGLExtraFunctions>
|
|
#include "renderer_base.h"
|
|
#include "qglview.h"
|
|
#include "glmesh.h"
|
|
#include "gltexture_manager.h"
|
|
#include "glshaders_headers.h"
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
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_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();
|
|
QVector4D corner_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)
|
|
prog->setUniformValue(QString("view_corners[%1]").arg(i).toLatin1().constData(), corner_dirs[i]);
|
|
}
|
|
|
|
|
|
void RendererBase::fillSelectionsBuffer(QVector<uchar> & buffer, const QList<ObjectBase *> & 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_specular .hasBitmap()) maps[0] << &(m->map_specular );
|
|
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->load(textures_manager);
|
|
m->_index = cur_materials_.size();
|
|
m->_changed = false;
|
|
glm.color_diffuse = QColor2QVector(m->color_diffuse );
|
|
glm.color_specular = QColor2QVector(m->color_specular);
|
|
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_specular .copyToQGLMap(glm.map[mtSpecular ]);
|
|
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";
|
|
//textures_maps.resize(maps_size, );
|
|
//cur_materials_[0].color_diffuse = QColor2QVector(Qt::red);
|
|
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.intensity = l->intensity;
|
|
so.startAngle = ang_start;
|
|
so.startAngleCos = cos(ang_start * deg2rad);
|
|
so.endAngle = ang_end;
|
|
so.endAngleCos = cos(ang_end * deg2rad);
|
|
so.color = QColor2QVector(l->color_);
|
|
so.constantAttenuation = l->decay_const;
|
|
so.linearAttenuation = l->decay_linear;
|
|
so.quadraticAttenuation = l->decay_quadratic;
|
|
//so.shadow = shadow;
|
|
//so.shadowColor = shadow;
|
|
}
|
|
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(l->direction(), 1);//, dir0(light->dir0), dir1(light->dir1);
|
|
pos = m * pos;
|
|
dir = (m * QVector4D(l->direction(),0)).normalized();//((m * dir) - pos).normalized();
|
|
so.position = pos;
|
|
so.direction = dir;
|
|
//so.shadowMatrix = l->shadow_matrix;
|
|
}
|
|
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) {
|
|
glDisableDepth();
|
|
setUniformCamera(prog, cam, false);
|
|
mesh->draw(view, 1);
|
|
}
|