/* QGLView Copyright (C) 2012 Ivan Pelipenko peri4ko@gmail.com 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 . */ #include "qglview.h" QGLView::QGLView(QWidget * parent): QGraphicsView(parent), fbo_selection(3) { setFrameShape(QFrame::NoFrame); setViewportUpdateMode(FullViewportUpdate); setCacheMode(CacheNone); QGLFormat f(QGL::DoubleBuffer | QGL::DepthBuffer | QGL::Rgba | QGL::AlphaChannel | QGL::DirectRendering | QGL::SampleBuffers); f.setSwapInterval(1); setViewport(new QGLWidget(f)); setMouseTracking(true); setFocusPolicy(Qt::WheelFocus); setScene(new QGraphicsScene()); setInteractive(true); objects_.is_root = true; painter_ = 0; backColor_ = Qt::black; hoverHaloColor_ = QColor(195, 140, 255, 96); selectionHaloColor_ = QColor(175, 255, 140); ambientColor_ = QColor(10, 10, 10); lastPos = QPoint(-1, -1); lineWidth_ = 1.; max_anisotropic = 1; max_texture_chanels = 8; cameraOrbit_ = lightEnabled_ = true; shaders_supported = selecting_ = customMouseMove_ = false; sel_button = Qt::LeftButton; sel_mod = Qt::NoModifier; renderer_ = 0; fps_cnt = 0; fps_tm = fps_ = 0.; sel_obj = hov_obj = 0; fogDensity_ = fogEnd_ = 1.; fogStart_ = 0.; fogMode_ = Exp; hoverHaloFill_ = 0.333; selectionHaloFill_ = 0.5; //lmode = Simple; shader_select = shader_halo = 0; setFeature(qglMSAA, false); setFeature(qglFXAA, false); setFeature(qglLinearFiltering, true); setFeature(qglAnisotropicLevel, 8); setFeature(qglHDR, false); setFeature(qglEyeAccomodationEnabled, false); setFeature(qglEyeAccomodationTime, 16.); setFeature(qglEyeAccomodationMaxSpeed, 0.2); setFeature(qglBloomEnabled, false); setFeature(qglBloomThreshold, 0.9); setFeature(qglBloomFactor, 1.); setFeature(qglBloomRadius, 8); setFeature(qglMotionBlurEnabled, false); setFeature(qglMotionBlurFactor, 1.); setFeature(qglMotionBlurSteps, 8); setFeature(qglShadowsEnabled, false); setFeature(qglShadowsMapSize, 512); setFeature(qglShadowsSoftEnabled, true); setFeature(qglDynamicReflectionsEnabled, false); setFeature(qglDynamicReflectionsMapSize, 512); mouse_first = mouseSelect_ = hoverHalo_ = selectionHalo_ = true; fogEnabled_ = is_init = grabMouse_ = mouseRotate_ = mouseThis_ = shaders_bind = changed_ = false; rmode = GLObjectBase::Fill; sel_mode = QGLView::SingleSelection; sel_pen = QPen(Qt::black, 1, Qt::DashLine); sel_brush = QBrush(QColor(170, 100, 255, 120)); camera().setAim(QVector3D(0,0,5.5)); camera().setPos(QVector3D(10, 5, 5.5)); emit cameraPosChanged(camera().pos()); //camera().aim_ = camera().pos_; ktm_.restart(); } QGLView::~QGLView() { if (shader_select != 0) delete shader_select; if (shader_halo != 0) delete shader_halo; //if (shader_rope != 0) delete shader_rope; } void QGLView::addObject(QWidget * o, Qt::WindowFlags f) { scene()->addWidget(o, f)->setCacheMode(QGraphicsItem::ItemCoordinateCache); /*QList il = collectGraphicItems(); foreach (QGraphicsItem * i, il) { QGraphicsProxyWidget * p = qgraphicsitem_cast(i); if (p == 0) continue; p->setCacheMode(QGraphicsItem::ItemCoordinateCache); }*/ } void QGLView::addObject(GLObjectBase * o) { o->setView(this); objects_.addChild(o); collectLights(); if (is_init) { globMutex.lock(); o->init(); globMutex.unlock(); } } void QGLView::selectObject(GLObjectBase * o) { emit selectionChanged(o, sel_obj); sel_obj = o; } void QGLView::drawBackground(QPainter * painter, const QRectF & rect) { static bool f = true; painter_ = painter; painter_->beginNativePainting(); if (f) { resizeGL(viewport()->width(), viewport()->height()); initializeGL(); } f = false; paintGL(); painter_->endNativePainting(); glDisableDepth(); glDisable(GL_BLEND); glDisable(GL_CULL_FACE); glReleaseTextures(); QGraphicsView::drawBackground(painter_, rect); } void QGLView::initializeGL() { //qDebug() << "init glview"; makeCurrent(); currentQGLView = (QGLWidget * )viewport(); currentGLTextureManager = &textures_manager; currentCamera = &camera(); //glEnable(GL_POLYGON_SMOOTH); glEnable(GL_TEXTURE_MAX_ANISOTROPY_EXT); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //glEnable(GL_TEXTURE_2D); //glEnable(GL_TEXTURE_CUBE_MAP); glEnableDepth(); glEnable(GL_CULL_FACE); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1); glActiveTextureChannel(3); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glActiveTextureChannel(0); //glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NONE); //glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NONE); //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glShadeModel(GL_SMOOTH); glCullFace(GL_BACK); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT, GL_DIFFUSE); textures_manager.loadTextures(); objects_.initInternal(); checkCaps(); shader_select = new QGLShaderProgram(context()); shader_halo = new QGLShaderProgram(context()); //shader_rope = new QGLShaderProgram(context()); reloadThisShaders(); is_init = true; resizeGL(viewport()->width(), viewport()->height()); //dynamic_cubemap.loadPathesFromDirectory("e"); //dynamic_cubemap.load(); /*dynamic_cubemap.loadFront("e/front.jpg"); dynamic_cubemap.loadBack("e/back.jpg"); dynamic_cubemap.loadLeft("e/left.jpg"); dynamic_cubemap.loadRight("e/right.jpg"); dynamic_cubemap.loadTop("e/top.jpg"); dynamic_cubemap.loadBottom("e/bottom.jpg");*/ //glGenerateMipmap(GL_TEXTURE_CUBE_MAP); //GLuint t = 0; //createGLTexture(t, QImage("e/bottom.jpg")); //qDebug() << bindTexture(QImage("e/bottom.jpg")); emit glInitializeDone(); } void QGLView::paintGL() { //QMutexLocker ml_v(&v_mutex); glEnable(GL_CULL_FACE); //glDisable(GL_CULL_FACE); camera().apply(aspect); //objects_.preparePos(camera()); start_rp.cam_offset_matrix = camera().offsetMatrix(); start_rp.proj_matrix = getGLMatrix(GL_PROJECTION_MATRIX); start_rp.view_matrix = getGLMatrix(GL_MODELVIEW_MATRIX); //objects_.buildTransform(); /// Selection detect //glClearFramebuffer(QColor(100, 0, 0, 0)); if (mouseSelect_) { glReleaseTextures(); glEnableDepth(); glDisable(GL_TEXTURE_1D); glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_CUBE_MAP); glDisable(GL_MULTISAMPLE); glDisable(GL_LIGHTING); glDisable(GL_BLEND); glDisable(GL_ALPHA_TEST); glDisable(GL_RESCALE_NORMAL); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); fbo_selection.bind(); fbo_selection.setWriteBuffer(0); glClearFramebuffer(QColor(0, 0, 0, 0)); if (shaders_supported) shader_select->bind(); renderSelection(); if (shaders_supported) shader_select->release(); uchar cgid[4] = {0, 0, 0, 0}; uint iid; GLObjectBase * so = 0; if (!rect().contains(lastPos)) { if (hov_obj != 0) { hov_obj = 0; emit hoverChanged(0, hov_obj); } } else { glReadPixels(lastPos.x(), viewport()->height() - lastPos.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, cgid); iid = (cgid[0] << 24) | (cgid[1] << 16) | (cgid[2] << 8) | cgid[3]; so = ids[iid]; //qDebug() <name() << cgid[3]; } if (selectionHalo_ && sel_obj) { fbo_selection.setWriteBuffer(2); renderHalo(sel_obj, ids.key(sel_obj), selectionHaloColor_, selectionHaloFill_); } if (hoverHalo_ && hov_obj) { fbo_selection.setWriteBuffer(1); renderHalo(hov_obj, iid, hoverHaloColor_, hoverHaloFill_); } fbo_selection.release(); glEnableDepth(); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_COLOR_ARRAY); } camera().apply(aspect); start_rp.cam_offset_matrix = camera().offsetMatrix(); //objects_.preparePos(camera()); static GLRendererBase * prev_rend = 0; if (prev_rend != renderer_) { prev_rend = renderer_; if (renderer_ != 0) { renderer_->init(viewport()->width(), viewport()->height()); renderer_->resize(viewport()->width(), viewport()->height()); renderer_->reloadShaders(); } } emit glBeginPaint(); if (renderer_ != 0) { renderer_->rp.prepare(); renderer_->prepareScene(); renderer_->renderScene(); } if (selectionHalo_ || hoverHalo_) { glReleaseTextures(); glReleaseShaders(); glReleaseFramebuffer(); glActiveTextureChannel(0); glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glDisable(GL_TEXTURE_CUBE_MAP); glDisable(GL_LIGHTING); glDisableDepth(); glBlendFunc(GL_SRC_ALPHA, GL_ONE); if (selectionHalo_ && sel_obj) { glBindTexture(GL_TEXTURE_2D, fbo_selection.colorTexture(2)); //qDebug() << "draw sel"; glDrawQuad(); } if (hoverHalo_ && hov_obj) { glBindTexture(GL_TEXTURE_2D, fbo_selection.colorTexture(1)); //qDebug() << "draw hover"; glDrawQuad(); } } if (selecting_ && painter_) { painter_->setPen(sel_pen); painter_->setBrush(sel_brush); painter_->drawRect(QRect(downPos, lastPos).normalized()); qDebug() << QRect(downPos, lastPos).normalized(); } glReleaseShaders(); glResetAllTransforms(); glReleaseFramebuffer(); emit glPainting(); /*releaseShaders(); glActiveTextureChannel(0); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4f(0.3, 0.5, 0.8, 0.5); glResetAllTransforms(); glBegin(GL_QUADS); glTexCoord2f(0.f, 0.f); glVertex2f(-1.f, -1.f); glTexCoord2f(1.f, 0.f); glVertex2f(1.f, -1.); glTexCoord2f(1.f, 1.f); glVertex2f(1.f, 1.f); glTexCoord2f(0.f, 1.f); glVertex2f(-1.f, 1.f); glEnd();*/ /* glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_LIGHTING); glActiveTextureChannel(0); glBindTexture(GL_TEXTURE_2D, fbo->texture()); glDisable(GL_DEPTH_TEST); glBegin(GL_QUADS); glColor3f(1.f, 1.f, 1.f); glTexCoord2f(0.f, 0.f); glVertex2f(-1.f, -1.f); glTexCoord2f(0.f, 1.f); glVertex2f(-1.f, 1.f); glTexCoord2f(1.f, 1.f); glVertex2f(1.f, 1.f); glTexCoord2f(1.f, 0.f); glVertex2f(1.f, -1.); glEnd(); glEnable(GL_DEPTH_TEST);*/ fps_tm += time.elapsed(); time.restart(); fps_cnt++; if (fps_tm < 1000.) return; fps_ = fps_cnt / fps_tm * 1000.; fps_tm = 0.; fps_cnt = 0; //glResetAllTransforms(); } void QGLView::renderHalo(const GLObjectBase * obj, const int iid, const QColor & color, const double & fill) { if (!shaders_supported) return; if (obj) { shader_halo->bind(); shader_halo->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); glActiveTextureChannel(0); glBindTexture(GL_TEXTURE_2D, fbo_selection.colorTexture()); shader_halo->setUniformValue("t0", 0); shader_halo->setUniformValue("dt", QVector2D(1. / viewport()->width(), 1. / viewport()->height())); shader_halo->setUniformValue("selected", QVector4D(float((iid >> 24) & 0xFF) / 255.f, float((iid >> 16) & 0xFF) / 255.f, float((iid >> 8) & 0xFF) / 255.f, float(iid & 0xFF) / 255.f)); shader_halo->setUniformValue("color", color); shader_halo->setUniformValue("fill", GLfloat(fill)); //qDebug() << "render halo" << iid << shader_halo->log() << shader_halo->programId(); glDisableDepth(); glDrawQuad(shader_halo); glDepthMask(GL_TRUE); //glFlush(); shader_halo->release(); } else { glClearFramebuffer(Qt::black, false); } } void QGLView::renderSelection() { cid = 1; ids.clear(); if (shaders_supported) { sh_id_loc = shader_select->uniformLocation("id"); shader_select->setUniformValue("z_far", GLfloat(depthEnd())); shader_select->setUniformValue("z_near", GLfloat(depthStart())); } //qDebug() << sh_id_loc; start_rp.view_matrix = getGLMatrix(GL_MODELVIEW_MATRIX); glPushMatrix(); renderSingleSelection(objects_); glPopMatrix(); } void QGLView::renderSingleSelection(GLObjectBase & o) { if (!o.isInit()) { o.init(); o.loadTextures(); } if (!o.visible_ || !o.select_) return; QMatrix4x4 curview = start_rp.view_matrix * start_rp.cam_offset_matrix * o.itransform_; ids.insert(cid, &o); if (shaders_supported){ setUniformMatrices(shader_select, start_rp.proj_matrix, curview); shader_select->setUniformValue(sh_id_loc, QVector4D(float((cid >> 24) & 0xFF) / 255.f, float((cid >> 16) & 0xFF) / 255.f, float((cid >> 8) & 0xFF) / 255.f, float(cid & 0xFF) / 255.f)); } else { setGLMatrix(curview); glColor4f(float((cid >> 24) & 0xFF) / 255.f, float((cid >> 16) & 0xFF) / 255.f, float((cid >> 8) & 0xFF) / 255.f, float(cid & 0xFF) / 255.f); } //qDebug() << o.name() << "assign to" << sh_id_loc << cid; //glColor4f(float((cid >> 24) & 0xFF) / 255.f, float((cid >> 16) & 0xFF) / 255.f, float((cid >> 8) & 0xFF) / 255.f, float(cid & 0xFF) / 255.f); ++cid; o.draw(0, true); foreach (GLObjectBase * i, o.children_) renderSingleSelection(*i); } void QGLView::collectLights() { lights_.clear(); collectObjectLights(&objects_); } QList QGLView::collectGraphicItems() { QList list = scene()->items(); foreach (QGraphicsItem * i, list) collectGraphicItems(list, i); return list; } void QGLView::collectGraphicItems(QList & list, QGraphicsItem * o) { QList cl = o->childItems(); foreach (QGraphicsItem * i, cl) { list << i; collectGraphicItems(list, i); } } void QGLView::collectObjectLights(GLObjectBase * o) { if (o->type_ == GLObjectBase::glLight) lights_ << globject_cast(o); foreach (GLObjectBase * i, o->children()) collectObjectLights(i); } void QGLView::checkCaps() { glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropic); //glGetIntegerv(GL_MAX_TEXTURE_UNITS, &max_texture_chanels); //qDebug() << max_texture_chanels; //qDebug() << max_texture_chanels; shaders_supported = QGLShaderProgram::hasOpenGLShaderPrograms(); } void QGLView::reloadThisShaders() { if (!shaders_supported) return; loadShaders(shader_select, "selection", "shaders"); loadShaders(shader_halo, "selection_halo", "shaders"); //loadShaders(shader_rope, "rope", "shaders"); } void QGLView::applyFog() { GLfloat fog_col[4] = {float(fogColor_.redF()), float(fogColor_.greenF()), float(fogColor_.blueF()), .0f}; if (fogEnabled_) { glEnable(GL_FOG); glFogf(GL_FOG_DENSITY, fogDensity_); glFogf(GL_FOG_START, fogStart_); glFogf(GL_FOG_END, fogEnd_); glFogi(GL_FOG_MODE, fogMode_); fog_col[0] = fogColor_.redF(); fog_col[1] = fogColor_.greenF(); fog_col[2] = fogColor_.blueF(); glFogfv(GL_FOG_COLOR, fog_col); } else glDisable(GL_FOG); } void QGLView::resizeGL(int width, int height) { if (!is_init) return; aspect = double(width) / double(height); if (renderer_ != 0) renderer_->resize(width, height); //qDebug() << "resize" << width << height; fbo_selection.resize(width, height); mouse_first = true; iaspect = (aspect == 0.) ? 0. : 1 / aspect; glViewport(0, 0, width, height); emit glResize(width, height); } void QGLView::mouseReleaseEvent(QMouseEvent * e) { QGraphicsView::mouseReleaseEvent(e); //setCursor(QCursor(Qt::ArrowCursor)); selecting_ = false; if (mouseThis_ && mouseSelect_ && e->button() == Qt::LeftButton) { if ((lastPos - downPos).manhattanLength() < 8) { if (sel_obj != hov_obj) selectObject(hov_obj); } } emit glMouseReleaseEvent(e); } void QGLView::mousePressEvent(QMouseEvent * e) { QGraphicsView::mousePressEvent(e); mouseThis_ = (scene()->itemAt(mapToScene(e->pos())) == 0); selecting_ = false; if (!mouseThis_) return; /// TODO select by rect //if (e->button() == sel_button && e->modifiers() == sel_mod) // selecting_ = true; lastPos = e->pos(); downPos = lastPos; //qDebug() << mouseThis_; emit glMousePressEvent(e); } void QGLView::mouseMoveEvent(QMouseEvent * e) { QGraphicsView::mouseMoveEvent(e); //qDebug() << scene()->itemAt(mapToScene(e->pos())); //lastPos = e->pos(); if (selecting_) { return; } if (!mouseThis_) return; //if (scene()->itemAt(mapToScene(e->pos())) != 0) return; ///qDebug() << e->x() << e->y(); if (mouseRotate_) { double dx = e->x() - lastPos.x(); double dy = e->y() - lastPos.y(); if (e->buttons() & Qt::LeftButton) { //camera().angle_z += dx / 4.; //camera().angle_xy += dy / 4.; if (cameraOrbit_) { camera().orbitZ(dx / 4.); camera().orbitXY(dy / 4.); } else { camera().rotateZ(dx / 4.); camera().rotateXY(dy / 4.); } emit cameraPosChanged(camera().pos()); } else if (e->buttons() & Qt::RightButton) { double ad = camera().distance(); camera().moveLeft(dx / 1000. * ad); camera().moveUp(dy / 1000. * ad); //camera().pos.setX(camera().pos.x() + camera().pos.z() * dx / 500.); //camera().pos.setY(camera().pos.y() - camera().pos.z() * dy / 500.); emit cameraPosChanged(camera().pos()); } //lights[0]->pos_ = camera().pos(); } if (customMouseMove_) emit customMouseMoveEvent(e->pos(), lastPos, e->buttons()); lastPos = e->pos(); if (grabMouse_) { //if (!isrunning) return; QCursor::setPos(mapToGlobal(rect().center())); static bool mouse_sec = false; if (mouse_sec) { mouse_sec = false; return; } if (mouse_first) { mouse_first = false; mouse_sec = true; //qDebug() << "first" << e->pos(); return; } lastPos = rect().center(); int dx = e->x() - lastPos.x(); int dy = e->y() - lastPos.y(); emit glMouseMoveEvent(new QMouseEvent(QEvent::MouseMove, QPoint(dx, dy), e->button(), e->buttons(), e->modifiers())); return; } //emit glMouseMoveEvent(e); } void QGLView::wheelEvent(QWheelEvent * e) { QGraphicsView::wheelEvent(e); if (scene()->itemAt(mapToScene(e->pos())) != 0) return; if (mouseRotate_) { if (e->delta() > 0) camera().flyCloser(0.1); //camera().pos.setZ(camera().pos.z() - 0.1 * camera().pos.z()); if (e->delta() < 0) camera().flyFarer(0.1); //camera().pos.setZ(camera().pos.z() + 0.1 * camera().pos.z()); emit cameraPosChanged(camera().pos()); } emit glWheelEvent(e); } void QGLView::leaveEvent(QEvent * ) { lastPos = QPoint(-1, -1); //qDebug() << lastPos; } void QGLView::processKeys() { if (ktm_.elapsed() < QApplication::keyboardInputInterval()) return; Qt::KeyboardModifiers km = QApplication::keyboardModifiers(); foreach (int i, keys_) emit keyEvent((Qt::Key)i, km); }