/* QGLView 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 . */ #include "glrendererbase.h" #include "globject.h" #include "qglview.h" GLRendererBase::GLRendererBase(QGLView * view_): view(*view_) { white_image = QImage(1, 1, QImage::Format_ARGB32); white_image.fill(0xFFFFFFFF); white_image_id = 0; violent_image = QImage(1, 1, QImage::Format_ARGB32); violent_image.fill(QColor(127, 127, 255)); violent_image_id = 0; } void GLRendererBase::setupLight(const Light & l, int inpass_index, int gl_index) { QVector3D lp = l.worldPos(), ld = (l.itransform_ * QVector4D(l.direction, 0.)).toVector3D().normalized(); GLfloat pos[] = {0.f, 0.f, 0.f, 0.f}; GLfloat dir[] = {0.f, 0.f, 0.f}; GLfloat col[] = {0.f, 0.f, 0.f}; pos[0] = l.light_type == Light::Directional ? -l.direction.x() : lp.x(); pos[1] = l.light_type == Light::Directional ? -l.direction.y() : lp.y(); pos[2] = l.light_type == Light::Directional ? -l.direction.z() : lp.z(); pos[3] = l.light_type == Light::Directional ? 0. : 1.; dir[0] = ld.x(); dir[1] = ld.y(); dir[2] = ld.z(); col[0] = l.visible_ ? l.color().redF() * l.intensity : 0.f; col[1] = l.visible_ ? l.color().greenF() * l.intensity : 0.f; col[2] = l.visible_ ? l.color().blueF() * l.intensity : 0.f; glEnable(gl_index); // glLightfv(gl_index, GL_AMBIENT, ambient); glLightfv(gl_index, GL_DIFFUSE, col); glLightfv(gl_index, GL_SPECULAR, col); glLightfv(gl_index, GL_POSITION, pos); glLightf(gl_index, GL_CONSTANT_ATTENUATION, l.decay_const); glLightf(gl_index, GL_LINEAR_ATTENUATION, l.decay_linear); glLightf(gl_index, GL_QUADRATIC_ATTENUATION, l.decay_quadratic); if (l.light_type == Light::Cone) { glLightfv(gl_index, GL_SPOT_DIRECTION, dir); glLightf(gl_index, GL_SPOT_CUTOFF, l.angle_end / 2.f); glLightf(gl_index, GL_SPOT_EXPONENT, (1.f - piClamp((l.angle_end - l.angle_start) / (l.angle_end + 0.001f), 0., 1.f)) * 128.f); } else { glLightf(gl_index, GL_SPOT_CUTOFF, 180.); } } void GLRendererBase::setupAmbientLight(const QColor & a, bool first_pass) { GLfloat ambient[] = {0.0f, 0.0f, 0.0f, 1.f}; if (first_pass) { ambient[0] = view.ambientColor_.redF(); ambient[1] = view.ambientColor_.greenF(); ambient[2] = view.ambientColor_.blueF(); } glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); } void GLRendererBase::setupShadersLights(int lights_count) { /*foreach (QOpenGLShaderProgram * i, view.shaders_ppl) { i->bind(); i->setUniformValue("lightsCount", lights_count); i->setUniformValue("acc_light", lights_count > 0); //i->setUniformValue("mat", mvm); }*/ } #define BIND_TEXTURE(ch, map) \ if (rp.prev_tex[ch] != mat.map.bitmap_id) { \ rp.prev_tex[ch] = mat.map.bitmap_id; \ glActiveTexture(GL_TEXTURE0 + ch); \ glBindTexture(GL_TEXTURE_2D, mat.map.bitmap_id); \ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, view.feature(QGLView::qglAnisotropicLevel).toInt()); \ } void GLRendererBase::setupTextures(GLObjectBase & o, GLRendererBase::RenderingParameters & rp, bool first_object) { if (first_object) { view.glReleaseTextures(); return; } setupShadersTextures(o, rp); Material & mat(o.material_); if (rp.light) { if (o.accept_light) { if (!rp.prev_light) { glSetLightEnabled(true); rp.prev_light = true; } } else { if (rp.prev_light) { glSetLightEnabled(false); rp.prev_light = false; } } } if (rp.fog) { if (o.accept_fog) { if (!rp.prev_fog) { glSetFogEnabled(true); rp.prev_fog = true; } } else { if (rp.prev_fog) { glSetFogEnabled(false); rp.prev_fog = false; } } } if (rp.textures) { BIND_TEXTURE(0, map_diffuse) BIND_TEXTURE(1, map_normal) BIND_TEXTURE(2, map_relief) BIND_TEXTURE(3, map_self_illumination) BIND_TEXTURE(4, map_specularity) BIND_TEXTURE(5, map_specular) glActiveTexture(GL_TEXTURE0); } } #undef BIND_TEXTURE void GLRendererBase::setupLights(int pass, int lights_per_pass) { int light_start, light_end, lmax; light_start = pass * lights_per_pass; light_end = qMin((pass + 1) * lights_per_pass, view.lights_.size()); setupAmbientLight(view.ambientColor_, pass == 0); if (!view.lights_.isEmpty()) { setupShadersLights(light_end - light_start); for (int i = light_start; i < light_end; ++i) setupLight(*view.lights_[i], i - light_start, GL_LIGHT0 + i - light_start); lmax = light_start + 8; for (int i = light_end; i < lmax; ++i) glDisable(GL_LIGHT0 + i - light_start); } else { setupShadersLights(0); for (int i = 0; i < 8; ++i) glDisable(GL_LIGHT0 + i); } } void GLRendererBase::applyFilteringParameters() { if (view.isFeatureEnabled(QGLView::qglLinearFiltering)) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, view.feature(QGLView::qglAnisotropicLevel).toInt()); } void GLRendererBase::renderObjects(int pass, int light_pass, void * shaders, bool textures, bool light, bool fog) { RenderingParameters rpl; rpl.pass = pass; rpl.light_pass = light_pass; rpl.shaders = shaders; rpl.textures = textures; rpl.light = rpl.prev_light = light; rpl.fog = rpl.prev_fog = fog; rpl.view_matrix = rp.view_matrix; rpl.prev_view_matrix = rp.prev_view_matrix; rpl.proj_matrix = rp.proj_matrix; rpl.prev_proj_matrix = rp.prev_proj_matrix; rpl.cam_offset_matrix = view.camera()->offsetMatrix(); // qDebug() << "view:" << rp.view_matrix; for (int i = 0; i < 32; ++i) rpl.prev_tex[i] = 0; setupTextures(view.objects_, rpl, true); glSetLightEnabled(rpl.prev_light); glSetFogEnabled(rpl.prev_fog); glSetCapEnabled(GL_TEXTURE_2D, rpl.textures); glSetCapEnabled(GL_BLEND, pass == GLObjectBase::Transparent); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_TEXTURE_CUBE_MAP); glPushMatrix(); renderSingleObject(view.objects_, rpl); glPopMatrix(); } void GLRendererBase::renderSingleObject(GLObjectBase & o, RenderingParameters & rpl) { if (!o.isInit()) o.init(); if (!o.isTexturesLoaded()) o.loadTextures(); if (!o.visible_) return; if (rpl.pass == o.pass_) { Material & mat(o.material_); QMatrix4x4 curview = rpl.view_matrix * rpl.cam_offset_matrix * o.itransform_, prevview = rpl.prev_view_matrix * rpl.cam_offset_matrix * o.itransform_; setupTextures(o, rpl, false); mat.apply((QOpenGLShaderProgram *)rpl.shaders); glSetPolygonMode(o.render_mode != GLObjectBase::View ? o.render_mode : (view.rmode != GLObjectBase::View ? view.rmode : GL_FILL)); glLineWidth(o.line_width > 0.f ? o.line_width : view.lineWidth_); glPointSize(o.line_width > 0.f ? o.line_width : view.lineWidth_); o.update(); if (o.pass_ == GLObjectBase::Transparent) { glActiveTexture(GL_TEXTURE0 + 3); if (mat.reflectivity > 0.f) { glEnable(GL_TEXTURE_CUBE_MAP); if (!mat.map_reflection.isEmpty()) mat.map_reflection.bind(); else glDisable(GL_TEXTURE_CUBE_MAP); } else glDisable(GL_TEXTURE_CUBE_MAP); if (rpl.light_pass > 0) glDisable(GL_TEXTURE_CUBE_MAP); GLfloat gm[16], bc[4] = {mat.reflectivity, mat.reflectivity, mat.reflectivity, mat.reflectivity}; glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, bc); glGetFloatv(GL_MODELVIEW_MATRIX, gm); glMatrixMode(GL_TEXTURE); /// glLoadTransposeMatrixf(gm); glScalef(-1., -1., -1.); glMatrixMode(GL_MODELVIEW); glActiveTexture(GL_TEXTURE0); } if (rpl.shaders) { // qDebug() << o.name() << curview << curview.determinant(); setUniformMatrices((QOpenGLShaderProgram *)rpl.shaders, rpl.proj_matrix, curview, rpl.prev_proj_matrix, prevview); } else { glMatrixMode(GL_MODELVIEW); setGLMatrix(curview); } o.draw((QOpenGLShaderProgram *)rpl.shaders); } foreach(GLObjectBase * i, o.children_) renderSingleObject(*i, rpl); } void GLRendererBase::renderShadow(Light * l, QOpenGLShaderProgram * prog, QMatrix4x4 mat) { Camera cam; QVector3D wp = l->worldPos(); cam.setPos(wp); cam.setAim(wp + (l->worldTransform() * QVector4D(l->direction)).toVector3D()); cam.setDepthStart(view.camera()->depthStart()); cam.setDepthEnd(view.camera()->depthEnd()); cam.setFOV(l->angle_end); cam.apply(1.); /*cam.rotateXY(l->angle_end); QVector3D rdir = l->direction * cos(l->angle_end / 2. * deg2rad); l->dir0 = cam.direction() - rdir; cam.rotateXY(-l->angle_end); cam.rotateZ(l->angle_end); l->dir1 = cam.direction() - rdir;*/ // qDebug() << rdir << l->dir0 << l->dir1; RenderingParameters rpl; rpl.pass = GLObjectBase::Solid; rpl.shaders = prog; rpl.textures = rpl.light = rpl.fog = false; rpl.view_matrix = getGLMatrix(GL_MODELVIEW_MATRIX); rpl.proj_matrix = getGLMatrix(GL_PROJECTION_MATRIX); rpl.cam_offset_matrix = cam.offsetMatrix(); QMatrix4x4 mbias; mbias.scale(0.5, 0.5, 0.5); mbias.translate(1., 1., 1.); l->shadow_matrix = mbias * rpl.proj_matrix * rpl.view_matrix * rpl.cam_offset_matrix * mat; //;// * mbias; // qDebug() << mbias; // glPushMatrix(); renderSingleShadow(view.objects_, rpl); // glPopMatrix(); } void GLRendererBase::renderSingleShadow(GLObjectBase & o, RenderingParameters & rpl) { if (!o.isInit()) o.init(); if (!o.visible_) return; if (rpl.shaders) { // qDebug() << o.name() << curview << curview.determinant(); setUniformMatrices((QOpenGLShaderProgram *)rpl.shaders, rpl.proj_matrix, rpl.view_matrix * rpl.cam_offset_matrix * o.itransform_); } else { glMatrixMode(GL_MODELVIEW); setGLMatrix(rpl.view_matrix * rpl.cam_offset_matrix * o.itransform_); } glPolygonMode(GL_FRONT_AND_BACK, o.render_mode != GLObjectBase::View ? o.render_mode : (view.rmode != GLObjectBase::View ? view.rmode : GL_FILL)); glLineWidth(o.line_width > 0.f ? o.line_width : view.lineWidth_); glPointSize(o.line_width > 0.f ? o.line_width : view.lineWidth_); o.draw((QOpenGLShaderProgram *)rpl.shaders, true); foreach(GLObjectBase * i, o.children_) renderSingleShadow(*i, rpl); } GLRendererBase::RenderingParameters::RenderingParameters() { shaders = nullptr; cur_shader = nullptr; } void GLRendererBase::RenderingParameters::prepare() { proj_matrix = getGLMatrix(GL_PROJECTION_MATRIX); view_matrix = getGLMatrix(GL_MODELVIEW_MATRIX); viewproj_matrix = proj_matrix * view_matrix; normal_matrix = view_matrix.normalMatrix(); proj_matrix_i = proj_matrix.inverted(); view_matrix_i = view_matrix.inverted(); viewproj_matrix_i = viewproj_matrix.inverted(); } void GLRendererBase::RenderingParameters::setUniform(QOpenGLShaderProgram * prog) { if (!prog) return; prog->setUniformValue("qgl_ModelViewMatrix", view_matrix); prog->setUniformValue("qgl_ProjectionMatrix", proj_matrix); prog->setUniformValue("qgl_ModelViewProjectionMatrix", viewproj_matrix); prog->setUniformValue("qgl_NormalMatrix", normal_matrix); prog->setUniformValue("qgl_ModelViewMatrixInverse", view_matrix_i); prog->setUniformValue("qgl_ProjectionMatrixInverse", proj_matrix_i); prog->setUniformValue("qgl_ModelViewProjectionMatrixInverse", viewproj_matrix_i); prog->setUniformValue("qgl_ModelViewMatrixTranspose", view_matrix.transposed()); prog->setUniformValue("qgl_ProjectionMatrixTranspose", proj_matrix.transposed()); prog->setUniformValue("qgl_ModelViewProjectionMatrixTranspose", viewproj_matrix.transposed()); prog->setUniformValue("qgl_ModelViewMatrixInverseTranspose", view_matrix_i.transposed()); prog->setUniformValue("qgl_ProjectionMatrixInverseTranspose", proj_matrix_i.transposed()); prog->setUniformValue("qgl_ModelViewProjectionMatrixInverseTranspose", viewproj_matrix_i.transposed()); }